# class ActiveRecord::ConnectionAdapters::SQLite3Adapter

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.

### Constants

ADAPTER_NAME
COLLATE_REGEX
NATIVE_DATABASE_TYPES

### Public Class Methods

new(connection, logger, connection_options, config) click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::QueryCache.new

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 95
    def initialize(connection, logger, connection_options, config)
      super(connection, logger, config)

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

      configure_connection
    end

### Public Instance Methods

active?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 142
    def active?
      @active != false
    end

allowed_index_name_length() click to toggle source

Returns 62. SQLite supports index names up to 64 characters. The rest is used by Rails internally to perform temporary rename operations

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 166
    def allowed_index_name_length
      index_name_length - 2
    end

clear_cache!() click to toggle source

Clears the prepared statements cache.

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 155
    def clear_cache!
      @statements.clear
    end

disconnect!() click to toggle source

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

Calls superclass method ActiveRecord::ConnectionAdapters::AbstractAdapter#disconnect!

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 148
    def disconnect!
      super
      @active = false
      @connection.close rescue nil
    end

encoding() click to toggle source

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

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 175
    def encoding
      @connection.encoding.to_s
    end

exec_delete(sql, name = "SQL", binds = []) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 238
    def exec_delete(sql, name = "SQL", binds = [])
      exec_query(sql, name, binds)
      @connection.changes
    end

Also aliased as: exec_update
exec_query(sql, name = nil, binds = [], prepare: false) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 205
    def exec_query(sql, name = nil, binds = [], prepare: false)
      type_casted_binds = type_casted_binds(binds)

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

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

exec_update(sql, name = "SQL", binds = [])
Alias for: exec_delete
explain(arel, binds = []) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 200
    def explain(arel, binds = [])
      sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
      SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
    end

foreign_keys(table_name) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 400
    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"])
        }
        ForeignKeyDefinition.new(table_name, row["table"], options)
      end
    end

last_inserted_id(result) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 244
    def last_inserted_id(result)
      @connection.last_insert_row_id
    end

new_column_from_field(table_name, field) click to toggle source

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

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 270
    def new_column_from_field(table_name, field) # :nondoc:
      case field["dflt_value"]
      when /^null$/i
        field["dflt_value"] = nil
      when /^'(.*)'$/m
        field["dflt_value"] = $1.gsub("''", "'")
      when /^"(.*)"$/m
        field["dflt_value"] = $1.gsub('""', '"')
      end

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

rename_table(table_name, new_name) click to toggle source

Renames a table.

Example:

    rename_table('octopuses', 'octopi')

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 331
    def rename_table(table_name, new_name)
      exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
      rename_table_indexes(table_name, new_name)
    end

requires_reloading?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 122
    def requires_reloading?
      true
    end

supports_datetime_with_precision?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 134
    def supports_datetime_with_precision?
      true
    end

supports_ddl_transactions?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 104
    def supports_ddl_transactions?
      true
    end

supports_explain?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 179
    def supports_explain?
      true
    end

supports_foreign_keys_in_create?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 126
    def supports_foreign_keys_in_create?
      sqlite_version >= "3.6.19"
    end

supports_index_sort_order?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 159
    def supports_index_sort_order?
      true
    end

supports_multi_insert?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 138
    def supports_multi_insert?
      sqlite_version >= "3.7.11"
    end

supports_partial_index?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 112
    def supports_partial_index?
      sqlite_version >= "3.8.0"
    end

supports_savepoints?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 108
    def supports_savepoints?
      true
    end

supports_statement_cache?() click to toggle source

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

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 118
    def supports_statement_cache?
      true
    end

supports_views?() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 130
    def supports_views?
      true
    end

valid_alter_table_type?(type) click to toggle source

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

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 338
    def valid_alter_table_type?(type)
      type.to_sym != :primary_key
    end

### Private Instance Methods

alter_table(table_name, options = {}) { |definition| ... } click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 422
    def alter_table(table_name, options = {})
      altered_table_name = "a#{table_name}"
      caller = lambda { |definition| yield definition if block_given? }

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

column_definitions(table_name)
Alias for: table_structure
configure_connection() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 574
    def configure_connection
      execute("PRAGMA foreign_keys = ON", "SCHEMA")
    end

copy_table(from, to, options = {}) { |definition| ... } click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 438
    def copy_table(from, to, options = {})
      from_primary_key = primary_key(from)
      options[:id] = false
      create_table(to, options) do |definition|
        @definition = definition
        @definition.primary_key(from_primary_key) if from_primary_key.present?
        columns(from).each do |column|
          column_name = options[:rename] ?
            (options[:rename][column.name] ||
             options[:rename][column.name.to_sym] ||
             column.name) : column.name
          next if column_name == from_primary_key

          @definition.column(column_name, column.type,
            limit: column.limit, default: column.default,
            precision: column.precision, scale: column.scale,
            null: column.null, collation: column.collation)
        end
        yield @definition if block_given?
      end
      copy_table_indexes(from, to, options[:rename] || {})
      copy_table_contents(from, to,
        @definition.columns.map(&:name),
        options[:rename] || {})
    end

copy_table_contents(from, to, columns, rename = {}) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 487
    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) } * ","

      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 = {}) click to toggle source

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

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

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

create_table_definition(*args) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 562
    def create_table_definition(*args)
      SQLite3::TableDefinition.new(*args)
    end

extract_foreign_key_action(specifier) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 566
    def extract_foreign_key_action(specifier)
      case specifier
      when "CASCADE"; :cascade
      when "SET NULL"; :nullify
      when "RESTRICT"; :restrict
      end
    end

move_table(from, to, options = {}, &block) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 433
    def move_table(from, to, options = {}, &block)
      copy_table(from, to, options, &block)
      drop_table(from)
    end

sqlite_version() click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 500
    def sqlite_version
      @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
    end

table_structure(table_name) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 415
    def table_structure(table_name)
      structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
      raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
      table_structure_with_collation(table_name, structure)
    end

Also aliased as: column_definitions
table_structure_with_collation(table_name, basic_structure) click to toggle source

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 523
    def table_structure_with_collation(table_name, basic_structure)
      collation_hash = {}
      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 = exec_query(sql, "SCHEMA").first

      if result
        # Splitting with left parentheses and picking up last will return all
        # columns separated with comma(,).
        columns_string = result["sql"].split("(").last

        columns_string.split(",").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
        end

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

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

          column
        end
      else
        basic_structure.to_hash
      end
    end

translate_exception(exception, message) click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::AbstractAdapter#translate_exception

    # File lib/active_record/connection_adapters/sqlite3_adapter.rb, line 504
    def translate_exception(exception, message)
      case exception.message
      # 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
      when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
        RecordNotUnique.new(message)
      when /.* may not be NULL/, /NOT NULL constraint failed: .*/
        NotNullViolation.new(message)
      when /FOREIGN KEY constraint failed/i
        InvalidForeignKey.new(message)
      else
        super
      end
    end