]> 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:46:31 +0000 (22:46 -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.

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

index 3bbb79d5508582acd09d0af9e6f262bf9f563656..9f54004552eebce82d0ad70ae690b8dd8ca75804 100644 (file)
@@ -1592,7 +1592,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 258f31c16fa3ba597565607650584fd421e0dc6f..209eea60b19762f3d0df9a987a29b4869da02372 100644 (file)
@@ -3610,3 +3610,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)