diff --git a/lib/trilogy_adapter/backwards_ar_compatibility.rb b/lib/trilogy_adapter/backwards_ar_compatibility.rb index 5fcf195..0289064 100644 --- a/lib/trilogy_adapter/backwards_ar_compatibility.rb +++ b/lib/trilogy_adapter/backwards_ar_compatibility.rb @@ -14,6 +14,49 @@ class ConnectionFailed < QueryAborted end end + # For ActiveRecord <= 6.1 + unless const_defined?("DatabaseConnectionError") + class DatabaseConnectionError < ConnectionNotEstablished + def initialize(message = nil) + super(message || "Database connection error") + end + + class << self + def hostname_error(hostname) + DatabaseConnectionError.new(<<~MSG) + There is an issue connecting with your hostname: #{hostname}.\n + Please check your database configuration and ensure there is a valid connection to your database. + MSG + end + + def username_error(username) + DatabaseConnectionError.new(<<~MSG) + There is an issue connecting to your database with your username/password, username: #{username}.\n + Please check your database configuration to ensure the username/password are valid. + MSG + end + end + end + end + + # For ActiveRecord <= 6.1 + unless NoDatabaseError.respond_to?(:db_error) + NoDatabaseError.class_exec do + def self.db_error(db_name) + NoDatabaseError.new(<<~MSG) + We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml file. + + To resolve this error: + + - Did you create the database for this app, or delete it? You may need to create your database. + - Has the database name changed? Check your database.yml config has the correct database name. + + To create your database, run:\n\n bin/rails db:create + MSG + end + end + end + require "active_record/connection_adapters/abstract_mysql_adapter" module ConnectionAdapters unless AbstractAdapter.private_instance_methods.include?(:with_raw_connection) @@ -36,16 +79,14 @@ def initialize(version_string, full_version_string = nil) # For ActiveRecord <= 7.0 def with_raw_connection(allow_retry: false, uses_transaction: true) @lock.synchronize do - @raw_connection = nil unless instance_variable_defined?(:@raw_connection) - verify! unless @verified # || (@raw_connection.server_status & 1).positive? + @raw_connection = @connection || nil unless instance_variable_defined?(:@raw_connection) + verify! unless @verified # || (@raw_connection&.closed? == false && (@raw_connection.server_status & 1).positive?) materialize_transactions if uses_transaction begin yield @raw_connection rescue StandardError => exception @verified = false unless exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout) - # raise translate_exception_class(exception, nil, nil) - # raise translate_exception(exception, message: exception.message, sql: nil, binds: nil) - raise exception + raise end end end @@ -97,6 +138,16 @@ def reconnect! end alias :reset! :reconnect! + def exec_rollback_db_transaction + # 16384 tests the bit flag for SERVER_SESSION_STATE_CHANGED, which gets set when the + # last statement executed has caused a change in the server's state. + if active? || (@raw_connection.server_status & 16384).positive? + super + else + @verified = false + end + end + # For ActiveRecord <= 6.1 if AbstractMysqlAdapter.instance_method(:execute).parameters.length < 3 # Adds an #execute specific to the TrilogyAdapter that allows @@ -104,20 +155,31 @@ def reconnect! alias raw_execute execute def execute(sql, name = nil, **kwargs) @raw_connection = nil unless instance_variable_defined?(:@raw_connection) - # 16384 tests the bit flag for SERVER_SESSION_STATE_CHANGED, which gets set when the - # last statement executed has caused a change in the server's state. # Was: (!@verified && !active?) - reconnect if @raw_connection.nil? || (!@verified && (@raw_connection&.server_status & 16384).zero?) + reconnect if @raw_connection.nil? || (!@verified && (@raw_connection.server_status & 16384).zero?) if default_timezone == :local @raw_connection.query_flags |= ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE else @raw_connection.query_flags &= ~::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE end raw_execute(sql, name) - rescue => original_exception - @verified = false unless original_exception.is_a?(Deadlocked) || original_exception.is_a?(LockWaitTimeout) + rescue => exception + return if exception.is_a?(Deadlocked) + + @verified = false unless exception.is_a?(LockWaitTimeout) raise end + else # For ActiveRecord 7.0 + def execute(sql, name = nil, **kwargs) + sql = transform_query(sql) + check_if_write_query(sql) + begin + super + rescue => exception + @verified = true if exception.is_a?(ActiveRecord::StatementInvalid) && (@raw_connection.server_status & 16384).positive? + raise + end + end end private