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: BindSubstitution, ExplainPrettyPrinter, StatementPool, Version

Constant Summary collapse

NATIVE_DATABASE_TYPES =
{
  primary_key:  'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
  string:       { name: "varchar", limit: 255 },
  text:         { name: "text" },
  integer:      { name: "integer" },
  float:        { name: "float" },
  decimal:      { name: "decimal" },
  datetime:     { name: "datetime" },
  timestamp:    { 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

#in_use, #last_use, #logger, #pool, #schema_cache, #visitor

Attributes included from QueryCache

#query_cache, #query_cache_enabled

Instance Method Summary collapse

Methods included from Savepoints

#create_savepoint, #release_savepoint, #rollback_to_savepoint

Methods inherited from AbstractAdapter

#active_threadsafe?, #case_insensitive_comparison, #case_sensitive_modifier, #close, #create_savepoint, #current_savepoint_name, #disable_extension, #disable_referential_integrity, #enable_extension, #expire, #extensions, #index_algorithms, #lease, #open_transactions, #prefetch_primary_key?, #raw_connection, #reconnect!, #release_savepoint, #reset!, #rollback_to_savepoint, #schema_creation, #substitute_at, #supports_bulk_alter?, #supports_count_distinct?, #supports_extensions?, #supports_transaction_isolation?, type_cast_config_to_boolean, type_cast_config_to_integer, #unprepared_statement, #unprepared_visitor, #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_table_name, #quoted_false, #quoted_true

Methods included from DatabaseStatements

#add_transaction_record, #begin_isolated_db_transaction, #begin_transaction, #commit_transaction, #current_transaction, #default_sequence_name, #delete, #empty_insert_statement_value, #exec_insert, #insert, #insert_fixture, #join_to_delete, #join_to_update, #limited_update_conditions, #reset_sequence!, #reset_transaction, #rollback_transaction, #sanitize_limit, #select_all, #select_one, #select_value, #select_values, #to_sql, #transaction, #transaction_isolation_levels, #transaction_open?, #update, #within_new_transaction

Methods included from SchemaStatements

#add_index, #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, #index_exists?, #index_name, #index_name_exists?, #initialize_schema_migrations_table, #remove_columns, #remove_index, #remove_reference, #remove_timestamps, #rename_index, #table_alias_for, #type_to_sql, #update_table_definition

Constructor Details

#initialize(connection, logger, config) ⇒ SQLite3Adapter

Returns a new instance of SQLite3Adapter.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 130

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

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

  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
    @prepared_statements = true
    @visitor = Arel::Visitors::SQLite.new self
  else
    @visitor = unprepared_visitor
  end
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


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

def active?
  @active != false
end

#adapter_nameObject

:nodoc:



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

def adapter_name #:nodoc:
  'SQLite'
end

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

:nodoc:



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

def add_column(table_name, column_name, type, options = {}) #:nodoc:
  if supports_add_column? && valid_alter_table_options( type, options )
    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



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

def allowed_index_name_length
  index_name_length - 2
end

#begin_db_transactionObject

:nodoc:



354
355
356
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 354

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

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

:nodoc:



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

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:



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

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



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

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.



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

def clear_cache!
  @statements.clear
end

#columns(table_name) ⇒ Object

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



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

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

    SQLite3Column.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0)
  end
end

#commit_db_transactionObject

:nodoc:



358
359
360
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 358

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

#delete_sql(sql, name = nil) ⇒ Object

:nodoc:



339
340
341
342
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 339

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.



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

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

#encodingObject

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



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

def encoding
  @connection.encoding.to_s
end

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



320
321
322
323
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 320

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

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



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

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)
      cols    = stmt.columns
      records = stmt.to_a
      stmt.close
      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:



330
331
332
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 330

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

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

DATABASE STATEMENTS ======================================



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

def explain(arel, binds = [])
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
  ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
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:



344
345
346
347
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 344

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



326
327
328
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 326

def last_inserted_id(result)
  @connection.last_insert_row_id
end

#native_database_typesObject

:nodoc:



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

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#primary_key(table_name) ⇒ Object

:nodoc:



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

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

#quote(value, column = nil) ⇒ Object

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



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

def quote(value, column = nil)
  if value.kind_of?(String) && column && column.type == :binary
    s = value.unpack("H*")[0]
    "x'#{s}'"
  else
    super
  end
end

#quote_column_name(name) ⇒ Object

:nodoc:



245
246
247
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 245

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

#quote_string(s) ⇒ Object

:nodoc:



237
238
239
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 237

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

#quote_table_name_for_assignment(table, attr) ⇒ Object



241
242
243
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 241

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.



251
252
253
254
255
256
257
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 251

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:



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

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:



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

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:



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

def rename_column(table_name, column_name, new_column_name) #:nodoc:
  unless columns(table_name).detect{|c| c.name == column_name.to_s }
    raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
  end
  alter_table(table_name, :rename => {column_name.to_s => 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')


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

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)


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

def requires_reloading?
  true
end

#rollback_db_transactionObject

:nodoc:



362
363
364
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 362

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

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



350
351
352
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 350

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

#supports_add_column?Boolean

Returns:

  • (Boolean)


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

def supports_add_column?
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


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

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


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

def supports_explain?
  true
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


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

def supports_index_sort_order?
  true
end

#supports_migrations?Boolean

Returns true, since this connection adapter supports migrations.

Returns:

  • (Boolean)


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

def supports_migrations? #:nodoc:
  true
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)


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

def supports_partial_index?
  sqlite_version >= '3.8.0'
end

#supports_primary_key?Boolean

:nodoc:

Returns:

  • (Boolean)


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

def supports_primary_key? #:nodoc:
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


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

def supports_savepoints?
  true
end

#supports_statement_cache?Boolean

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

Returns:

  • (Boolean)


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

def supports_statement_cache?
  true
end

#table_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)


381
382
383
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 381

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

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

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



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

def tables(name = nil, table_name = nil) #:nodoc:
  sql = <<-SQL
    SELECT name
    FROM sqlite_master
    WHERE type = 'table' 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

#type_cast(value, column) ⇒ Object

:nodoc:



259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 259

def type_cast(value, column) # :nodoc:
  return value.to_f if BigDecimal === value
  return super unless String === value
  return super unless column && value

  value = super
  if column.type == :string && value.encoding == Encoding::ASCII_8BIT
    logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
    value = value.encode Encoding::UTF_8
  end
  value
end

#update_sql(sql, name = nil) ⇒ Object

:nodoc:



334
335
336
337
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 334

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

#valid_alter_table_options(type, options) ⇒ Object

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



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

def valid_alter_table_options( type, options)
  type.to_sym != :primary_key
end