From 69700c9ee760c5880fc80af70a89b42bb791cf98 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 29 Jul 2019 08:40:57 -0700 Subject: Move DatabaseAlreadyExists detection to DB adapter Previously it was the responsibility of the database tasks to translate the invalid statement from creating a duplicate database into an ActiveRecord::Tasks::DatabaseAlreadyExists error. It's actually easier for us to do this detection inside of the adapter, where we already do a case statement on the return code to translate the error. This commit introduces ActiveRecord::DatabaseAlreadyExists, a subclass of StatementInvalid, and updates both AbstractMysqlAdapter and PostgresqlAdapter to return this more specific exception in that case. Because this is a subclass of the old exception, StatementInvalid, it should be backwards compatible with any code expecting that from create_database. This works for both create_database and exectute("CREATE DATABASE") --- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 3 +++ .../lib/active_record/connection_adapters/postgresql_adapter.rb | 3 +++ activerecord/lib/active_record/errors.rb | 4 ++++ activerecord/lib/active_record/tasks/database_tasks.rb | 1 - activerecord/lib/active_record/tasks/mysql_database_tasks.rb | 6 ------ activerecord/lib/active_record/tasks/postgresql_database_tasks.rb | 6 ------ activerecord/test/cases/tasks/mysql_rake_test.rb | 6 ++---- activerecord/test/cases/tasks/postgresql_rake_test.rb | 2 +- 8 files changed, 13 insertions(+), 18 deletions(-) (limited to 'activerecord') 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 800235a302..0fe16270ed 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -573,6 +573,7 @@ module ActiveRecord end # See https://dev.mysql.com/doc/refman/5.7/en/server-error-reference.html + ER_DB_CREATE_EXISTS = 1007 ER_DUP_ENTRY = 1062 ER_NOT_NULL_VIOLATION = 1048 ER_NO_REFERENCED_ROW = 1216 @@ -592,6 +593,8 @@ module ActiveRecord def translate_exception(exception, message:, sql:, binds:) case error_number(exception) + when ER_DB_CREATE_EXISTS + DatabaseAlreadyExists.new(message, sql: sql, binds: binds) when ER_DUP_ENTRY RecordNotUnique.new(message, sql: sql, binds: binds) when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2 diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 0a7c6d8ac4..f33ba87435 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -467,6 +467,7 @@ module ActiveRecord UNIQUE_VIOLATION = "23505" SERIALIZATION_FAILURE = "40001" DEADLOCK_DETECTED = "40P01" + DUPLICATE_DATABASE = "42P04" LOCK_NOT_AVAILABLE = "55P03" QUERY_CANCELED = "57014" @@ -488,6 +489,8 @@ module ActiveRecord SerializationFailure.new(message, sql: sql, binds: binds) when DEADLOCK_DETECTED Deadlocked.new(message, sql: sql, binds: binds) + when DUPLICATE_DATABASE + DatabaseAlreadyExists.new(message, sql: sql, binds: binds) when LOCK_NOT_AVAILABLE LockWaitTimeout.new(message, sql: sql, binds: binds) when QUERY_CANCELED diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index bf7d99449d..509f21c9a5 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -187,6 +187,10 @@ module ActiveRecord class NoDatabaseError < StatementInvalid end + # Raised when creating a database if it exists. + class DatabaseAlreadyExists < StatementInvalid + end + # Raised when PostgreSQL returns 'cached plan must not change result type' and # we cannot retry gracefully (e.g. inside a transaction) class PreparedStatementCacheExpired < StatementInvalid diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 5d1ce19829..300e67b0aa 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -4,7 +4,6 @@ require "active_record/database_configurations" module ActiveRecord module Tasks # :nodoc: - class DatabaseAlreadyExists < StandardError; end # :nodoc: class DatabaseNotSupported < StandardError; end # :nodoc: # ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index e3efeb75b5..b9a8ccb22c 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -15,12 +15,6 @@ module ActiveRecord establish_connection configuration_without_database connection.create_database configuration["database"], creation_options establish_connection configuration - rescue ActiveRecord::StatementInvalid => error - if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS - raise DatabaseAlreadyExists - else - raise - end end def drop diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb index 626ffdfdf9..fc37db216d 100644 --- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb @@ -21,12 +21,6 @@ module ActiveRecord connection.create_database configuration["database"], configuration.merge("encoding" => encoding) establish_connection configuration - rescue ActiveRecord::StatementInvalid => error - if error.cause.is_a?(PG::DuplicateDatabase) - raise DatabaseAlreadyExists - else - raise - end end def drop diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index ac3c5bc26e..4b039395e8 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -93,11 +93,9 @@ if current_adapter?(:Mysql2Adapter) with_stubbed_connection_establish_connection do ActiveRecord::Base.connection.stub( :create_database, - proc { raise ActiveRecord::StatementInvalid } + proc { raise ActiveRecord::DatabaseAlreadyExists } ) do - ActiveRecord::Base.connection.stub(:error_number, 1007) do - ActiveRecord::Tasks::DatabaseTasks.create @configuration - end + ActiveRecord::Tasks::DatabaseTasks.create @configuration assert_equal "Database 'my-app-db' already exists\n", $stderr.string end diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb index f9df650687..d74ba0580d 100644 --- a/activerecord/test/cases/tasks/postgresql_rake_test.rb +++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb @@ -129,7 +129,7 @@ if current_adapter?(:PostgreSQLAdapter) with_stubbed_connection_establish_connection do ActiveRecord::Base.connection.stub( :create_database, - proc { raise ActiveRecord::Tasks::DatabaseAlreadyExists } + proc { raise ActiveRecord::DatabaseAlreadyExists } ) do ActiveRecord::Tasks::DatabaseTasks.create @configuration -- cgit v1.2.3