mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-29 20:14:55 -04:00
- Liberalized an assertion that was added as part of 🎫3347
to protect against unknown conditions when splicing inner joins together within joined eager loads with ``innerjoin=True``; if some of the joins use a "secondary" table, the assertion needs to unwrap further joins in order to pass. fixes #3412
This commit is contained in:
Vendored
+10
@@ -18,6 +18,16 @@
|
||||
.. changelog::
|
||||
:version: 1.0.4
|
||||
|
||||
.. change::
|
||||
:tags: bug, orm
|
||||
:tickets: 3412, 3347
|
||||
|
||||
Liberalized an assertion that was added as part of :ticket:`3347`
|
||||
to protect against unknown conditions when splicing inner joins
|
||||
together within joined eager loads with ``innerjoin=True``; if
|
||||
some of the joins use a "secondary" table, the assertion needs to
|
||||
unwrap further joins in order to pass.
|
||||
|
||||
.. change::
|
||||
:tags: bug, schema
|
||||
:tickets: 3411
|
||||
|
||||
@@ -849,7 +849,11 @@ class _ORMJoin(expression.Join):
|
||||
Given join(a, b) and join(b, c), return join(a, b).join(c)
|
||||
|
||||
"""
|
||||
assert self.right is other.left
|
||||
leftmost = other
|
||||
while isinstance(leftmost, sql.Join):
|
||||
leftmost = leftmost.left
|
||||
|
||||
assert self.right is leftmost
|
||||
|
||||
left = _ORMJoin(
|
||||
self.left, other.left,
|
||||
|
||||
@@ -2301,6 +2301,140 @@ class InnerJoinSplicingTest(fixtures.MappedTest, testing.AssertsCompiledSQL):
|
||||
)
|
||||
|
||||
|
||||
class InnerJoinSplicingWSecondaryTest(
|
||||
fixtures.MappedTest, testing.AssertsCompiledSQL):
|
||||
__dialect__ = 'default'
|
||||
__backend__ = True # exercise hardcore join nesting on backends
|
||||
|
||||
@classmethod
|
||||
def define_tables(cls, metadata):
|
||||
Table(
|
||||
'a', metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
Column('bid', ForeignKey('b.id'))
|
||||
)
|
||||
|
||||
Table(
|
||||
'b', metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
Column('cid', ForeignKey('c.id'))
|
||||
)
|
||||
|
||||
Table(
|
||||
'c', metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
)
|
||||
|
||||
Table('ctod', metadata,
|
||||
Column('cid', ForeignKey('c.id'), primary_key=True),
|
||||
Column('did', ForeignKey('d.id'), primary_key=True),
|
||||
)
|
||||
Table('d', metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setup_classes(cls):
|
||||
|
||||
class A(cls.Comparable):
|
||||
pass
|
||||
|
||||
class B(cls.Comparable):
|
||||
pass
|
||||
|
||||
class C(cls.Comparable):
|
||||
pass
|
||||
|
||||
class D(cls.Comparable):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def setup_mappers(cls):
|
||||
A, B, C, D = (
|
||||
cls.classes.A, cls.classes.B, cls.classes.C,
|
||||
cls.classes.D)
|
||||
mapper(A, cls.tables.a, properties={
|
||||
'b': relationship(B)
|
||||
})
|
||||
mapper(B, cls.tables.b, properties=odict([
|
||||
('c', relationship(C)),
|
||||
]))
|
||||
mapper(C, cls.tables.c, properties=odict([
|
||||
('ds', relationship(D, secondary=cls.tables.ctod,
|
||||
order_by=cls.tables.d.c.id)),
|
||||
]))
|
||||
mapper(D, cls.tables.d)
|
||||
|
||||
@classmethod
|
||||
def _fixture_data(cls):
|
||||
A, B, C, D = (
|
||||
cls.classes.A, cls.classes.B, cls.classes.C,
|
||||
cls.classes.D)
|
||||
|
||||
d1, d2, d3 = D(id=1), D(id=2), D(id=3)
|
||||
return [
|
||||
A(
|
||||
id=1,
|
||||
b=B(
|
||||
c=C(
|
||||
id=1,
|
||||
ds=[d1, d2]
|
||||
)
|
||||
)
|
||||
),
|
||||
A(
|
||||
id=2,
|
||||
b=B(
|
||||
c=C(
|
||||
id=2,
|
||||
ds=[d2, d3]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def insert_data(cls):
|
||||
s = Session(testing.db)
|
||||
s.add_all(cls._fixture_data())
|
||||
s.commit()
|
||||
|
||||
def _assert_result(self, query):
|
||||
def go():
|
||||
eq_(
|
||||
query.all(),
|
||||
self._fixture_data()
|
||||
)
|
||||
|
||||
self.assert_sql_count(
|
||||
testing.db,
|
||||
go,
|
||||
1
|
||||
)
|
||||
|
||||
def test_joined_across(self):
|
||||
A = self.classes.A
|
||||
|
||||
s = Session()
|
||||
q = s.query(A) \
|
||||
.options(
|
||||
joinedload('b').
|
||||
joinedload('c', innerjoin=True).
|
||||
joinedload('ds', innerjoin=True))
|
||||
self.assert_compile(
|
||||
q,
|
||||
"SELECT a.id AS a_id, a.bid AS a_bid, d_1.id AS d_1_id, "
|
||||
"c_1.id AS c_1_id, b_1.id AS b_1_id, b_1.cid AS b_1_cid "
|
||||
"FROM a LEFT OUTER JOIN "
|
||||
"(b AS b_1 JOIN "
|
||||
"(c AS c_1 JOIN ctod AS ctod_1 ON c_1.id = ctod_1.cid) "
|
||||
"ON c_1.id = b_1.cid "
|
||||
"JOIN d AS d_1 ON d_1.id = ctod_1.did) ON b_1.id = a.bid "
|
||||
"ORDER BY d_1.id"
|
||||
)
|
||||
self._assert_result(q)
|
||||
|
||||
|
||||
class SubqueryAliasingTest(fixtures.MappedTest, testing.AssertsCompiledSQL):
|
||||
|
||||
"""test #2188"""
|
||||
|
||||
Reference in New Issue
Block a user