From: Mike Bayer Date: Wed, 14 Nov 2018 16:02:40 +0000 (-0500) Subject: Add secondary selectable to FROM clauses for correlated exists X-Git-Tag: rel_1_3_0b1~8^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fdfd1680603f6663182ba4766ae2f5abdd3ced45;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Add secondary selectable to FROM clauses for correlated exists In continuing with a similar theme as that of very recent :ticket:`4349`, repaired issue with :meth:`.RelationshipProperty.Comparator.any` and :meth:`.RelationshipProperty.Comparator.has` where the "secondary" selectable needs to be explicitly part of the FROM clause in the EXISTS subquery to suit the case where this "secondary" is a :class:`.Join` object. Fixes: #4366 Change-Id: Icd0d0c3871bbd0059f0c9256e2b980edc2c90551 --- diff --git a/doc/build/changelog/unreleased_12/4366.rst b/doc/build/changelog/unreleased_12/4366.rst new file mode 100644 index 0000000000..d3332633a5 --- /dev/null +++ b/doc/build/changelog/unreleased_12/4366.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, orm + :tickets: 4366 + + In continuing with a similar theme as that of very recent :ticket:`4349`, + repaired issue with :meth:`.RelationshipProperty.Comparator.any` and + :meth:`.RelationshipProperty.Comparator.has` where the "secondary" + selectable needs to be explicitly part of the FROM clause in the + EXISTS subquery to suit the case where this "secondary" is a :class:`.Join` + object. \ No newline at end of file diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index adcaa59271..e7896c4234 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -1110,9 +1110,12 @@ class RelationshipProperty(StrategizedProperty): crit = j & sql.True_._ifnone(criterion) - ex = sql.exists([1], crit, from_obj=dest).correlate_except(dest) if secondary is not None: - ex = ex.correlate_except(secondary) + ex = sql.exists([1], crit, from_obj=[dest, secondary]).\ + correlate_except(dest, secondary) + else: + ex = sql.exists([1], crit, from_obj=dest).\ + correlate_except(dest) return ex def any(self, criterion=None, **kwargs): diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 0cd0b1a7e5..f287323604 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -2308,6 +2308,8 @@ class FilterTest(QueryTest, AssertsCompiledSQL): ) def test_any(self): + # see also HasAnyTest, a newer suite which tests these at the level of + # SQL compilation User, Address = self.classes.User, self.classes.Address sess = create_session() @@ -2344,6 +2346,8 @@ class FilterTest(QueryTest, AssertsCompiledSQL): filter(~User.addresses.any()).all() def test_any_doesnt_overcorrelate(self): + # see also HasAnyTest, a newer suite which tests these at the level of + # SQL compilation User, Address = self.classes.User, self.classes.Address sess = create_session() @@ -2356,6 +2360,8 @@ class FilterTest(QueryTest, AssertsCompiledSQL): Address.email_address == 'fred@fred.com')).all() def test_has(self): + # see also HasAnyTest, a newer suite which tests these at the level of + # SQL compilation Dingaling, User, Address = ( self.classes.Dingaling, self.classes.User, self.classes.Address) @@ -2607,6 +2613,23 @@ class HasAnyTest( id = Column(Integer, primary_key=True) b_id = Column(ForeignKey(B.id)) + d = relationship( + 'D', + secondary="join(B, C)", + primaryjoin="A.b_id == B.id", + secondaryjoin="C.d_id == D.id", + uselist=False) + + def test_has_composite_secondary(self): + A, D = self.classes("A", "D") + s = Session() + self.assert_compile( + s.query(A).filter(A.d.has(D.id == 1)), + "SELECT a.id AS a_id, a.b_id AS a_b_id FROM a WHERE EXISTS " + "(SELECT 1 FROM d, b JOIN c ON c.id = b.c_id " + "WHERE a.b_id = b.id AND c.d_id = d.id AND d.id = :id_1)" + ) + def test_has_many_to_one(self): B, C = self.classes("B", "C") s = Session()