Module: ArJdbc::SQLite3

Included in:
ActiveRecord::ConnectionAdapters::SQLite3Adapter
Defined in:
lib/arjdbc/sqlite3/adapter.rb

Overview

All the code in this module is a copy of ConnectionAdapters::SQLite3Adapter from active_record 5. The constants at the front of this file are to allow the rest of the file to remain with no modifications from its original source. If you hack on this file try not to modify this module and instead try and put those overrides in SQL3Adapter below. We try and keep a copy of the Rails this adapter supports with the current goal of being able to diff changes easily over time and to also eventually remove this module from ARJDBC altogether.

Defined Under Namespace

Classes: StatementPool

Constant Summary collapse

ConnectionAdapters =

DIFFERENCE: Some common constant names to reduce differences in rest of this module from AR5 version

::ActiveRecord::ConnectionAdapters
IndexDefinition =
::ActiveRecord::ConnectionAdapters::IndexDefinition
ForeignKeyDefinition =
::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
Quoting =
::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
RecordNotUnique =
::ActiveRecord::RecordNotUnique
SchemaCreation =
ConnectionAdapters::SQLite3::SchemaCreation
SQLite3Adapter =
ConnectionAdapters::AbstractAdapter
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" },
    json:         { name: "json" },
}
DEFAULT_PRAGMAS =
{
  "foreign_keys"        => true,
  "journal_mode"        => :wal,
  "synchronous"         => :normal,
  "mmap_size"           => 134217728, # 128 megabytes
  "journal_size_limit"  => 67108864, # 64 megabytes
  "cache_size"          => 2000
}
FK_REGEX =
/.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
DEFERRABLE_REGEX =
/DEFERRABLE INITIALLY (\w+)/

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.database_exists?(config) ⇒ Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/arjdbc/sqlite3/adapter.rb', line 100

def self.database_exists?(config)
  @config[:database] == ":memory:" || File.exist?(@config[:database].to_s)
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


176
177
178
179
180
181
182
183
184
185
# File 'lib/arjdbc/sqlite3/adapter.rb', line 176

def active?
  if connected?
    @lock.synchronize do
      if @raw_connection&.active?
        verified!
        true
      end
    end
  end || false
end

#add_column(table_name, column_name, type, **options) ⇒ Object

:nodoc:



277
278
279
280
281
282
283
284
285
# File 'lib/arjdbc/sqlite3/adapter.rb', line 277

def add_column(table_name, column_name, type, **options) #:nodoc:
  if invalid_alter_table_type?(type, options)
    alter_table(table_name) do |definition|
      definition.column(column_name, type, **options)
    end
  else
    super
  end
end

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

:nodoc:



348
349
350
# File 'lib/arjdbc/sqlite3/adapter.rb', line 348

def add_reference(table_name, ref_name, **options) # :nodoc:
  super(table_name, ref_name, type: :integer, **options)
end

#add_timestamps(table_name, **options) ⇒ Object



335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/arjdbc/sqlite3/adapter.rb', line 335

def add_timestamps(table_name, **options)
  options[:null] = false if options[:null].nil?

  if !options.key?(:precision)
    options[:precision] = 6
  end

  alter_table(table_name) do |definition|
    definition.column :created_at, :datetime, **options
    definition.column :updated_at, :datetime, **options
  end
end

#build_insert_sql(insert) ⇒ Object

:nodoc:



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/arjdbc/sqlite3/adapter.rb', line 391

def build_insert_sql(insert) # :nodoc:
  sql = +"INSERT #{insert.into} #{insert.values_list}"

  if insert.skip_duplicates?
    sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
  elsif insert.update_duplicates?
    sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
    if insert.raw_update_sql?
      sql << insert.raw_update_sql
    else
      sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
      sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
    end
  end

  sql << " RETURNING #{insert.returning}" if insert.returning
  sql
end

#change_column(table_name, column_name, type, **options) ⇒ Object

:nodoc:



323
324
325
326
327
# File 'lib/arjdbc/sqlite3/adapter.rb', line 323

def change_column(table_name, column_name, type, **options) #:nodoc:
  alter_table(table_name) do |definition|
    definition.change_column(column_name, type, **options)
  end
end

#change_column_default(table_name, column_name, default_or_changes) ⇒ Object

:nodoc:



304
305
306
307
308
309
310
# File 'lib/arjdbc/sqlite3/adapter.rb', line 304

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:



312
313
314
315
316
317
318
319
320
321
# File 'lib/arjdbc/sqlite3/adapter.rb', line 312

def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
  validate_change_column_null_argument!(null)

  unless null || default.nil?
    internal_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

#check_all_foreign_keys_valid!Object

:nodoc:



240
241
242
243
244
245
246
247
248
# File 'lib/arjdbc/sqlite3/adapter.rb', line 240

def check_all_foreign_keys_valid! # :nodoc:
  sql = "PRAGMA foreign_key_check"
  result = execute(sql)

  unless result.blank?
    tables = result.map { |row| row["table"] }
    raise ActiveRecord::StatementInvalid.new("Foreign key violations found: #{tables.join(", ")}", sql: sql)
  end
end

#check_versionObject



422
423
424
425
426
# File 'lib/arjdbc/sqlite3/adapter.rb', line 422

def check_version
  if database_version < "3.8.0"
    raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
  end
end

#connected?Boolean

Returns:

  • (Boolean)


172
173
174
# File 'lib/arjdbc/sqlite3/adapter.rb', line 172

def connected?
  !(@raw_connection.nil? || @raw_connection.closed?)
end

#disable_referential_integrityObject

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



226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/arjdbc/sqlite3/adapter.rb', line 226

def disable_referential_integrity # :nodoc:
  old_foreign_keys = query_value("PRAGMA foreign_keys")
  old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")

  begin
    execute("PRAGMA defer_foreign_keys = ON")
    execute("PRAGMA foreign_keys = OFF")
    yield
  ensure
    execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
    execute("PRAGMA foreign_keys = #{old_foreign_keys}")
  end
end

#disconnect!Object

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



195
196
197
198
199
200
201
# File 'lib/arjdbc/sqlite3/adapter.rb', line 195

def disconnect!
  @lock.synchronize do
    super
    @raw_connection&.close rescue nil
    @raw_connection = nil
  end
end

#encodingObject

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



212
213
214
# File 'lib/arjdbc/sqlite3/adapter.rb', line 212

def encoding
  any_raw_connection.encoding.to_s
end

#foreign_keys(table_name) ⇒ Object



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/arjdbc/sqlite3/adapter.rb', line 355

def foreign_keys(table_name)
  # SQLite returns 1 row for each column of composite foreign keys.
  fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
  # Deferred or immediate foreign keys can only be seen in the CREATE TABLE sql
  fk_defs = table_structure_sql(table_name)
              .select do |column_string|
                column_string.start_with?("CONSTRAINT") &&
                column_string.include?("FOREIGN KEY")
              end
              .to_h do |fk_string|
                _, from, table, to = fk_string.match(FK_REGEX).to_a
                _, mode = fk_string.match(DEFERRABLE_REGEX).to_a
                deferred = mode&.downcase&.to_sym || false
                [[table, from, to], deferred]
              end

  grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
  grouped_fk.map do |group|
    row = group.first
    options = {
      on_delete: extract_foreign_key_action(row["on_delete"]),
      on_update: extract_foreign_key_action(row["on_update"]),
      deferrable: fk_defs[[row["table"], row["from"], row["to"]]]
    }

    if group.one?
      options[:column] = row["from"]
      options[:primary_key] = row["to"]
    else
      options[:column] = group.map { |row| row["from"] }
      options[:primary_key] = group.map { |row| row["to"] }
    end
    ForeignKeyDefinition.new(table_name, row["table"], options)
  end
end

#get_database_versionObject

:nodoc:



418
419
420
# File 'lib/arjdbc/sqlite3/adapter.rb', line 418

def get_database_version # :nodoc:
  SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
end

#native_database_typesObject

:nodoc:



207
208
209
# File 'lib/arjdbc/sqlite3/adapter.rb', line 207

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#new_column_from_field(table_name, field, definitions) ⇒ Object

DIFFERENCE: here to



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/arjdbc/sqlite3/adapter.rb', line 429

def new_column_from_field(table_name, field, definitions)
  default = field["dflt_value"]

   = (field["type"])
  default_value = extract_value_from_default(default)
  generated_type = extract_generated_type(field)

  if generated_type.present?
    default_function = default
  else
    default_function = extract_default_function(default_value, default)
  end

  rowid = is_column_the_rowid?(field, definitions)

  ActiveRecord::ConnectionAdapters::SQLite3Column.new(
    field["name"],
    default_value,
    ,
    field["notnull"].to_i == 0,
    default_function,
    collation: field["collation"],
    auto_increment: field["auto_increment"],
    rowid: rowid,
    generated_type: generated_type
  )
end

#primary_keys(table_name) ⇒ Object

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



252
253
254
255
# File 'lib/arjdbc/sqlite3/adapter.rb', line 252

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:



287
288
289
290
291
292
# File 'lib/arjdbc/sqlite3/adapter.rb', line 287

def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
  alter_table(table_name) do |definition|
    definition.remove_column column_name
    definition.foreign_keys.delete_if { |fk| fk.column == column_name.to_s }
  end
end

#remove_columns(table_name, *column_names, type: nil, **options) ⇒ Object

:nodoc:



294
295
296
297
298
299
300
301
302
# File 'lib/arjdbc/sqlite3/adapter.rb', line 294

def remove_columns(table_name, *column_names, type: nil, **options) # :nodoc:
  alter_table(table_name) do |definition|
    column_names.each do |column_name|
      definition.remove_column column_name
    end
    column_names = column_names.map(&:to_s)
    definition.foreign_keys.delete_if { |fk| column_names.include?(fk.column) }
  end
end

#remove_index(table_name, column_name = nil, **options) ⇒ Object

:nodoc:



257
258
259
260
261
262
263
# File 'lib/arjdbc/sqlite3/adapter.rb', line 257

def remove_index(table_name, column_name = nil, **options) # :nodoc:
  return if options[:if_exists] && !index_exists?(table_name, column_name, **options)

  index_name = index_name_for_remove(table_name, column_name, options)

  internal_exec_query "DROP INDEX #{quote_column_name(index_name)}"
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object

:nodoc:



329
330
331
332
333
# File 'lib/arjdbc/sqlite3/adapter.rb', line 329

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, **options) ⇒ Object

Renames a table.

Example: rename_table('octopuses', 'octopi')



269
270
271
272
273
274
275
# File 'lib/arjdbc/sqlite3/adapter.rb', line 269

def rename_table(table_name, new_name, **options)
  validate_table_length!(new_name) unless options[:_uses_legacy_table_name]      
  schema_cache.clear_data_source_cache!(table_name.to_s)
  schema_cache.clear_data_source_cache!(new_name.to_s)
  internal_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)


124
125
126
# File 'lib/arjdbc/sqlite3/adapter.rb', line 124

def requires_reloading?
  true
end

#return_value_after_insert?(column) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


187
188
189
# File 'lib/arjdbc/sqlite3/adapter.rb', line 187

def return_value_after_insert?(column) # :nodoc:
  column.auto_populated?
end

#shared_cache?Boolean

:nodoc:

Returns:

  • (Boolean)


410
411
412
# File 'lib/arjdbc/sqlite3/adapter.rb', line 410

def shared_cache? # :nodoc:
  @config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
end

#supports_check_constraints?Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/arjdbc/sqlite3/adapter.rb', line 132

def supports_check_constraints?
  true
end

#supports_common_table_expressions?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/arjdbc/sqlite3/adapter.rb', line 148

def supports_common_table_expressions?
  database_version >= "3.8.3"
end

#supports_concurrent_connections?Boolean

DIFFERENCE: active?, reconnect!, disconnect! handles by arjdbc core

Returns:

  • (Boolean)


164
165
166
# File 'lib/arjdbc/sqlite3/adapter.rb', line 164

def supports_concurrent_connections?
  !@memory_database
end

#supports_datetime_with_precision?Boolean

Returns:

  • (Boolean)


140
141
142
# File 'lib/arjdbc/sqlite3/adapter.rb', line 140

def supports_datetime_with_precision?
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/arjdbc/sqlite3/adapter.rb', line 104

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


216
217
218
# File 'lib/arjdbc/sqlite3/adapter.rb', line 216

def supports_explain?
  true
end

#supports_expression_index?Boolean

Returns:

  • (Boolean)


120
121
122
# File 'lib/arjdbc/sqlite3/adapter.rb', line 120

def supports_expression_index?
  database_version >= "3.9.0"
end

#supports_foreign_keys?Boolean

Returns:

  • (Boolean)


128
129
130
# File 'lib/arjdbc/sqlite3/adapter.rb', line 128

def supports_foreign_keys?
  true
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


203
204
205
# File 'lib/arjdbc/sqlite3/adapter.rb', line 203

def supports_index_sort_order?
  true
end

#supports_insert_on_conflict?Boolean Also known as: supports_insert_on_duplicate_skip?, supports_insert_on_duplicate_update?, supports_insert_conflict_target?

Returns:

  • (Boolean)


156
157
158
# File 'lib/arjdbc/sqlite3/adapter.rb', line 156

def supports_insert_on_conflict?
  database_version >= "3.24.0"
end

#supports_insert_returning?Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/arjdbc/sqlite3/adapter.rb', line 152

def supports_insert_returning?
  database_version >= "3.35.0"
end

#supports_json?Boolean

Returns:

  • (Boolean)


144
145
146
# File 'lib/arjdbc/sqlite3/adapter.rb', line 144

def supports_json?
  true
end

#supports_lazy_transactions?Boolean

Returns:

  • (Boolean)


220
221
222
# File 'lib/arjdbc/sqlite3/adapter.rb', line 220

def supports_lazy_transactions?
  true
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/arjdbc/sqlite3/adapter.rb', line 116

def supports_partial_index?
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


108
109
110
# File 'lib/arjdbc/sqlite3/adapter.rb', line 108

def supports_savepoints?
  true
end

#supports_transaction_isolation?Boolean

Returns:

  • (Boolean)


112
113
114
# File 'lib/arjdbc/sqlite3/adapter.rb', line 112

def supports_transaction_isolation?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/arjdbc/sqlite3/adapter.rb', line 136

def supports_views?
  true
end

#supports_virtual_columns?Boolean

Returns:

  • (Boolean)


168
169
170
# File 'lib/arjdbc/sqlite3/adapter.rb', line 168

def supports_virtual_columns?
  database_version >= "3.31.0"
end

#use_insert_returning?Boolean

Returns:

  • (Boolean)


414
415
416
# File 'lib/arjdbc/sqlite3/adapter.rb', line 414

def use_insert_returning?
  @use_insert_returning
end