]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Proof-of-concept implementation of supporting bindparam for offset and limit on a...
authorDobes Vandermeer <dvandermeer@roovy.com>
Thu, 24 Apr 2014 22:20:57 +0000 (15:20 -0700)
committerDobes Vandermeer <dvandermeer@roovy.com>
Thu, 24 Apr 2014 22:20:57 +0000 (15:20 -0700)
lib/sqlalchemy/dialects/firebird/base.py
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/dialects/sqlite/base.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/selectable.py
test/orm/test_query.py

index 21db57b68db0a2436835e7d3422e61639278142d..fdadb61c1d7db1683fa90b96535891a09a681524 100644 (file)
@@ -76,7 +76,7 @@ from sqlalchemy import exc, types as sqltypes, sql, util
 from sqlalchemy.sql import expression
 from sqlalchemy.engine import base, default, reflection
 from sqlalchemy.sql import compiler
-
+from sqlalchemy.sql.elements import _literal_as_binds
 
 from sqlalchemy.types import (BIGINT, BLOB, DATE, FLOAT, INTEGER, NUMERIC,
                               SMALLINT, TEXT, TIME, TIMESTAMP, Integer)
@@ -301,9 +301,9 @@ class FBCompiler(sql.compiler.SQLCompiler):
 
         result = ""
         if select._limit:
-            result += "FIRST %s " % self.process(sql.literal(select._limit))
+            result += "FIRST %s " % self.process(_literal_as_binds(select._limit))
         if select._offset:
-            result += "SKIP %s " % self.process(sql.literal(select._offset))
+            result += "SKIP %s " % self.process(_literal_as_binds(select._offset))
         if select._distinct:
             result += "DISTINCT "
         return result
index f69a6e01085f7824c63b39c1c4167719a6702aa2..d63e3ed8791265480828a33c607f030f6b5af844 100644 (file)
@@ -346,6 +346,7 @@ from ... import sql, schema, exc, util
 from ...engine import default, reflection
 from ...sql import compiler, expression, operators
 from ... import types as sqltypes
+from sqlalchemy.sql.elements import _literal_as_binds
 
 try:
     from uuid import UUID as _python_UUID
@@ -1144,11 +1145,11 @@ class PGCompiler(compiler.SQLCompiler):
     def limit_clause(self, select):
         text = ""
         if select._limit is not None:
-            text += " \n LIMIT " + self.process(sql.literal(select._limit))
+            text += " \n LIMIT " + self.process(_literal_as_binds(select._limit))
         if select._offset is not None:
             if select._limit is None:
                 text += " \n LIMIT ALL"
-            text += " OFFSET " + self.process(sql.literal(select._offset))
+            text += " OFFSET " + self.process(_literal_as_binds(select._offset))
         return text
 
     def format_from_hint_text(self, sqltext, table, hint, iscrud):
index 90df9c1928b70900c48cba1ebe9332220e15df70..1ae56523269474922f7aea1a7682970375b4e0db 100644 (file)
@@ -185,6 +185,7 @@ from ... import types as sqltypes, schema as sa_schema
 from ... import util
 from ...engine import default, reflection
 from ...sql import compiler
+from sqlalchemy.sql.elements import _literal_as_binds
 
 from ...types import (BLOB, BOOLEAN, CHAR, DATE, DECIMAL, FLOAT, INTEGER, REAL,
                       NUMERIC, SMALLINT, TEXT, TIMESTAMP, VARCHAR)
@@ -520,11 +521,11 @@ class SQLiteCompiler(compiler.SQLCompiler):
     def limit_clause(self, select):
         text = ""
         if select._limit is not None:
-            text += "\n LIMIT " + self.process(sql.literal(select._limit))
+            text += "\n LIMIT " + self.process(_literal_as_binds(select._limit))
         if select._offset is not None:
             if select._limit is None:
                 text += "\n LIMIT " + self.process(sql.literal(-1))
-            text += " OFFSET " + self.process(sql.literal(select._offset))
+            text += " OFFSET " + self.process(_literal_as_binds(select._offset))
         else:
             text += " OFFSET " + self.process(sql.literal(0))
         return text
index 31193ab1789e2e27d1d69dc3f6c21bd1db89d1c4..b59e8a941ca00c51d7bbcf75b8b56eab9b4685c5 100644 (file)
@@ -29,6 +29,7 @@ from .. import util, exc
 import decimal
 import itertools
 import operator
+from sqlalchemy.sql.elements import _literal_as_binds
 
 RESERVED_WORDS = set([
     'all', 'analyse', 'analyze', 'and', 'any', 'array',
@@ -1625,11 +1626,11 @@ class SQLCompiler(Compiled):
     def limit_clause(self, select):
         text = ""
         if select._limit is not None:
-            text += "\n LIMIT " + self.process(elements.literal(select._limit))
+            text += "\n LIMIT " + self.process(_literal_as_binds(select._limit))
         if select._offset is not None:
             if select._limit is None:
                 text += "\n LIMIT -1"
-            text += " OFFSET " + self.process(elements.literal(select._offset))
+            text += " OFFSET " + self.process(_literal_as_binds(select._offset))
         return text
 
     def visit_table(self, table, asfrom=False, iscrud=False, ashint=False,
index afcf437e9dbf7cb1b0db675b983039e1ca5826c8..5995c1f8aee24b005207426415341620c9a1465c 100644 (file)
@@ -1562,9 +1562,9 @@ class GenerativeSelect(SelectBase):
                 self._execution_options.union(
                   {'autocommit': autocommit})
         if limit is not None:
-            self._limit = util.asint(limit)
+            self._limit = limit
         if offset is not None:
-            self._offset = util.asint(offset)
+            self._offset = offset
         self._bind = bind
 
         if order_by is not None:
@@ -1644,14 +1644,14 @@ class GenerativeSelect(SelectBase):
         """return a new selectable with the given LIMIT criterion
         applied."""
 
-        self._limit = util.asint(limit)
+        self._limit = limit
 
     @_generative
     def offset(self, offset):
         """return a new selectable with the given OFFSET criterion
         applied."""
 
-        self._offset = util.asint(offset)
+        self._offset = offset
 
     @_generative
     def order_by(self, *clauses):
@@ -1712,6 +1712,12 @@ class GenerativeSelect(SelectBase):
             self._group_by_clause = ClauseList(*clauses)
 
 
+    def _copy_internals(self, clone=_clone, **kw):
+        if isinstance(self._limit, ClauseElement):
+            self._limit = clone(self._limit)
+        if isinstance(self._offset, ClauseElement):
+            self._offset = clone(self._offset)
+
 class CompoundSelect(GenerativeSelect):
     """Forms the basis of ``UNION``, ``UNION ALL``, and other
         SELECT-based set operations.
@@ -1930,6 +1936,7 @@ class CompoundSelect(GenerativeSelect):
                 "addition of columns to underlying selectables")
 
     def _copy_internals(self, clone=_clone, **kw):
+        super(CompoundSelect, self)._copy_internals(clone, **kw)
         self._reset_exported()
         self.selects = [clone(s, **kw) for s in self.selects]
         if hasattr(self, '_col_map'):
@@ -2380,6 +2387,7 @@ class Select(HasPrefixes, GenerativeSelect):
         return False
 
     def _copy_internals(self, clone=_clone, **kw):
+        super(Select, self)._copy_internals(clone, **kw)
 
         # Select() object has been cloned and probably adapted by the
         # given clone function.  Apply the cloning function to internal
index 3b483b7c052161a79504dfdfdf3484321467325a..4b5e860e3bbef9ed9a74b3517862b7060caa343a 100644 (file)
@@ -1275,6 +1275,15 @@ class FilterTest(QueryTest, AssertsCompiledSQL):
         assert [] == sess.query(User).order_by(User.id)[3:3]
         assert [] == sess.query(User).order_by(User.id)[0:0]
 
+    def test_select_with_bindparam_limit(self):
+        """Does a query allow bindparam for the limit?"""
+        sess = create_session()
+        users = []
+
+        q1 = sess.query(self.classes.User).order_by(self.classes.User.id).limit(sa.bindparam('n'))
+        for n in xrange(1,4):
+            users[:] = q1.params(n=n).all()
+            assert len(users) == n
 
     @testing.requires.boolean_col_expressions
     def test_exists(self):