summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-05-10 22:52:49 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2021-05-10 23:42:41 -0400
commitb146a0c64144639bf02bafda239238e3a8f5c84d (patch)
treec9c21a160e14aa177a40f11cf436bc0b2e160659 /lib/sqlalchemy/sql
parent39c2815fb25052c181f98ca52a57fd7449d7090c (diff)
downloadsqlalchemy-b146a0c64144639bf02bafda239238e3a8f5c84d.tar.gz
set bindparam.expanding in coercion again
Adjusted the logic added as part of :ticket:`6397` in 1.4.12 so that internal mutation of the :class:`.BindParameter` object occurs within the clause construction phase as it did before, rather than in the compilation phase. In the latter case, the mutation still produced side effects against the incoming construct and additionally could potentially interfere with other internal mutation routines. In order to solve the issue of the correct operator being present on the BindParameter.expand_op, we necessarily have to expand the BinaryExpression._negate() routine to flip the operator on the BindParameter also. Fixes: #6460 Change-Id: I1e53a9aeee4de4fc11af51d7593431532731561b
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/coercions.py7
-rw-r--r--lib/sqlalchemy/sql/compiler.py22
-rw-r--r--lib/sqlalchemy/sql/elements.py19
-rw-r--r--lib/sqlalchemy/sql/lambdas.py2
4 files changed, 26 insertions, 24 deletions
diff --git a/lib/sqlalchemy/sql/coercions.py b/lib/sqlalchemy/sql/coercions.py
index 820fc1bf1..517bfd57d 100644
--- a/lib/sqlalchemy/sql/coercions.py
+++ b/lib/sqlalchemy/sql/coercions.py
@@ -561,9 +561,10 @@ class InElementImpl(RoleImpl):
return element.self_group(against=operator)
elif isinstance(element, elements.BindParameter):
- # previously we were adding expanding flags here but
- # we now do this in the compiler where we have more context
- # see compiler.py -> _render_in_expr_w_bindparam
+ element = element._clone(maintain_key=True)
+ element.expanding = True
+ element.expand_op = operator
+
return element
else:
return element
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index dedd75f5c..734e65492 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -1904,32 +1904,14 @@ class SQLCompiler(Compiled):
binary, override_operator=operators.match_op
)
- def visit_in_op_binary(self, binary, operator, **kw):
- return self._render_in_expr_w_bindparam(binary, operator, **kw)
-
def visit_not_in_op_binary(self, binary, operator, **kw):
# The brackets are required in the NOT IN operation because the empty
# case is handled using the form "(col NOT IN (null) OR 1 = 1)".
# The presence of the OR makes the brackets required.
- return "(%s)" % self._render_in_expr_w_bindparam(
- binary, operator, **kw
+ return "(%s)" % self._generate_generic_binary(
+ binary, OPERATORS[operator], **kw
)
- def _render_in_expr_w_bindparam(self, binary, operator, **kw):
- opstring = OPERATORS[operator]
-
- if isinstance(binary.right, elements.BindParameter):
- if not binary.right.expanding or not binary.right.expand_op:
- # note that by cloning here, we rely upon the
- # _cache_key_bind_match dictionary to resolve
- # clones of bindparam() objects to the ones that are
- # present in our cache key.
- binary.right = binary.right._clone(maintain_key=True)
- binary.right.expanding = True
- binary.right.expand_op = operator
-
- return self._generate_generic_binary(binary, opstring, **kw)
-
def visit_empty_set_op_expr(self, type_, expand_op):
if expand_op is operators.not_in_op:
if len(type_) > 1:
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 416a4e82e..cdb1dbca8 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -256,6 +256,15 @@ class ClauseElement(
return c
+ def _negate_in_binary(self, negated_op, original_op):
+ """a hook to allow the right side of a binary expression to respond
+ to a negation of the binary expression.
+
+ Used for the special case of expanding bind parameter with IN.
+
+ """
+ return self
+
def _with_binary_element_type(self, type_):
"""in the context of binary expression, convert the type of this
object to the one given.
@@ -1510,6 +1519,14 @@ class BindParameter(roles.InElementRole, ColumnElement):
literal_execute=True,
)
+ def _negate_in_binary(self, negated_op, original_op):
+ if self.expand_op is original_op:
+ bind = self._clone()
+ bind.expand_op = negated_op
+ return bind
+ else:
+ return self
+
def _with_binary_element_type(self, type_):
c = ClauseElement._clone(self)
c.type = type_
@@ -3729,7 +3746,7 @@ class BinaryExpression(ColumnElement):
if self.negate is not None:
return BinaryExpression(
self.left,
- self.right,
+ self.right._negate_in_binary(self.negate, self.operator),
self.negate,
negate=self.operator,
type_=self.type,
diff --git a/lib/sqlalchemy/sql/lambdas.py b/lib/sqlalchemy/sql/lambdas.py
index ddc4774db..b3f47252a 100644
--- a/lib/sqlalchemy/sql/lambdas.py
+++ b/lib/sqlalchemy/sql/lambdas.py
@@ -270,6 +270,8 @@ class LambdaElement(elements.ClauseElement):
bind = bindparam_lookup[thing.key]
if thing.expanding:
bind.expanding = True
+ bind.expand_op = thing.expand_op
+ bind.type = thing.type
return bind
if self._rec.is_sequence: