diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-03-17 11:04:58 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-03-17 13:56:17 -0400 |
| commit | 5f9c45400556f821550e7a39331f1bd5af5a34ce (patch) | |
| tree | f35043d3f4e416eb3734908952da55caefcda994 /lib/sqlalchemy/sql/elements.py | |
| parent | ecb392c5f927ab117f9704ce373bf2af1dbe5b69 (diff) | |
| download | sqlalchemy-5f9c45400556f821550e7a39331f1bd5af5a34ce.tar.gz | |
Use explicit names for mapper _get_clause parameters
Fixed a critical regression in the relationship lazy loader where the SQL
criteria used to fetch a related many-to-one object could go stale in
relation to other memoized structures within the loader if the mapper had
configuration changes, such as can occur when mappers are late configured
or configured on demand, producing a comparison to None and returning no
object. Huge thanks to Alan Hamlett for their help tracking this down late
into the night.
The primary change is that mapper._get_clause() uses a fixed name
for its bound parameters, which is memoized under a lambda statement
in the case of many-to-one lazy loading. This has implications for some other
logic namely the .compare() used by loader strategies to determine
use_get needed to be adjusted.
This change also repairs the lambda module's behavior of removing
the "required" flag from bound parameters, which caused this issue
to also fail silently rather than issuing an error for a required
bind parameter.
Fixes: #6055
Change-Id: I19e1aba9207a049873e0f13c19bad7541e223cfd
Diffstat (limited to 'lib/sqlalchemy/sql/elements.py')
| -rw-r--r-- | lib/sqlalchemy/sql/elements.py | 6 |
1 files changed, 3 insertions, 3 deletions
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index b26918f2f..29023c9fe 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -1400,14 +1400,14 @@ class BindParameter(roles.InElementRole, ColumnElement): else: self.type = type_ - def _with_value(self, value, maintain_key=False): + def _with_value(self, value, maintain_key=False, required=NO_ARG): """Return a copy of this :class:`.BindParameter` with the given value set. """ cloned = self._clone(maintain_key=maintain_key) cloned.value = value cloned.callable = None - cloned.required = False + cloned.required = required if required is not NO_ARG else self.required if cloned.type is type_api.NULLTYPE: cloned.type = type_api._resolve_value_to_type(value) return cloned @@ -1826,7 +1826,7 @@ class TextClause( replace_context=err, ) else: - new_params[key] = existing._with_value(value) + new_params[key] = existing._with_value(value, required=False) @util.preload_module("sqlalchemy.sql.selectable") def columns(self, *cols, **types): |
