summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2018-12-21 17:35:12 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2018-12-21 21:50:55 -0500
commitc495769751e8b19d54fb92388ced587b5d13b85d (patch)
treeb0a4415469bb608f78fbcdfb4b240ecc7f54b37b /lib
parent41f47fb72c5e9382b92f1c66f54771788be648ad (diff)
downloadsqlalchemy-c495769751e8b19d54fb92388ced587b5d13b85d.tar.gz
Maintain compiled_params / replacement_expressions within expanding IN
Fixed issue in "expanding IN" feature where using the same bound parameter name more than once in a query would lead to a KeyError within the process of rewriting the parameters in the query. Fixes: #4394 Change-Id: Ibcadce9fefbcb060266d9447c2044ee6efeccf5a
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/engine/default.py82
1 files changed, 48 insertions, 34 deletions
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 5c96e4240..028abc4c2 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -726,50 +726,64 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
positiontup = None
replacement_expressions = {}
+ to_update_sets = {}
+
for name in (
self.compiled.positiontup if compiled.positional
else self.compiled.binds
):
parameter = self.compiled.binds[name]
if parameter.expanding:
- values = compiled_params.pop(name)
- if not values:
- to_update = []
- replacement_expressions[name] = (
- self.compiled.visit_empty_set_expr(
- parameter._expanding_in_types
- if parameter._expanding_in_types
- else [parameter.type]
+
+ if name in replacement_expressions:
+ to_update = to_update_sets[name]
+ else:
+ # we are removing the parameter from compiled_params
+ # because it is a list value, which is not expected by
+ # TypeEngine objects that would otherwise be asked to
+ # process it. the single name is being replaced with
+ # individual numbered parameters for each value in the
+ # param.
+ values = compiled_params.pop(name)
+
+ if not values:
+ to_update = to_update_sets[name] = []
+ replacement_expressions[name] = (
+ self.compiled.visit_empty_set_expr(
+ parameter._expanding_in_types
+ if parameter._expanding_in_types
+ else [parameter.type]
+ )
)
- )
- elif isinstance(values[0], (tuple, list)):
- to_update = [
- ("%s_%s_%s" % (name, i, j), value)
- for i, tuple_element in enumerate(values, 1)
- for j, value in enumerate(tuple_element, 1)
- ]
- replacement_expressions[name] = ", ".join(
- "(%s)" % ", ".join(
+ elif isinstance(values[0], (tuple, list)):
+ to_update = to_update_sets[name] = [
+ ("%s_%s_%s" % (name, i, j), value)
+ for i, tuple_element in enumerate(values, 1)
+ for j, value in enumerate(tuple_element, 1)
+ ]
+ replacement_expressions[name] = ", ".join(
+ "(%s)" % ", ".join(
+ self.compiled.bindtemplate % {
+ "name":
+ to_update[i * len(tuple_element) + j][0]
+ }
+ for j, value in enumerate(tuple_element)
+ )
+ for i, tuple_element in enumerate(values)
+
+ )
+ else:
+ to_update = to_update_sets[name] = [
+ ("%s_%s" % (name, i), value)
+ for i, value in enumerate(values, 1)
+ ]
+ replacement_expressions[name] = ", ".join(
self.compiled.bindtemplate % {
- "name":
- to_update[i * len(tuple_element) + j][0]
- }
- for j, value in enumerate(tuple_element)
+ "name": key}
+ for key, value in to_update
)
- for i, tuple_element in enumerate(values)
- )
- else:
- to_update = [
- ("%s_%s" % (name, i), value)
- for i, value in enumerate(values, 1)
- ]
- replacement_expressions[name] = ", ".join(
- self.compiled.bindtemplate % {
- "name": key}
- for key, value in to_update
- )
compiled_params.update(to_update)
processors.update(
(key, processors[name])
@@ -783,7 +797,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
positiontup.append(name)
def process_expanding(m):
- return replacement_expressions.pop(m.group(1))
+ return replacement_expressions[m.group(1)]
self.statement = re.sub(
r"\[EXPANDING_(\S+)\]",