From: Mike Bayer Date: Sun, 8 Aug 2010 00:58:23 +0000 (-0400) Subject: - the _Label construct, i.e. the one that is produced X-Git-Tag: rel_0_6_4~51 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cbb0a03a560fd33933971e7b4c9293918612a2cf;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - the _Label construct, i.e. the one that is produced whenever you say somecol.label(), now counts itself in its "proxy_set" unioned with that of it's contained column's proxy set, instead of directly returning that of the contained column. This allows column correspondence operations which depend on the identity of the _Labels themselves to return the correct result - fixes ORM bug [ticket:1852]. --- diff --git a/CHANGES b/CHANGES index 9baf904874..0f35f9261a 100644 --- a/CHANGES +++ b/CHANGES @@ -61,6 +61,13 @@ CHANGES when joinedload() or subqueryload() options are applied to a dynamic attribute, instead of failure / silent failure. [ticket:1864] + + - Fixed bug whereby generating a Query derived + from one which had the same column repeated + with different label names, typically + in some UNION situations, would fail to + propagate the inner columns completely to + the outer query. [ticket:1852] - sql - Changed the scheme used to generate truncated @@ -111,6 +118,16 @@ CHANGES columns in a reflected table would cause an attempt to remove the reflected constraint from the table a second time, raising a KeyError. [ticket:1865] + + - the _Label construct, i.e. the one that is produced + whenever you say somecol.label(), now counts itself + in its "proxy_set" unioned with that of it's + contained column's proxy set, instead of + directly returning that of the contained column. + This allows column correspondence + operations which depend on the identity of the + _Labels themselves to return the correct result + - fixes ORM bug [ticket:1852]. - declarative - if @classproperty is used with a regular class-bound diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 4b8df74c6e..0a5edb42f1 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -3187,7 +3187,8 @@ class _Label(ColumnElement): self._element = element self._type = type_ self.quote = element.quote - + self.proxies = [element] + @util.memoized_property def type(self): return sqltypes.to_instance( @@ -3198,17 +3199,13 @@ class _Label(ColumnElement): def element(self): return self._element.self_group(against=operators.as_) - def _proxy_attr(name): - get = attrgetter(name) - def attr(self): - return get(self.element) - return property(attr) + @property + def primary_key(self): + return self.element.primary_key - proxies = _proxy_attr('proxies') - base_columns = _proxy_attr('base_columns') - proxy_set = _proxy_attr('proxy_set') - primary_key = _proxy_attr('primary_key') - foreign_keys = _proxy_attr('foreign_keys') + @property + def foreign_keys(self): + return self.element.foreign_keys def get_children(self, **kwargs): return self.element, @@ -3225,6 +3222,7 @@ class _Label(ColumnElement): e = self.element._make_proxy(selectable, name=self.name) else: e = column(self.name)._make_proxy(selectable=selectable) + e.proxies.append(self) return e diff --git a/test/orm/test_query.py b/test/orm/test_query.py index ebe72b5655..cc2f046cd3 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -1132,6 +1132,22 @@ class SetOpsTest(QueryTest, AssertsCompiledSQL): (User(id=10, name=u'chuck'), u'y') ] ) + + c1, c2 = column('c1'), column('c2') + q1 = s.query(User, c1.label('foo'), c1.label('bar')) + q2 = s.query(User, c1.label('foo'), c2.label('bar')) + q3 = q1.union(q2) + self.assert_compile( + q3, + "SELECT anon_1.users_id AS anon_1_users_id, " + "anon_1.users_name AS anon_1_users_name, " + "anon_1.foo AS anon_1_foo, anon_1.bar AS anon_1_bar " + "FROM (SELECT users.id AS users_id, users.name AS users_name, " + "c1 AS foo, c1 AS bar FROM users UNION SELECT users.id AS " + "users_id, users.name AS users_name, c1 AS foo, c2 AS bar " + "FROM users) AS anon_1", + use_default_dialect=True + ) @testing.fails_on('mysql', "mysql doesn't support intersect") def test_intersect(self): diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py index 062ed5f1b2..5bebbe05f4 100644 --- a/test/sql/test_selectable.py +++ b/test/sql/test_selectable.py @@ -28,20 +28,40 @@ table2 = Table('table2', metadata, class SelectableTest(TestBase, AssertsExecutionResults): - def test_distance_on_labels(self): - + def test_indirect_correspondence_on_labels(self): + # this test depends upon 'distance' to + # get the right result + # same column three times s = select([table1.c.col1.label('c2'), table1.c.col1, table1.c.col1.label('c1')]) - # didnt do this yet...col.label().make_proxy() has same - # "distance" as col.make_proxy() so far assert - # s.corresponding_column(table1.c.col1) is s.c.col1 + # this tests the same thing as + # test_direct_correspondence_on_labels below - + # that the presence of label() affects the 'distance' + assert s.corresponding_column(table1.c.col1) is s.c.col1 assert s.corresponding_column(s.c.col1) is s.c.col1 assert s.corresponding_column(s.c.c1) is s.c.c1 + def test_direct_correspondence_on_labels(self): + # this test depends on labels being part + # of the proxy set to get the right result + + l1, l2 = table1.c.col1.label('foo'), table1.c.col1.label('bar') + sel = select([l1, l2]) + + sel2 = sel.alias() + assert sel2.corresponding_column(l1) is sel2.c.foo + assert sel2.corresponding_column(l2) is sel2.c.bar + + sel2 = select([table1.c.col1.label('foo'), table1.c.col2.label('bar')]) + + sel3 = sel.union(sel2).alias() + assert sel3.corresponding_column(l1) is sel3.c.foo + assert sel3.corresponding_column(l2) is sel3.c.bar + def test_distance_on_aliases(self): a1 = table1.alias('a1') for s in (select([a1, table1], use_labels=True),