From: Mike Bayer Date: Tue, 14 Sep 2010 02:55:54 +0000 (-0400) Subject: - Fixed bug that would prevent "subqueryload" from X-Git-Tag: rel_0_6_5~63^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=03523970d993b06ff8bed86e2af3ea153cd6112b;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed bug that would prevent "subqueryload" from working correctly with single table inheritance for a relationship from a subclass - the "where type in (x, y, z)" only gets placed on the inside, instead of repeatedly. - When using from_self() with single table inheritance, the "where type in (x, y, z)" is placed on the outside of the query only, instead of repeatedly. May make some more adjustments to this. --- diff --git a/CHANGES b/CHANGES index a39fac9f0b..657695c7e4 100644 --- a/CHANGES +++ b/CHANGES @@ -47,7 +47,18 @@ CHANGES - Placing passive_deletes=True on a many-to-one emits a warning, since you probably intended to put it on the one-to-many side. - + + - Fixed bug that would prevent "subqueryload" from + working correctly with single table inheritance + for a relationship from a subclass - the "where + type in (x, y, z)" only gets placed on the inside, + instead of repeatedly. + + - When using from_self() with single table inheritance, + the "where type in (x, y, z)" is placed on the outside + of the query only, instead of repeatedly. May make + some more adjustments to this. + 0.6.4 ===== - orm diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 6c5e8c81ca..b22a10b55e 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -98,6 +98,7 @@ class Query(object): _attributes = util.frozendict() _with_options = () _with_hints = () + _enable_single_crit = True def __init__(self, entities, session=None): self.session = session @@ -701,12 +702,17 @@ class Query(object): """ fromclause = self.with_labels().enable_eagerloads(False).\ + _enable_single_crit(False).\ statement.correlate(None) q = self._from_selectable(fromclause) if entities: q._set_entities(entities) return q - + + @_generative() + def _enable_single_crit(self, val): + self._enable_single_crit = val + @_generative() def _from_selectable(self, fromclause): for attr in ('_statement', '_criterion', '_order_by', '_group_by', @@ -1936,7 +1942,8 @@ class Query(object): else: from_obj = context.froms - self._adjust_for_single_inheritance(context) + if self._enable_single_crit: + self._adjust_for_single_inheritance(context) whereclause = context.whereclause @@ -2273,7 +2280,8 @@ class Query(object): # i.e. when each _MappedEntity has its own FROM froms = context.froms - self._adjust_for_single_inheritance(context) + if self._enable_single_crit: + self._adjust_for_single_inheritance(context) if not context.primary_columns: if self._only_load_props: @@ -2405,6 +2413,7 @@ class Query(object): selected from the total results. """ + for entity, (mapper, adapter, s, i, w) in \ self._mapper_adapter_map.iteritems(): single_crit = mapper._single_table_criterion diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index b0a18b7ddc..3e6b6a21f6 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -765,6 +765,7 @@ class SubqueryLoader(AbstractRelationshipLoader): ("orig_query", SubqueryLoader): orig_query, ('subquery_path', None) : subq_path } + q = q._enable_single_crit(False) # figure out what's being joined. a.k.a. the fun part to_join = [ diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index 4b7078eb51..4204309540 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -8,7 +8,7 @@ from test.orm._base import MappedTest, ComparableEntity from sqlalchemy.test.schema import Table, Column -class SingleInheritanceTest(MappedTest): +class SingleInheritanceTest(testing.AssertsCompiledSQL, MappedTest): @classmethod def define_tables(cls, metadata): Table('employees', metadata, @@ -26,6 +26,7 @@ class SingleInheritanceTest(MappedTest): @classmethod def setup_classes(cls): + global Employee, Manager, Engineer, JuniorEngineer class Employee(ComparableEntity): pass class Manager(Employee): @@ -114,6 +115,31 @@ class SingleInheritanceTest(MappedTest): # session.query(Employee.name, Manager.manager_data, Engineer.engineer_info).all(), # [] # ) + + @testing.resolve_artifact_names + def test_from_self(self): + sess = create_session() + self.assert_compile(sess.query(Engineer).from_self(), + 'SELECT anon_1.employees_employee_id AS ' + 'anon_1_employees_employee_id, ' + 'anon_1.employees_name AS ' + 'anon_1_employees_name, ' + 'anon_1.employees_manager_data AS ' + 'anon_1_employees_manager_data, ' + 'anon_1.employees_engineer_info AS ' + 'anon_1_employees_engineer_info, ' + 'anon_1.employees_type AS ' + 'anon_1_employees_type FROM (SELECT ' + 'employees.employee_id AS ' + 'employees_employee_id, employees.name AS ' + 'employees_name, employees.manager_data AS ' + 'employees_manager_data, ' + 'employees.engineer_info AS ' + 'employees_engineer_info, employees.type ' + 'AS employees_type FROM employees) AS ' + 'anon_1 WHERE anon_1.employees_type IN ' + '(:type_1, :type_2)', + use_default_dialect=True) @testing.resolve_artifact_names def test_select_from(self): @@ -182,6 +208,54 @@ class SingleInheritanceTest(MappedTest): assert len(rq.join(Report.employee.of_type(Manager)).all()) == 1 assert len(rq.join(Report.employee.of_type(Engineer)).all()) == 0 +class RelationshipFromSingleTest(testing.AssertsCompiledSQL, MappedTest): + @classmethod + def define_tables(cls, metadata): + Table('employee', metadata, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), + Column('name', String(50)), + Column('type', String(20)), + ) + + Table('employee_stuff', metadata, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), + Column('employee_id', Integer, ForeignKey('employee.id')), + Column('name', String(50)), + ) + + @classmethod + def setup_classes(cls): + class Employee(ComparableEntity): + pass + class Manager(Employee): + pass + class Stuff(ComparableEntity): + pass + + @testing.resolve_artifact_names + def test_subquery_load(self): + mapper(Employee, employee, polymorphic_on=employee.c.type, polymorphic_identity='employee') + mapper(Manager, inherits=Employee, polymorphic_identity='manager', properties={ + 'stuff':relationship(Stuff) + }) + mapper(Stuff, employee_stuff) + + sess = create_session() + context = sess.query(Manager).options(subqueryload('stuff'))._compile_context() + subq = context.attributes[('subquery', (class_mapper(Employee), 'stuff'))] + + self.assert_compile(subq, + 'SELECT employee_stuff.id AS ' + 'employee_stuff_id, employee_stuff.employee' + '_id AS employee_stuff_employee_id, ' + 'employee_stuff.name AS ' + 'employee_stuff_name, anon_1.employee_id ' + 'AS anon_1_employee_id FROM (SELECT ' + 'employee.id AS employee_id FROM employee ' + 'WHERE employee.type IN (?)) AS anon_1 ' + 'JOIN employee_stuff ON anon_1.employee_id ' + '= employee_stuff.employee_id ORDER BY ' + 'anon_1.employee_id') class RelationshipToSingleTest(MappedTest): @classmethod