提交 69700c9e 编写于 作者: J John Hawthorn

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")
上级 2e6d9af6
...@@ -573,6 +573,7 @@ def extract_precision(sql_type) ...@@ -573,6 +573,7 @@ def extract_precision(sql_type)
end end
# See https://dev.mysql.com/doc/refman/5.7/en/server-error-reference.html # See https://dev.mysql.com/doc/refman/5.7/en/server-error-reference.html
ER_DB_CREATE_EXISTS = 1007
ER_DUP_ENTRY = 1062 ER_DUP_ENTRY = 1062
ER_NOT_NULL_VIOLATION = 1048 ER_NOT_NULL_VIOLATION = 1048
ER_NO_REFERENCED_ROW = 1216 ER_NO_REFERENCED_ROW = 1216
...@@ -592,6 +593,8 @@ def extract_precision(sql_type) ...@@ -592,6 +593,8 @@ def extract_precision(sql_type)
def translate_exception(exception, message:, sql:, binds:) def translate_exception(exception, message:, sql:, binds:)
case error_number(exception) case error_number(exception)
when ER_DB_CREATE_EXISTS
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
when ER_DUP_ENTRY when ER_DUP_ENTRY
RecordNotUnique.new(message, sql: sql, binds: binds) 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 when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
......
...@@ -467,6 +467,7 @@ def check_version # :nodoc: ...@@ -467,6 +467,7 @@ def check_version # :nodoc:
UNIQUE_VIOLATION = "23505" UNIQUE_VIOLATION = "23505"
SERIALIZATION_FAILURE = "40001" SERIALIZATION_FAILURE = "40001"
DEADLOCK_DETECTED = "40P01" DEADLOCK_DETECTED = "40P01"
DUPLICATE_DATABASE = "42P04"
LOCK_NOT_AVAILABLE = "55P03" LOCK_NOT_AVAILABLE = "55P03"
QUERY_CANCELED = "57014" QUERY_CANCELED = "57014"
...@@ -488,6 +489,8 @@ def translate_exception(exception, message:, sql:, binds:) ...@@ -488,6 +489,8 @@ def translate_exception(exception, message:, sql:, binds:)
SerializationFailure.new(message, sql: sql, binds: binds) SerializationFailure.new(message, sql: sql, binds: binds)
when DEADLOCK_DETECTED when DEADLOCK_DETECTED
Deadlocked.new(message, sql: sql, binds: binds) Deadlocked.new(message, sql: sql, binds: binds)
when DUPLICATE_DATABASE
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
when LOCK_NOT_AVAILABLE when LOCK_NOT_AVAILABLE
LockWaitTimeout.new(message, sql: sql, binds: binds) LockWaitTimeout.new(message, sql: sql, binds: binds)
when QUERY_CANCELED when QUERY_CANCELED
......
...@@ -187,6 +187,10 @@ class PreparedStatementInvalid < ActiveRecordError ...@@ -187,6 +187,10 @@ class PreparedStatementInvalid < ActiveRecordError
class NoDatabaseError < StatementInvalid class NoDatabaseError < StatementInvalid
end 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 # Raised when PostgreSQL returns 'cached plan must not change result type' and
# we cannot retry gracefully (e.g. inside a transaction) # we cannot retry gracefully (e.g. inside a transaction)
class PreparedStatementCacheExpired < StatementInvalid class PreparedStatementCacheExpired < StatementInvalid
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
module ActiveRecord module ActiveRecord
module Tasks # :nodoc: module Tasks # :nodoc:
class DatabaseAlreadyExists < StandardError; end # :nodoc:
class DatabaseNotSupported < StandardError; end # :nodoc: class DatabaseNotSupported < StandardError; end # :nodoc:
# ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates # ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
......
...@@ -15,12 +15,6 @@ def create ...@@ -15,12 +15,6 @@ def create
establish_connection configuration_without_database establish_connection configuration_without_database
connection.create_database configuration["database"], creation_options connection.create_database configuration["database"], creation_options
establish_connection configuration establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS
raise DatabaseAlreadyExists
else
raise
end
end end
def drop def drop
......
...@@ -21,12 +21,6 @@ def create(master_established = false) ...@@ -21,12 +21,6 @@ def create(master_established = false)
connection.create_database configuration["database"], connection.create_database configuration["database"],
configuration.merge("encoding" => encoding) configuration.merge("encoding" => encoding)
establish_connection configuration establish_connection configuration
rescue ActiveRecord::StatementInvalid => error
if error.cause.is_a?(PG::DuplicateDatabase)
raise DatabaseAlreadyExists
else
raise
end
end end
def drop def drop
......
...@@ -93,11 +93,9 @@ def test_create_when_database_exists_outputs_info_to_stderr ...@@ -93,11 +93,9 @@ def test_create_when_database_exists_outputs_info_to_stderr
with_stubbed_connection_establish_connection do with_stubbed_connection_establish_connection do
ActiveRecord::Base.connection.stub( ActiveRecord::Base.connection.stub(
:create_database, :create_database,
proc { raise ActiveRecord::StatementInvalid } proc { raise ActiveRecord::DatabaseAlreadyExists }
) do ) do
ActiveRecord::Base.connection.stub(:error_number, 1007) do ActiveRecord::Tasks::DatabaseTasks.create @configuration
ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
assert_equal "Database 'my-app-db' already exists\n", $stderr.string assert_equal "Database 'my-app-db' already exists\n", $stderr.string
end end
......
...@@ -129,7 +129,7 @@ def test_create_when_database_exists_outputs_info_to_stderr ...@@ -129,7 +129,7 @@ def test_create_when_database_exists_outputs_info_to_stderr
with_stubbed_connection_establish_connection do with_stubbed_connection_establish_connection do
ActiveRecord::Base.connection.stub( ActiveRecord::Base.connection.stub(
:create_database, :create_database,
proc { raise ActiveRecord::Tasks::DatabaseAlreadyExists } proc { raise ActiveRecord::DatabaseAlreadyExists }
) do ) do
ActiveRecord::Tasks::DatabaseTasks.create @configuration ActiveRecord::Tasks::DatabaseTasks.create @configuration
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册