From: Mike Bayer Date: Thu, 29 Jul 2021 14:10:28 +0000 (-0400) Subject: accommodate for cloned bindparams w/ maintain_key X-Git-Tag: rel_1_4_23~26^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1ccaac8293ee69f22dd01766a26c3ca7893c6a83;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git accommodate for cloned bindparams w/ maintain_key Fixed issue where a bound parameter object that was "cloned" would cause a name conflict in the compiler, if more than one clone of this parameter were used at the same time in a single statement. This could occur in particular with things like ORM single table inheritance queries that indicated the same "discriminator" value multiple times in one query. Fixes: #6824 Change-Id: Iba7a786fc5a2341ff7d07fc666d24ed790ad4fe8 --- diff --git a/doc/build/changelog/unreleased_14/6824.rst b/doc/build/changelog/unreleased_14/6824.rst new file mode 100644 index 0000000000..48086e0ed0 --- /dev/null +++ b/doc/build/changelog/unreleased_14/6824.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, orm, sql + :tickets: 6824 + + Fixed issue where a bound parameter object that was "cloned" would cause a + name conflict in the compiler, if more than one clone of this parameter + were used at the same time in a single statement. This could occur in + particular with things like ORM single table inheritance queries that + indicated the same "discriminator" value multiple times in one query. + diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 29ef8047ef..a81507acb9 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -2311,8 +2311,14 @@ class SQLCompiler(Compiled): existing = self.binds[name] if existing is not bindparam: if ( - existing.unique or bindparam.unique - ) and not existing.proxy_set.intersection(bindparam.proxy_set): + (existing.unique or bindparam.unique) + and not existing.proxy_set.intersection( + bindparam.proxy_set + ) + and not existing._cloned_set.intersection( + bindparam._cloned_set + ) + ): raise exc.CompileError( "Bind parameter '%s' conflicts with " "unique bind parameter of the same name" % name diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index 873b808ec9..32cc570204 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -162,6 +162,20 @@ class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest): assert row.name == "Kurt" assert row.employee_id == e1.employee_id + def test_discrim_bound_param_cloned_ok(self): + """Test #6824""" + Manager = self.classes.Manager + + subq1 = select(Manager.employee_id).label("foo") + subq2 = select(Manager.employee_id).label("bar") + self.assert_compile( + select(subq1, subq2), + "SELECT (SELECT employees.employee_id FROM employees " + "WHERE employees.type IN ([POSTCOMPILE_type_1])) AS foo, " + "(SELECT employees.employee_id FROM employees " + "WHERE employees.type IN ([POSTCOMPILE_type_1])) AS bar", + ) + def test_multi_qualification(self): Manager, Engineer = (self.classes.Manager, self.classes.Engineer) diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py index 40faab4867..d270218b24 100644 --- a/test/sql/test_compiler.py +++ b/test/sql/test_compiler.py @@ -3589,6 +3589,22 @@ class BindParameterTest(AssertsCompiledSQL, fixtures.TestBase): s, ) + def test_unique_binds_no_clone_collision(self): + """test #6824""" + bp = bindparam("foo", unique=True) + + bpc1 = bp._clone(maintain_key=True) + bpc2 = bp._clone(maintain_key=True) + + stmt1 = select(bp, bpc1, bpc2) + + # OK, still strange that the double-dedupe logic is still *duping* + # the label name, but that's a different issue + self.assert_compile( + stmt1, + "SELECT :foo_1 AS anon_1, :foo_1 AS anon__1, :foo_1 AS anon__1", + ) + def _test_binds_no_hash_collision(self): """test that construct_params doesn't corrupt dict due to hash collisions"""