Class: ActiveRecord::ConnectionAdapters::SQLite3Adapter

Inherits:
AbstractAdapter show all
Includes:
ActiveRecord::ConnectionAdapters::SQLite3::Quoting
Defined in:
activerecord/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

Constants included from ActiveSupport::Callbacks

ActiveSupport::Callbacks::CALLBACK_FILTER_TYPES

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

#quote_column_name, #quote_string, #quote_table_name_for_assignment, #quoted_time

Methods inherited from AbstractAdapter

#adapter_name, #case_insensitive_comparison, #case_sensitive_comparison, #close, #collector, #column_name_for_operation, #combine_bind_parameters, #disable_extension, #disable_referential_integrity, #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_json?, #supports_transaction_isolation?, type_cast_config_to_boolean, type_cast_config_to_integer, #type_map, #unprepared_statement, #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, #prepare_binds_for_database, #quote, #quote_column_name, #quote_default_expression, #quote_string, #quote_table_name, #quote_table_name_for_assignment, #quoted_date, #quoted_false, #quoted_time, #quoted_true, #type_cast, #type_cast_from_column, #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_reference, #add_timestamps, #assume_migrated_upto_version, #change_column_comment, #change_table, #change_table_comment, #column_exists?, #columns_for_distinct, #create_join_table, #create_table, #drop_join_table, #drop_table, #dump_schema_information, #foreign_key_column_for, #foreign_key_exists?, #foreign_key_for, #foreign_key_for!, #foreign_key_options, #foreign_keys, #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_options, #type_to_sql, #update_table_definition

Methods included from ActiveSupport::Callbacks

#run_callbacks

Methods included from ActiveSupport::Concern

#append_features, #class_methods, extended, #included

Constructor Details

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

Returns a new instance of SQLite3Adapter.


86
87
88
89
90
91
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 86

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]))
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)

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

def active?
  @active != false
end

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

:nodoc:


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

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


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

def allowed_index_name_length
  index_name_length - 2
end

#arel_visitorObject

:nodoc:


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

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

#begin_db_transactionObject

:nodoc:


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

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

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

:nodoc:


404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 404

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)
      self.collation = options[:collation] if options.include?(:collation)
    end
  end
end

#change_column_default(table_name, column_name, default_or_changes) ⇒ Object

:nodoc:


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

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:


395
396
397
398
399
400
401
402
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 395

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.


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

def clear_cache!
  @statements.clear
end

#columns(table_name) ⇒ Object

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


302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 302

def columns(table_name) # :nodoc:
  table_name = table_name.to_s
  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

    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
end

#commit_db_transactionObject

:nodoc:


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

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

#data_source_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)

279
280
281
282
283
284
285
286
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 279

def data_source_exists?(table_name)
  return false unless table_name.present?

  sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
  sql << " AND name = #{quote(table_name)}"

  select_values(sql, 'SCHEMA').any?
end

#data_sourcesObject


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

def data_sources
  select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", 'SCHEMA')
end

#disconnect!Object

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


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

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

#encodingObject

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


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

def encoding
  @connection.encoding.to_s
end

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


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

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

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


190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 190

def exec_query(sql, name = nil, binds = [], prepare: false)
  type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }

  log(sql, name, binds) 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
      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)
    end

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

#exec_rollback_db_transactionObject

:nodoc:


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

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

#execute(sql, name = nil) ⇒ Object

:nodoc:


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

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

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

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


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

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

#indexes(table_name, name = nil) ⇒ Object

Returns an array of indexes for the given table.


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 322

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

#last_inserted_id(result) ⇒ Object


227
228
229
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 227

def last_inserted_id(result)
  @connection.last_insert_row_id
end

#native_database_typesObject

:nodoc:


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

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#primary_keys(table_name) ⇒ Object

:nodoc:


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

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:


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

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:


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

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:


419
420
421
422
423
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 419

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

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

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)

120
121
122
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 120

def requires_reloading?
  true
end

#schema_creationObject

:nodoc:


78
79
80
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 78

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

#supports_datetime_with_precision?Boolean

Returns:

  • (Boolean)

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

def supports_datetime_with_precision?
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)

93
94
95
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 93

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)

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

def supports_explain?
  true
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)

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

def supports_index_sort_order?
  true
end

#supports_migrations?Boolean

Returns true, since this connection adapter supports migrations.

Returns:

  • (Boolean)

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

def supports_migrations? #:nodoc:
  true
end

#supports_multi_insert?Boolean

Returns:

  • (Boolean)

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

def supports_multi_insert?
  sqlite_version >= '3.7.11'
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)

101
102
103
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 101

def supports_partial_index?
  sqlite_version >= '3.8.0'
end

#supports_primary_key?Boolean

:nodoc:

Returns:

  • (Boolean)

116
117
118
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 116

def supports_primary_key? #:nodoc:
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)

97
98
99
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 97

def supports_savepoints?
  true
end

#supports_statement_cache?Boolean

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

Returns:

  • (Boolean)

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

def supports_statement_cache?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)

124
125
126
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 124

def supports_views?
  true
end

#table_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)

269
270
271
272
273
274
275
276
277
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 269

def table_exists?(table_name)
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
    #table_exists? currently checks both tables and views.
    This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
    Use #data_source_exists? instead.
  MSG

  data_source_exists?(table_name)
end

#tables(name = nil) ⇒ Object

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


249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 249

def tables(name = nil) # :nodoc:
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
    #tables currently returns both tables and views.
    This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
    Use #data_sources instead.
  MSG

  if name
    ActiveSupport::Deprecation.warn(<<-MSG.squish)
      Passing arguments to #tables is deprecated without replacement.
    MSG
  end

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

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

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

#valid_type?(type) ⇒ Boolean

Returns:

  • (Boolean)

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

def valid_type?(type)
  true
end

#view_exists?(view_name) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)

292
293
294
295
296
297
298
299
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 292

def view_exists?(view_name) # :nodoc:
  return false unless view_name.present?

  sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
  sql << " AND name = #{quote(view_name)}"

  select_values(sql, 'SCHEMA').any?
end

#viewsObject

:nodoc:


288
289
290
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 288

def views # :nodoc:
  select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
end