Class: ActiveRecord::ConnectionAdapters::SQLite3Adapter

Inherits:
AbstractAdapter show all
Includes:
Savepoints
Defined in:
lib/active_record/connection_adapters/sqlite3_adapter.rb

Overview

The SQLite3 adapter works SQLite 3.6.16 or newer with the sqlite3-ruby drivers (available as gem from rubygems.org/gems/sqlite3).

Options:

  • :database - Path to the database file.

Defined Under Namespace

Classes: ExplainPrettyPrinter, StatementPool, Version

Constant Summary collapse

ADAPTER_NAME =
'SQLite'.freeze
NATIVE_DATABASE_TYPES =
{
  primary_key:  'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
  string:       { name: "varchar" },
  text:         { name: "text" },
  integer:      { name: "integer" },
  float:        { name: "float" },
  decimal:      { name: "decimal" },
  datetime:     { name: "datetime" },
  time:         { name: "time" },
  date:         { name: "date" },
  binary:       { name: "blob" },
  boolean:      { name: "boolean" }
}

Constants inherited from AbstractAdapter

AbstractAdapter::SIMPLE_INT

Instance Attribute Summary

Attributes inherited from AbstractAdapter

#logger, #owner, #pool, #prepared_statements, #schema_cache, #visitor

Attributes included from QueryCache

#query_cache, #query_cache_enabled

Attributes included from DatabaseStatements

#transaction_manager

Instance Method Summary collapse

Methods included from Savepoints

#create_savepoint, #release_savepoint, #rollback_to_savepoint

Methods inherited from AbstractAdapter

#adapter_name, #case_insensitive_comparison, #case_sensitive_comparison, #case_sensitive_modifier, #close, #collector, #column_name_for_operation, #create_savepoint, #current_savepoint_name, #disable_extension, #disable_referential_integrity, #enable_extension, #expire, #extensions, #index_algorithms, #lease, #lookup_cast_type, #new_column, #prefetch_primary_key?, #raw_connection, #reconnect!, #release_savepoint, #reset!, #rollback_to_savepoint, #schema_creation, #substitute_at, #supports_bulk_alter?, #supports_extensions?, #supports_foreign_keys?, #supports_indexes_in_create?, #supports_transaction_isolation?, type_cast_config_to_boolean, type_cast_config_to_integer, #type_map, #unprepared_statement, #valid_type?, #verify!

Methods included from ColumnDumper

#column_spec, #migration_keys, #prepare_column_options

Methods included from QueryCache

#cache, #clear_query_cache, dirties_query_cache, #disable_query_cache!, #enable_query_cache!, included, #select_all, #uncached

Methods included from DatabaseLimits

#column_name_length, #columns_per_multicolumn_index, #columns_per_table, #in_clause_length, #index_name_length, #indexes_per_table, #joins_per_query, #sql_query_length, #table_alias_length, #table_name_length

Methods included from Quoting

#quote, #quote_table_name, #quoted_false, #quoted_true, #type_cast, #unquoted_false, #unquoted_true

Methods included from DatabaseStatements

#add_transaction_record, #begin_isolated_db_transaction, #cacheable_query, #default_sequence_name, #delete, #empty_insert_statement_value, #exec_insert, #insert, #insert_fixture, #join_to_delete, #join_to_update, #reset_sequence!, #reset_transaction, #sanitize_limit, #select_all, #select_one, #select_value, #select_values, #to_sql, #transaction, #transaction_isolation_levels, #transaction_open?, #truncate, #update

Methods included from SchemaStatements

#add_foreign_key, #add_index, #add_index_options, #add_reference, #add_timestamps, #assume_migrated_upto_version, #change_table, #column_exists?, #columns_for_distinct, #create_join_table, #create_table, #drop_join_table, #drop_table, #dump_schema_information, #foreign_key_column_for, #foreign_keys, #index_exists?, #index_name, #index_name_exists?, #initialize_schema_migrations_table, #remove_columns, #remove_foreign_key, #remove_index, #remove_reference, #remove_timestamps, #rename_index, #table_alias_for, #type_to_sql, #update_table_definition

Methods included from TimestampDefaultDeprecation

#emit_warning_if_null_unspecified

Constructor Details

#initialize(connection, logger, connection_options, config) ⇒ SQLite3Adapter

Returns a new instance of SQLite3Adapter.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 134

def initialize(connection, logger, connection_options, config)
  super(connection, logger)

  @active     = nil
  @statements = StatementPool.new(@connection,
                                  self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
  @config = config

  @visitor = Arel::Visitors::SQLite.new self

  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
    @prepared_statements = true
  else
    @prepared_statements = false
  end
end

Instance Method Details

#_quote(value) ⇒ Object

QUOTING ==================================================



229
230
231
232
233
234
235
236
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 229

def _quote(value) # :nodoc:
  case value
  when Type::Binary::Data
    "x'#{value.hex}'"
  else
    super
  end
end

#_type_cast(value) ⇒ Object

:nodoc:



238
239
240
241
242
243
244
245
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 238

def _type_cast(value) # :nodoc:
  case value
  when BigDecimal
    value.to_f
  else
    super
  end
end

#active?Boolean

Returns:

  • (Boolean)


186
187
188
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 186

def active?
  @active != false
end

#add_column(table_name, column_name, type, options = {}) ⇒ Object

:nodoc:



456
457
458
459
460
461
462
463
464
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 456

def add_column(table_name, column_name, type, options = {}) #:nodoc:
  if valid_alter_table_type?(type)
    super(table_name, column_name, type, options)
  else
    alter_table(table_name) do |definition|
      definition.column(column_name, type, options)
    end
  end
end

#allowed_index_name_lengthObject

Returns 62. SQLite supports index names up to 64 characters. The rest is used by rails internally to perform temporary rename operations



210
211
212
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 210

def allowed_index_name_length
  index_name_length - 2
end

#begin_db_transactionObject

:nodoc:



356
357
358
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 356

def begin_db_transaction #:nodoc:
  log('begin transaction',nil) { @connection.transaction }
end

#change_column(table_name, column_name, type, options = {}) ⇒ Object

:nodoc:



487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 487

def change_column(table_name, column_name, type, options = {}) #:nodoc:
  alter_table(table_name) do |definition|
    include_default = options_include_default?(options)
    definition[column_name].instance_eval do
      self.type    = type
      self.limit   = options[:limit] if options.include?(:limit)
      self.default = options[:default] if include_default
      self.null    = options[:null] if options.include?(:null)
      self.precision = options[:precision] if options.include?(:precision)
      self.scale   = options[:scale] if options.include?(:scale)
    end
  end
end

#change_column_default(table_name, column_name, default) ⇒ Object

:nodoc:



472
473
474
475
476
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 472

def change_column_default(table_name, column_name, default) #:nodoc:
  alter_table(table_name) do |definition|
    definition[column_name].default = default
  end
end

#change_column_null(table_name, column_name, null, default = nil) ⇒ Object



478
479
480
481
482
483
484
485
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 478

def change_column_null(table_name, column_name, null, default = nil)
  unless null || default.nil?
    exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  end
  alter_table(table_name) do |definition|
    definition[column_name].null = null
  end
end

#clear_cache!Object

Clears the prepared statements cache.



199
200
201
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 199

def clear_cache!
  @statements.clear
end

#columns(table_name) ⇒ Object

Returns an array of Column objects for the table specified by table_name.



388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 388

def columns(table_name) #:nodoc:
  table_structure(table_name).map do |field|
    case field["dflt_value"]
    when /^null$/i
      field["dflt_value"] = nil
    when /^'(.*)'$/m
      field["dflt_value"] = $1.gsub("''", "'")
    when /^"(.*)"$/m
      field["dflt_value"] = $1.gsub('""', '"')
    end

    sql_type = field['type']
    cast_type = lookup_cast_type(sql_type)
    new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
  end
end

#commit_db_transactionObject

:nodoc:



360
361
362
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 360

def commit_db_transaction #:nodoc:
  log('commit transaction',nil) { @connection.commit }
end

#delete_sql(sql, name = nil) ⇒ Object

:nodoc:



341
342
343
344
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 341

def delete_sql(sql, name = nil) #:nodoc:
  sql += " WHERE 1=1" unless sql =~ /WHERE/i
  super sql, name
end

#disconnect!Object

Disconnects from the database if already connected. Otherwise, this method does nothing.



192
193
194
195
196
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 192

def disconnect!
  super
  @active = false
  @connection.close rescue nil
end

#encodingObject

Returns the current database encoding format as a string, eg: ‘UTF-8’



219
220
221
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 219

def encoding
  @connection.encoding.to_s
end

#exec_delete(sql, name = 'SQL', binds = []) ⇒ Object Also known as: exec_update



322
323
324
325
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 322

def exec_delete(sql, name = 'SQL', binds = [])
  exec_query(sql, name, binds)
  @connection.changes
end

#exec_query(sql, name = nil, binds = []) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 292

def exec_query(sql, name = nil, binds = [])
  type_casted_binds = binds.map { |col, val|
    [col, type_cast(val, col)]
  }

  log(sql, name, type_casted_binds) do
    # Don't cache statements if they are not prepared
    if without_prepared_statement?(binds)
      stmt    = @connection.prepare(sql)
      begin
        cols    = stmt.columns
        records = stmt.to_a
      ensure
        stmt.close
      end
      stmt = records
    else
      cache = @statements[sql] ||= {
        :stmt => @connection.prepare(sql)
      }
      stmt = cache[:stmt]
      cols = cache[:cols] ||= stmt.columns
      stmt.reset!
      stmt.bind_params type_casted_binds.map { |_, val| val }
    end

    ActiveRecord::Result.new(cols, stmt.to_a)
  end
end

#execute(sql, name = nil) ⇒ Object

:nodoc:



332
333
334
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 332

def execute(sql, name = nil) #:nodoc:
  log(sql, name) { @connection.execute(sql) }
end

#explain(arel, binds = []) ⇒ Object

– DATABASE STATEMENTS ====================================== ++



273
274
275
276
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 273

def explain(arel, binds = [])
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
  ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
end

#indexes(table_name, name = nil) ⇒ Object

Returns an array of indexes for the given table.



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 406

def indexes(table_name, name = nil) #:nodoc:
  exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
    sql = <<-SQL
      SELECT sql
      FROM sqlite_master
      WHERE name=#{quote(row['name'])} AND type='index'
      UNION ALL
      SELECT sql
      FROM sqlite_temp_master
      WHERE name=#{quote(row['name'])} AND type='index'
    SQL
    index_sql = exec_query(sql).first['sql']
    match = /\sWHERE\s+(.+)$/i.match(index_sql)
    where = match[1] if match
    IndexDefinition.new(
      table_name,
      row['name'],
      row['unique'] != 0,
      exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
        col['name']
      }, nil, nil, where)
  end
end

#insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) ⇒ Object Also known as: create

:nodoc:



346
347
348
349
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 346

def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
  super
  id_value || @connection.last_insert_row_id
end

#last_inserted_id(result) ⇒ Object



328
329
330
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 328

def last_inserted_id(result)
  @connection.last_insert_row_id
end

#native_database_typesObject

:nodoc:



214
215
216
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 214

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#primary_key(table_name) ⇒ Object

:nodoc:



430
431
432
433
434
435
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 430

def primary_key(table_name) #:nodoc:
  column = table_structure(table_name).find { |field|
    field['pk'] == 1
  }
  column && column['name']
end

#quote_column_name(name) ⇒ Object

:nodoc:



255
256
257
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 255

def quote_column_name(name) #:nodoc:
  %Q("#{name.to_s.gsub('"', '""')}")
end

#quote_string(s) ⇒ Object

:nodoc:



247
248
249
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 247

def quote_string(s) #:nodoc:
  @connection.class.quote(s)
end

#quote_table_name_for_assignment(table, attr) ⇒ Object



251
252
253
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 251

def quote_table_name_for_assignment(table, attr)
  quote_column_name(attr)
end

#quoted_date(value) ⇒ Object

Quote date/time values for use in SQL input. Includes microseconds if the value is a Time responding to usec.



261
262
263
264
265
266
267
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 261

def quoted_date(value) #:nodoc:
  if value.respond_to?(:usec)
    "#{super}.#{sprintf("%06d", value.usec)}"
  else
    super
  end
end

#remove_column(table_name, column_name, type = nil, options = {}) ⇒ Object

:nodoc:



466
467
468
469
470
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 466

def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
  alter_table(table_name) do |definition|
    definition.remove_column column_name
  end
end

#remove_index!(table_name, index_name) ⇒ Object

:nodoc:



437
438
439
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 437

def remove_index!(table_name, index_name) #:nodoc:
  exec_query "DROP INDEX #{quote_column_name(index_name)}"
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object

:nodoc:



501
502
503
504
505
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 501

def rename_column(table_name, column_name, new_column_name) #:nodoc:
  column = column_for(table_name, column_name)
  alter_table(table_name, rename: {column.name => new_column_name.to_s})
  rename_column_indexes(table_name, column.name, new_column_name)
end

#rename_table(table_name, new_name) ⇒ Object

Renames a table.

Example:

rename_table('octopuses', 'octopi')


445
446
447
448
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 445

def rename_table(table_name, new_name)
  exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
  rename_table_indexes(table_name, new_name)
end

#requires_reloading?Boolean

Returns:

  • (Boolean)


178
179
180
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 178

def requires_reloading?
  true
end

#rollback_db_transactionObject

:nodoc:



364
365
366
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 364

def rollback_db_transaction #:nodoc:
  log('rollback transaction',nil) { @connection.rollback }
end

#select_rows(sql, name = nil, binds = []) ⇒ Object



352
353
354
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 352

def select_rows(sql, name = nil, binds = [])
  exec_query(sql, name, binds).rows
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


151
152
153
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 151

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


223
224
225
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 223

def supports_explain?
  true
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


203
204
205
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 203

def supports_index_sort_order?
  true
end

#supports_migrations?Boolean

Returns true, since this connection adapter supports migrations.

Returns:

  • (Boolean)


170
171
172
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 170

def supports_migrations? #:nodoc:
  true
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)


159
160
161
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 159

def supports_partial_index?
  sqlite_version >= '3.8.0'
end

#supports_primary_key?Boolean

:nodoc:

Returns:

  • (Boolean)


174
175
176
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 174

def supports_primary_key? #:nodoc:
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


155
156
157
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 155

def supports_savepoints?
  true
end

#supports_statement_cache?Boolean

Returns true, since this connection adapter supports prepared statement caching.

Returns:

  • (Boolean)


165
166
167
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 165

def supports_statement_cache?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)


182
183
184
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 182

def supports_views?
  true
end

#table_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)


383
384
385
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 383

def table_exists?(table_name)
  table_name && tables(nil, table_name).any?
end

#tables(name = nil, table_name = nil) ⇒ Object

SCHEMA STATEMENTS ========================================



370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 370

def tables(name = nil, table_name = nil) #:nodoc:
  sql = <<-SQL
    SELECT name
    FROM sqlite_master
    WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
  SQL
  sql << " AND name = #{quote_table_name(table_name)}" if table_name

  exec_query(sql, 'SCHEMA').map do |row|
    row['name']
  end
end

#update_sql(sql, name = nil) ⇒ Object

:nodoc:



336
337
338
339
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 336

def update_sql(sql, name = nil) #:nodoc:
  super
  @connection.changes
end

#valid_alter_table_type?(type) ⇒ Boolean

See: www.sqlite.org/lang_altertable.html SQLite has an additional restriction on the ALTER TABLE statement

Returns:

  • (Boolean)


452
453
454
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 452

def valid_alter_table_type?(type)
  type.to_sym != :primary_key
end