123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::ConnectionAdapters::SQLite3Adapter

Overview

Active Record SQLite3 Adapter

The SQLite3 adapter works with the sqlite3-ruby drivers (available as gem from rubygems.org/gems/sqlite3).

Options:

  • :database - Path to the database file.

Constant Summary

::ActiveSupport::Callbacks - Included

CALLBACK_FILTER_TYPES

DatabaseStatements - Included

DEFAULT_INSERT_VALUE, HIGH_PRECISION_CURRENT_TIMESTAMP

AbstractAdapter - Inherited

ADAPTER_NAME, COMMENT_REGEX, DEFAULT_READ_QUERY, EXCEPTION_IMMEDIATE, EXCEPTION_NEVER, EXTENDED_TYPE_MAPS, SIMPLE_INT, TYPE_MAP

SQLite3::Quoting - Included

QUOTED_COLUMN_NAMES, QUOTED_TABLE_NAMES

SQLite3::SchemaStatements - Included

INTEGER_REGEX

SQLite3::DatabaseStatements - Included

HIGH_PRECISION_CURRENT_TIMESTAMP, READ_QUERY

Class Attribute Summary

Class Method Summary

Instance Attribute Summary

AbstractAdapter - Inherited

#__callbacks, #__callbacks?,
#active?

Checks whether the connection to the database is still active.

#connected?

Checks whether the connection to the database was established.

#database_exists?,
#in_use?
#lock, #logger, #owner, #pool, #pool=,
#prepared_statements
#preventing_writes?

Determines whether writes are currently being prevented.

#replica?,
#requires_reloading?

Returns true if its required to reload the connection between requests for development mode.

#savepoint_errors_invalidate_transactions?

Do TransactionRollbackErrors on savepoints affect the parent transaction?

#supports_advisory_locks?

Does this adapter support application-enforced advisory locking?

#supports_bulk_alter?,
#supports_check_constraints?

Does this adapter support creating check constraints?

#supports_comments?

Does this adapter support metadata comments on database objects (tables, columns, indexes)?

#supports_comments_in_create?

Can comments for tables, columns, and indexes be specified in create/alter table statements?

#supports_common_table_expressions?, #supports_concurrent_connections?,
#supports_datetime_with_precision?

Does this adapter support datetime with precision?

#supports_ddl_transactions?

Does this adapter support DDL rollbacks in transactions? That is, would CREATE TABLE or ALTER TABLE get rolled back by a transaction?

#supports_deferrable_constraints?

Does this adapter support creating deferrable constraints?

#supports_exclusion_constraints?

Does this adapter support creating exclusion constraints?

#supports_explain?

Does this adapter support explain?

#supports_expression_index?

Does this adapter support expression indices?

#supports_extensions?

Does this adapter support database extensions?

#supports_foreign_keys?

Does this adapter support creating foreign key constraints?

#supports_foreign_tables?

Does this adapter support foreign/external tables?

#supports_index_include?

Does this adapter support including non-key columns?

#supports_index_sort_order?

Does this adapter support index sort order?

#supports_indexes_in_create?

Does this adapter support creating indexes in the same statement as creating the table?

#supports_insert_conflict_target?, #supports_insert_on_duplicate_skip?, #supports_insert_on_duplicate_update?, #supports_insert_returning?,
#supports_json?

Does this adapter support JSON data type?

#supports_lazy_transactions?,
#supports_materialized_views?

Does this adapter support materialized views?

#supports_nulls_not_distinct?,
#supports_optimizer_hints?

Does this adapter support optimizer hints?

#supports_partial_index?

Does this adapter support partial indices?

#supports_partitioned_indexes?, #supports_restart_db_transaction?,
#supports_savepoints?

Does this adapter support savepoints?

#supports_transaction_isolation?

Does this adapter support setting the isolation level for a transaction?

#supports_unique_constraints?

Does this adapter support creating unique constraints?

#supports_validate_constraints?

Does this adapter support creating invalid constraints?

#supports_views?

Does this adapter support views?

#supports_virtual_columns?

Does this adapter support virtual columns?

#visitor, #reconnect_can_restore_state?, #advisory_locks_enabled?, #async_enabled?, #lock_thread=

DatabaseStatements - Included

SchemaStatements - Included

Instance Method Summary

SQLite3::DatabaseStatements - Included

SQLite3::SchemaStatements - Included

#add_check_constraint, #add_foreign_key, #check_constraints, #create_schema_dumper,
#indexes

Returns an array of indexes for the given table.

#remove_check_constraint, #remove_foreign_key,
#schema_creation

:nodoc.

#assert_valid_deferrable, #create_table_definition, #data_source_sql, #extract_generated_type,
#is_column_the_rowid?

if a rowid table has a primary key that consists of a single column and the declared type of that column is “INTEGER” in any mixture of upper and lower case, then the column becomes an alias for the rowid.

#new_column_from_field, #quoted_scope, #valid_table_definition_options, #validate_index_length!

SQLite3::Quoting - Included

AbstractAdapter - Inherited

#adapter_name

Returns the human-readable name of the adapter.

#check_all_foreign_keys_valid!

Override to check all foreign key constraints in a database.

#clear_cache!

Clear any caching the database adapter may be doing.

#close

Check the connection back in to the connection pool.

#connect!, #connection_retries, #default_timezone,
#disable_extension

This is meant to be implemented by the adapters that support extensions.

#disable_referential_integrity

Override to turn off referential integrity while executing &block.

#discard!

Immediately forget this connection ever existed.

#disconnect!

Disconnects from the database if already connected.

#enable_extension

This is meant to be implemented by the adapters that support extensions.

#expire

this method must only be called while holding connection pool’s mutex.

#extensions

A list of extensions, to be filled in by adapters that support them.

#index_algorithms

A list of index algorithms, to be filled by adapters that support them.

#lease

this method must only be called while holding connection pool’s mutex.

#prefetch_primary_key?

Should primary key values be selected from their corresponding sequence before the insert statement? If true, next_sequence_value is called before each insert to set the record’s primary key.

#prepared_statements?,
#raw_connection

Provides access to the underlying database driver for this adapter.

#reconnect!

Disconnects from the database if already connected, and establishes a new connection with the database.

#reset!

Reset the state of this connection, directing the DBMS to clear transactions and other connection-related server-side state.

#retry_deadline,
#role

The role (e.g. :writing) for the current connection.

#schema_cache,
#schema_version

Returns the version identifier of the schema currently available in the database.

#shard

The shard (e.g. :default) for the current connection.

#throw_away!

Removes the connection from the pool and disconnect it.

#unprepared_statement,
#verify!

Checks whether the connection to the database is still active (i.e. not stale).

#any_raw_connection

Returns a raw connection for internal use with methods that are known to both be thread-safe and not rely upon actual server communication.

#arel_visitor, #backoff,
#build_result

Builds the result object.

#build_statement_pool, #can_perform_case_insensitive_comparison_for?, #collector, #column_for, #column_for_attribute,
#configure_connection

Perform any necessary initialization upon the newly-established connection settings, run queries to configure any application-global “session” variables, etc.

#default_prepared_statements, #extended_type_map_key, #invalidate_transaction, #log, #reconnect, #retryable_connection_error?, #retryable_query_error?, #transform_query, #translate_exception, #translate_exception_class, #type_map,
#valid_raw_connection

Similar to any_raw_connection, but ensures it is validated and connected.

#verified!

Mark the connection as verified.

#warning_ignored?,
#with_raw_connection

Lock the monitor, ensure we’re properly connected and transactions are materialized, and then yield the underlying raw connection object.

#without_prepared_statement?,
#add_enum_value

This is meant to be implemented by the adapters that support custom enum types.

#build_insert_sql

Called by ::ActiveRecord::InsertAll, Passed an instance of ::ActiveRecord::InsertAll::Builder, This method implements standard bulk inserts for all databases, but should be overridden by adapters to implement common features with non-standard syntax like handling duplicates or returning values.

#case_insensitive_comparison, #case_sensitive_comparison, #check_if_write_query, #check_version, #clean!, #connection_class,
#create_enum

This is meant to be implemented by the adapters that support custom enum types.

#database_version, #default_index_type?, #default_uniqueness_comparison,
#drop_enum

This is meant to be implemented by the adapters that support custom enum types.

#get_advisory_lock

This is meant to be implemented by the adapters that support advisory locks.

#get_database_version, #prepared_statements_disabled_cache,
#release_advisory_lock

This is meant to be implemented by the adapters that support advisory locks.

#rename_enum

This is meant to be implemented by the adapters that support custom enum types.

#rename_enum_value

This is meant to be implemented by the adapters that support custom enum types.

#return_value_after_insert?,
#seconds_idle

Seconds since this connection was returned to the pool.

#steal!

this method must only be called while holding connection pool’s mutex (and a desire for segfaults).

#valid_type?, #with_instrumenter

Savepoints - Included

DatabaseLimits - Included

#index_name_length

Returns the maximum length of an index name.

#table_alias_length

Returns the maximum length of a table alias.

#table_name_length

Returns the maximum length of a table name.

#bind_params_length, #max_identifier_length

Quoting - Included

#quote

Quotes the column value to help prevent SQL injection attacks.

#quote_column_name

Quotes the column name.

#quote_string

Quotes a string, escaping any ‘ (single quote) and \ (backslash) characters.

#quote_table_name

Quotes the table name.

#quote_table_name_for_assignment

Override to return the quoted table name for assignment.

#quoted_date

Quote date/time values for use in SQL input.

#quoted_false, #quoted_true,
#type_cast

Cast a value to a type that the database understands.

#unquoted_false, #unquoted_true, #lookup_cast_type, #type_casted_binds,
#cast_bound_value

Cast a value to be used as a bound parameter of unknown type.

#lookup_cast_type_from_column

If you are having to call this function, you are likely doing something wrong.

#quote_default_expression, #quoted_binary, #quoted_time, #sanitize_as_sql_comment

DatabaseStatements - Included

#add_transaction_record

Register a record with the current transaction so that its after_commit and after_rollback callbacks can be called.

#begin_db_transaction

Begins the transaction (and turns off auto-committing).

#begin_isolated_db_transaction

Begins the transaction with the isolation level set.

#commit_db_transaction

Commits the transaction (and turns on auto-committing).

#create
#default_sequence_name,
#delete

Executes the delete statement and returns the number of rows affected.

#disable_lazy_transactions!, #empty_insert_statement_value, #enable_lazy_transactions!,
#exec_delete

Executes delete sql statement in the context of this connection using binds as the bind substitutes.

#exec_insert

Executes insert sql statement in the context of this connection using binds as the bind substitutes.

#exec_query

Executes sql statement in the context of this connection using binds as the bind substitutes.

#exec_update

Executes update sql statement in the context of this connection using binds as the bind substitutes.

#execute

Executes the SQL statement in the context of this connection and returns the raw result from the connection adapter.

#high_precision_current_timestamp

Returns an ::Arel SQL literal for the CURRENT_TIMESTAMP for usage with arbitrary precision date/time columns.

#initialize,
#insert

Executes an INSERT query and returns the new record’s ID.

#insert_fixture

Inserts the given fixture into the table.

#insert_fixtures_set,
#reset_sequence!

Set the sequence to the max value of the table’s column.

#restart_db_transaction,
#rollback_db_transaction

Rolls back the transaction (and turns on auto-committing).

#rollback_to_savepoint,
#sanitize_limit

Sanitizes the given LIMIT parameter in order to prevent SQL injection.

#select_all

Returns an ::ActiveRecord::Result instance.

#select_one

Returns a record hash with the column names as keys and column values as values.

#select_rows

Returns an array of arrays containing the field values.

#select_value

Returns a single value from a record.

#select_values

Returns an array of the values of the first column in a select:

#to_sql

Converts an arel AST to SQL.

#transaction

Runs the given block in a database transaction, and returns the result of the block.

#transaction_isolation_levels,
#truncate

Executes the truncate statement.

#update

Executes the update statement and returns the number of rows affected.

#write_query?

Determines whether the SQL statement is a write query.

#arel_from_relation, #build_fixture_sql, #build_fixture_statements, #build_truncate_statement, #build_truncate_statements, #combine_multi_statements, #default_insert_value, #execute_batch, #extract_table_ref_from_insert_sql, #internal_execute, #last_inserted_id, #raw_execute, #returning_column_values,
#select

Returns an ::ActiveRecord::Result instance.

#single_value_from_rows, #with_multi_statements,
#cacheable_query

This is used in the ::ActiveRecord::StatementCache object.

#exec_insert_all, #exec_restart_db_transaction, #exec_rollback_db_transaction, #explain, #internal_exec_query, #mark_transaction_written_if_write, #query, #query_value, #query_values, #reset_transaction, #truncate_tables,
#with_yaml_fallback

::ActiveRecord::Fixture value is quoted by ::Arel, however scalar values are not quotable.

#sql_for_insert, #to_sql_and_binds

SchemaStatements - Included

#add_belongs_to
#add_check_constraint

Adds a new check constraint to the table.

#add_column

Add a new type column named column_name to table_name.

#add_foreign_key

Adds a new foreign key.

#add_index

Adds a new index to the table.

#add_reference

Adds a reference.

#add_timestamps

Adds timestamps (created_at and updated_at) columns to table_name.

#assume_migrated_upto_version,
#build_create_table_definition

Returns a TableDefinition object containing information about the table that would be created if the same arguments were passed to #create_table.

#change_column

Changes the column’s definition according to the new options.

#change_column_comment

Changes the comment for a column or removes it if nil.

#change_column_default

Sets a new default value for a column:

#change_column_null

Sets or removes a NOT NULL constraint on a column.

#change_table

A block for changing columns in table.

#change_table_comment

Changes the comment for a table or removes it if nil.

#check_constraint_exists?

Checks to see if a check constraint exists on a table for a given check constraint definition.

#check_constraints

Returns an array of check constraints for the given table.

#column_exists?

Checks to see if a column exists in a given table.

#columns

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

#create_join_table

Creates a new join table with the name created using the lexical order of the first two arguments.

#create_table

Creates a new table with the name table_name.

#data_source_exists?

Checks to see if the data source name exists on the database.

#data_sources

Returns the relation names usable to back Active Record models.

#drop_join_table

Drops the join table specified by the given arguments.

#drop_table

Drops a table from the database.

#foreign_key_exists?

Checks to see if a foreign key exists on a table for a given foreign key definition.

#foreign_keys

Returns an array of foreign keys for the given table.

#index_exists?

Checks to see if an index exists on a table for a given index definition.

#index_name_exists?

Verifies the existence of an index with a given name.

#indexes

Returns an array of indexes for the given table.

#max_index_name_size

Returns the maximum length of an index name in bytes.

#native_database_types

Returns a hash of mappings from the abstract data types to the native database types.

#options_include_default?,
#primary_key

Returns just a table’s primary key.

#remove_belongs_to
#remove_check_constraint

Removes the given check constraint from the table.

#remove_column

Removes the column from the table definition.

#remove_columns

Removes the given columns from the table definition.

#remove_foreign_key

Removes the given foreign key from the table.

#remove_index

Removes the given index from the table.

#remove_reference

Removes the reference(s).

#remove_timestamps

Removes the timestamp columns (created_at and updated_at) from the table definition.

#rename_column

Renames a column.

#rename_index

Renames an index.

#rename_table

Renames a table.

#table_alias_for

Truncates a table alias according to the limits of the current adapter.

#table_comment

Returns the table comment that’s stored in database metadata.

#table_exists?

Checks to see if the table table_name exists on the database.

#table_options,
#tables

Returns an array of table names defined in the database.

#view_exists?

Checks to see if the view view_name exists on the database.

#views

Returns an array of view names defined in the database.

#add_column_for_alter, #add_index_sort_order,
#add_options_for_index_columns

Overridden by the MySQL adapter for supporting index lengths and by the PostgreSQL adapter for supporting operator classes.

#add_timestamps_for_alter, #can_remove_index_by_name?, #change_column_default_for_alter, #check_constraint_for, #check_constraint_for!, #check_constraint_name, #column_options_keys, #create_alter_table, #create_table_definition, #data_source_sql,
#expression_column_name?

Try to identify whether the given column name is an expression.

#extract_foreign_key_action,
#extract_new_comment_value
#extract_new_default_value, #fetch_type_metadata, #foreign_key_for, #foreign_key_for!, #foreign_key_name, #generate_index_name, #index_column_names, #index_name_for_remove, #index_name_options, #insert_versions_sql, #options_for_index_columns, #quoted_scope, #reference_name_for_table, #remove_column_for_alter, #remove_columns_for_alter, #remove_timestamps_for_alter, #rename_column_indexes, #rename_column_sql, #rename_table_indexes, #strip_table_name_prefix_and_suffix, #validate_change_column_null_argument!, #validate_create_table_options!, #validate_index_length!, #validate_table_length!, #add_columns, #add_index_options,
#build_add_column_definition

Builds an AlterTable object for adding a column to a table.

#build_change_column_default_definition
#build_create_index_definition

Builds a CreateIndexDefinition object.

#build_create_join_table_definition

Builds a TableDefinition object for a join table.

#bulk_change_table, #check_constraint_options,
#columns_for_distinct

Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.

#create_schema_dumper, #distinct_relation_for_primary_key, #dump_schema_information, #foreign_key_column_for, #foreign_key_options, #index_algorithm, #index_name, #internal_string_options_for_primary_key, #quoted_columns_for_index,
#schema_creation

Returns an instance of SchemaCreation, which can be used to visit a schema definition object and return DDL.

#type_to_sql, #update_table_definition, #valid_column_definition_options, #valid_primary_key_options, #valid_table_definition_options

::ActiveRecord::Migration::JoinTable - Included

::ActiveSupport::Callbacks - Included

#run_callbacks

Runs the callbacks for the given event.

#halted_callback_hook

A hook invoked every time a before callback is halted.

Constructor Details

.newSQLite3Adapter

[ GitHub ]

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

def initialize(...)
  super

  @memory_database = false
  case @config[:database].to_s
  when ""
    raise ArgumentError, "No database file specified. Missing argument: database"
  when ":memory:"
    @memory_database = true
  when /\Afile:/
  else
    # Otherwise we have a path relative to Rails.root
    @config[:database] = File.expand_path(@config[:database], Rails.root) if defined?(Rails.root)
    dirname = File.dirname(@config[:database])
    unless File.directory?(dirname)
      begin
        Dir.mkdir(dirname)
      rescue Errno::ENOENT => error
        if error.message.include?("No such file or directory")
          raise ActiveRecord::NoDatabaseError.new(connection_pool: @pool)
        else
          raise
        end
      end
    end
  end

  @config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
  @connection_parameters = @config.merge(database: @config[:database].to_s, results_as_hash: true)
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
end

Class Attribute Details

.strict_strings_by_default (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 64

class_attribute :strict_strings_by_default, default: false

.strict_strings_by_default?Boolean (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 64

class_attribute :strict_strings_by_default, default: false

Class Method Details

.dbconsole(config, options = {})

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 41

def dbconsole(config, options = {})
  args = []

  args << "-#{options[:mode]}" if options[:mode]
  args << "-header" if options[:header]
  args << File.expand_path(config.database, Rails.respond_to?(:root) ? Rails.root : nil)

  find_cmd_and_exec("sqlite3", *args)
end

.initialize_type_map(m) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 462

def initialize_type_map(m)
  super
  register_class_with_limit m, %r(int)i, SQLite3Integer
end

.new_client(config)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 31

def new_client(config)
  ::SQLite3::Database.new(config[:database].to_s, config)
rescue Errno::ENOENT => error
  if error.message.include?("No such file or directory")
    raise ActiveRecord::NoDatabaseError
  else
    raise
  end
end

Instance Attribute Details

#active? (readonly)

Alias for #connected?.

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 206

alias_method :active?, :connected?

#connected?Boolean (readonly) Also known as: #active?

[ GitHub ]

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

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

#database_exists?Boolean (readonly)

[ GitHub ]

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

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

#requires_reloading?Boolean (readonly)

[ GitHub ]

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

def requires_reloading?
  true
end

#shared_cache?Boolean (readonly)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 431

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

#strict_strings_by_default (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 64

class_attribute :strict_strings_by_default, default: false

#strict_strings_by_default?Boolean (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 64

class_attribute :strict_strings_by_default, default: false

#supports_check_constraints?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 163

def supports_check_constraints?
  true
end

#supports_common_table_expressions?Boolean (readonly)

[ GitHub ]

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

def supports_common_table_expressions?
  database_version >= "3.8.3"
end

#supports_concurrent_connections?Boolean (readonly)

[ GitHub ]

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

def supports_concurrent_connections?
  !@memory_database
end

#supports_datetime_with_precision?Boolean (readonly)

[ GitHub ]

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

def supports_datetime_with_precision?
  true
end

#supports_ddl_transactions?Boolean (readonly)

[ GitHub ]

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

def supports_ddl_transactions?
  true
end

#supports_deferrable_constraints?Boolean (readonly)

[ GitHub ]

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

def supports_deferrable_constraints?
  true
end

#supports_explain?Boolean (readonly)

[ GitHub ]

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

def supports_explain?
  true
end

#supports_expression_index?Boolean (readonly)

[ GitHub ]

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

def supports_expression_index?
  database_version >= "3.9.0"
end

#supports_foreign_keys?Boolean (readonly)

[ GitHub ]

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

def supports_foreign_keys?
  true
end

#supports_index_sort_order?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 219

def supports_index_sort_order?
  true
end

#supports_insert_conflict_target? (readonly)

[ GitHub ]

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

alias supports_insert_conflict_target? supports_insert_on_conflict?

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

[ GitHub ]

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

def supports_insert_on_conflict?
  database_version >= "3.24.0"
end

#supports_insert_on_duplicate_skip? (readonly)

[ GitHub ]

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

alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?

#supports_insert_on_duplicate_update? (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 191

alias supports_insert_on_duplicate_update? supports_insert_on_conflict?

#supports_insert_returning?Boolean (readonly)

[ GitHub ]

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

def supports_insert_returning?
  database_version >= "3.35.0"
end

#supports_json?Boolean (readonly)

[ GitHub ]

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

def supports_json?
  true
end

#supports_lazy_transactions?Boolean (readonly)

[ GitHub ]

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

def supports_lazy_transactions?
  true
end

#supports_partial_index?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 147

def supports_partial_index?
  true
end

#supports_savepoints?Boolean (readonly)

[ GitHub ]

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

def supports_savepoints?
  true
end

#supports_transaction_isolation?Boolean (readonly)

[ GitHub ]

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

def supports_transaction_isolation?
  true
end

#supports_views?Boolean (readonly)

[ GitHub ]

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

def supports_views?
  true
end

#supports_virtual_columns?Boolean (readonly)

[ GitHub ]

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

def supports_virtual_columns?
  database_version >= "3.31.0"
end

#use_insert_returning?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 435

def use_insert_returning?
  @use_insert_returning
end

Instance Method Details

#add_belongs_to(table_name, ref_name, **options)

Alias for #add_reference.

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 372

alias :add_belongs_to :add_reference

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

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 297

def add_column(table_name, column_name, type, **options) # :nodoc:
  type = type.to_sym
  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) Also known as: #add_belongs_to

This method is for internal use only.
[ GitHub ]

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

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

#add_timestamps(table_name, **options)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 356

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

#alter_table(table_name, foreign_keys = foreign_keys(table_name), check_constraints = check_constraints(table_name), **options) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 528

def alter_table(
  table_name,
  foreign_keys = foreign_keys(table_name),
  check_constraints = check_constraints(table_name),
  **options
)
  altered_table_name = "a#{table_name}"

  caller = lambda do |definition|
    rename = options[:rename] || {}
    foreign_keys.each do |fk|
      if column = rename[fk.options[:column]]
        fk.options[:column] = column
      end
      to_table = strip_table_name_prefix_and_suffix(fk.to_table)
      definition.foreign_key(to_table, **fk.options)
    end

    check_constraints.each do |chk|
      definition.check_constraint(chk.expression, **chk.options)
    end

    yield definition if block_given?
  end

  transaction do
    disable_referential_integrity do
      move_table(table_name, altered_table_name, options.merge(temporary: true))
      move_table(altered_table_name, table_name, &caller)
    end
  end
end

#arel_visitor (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 741

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

#bind_params_length (private)

See www.sqlite.org/limits.html, the default value is 999 when not configured.

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 474

def bind_params_length
  999
end

#build_insert_sql(insert)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 412

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

#build_statement_pool (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 745

def build_statement_pool
  StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
end

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

This method is for internal use only.
[ GitHub ]

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

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)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 325

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)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 333

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!

This method is for internal use only.
[ GitHub ]

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

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, connection_pool: @pool)
  end
end

#check_version

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 443

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

#column_definitions(table_name) (private)

Alias for #table_structure.

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 487

alias column_definitions table_structure

#configure_connection (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 763

def configure_connection
  if @config[:timeout] && @config[:retries]
    raise ArgumentError, "Cannot specify both timeout and retries arguments"
  elsif @config[:timeout]
    @raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
  elsif @config[:retries]
    retries = self.class.type_cast_config_to_integer(@config[:retries])
    raw_connection.busy_handler do |count|
      count <= retries
    end
  end

  super

  pragmas = @config.fetch(:pragmas, {}).stringify_keys
  DEFAULT_PRAGMAS.merge(pragmas).each do |pragma, value|
    if ::SQLite3::Pragmas.method_defined?("#{pragma}=")
      @raw_connection.public_send("#{pragma}=", value)
    else
      warn "Unknown SQLite pragma: #{pragma}"
    end
  end
end

#connect (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 749

def connect
  @raw_connection = self.class.new_client(@connection_parameters)
rescue ConnectionNotEstablished => ex
  raise ex.set_pool(@pool)
end

#copy_table(from, to, options = {}) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 566

def copy_table(from, to, options = {})
  from_primary_key = primary_key(from)
  options[:id] = false
  create_table(to, **options) do |definition|
    @definition = definition
    if from_primary_key.is_a?(Array)
      @definition.primary_keys from_primary_key
    end

    columns(from).each do |column|
      column_name = options[:rename] ?
        (options[:rename][column.name] ||
         options[:rename][column.name.to_sym] ||
         column.name) : column.name

      column_options = {
        limit: column.limit,
        precision: column.precision,
        scale: column.scale,
        null: column.null,
        collation: column.collation,
        primary_key: column_name == from_primary_key
      }

      if column.virtual?
        column_options[:as] = column.default_function
        column_options[:stored] = column.virtual_stored?
        column_options[:type] = column.type
      elsif column.has_default?
        type = lookup_cast_type_from_column(column)
        default = type.deserialize(column.default)
        default = -> { column.default_function } if default.nil?

        unless column.auto_increment?
          column_options[:default] = default
        end
      end

      column_type = column.virtual? ? :virtual : (column.bigint? ? :bigint : column.type)
      @definition.column(column_name, column_type, **column_options)
    end

    yield @definition if block_given?
  end
  copy_table_indexes(from, to, options[:rename] || {})

  columns_to_copy = @definition.columns.reject { |col| col.options.key?(:as) }.map(&:name)
  copy_table_contents(from, to,
    columns_to_copy,
    options[:rename] || {})
end

#copy_table_contents(from, to, columns, rename = {}) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 646

def copy_table_contents(from, to, columns, rename = {})
  column_mappings = Hash[columns.map { |name| [name, name] }]
  rename.each { |a| column_mappings[a.last] = a.first }
  from_columns = columns(from).collect(&:name)
  columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
  from_columns_to_copy = columns.map { |col| column_mappings[col] }
  quoted_columns = columns.map { |col| quote_column_name(col) } * ","
  quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","

  internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
             SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
end

#copy_table_indexes(from, to, rename = {}) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 618

def copy_table_indexes(from, to, rename = {})
  indexes(from).each do |index|
    name = index.name
    if to == "a#{from}"
      name = "t#{name}"
    elsif from == "a#{to}"
      name = name[1..-1]
    end

    columns = index.columns
    if columns.is_a?(Array)
      to_column_names = columns(to).map(&:name)
      columns = columns.map { |c| rename[c] || c }.select do |column|
        to_column_names.include?(column)
      end
    end

    unless columns.empty?
      # index name can't be the same
      options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
      options[:unique] = true if index.unique
      options[:where] = index.where if index.where
      options[:order] = index.orders if index.orders
      add_index(to, columns, **options)
    end
  end
end

#disable_referential_integrity

This method is for internal use only.

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

[ GitHub ]

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

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!

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

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 212

def disconnect!
  super

  @raw_connection&.close rescue nil
  @raw_connection = nil
end

#encoding

Returns the current database encoding format as a string, e.g. ‘UTF-8’

[ GitHub ]

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

def encoding
  any_raw_connection.encoding.to_s
end

#extract_default_function(default_value, default) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 512

def extract_default_function(default_value, default)
  default if has_default_function?(default_value, default)
end

#extract_value_from_default(default) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 489

def extract_value_from_default(default)
  case default
  when /^null$/i
    nil
  # Quoted types
  when /^'([^|]*)'$/m
    $1.gsub("''", "'")
  # Quoted types
  when /^"([^|]*)"$/m
    $1.gsub('""', '"')
  # Numeric types
  when /\A-?\d+(\.\d*)?\z/
    $&
  # Binary columns
  when /x'(.*)'/
    [ $1 ].pack("H*")
  else
    # Anything else is blank or some function
    # and we can't know the value of that, so return nil.
    nil
  end
end

#foreign_keys(table_name)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 376

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_version

This method is for internal use only.
[ GitHub ]

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

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

#has_default_function?(default_value, default) ⇒ Boolean (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 516

def has_default_function?(default_value, default)
  !default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
end

#invalid_alter_table_type?(type, options) ⇒ Boolean (private)

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

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 522

def invalid_alter_table_type?(type, options)
  type == :primary_key || options[:primary_key] ||
    options[:null] == false && options[:default].nil? ||
    (type == :virtual && options[:stored])
end

#move_table(from, to, options = {}, &block) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 561

def move_table(from, to, options = {}, &block)
  copy_table(from, to, options, &block)
  drop_table(from)
end

#native_database_types

This method is for internal use only.
[ GitHub ]

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

def native_database_types # :nodoc:
  NATIVE_DATABASE_TYPES
end

#primary_keys(table_name)

This method is for internal use only.

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

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 272

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

#reconnect (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 755

def reconnect
  if active?
    @raw_connection.rollback rescue nil
  else
    connect
  end
end

#remove_column(table_name, column_name, type = nil, **options)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 308

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)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 315

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)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 277

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)

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

#rename_column(table_name, column_name, new_column_name)

This method is for internal use only.
[ GitHub ]

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

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)

Renames a table.

Example:

rename_table('octopuses', 'octopi')
[ GitHub ]

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

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)
  exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
  rename_table_indexes(table_name, new_name, **options)
end

#reset!

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 208

alias :reset! :reconnect!

#table_structure(table_name) (private) Also known as: #column_definitions

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 478

def table_structure(table_name)
  structure = if supports_virtual_columns?
    internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
  else
    internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
  end
  raise ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'", connection_pool: @pool) if structure.empty?
  table_structure_with_collation(table_name, structure)
end

#table_structure_sql(table_name) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 719

def table_structure_sql(table_name)
  sql = <<~SQL
    SELECT sql FROM
      (SELECT * FROM sqlite_master UNION ALL
       SELECT * FROM sqlite_temp_master)
    WHERE type = 'table' AND name = #{quote(table_name)}
  SQL

  # Result will have following sample string
  # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  #                       "password_digest" varchar COLLATE "NOCASE");
  result = query_value(sql, "SCHEMA")

  return [] unless result

  # Splitting with left parentheses and discarding the first part will return all
  # columns separated with comma(,).
  columns_string = result.split("(", 2).last

  columns_string.split(",").map(&:strip)
end

#table_structure_with_collation(table_name, basic_structure) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 681

def table_structure_with_collation(table_name, basic_structure)
  collation_hash = {}
  auto_increments = {}
  generated_columns = {}

  column_strings = table_structure_sql(table_name)

  if column_strings.any?
    column_strings.each do |column_string|
      # This regex will match the column name and collation type and will save
      # the value in $1 and $2 respectively.
      collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
      auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
      generated_columns[$1] = $2 if GENERATED_ALWAYS_AS_REGEX =~ column_string
    end

    basic_structure.map do |column|
      column_name = column["name"]

      if collation_hash.has_key? column_name
        column["collation"] = collation_hash[column_name]
      end

      if auto_increments.has_key?(column_name)
        column["auto_increment"] = true
      end

      if generated_columns.has_key?(column_name)
        column["dflt_value"] = generated_columns[column_name]
      end

      column
    end
  else
    basic_structure.to_a
  end
end

#translate_exception(exception, message:, sql:, binds:) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb', line 659

def translate_exception(exception, message:, sql:, binds:)
  # SQLite 3.8.2 returns a newly formatted error message:
  #   UNIQUE constraint failed: *table_name*.*column_name*
  # Older versions of SQLite return:
  #   column *column_name* is not unique
  if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
    RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
  elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
    NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
  elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
    InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
  elsif exception.message.match?(/called on a closed database/i)
    ConnectionNotEstablished.new(exception, connection_pool: @pool)
  else
    super
  end
end