# module ActiveRecord::ConnectionAdapters::DatabaseStatements

### Public Class Methods

new() click to toggle source
Calls superclass method

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 4
    def initialize
      super
      reset_transaction
    end

### Public Instance Methods

add_transaction_record(record) click to toggle source

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

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 255
    def add_transaction_record(record)
      current_transaction.add_record(record)
    end

begin_db_transaction() click to toggle source

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

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 264
    def begin_db_transaction()    end

begin_isolated_db_transaction(isolation) click to toggle source

Begins the transaction with the isolation level set. Raises an error by default; adapters that support setting the isolation level should implement this method.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 278
    def begin_isolated_db_transaction(isolation)
      raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
    end

commit_db_transaction() click to toggle source

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

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 283
    def commit_db_transaction()   end

create(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
Alias for: insert
delete(arel, name = nil, binds = []) click to toggle source

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

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 144
    def delete(arel, name = nil, binds = [])
      exec_delete(to_sql(arel, binds), name, binds)
    end

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

Executes delete `sql` statement in the context of this connection using `binds` as the bind substitutes. `name` is logged along with the executed `sql` statement.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 108
    def exec_delete(sql, name = nil, binds = [])
      exec_query(sql, name, binds)
    end

exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) click to toggle source

Executes insert `sql` statement in the context of this connection using `binds` as the bind substitutes. `name` is logged along with the executed `sql` statement.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 100
    def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
      sql, binds = sql_for_insert(sql, pk, nil, sequence_name, binds)
      exec_query(sql, name, binds)
    end

exec_query(sql, name = "SQL", binds = [], prepare: false) click to toggle source

Executes `sql` statement in the context of this connection using `binds` as the bind substitutes. `name` is logged along with the executed `sql` statement.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 93
    def exec_query(sql, name = "SQL", binds = [], prepare: false)
      raise NotImplementedError
    end

exec_update(sql, name = nil, binds = []) click to toggle source

Executes update `sql` statement in the context of this connection using `binds` as the bind substitutes. `name` is logged along with the executed `sql` statement.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 120
    def exec_update(sql, name = nil, binds = [])
      exec_query(sql, name, binds)
    end

execute(sql, name = nil) click to toggle source

Executes the SQL statement in the context of this connection and returns the raw result from the connection adapter. Note: depending on your database connector, the result returned by this method may be manually memory managed. Consider using the #exec_query wrapper instead.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 86
    def execute(sql, name = nil)
      raise NotImplementedError
    end

insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) click to toggle source

Executes an INSERT query and returns the new record's ID

`id_value` will be returned unless the value is `nil`, in which case the database will attempt to calculate the last inserted id and return that value.

If the next id was calculated in advance (as in Oracle), it should be passed in as `id_value`.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 132
    def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
      value = exec_insert(to_sql(arel, binds), name, binds, pk, sequence_name)
      id_value || last_inserted_id(value)
    end

Also aliased as: create
rollback_db_transaction() click to toggle source

Rolls back the transaction (and turns on auto-committing). Must be done if the transaction block raises an exception or returns false.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 287
    def rollback_db_transaction
      exec_rollback_db_transaction
    end

select_all(arel, name = nil, binds = [], preparable: nil) click to toggle source

Returns an ActiveRecord::Result instance.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 31
    def select_all(arel, name = nil, binds = [], preparable: nil)
      arel, binds = binds_from_relation arel, binds
      sql = to_sql(arel, binds)
      if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
        preparable = false
      else
        preparable = visitor.preparable
      end
      if prepared_statements && preparable
        select_prepared(sql, name, binds)
      else
        select(sql, name, binds)
      end
    end

select_one(arel, name = nil, binds = []) click to toggle source

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

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 48
    def select_one(arel, name = nil, binds = [])
      select_all(arel, name, binds).first
    end

select_rows(arel, name = nil, binds = []) click to toggle source

Returns an array of arrays containing the field values. Order is the same as that returned by `columns`.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 65
    def select_rows(arel, name = nil, binds = [])
      select_all(arel, name, binds).rows
    end

select_value(arel, name = nil, binds = []) click to toggle source

Returns a single value from a record

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 53
    def select_value(arel, name = nil, binds = [])
      single_value_from_rows(select_rows(arel, name, binds))
    end

select_values(arel, name = nil, binds = []) click to toggle source

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

    select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 59
    def select_values(arel, name = nil, binds = [])
      select_rows(arel, name, binds).map(&:first)
    end

supports_statement_cache?() click to toggle source

Returns `true` when the connection adapter supports prepared statement caching, otherwise returns `false`

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 150
    def supports_statement_cache?
      false
    end

to_sql(arel, binds = []) click to toggle source

Converts an arel AST to SQL

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 10
    def to_sql(arel, binds = [])
      if arel.respond_to?(:ast)
        collected = visitor.accept(arel.ast, collector)
        collected.compile(binds, self).freeze
      else
        arel.dup.freeze
      end
    end

transaction(requires_new: nil, isolation: nil, joinable: true) { || ... } click to toggle source

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

## Nested transactions support¶ ↑

Most databases don't support true nested transactions. At the time of writing, the only database that supports true nested transactions that we're aware of, is MS-SQL.

In order to get around this problem, transaction will emulate the effect of nested transactions, by using savepoints: dev.mysql.com/doc/refman/5.7/en/savepoint.html Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8' supports savepoints.

It is safe to call this method if a database transaction is already open, i.e. if transaction is called within another transaction block. In case of a nested call, transaction will behave as follows:

- The block will be run without doing anything. All database statements that happen within the block are effectively appended to the already open database transaction.

- However, if `:requires_new` is set, the block will be wrapped in a database savepoint acting as a sub-transaction.

### Caveats¶ ↑

MySQL doesn't support DDL transactions. If you perform a DDL operation, then any created savepoints will be automatically released. For example, if you've created a savepoint, then you execute a CREATE TABLE statement, then the savepoint that was created will be automatically released.

This means that, on MySQL, you shouldn't execute DDL operations inside a transaction call that you know might create a savepoint. Otherwise, transaction will raise exceptions when it tries to release the already-automatically-released savepoints:

    Model.connection.transaction do  # BEGIN
      Model.connection.transaction(requires_new: true) do  # CREATE SAVEPOINT active_record_1
        Model.connection.create_table(...)
        # active_record_1 now automatically released
      end  # RELEASE SAVEPOINT active_record_1  <--- BOOM! database error!
    end

## Transaction isolation¶ ↑

If your database supports setting the isolation level for a transaction, you can set it like so:

    Post.transaction(isolation: :serializable) do
      # ...
    end

Valid isolation levels are:

- `:read_uncommitted`

- `:read_committed`

- `:repeatable_read`

- `:serializable`

You should consult the documentation for your database to understand the semantics of these different levels:

- www.postgresql.org/docs/current/static/transaction-iso.html

- dev.mysql.com/doc/refman/5.7/en/set-transaction.html

An ActiveRecord::TransactionIsolationError will be raised if:

- The adapter does not support setting the isolation level

- You are joining an existing open transaction

- You are creating a nested (savepoint) transaction

The mysql2 and postgresql adapters support setting the transaction isolation level.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 228
    def transaction(requires_new: nil, isolation: nil, joinable: true)
      if !requires_new && current_transaction.joinable?
        if isolation
          raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
        end
        yield
      else
        transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
      end
    rescue ActiveRecord::Rollback
      # rollbacks are silently swallowed
    end

transaction_isolation_levels() click to toggle source

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 266
    def transaction_isolation_levels
      {
        read_uncommitted: "READ UNCOMMITTED",
        read_committed:   "READ COMMITTED",
        repeatable_read:  "REPEATABLE READ",
        serializable:     "SERIALIZABLE"
      }
    end

transaction_open?() click to toggle source

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 245
    def transaction_open?
      current_transaction.open?
    end

transaction_state() click to toggle source

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 259
    def transaction_state
      current_transaction.state
    end

truncate(table_name, name = nil) click to toggle source

Executes the truncate statement.

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 113
    def truncate(table_name, name = nil)
      raise NotImplementedError
    end

update(arel, name = nil, binds = []) click to toggle source

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

    # File lib/active_record/connection_adapters/abstract/database_statements.rb, line 139
    def update(arel, name = nil, binds = [])
      exec_update(to_sql(arel, binds), name, binds)
    end