class WithAdvisoryLock::DatabaseAdapterSupport

Public Class Methods

new(connection) click to toggle source
# File lib/with_advisory_lock/database_adapter_support.rb, line 7
def initialize(connection)
  @connection = connection
  @sym_name   = connection.adapter_name.downcase.to_sym
end

Public Instance Methods

mysql?() click to toggle source
# File lib/with_advisory_lock/database_adapter_support.rb, line 12
def mysql?
  %i[mysql mysql2].include? @sym_name
end
mysql_nested_lock_support?() click to toggle source

Nested lock support for MySQL was introduced in 5.7.5 Checking by version number is complicated by MySQL compatible DBs (like MariaDB) having their own versioning schemes Therefore, we check for nested lock support by simply trying a nested lock, then testing and caching the outcome

# File lib/with_advisory_lock/database_adapter_support.rb, line 19
def mysql_nested_lock_support?
  return false unless mysql?

  # We select the MySQL version this way and cache on it, as MySQL will report versions like "5.7.5", and MariaDB will
  # report versions like "10.3.8-MariaDB", which allow us to cache on features without introducing problems.
  version = @connection.select_value("SELECT version()")

  @@mysql_nl_cache_mutex.synchronize do
    return @@mysql_nl_cache[version] if @@mysql_nl_cache.keys.include?(version)

    lock_1 = "\"nested-test-1-#{SecureRandom.hex}\""
    lock_2 = "\"nested-test-2-#{SecureRandom.hex}\""

    get_1  = @connection.select_value("SELECT GET_LOCK(#{lock_1}, 0) AS t#{SecureRandom.hex}")
    get_2  = @connection.select_value("SELECT GET_LOCK(#{lock_2}, 0) AS t#{SecureRandom.hex}")

    # Both locks should succeed in old and new MySQL versions with "1"
    raise RuntimeError, "Unexpected nested lock acquire result #{get_1}, #{get_2}" unless [get_1, get_2] == [1, 1]

    release_1 = @connection.select_value("SELECT RELEASE_LOCK(#{lock_1}) AS t#{SecureRandom.hex}")
    release_2 = @connection.select_value("SELECT RELEASE_LOCK(#{lock_2}) AS t#{SecureRandom.hex}")

    # In MySQL <  5.7.5 release_1 will return  nil (not currently locked) and release_2 will return 1 (successfully unlocked)
    # In MySQL >= 5.7.5 release_1 and release_2 will return 1 (both successfully unlocked)
    # See https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock for more
    @@mysql_nl_cache[version] = case [release_1, release_2]
                                when [1, 1]
                                  true
                                when [nil, 1]
                                  false
                                else
                                  raise RuntimeError, "Unexpected nested lock release result #{release_1}, #{release_2}"
                                end
  end
end
postgresql?() click to toggle source
# File lib/with_advisory_lock/database_adapter_support.rb, line 55
def postgresql?
  %i[postgresql empostgresql postgis].include? @sym_name
end
sqlite?() click to toggle source
# File lib/with_advisory_lock/database_adapter_support.rb, line 59
def sqlite?
  :sqlite3 == @sym_name
end