]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- additional adjustment to the fix made in 8ad968f33100baeb3b13c7e0b724b6b79ab4277f
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Feb 2016 03:46:31 +0000 (22:46 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 22 Feb 2016 03:49:29 +0000 (22:49 -0500)
for ref #3657.  The Oracle dialect makes more use of the "select_wraps_for"
feature than SQL server because Oracle doesn't have "TOP" for a limit-only
select, so tests are showing more happening here.  In the case where
the select() has some dupe columns, these are deduped from the .c collection
so a positional match between the wrapper and original can't use .inner_columns,
because these collections wont match.  Using _columns_plus_names
instead which is the deduped collection that determines the SELECT display,
which definitely have to match up.

(cherry picked from commit aa9ce3f521f254da9879ede011e520ec35b8270e)

lib/sqlalchemy/sql/compiler.py
test/sql/test_compiler.py

index b75dc1c076d39137a530045770fa949d5b9b9bf8..8600dbaebd448abb0ceed2d95af5878e4ad5bc9d 100644 (file)
@@ -1640,7 +1640,10 @@ class SQLCompiler(Compiled):
             # rewrite the targeted columns in the result map
 
             translate = dict(
-                zip(select.inner_columns, select_wraps_for.inner_columns)
+                zip(
+                    [name for (key, name) in select._columns_plus_names],
+                    [name for (key, name) in
+                     select_wraps_for._columns_plus_names])
             )
 
             self._result_columns = [
index 1c5dc234025f5103d2fc5f9c269c8bcefd8d2769..041cd37b6f6eb876535c35d748ceb565ccd856f6 100644 (file)
@@ -3878,3 +3878,43 @@ class ResultMapTest(fixtures.TestBase):
             proxied
         ):
             is_(orig_obj, proxied_obj)
+
+    def test_select_wraps_for_translate_ambiguity_dupe_cols(self):
+        # test for issue #3657
+        t = table('a', column('x'), column('y'), column('z'))
+
+        l1, l2, l3 = t.c.z.label('a'), t.c.x.label('b'), t.c.x.label('c')
+        orig = [t.c.x, t.c.y, l1, l2, l3]
+
+        # create the statement with some duplicate columns.  right now
+        # the behavior is that these redundant columns are deduped.
+        stmt = select([t.c.x, t.c.y, l1, t.c.y, l2, t.c.x, l3])
+
+        # so the statement has 7 inner columns...
+        eq_(len(list(stmt.inner_columns)), 7)
+
+        # but only exposes 5 of them, the other two are dupes of x and y
+        eq_(len(stmt.c), 5)
+
+        # and when it generates a SELECT it will also render only 5
+        eq_(len(stmt._columns_plus_names), 5)
+
+        wrapped = stmt._generate()
+        wrapped = wrapped.column(
+            func.ROW_NUMBER().over(order_by=t.c.z)).alias()
+
+        # so when we wrap here we're going to have only 5 columns
+        wrapped_again = select([c for c in wrapped.c])
+
+        # so the compiler logic that matches up the "wrapper" to the
+        # "select_wraps_for" can't use inner_columns to match because
+        # these collections are not the same
+        compiled = wrapped_again.compile(
+            compile_kwargs={'select_wraps_for': stmt})
+
+        proxied = [obj[0] for (k, n, obj, type_) in compiled._result_columns]
+        for orig_obj, proxied_obj in zip(
+            orig,
+            proxied
+        ):
+            is_(orig_obj, proxied_obj)