Mike Bayer avatar Mike Bayer committed ed9551d

- merge b1d3e3aea985 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].

Comments (0)

Files changed (5)

     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

lib/sqlalchemy/orm/query.py

                 "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)
 

lib/sqlalchemy/sql/expression.py

         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
 
             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:

test/orm/test_query.py

             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):

test/sql/test_selectable.py

 )
 
 
-class SelectableTest(TestBase, AssertsExecutionResults):
+class SelectableTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
 
     def test_indirect_correspondence_on_labels(self):
         # this test depends upon 'distance' to
                               "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):
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.