Remove legacy Query support from _paginate_query

It relies on risky stuff like nested read transactions, which are known
to be problematic on SQLite.

Change-Id: I61a885c0cb7555919279b3e21e872752dcffc64b
This commit is contained in:
Dmitry Tantsur 2023-08-15 16:40:55 +02:00 committed by Doug Goldstein
parent 960b7ba393
commit d44a658836
No known key found for this signature in database
2 changed files with 29 additions and 41 deletions

View File

@ -362,6 +362,8 @@ def _paginate_query(model, limit=None, marker=None, sort_key=None,
# populated, so we need to ask if it is None. # populated, so we need to ask if it is None.
if query is None: if query is None:
query = sa.select(model) query = sa.select(model)
elif isinstance(query, sa_orm.Query):
raise TypeError("Use SQLAlchemy 2.0 queries")
sort_keys = ['id'] sort_keys = ['id']
if sort_key and sort_key not in sort_keys: if sort_key and sort_key not in sort_keys:
sort_keys.insert(0, sort_key) sort_keys.insert(0, sort_key)
@ -373,35 +375,19 @@ def _paginate_query(model, limit=None, marker=None, sort_key=None,
_('The sort_key value "%(key)s" is an invalid field for sorting') _('The sort_key value "%(key)s" is an invalid field for sorting')
% {'key': sort_key}) % {'key': sort_key})
with _session_for_read() as session: with _session_for_read() as session:
# NOTE(TheJulia): SQLAlchemy 2.0 no longer returns pre-uniqued result # We have a sqlalchemy.sql.selectable.Select
# sets in ORM mode, so we need to explicitly ask for it to be unique # (most likely) which utilizes the unified select interface.
# before returning it to the caller. res = session.execute(query).fetchall()
if isinstance(query, sa_orm.Query): if len(res) == 0:
# The classic "Legacy" ORM query object result set which is # Return an empty list instead of a class with no objects.
# deprecated in advance of SQLAlchemy 2.0. return []
# TODO(TheJulia): Calls of this style basically need to be if return_base_tuple:
# eliminated in ironic as returning this way does not allow # The caller expects a tuple, lets just give it to them.
# commit or rollback in enginefacade to occur until the returned return [tuple(r) for r in res]
# object is garbage collected as ORM Query objects allow # Everything is a tuple in a resultset from the unified interface
# for DB interactions to occur after the fact, so it remains # but for objects, our model expects just object access,
# connected to the DB.. # so we extract and return them.
# Save the query.all() results, but don't return yet, so we ref = [r[0] for r in res]
# begin to exit and unwind the session.
ref = query.all()
else:
# In this case, we have a sqlalchemy.sql.selectable.Select
# (most likely) which utilizes the unified select interface.
res = session.execute(query).fetchall()
if len(res) == 0:
# Return an empty list instead of a class with no objects.
return []
if return_base_tuple:
# The caller expects a tuple, lets just give it to them.
return [tuple(r) for r in res]
# Everything is a tuple in a resultset from the unified interface
# but for objects, our model expects just object access,
# so we extract and return them.
ref = [r[0] for r in res]
# Return the results to the caller, outside of the session context # Return the results to the caller, outside of the session context
# if an ORM object, because we want the session to close. # if an ORM object, because we want the session to close.
return ref return ref
@ -1098,12 +1084,10 @@ class Connection(api.Connection):
shard_node_ids = sa.select(models.Node) \ shard_node_ids = sa.select(models.Node) \
.where(models.Node.shard.in_(shards)) \ .where(models.Node.shard.in_(shards)) \
.with_only_columns(models.Node.id) .with_only_columns(models.Node.id)
with _session_for_read() as session: query = sa.select(models.Port) \
query = session.query(models.Port).filter( .where(models.Port.node_id.in_(shard_node_ids))
models.Port.node_id.in_(shard_node_ids)) return _paginate_query(
ports = _paginate_query( models.Port, limit, marker, sort_key, sort_dir, query)
models.Port, limit, marker, sort_key, sort_dir, query)
return ports
def get_ports_by_node_id(self, node_id, limit=None, marker=None, def get_ports_by_node_id(self, node_id, limit=None, marker=None,
sort_key=None, sort_dir=None, owner=None, sort_key=None, sort_dir=None, owner=None,
@ -2654,9 +2638,8 @@ class Connection(api.Connection):
def get_deploy_template_list(self, limit=None, marker=None, def get_deploy_template_list(self, limit=None, marker=None,
sort_key=None, sort_dir=None): sort_key=None, sort_dir=None):
with _session_for_read() as session: query = sa.select(models.DeployTemplate).options(
query = session.query(models.DeployTemplate).options( selectinload(models.DeployTemplate.steps))
selectinload(models.DeployTemplate.steps))
return _paginate_query(models.DeployTemplate, limit, marker, return _paginate_query(models.DeployTemplate, limit, marker,
sort_key, sort_dir, query) sort_key, sort_dir, query)
@ -2885,9 +2868,8 @@ class Connection(api.Connection):
def get_node_history_by_node_id(self, node_id, limit=None, marker=None, def get_node_history_by_node_id(self, node_id, limit=None, marker=None,
sort_key=None, sort_dir=None): sort_key=None, sort_dir=None):
with _session_for_read() as session: query = sa.select(models.NodeHistory) \
query = session.query(models.NodeHistory) .where(models.NodeHistory.node_id == node_id)
query = query.where(models.NodeHistory.node_id == node_id)
return _paginate_query(models.NodeHistory, limit, marker, return _paginate_query(models.NodeHistory, limit, marker,
sort_key, sort_dir, query) sort_key, sort_dir, query)

View File

@ -0,0 +1,6 @@
---
other:
- |
Removes support for pre-SQLAlchemy 2.0 query objects from the internal
database API. Downstream plug-ins must be adjusted to use the new-style
queries.