Cleaned up and optimized sqlalchemy base

- Cleaned up sql syntax.
- Fixed minor inconsistencies.
- Improved test coverage.

Change-Id: I5986dc9fdd1607119a71872637f836d211186c1e
This commit is contained in:
Erik Olof Gunnar Andersson 2022-10-09 00:02:14 -07:00
parent b929827271
commit 03c729c479
4 changed files with 140 additions and 116 deletions
designate

@ -78,7 +78,7 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
@property
def session(self):
# NOTE: This uses a thread local store, allowing each greenthread to
# have it's own session stored correctly. Without this, each
# have its own session stored correctly. Without this, each
# greenthread may end up using a single global session, which
# leads to bad things happening.
@ -101,51 +101,38 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
@staticmethod
def _apply_criterion(table, query, criterion):
if criterion is not None:
for name, value in criterion.items():
column = getattr(table.c, name)
if criterion is None:
return query
for name, value in criterion.items():
column = getattr(table.c, name)
if isinstance(value, str):
# Wildcard value: '%'
if isinstance(value, str) and '%' in value:
if '%' in value:
query = query.where(column.like(value))
elif (isinstance(value, str) and
value.startswith('!')):
queryval = value[1:]
query = query.where(column != queryval)
elif (isinstance(value, str) and
value.startswith('<=')):
queryval = value[2:]
query = query.where(column <= queryval)
elif (isinstance(value, str) and
value.startswith('<')):
queryval = value[1:]
query = query.where(column < queryval)
elif (isinstance(value, str) and
value.startswith('>=')):
queryval = value[2:]
query = query.where(column >= queryval)
elif (isinstance(value, str) and
value.startswith('>')):
queryval = value[1:]
query = query.where(column > queryval)
elif (isinstance(value, str) and
value.startswith('BETWEEN')):
elements = [i.strip(" ") for i in
value.split(" ", 1)[1].strip(" ").split(",")]
query = query.where(between(
column, elements[0], elements[1]))
elif isinstance(value, list):
query = query.where(column.in_(value))
elif value.startswith('!'):
query = query.where(column != value[1:])
elif value.startswith('<='):
query = query.where(column <= value[2:])
elif value.startswith('<'):
query = query.where(column < value[1:])
elif value.startswith('>='):
query = query.where(column >= value[2:])
elif value.startswith('>'):
query = query.where(column > value[1:])
elif value.startswith('BETWEEN'):
elements = [i.strip(' ') for i in
value.split(' ', 1)[1].strip(' ').split(',')]
query = query.where(
between(column, elements[0], elements[1])
)
else:
query = query.where(column == value)
elif isinstance(value, list):
query = query.where(column.in_(value))
else:
query = query.where(column == value)
return query
@ -211,8 +198,7 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
try:
resultproxy = self.session.execute(query, [dict(values)])
except oslo_db_exception.DBDuplicateEntry:
msg = "Duplicate %s" % obj.obj_name()
raise exc_dup(msg)
raise exc_dup("Duplicate %s" % obj.obj_name())
# Refetch the row, for generated columns etc
query = select([table]).where(
@ -247,8 +233,7 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
results = resultproxy.fetchall()
if len(results) != 1:
msg = "Could not find %s" % cls.obj_name()
raise exc_notfound(msg)
raise exc_notfound("Could not find %s" % cls.obj_name())
else:
return _set_object_from_model(cls(), results[0])
else:
@ -300,13 +285,17 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
records_table,
recordsets_table.c.id == records_table.c.recordset_id)
inner_q = select([recordsets_table.c.id, # 0 - RS ID
zones_table.c.name] # 1 - ZONE NAME
).select_from(rzjoin).\
inner_q = (
select([recordsets_table.c.id, # 0 - RS ID
zones_table.c.name]). # 1 - ZONE NAME
select_from(rzjoin).
where(zones_table.c.deleted == '0')
)
count_q = select([func.count(distinct(recordsets_table.c.id))]).\
count_q = (
select([func.count(distinct(recordsets_table.c.id))]).
select_from(rzjoin).where(zones_table.c.deleted == '0')
)
if index_hint:
inner_q = inner_q.with_hint(recordsets_table, index_hint,
@ -507,7 +496,7 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
current_rrset.records.append(rrdata)
else:
# We've already got an rrset, add the rdata
# We've already got a rrset, add the rdata
if record[r_map['id']] is not None:
rrdata = objects.Record()
@ -517,8 +506,8 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
current_rrset.records.append(rrdata)
# If the last record examined was a new rrset, or there is only 1 rrset
if len(rrsets) == 0 or \
(len(rrsets) != 0 and rrsets[-1] != current_rrset):
if (len(rrsets) == 0 or
(len(rrsets) != 0 and rrsets[-1] != current_rrset)):
if current_rrset is not None:
rrsets.append(current_rrset)
@ -539,9 +528,11 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
for skip_value in skip_values:
values.pop(skip_value, None)
query = table.update()\
.where(table.c.id == obj.id)\
.values(**values)
query = (
table.update().
where(table.c.id == obj.id).
values(**values)
)
query = self._apply_tenant_criteria(context, table, query)
query = self._apply_deleted_criteria(context, table, query)
@ -550,12 +541,10 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
try:
resultproxy = self.session.execute(query)
except oslo_db_exception.DBDuplicateEntry:
msg = "Duplicate %s" % obj.obj_name()
raise exc_dup(msg)
raise exc_dup("Duplicate %s" % obj.obj_name())
if resultproxy.rowcount != 1:
msg = "Could not find %s" % obj.obj_name()
raise exc_notfound(msg)
raise exc_notfound("Could not find %s" % obj.obj_name())
# Refetch the row, for generated columns etc
query = select([table]).where(table.c.id == obj.id)
@ -598,8 +587,7 @@ class SQLAlchemy(object, metaclass=abc.ABCMeta):
resultproxy = self.session.execute(query)
if resultproxy.rowcount != 1:
msg = "Could not find %s" % obj.obj_name()
raise exc_notfound(msg)
raise exc_notfound("Could not find %s" % obj.obj_name())
# Refetch the row, for generated columns etc
query = select([table]).where(table.c.id == obj.id)

@ -37,79 +37,79 @@ class SQLAlchemyTestCase(TestCase):
self.query = mock.Mock()
def test_wildcard(self):
criterion = {"a": "%foo%"}
criterion = {'a': '%foo%'}
op = dummy_table.c.a.like("%foo")
op = dummy_table.c.a.like('%foo')
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operators.like_op, "%foo%", escape=None)
func.assert_called_with(operators.like_op, '%foo%', escape=None)
self.query.where.assert_called_with(op)
def test_ne(self):
criterion = {"a": "!foo"}
criterion = {'a': '!foo'}
op = dummy_table.c.a != "foo"
op = dummy_table.c.a != 'foo'
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operator.ne, "foo")
func.assert_called_with(operator.ne, 'foo')
self.query.where.assert_called_with(op)
def test_le(self):
criterion = {"a": "<=foo"}
criterion = {'a': '<=foo'}
op = dummy_table.c.a <= "foo"
op = dummy_table.c.a <= 'foo'
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operator.le, "foo")
func.assert_called_with(operator.le, 'foo')
self.query.where.assert_called_with(op)
def test_lt(self):
criterion = {"a": "<foo"}
criterion = {'a': '<foo'}
op = dummy_table.c.a < "foo"
op = dummy_table.c.a < 'foo'
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operator.lt, "foo")
func.assert_called_with(operator.lt, 'foo')
self.query.where.assert_called_with(op)
def test_ge(self):
criterion = {"a": ">=foo"}
criterion = {'a': '>=foo'}
op = dummy_table.c.a >= "foo"
op = dummy_table.c.a >= 'foo'
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operator.ge, "foo")
func.assert_called_with(operator.ge, 'foo')
self.query.where.assert_called_with(op)
def test_gt(self):
criterion = {"a": ">foo"}
criterion = {'a': '>foo'}
op = dummy_table.c.a > "foo"
op = dummy_table.c.a > 'foo'
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operator.gt, "foo")
func.assert_called_with(operator.gt, 'foo')
self.query.where.assert_called_with(op)
def test_between(self):
criterion = {"a": "BETWEEN 1,3"}
criterion = {'a': 'BETWEEN 1,3'}
op = dummy_table.c.a.between(1, 3)
with mock.patch.object(dummy_table.c.a, 'operate') as func:
@ -120,3 +120,39 @@ class SQLAlchemyTestCase(TestCase):
func.assert_called_with(operators.between_op, '1', '3',
symmetric=False)
self.query.where.assert_called_with(op)
def test_regular_string(self):
criterion = {'a': 'foo'}
op = dummy_table.c.a.like('foo')
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operator.eq, 'foo')
self.query.where.assert_called_with(op)
def test_list(self):
criterion = {'a': ['foo']}
op = dummy_table.c.a.between(1, 3)
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operators.in_op, ['foo'])
self.query.where.assert_called_with(op)
def test_boolean(self):
criterion = {'a': True}
op = dummy_table.c.a.like('foo')
with mock.patch.object(dummy_table.c.a, 'operate') as func:
func.return_value = op
base.SQLAlchemy._apply_criterion(
dummy_table, self.query, criterion)
func.assert_called_with(operator.eq, True)
self.query.where.assert_called_with(op)

@ -1544,7 +1544,7 @@ class StorageTestCase(object):
def test_create_tld(self):
values = {
'name': 'com',
'description': u'This is a comment.'
'description': 'This is a comment.'
}
result = self.storage.create_tld(
@ -1869,8 +1869,8 @@ class StorageTestCase(object):
def test_create_pool_with_all_relations(self):
values = {
'name': u'Pool',
'description': u'Pool description',
'name': 'Pool',
'description': 'Pool description',
'attributes': [{'key': 'scope', 'value': 'public'}],
'ns_records': [{'priority': 1, 'hostname': 'ns1.example.org.'}],
'nameservers': [{'host': "192.0.2.1", 'port': 53}],
@ -2029,8 +2029,8 @@ class StorageTestCase(object):
def test_update_pool_with_all_relations(self):
values = {
'name': u'Pool-A',
'description': u'Pool-A description',
'name': 'Pool-A',
'description': 'Pool-A description',
'attributes': [{'key': 'scope', 'value': 'public'}],
'ns_records': [{'priority': 1, 'hostname': 'ns1.example.org.'}],
'nameservers': [{'host': "192.0.2.1", 'port': 53}],
@ -2054,8 +2054,8 @@ class StorageTestCase(object):
# we trigger an update rather than a create.
values = {
'id': created_pool_id,
'name': u'Pool-B',
'description': u'Pool-B description',
'name': 'Pool-B',
'description': 'Pool-B description',
'attributes': [{'key': 'scope', 'value': 'private'}],
'ns_records': [{'priority': 1, 'hostname': 'ns2.example.org.'}],
'nameservers': [{'host': "192.0.2.5", 'port': 53}],
@ -2534,7 +2534,7 @@ class StorageTestCase(object):
pool = self.create_pool(fixture=0)
# Create 10 PoolTargets
created = [self.create_pool_target(pool, description=u'Target %d' % i)
created = [self.create_pool_target(pool, description='Target %d' % i)
for i in range(10)]
# Ensure we can page through the results.
@ -2546,9 +2546,9 @@ class StorageTestCase(object):
# Create two pool_targets
pool_target_one = self.create_pool_target(
pool, fixture=0, description=u'One')
pool, fixture=0, description='One')
pool_target_two = self.create_pool_target(
pool, fixture=1, description=u'Two')
pool, fixture=1, description='Two')
# Verify pool_target_one
criterion = dict(description=pool_target_one['description'])
@ -2588,9 +2588,9 @@ class StorageTestCase(object):
# Create two pool_targets
pool_target_one = self.create_pool_target(
pool, fixture=0, description=u'One')
pool, fixture=0, description='One')
pool_target_two = self.create_pool_target(
pool, fixture=1, description=u'Two')
pool, fixture=1, description='Two')
# Verify pool_target_one
criterion = dict(description=pool_target_one['description'])
@ -2622,16 +2622,16 @@ class StorageTestCase(object):
def test_update_pool_target(self):
pool = self.create_pool(fixture=0)
pool_target = self.create_pool_target(pool, description=u'One')
pool_target = self.create_pool_target(pool, description='One')
# Update the pool_target
pool_target.description = u'Two'
pool_target.description = 'Two'
pool_target = self.storage.update_pool_target(
self.admin_context, pool_target)
# Verify the new values
self.assertEqual(u'Two', pool_target.description)
self.assertEqual('Two', pool_target.description)
# Ensure the version column was incremented
self.assertEqual(2, pool_target.version)

@ -30,27 +30,27 @@ class SqlalchemyStorageTest(StorageTestCase, TestCase):
def test_schema_table_names(self):
table_names = [
u'blacklists',
u'pool_also_notifies',
u'pool_attributes',
u'pool_nameservers',
u'pool_ns_records',
u'pool_target_masters',
u'pool_target_options',
u'pool_targets',
u'pools',
u'quotas',
u'records',
u'recordsets',
u'service_statuses',
u'tlds',
u'tsigkeys',
u'zone_attributes',
u'zone_masters',
u'zone_tasks',
u'zone_transfer_accepts',
u'zone_transfer_requests',
u'zones'
'blacklists',
'pool_also_notifies',
'pool_attributes',
'pool_nameservers',
'pool_ns_records',
'pool_target_masters',
'pool_target_options',
'pool_targets',
'pools',
'quotas',
'records',
'recordsets',
'service_statuses',
'tlds',
'tsigkeys',
'zone_attributes',
'zone_masters',
'zone_tasks',
'zone_transfer_accepts',
'zone_transfer_requests',
'zones'
]
inspector = self.storage.get_inspector()