From ce577d48449588d3e5395c08c7f4d04cb8bb325f Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 2 Sep 2016 11:27:58 -0400 Subject: Repair clauselist comparison to account for clause ordering Fixed bug where the "simple many-to-one" condition that allows lazy loading to use get() from identity map would fail to be invoked if the primaryjoin of the relationship had multiple clauses separated by AND which were not in the same order as that of the primary key columns being compared in each clause. This ordering difference occurs for a composite foreign key where the table-bound columns on the referencing side were not in the same order in the .c collection as the primary key columns on the referenced side....which in turn occurs a lot if one is using declarative mixins and/or declared_attr to set up columns. Change-Id: I66cce74f614c04ed693dc0d58ac8c952b2f8ae54 Fixes: #3788 --- test/sql/test_utils.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 test/sql/test_utils.py (limited to 'test/sql') diff --git a/test/sql/test_utils.py b/test/sql/test_utils.py new file mode 100644 index 000000000..09d7e98af --- /dev/null +++ b/test/sql/test_utils.py @@ -0,0 +1,78 @@ +from sqlalchemy.testing import fixtures, is_true, is_false +from sqlalchemy import MetaData, Table, Column, Integer +from sqlalchemy import and_, or_ +from sqlalchemy.sql.elements import ClauseList +from sqlalchemy.sql import operators + + +class CompareClausesTest(fixtures.TestBase): + def setup(self): + m = MetaData() + self.a = Table( + 'a', m, + Column('x', Integer), + Column('y', Integer) + ) + + self.b = Table( + 'b', m, + Column('y', Integer), + Column('z', Integer) + ) + + def test_compare_clauselist_associative(self): + + l1 = and_( + self.a.c.x == self.b.c.y, + self.a.c.y == self.b.c.z + ) + + l2 = and_( + self.a.c.y == self.b.c.z, + self.a.c.x == self.b.c.y, + ) + + l3 = and_( + self.a.c.x == self.b.c.z, + self.a.c.y == self.b.c.y + ) + + is_true(l1.compare(l1)) + is_true(l1.compare(l2)) + is_false(l1.compare(l3)) + + def test_compare_clauselist_not_associative(self): + + l1 = ClauseList( + self.a.c.x, self.a.c.y, self.b.c.y, operator=operators.sub) + + l2 = ClauseList( + self.b.c.y, self.a.c.x, self.a.c.y, operator=operators.sub) + + is_true(l1.compare(l1)) + is_false(l1.compare(l2)) + + def test_compare_clauselist_assoc_different_operator(self): + + l1 = and_( + self.a.c.x == self.b.c.y, + self.a.c.y == self.b.c.z + ) + + l2 = or_( + self.a.c.y == self.b.c.z, + self.a.c.x == self.b.c.y, + ) + + is_false(l1.compare(l2)) + + def test_compare_clauselist_not_assoc_different_operator(self): + + l1 = ClauseList( + self.a.c.x, self.a.c.y, self.b.c.y, operator=operators.sub) + + l2 = ClauseList( + self.a.c.x, self.a.c.y, self.b.c.y, operator=operators.div) + + is_false(l1.compare(l2)) + -- cgit v1.2.1