Skip to content

Commit 5b1d487

Browse files
stephenfinmelwitt
authored andcommitted
db: Resolve additional SAWarning warnings
Resolving the following SAWarning warnings: Coercing Subquery object into a select() for use in IN(); please pass a select() construct explicitly SELECT statement has a cartesian product between FROM element(s) "foo" and FROM element "bar". Apply join condition(s) between each element to resolve. While the first of these was a trivial fix, the second one is a little more involved. It was caused by attempting to build a query across tables that had no relationship as part of our archive logic. For example, consider the following queries, generated early in '_get_fk_stmts': SELECT instances.uuid FROM instances, security_group_instance_association WHERE security_group_instance_association.instance_uuid = instances.uuid AND instances.id IN (__[POSTCOMPILE_id_1]) SELECT security_groups.id FROM security_groups, security_group_instance_association, instances WHERE security_group_instance_association.security_group_id = security_groups.id AND instances.id IN (__[POSTCOMPILE_id_1]) While the first of these is fine, the second is clearly wrong: why are we filtering on a field that is of no relevance to our join? These were generated because we were attempting to archive one or more instances (in this case, the instance with id=1) and needed to find related tables to archive at the same time. A related table is any table that references our "source" table - 'instances' here - by way of a foreign key. For each of *these* tables, we then lookup each foreign key and join back to the source table, filtering by matching entries in the source table. The issue here is that we're looking up every foreign key. What we actually want to do is lookup only the foreign keys that point back to our source table. This flaw is why we were generating the second SELECT above: the 'security_group_instance_association' has two foreign keys, one pointing to our 'instances' table but also another pointing to the 'security_groups' table. We want the first but not the second. Resolve this by checking if the table that each foreign key points to is actually the source table and simply skip if not. With this issue resolved, we can enable errors on SAWarning warnings in general without any filters. Conflicts: nova/tests/fixtures/nova.py NOTE(melwitt): The conflict is because change I63f57980e01f472a25821790610f0836f1882a7f (tests: Restore - don't reset - warning filters) is not in Xena. Change-Id: I63208c7bd5f9f4c3d5e4a40bd0f6253d0f042a37 Signed-off-by: Stephen Finucane <[email protected]> (cherry picked from commit 8142b9d) (cherry picked from commit ce2cc54)
1 parent f25cebf commit 5b1d487

File tree

3 files changed

+25
-4
lines changed

3 files changed

+25
-4
lines changed

nova/db/main/api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4332,6 +4332,12 @@ def _get_fk_stmts(metadata, conn, table, column, records):
43324332
fk_column = fk_table.c.id
43334333

43344334
for fk in fk_table.foreign_keys:
4335+
if table != fk.column.table:
4336+
# if the foreign key doesn't actually point to the table we're
4337+
# archiving entries from then it's not relevant; trying to
4338+
# resolve this would result in a cartesian product
4339+
continue
4340+
43354341
# We need to find the records in the referring (child) table that
43364342
# correspond to the records in our (parent) table so we can archive
43374343
# them.
@@ -4378,6 +4384,7 @@ def _get_fk_stmts(metadata, conn, table, column, records):
43784384
# deque.
43794385
fk_delete = fk_table.delete().where(fk_column.in_(fk_records))
43804386
deletes.appendleft(fk_delete)
4387+
43814388
# Repeat for any possible nested child tables.
43824389
i, d = _get_fk_stmts(metadata, conn, fk_table, fk_column, fk_records)
43834390
inserts.extendleft(i)

nova/objects/cell_mapping.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,15 @@ def _get_by_project_id_from_db(context, project_id):
279279
# SELECT DISTINCT cell_id FROM instance_mappings \
280280
# WHERE project_id = $project_id;
281281
cell_ids = context.session.query(
282-
api_db_models.InstanceMapping.cell_id).filter_by(
283-
project_id=project_id).distinct().subquery()
282+
api_db_models.InstanceMapping.cell_id
283+
).filter_by(
284+
project_id=project_id
285+
).distinct()
284286
# SELECT cell_mappings WHERE cell_id IN ($cell_ids);
285-
return context.session.query(api_db_models.CellMapping).filter(
286-
api_db_models.CellMapping.id.in_(cell_ids)).all()
287+
return context.session.query(
288+
api_db_models.CellMapping).filter(
289+
api_db_models.CellMapping.id.in_(cell_ids)
290+
).all()
287291

288292
@classmethod
289293
def get_by_project_id(cls, context, project_id):

nova/tests/fixtures/nova.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,16 @@ def setUp(self):
833833

834834
self.addCleanup(warnings.resetwarnings)
835835

836+
# Enable general SQLAlchemy warnings also to ensure we're not doing
837+
# silly stuff. It's possible that we'll need to filter things out here
838+
# with future SQLAlchemy versions, but that's a good thing
839+
840+
warnings.filterwarnings(
841+
'error',
842+
module='nova',
843+
category=sqla_exc.SAWarning,
844+
)
845+
836846

837847
class ConfPatcher(fixtures.Fixture):
838848
"""Fixture to patch and restore global CONF.

0 commit comments

Comments
 (0)