"""
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
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)
# 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),
# 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)
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):
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
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 \
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:
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,
"""
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,\
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)
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`.
"""
_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,
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:
"""
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):
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