diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2018-12-21 17:35:12 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2018-12-21 21:50:55 -0500 |
commit | c495769751e8b19d54fb92388ced587b5d13b85d (patch) | |
tree | b0a4415469bb608f78fbcdfb4b240ecc7f54b37b /lib | |
parent | 41f47fb72c5e9382b92f1c66f54771788be648ad (diff) | |
download | sqlalchemy-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.py | 82 |
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+)\]", |