]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- the _Label construct, i.e. the one that is produced
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 8 Aug 2010 00:58:23 +0000 (20:58 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 8 Aug 2010 00:58:23 +0000 (20:58 -0400)
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].

CHANGES
lib/sqlalchemy/sql/expression.py
test/orm/test_query.py
test/sql/test_selectable.py

diff --git a/CHANGES b/CHANGES
index 9baf9048740ae9ca29cc3b18b1653df8be172b8b..0f35f9261a76fc15fdbe5ccb96f1ce6ce34c4c81 100644 (file)
--- 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
index 4b8df74c6e38463835d4a6b326138ab8627858d0..0a5edb42f1c57884276a8d753fc7da9aef5159b9 100644 (file)
@@ -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
 
index ebe72b56550032259ddf1f26f2e048de1e34df24..cc2f046cd38feb6c195596d48bb00011ac8fd83f 100644 (file)
@@ -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):
index 062ed5f1b25cdc890d072760656872d93416728c..5bebbe05f4975276c755bd9255a56c3ce2a93d95 100644 (file)
@@ -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),