summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/base.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-10-19 14:07:32 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2021-10-19 15:10:14 -0400
commit18b4a3437a60fbfa0c25287d9a3b83d7c9d4f762 (patch)
treec9c5758b40c29cf67fe38674f0e2893912dda937 /lib/sqlalchemy/sql/base.py
parente86c40b9254f25ca9a765622f911c6dbd4bd2f1f (diff)
downloadsqlalchemy-18b4a3437a60fbfa0c25287d9a3b83d7c9d4f762.tar.gz
process bulk_update_tuples before cache key or compilation
Fixed regression where the use of a :class:`_orm.hybrid_property` attribute or a mapped :func:`_orm.composite` attribute as a key passed to the :meth:`_dml.Update.values` method for an ORM-enabled :class:`_dml.Update` statement, as well as when using it via the legacy :meth:`_orm.Query.update` method, would be processed for incoming ORM/hybrid/composite values within the compilation stage of the UPDATE statement, which meant that in those cases where caching occurred, subsequent invocations of the same statement would no longer receive the correct values. This would include not only hybrids that use the :meth:`_orm.hybrid_property.update_expression` method, but any use of a plain hybrid attribute as well. For composites, the issue instead caused a non-repeatable cache key to be generated, which would break caching and could fill up the statement cache with repeated statements. The :class:`_dml.Update` construct now handles the processing of key/value pairs passed to :meth:`_dml.Update.values` and :meth:`_dml.Update.ordered_values` up front when the construct is first generated, before the cache key has been generated so that the key/value pairs are processed each time, and so that the cache key is generated against the individual column/value pairs that will ultimately be used in the statement. Fixes: #7209 Change-Id: I08f248d1d60ea9690b014c21439b775d951fb9e5
Diffstat (limited to 'lib/sqlalchemy/sql/base.py')
-rw-r--r--lib/sqlalchemy/sql/base.py23
1 files changed, 17 insertions, 6 deletions
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py
index b235f5132..aba80222a 100644
--- a/lib/sqlalchemy/sql/base.py
+++ b/lib/sqlalchemy/sql/base.py
@@ -515,12 +515,20 @@ class CompileState(object):
@classmethod
def get_plugin_class(cls, statement):
plugin_name = statement._propagate_attrs.get(
- "compile_state_plugin", "default"
+ "compile_state_plugin", None
)
+
+ if plugin_name:
+ key = (plugin_name, statement._effective_plugin_target)
+ if key in cls.plugins:
+ return cls.plugins[key]
+
+ # there's no case where we call upon get_plugin_class() and want
+ # to get None back, there should always be a default. return that
+ # if there was no plugin-specific class (e.g. Insert with "orm"
+ # plugin)
try:
- return cls.plugins[
- (plugin_name, statement._effective_plugin_target)
- ]
+ return cls.plugins[("default", statement._effective_plugin_target)]
except KeyError:
return None
@@ -1665,7 +1673,7 @@ def _entity_namespace(entity):
raise
-def _entity_namespace_key(entity, key):
+def _entity_namespace_key(entity, key, default=NO_ARG):
"""Return an entry from an entity_namespace.
@@ -1676,7 +1684,10 @@ def _entity_namespace_key(entity, key):
try:
ns = _entity_namespace(entity)
- return getattr(ns, key)
+ if default is not NO_ARG:
+ return getattr(ns, key, default)
+ else:
+ return getattr(ns, key)
except AttributeError as err:
util.raise_(
exc.InvalidRequestError(