Class: ActiveRecord::ConnectionAdapters::SQLite3Adapter

Inherits:
AbstractAdapter show all
Includes:
ActiveRecord::ConnectionAdapters::SQLite3::ColumnDumper, ActiveRecord::ConnectionAdapters::SQLite3::Quoting, ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements
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: 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

#lock, #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 ActiveRecord::ConnectionAdapters::SQLite3::Quoting

#quote_column_name, #quote_string, #quote_table_name_for_assignment, #quoted_binary, #quoted_time

Methods inherited from AbstractAdapter

#adapter_name, #case_insensitive_comparison, #case_sensitive_comparison, #close, #collector, #column_name_for_operation, #columns, #combine_bind_parameters, #default_index_type?, #disable_extension, #enable_extension, #expire, #extensions, #get_advisory_lock, #index_algorithms, #lease, #lookup_cast_type, #new_column, #prefetch_primary_key?, #raw_connection, #reconnect!, #release_advisory_lock, #reset!, #steal!, #supports_advisory_locks?, #supports_bulk_alter?, #supports_comments?, #supports_comments_in_create?, #supports_expression_index?, #supports_extensions?, #supports_foreign_keys?, #supports_json?, #supports_migrations?, #supports_primary_key?, #supports_transaction_isolation?, #supports_virtual_columns?, type_cast_config_to_boolean, type_cast_config_to_integer, #type_map, #unprepared_statement, #valid_type?, #verify!

Methods included from Savepoints

#create_savepoint, #current_savepoint_name, #exec_rollback_to_savepoint, #release_savepoint

Methods included from ColumnDumper

#column_spec, #column_spec_for_primary_key, #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

#fetch_type_metadata, #lookup_cast_type_from_column, #quote, #quote_column_name, #quote_default_expression, #quote_string, #quote_table_name, #quote_table_name_for_assignment, #quoted_binary, #quoted_date, #quoted_false, #quoted_time, #quoted_true, #type_cast, #type_cast_from_column, #type_casted_binds, #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_update, #reset_sequence!, #reset_transaction, #rollback_db_transaction, #rollback_to_savepoint, #sanitize_limit, #select_all, #select_one, #select_rows, #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_timestamps, #assume_migrated_upto_version, #change_column_comment, #change_table, #change_table_comment, #column_exists?, #columns, #columns_for_distinct, #create_join_table, #create_table, #data_source_exists?, #data_sources, #drop_join_table, #drop_table, #dump_schema_information, #foreign_key_column_for, #foreign_key_exists?, #foreign_key_for, #foreign_key_for!, #foreign_key_options, #index_exists?, #index_name, #index_name_exists?, #initialize_internal_metadata_table, #initialize_schema_migrations_table, #insert_versions_sql, #internal_string_options_for_primary_key, #options_include_default?, #primary_key, #remove_columns, #remove_foreign_key, #remove_reference, #remove_timestamps, #rename_index, #table_alias_for, #table_comment, #table_exists?, #table_options, #tables, #type_to_sql, #view_exists?, #views

Constructor Details

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

Returns a new instance of SQLite3Adapter.


95
96
97
98
99
100
101
102
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 95

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

  @active     = nil
  @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))

  configure_connection
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)

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

def active?
  @active != false
end

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

:nodoc:


342
343
344
345
346
347
348
349
350
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 342

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

#add_reference(table_name, ref_name, **options) ⇒ Object Also known as: add_belongs_to

:nodoc:


395
396
397
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 395

def add_reference(table_name, ref_name, **options) # :nodoc:
  super(table_name, ref_name, type: :integer, **options)
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


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

def allowed_index_name_length
  index_name_length - 2
end

#arel_visitorObject

:nodoc:


91
92
93
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 91

def arel_visitor # :nodoc:
  Arel::Visitors::SQLite.new(self)
end

#begin_db_transactionObject

:nodoc:


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

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

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

:nodoc:


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

def change_column(table_name, column_name, type, options = {}) #:nodoc:
  alter_table(table_name) do |definition|
    definition[column_name].instance_eval do
      self.type    = type
      self.limit   = options[:limit] if options.include?(:limit)
      self.default = options[:default] if options.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)
      self.collation = options[:collation] if options.include?(:collation)
    end
  end
end

#change_column_default(table_name, column_name, default_or_changes) ⇒ Object

:nodoc:


358
359
360
361
362
363
364
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 358

def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
  default = extract_new_default_value(default_or_changes)

  alter_table(table_name) do |definition|
    definition[column_name].default = default
  end
end

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

:nodoc:


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

def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
  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.


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

def clear_cache!
  @statements.clear
end

#commit_db_transactionObject

:nodoc:


260
261
262
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 260

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

#disable_referential_integrityObject

REFERENTIAL INTEGRITY ====================================


185
186
187
188
189
190
191
192
193
194
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 185

def disable_referential_integrity # :nodoc:
  old = select_value("PRAGMA foreign_keys")

  begin
    execute("PRAGMA foreign_keys = OFF")
    yield
  ensure
    execute("PRAGMA foreign_keys = #{old}")
  end
end

#disconnect!Object

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


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

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

#encodingObject

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


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

def encoding
  @connection.encoding.to_s
end

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


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

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

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


205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 205

def exec_query(sql, name = nil, binds = [], prepare: false)
  type_casted_binds = type_casted_binds(binds)

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

      ActiveRecord::Result.new(cols, records)
    end
  end
end

#exec_rollback_db_transactionObject

:nodoc:


264
265
266
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 264

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

#execute(sql, name = nil) ⇒ Object

:nodoc:


248
249
250
251
252
253
254
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 248

def execute(sql, name = nil) #:nodoc:
  log(sql, name) do
    ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
      @connection.execute(sql)
    end
  end
end

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

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


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

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

#foreign_keys(table_name) ⇒ Object


400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 400

def foreign_keys(table_name)
  fk_info = select_all("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
  fk_info.map do |row|
    options = {
      column: row["from"],
      primary_key: row["to"],
      on_delete: extract_foreign_key_action(row["on_delete"]),
      on_update: extract_foreign_key_action(row["on_update"])
    }
    ForeignKeyDefinition.new(table_name, row["table"], options)
  end
end

#indexes(table_name, name = nil) ⇒ Object

Returns an array of indexes for the given table.


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

def indexes(table_name, name = nil) #:nodoc:
  if name
    ActiveSupport::Deprecation.warn(<<-MSG.squish)
      Passing name to #indexes is deprecated without replacement.
    MSG
  end

  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

#last_inserted_id(result) ⇒ Object


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

def last_inserted_id(result)
  @connection.last_insert_row_id
end

#native_database_typesObject

:nodoc:


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

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#new_column_from_field(table_name, field) ⇒ Object

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


270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 270

def new_column_from_field(table_name, field) # :nondoc:
  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

  collation = field["collation"]
  sql_type = field["type"]
   = (sql_type)
  new_column(field["name"], field["dflt_value"], , field["notnull"].to_i == 0, table_name, nil, collation)
end

#primary_keys(table_name) ⇒ Object

:nodoc:


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

def primary_keys(table_name) # :nodoc:
  pks = table_structure(table_name).select { |f| f["pk"] > 0 }
  pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
end

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

:nodoc:


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

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, options = {}) ⇒ Object

:nodoc:


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

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

#rename_column(table_name, column_name, new_column_name) ⇒ Object

:nodoc:


389
390
391
392
393
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 389

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

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

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)

122
123
124
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 122

def requires_reloading?
  true
end

#schema_creationObject

:nodoc:


87
88
89
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 87

def schema_creation # :nodoc:
  SQLite3::SchemaCreation.new self
end

#supports_datetime_with_precision?Boolean

Returns:

  • (Boolean)

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

def supports_datetime_with_precision?
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)

104
105
106
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 104

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)

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

def supports_explain?
  true
end

#supports_foreign_keys_in_create?Boolean

Returns:

  • (Boolean)

126
127
128
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 126

def supports_foreign_keys_in_create?
  sqlite_version >= "3.6.19"
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)

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

def supports_index_sort_order?
  true
end

#supports_multi_insert?Boolean

Returns:

  • (Boolean)

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

def supports_multi_insert?
  sqlite_version >= "3.7.11"
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)

112
113
114
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 112

def supports_partial_index?
  sqlite_version >= "3.8.0"
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)

108
109
110
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 108

def supports_savepoints?
  true
end

#supports_statement_cache?Boolean

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

Returns:

  • (Boolean)

118
119
120
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 118

def supports_statement_cache?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)

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

def supports_views?
  true
end

#update_table_definition(table_name, base) ⇒ Object

:nodoc:


83
84
85
# File 'lib/active_record/connection_adapters/sqlite3_adapter.rb', line 83

def update_table_definition(table_name, base) # :nodoc:
  SQLite3::Table.new(table_name, base)
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)

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

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