From: Mike Bayer Date: Wed, 18 May 2011 16:12:00 +0000 (-0400) Subject: - merge 00380cf3c304ea7f1c7397f17b828166364c36ac from default X-Git-Tag: rel_0_6_8~11 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=94fc44ac020da38e29340955e9cb696133080a03;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - merge 00380cf3c304ea7f1c7397f17b828166364c36ac from default - Fixed bug whereby nesting a label of a select() with another label in it would produce incorrect exported columns. Among other things this would break an ORM column_property() mapping against another column_property(). [ticket:2167]. --- diff --git a/CHANGES b/CHANGES index 1b93682ad2..5824d0b644 100644 --- a/CHANGES +++ b/CHANGES @@ -53,6 +53,12 @@ CHANGES test coverage for all column default assignment patterns. [ticket:2147] + - Fixed bug whereby nesting a label of a select() + with another label in it would produce incorrect + exported columns. Among other things this would + break an ORM column_property() mapping against + another column_property(). [ticket:2167]. + 0.6.7 ===== - orm diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 30dae14c28..c5bf59d629 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -2743,11 +2743,11 @@ class _ColumnEntity(_QueryEntity): "expected - got '%r'" % column ) - # if the Column is unnamed, give it a + # If the Column is unnamed, give it a # label() so that mutable column expressions # can be located in the result even # if the expression's identity has been changed - # due to adaption + # due to adaption. if not column._label: column = column.label(None) diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index aea1504a41..75e33b6b4d 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -3493,11 +3493,7 @@ class _Label(ColumnElement): return self.element._from_objects def _make_proxy(self, selectable, name = None): - if isinstance(self.element, (Selectable, ColumnElement)): - e = self.element._make_proxy(selectable, name=self.name) - else: - e = column(self.name)._make_proxy(selectable=selectable) - + e = self.element._make_proxy(selectable, name=name or self.name) e.proxies.append(self) return e @@ -3606,6 +3602,12 @@ class ColumnClause(_Immutable, ColumnElement): return self.name def label(self, name): + # currently, anonymous labels don't occur for + # ColumnClause. The use at the moment + # is that they do not generate nicely for + # is_literal clauses. We would like to change + # this so that label(None) acts as would be expected. + # See [ticket:2168]. if name is None: return self else: diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 43a415fde6..6067964ef1 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -4517,6 +4517,26 @@ class ExternalColumnsTest(QueryTest): eq_(o1.address.user.count, 1) self.assert_sql_count(testing.db, go, 1) + def test_external_columns_compound(self): + # see [ticket:2167] for background + + mapper(User, users, properties={ + 'fullname':column_property(users.c.name.label('x')) + }) + + mapper(Address, addresses, properties={ + 'username':column_property( + select([User.fullname]).\ + where(User.id==addresses.c.user_id).label('y')) + }) + sess = create_session() + a1 = sess.query(Address).first() + eq_(a1.username, "jack") + + sess = create_session() + a1 = sess.query(Address).from_self().first() + eq_(a1.username, "jack") + class TestOverlyEagerEquivalentCols(_base.MappedTest): @classmethod def define_tables(cls, metadata): diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py index 82df3b1d6b..d67e721a03 100644 --- a/test/sql/test_selectable.py +++ b/test/sql/test_selectable.py @@ -26,7 +26,7 @@ table2 = Table('table2', metadata, ) -class SelectableTest(TestBase, AssertsExecutionResults): +class SelectableTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL): def test_indirect_correspondence_on_labels(self): # this test depends upon 'distance' to @@ -371,6 +371,54 @@ class SelectableTest(TestBase, AssertsExecutionResults): "side to a subquery using alias\(\)\?", t1t2.join, t2t3.select(use_labels=True)) + def test_multi_label_chain_naming_col(self): + # See [ticket:2167] for this one. + l1 = table1.c.col1.label('a') + l2 = select([l1]).label('b') + s = select([l2]) + assert s.c.b is not None + self.assert_compile( + s.select(), + "SELECT b FROM (SELECT (SELECT table1.col1 AS a FROM table1) AS b)" + ) + + s2 = select([s.label('c')]) + self.assert_compile( + s2.select(), + "SELECT c FROM (SELECT (SELECT (SELECT table1.col1 AS a FROM table1) AS b) AS c)" + ) + +class AnonLabelTest(TestBase): + """Test behaviors that we hope to change with [ticket:2168].""" + + def test_anon_labels_named_column(self): + c1 = column('x') + + # surprising + assert c1.label(None) is c1 + eq_(str(select([c1.label(None)])), "SELECT x") + + def test_anon_labels_literal_column(self): + c1 = literal_column('x') + assert c1.label(None) is c1 + eq_(str(select([c1.label(None)])), "SELECT x") + + def test_anon_labels_func(self): + c1 = func.count('*') + assert c1.label(None) is not c1 + + eq_(str(select([c1])), "SELECT count(:param_1) AS count_1") + c2 = select([c1]).compile() + + eq_(str(select([c1.label(None)])), "SELECT count(:param_1) AS count_1") + + def test_named_labels_named_column(self): + c1 = column('x') + eq_(str(select([c1.label('y')])), "SELECT x AS y") + + def test_named_labels_literal_column(self): + c1 = literal_column('x') + eq_(str(select([c1.label('y')])), "SELECT x AS y") class PrimaryKeyTest(TestBase, AssertsExecutionResults):