--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 4241
+
+ Fixed issue in single-inheritance loading where the use of an aliased
+ entity against a single-inheritance subclass in conjunction with the
+ :meth:`.Query.select_from` method would cause the SQL to be rendered with
+ the unaliased table mixed in to the query, causing a cartesian product. In
+ particular this was affecting the new "selectin" loader when used against a
+ single-inheritance subclass.
"""
search = set(self._mapper_adapter_map.values())
- if self._select_from_entity:
- # based on the behavior in _set_select_from,
- # when we have self._select_from_entity, we don't
- # have _from_obj_alias.
- # assert self._from_obj_alias is None
- search = search.union([(self._select_from_entity, None)])
+ if self._select_from_entity and \
+ self._select_from_entity not in self._mapper_adapter_map:
+ insp = inspect(self._select_from_entity)
+ if insp.is_aliased_class:
+ adapter = insp._adapter
+ else:
+ adapter = None
+ search = search.union([(self._select_from_entity, adapter)])
for (ext_info, adapter) in search:
if ext_info in self._join_entities:
'anon_1',
use_default_dialect=True)
+ def test_select_from_aliased_w_subclass(self):
+ Engineer = self.classes.Engineer
+
+ sess = create_session()
+
+ a1 = aliased(Engineer)
+ self.assert_compile(
+ sess.query(a1.employee_id).select_from(a1),
+ "SELECT employees_1.employee_id AS employees_1_employee_id "
+ "FROM employees AS employees_1 WHERE employees_1.type "
+ "IN (:type_1, :type_2)",
+ )
+
+ self.assert_compile(
+ sess.query(literal('1')).select_from(a1),
+ "SELECT :param_1 AS param_1 FROM employees AS employees_1 "
+ "WHERE employees_1.type IN (:type_1, :type_2)"
+ )
+
def test_union_modifiers(self):
Engineer, Manager = self.classes("Engineer", "Manager")
a1 = q.all()[0]
is_true('c1_m2o' in a1.b.__dict__)
is_true('c2_m2o' in a1.b.__dict__)
+
+
+class SingleInhSubclassTest(
+ fixtures.DeclarativeMappedTest,
+ testing.AssertsExecutionResults):
+
+ @classmethod
+ def setup_classes(cls):
+ Base = cls.DeclarativeBasic
+
+ class User(Base):
+ __tablename__ = 'user'
+
+ id = Column(Integer, primary_key=True)
+ type = Column(String(10))
+
+ __mapper_args__ = {'polymorphic_on': type}
+
+ class EmployerUser(User):
+ roles = relationship('Role', lazy='selectin')
+ __mapper_args__ = {'polymorphic_identity': 'employer'}
+
+ class Role(Base):
+ __tablename__ = 'role'
+
+ id = Column(Integer, primary_key=True)
+ user_id = Column(Integer, ForeignKey('user.id'))
+
+ @classmethod
+ def insert_data(cls):
+ EmployerUser, Role = cls.classes("EmployerUser", "Role")
+
+ s = Session()
+ s.add(EmployerUser(roles=[Role(), Role(), Role()]))
+ s.commit()
+
+ def test_load(self):
+ EmployerUser, = self.classes("EmployerUser")
+ s = Session()
+
+ q = s.query(EmployerUser)
+
+ self.assert_sql_execution(
+ testing.db,
+ q.all,
+ CompiledSQL(
+ 'SELECT "user".id AS user_id, "user".type AS user_type '
+ 'FROM "user" WHERE "user".type IN (:type_1)',
+ {'type_1': 'employer'}
+ ),
+ CompiledSQL(
+ 'SELECT user_1.id AS user_1_id, role.id AS role_id, '
+ 'role.user_id AS role_user_id FROM "user" AS user_1 '
+ 'JOIN role ON user_1.id = role.user_id WHERE user_1.id IN '
+ '([EXPANDING_primary_keys]) '
+ 'AND user_1.type IN (:type_1) ORDER BY user_1.id',
+ {'primary_keys': [1], 'type_1': 'employer'}
+ ),
+ )
\ No newline at end of file