Add a specific exception for 'unknown database' errors
Add an exception filter to distinguish 'unknown database' errors from other DBAPIError's. This effectively means we no longer unconditionally print a traceback to logs, when someone tries to connect to a database, that does not exist, but just raise a specific exception instead and let the caller handle it. Closes-Bug: #1631033 Change-Id: Iebc1ec1e8153917fbcde116d04bc171272993bbd
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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<database>.+)'\"")
|
||||
@filters("mysql", sqla_exc.OperationalError,
|
||||
r".*1049,.*Unknown database '(?P<database>.+)'\"")
|
||||
@filters("postgresql", sqla_exc.OperationalError,
|
||||
r".*database \"(?P<database>.+)\" 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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user