]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
merged [ticket:1486] fix from 0.6
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 28 Jul 2009 17:47:54 +0000 (17:47 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 28 Jul 2009 17:47:54 +0000 (17:47 +0000)
CHANGES
lib/sqlalchemy/sql/expression.py
lib/sqlalchemy/sql/util.py
test/sql/test_selectable.py

diff --git a/CHANGES b/CHANGES
index eeba6f8bffb5aff0ef2a73cc829c5379132dbd82..e79049b77e8104ece303d0844c1ea2715703dc78 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -45,6 +45,11 @@ CHANGES
       [ticket:1424].  See
       http://www.sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery
       for an example.
+
+    - Fixed a somewhat hypothetical issue which would result
+      in the wrong primary key being calculated for a mapper
+      using the old polymorphic_union function - but this
+      is old stuff.  [ticket:1486]
       
 - sql
     - Fixed a bug in extract() introduced in 0.5.4 whereby
index 0dae6716d4dfacab202a488c78108c7a68670a99..83897ef051cca420576616ee5c7b8dcaf5749f58 100644 (file)
@@ -3170,10 +3170,19 @@ class CompoundSelect(_SelectBaseMixin, FromClause):
 
     def _populate_column_collection(self):
         for cols in zip(*[s.c for s in self.selects]):
-            proxy = cols[0]._make_proxy(self, name=self.use_labels and cols[0]._label or None)
+            # 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)
             
-            # place a 'weight' annotation corresponding to how low in the list of select()s
-            # the column occurs, so that the corresponding_column() operation
+            # 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
             proxy.proxies = [c._annotate({'weight':i + 1}) for i, c in enumerate(cols)]
             
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 a172eb45230596d3f7670529ba89133c8b8cc8b2..b0501c913498fde138566bb4fdc9de2a3c0f66a9 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()