module Sequel::ActiveRecordConnection

Constants

ACTIVERECORD_CALLBACKS
Error
TRANSACTION_ISOLATION_MAP

Attributes

activerecord_model[RW]

Public Class Methods

extended(db) click to toggle source
# File lib/sequel/extensions/activerecord_connection.rb, line 18
def self.extended(db)
  db.activerecord_model = ActiveRecord::Base
  db.opts[:test] = false unless db.opts.key?(:test)

  begin
    require "sequel/extensions/activerecord_connection/#{db.adapter_scheme}"
    db.extend Sequel::ActiveRecordConnection.const_get(db.adapter_scheme.capitalize)
  rescue LoadError
    # assume the Sequel adapter already works with Active Record
  end
end

Public Instance Methods

connect(*) click to toggle source

Ensure Sequel is not creating its own connection anywhere.

# File lib/sequel/extensions/activerecord_connection.rb, line 33
def connect(*)
  raise Error, "creating a Sequel connection is not allowed"
end
log_connection_yield(sql, conn, args = nil) click to toggle source

Log executed queries into Active Record logger as well.

Calls superclass method
# File lib/sequel/extensions/activerecord_connection.rb, line 45
def log_connection_yield(sql, conn, args = nil)
  sql += "; #{args.inspect}" if args

  activerecord_log(sql) { super }
end
synchronize(*) { |raw_connection| ... } click to toggle source

Avoid calling Sequel's connection pool, instead use Active Record's.

# File lib/sequel/extensions/activerecord_connection.rb, line 38
def synchronize(*)
  activerecord_lock do
    yield activerecord_connection.raw_connection
  end
end
timezone() click to toggle source

Match database timezone with Active Record.

# File lib/sequel/extensions/activerecord_connection.rb, line 52
def timezone
  @timezone || ActiveRecord::Base.default_timezone
end

Private Instance Methods

_trans(conn) click to toggle source

Synchronizes transaction state with ActiveRecord. Sequel uses this information to know whether we're in a transaction, whether to create a savepoint, when to run transaction/savepoint hooks etc.

Calls superclass method
# File lib/sequel/extensions/activerecord_connection.rb, line 61
def _trans(conn)
  hash = super || { savepoints: [], activerecord: true }

  # add any ActiveRecord transactions/savepoints that have been opened
  # directly via ActiveRecord::Base.transaction
  while hash[:savepoints].length < activerecord_connection.open_transactions
    hash[:savepoints] << { activerecord: true }
  end

  # remove any ActiveRecord transactions/savepoints that have been closed
  # directly via ActiveRecord::Base.transaction
  while hash[:savepoints].length > activerecord_connection.open_transactions && hash[:savepoints].last[:activerecord]
    hash[:savepoints].pop
  end

  # sync knowledge about joinability of current ActiveRecord transaction/savepoint
  if activerecord_connection.transaction_open? && !activerecord_connection.current_transaction.joinable?
    hash[:savepoints].last[:auto_savepoint] = true
  end

  if hash[:savepoints].empty? && hash[:activerecord]
    Sequel.synchronize { @transactions.delete(conn) }
  else
    Sequel.synchronize { @transactions[conn] = hash }
  end

  super
end
activerecord_connection() click to toggle source
# File lib/sequel/extensions/activerecord_connection.rb, line 142
def activerecord_connection
  activerecord_model.connection
end
activerecord_lock() { || ... } click to toggle source

Active Record doesn't guarantee that a single connection can only be used by one thread at a time, so we need to use locking, which is what Active Record does internally as well.

# File lib/sequel/extensions/activerecord_connection.rb, line 132
def activerecord_lock
  return yield if ActiveRecord.version < Gem::Version.new("5.1.0")

  activerecord_connection.lock.synchronize do
    ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
      yield
    end
  end
end
activerecord_log(sql, &block) click to toggle source
# File lib/sequel/extensions/activerecord_connection.rb, line 146
def activerecord_log(sql, &block)
  ActiveSupport::Notifications.instrument(
    "sql.active_record",
    sql:        sql,
    name:       "Sequel",
    connection: activerecord_connection,
    &block
  )
end
add_savepoint_hook(conn, type, block) click to toggle source

When Active Record holds the savepoint, we cannot use Sequel hooks, because Sequel doesn't have knowledge of when the savepoint is released. So in this case we register an Active Record hook using the after_commit_everywhere gem.

Calls superclass method
# File lib/sequel/extensions/activerecord_connection.rb, line 121
def add_savepoint_hook(conn, type, block)
  if _trans(conn)[:savepoints].last[:activerecord]
    ACTIVERECORD_CALLBACKS.public_send(type, &block)
  else
    super
  end
end
add_transaction_hook(conn, type, block) click to toggle source

When Active Record holds the transaction, we cannot use Sequel hooks, because Sequel doesn't have knowledge of when the transaction is committed. So in this case we register an Active Record hook using the after_commit_everywhere gem.

Calls superclass method
# File lib/sequel/extensions/activerecord_connection.rb, line 109
def add_transaction_hook(conn, type, block)
  if _trans(conn)[:activerecord]
    ACTIVERECORD_CALLBACKS.public_send(type, &block)
  else
    super
  end
end
begin_transaction(conn, opts = OPTS) click to toggle source
# File lib/sequel/extensions/activerecord_connection.rb, line 90
def begin_transaction(conn, opts = OPTS)
  isolation = TRANSACTION_ISOLATION_MAP.fetch(opts[:isolation]) if opts[:isolation]
  joinable  = !opts[:auto_savepoint]

  activerecord_connection.begin_transaction(isolation: isolation, joinable: joinable)
end
commit_transaction(conn, opts = OPTS) click to toggle source
# File lib/sequel/extensions/activerecord_connection.rb, line 97
def commit_transaction(conn, opts = OPTS)
  activerecord_connection.commit_transaction
end
rollback_transaction(conn, opts = OPTS) click to toggle source
# File lib/sequel/extensions/activerecord_connection.rb, line 101
def rollback_transaction(conn, opts = OPTS)
  activerecord_connection.rollback_transaction
end