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 ------ 6 files changed, 10 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record') 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 -- cgit v1.2.3