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, #exec_rollback_to_savepoint, #release_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!, #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, #exec_rollback_to_savepoint, #insert, #insert_fixture, #join_to_delete, #join_to_update, #reset_sequence!, #reset_transaction, #rollback_db_transaction, #rollback_to_savepoint, #sanitize_limit, #select_all, #select_one, #select_value, #select_values, #to_sql, #transaction, #transaction_isolation_levels, #transaction_open?, #transaction_state, #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.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 124

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 ==================================================



219
220
221
222
223
224
225
226
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 219

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

#_type_cast(value) ⇒ Object

:nodoc:



228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 228

def _type_cast(value) # :nodoc:
  case value
  when BigDecimal
    value.to_f
  when String
    if value.encoding == Encoding::ASCII_8BIT
      super(value.encode(Encoding::UTF_8))
    else
      super
    end
  else
    super
  end
end

#active?Boolean

Returns:

  • (Boolean)


176
177
178
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 176

def active?
  @active != false
end

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

:nodoc:



451
452
453
454
455
456
457
458
459
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 451

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



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

def allowed_index_name_length
  index_name_length - 2
end

#begin_db_transactionObject

:nodoc:



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

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

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

:nodoc:



482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 482

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:



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

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



473
474
475
476
477
478
479
480
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 473

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.



189
190
191
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 189

def clear_cache!
  @statements.clear
end

#columns(table_name) ⇒ Object

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



384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 384

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:



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

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

#delete_sql(sql, name = nil) ⇒ Object

:nodoc:



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

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.



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

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

#encodingObject

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



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

def encoding
  @connection.encoding.to_s
end

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



318
319
320
321
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 318

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

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



288
289
290
291
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
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 288

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

#exec_rollback_db_transactionObject

:nodoc:



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

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

#execute(sql, name = nil) ⇒ Object

:nodoc:



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

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

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

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



269
270
271
272
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 269

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.



402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 402

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:



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

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



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

def last_inserted_id(result)
  @connection.last_insert_row_id
end

#native_database_typesObject

:nodoc:



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

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#primary_key(table_name) ⇒ Object

:nodoc:



426
427
428
429
430
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 426

def primary_key(table_name) #:nodoc:
  pks = table_structure(table_name).select { |f| f['pk'] > 0 }
  return nil unless pks.count == 1
  pks[0]['name']
end

#quote_column_name(name) ⇒ Object

:nodoc:



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

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

#quote_string(s) ⇒ Object

:nodoc:



243
244
245
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 243

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

#quote_table_name_for_assignment(table, attr) ⇒ Object



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

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.



257
258
259
260
261
262
263
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 257

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:



461
462
463
464
465
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 461

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:



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

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:



496
497
498
499
500
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 496

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')


440
441
442
443
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 440

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)


168
169
170
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 168

def requires_reloading?
  true
end

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



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

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

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


141
142
143
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 141

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


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

def supports_explain?
  true
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


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

def supports_index_sort_order?
  true
end

#supports_migrations?Boolean

Returns true, since this connection adapter supports migrations.

Returns:

  • (Boolean)


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

def supports_migrations? #:nodoc:
  true
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)


149
150
151
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 149

def supports_partial_index?
  sqlite_version >= '3.8.0'
end

#supports_primary_key?Boolean

:nodoc:

Returns:

  • (Boolean)


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

def supports_primary_key? #:nodoc:
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 145

def supports_savepoints?
  true
end

#supports_statement_cache?Boolean

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

Returns:

  • (Boolean)


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

def supports_statement_cache?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)


172
173
174
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 172

def supports_views?
  true
end

#table_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)


379
380
381
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 379

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

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

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



366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 366

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:



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

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)


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

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