summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--oslo_db/sqlalchemy/engines.py28
-rw-r--r--oslo_db/sqlalchemy/exc_filters.py89
-rw-r--r--oslo_db/sqlalchemy/provision.py10
-rw-r--r--oslo_db/sqlalchemy/utils.py63
-rw-r--r--oslo_db/tests/sqlalchemy/test_exc_filters.py11
-rw-r--r--oslo_db/tests/sqlalchemy/test_utils.py18
-rw-r--r--oslo_db/tests/test_api.py6
7 files changed, 52 insertions, 173 deletions
diff --git a/oslo_db/sqlalchemy/engines.py b/oslo_db/sqlalchemy/engines.py
index 7c36c8a..847c71a 100644
--- a/oslo_db/sqlalchemy/engines.py
+++ b/oslo_db/sqlalchemy/engines.py
@@ -22,7 +22,6 @@ import logging
import os
import re
import time
-from urllib import parse
import debtcollector.removals
import debtcollector.renames
@@ -130,31 +129,6 @@ def _setup_logging(connection_debug=0):
logger.setLevel(logging.WARNING)
-def _extend_url_parameters(url, connection_parameters):
- # TODO(zzzeek): remove hasattr() conditional when SQLAlchemy 1.4 is the
- # minimum version in requirements; call update_query_string()
- # unconditionally
- if hasattr(url, "update_query_string"):
- return url.update_query_string(connection_parameters, append=True)
-
- # TODO(zzzeek): remove the remainder of this method when SQLAlchemy 1.4
- # is the minimum version in requirements
- for key, value in parse.parse_qs(
- connection_parameters).items():
- if key in url.query:
- existing = url.query[key]
- if not isinstance(existing, list):
- url.query[key] = existing = utils.to_list(existing)
- existing.extend(value)
- value = existing
- else:
- url.query[key] = value
- if len(value) == 1:
- url.query[key] = value[0]
-
- return url
-
-
def _vet_url(url):
if "+" not in url.drivername and not url.drivername.startswith("sqlite"):
if url.drivername.startswith("mysql"):
@@ -201,7 +175,7 @@ def create_engine(sql_connection, sqlite_fk=False, mysql_sql_mode=None,
url = utils.make_url(sql_connection)
if connection_parameters:
- url = _extend_url_parameters(url, connection_parameters)
+ url = url.update_query_string(connection_parameters, append=True)
_vet_url(url)
diff --git a/oslo_db/sqlalchemy/exc_filters.py b/oslo_db/sqlalchemy/exc_filters.py
index 420b5c7..4ad7721 100644
--- a/oslo_db/sqlalchemy/exc_filters.py
+++ b/oslo_db/sqlalchemy/exc_filters.py
@@ -44,7 +44,7 @@ def filters(dbname, exception_type, regex):
"""
def _receive(fn):
_registry[dbname][exception_type].extend(
- (fn, re.compile(reg))
+ (fn, re.compile(reg, re.DOTALL))
for reg in
((regex,) if not isinstance(regex, tuple) else regex)
)
@@ -432,50 +432,51 @@ def handler(context):
dialect = compat.dialect_from_exception_context(context)
for per_dialect in _dialect_registries(dialect):
- for exc in (
- context.sqlalchemy_exception,
- context.original_exception):
+ for exc in (context.sqlalchemy_exception, context.original_exception):
for super_ in exc.__class__.__mro__:
- if super_ in per_dialect:
- regexp_reg = per_dialect[super_]
- for fn, regexp in regexp_reg:
- match = regexp.match(exc.args[0])
- if match:
- try:
- fn(
- exc,
- match,
- dialect.name,
- context.is_disconnect)
- except exception.DBError as dbe:
- if (
- context.connection is not None and
- not context.connection.closed and
- not context.connection.invalidated and
- ROLLBACK_CAUSE_KEY
- in context.connection.info
- ):
- dbe.cause = \
- context.connection.info.pop(
- ROLLBACK_CAUSE_KEY)
-
- if isinstance(
- dbe, exception.DBConnectionError):
- context.is_disconnect = True
-
- # new in 2.0.5
- if (
- hasattr(context, "is_pre_ping") and
- context.is_pre_ping
- ):
- # if this is a pre-ping, need to
- # integrate with the built
- # in pre-ping handler that doesnt know
- # about DBConnectionError, just needs
- # the updated status
- return None
-
- return dbe
+ if super_ not in per_dialect:
+ continue
+
+ regexp_reg = per_dialect[super_]
+ for fn, regexp in regexp_reg:
+ match = regexp.match(exc.args[0])
+ if not match:
+ continue
+
+ try:
+ fn(
+ exc,
+ match,
+ dialect.name,
+ context.is_disconnect,
+ )
+ except exception.DBError as dbe:
+ if (
+ context.connection is not None and
+ not context.connection.closed and
+ not context.connection.invalidated and
+ ROLLBACK_CAUSE_KEY in context.connection.info
+ ):
+ dbe.cause = context.connection.info.pop(
+ ROLLBACK_CAUSE_KEY,
+ )
+
+ if isinstance(dbe, exception.DBConnectionError):
+ context.is_disconnect = True
+
+ # new in 2.0.5
+ if (
+ hasattr(context, "is_pre_ping") and
+ context.is_pre_ping
+ ):
+ # if this is a pre-ping, need to
+ # integrate with the built
+ # in pre-ping handler that doesnt know
+ # about DBConnectionError, just needs
+ # the updated status
+ return None
+
+ return dbe
def register_engine(engine):
diff --git a/oslo_db/sqlalchemy/provision.py b/oslo_db/sqlalchemy/provision.py
index a6cc527..96e050c 100644
--- a/oslo_db/sqlalchemy/provision.py
+++ b/oslo_db/sqlalchemy/provision.py
@@ -493,15 +493,7 @@ class BackendImpl(object, metaclass=abc.ABCMeta):
"""
url = utils.make_url(base_url)
-
- # TODO(zzzeek): remove hasattr() conditional in favor of "url.set()"
- # when SQLAlchemy 1.4 is the minimum version in requirements
- if hasattr(url, "set"):
- url = url.set(database=ident)
- else:
- # TODO(zzzeek): remove when SQLAlchemy 1.4
- # is the minimum version in requirements
- url.database = ident
+ url = url.set(database=ident)
return url
diff --git a/oslo_db/sqlalchemy/utils.py b/oslo_db/sqlalchemy/utils.py
index 58b2486..f8f57c4 100644
--- a/oslo_db/sqlalchemy/utils.py
+++ b/oslo_db/sqlalchemy/utils.py
@@ -29,8 +29,6 @@ import debtcollector.removals
from oslo_utils import timeutils
import sqlalchemy
from sqlalchemy import Boolean
-from sqlalchemy import CheckConstraint
-from sqlalchemy import Column
from sqlalchemy.engine import Connectable
from sqlalchemy.engine import url as sa_url
from sqlalchemy import exc
@@ -42,7 +40,6 @@ from sqlalchemy import MetaData
from sqlalchemy.sql.expression import cast
from sqlalchemy.sql.expression import literal_column
from sqlalchemy.sql import text
-from sqlalchemy import String
from sqlalchemy import Table
from oslo_db._i18n import _
@@ -443,23 +440,6 @@ def get_table(engine, name):
return Table(name, metadata, autoload_with=engine)
-def _get_not_supported_column(col_name_col_instance, column_name):
- try:
- column = col_name_col_instance[column_name]
- except KeyError:
- msg = _("Please specify column %s in col_name_col_instance "
- "param. It is required because column has unsupported "
- "type by SQLite.")
- raise exception.ColumnError(msg % column_name)
-
- if not isinstance(column, Column):
- msg = _("col_name_col_instance param has wrong type of "
- "column instance for column %s It should be instance "
- "of sqlalchemy.Column.")
- raise exception.ColumnError(msg % column_name)
- return column
-
-
def drop_old_duplicate_entries_from_table(engine, table_name,
use_soft_delete, *uc_column_names):
"""Drop all old rows having the same values for columns in uc_columns.
@@ -519,49 +499,6 @@ def drop_old_duplicate_entries_from_table(engine, table_name,
conn.execute(delete_statement)
-def _get_default_deleted_value(table):
- if isinstance(table.c.id.type, Integer):
- return 0
- if isinstance(table.c.id.type, String):
- return ""
- raise exception.ColumnError(_("Unsupported id columns type"))
-
-
-def _restore_indexes_on_deleted_columns(engine, table_name, indexes):
- table = get_table(engine, table_name)
-
- real_indexes = get_indexes(engine, table_name)
- existing_index_names = dict(
- [(index['name'], index['column_names']) for index in real_indexes])
-
- # NOTE(boris-42): Restore indexes on `deleted` column
- for index in indexes:
- if 'deleted' not in index['column_names']:
- continue
- name = index['name']
- if name in existing_index_names:
- column_names = [table.c[c] for c in existing_index_names[name]]
- old_index = Index(name, *column_names, unique=index["unique"])
- old_index.drop(engine)
-
- column_names = [table.c[c] for c in index['column_names']]
- new_index = Index(index["name"], *column_names, unique=index["unique"])
- new_index.create(engine)
-
-
-def _is_deleted_column_constraint(constraint):
- # NOTE(boris-42): There is no other way to check is CheckConstraint
- # associated with deleted column.
- if not isinstance(constraint, CheckConstraint):
- return False
- sqltext = str(constraint.sqltext)
- # NOTE(zzzeek): SQLite never reflected CHECK contraints here
- # in any case until version 1.1. Safe to assume that any CHECK
- # that's talking about the value of "deleted in (something)" is
- # the boolean constraint we're looking to get rid of.
- return bool(re.match(r".*deleted in \(.*\)", sqltext, re.I))
-
-
def get_db_connection_info(conn_pieces):
database = conn_pieces.path.strip('/')
loc_pieces = conn_pieces.netloc.split('@')
diff --git a/oslo_db/tests/sqlalchemy/test_exc_filters.py b/oslo_db/tests/sqlalchemy/test_exc_filters.py
index 796ba6c..816ef7e 100644
--- a/oslo_db/tests/sqlalchemy/test_exc_filters.py
+++ b/oslo_db/tests/sqlalchemy/test_exc_filters.py
@@ -416,16 +416,7 @@ class TestNonExistentDatabase(
super(TestNonExistentDatabase, self).setUp()
url = utils.make_url(self.engine.url)
-
- # TODO(zzzeek): remove hasattr() conditional in favor of "url.set()"
- # when SQLAlchemy 1.4 is the minimum version in requirements
- if hasattr(url, "set"):
- self.url = url.set(database="non_existent_database")
- else:
- # TODO(zzzeek): remove when SQLAlchemy 1.4
- # is the minimum version in requirements
- url.database = 'non_existent_database'
- self.url = url
+ self.url = url.set(database="non_existent_database")
def test_raise(self):
matched = self.assertRaises(
diff --git a/oslo_db/tests/sqlalchemy/test_utils.py b/oslo_db/tests/sqlalchemy/test_utils.py
index 059d015..94a52eb 100644
--- a/oslo_db/tests/sqlalchemy/test_utils.py
+++ b/oslo_db/tests/sqlalchemy/test_utils.py
@@ -19,7 +19,6 @@ from urllib import parse
import fixtures
import sqlalchemy
from sqlalchemy import Boolean, Index, Integer, DateTime, String
-from sqlalchemy import CheckConstraint
from sqlalchemy import MetaData, Table, Column
from sqlalchemy import ForeignKey, ForeignKeyConstraint
from sqlalchemy.dialects.postgresql import psycopg2
@@ -801,23 +800,6 @@ class TestMigrationUtils(db_test_base._DbTestCase):
for value in soft_deleted_values:
self.assertIn(value['id'], deleted_rows_ids)
- def test_detect_boolean_deleted_constraint_detection(self):
- table_name = 'abc'
- table = Table(table_name, self.meta,
- Column('id', Integer, primary_key=True),
- Column('deleted', Boolean(create_constraint=True)))
- ck = [
- const for const in table.constraints if
- isinstance(const, CheckConstraint)][0]
-
- self.assertTrue(utils._is_deleted_column_constraint(ck))
-
- self.assertFalse(
- utils._is_deleted_column_constraint(
- CheckConstraint("deleted > 5")
- )
- )
-
def test_get_foreign_key_constraint_name(self):
table_1 = Table('table_name_1', self.meta,
Column('id', Integer, primary_key=True),
diff --git a/oslo_db/tests/test_api.py b/oslo_db/tests/test_api.py
index 2540780..d45dce6 100644
--- a/oslo_db/tests/test_api.py
+++ b/oslo_db/tests/test_api.py
@@ -210,7 +210,8 @@ class DBRetryRequestCase(DBAPITestCase):
some_method()
- def test_retry_wrapper_reaches_limit(self):
+ @mock.patch('oslo_db.api.time.sleep', return_value=None)
+ def test_retry_wrapper_reaches_limit(self, mock_sleep):
max_retries = 2
@api.wrap_db_retry(max_retries=max_retries)
@@ -222,7 +223,8 @@ class DBRetryRequestCase(DBAPITestCase):
self.assertRaises(ValueError, some_method, res)
self.assertEqual(max_retries + 1, res['result'])
- def test_retry_wrapper_exception_checker(self):
+ @mock.patch('oslo_db.api.time.sleep', return_value=None)
+ def test_retry_wrapper_exception_checker(self, mock_sleep):
def exception_checker(exc):
return isinstance(exc, ValueError) and exc.args[0] < 5