source_selectable=adapt_from,
source_polymorphic=True,
of_type_mapper=of_type_mapper,
+ alias_secondary=True,
)
if sj is not None:
return pj & sj
dest_polymorphic=False,
dest_selectable=None,
of_type_mapper=None,
+ alias_secondary=False,
):
+
+ aliased = False
+
+ if alias_secondary and self.secondary is not None:
+ aliased = True
+
if source_selectable is None:
if source_polymorphic and self.parent.with_polymorphic:
source_selectable = self.parent._with_polymorphic_selectable
- aliased = False
if dest_selectable is None:
dest_selectable = self.entity.selectable
if dest_polymorphic and self.mapper.with_polymorphic:
if self._is_self_referential and source_selectable is None:
dest_selectable = dest_selectable.alias()
aliased = True
- else:
+ elif dest_selectable is not self.mapper._with_polymorphic_selectable:
aliased = True
dest_mapper = of_type_mapper or self.mapper
single_crit = dest_mapper._single_table_criterion
- aliased = aliased or (source_selectable is not None)
+ aliased = aliased or (
+ source_selectable is not None
+ and (
+ source_selectable
+ is not self.parent._with_polymorphic_selectable
+ or source_selectable._is_from_container # e.g an alias
+ )
+ )
(
primaryjoin,
+from sqlalchemy import and_
from sqlalchemy import ForeignKey
from sqlalchemy import inspect
from sqlalchemy import Integer
+from sqlalchemy import join
from sqlalchemy import String
from sqlalchemy import testing
+from sqlalchemy.orm import aliased
from sqlalchemy.orm import Bundle
from sqlalchemy.orm import configure_mappers
from sqlalchemy.orm import defaultload
from sqlalchemy.orm import defer
+from sqlalchemy.orm import join as orm_join
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import Load
from sqlalchemy.orm import mapper
go()
+class JoinConditionTest(fixtures.DeclarativeMappedTest):
+ @classmethod
+ def setup_classes(cls):
+ class A(cls.DeclarativeBasic):
+ __tablename__ = "a"
+
+ id = Column(Integer, primary_key=True)
+ b_id = Column(ForeignKey("b.id"))
+ b = relationship("B")
+
+ class B(cls.DeclarativeBasic):
+ __tablename__ = "b"
+
+ id = Column(Integer, primary_key=True)
+ d_id = Column(ForeignKey("d.id"))
+
+ class C(cls.DeclarativeBasic):
+ __tablename__ = "c"
+
+ id = Column(Integer, primary_key=True)
+ a_id = Column(ForeignKey("a.id"))
+ d_id = Column(ForeignKey("d.id"))
+
+ class D(cls.DeclarativeBasic):
+ __tablename__ = "d"
+
+ id = Column(Integer, primary_key=True)
+
+ j = join(B, D, B.d_id == D.id).join(C, C.d_id == D.id)
+
+ A.d = relationship(
+ "D",
+ secondary=j,
+ primaryjoin=and_(A.b_id == B.id, A.id == C.a_id),
+ secondaryjoin=D.id == B.d_id,
+ uselist=False,
+ viewonly=True,
+ )
+
+ def test_a_to_b_plain(self):
+ A, B = self.classes("A", "B")
+
+ # should not use aliasing or adaption so should be cheap
+ @profiling.function_call_count(times=50)
+ def go():
+ orm_join(A, B, A.b)
+
+ go()
+
+ def test_a_to_b_aliased(self):
+ A, B = self.classes("A", "B")
+
+ a1 = aliased(A)
+
+ # uses aliasing, therefore adaption which is expensive
+ @profiling.function_call_count(times=50)
+ def go():
+ orm_join(a1, B, a1.b)
+
+ go()
+
+ def test_a_to_d(self):
+ A, D = self.classes("A", "D")
+
+ # the join condition between A and D uses a secondary selectable with
+ # overlap so incurs aliasing, which is expensive, there is also a check
+ # that determines that this overlap exists which is not currently
+ # cached
+ @profiling.function_call_count(times=50)
+ def go():
+ orm_join(A, D, A.d)
+
+ go()
+
+ def test_a_to_d_aliased(self):
+ A, D = self.classes("A", "D")
+
+ a1 = aliased(A)
+
+ # aliased, uses adaption therefore expensive
+ @profiling.function_call_count(times=50)
+ def go():
+ orm_join(a1, D, a1.d)
+
+ go()
+
+
class BranchedOptionTest(fixtures.MappedTest):
@classmethod
def define_tables(cls, metadata):
test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.7_sqlite_pysqlite_dbapiunicode_cextensions 24244
test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 27251
+# TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased
+
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 10313
+
+# TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain
+
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 3256
+
+# TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d
+
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 106798
+
+# TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased
+
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 104564
+
# TEST: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query
test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_mssql_pyodbc_dbapiunicode_cextensions 418033