Module: ArJdbc::SQLite3

Includes:
ActiveRecord::ConnectionAdapters::SQLite3::ColumnDumper, ActiveRecord::ConnectionAdapters::SQLite3::Quoting, ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements
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
Quoting =
::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
RecordNotUnique =
::ActiveRecord::RecordNotUnique
SchemaCreation =
ConnectionAdapters::SQLite3::SchemaCreation
SQLite3Adapter =
ConnectionAdapters::AbstractAdapter
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" }
}

Instance Method Summary collapse

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


122
123
124
# File 'lib/arjdbc/sqlite3/adapter.rb', line 122

def active?
  @active != false
end

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

:nodoc:



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

def add_column(table_name, column_name, type, options = {}) #:nodoc:
  if valid_alter_table_type?(type) && !options[:primary_key]
    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:



376
377
378
# File 'lib/arjdbc/sqlite3/adapter.rb', line 376

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



146
147
148
# File 'lib/arjdbc/sqlite3/adapter.rb', line 146

def allowed_index_name_length
  index_name_length - 2
end

#arel_visitorObject

:nodoc:



71
72
73
# File 'lib/arjdbc/sqlite3/adapter.rb', line 71

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

#begin_db_transactionObject

:nodoc:



237
238
239
# File 'lib/arjdbc/sqlite3/adapter.rb', line 237

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

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

:nodoc:



356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/arjdbc/sqlite3/adapter.rb', line 356

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:



339
340
341
342
343
344
345
# File 'lib/arjdbc/sqlite3/adapter.rb', line 339

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:



347
348
349
350
351
352
353
354
# File 'lib/arjdbc/sqlite3/adapter.rb', line 347

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.



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

def clear_cache!
  @statements.clear
end

#commit_db_transactionObject

:nodoc:



241
242
243
# File 'lib/arjdbc/sqlite3/adapter.rb', line 241

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

#disable_referential_integrityObject

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



165
166
167
168
169
170
171
172
173
174
# File 'lib/arjdbc/sqlite3/adapter.rb', line 165

def disable_referential_integrity # :nodoc:
  old = query_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.



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

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

#encodingObject

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



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

def encoding
  @connection.encoding.to_s
end

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



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

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

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



186
187
188
189
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
# File 'lib/arjdbc/sqlite3/adapter.rb', line 186

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:



245
246
247
# File 'lib/arjdbc/sqlite3/adapter.rb', line 245

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

#execute(sql, name = nil) ⇒ Object

:nodoc:



229
230
231
232
233
234
235
# File 'lib/arjdbc/sqlite3/adapter.rb', line 229

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



180
181
182
183
184
# File 'lib/arjdbc/sqlite3/adapter.rb', line 180

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

#foreign_keys(table_name) ⇒ Object



381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/arjdbc/sqlite3/adapter.rb', line 381

def foreign_keys(table_name)
  fk_info = exec_query("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"])
    }
    # DIFFERENCE: FQN
    ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row["table"], options)
  end
end

#indexes(table_name, name = nil) ⇒ Object

Returns an array of indexes for the given table.



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/arjdbc/sqlite3/adapter.rb', line 268

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

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



75
76
77
78
79
80
81
82
# File 'lib/arjdbc/sqlite3/adapter.rb', line 75

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

#last_inserted_id(result) ⇒ Object



225
226
227
# File 'lib/arjdbc/sqlite3/adapter.rb', line 225

def last_inserted_id(result)
  @connection.last_insert_row_id
end

#native_database_typesObject

:nodoc:



150
151
152
# File 'lib/arjdbc/sqlite3/adapter.rb', line 150

def native_database_types #:nodoc:
  NATIVE_DATABASE_TYPES
end

#new_column_from_field(table_name, field) ⇒ Object

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



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/arjdbc/sqlite3/adapter.rb', line 251

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:



298
299
300
301
# File 'lib/arjdbc/sqlite3/adapter.rb', line 298

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:



333
334
335
336
337
# File 'lib/arjdbc/sqlite3/adapter.rb', line 333

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:



303
304
305
306
# File 'lib/arjdbc/sqlite3/adapter.rb', line 303

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:



370
371
372
373
374
# File 'lib/arjdbc/sqlite3/adapter.rb', line 370

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



312
313
314
315
# File 'lib/arjdbc/sqlite3/adapter.rb', line 312

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)


102
103
104
# File 'lib/arjdbc/sqlite3/adapter.rb', line 102

def requires_reloading?
  true
end

#schema_creationObject

:nodoc:



66
67
68
69
# File 'lib/arjdbc/sqlite3/adapter.rb', line 66

def schema_creation # :nodoc:
  # DIFFERENCE: FQN
  ::ActiveRecord::ConnectionAdapters::SQLite3::SchemaCreation.new self
end

#supports_datetime_with_precision?Boolean

Returns:

  • (Boolean)


114
115
116
# File 'lib/arjdbc/sqlite3/adapter.rb', line 114

def supports_datetime_with_precision?
  true
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/arjdbc/sqlite3/adapter.rb', line 84

def supports_ddl_transactions?
  true
end

#supports_explain?Boolean

Returns:

  • (Boolean)


159
160
161
# File 'lib/arjdbc/sqlite3/adapter.rb', line 159

def supports_explain?
  true
end

#supports_foreign_keys_in_create?Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/arjdbc/sqlite3/adapter.rb', line 106

def supports_foreign_keys_in_create?
  sqlite_version >= "3.6.19"
end

#supports_index_sort_order?Boolean

Returns:

  • (Boolean)


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

def supports_index_sort_order?
  true
end

#supports_multi_insert?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/arjdbc/sqlite3/adapter.rb', line 118

def supports_multi_insert?
  sqlite_version >= "3.7.11"
end

#supports_partial_index?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/arjdbc/sqlite3/adapter.rb', line 92

def supports_partial_index?
  sqlite_version >= "3.8.0"
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/arjdbc/sqlite3/adapter.rb', line 88

def supports_savepoints?
  true
end

#supports_statement_cache?Boolean

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

Returns:

  • (Boolean)


98
99
100
# File 'lib/arjdbc/sqlite3/adapter.rb', line 98

def supports_statement_cache?
  true
end

#supports_views?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/arjdbc/sqlite3/adapter.rb', line 110

def supports_views?
  true
end

#update_table_definition(table_name, base) ⇒ Object

:nodoc:



61
62
63
64
# File 'lib/arjdbc/sqlite3/adapter.rb', line 61

def update_table_definition(table_name, base) # :nodoc:
  # DIFFERENCE: FQN
  ::ActiveRecord::ConnectionAdapters::SQLite3::Table.new(table_name, base)
end

#valid_alter_table_type?(type) ⇒ Boolean

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

Returns:

  • (Boolean)


319
320
321
# File 'lib/arjdbc/sqlite3/adapter.rb', line 319

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