diff --git a/oslo_db/exception.py b/oslo_db/exception.py index 2d118cd..e66fe98 100644 --- a/oslo_db/exception.py +++ b/oslo_db/exception.py @@ -155,6 +155,18 @@ class DBNonExistentTable(DBError): super(DBNonExistentTable, self).__init__(inner_exception) +class DBNonExistentDatabase(DBError): + """Database does not exist. + + :param database: database name + :type database: str + """ + + def __init__(self, database, inner_exception=None): + self.database = database + super(DBNonExistentDatabase, self).__init__(inner_exception) + + class DBDeadlock(DBError): """Database dead lock error. diff --git a/oslo_db/sqlalchemy/exc_filters.py b/oslo_db/sqlalchemy/exc_filters.py index 9e5a2e5..884d26d 100644 --- a/oslo_db/sqlalchemy/exc_filters.py +++ b/oslo_db/sqlalchemy/exc_filters.py @@ -285,6 +285,24 @@ def _check_table_non_existing( raise exception.DBNonExistentTable(match.group("table"), programming_error) +@filters("mysql", sqla_exc.InternalError, + r".*1049,.*Unknown database '(?P.+)'\"") +@filters("mysql", sqla_exc.OperationalError, + r".*1049,.*Unknown database '(?P.+)'\"") +@filters("postgresql", sqla_exc.OperationalError, + r".*database \"(?P.+)\" does not exist") +@filters("sqlite", sqla_exc.OperationalError, + ".*unable to open database file.*") +def _check_database_non_existing( + error, match, engine_name, is_disconnect): + try: + database = match.group("database") + except IndexError: + database = None + + raise exception.DBNonExistentDatabase(database, error) + + @filters("ibm_db_sa", sqla_exc.IntegrityError, r"^.*SQL0803N.*$") def _db2_dupe_key_error(integrity_error, match, engine_name, is_disconnect): """Filter for DB2 duplicate key errors. diff --git a/oslo_db/tests/sqlalchemy/test_exc_filters.py b/oslo_db/tests/sqlalchemy/test_exc_filters.py index e31f8dd..5eef4d1 100644 --- a/oslo_db/tests/sqlalchemy/test_exc_filters.py +++ b/oslo_db/tests/sqlalchemy/test_exc_filters.py @@ -21,6 +21,7 @@ import mock from oslotest import base as oslo_test_base import six import sqlalchemy as sqla +from sqlalchemy.engine import url as sqla_url from sqlalchemy import event import sqlalchemy.exc from sqlalchemy.ext.declarative import declarative_base @@ -379,6 +380,69 @@ class TestNonExistentTableMySQL( self.assertEqual("foo", matched.table) +class TestNonExistentDatabase( + _SQLAExceptionMatcher, + test_base.DbTestCase): + + def setUp(self): + super(TestNonExistentDatabase, self).setUp() + + url = sqla_url.make_url(str(self.engine.url)) + url.database = 'non_existent_database' + self.url = url + + def test_raise(self): + matched = self.assertRaises( + exception.DBNonExistentDatabase, + engines.create_engine, + sqla_url.make_url( + 'sqlite:////non_existent_dir/non_existent_database') + ) + self.assertIsNone(matched.database) + self.assertInnerException( + matched, + sqlalchemy.exc.OperationalError, + 'unable to open database file', + ) + + +class TestNonExistentDatabaseMySQL( + TestNonExistentDatabase, + test_base.MySQLOpportunisticTestCase): + + def test_raise(self): + matched = self.assertRaises( + exception.DBNonExistentDatabase, + engines.create_engine, + self.url + ) + self.assertEqual('non_existent_database', matched.database) + # NOTE(rpodolyaka) cannot check precisely with assertInnerException + # since MySQL errors are not the same depending on its version + self.assertIsInstance( + matched.inner_exception, + (sqlalchemy.exc.InternalError, sqlalchemy.exc.OperationalError), + ) + + +class TestNonExistentDatabasePostgreSQL( + TestNonExistentDatabase, + test_base.PostgreSQLOpportunisticTestCase): + + def test_raise(self): + matched = self.assertRaises( + exception.DBNonExistentDatabase, + engines.create_engine, + self.url + ) + self.assertEqual('non_existent_database', matched.database) + self.assertInnerException( + matched, + sqlalchemy.exc.OperationalError, + 'fatal: database "non_existent_database" does not exist\n', + ) + + class TestReferenceErrorSQLite(_SQLAExceptionMatcher, test_base.DbTestCase): def setUp(self):