From: Mike Bayer Date: Mon, 22 Feb 2016 03:46:31 +0000 (-0500) Subject: - additional adjustment to the fix made in 8ad968f33100baeb3b13c7e0b724b6b79ab4277f X-Git-Tag: rel_1_1_0b1~98^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eff7b4f29d2a98ef4ccd95b693c7d653eaa543eb;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - additional adjustment to the fix made in 8ad968f33100baeb3b13c7e0b724b6b79ab4277f 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) --- diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index b75dc1c076..8600dbaebd 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -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 = [ diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py index 1c5dc23402..041cd37b6f 100644 --- a/test/sql/test_compiler.py +++ b/test/sql/test_compiler.py @@ -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)