diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c404ce7b6d..94729c9db5 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,13 @@ +* All connection adapters `execute` now raises `ActiveRecord::ConnectionNotEstablished` rather than + `ActiveRecord::InvalidStatement` when they encounter a connection error. + + *Jean Boussier* + +* `Mysql2Adapter#quote_string` now raises `ActiveRecord::ConnectionNotEstablished` rather than + `ActiveRecord::InvalidStatement` when it can't connect to the MySQL server. + + *Jean Boussier* + * Add support for check constraints that are `NOT VALID` via `validate: false` (PostgreSQL-only). *Alex Robbin* diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 754b26b7a3..f313abf4bc 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -650,6 +650,12 @@ module ActiveRecord def translate_exception(exception, message:, sql:, binds:) case error_number(exception) + when nil + if exception.message.match?(/MySQL client is not connected/i) + ConnectionNotEstablished.new(exception) + else + super + end when ER_DB_CREATE_EXISTS DatabaseAlreadyExists.new(message, sql: sql, binds: binds) when ER_DUP_ENTRY diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index d1c4cc48db..7017412b07 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -478,6 +478,12 @@ module ActiveRecord return exception unless exception.respond_to?(:result) case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE) + when nil + if exception.message.match?(/connection is closed/i) + ConnectionNotEstablished.new(exception) + else + super + end when UNIQUE_VIOLATION RecordNotUnique.new(message, sql: sql, binds: binds) when FOREIGN_KEY_VIOLATION diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 0dcc8fd9f3..8964147a6c 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -475,17 +475,18 @@ module ActiveRecord end def translate_exception(exception, message:, sql:, binds:) - 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: .*/ + if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i) RecordNotUnique.new(message, sql: sql, binds: binds) - when /.* may not be NULL/, /NOT NULL constraint failed: .*/ + elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i) NotNullViolation.new(message, sql: sql, binds: binds) - when /FOREIGN KEY constraint failed/i + elsif exception.message.match?(/FOREIGN KEY constraint failed/i) InvalidForeignKey.new(message, sql: sql, binds: binds) + elsif exception.message.match?(/called on a closed database/i) + ConnectionNotEstablished.new(exception) else super end diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index e46c2a669e..afe2cc68bb 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -58,7 +58,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase def test_execute_after_disconnect @connection.disconnect! - error = assert_raise(ActiveRecord::StatementInvalid) do + error = assert_raise(ActiveRecord::ConnectionNotEstablished) do @connection.execute("SELECT 1") end assert_kind_of Mysql2::Error, error.cause @@ -67,7 +67,7 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase def test_quote_after_disconnect @connection.disconnect! - assert_raise(ActiveRecord::StatementInvalid) do + assert_raise(ActiveRecord::ConnectionNotEstablished) do @connection.quote("string") end end diff --git a/activerecord/test/cases/disconnected_test.rb b/activerecord/test/cases/disconnected_test.rb index ff104c003d..8b2a5e999a 100644 --- a/activerecord/test/cases/disconnected_test.rb +++ b/activerecord/test/cases/disconnected_test.rb @@ -22,8 +22,10 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase test "can't execute statements while disconnected" do @connection.execute "SELECT count(*) from products" @connection.disconnect! - assert_raises(ActiveRecord::StatementInvalid) do - @connection.execute "SELECT count(*) from products" + assert_raises(ActiveRecord::ConnectionNotEstablished) do + silence_warnings do + @connection.execute "SELECT count(*) from products" + end end end end