From: Dobes Vandermeer Date: Fri, 25 Apr 2014 17:42:12 +0000 (-0700) Subject: Use _offset_clause and _limit_clause, which are always Visitable and usually a BindPa... X-Git-Tag: rel_1_0_0b1~434^2~1^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4af172b644d90f1bcab3de2bd0501a9cf50dc1d5;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Use _offset_clause and _limit_clause, which are always Visitable and usually a BindParameter, instead of _offset and _limit in GenerativeSelect. --- diff --git a/lib/sqlalchemy/dialects/firebird/base.py b/lib/sqlalchemy/dialects/firebird/base.py index fdadb61c1d..431a16a6e0 100644 --- a/lib/sqlalchemy/dialects/firebird/base.py +++ b/lib/sqlalchemy/dialects/firebird/base.py @@ -300,10 +300,10 @@ class FBCompiler(sql.compiler.SQLCompiler): """ result = "" - if select._limit: - result += "FIRST %s " % self.process(_literal_as_binds(select._limit)) - if select._offset: - result += "SKIP %s " % self.process(_literal_as_binds(select._offset)) + if select._limit_clause: + result += "FIRST %s " % self.process(select._limit_clause) + if select._offset_clause: + result += "SKIP %s " % self.process(select._offset_clause) if select._distinct: result += "DISTINCT " return result diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index 9a8cddd988..6a13d1dca9 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -741,15 +741,16 @@ class MSSQLCompiler(compiler.SQLCompiler): def get_select_precolumns(self, select): """ MS-SQL puts TOP, it's version of LIMIT here """ - if select._distinct or select._limit is not None: + limit = select._limit + if select._distinct or limit is not None: s = select._distinct and "DISTINCT " or "" # ODBC drivers and possibly others # don't support bind params in the SELECT clause on SQL Server. # so have to use literal here. - if select._limit is not None: + if limit is not None: if not select._offset: - s += "TOP %d " % select._limit + s += "TOP %d " % limit return s return compiler.SQLCompiler.get_select_precolumns(self, select) diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index ba6e7b6258..4ad45f9351 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -1568,15 +1568,15 @@ class MySQLCompiler(compiler.SQLCompiler): # The latter is more readable for offsets but we're stuck with the # former until we can refine dialects by server revision. - limit, offset = select._limit, select._offset + limit_clause, offset_clause = select._limit_clause, select._offset_clause - if (limit, offset) == (None, None): + if (limit_clause, offset_clause) == (None, None): return '' - elif offset is not None: + elif offset_clause is not None: # As suggested by the MySQL docs, need to apply an # artificial limit if one wasn't provided # http://dev.mysql.com/doc/refman/5.0/en/select.html - if limit is None: + if limit_clause is None: # hardwire the upper limit. Currently # needed by OurSQL with Python 3 # (https://bugs.launchpad.net/oursql/+bug/686232), @@ -1584,15 +1584,15 @@ class MySQLCompiler(compiler.SQLCompiler): # bound as part of MySQL's "syntax" for OFFSET with # no LIMIT return ' \n LIMIT %s, %s' % ( - self.process(sql.literal(offset)), + self.process(offset_clause), "18446744073709551615") else: return ' \n LIMIT %s, %s' % ( - self.process(sql.literal(offset)), - self.process(sql.literal(limit))) + self.process(offset_clause), + self.process(limit_clause)) else: # No offset provided, so just use the limit - return ' \n LIMIT %s' % (self.process(sql.literal(limit)),) + return ' \n LIMIT %s' % (self.process(limit_clause),) def update_limit_clause(self, update_stmt): limit = update_stmt.kwargs.get('%s_limit' % self.dialect.name, None) diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index d63e3ed879..4e5f9d703f 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -1144,12 +1144,12 @@ class PGCompiler(compiler.SQLCompiler): def limit_clause(self, select): text = "" - if select._limit is not None: - text += " \n LIMIT " + self.process(_literal_as_binds(select._limit)) - if select._offset is not None: - if select._limit is None: + if select._limit_clause is not None: + text += " \n LIMIT " + self.process(select._limit_clause) + if select._offset_clause is not None: + if select._limit_clause is None: text += " \n LIMIT ALL" - text += " OFFSET " + self.process(_literal_as_binds(select._offset)) + text += " OFFSET " + self.process(select._offset_clause) return text def format_from_hint_text(self, sqltext, table, hint, iscrud): diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 1ae5652326..02582709b5 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -520,12 +520,12 @@ class SQLiteCompiler(compiler.SQLCompiler): def limit_clause(self, select): text = "" - if select._limit is not None: - text += "\n LIMIT " + self.process(_literal_as_binds(select._limit)) - if select._offset is not None: - if select._limit is None: + if select._limit_clause is not None: + text += "\n LIMIT " + self.process(select._limit_clause) + if select._offset_clause is not None: + if select._limit_clause is None: text += "\n LIMIT " + self.process(sql.literal(-1)) - text += " OFFSET " + self.process(_literal_as_binds(select._offset)) + text += " OFFSET " + self.process(select._offset_clause) else: text += " OFFSET " + self.process(sql.literal(0)) return text diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index b59e8a941c..557ab15316 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -774,7 +774,7 @@ class SQLCompiler(Compiled): text += " GROUP BY " + group_by text += self.order_by_clause(cs, **kwargs) - text += (cs._limit is not None or cs._offset is not None) and \ + text += (cs._limit_clause is not None or cs._offset_clause is not None) and \ self.limit_clause(cs) or "" if self.ctes and \ @@ -1557,7 +1557,7 @@ class SQLCompiler(Compiled): text += self.order_by_clause(select, order_by_select=order_by_select, **kwargs) - if select._limit is not None or select._offset is not None: + if select._limit_clause is not None or select._offset_clause is not None: text += self.limit_clause(select) if select._for_update_arg is not None: @@ -1625,12 +1625,12 @@ class SQLCompiler(Compiled): def limit_clause(self, select): text = "" - if select._limit is not None: - text += "\n LIMIT " + self.process(_literal_as_binds(select._limit)) - if select._offset is not None: - if select._limit is None: + if select._limit_clause is not None: + text += "\n LIMIT " + self.process(select._limit_clause) + if select._offset_clause is not None: + if select._limit_clause is None: text += "\n LIMIT -1" - text += " OFFSET " + self.process(_literal_as_binds(select._offset)) + text += " OFFSET " + self.process(select._offset_clause) return text def visit_table(self, table, asfrom=False, iscrud=False, ashint=False, diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index 5995c1f8ae..46c36e0fc5 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -10,7 +10,7 @@ SQL tables and derived rowsets. """ from .elements import ClauseElement, TextClause, ClauseList, \ - and_, Grouping, UnaryExpression, literal_column + and_, Grouping, UnaryExpression, literal_column, _literal_as_binds, BindParameter, Null from .elements import _clone, \ _literal_as_text, _interpret_as_column_or_from, _expand_cloned,\ _select_iterables, _anonymous_label, _clause_element_as_expr,\ @@ -28,6 +28,8 @@ import operator import collections from .annotation import Annotated import itertools +from sqlalchemy.sql.visitors import Visitable + def _interpret_as_from(element): insp = inspection.inspect(element, raiseerr=False) @@ -46,6 +48,22 @@ def _interpret_as_select(element): element = element.select() return element +def _offset_or_limit_clause(element, name=None, type_=None): + """ + If the element is a custom clause of some sort, returns (None, element) + If the element is a BindParameter, return (element.effective_value, element) + Otherwise, assume element is an int and create a new bindparam and return (asint(element), BindParameter(...)) + """ + if element is None: + return None + if hasattr(element, '__clause_element__'): + return element.__clause_element__() + if isinstance(element, Visitable): + return element + + value = util.asint(element) + return BindParameter(name, value, type_=type_, unique=True) + def subquery(alias, *args, **kwargs): """Return an :class:`.Alias` object derived from a :class:`.Select`. @@ -1536,8 +1554,8 @@ class GenerativeSelect(SelectBase): """ _order_by_clause = ClauseList() _group_by_clause = ClauseList() - _limit = None - _offset = None + _limit_clause = None + _offset_clause = None _for_update_arg = None def __init__(self, @@ -1562,9 +1580,9 @@ class GenerativeSelect(SelectBase): self._execution_options.union( {'autocommit': autocommit}) if limit is not None: - self._limit = limit + self._limit_clause = _offset_or_limit_clause(limit) if offset is not None: - self._offset = offset + self._offset_clause = _offset_or_limit_clause(offset) self._bind = bind if order_by is not None: @@ -1639,19 +1657,31 @@ class GenerativeSelect(SelectBase): """ self.use_labels = True + @property + def _limit(self): + if not isinstance(self._limit_clause, BindParameter): + return None + return util.asint(self._limit_clause.effective_value) + + @property + def _offset(self): + if not isinstance(self._offset_clause, BindParameter): + return None + return util.asint(self._offset_clause.effective_value) + @_generative def limit(self, limit): """return a new selectable with the given LIMIT criterion applied.""" - self._limit = limit + self._limit_clause = _offset_or_limit_clause(limit) @_generative def offset(self, offset): """return a new selectable with the given OFFSET criterion applied.""" - self._offset = offset + self._offset_clause = _offset_or_limit_clause(offset) @_generative def order_by(self, *clauses): @@ -1713,10 +1743,10 @@ class GenerativeSelect(SelectBase): 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) + if self._limit_clause is not None: + self._limit_clause = clone(self._limit_clause, **kw) + if self._offset_clause is not None: + self._offset_clause = clone(self._offset_clause, **kw) class CompoundSelect(GenerativeSelect): """Forms the basis of ``UNION``, ``UNION ALL``, and other