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

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.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 112

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



207
208
209
210
211
212
213
214
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 207

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

#_type_cast(value) ⇒ Object

:nodoc:



216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 216

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)


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

def active?
  @active != false
end

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

:nodoc:



441
442
443
444
445
446
447
448
449
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 441

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



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

def allowed_index_name_length
  index_name_length - 2
end

#begin_db_transactionObject

:nodoc:



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

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

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

:nodoc:



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

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:



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

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



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

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.



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

def clear_cache!
  @statements.clear
end

#columns(table_name) ⇒ Object

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



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 374

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:



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

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

#delete_sql(sql, name = nil) ⇒ Object

:nodoc:



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

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.



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

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

#encodingObject

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



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

def encoding
  @connection.encoding.to_s
end

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



306
307
308
309
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 306

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

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



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 276

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:



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

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

#execute(sql, name = nil) ⇒ Object

:nodoc:



316
317
318
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 316

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

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

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



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

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.



392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 392

def indexes(table_name, name = nil) #:nodoc:
  exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
    sql = "SELECT sql\nFROM sqlite_master\nWHERE name=\#{quote(row['name'])} AND type='index'\nUNION ALL\nSELECT sql\nFROM sqlite_temp_master\nWHERE name=\#{quote(row['name'])} AND type='index'\n"
    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:



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

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



312
313
314
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 312

def last_inserted_id(result)
  @connection.last_insert_row_id
end

#native_database_typesObject

:nodoc:



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

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#primary_key(table_name) ⇒ Object

:nodoc:



416
417
418
419
420
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 416

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:



239
240
241
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 239

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

#quote_string(s) ⇒ Object

:nodoc:



231
232
233
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 231

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

#quote_table_name_for_assignment(table, attr) ⇒ Object



235
236
237
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 235

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.



245
246
247
248
249
250
251
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 245

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:



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

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:



422
423
424
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 422

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:



486
487
488
489
490
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 486

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


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

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)


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

def requires_reloading?
  true
end

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



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

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

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


129
130
131
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 129

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


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

def supports_explain?
  true
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


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

def supports_index_sort_order?
  true
end

#supports_migrations?Boolean

Returns true, since this connection adapter supports migrations.

Returns:

  • (Boolean)


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

def supports_migrations? #:nodoc:
  true
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)


137
138
139
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 137

def supports_partial_index?
  sqlite_version >= '3.8.0'
end

#supports_primary_key?Boolean

:nodoc:

Returns:

  • (Boolean)


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

def supports_primary_key? #:nodoc:
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 133

def supports_savepoints?
  true
end

#supports_statement_cache?Boolean

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

Returns:

  • (Boolean)


143
144
145
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 143

def supports_statement_cache?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)


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

def supports_views?
  true
end

#table_exists?(table_name) ⇒ Boolean Also known as: data_source_exists?

Returns:

  • (Boolean)


368
369
370
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 368

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

#tables(name = nil, table_name = nil) ⇒ Object Also known as: data_sources

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



354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 354

def tables(name = nil, table_name = nil) #:nodoc:
  sql = "SELECT name\nFROM sqlite_master\nWHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'\n"
  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:



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

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)


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

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