summaryrefslogtreecommitdiff
path: root/test/sql
diff options
context:
space:
mode:
Diffstat (limited to 'test/sql')
-rw-r--r--test/sql/test_compiler.py98
1 files changed, 98 insertions, 0 deletions
diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py
index e19d755a8..29b06851a 100644
--- a/test/sql/test_compiler.py
+++ b/test/sql/test_compiler.py
@@ -3835,6 +3835,104 @@ class BindParameterTest(AssertsCompiledSQL, fixtures.TestBase):
{"myid_1": 10},
)
+ def test_construct_duped_params_w_bind_clones_post(self):
+ """same as previous test_construct_params_w_bind_clones_post but
+ where the binds have been used
+ repeatedly, and the adaption occurs on a per-subquery basis.
+ test for #6391
+
+ """
+
+ inner_stmt = select(table1.c.myid).where(table1.c.myid == 5)
+
+ stmt = union(inner_stmt, inner_stmt, inner_stmt)
+
+ # get the original bindparam.
+ original_bind = inner_stmt._where_criteria[0].right
+
+ # same bind three times
+ is_(stmt.selects[0]._where_criteria[0].right, original_bind)
+ is_(stmt.selects[1]._where_criteria[0].right, original_bind)
+ is_(stmt.selects[2]._where_criteria[0].right, original_bind)
+
+ # it's anonymous so unique=True
+ is_true(original_bind.unique)
+
+ # cache key against hte original param
+ cache_key = stmt._generate_cache_key()
+
+ # now adapt the statement and separately adapt the inner
+ # SELECTs, since if these subqueries are also ORM then they get adapted
+ # separately.
+ stmt_adapted = sql_util.ClauseAdapter(table1).traverse(stmt)
+ stmt_adapted.selects[0] = sql_util.ClauseAdapter(table1).traverse(
+ stmt_adapted.selects[0]
+ )
+ stmt_adapted.selects[1] = sql_util.ClauseAdapter(table1).traverse(
+ stmt_adapted.selects[1]
+ )
+ stmt_adapted.selects[2] = sql_util.ClauseAdapter(table1).traverse(
+ stmt_adapted.selects[2]
+ )
+
+ # new bind parameter has a different key but same
+ # identifying key
+
+ new_bind_one = stmt_adapted.selects[0]._where_criteria[0].right
+ new_bind_two = stmt_adapted.selects[1]._where_criteria[0].right
+ new_bind_three = stmt_adapted.selects[2]._where_criteria[0].right
+
+ for new_bind in (new_bind_one, new_bind_two, new_bind_three):
+ eq_(original_bind._identifying_key, new_bind._identifying_key)
+ ne_(original_bind.key, new_bind.key)
+
+ # compile the adapted statement but set the cache key to the one
+ # generated from the unadapted statement. this will look like
+ # when the ORM runs clause adaption inside of visit_select, after
+ # the cache key is generated but before the compiler is given the
+ # core select statement to actually render.
+ compiled = stmt_adapted.compile(cache_key=cache_key)
+
+ # the same parameter was split into three distinct ones, due to
+ # the separate adaption on a per-subquery basis. but they still
+ # refer to the original in their _cloned_set and this is what
+ # has to match up to what's in the cache key.
+ # params set up as 5
+ eq_(
+ compiled.construct_params(
+ params={},
+ ),
+ {"myid_1": 5, "myid_2": 5, "myid_3": 5},
+ )
+
+ # also works w the original cache key
+ eq_(
+ compiled.construct_params(
+ params={}, extracted_parameters=cache_key[1]
+ ),
+ {"myid_1": 5, "myid_2": 5, "myid_3": 5},
+ )
+
+ # now make a totally new statement with the same cache key
+ new_inner_stmt = select(table1.c.myid).where(table1.c.myid == 10)
+ new_stmt = union(new_inner_stmt, new_inner_stmt, new_inner_stmt)
+
+ new_cache_key = new_stmt._generate_cache_key()
+
+ # cache keys match
+ eq_(cache_key.key, new_cache_key.key)
+
+ # ensure we get "10" from construct params. if it matched
+ # based on .key and not ._identifying_key, it would not see that
+ # the bind parameter is part of the cache key.
+ # before #6391 was fixed you would see 5, 5, 10
+ eq_(
+ compiled.construct_params(
+ params={}, extracted_parameters=new_cache_key[1]
+ ),
+ {"myid_1": 10, "myid_2": 10, "myid_3": 10},
+ )
+
def test_construct_params_w_bind_clones_pre(self):
"""test that a BindParameter that has been cloned before the cache
key was generated, and was doubled up just to make sure it has to