]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
improved reduce_columns() to navigate around a CompositeSelect that buries foreign...
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 28 Jul 2009 17:13:31 +0000 (17:13 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 28 Jul 2009 17:13:31 +0000 (17:13 +0000)
on later selectables, [ticket:1486]

lib/sqlalchemy/sql/expression.py
lib/sqlalchemy/sql/util.py
test/sql/test_selectable.py

index 2da5184e69c34a1a6b971faabfe2b1ea0d3fad6f..91e0e74ae45e7812d09aeddf56c5bd17c6ea43c4 100644 (file)
@@ -3321,9 +3321,17 @@ class CompoundSelect(_SelectBaseMixin, FromClause):
 
     def _populate_column_collection(self):
         for cols in zip(*[s.c for s in self.selects]):
+            # this is a slightly hacky thing - the union exports a column that
+            # resembles just that of the *first* selectable.  to get at a "composite" column,
+            # particularly foreign keys, you have to dig through the proxies collection 
+            # which we generate below.  We may want to improve upon this,
+            # such as perhaps _make_proxy can accept a list of other columns that
+            # are "shared" - schema.column can then copy all the ForeignKeys in.
+            # this would allow the union() to have all those fks too.
             proxy = cols[0]._make_proxy(
                             self, name=self.use_labels and cols[0]._label or None)
             
+            # hand-construct the "proxies" collection to include all derived columns
             # place a 'weight' annotation corresponding to how low in the list of
             # select()s the column occurs, so that the corresponding_column() operation
             # can resolve conflicts
index ac95c3a20950a8d45eb66dce9a6322d8cde0171a..27ae3e624750ed4b7ce19b8d9935fc430ed78630 100644 (file)
@@ -298,7 +298,7 @@ def reduce_columns(columns, *clauses, **kw):
 
     omit = util.column_set()
     for col in columns:
-        for fk in col.foreign_keys:
+        for fk in chain(*[c.foreign_keys for c in col.proxy_set]):
             for c in columns:
                 if c is col:
                     continue
index 670ae1fd0c410f5628ac1eff97b0ac7e60a46dc4..0e0cef38f23e223023fcbfaf97793a9c5a677eb2 100644 (file)
@@ -5,7 +5,7 @@ from sqlalchemy import *
 from sqlalchemy.test import *
 from sqlalchemy.sql import util as sql_util, visitors
 from sqlalchemy import exc
-from sqlalchemy.sql import table, column
+from sqlalchemy.sql import table, column, null
 from sqlalchemy import util
 
 metadata = MetaData()
@@ -416,19 +416,54 @@ class ReduceTest(TestBase, AssertsExecutionResults):
             Column('magazine_page_id', Integer, ForeignKey('magazine_page.page_id'), primary_key=True),
         )
         
-        from sqlalchemy.orm.util import polymorphic_union
-        pjoin = polymorphic_union(
-            {
-                'm': page_table.join(magazine_page_table),
-                'c': page_table.join(magazine_page_table).join(classified_page_table),
-            }, None, 'page_join')
-        
+        # this is essentially the union formed by the ORM's polymorphic_union function.
+        # we define two versions with different ordering of selects.
+
+        # the first selectable has the "real" column classified_page.magazine_page_id
+        pjoin = union(
+            select([
+                page_table.c.id, 
+                magazine_page_table.c.page_id, 
+                classified_page_table.c.magazine_page_id
+            ]).select_from(page_table.join(magazine_page_table).join(classified_page_table)),
+
+            select([
+                page_table.c.id, 
+                magazine_page_table.c.page_id, 
+                cast(null(), Integer).label('magazine_page_id')
+            ]).select_from(page_table.join(magazine_page_table)),
+            
+        ).alias('pjoin')
+
         eq_(
             util.column_set(sql_util.reduce_columns([pjoin.c.id, pjoin.c.page_id, pjoin.c.magazine_page_id])),
             util.column_set([pjoin.c.id])
         )    
-    
+
+        # the first selectable has a CAST, which is a placeholder for
+        # classified_page.magazine_page_id in the second selectable.  reduce_columns
+        # needs to take into account all foreign keys derived from pjoin.c.magazine_page_id.
+        # the UNION construct currently makes the external column look like that of the first
+        # selectable only.
+        pjoin = union(
+            select([
+                page_table.c.id, 
+                magazine_page_table.c.page_id, 
+                cast(null(), Integer).label('magazine_page_id')
+            ]).select_from(page_table.join(magazine_page_table)),
             
+            select([
+                page_table.c.id, 
+                magazine_page_table.c.page_id, 
+                classified_page_table.c.magazine_page_id
+            ]).select_from(page_table.join(magazine_page_table).join(classified_page_table))
+        ).alias('pjoin')
+
+        eq_(
+            util.column_set(sql_util.reduce_columns([pjoin.c.id, pjoin.c.page_id, pjoin.c.magazine_page_id])),
+            util.column_set([pjoin.c.id])
+        )    
+
 class DerivedTest(TestBase, AssertsExecutionResults):
     def test_table(self):
         meta = MetaData()