diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 95fa78aa0f33..c73fe9fdb5db 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Improve detection of ActiveRecord::StatementTimeout with mysql2 adapter in the edge case when the query is terminated during filesort. + + *Kir Shatrov* + * Stop trying to read yaml file fixtures when loading Active Record fixtures. *Gannon McGibbon* 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 0fe16270edc8..3f9ba5ae02ce 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -574,6 +574,7 @@ def extract_precision(sql_type) # See https://dev.mysql.com/doc/refman/5.7/en/server-error-reference.html ER_DB_CREATE_EXISTS = 1007 + ER_FILSORT_ABORT = 1028 ER_DUP_ENTRY = 1062 ER_NOT_NULL_VIOLATION = 1048 ER_NO_REFERENCED_ROW = 1216 @@ -617,7 +618,7 @@ def translate_exception(exception, message:, sql:, binds:) Deadlocked.new(message, sql: sql, binds: binds) when ER_LOCK_WAIT_TIMEOUT LockWaitTimeout.new(message, sql: sql, binds: binds) - when ER_QUERY_TIMEOUT + when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT StatementTimeout.new(message, sql: sql, binds: binds) when ER_QUERY_INTERRUPTED QueryCanceled.new(message, sql: sql, binds: binds) diff --git a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb index cfc182377347..189d5e0bb9dc 100644 --- a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb +++ b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb @@ -240,6 +240,21 @@ def test_read_timeout_exception ActiveRecord::Base.establish_connection :arunit end + def test_statement_timeout_error_codes + raw_conn = @conn.raw_connection + assert_raises(ActiveRecord::StatementTimeout) do + raw_conn.stub(:query, ->(_sql) { raise Mysql2::Error.new("fail", 50700, ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::ER_FILSORT_ABORT) }) { + @conn.execute("SELECT 1") + } + end + + assert_raises(ActiveRecord::StatementTimeout) do + raw_conn.stub(:query, ->(_sql) { raise Mysql2::Error.new("fail", 50700, ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::ER_QUERY_TIMEOUT) }) { + @conn.execute("SELECT 1") + } + end + end + private def with_example_table(definition = "id int auto_increment primary key, number int, data varchar(255)", &block) super(@conn, "ex", definition, &block)