]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug where the combination of "limit" rendering as
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 30 Apr 2014 23:06:26 +0000 (19:06 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 30 Apr 2014 23:07:45 +0000 (19:07 -0400)
"SELECT FIRST n ROWS" using a bound parameter (only firebird has both),
combined with column-level subqueries
which also feature "limit" as well as "positional" bound parameters
(e.g. qmark style) would erroneously assign the subquery-level positions
before that of the enclosing SELECT, thus returning parameters which
are out of order. Fixes #3038

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/sql/compiler.py
test/dialect/test_firebird.py
test/sql/test_compiler.py

index f51244815f431e602f4488d24862c9b1bd488a13..cd8cb0ac16701f6db6a6f3ad1218c6bb3ea9c31a 100644 (file)
         needs to have a table-bound column present if the index is to be
         manually added to the table, either via inline declaration or via
         :meth:`.Table.append_constraint`.
+        :tags: bug, firebird
+        :tickets: 3038
+
+        Fixed bug where the combination of "limit" rendering as
+        "SELECT FIRST n ROWS" using a bound parameter (only firebird has both),
+        combined with column-level subqueries
+        which also feature "limit" as well as "positional" bound parameters
+        (e.g. qmark style) would erroneously assign the subquery-level positions
+        before that of the enclosing SELECT, thus returning parameters which
+        are out of order.
 
     .. change::
         :tags: bug, mssql
index 169dc2cc3400238807d762a7b439f69a9c7c19d2..a7465204a67907927e99e14308158d64d490d521 100644 (file)
@@ -1481,19 +1481,6 @@ class SQLCompiler(Compiled):
                 'within_columns_clause': False
             })
 
-        # the actual list of columns to print in the SELECT column list.
-        inner_columns = [
-            c for c in [
-                self._label_select_column(select,
-                                    column,
-                                    populate_result_map, asfrom,
-                                    column_clause_args,
-                                    name=name)
-                for name, column in select._columns_plus_names
-                ]
-            if c is not None
-        ]
-
         text = "SELECT "  # we're off to a good start !
 
         if select._hints:
@@ -1514,6 +1501,20 @@ class SQLCompiler(Compiled):
             text += self._generate_prefixes(select, select._prefixes, **kwargs)
 
         text += self.get_select_precolumns(select)
+
+        # the actual list of columns to print in the SELECT column list.
+        inner_columns = [
+            c for c in [
+                self._label_select_column(select,
+                                    column,
+                                    populate_result_map, asfrom,
+                                    column_clause_args,
+                                    name=name)
+                for name, column in select._columns_plus_names
+                ]
+            if c is not None
+        ]
+
         text += ', '.join(inner_columns)
 
         if froms:
index 222e34b939eadc0503686de10312310ad810ee96..86464c8cb084ac7af29a73d48e108d4804123450 100644 (file)
@@ -415,8 +415,8 @@ class MiscTest(fixtures.TestBase):
     @testing.provide_metadata
     def test_rowcount_flag(self):
         metadata = self.metadata
-        engine = engines.testing_engine(options={'enable_rowcount'
-                : True})
+        engine = engines.testing_engine(
+                        options={'enable_rowcount': True})
         assert engine.dialect.supports_sane_rowcount
         metadata.bind = engine
         t = Table('t1', metadata, Column('data', String(10)))
@@ -431,6 +431,7 @@ class MiscTest(fixtures.TestBase):
         r = \
             t.delete().execution_options(enable_rowcount=False).execute()
         eq_(r.rowcount, -1)
+        engine.dispose()
         engine = engines.testing_engine(options={'enable_rowcount'
                 : False})
         assert not engine.dialect.supports_sane_rowcount
@@ -444,6 +445,8 @@ class MiscTest(fixtures.TestBase):
         eq_(r.rowcount, -1)
         r = t.delete().execution_options(enable_rowcount=True).execute()
         eq_(r.rowcount, 1)
+        r.close()
+        engine.dispose()
 
     def test_percents_in_text(self):
         for expr, result in (text("select '%' from rdb$database"), '%'
index e96990c610b2cfb40f9a60f6fa8caff8e6200760..1be76c69626818f6171fb0da2c11a5cfd63968fb 100644 (file)
@@ -28,6 +28,7 @@ from sqlalchemy.engine import default
 from sqlalchemy.dialects import mysql, mssql, postgresql, oracle, \
             sqlite, sybase
 from sqlalchemy.ext.compiler import compiles
+from sqlalchemy.sql import compiler
 
 table1 = table('mytable',
     column('myid', Integer),
@@ -181,6 +182,34 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
                 checkparams=params
             )
 
+    def test_select_precol_compile_ordering(self):
+        s1 = select([column('x')]).select_from('a').limit(5).as_scalar()
+        s2 = select([s1]).limit(10)
+
+        class MyCompiler(compiler.SQLCompiler):
+            def get_select_precolumns(self, select):
+                result = ""
+                if select._limit:
+                    result += "FIRST %s " % self.process(literal(select._limit))
+                if select._offset:
+                    result += "SKIP %s " % self.process(literal(select._offset))
+                return result
+
+            def limit_clause(self, select):
+                return ""
+
+        dialect = default.DefaultDialect()
+        dialect.statement_compiler = MyCompiler
+        dialect.paramstyle = 'qmark'
+        dialect.positional = True
+        self.assert_compile(
+            s2,
+            "SELECT FIRST ? (SELECT FIRST ? x FROM a) AS anon_1",
+            checkpositional=(10, 5),
+            dialect=dialect
+        )
+
+
     def test_from_subquery(self):
         """tests placing select statements in the column clause of
         another select, for the