mirror of
https://github.com/janeczku/calibre-web
synced 2025-11-23 02:14:48 +00:00
Move vendored libraries to vendor directory
This commit is contained in:
68
vendor/sqlalchemy/sql/__init__.py
vendored
Normal file
68
vendor/sqlalchemy/sql/__init__.py
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
# sql/__init__.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from .expression import (
|
||||
Alias,
|
||||
ClauseElement,
|
||||
ColumnCollection,
|
||||
ColumnElement,
|
||||
CompoundSelect,
|
||||
Delete,
|
||||
FromClause,
|
||||
Insert,
|
||||
Join,
|
||||
Select,
|
||||
Selectable,
|
||||
TableClause,
|
||||
Update,
|
||||
alias,
|
||||
and_,
|
||||
asc,
|
||||
between,
|
||||
bindparam,
|
||||
case,
|
||||
cast,
|
||||
collate,
|
||||
column,
|
||||
delete,
|
||||
desc,
|
||||
distinct,
|
||||
except_,
|
||||
except_all,
|
||||
exists,
|
||||
extract,
|
||||
false,
|
||||
func,
|
||||
insert,
|
||||
intersect,
|
||||
intersect_all,
|
||||
join,
|
||||
label,
|
||||
literal,
|
||||
literal_column,
|
||||
modifier,
|
||||
not_,
|
||||
null,
|
||||
or_,
|
||||
outerjoin,
|
||||
outparam,
|
||||
over,
|
||||
select,
|
||||
subquery,
|
||||
table,
|
||||
text,
|
||||
true,
|
||||
tuple_,
|
||||
type_coerce,
|
||||
union,
|
||||
union_all,
|
||||
update,
|
||||
)
|
||||
|
||||
from .visitors import ClauseVisitor
|
||||
|
||||
__tmp = locals().keys()
|
||||
__all__ = sorted([i for i in __tmp if not i.startswith('__')])
|
||||
2560
vendor/sqlalchemy/sql/compiler.py
vendored
Normal file
2560
vendor/sqlalchemy/sql/compiler.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6582
vendor/sqlalchemy/sql/expression.py
vendored
Normal file
6582
vendor/sqlalchemy/sql/expression.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
255
vendor/sqlalchemy/sql/functions.py
vendored
Normal file
255
vendor/sqlalchemy/sql/functions.py
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
# sql/functions.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from .. import types as sqltypes, schema
|
||||
from .expression import (
|
||||
ClauseList, Function, _literal_as_binds, literal_column, _type_from_args,
|
||||
cast, extract
|
||||
)
|
||||
from . import operators
|
||||
from .visitors import VisitableType
|
||||
from .. import util
|
||||
|
||||
_registry = util.defaultdict(dict)
|
||||
|
||||
|
||||
def register_function(identifier, fn, package="_default"):
|
||||
"""Associate a callable with a particular func. name.
|
||||
|
||||
This is normally called by _GenericMeta, but is also
|
||||
available by itself so that a non-Function construct
|
||||
can be associated with the :data:`.func` accessor (i.e.
|
||||
CAST, EXTRACT).
|
||||
|
||||
"""
|
||||
reg = _registry[package]
|
||||
reg[identifier] = fn
|
||||
|
||||
|
||||
class _GenericMeta(VisitableType):
|
||||
def __init__(cls, clsname, bases, clsdict):
|
||||
cls.name = name = clsdict.get('name', clsname)
|
||||
cls.identifier = identifier = clsdict.get('identifier', name)
|
||||
package = clsdict.pop('package', '_default')
|
||||
# legacy
|
||||
if '__return_type__' in clsdict:
|
||||
cls.type = clsdict['__return_type__']
|
||||
register_function(identifier, cls, package)
|
||||
super(_GenericMeta, cls).__init__(clsname, bases, clsdict)
|
||||
|
||||
|
||||
class GenericFunction(Function):
|
||||
"""Define a 'generic' function.
|
||||
|
||||
A generic function is a pre-established :class:`.Function`
|
||||
class that is instantiated automatically when called
|
||||
by name from the :data:`.func` attribute. Note that
|
||||
calling any name from :data:`.func` has the effect that
|
||||
a new :class:`.Function` instance is created automatically,
|
||||
given that name. The primary use case for defining
|
||||
a :class:`.GenericFunction` class is so that a function
|
||||
of a particular name may be given a fixed return type.
|
||||
It can also include custom argument parsing schemes as well
|
||||
as additional methods.
|
||||
|
||||
Subclasses of :class:`.GenericFunction` are automatically
|
||||
registered under the name of the class. For
|
||||
example, a user-defined function ``as_utc()`` would
|
||||
be available immediately::
|
||||
|
||||
from sqlalchemy.sql.functions import GenericFunction
|
||||
from sqlalchemy.types import DateTime
|
||||
|
||||
class as_utc(GenericFunction):
|
||||
type = DateTime
|
||||
|
||||
print select([func.as_utc()])
|
||||
|
||||
User-defined generic functions can be organized into
|
||||
packages by specifying the "package" attribute when defining
|
||||
:class:`.GenericFunction`. Third party libraries
|
||||
containing many functions may want to use this in order
|
||||
to avoid name conflicts with other systems. For example,
|
||||
if our ``as_utc()`` function were part of a package
|
||||
"time"::
|
||||
|
||||
class as_utc(GenericFunction):
|
||||
type = DateTime
|
||||
package = "time"
|
||||
|
||||
The above function would be available from :data:`.func`
|
||||
using the package name ``time``::
|
||||
|
||||
print select([func.time.as_utc()])
|
||||
|
||||
A final option is to allow the function to be accessed
|
||||
from one name in :data:`.func` but to render as a different name.
|
||||
The ``identifier`` attribute will override the name used to
|
||||
access the function as loaded from :data:`.func`, but will retain
|
||||
the usage of ``name`` as the rendered name::
|
||||
|
||||
class GeoBuffer(GenericFunction):
|
||||
type = Geometry
|
||||
package = "geo"
|
||||
name = "ST_Buffer"
|
||||
identifier = "buffer"
|
||||
|
||||
The above function will render as follows::
|
||||
|
||||
>>> print func.geo.buffer()
|
||||
ST_Buffer()
|
||||
|
||||
.. versionadded:: 0.8 :class:`.GenericFunction` now supports
|
||||
automatic registration of new functions as well as package
|
||||
and custom naming support.
|
||||
|
||||
.. versionchanged:: 0.8 The attribute name ``type`` is used
|
||||
to specify the function's return type at the class level.
|
||||
Previously, the name ``__return_type__`` was used. This
|
||||
name is still recognized for backwards-compatibility.
|
||||
|
||||
"""
|
||||
__metaclass__ = _GenericMeta
|
||||
|
||||
coerce_arguments = True
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
parsed_args = kwargs.pop('_parsed_args', None)
|
||||
if parsed_args is None:
|
||||
parsed_args = [_literal_as_binds(c) for c in args]
|
||||
self.packagenames = []
|
||||
self._bind = kwargs.get('bind', None)
|
||||
self.clause_expr = ClauseList(
|
||||
operator=operators.comma_op,
|
||||
group_contents=True, *parsed_args).self_group()
|
||||
self.type = sqltypes.to_instance(
|
||||
kwargs.pop("type_", None) or getattr(self, 'type', None))
|
||||
|
||||
|
||||
register_function("cast", cast)
|
||||
register_function("extract", extract)
|
||||
|
||||
|
||||
class next_value(GenericFunction):
|
||||
"""Represent the 'next value', given a :class:`.Sequence`
|
||||
as it's single argument.
|
||||
|
||||
Compiles into the appropriate function on each backend,
|
||||
or will raise NotImplementedError if used on a backend
|
||||
that does not provide support for sequences.
|
||||
|
||||
"""
|
||||
type = sqltypes.Integer()
|
||||
name = "next_value"
|
||||
|
||||
def __init__(self, seq, **kw):
|
||||
assert isinstance(seq, schema.Sequence), \
|
||||
"next_value() accepts a Sequence object as input."
|
||||
self._bind = kw.get('bind', None)
|
||||
self.sequence = seq
|
||||
|
||||
@property
|
||||
def _from_objects(self):
|
||||
return []
|
||||
|
||||
|
||||
class AnsiFunction(GenericFunction):
|
||||
def __init__(self, **kwargs):
|
||||
GenericFunction.__init__(self, **kwargs)
|
||||
|
||||
|
||||
class ReturnTypeFromArgs(GenericFunction):
|
||||
"""Define a function whose return type is the same as its arguments."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
args = [_literal_as_binds(c) for c in args]
|
||||
kwargs.setdefault('type_', _type_from_args(args))
|
||||
kwargs['_parsed_args'] = args
|
||||
GenericFunction.__init__(self, *args, **kwargs)
|
||||
|
||||
|
||||
class coalesce(ReturnTypeFromArgs):
|
||||
pass
|
||||
|
||||
|
||||
class max(ReturnTypeFromArgs):
|
||||
pass
|
||||
|
||||
|
||||
class min(ReturnTypeFromArgs):
|
||||
pass
|
||||
|
||||
|
||||
class sum(ReturnTypeFromArgs):
|
||||
pass
|
||||
|
||||
|
||||
class now(GenericFunction):
|
||||
type = sqltypes.DateTime
|
||||
|
||||
|
||||
class concat(GenericFunction):
|
||||
type = sqltypes.String
|
||||
|
||||
|
||||
class char_length(GenericFunction):
|
||||
type = sqltypes.Integer
|
||||
|
||||
def __init__(self, arg, **kwargs):
|
||||
GenericFunction.__init__(self, arg, **kwargs)
|
||||
|
||||
|
||||
class random(GenericFunction):
|
||||
pass
|
||||
|
||||
|
||||
class count(GenericFunction):
|
||||
"""The ANSI COUNT aggregate function. With no arguments,
|
||||
emits COUNT \*.
|
||||
|
||||
"""
|
||||
type = sqltypes.Integer
|
||||
|
||||
def __init__(self, expression=None, **kwargs):
|
||||
if expression is None:
|
||||
expression = literal_column('*')
|
||||
GenericFunction.__init__(self, expression, **kwargs)
|
||||
|
||||
|
||||
class current_date(AnsiFunction):
|
||||
type = sqltypes.Date
|
||||
|
||||
|
||||
class current_time(AnsiFunction):
|
||||
type = sqltypes.Time
|
||||
|
||||
|
||||
class current_timestamp(AnsiFunction):
|
||||
type = sqltypes.DateTime
|
||||
|
||||
|
||||
class current_user(AnsiFunction):
|
||||
type = sqltypes.String
|
||||
|
||||
|
||||
class localtime(AnsiFunction):
|
||||
type = sqltypes.DateTime
|
||||
|
||||
|
||||
class localtimestamp(AnsiFunction):
|
||||
type = sqltypes.DateTime
|
||||
|
||||
|
||||
class session_user(AnsiFunction):
|
||||
type = sqltypes.String
|
||||
|
||||
|
||||
class sysdate(AnsiFunction):
|
||||
type = sqltypes.DateTime
|
||||
|
||||
|
||||
class user(AnsiFunction):
|
||||
type = sqltypes.String
|
||||
836
vendor/sqlalchemy/sql/operators.py
vendored
Normal file
836
vendor/sqlalchemy/sql/operators.py
vendored
Normal file
@@ -0,0 +1,836 @@
|
||||
# sql/operators.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Defines operators used in SQL expressions."""
|
||||
|
||||
from operator import (
|
||||
and_, or_, inv, add, mul, sub, mod, truediv, lt, le, ne, gt, ge, eq, neg,
|
||||
getitem, lshift, rshift
|
||||
)
|
||||
|
||||
# Py2K
|
||||
from operator import (div,)
|
||||
# end Py2K
|
||||
|
||||
from ..util import symbol
|
||||
|
||||
|
||||
class Operators(object):
|
||||
"""Base of comparison and logical operators.
|
||||
|
||||
Implements base methods :meth:`operate` and :meth:`reverse_operate`,
|
||||
as well as :meth:`__and__`, :meth:`__or__`, :meth:`__invert__`.
|
||||
|
||||
Usually is used via its most common subclass
|
||||
:class:`.ColumnOperators`.
|
||||
|
||||
"""
|
||||
def __and__(self, other):
|
||||
"""Implement the ``&`` operator.
|
||||
|
||||
When used with SQL expressions, results in an
|
||||
AND operation, equivalent to
|
||||
:func:`~.expression.and_`, that is::
|
||||
|
||||
a & b
|
||||
|
||||
is equivalent to::
|
||||
|
||||
from sqlalchemy import and_
|
||||
and_(a, b)
|
||||
|
||||
Care should be taken when using ``&`` regarding
|
||||
operator precedence; the ``&`` operator has the highest precedence.
|
||||
The operands should be enclosed in parenthesis if they contain
|
||||
further sub expressions::
|
||||
|
||||
(a == 2) & (b == 4)
|
||||
|
||||
"""
|
||||
return self.operate(and_, other)
|
||||
|
||||
def __or__(self, other):
|
||||
"""Implement the ``|`` operator.
|
||||
|
||||
When used with SQL expressions, results in an
|
||||
OR operation, equivalent to
|
||||
:func:`~.expression.or_`, that is::
|
||||
|
||||
a | b
|
||||
|
||||
is equivalent to::
|
||||
|
||||
from sqlalchemy import or_
|
||||
or_(a, b)
|
||||
|
||||
Care should be taken when using ``|`` regarding
|
||||
operator precedence; the ``|`` operator has the highest precedence.
|
||||
The operands should be enclosed in parenthesis if they contain
|
||||
further sub expressions::
|
||||
|
||||
(a == 2) | (b == 4)
|
||||
|
||||
"""
|
||||
return self.operate(or_, other)
|
||||
|
||||
def __invert__(self):
|
||||
"""Implement the ``~`` operator.
|
||||
|
||||
When used with SQL expressions, results in a
|
||||
NOT operation, equivalent to
|
||||
:func:`~.expression.not_`, that is::
|
||||
|
||||
~a
|
||||
|
||||
is equivalent to::
|
||||
|
||||
from sqlalchemy import not_
|
||||
not_(a)
|
||||
|
||||
"""
|
||||
return self.operate(inv)
|
||||
|
||||
def op(self, opstring, precedence=0):
|
||||
"""produce a generic operator function.
|
||||
|
||||
e.g.::
|
||||
|
||||
somecolumn.op("*")(5)
|
||||
|
||||
produces::
|
||||
|
||||
somecolumn * 5
|
||||
|
||||
This function can also be used to make bitwise operators explicit. For
|
||||
example::
|
||||
|
||||
somecolumn.op('&')(0xff)
|
||||
|
||||
is a bitwise AND of the value in ``somecolumn``.
|
||||
|
||||
:param operator: a string which will be output as the infix operator
|
||||
between this element and the expression passed to the
|
||||
generated function.
|
||||
|
||||
:param precedence: precedence to apply to the operator, when
|
||||
parenthesizing expressions. A lower number will cause the expression
|
||||
to be parenthesized when applied against another operator with
|
||||
higher precedence. The default value of ``0`` is lower than all
|
||||
operators except for the comma (``,``) and ``AS`` operators.
|
||||
A value of 100 will be higher or equal to all operators, and -100
|
||||
will be lower than or equal to all operators.
|
||||
|
||||
.. versionadded:: 0.8 - added the 'precedence' argument.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`types_operators`
|
||||
|
||||
"""
|
||||
operator = custom_op(opstring, precedence)
|
||||
|
||||
def against(other):
|
||||
return operator(self, other)
|
||||
return against
|
||||
|
||||
def operate(self, op, *other, **kwargs):
|
||||
"""Operate on an argument.
|
||||
|
||||
This is the lowest level of operation, raises
|
||||
:class:`NotImplementedError` by default.
|
||||
|
||||
Overriding this on a subclass can allow common
|
||||
behavior to be applied to all operations.
|
||||
For example, overriding :class:`.ColumnOperators`
|
||||
to apply ``func.lower()`` to the left and right
|
||||
side::
|
||||
|
||||
class MyComparator(ColumnOperators):
|
||||
def operate(self, op, other):
|
||||
return op(func.lower(self), func.lower(other))
|
||||
|
||||
:param op: Operator callable.
|
||||
:param \*other: the 'other' side of the operation. Will
|
||||
be a single scalar for most operations.
|
||||
:param \**kwargs: modifiers. These may be passed by special
|
||||
operators such as :meth:`ColumnOperators.contains`.
|
||||
|
||||
|
||||
"""
|
||||
raise NotImplementedError(str(op))
|
||||
|
||||
def reverse_operate(self, op, other, **kwargs):
|
||||
"""Reverse operate on an argument.
|
||||
|
||||
Usage is the same as :meth:`operate`.
|
||||
|
||||
"""
|
||||
raise NotImplementedError(str(op))
|
||||
|
||||
|
||||
class custom_op(object):
|
||||
"""Represent a 'custom' operator.
|
||||
|
||||
:class:`.custom_op` is normally instantitated when the
|
||||
:meth:`.ColumnOperators.op` method is used to create a
|
||||
custom operator callable. The class can also be used directly
|
||||
when programmatically constructing expressions. E.g.
|
||||
to represent the "factorial" operation::
|
||||
|
||||
from sqlalchemy.sql import UnaryExpression
|
||||
from sqlalchemy.sql import operators
|
||||
from sqlalchemy import Numeric
|
||||
|
||||
unary = UnaryExpression(table.c.somecolumn,
|
||||
modifier=operators.custom_op("!"),
|
||||
type_=Numeric)
|
||||
|
||||
"""
|
||||
__name__ = 'custom_op'
|
||||
|
||||
def __init__(self, opstring, precedence=0):
|
||||
self.opstring = opstring
|
||||
self.precedence = precedence
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, custom_op) and \
|
||||
other.opstring == self.opstring
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
|
||||
def __call__(self, left, right, **kw):
|
||||
return left.operate(self, right, **kw)
|
||||
|
||||
|
||||
class ColumnOperators(Operators):
|
||||
"""Defines boolean, comparison, and other operators for
|
||||
:class:`.ColumnElement` expressions.
|
||||
|
||||
By default, all methods call down to
|
||||
:meth:`.operate` or :meth:`.reverse_operate`,
|
||||
passing in the appropriate operator function from the
|
||||
Python builtin ``operator`` module or
|
||||
a SQLAlchemy-specific operator function from
|
||||
:mod:`sqlalchemy.expression.operators`. For example
|
||||
the ``__eq__`` function::
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.operate(operators.eq, other)
|
||||
|
||||
Where ``operators.eq`` is essentially::
|
||||
|
||||
def eq(a, b):
|
||||
return a == b
|
||||
|
||||
The core column expression unit :class:`.ColumnElement`
|
||||
overrides :meth:`.Operators.operate` and others
|
||||
to return further :class:`.ColumnElement` constructs,
|
||||
so that the ``==`` operation above is replaced by a clause
|
||||
construct.
|
||||
|
||||
See also:
|
||||
|
||||
:ref:`types_operators`
|
||||
|
||||
:attr:`.TypeEngine.comparator_factory`
|
||||
|
||||
:class:`.ColumnOperators`
|
||||
|
||||
:class:`.PropComparator`
|
||||
|
||||
"""
|
||||
|
||||
timetuple = None
|
||||
"""Hack, allows datetime objects to be compared on the LHS."""
|
||||
|
||||
def __lt__(self, other):
|
||||
"""Implement the ``<`` operator.
|
||||
|
||||
In a column context, produces the clause ``a < b``.
|
||||
|
||||
"""
|
||||
return self.operate(lt, other)
|
||||
|
||||
def __le__(self, other):
|
||||
"""Implement the ``<=`` operator.
|
||||
|
||||
In a column context, produces the clause ``a <= b``.
|
||||
|
||||
"""
|
||||
return self.operate(le, other)
|
||||
|
||||
__hash__ = Operators.__hash__
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Implement the ``==`` operator.
|
||||
|
||||
In a column context, produces the clause ``a = b``.
|
||||
If the target is ``None``, produces ``a IS NULL``.
|
||||
|
||||
"""
|
||||
return self.operate(eq, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
"""Implement the ``!=`` operator.
|
||||
|
||||
In a column context, produces the clause ``a != b``.
|
||||
If the target is ``None``, produces ``a IS NOT NULL``.
|
||||
|
||||
"""
|
||||
return self.operate(ne, other)
|
||||
|
||||
def __gt__(self, other):
|
||||
"""Implement the ``>`` operator.
|
||||
|
||||
In a column context, produces the clause ``a > b``.
|
||||
|
||||
"""
|
||||
return self.operate(gt, other)
|
||||
|
||||
def __ge__(self, other):
|
||||
"""Implement the ``>=`` operator.
|
||||
|
||||
In a column context, produces the clause ``a >= b``.
|
||||
|
||||
"""
|
||||
return self.operate(ge, other)
|
||||
|
||||
def __neg__(self):
|
||||
"""Implement the ``-`` operator.
|
||||
|
||||
In a column context, produces the clause ``-a``.
|
||||
|
||||
"""
|
||||
return self.operate(neg)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Implement the [] operator.
|
||||
|
||||
This can be used by some database-specific types
|
||||
such as Postgresql ARRAY and HSTORE.
|
||||
|
||||
"""
|
||||
return self.operate(getitem, index)
|
||||
|
||||
def __lshift__(self, other):
|
||||
"""implement the << operator.
|
||||
|
||||
Not used by SQLAlchemy core, this is provided
|
||||
for custom operator systems which want to use
|
||||
<< as an extension point.
|
||||
"""
|
||||
return self.operate(lshift, other)
|
||||
|
||||
def __rshift__(self, other):
|
||||
"""implement the >> operator.
|
||||
|
||||
Not used by SQLAlchemy core, this is provided
|
||||
for custom operator systems which want to use
|
||||
>> as an extension point.
|
||||
"""
|
||||
return self.operate(rshift, other)
|
||||
|
||||
def concat(self, other):
|
||||
"""Implement the 'concat' operator.
|
||||
|
||||
In a column context, produces the clause ``a || b``,
|
||||
or uses the ``concat()`` operator on MySQL.
|
||||
|
||||
"""
|
||||
return self.operate(concat_op, other)
|
||||
|
||||
def like(self, other, escape=None):
|
||||
"""Implement the ``like`` operator.
|
||||
|
||||
In a column context, produces the clause ``a LIKE other``.
|
||||
|
||||
E.g.::
|
||||
|
||||
select([sometable]).where(sometable.c.column.like("%foobar%"))
|
||||
|
||||
:param other: expression to be compared
|
||||
:param escape: optional escape character, renders the ``ESCAPE``
|
||||
keyword, e.g.::
|
||||
|
||||
somecolumn.like("foo/%bar", escape="/")
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`.ColumnOperators.ilike`
|
||||
|
||||
"""
|
||||
return self.operate(like_op, other, escape=escape)
|
||||
|
||||
def ilike(self, other, escape=None):
|
||||
"""Implement the ``ilike`` operator.
|
||||
|
||||
In a column context, produces the clause ``a ILIKE other``.
|
||||
|
||||
E.g.::
|
||||
|
||||
select([sometable]).where(sometable.c.column.ilike("%foobar%"))
|
||||
|
||||
:param other: expression to be compared
|
||||
:param escape: optional escape character, renders the ``ESCAPE``
|
||||
keyword, e.g.::
|
||||
|
||||
somecolumn.ilike("foo/%bar", escape="/")
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`.ColumnOperators.like`
|
||||
|
||||
"""
|
||||
return self.operate(ilike_op, other, escape=escape)
|
||||
|
||||
def in_(self, other):
|
||||
"""Implement the ``in`` operator.
|
||||
|
||||
In a column context, produces the clause ``a IN other``.
|
||||
"other" may be a tuple/list of column expressions,
|
||||
or a :func:`~.expression.select` construct.
|
||||
|
||||
"""
|
||||
return self.operate(in_op, other)
|
||||
|
||||
def notin_(self, other):
|
||||
"""implement the ``NOT IN`` operator.
|
||||
|
||||
This is equivalent to using negation with :meth:`.ColumnOperators.in_`,
|
||||
i.e. ``~x.in_(y)``.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`.ColumnOperators.in_`
|
||||
|
||||
"""
|
||||
return self.operate(notin_op, other)
|
||||
|
||||
def notlike(self, other, escape=None):
|
||||
"""implement the ``NOT LIKE`` operator.
|
||||
|
||||
This is equivalent to using negation with
|
||||
:meth:`.ColumnOperators.like`, i.e. ``~x.like(y)``.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`.ColumnOperators.like`
|
||||
|
||||
"""
|
||||
return self.operate(notlike_op, other, escape=escape)
|
||||
|
||||
def notilike(self, other, escape=None):
|
||||
"""implement the ``NOT ILIKE`` operator.
|
||||
|
||||
This is equivalent to using negation with
|
||||
:meth:`.ColumnOperators.ilike`, i.e. ``~x.ilike(y)``.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
.. seealso::
|
||||
|
||||
:meth:`.ColumnOperators.ilike`
|
||||
|
||||
"""
|
||||
return self.operate(notilike_op, other, escape=escape)
|
||||
|
||||
def is_(self, other):
|
||||
"""Implement the ``IS`` operator.
|
||||
|
||||
Normally, ``IS`` is generated automatically when comparing to a
|
||||
value of ``None``, which resolves to ``NULL``. However, explicit
|
||||
usage of ``IS`` may be desirable if comparing to boolean values
|
||||
on certain platforms.
|
||||
|
||||
.. versionadded:: 0.7.9
|
||||
|
||||
.. seealso:: :meth:`.ColumnOperators.isnot`
|
||||
|
||||
"""
|
||||
return self.operate(is_, other)
|
||||
|
||||
def isnot(self, other):
|
||||
"""Implement the ``IS NOT`` operator.
|
||||
|
||||
Normally, ``IS NOT`` is generated automatically when comparing to a
|
||||
value of ``None``, which resolves to ``NULL``. However, explicit
|
||||
usage of ``IS NOT`` may be desirable if comparing to boolean values
|
||||
on certain platforms.
|
||||
|
||||
.. versionadded:: 0.7.9
|
||||
|
||||
.. seealso:: :meth:`.ColumnOperators.is_`
|
||||
|
||||
"""
|
||||
return self.operate(isnot, other)
|
||||
|
||||
def startswith(self, other, **kwargs):
|
||||
"""Implement the ``startwith`` operator.
|
||||
|
||||
In a column context, produces the clause ``LIKE '<other>%'``
|
||||
|
||||
"""
|
||||
return self.operate(startswith_op, other, **kwargs)
|
||||
|
||||
def endswith(self, other, **kwargs):
|
||||
"""Implement the 'endswith' operator.
|
||||
|
||||
In a column context, produces the clause ``LIKE '%<other>'``
|
||||
|
||||
"""
|
||||
return self.operate(endswith_op, other, **kwargs)
|
||||
|
||||
def contains(self, other, **kwargs):
|
||||
"""Implement the 'contains' operator.
|
||||
|
||||
In a column context, produces the clause ``LIKE '%<other>%'``
|
||||
|
||||
"""
|
||||
return self.operate(contains_op, other, **kwargs)
|
||||
|
||||
def match(self, other, **kwargs):
|
||||
"""Implements the 'match' operator.
|
||||
|
||||
In a column context, this produces a MATCH clause, i.e.
|
||||
``MATCH '<other>'``. The allowed contents of ``other``
|
||||
are database backend specific.
|
||||
|
||||
"""
|
||||
return self.operate(match_op, other, **kwargs)
|
||||
|
||||
def desc(self):
|
||||
"""Produce a :func:`~.expression.desc` clause against the
|
||||
parent object."""
|
||||
return self.operate(desc_op)
|
||||
|
||||
def asc(self):
|
||||
"""Produce a :func:`~.expression.asc` clause against the
|
||||
parent object."""
|
||||
return self.operate(asc_op)
|
||||
|
||||
def nullsfirst(self):
|
||||
"""Produce a :func:`~.expression.nullsfirst` clause against the
|
||||
parent object."""
|
||||
return self.operate(nullsfirst_op)
|
||||
|
||||
def nullslast(self):
|
||||
"""Produce a :func:`~.expression.nullslast` clause against the
|
||||
parent object."""
|
||||
return self.operate(nullslast_op)
|
||||
|
||||
def collate(self, collation):
|
||||
"""Produce a :func:`~.expression.collate` clause against
|
||||
the parent object, given the collation string."""
|
||||
return self.operate(collate, collation)
|
||||
|
||||
def __radd__(self, other):
|
||||
"""Implement the ``+`` operator in reverse.
|
||||
|
||||
See :meth:`.ColumnOperators.__add__`.
|
||||
|
||||
"""
|
||||
return self.reverse_operate(add, other)
|
||||
|
||||
def __rsub__(self, other):
|
||||
"""Implement the ``-`` operator in reverse.
|
||||
|
||||
See :meth:`.ColumnOperators.__sub__`.
|
||||
|
||||
"""
|
||||
return self.reverse_operate(sub, other)
|
||||
|
||||
def __rmul__(self, other):
|
||||
"""Implement the ``*`` operator in reverse.
|
||||
|
||||
See :meth:`.ColumnOperators.__mul__`.
|
||||
|
||||
"""
|
||||
return self.reverse_operate(mul, other)
|
||||
|
||||
def __rdiv__(self, other):
|
||||
"""Implement the ``/`` operator in reverse.
|
||||
|
||||
See :meth:`.ColumnOperators.__div__`.
|
||||
|
||||
"""
|
||||
return self.reverse_operate(div, other)
|
||||
|
||||
def between(self, cleft, cright):
|
||||
"""Produce a :func:`~.expression.between` clause against
|
||||
the parent object, given the lower and upper range."""
|
||||
return self.operate(between_op, cleft, cright)
|
||||
|
||||
def distinct(self):
|
||||
"""Produce a :func:`~.expression.distinct` clause against the
|
||||
parent object.
|
||||
|
||||
"""
|
||||
return self.operate(distinct_op)
|
||||
|
||||
def __add__(self, other):
|
||||
"""Implement the ``+`` operator.
|
||||
|
||||
In a column context, produces the clause ``a + b``
|
||||
if the parent object has non-string affinity.
|
||||
If the parent object has a string affinity,
|
||||
produces the concatenation operator, ``a || b`` -
|
||||
see :meth:`.ColumnOperators.concat`.
|
||||
|
||||
"""
|
||||
return self.operate(add, other)
|
||||
|
||||
def __sub__(self, other):
|
||||
"""Implement the ``-`` operator.
|
||||
|
||||
In a column context, produces the clause ``a - b``.
|
||||
|
||||
"""
|
||||
return self.operate(sub, other)
|
||||
|
||||
def __mul__(self, other):
|
||||
"""Implement the ``*`` operator.
|
||||
|
||||
In a column context, produces the clause ``a * b``.
|
||||
|
||||
"""
|
||||
return self.operate(mul, other)
|
||||
|
||||
def __div__(self, other):
|
||||
"""Implement the ``/`` operator.
|
||||
|
||||
In a column context, produces the clause ``a / b``.
|
||||
|
||||
"""
|
||||
return self.operate(div, other)
|
||||
|
||||
def __mod__(self, other):
|
||||
"""Implement the ``%`` operator.
|
||||
|
||||
In a column context, produces the clause ``a % b``.
|
||||
|
||||
"""
|
||||
return self.operate(mod, other)
|
||||
|
||||
def __truediv__(self, other):
|
||||
"""Implement the ``//`` operator.
|
||||
|
||||
In a column context, produces the clause ``a / b``.
|
||||
|
||||
"""
|
||||
return self.operate(truediv, other)
|
||||
|
||||
def __rtruediv__(self, other):
|
||||
"""Implement the ``//`` operator in reverse.
|
||||
|
||||
See :meth:`.ColumnOperators.__truediv__`.
|
||||
|
||||
"""
|
||||
return self.reverse_operate(truediv, other)
|
||||
|
||||
|
||||
def from_():
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def as_():
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def exists():
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def is_(a, b):
|
||||
return a.is_(b)
|
||||
|
||||
|
||||
def isnot(a, b):
|
||||
return a.isnot(b)
|
||||
|
||||
|
||||
def collate(a, b):
|
||||
return a.collate(b)
|
||||
|
||||
|
||||
def op(a, opstring, b):
|
||||
return a.op(opstring)(b)
|
||||
|
||||
|
||||
def like_op(a, b, escape=None):
|
||||
return a.like(b, escape=escape)
|
||||
|
||||
|
||||
def notlike_op(a, b, escape=None):
|
||||
return a.notlike(b, escape=escape)
|
||||
|
||||
|
||||
def ilike_op(a, b, escape=None):
|
||||
return a.ilike(b, escape=escape)
|
||||
|
||||
|
||||
def notilike_op(a, b, escape=None):
|
||||
return a.notilike(b, escape=escape)
|
||||
|
||||
|
||||
def between_op(a, b, c):
|
||||
return a.between(b, c)
|
||||
|
||||
|
||||
def in_op(a, b):
|
||||
return a.in_(b)
|
||||
|
||||
|
||||
def notin_op(a, b):
|
||||
return a.notin_(b)
|
||||
|
||||
|
||||
def distinct_op(a):
|
||||
return a.distinct()
|
||||
|
||||
|
||||
def startswith_op(a, b, escape=None):
|
||||
return a.startswith(b, escape=escape)
|
||||
|
||||
|
||||
def notstartswith_op(a, b, escape=None):
|
||||
return ~a.startswith(b, escape=escape)
|
||||
|
||||
|
||||
def endswith_op(a, b, escape=None):
|
||||
return a.endswith(b, escape=escape)
|
||||
|
||||
|
||||
def notendswith_op(a, b, escape=None):
|
||||
return ~a.endswith(b, escape=escape)
|
||||
|
||||
|
||||
def contains_op(a, b, escape=None):
|
||||
return a.contains(b, escape=escape)
|
||||
|
||||
|
||||
def notcontains_op(a, b, escape=None):
|
||||
return ~a.contains(b, escape=escape)
|
||||
|
||||
|
||||
def match_op(a, b):
|
||||
return a.match(b)
|
||||
|
||||
|
||||
def comma_op(a, b):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def concat_op(a, b):
|
||||
return a.concat(b)
|
||||
|
||||
|
||||
def desc_op(a):
|
||||
return a.desc()
|
||||
|
||||
|
||||
def asc_op(a):
|
||||
return a.asc()
|
||||
|
||||
|
||||
def nullsfirst_op(a):
|
||||
return a.nullsfirst()
|
||||
|
||||
|
||||
def nullslast_op(a):
|
||||
return a.nullslast()
|
||||
|
||||
|
||||
_commutative = set([eq, ne, add, mul])
|
||||
|
||||
_comparison = set([eq, ne, lt, gt, ge, le, between_op])
|
||||
|
||||
|
||||
def is_comparison(op):
|
||||
return op in _comparison
|
||||
|
||||
|
||||
def is_commutative(op):
|
||||
return op in _commutative
|
||||
|
||||
|
||||
def is_ordering_modifier(op):
|
||||
return op in (asc_op, desc_op,
|
||||
nullsfirst_op, nullslast_op)
|
||||
|
||||
_associative = _commutative.union([concat_op, and_, or_])
|
||||
|
||||
_natural_self_precedent = _associative.union([getitem])
|
||||
"""Operators where if we have (a op b) op c, we don't want to
|
||||
parenthesize (a op b).
|
||||
|
||||
"""
|
||||
|
||||
_smallest = symbol('_smallest', canonical=-100)
|
||||
_largest = symbol('_largest', canonical=100)
|
||||
|
||||
_PRECEDENCE = {
|
||||
from_: 15,
|
||||
getitem: 15,
|
||||
mul: 8,
|
||||
truediv: 8,
|
||||
# Py2K
|
||||
div: 8,
|
||||
# end Py2K
|
||||
mod: 8,
|
||||
neg: 8,
|
||||
add: 7,
|
||||
sub: 7,
|
||||
|
||||
concat_op: 6,
|
||||
match_op: 6,
|
||||
|
||||
ilike_op: 6,
|
||||
notilike_op: 6,
|
||||
like_op: 6,
|
||||
notlike_op: 6,
|
||||
in_op: 6,
|
||||
notin_op: 6,
|
||||
|
||||
is_: 6,
|
||||
isnot: 6,
|
||||
|
||||
eq: 5,
|
||||
ne: 5,
|
||||
gt: 5,
|
||||
lt: 5,
|
||||
ge: 5,
|
||||
le: 5,
|
||||
|
||||
between_op: 5,
|
||||
distinct_op: 5,
|
||||
inv: 5,
|
||||
and_: 3,
|
||||
or_: 2,
|
||||
comma_op: -1,
|
||||
collate: 7,
|
||||
as_: -1,
|
||||
exists: 0,
|
||||
_smallest: _smallest,
|
||||
_largest: _largest
|
||||
}
|
||||
|
||||
|
||||
def is_precedent(operator, against):
|
||||
if operator is against and operator in _natural_self_precedent:
|
||||
return False
|
||||
else:
|
||||
return (_PRECEDENCE.get(operator,
|
||||
getattr(operator, 'precedence', _smallest)) <=
|
||||
_PRECEDENCE.get(against,
|
||||
getattr(against, 'precedence', _largest)))
|
||||
921
vendor/sqlalchemy/sql/util.py
vendored
Normal file
921
vendor/sqlalchemy/sql/util.py
vendored
Normal file
@@ -0,0 +1,921 @@
|
||||
# sql/util.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from .. import exc, schema, util, sql
|
||||
from ..util import topological
|
||||
from . import expression, operators, visitors
|
||||
from itertools import chain
|
||||
from collections import deque
|
||||
|
||||
"""Utility functions that build upon SQL and Schema constructs."""
|
||||
|
||||
|
||||
def sort_tables(tables, skip_fn=None, extra_dependencies=None):
|
||||
"""sort a collection of Table objects in order of
|
||||
their foreign-key dependency."""
|
||||
|
||||
tables = list(tables)
|
||||
tuples = []
|
||||
if extra_dependencies is not None:
|
||||
tuples.extend(extra_dependencies)
|
||||
|
||||
def visit_foreign_key(fkey):
|
||||
if fkey.use_alter:
|
||||
return
|
||||
elif skip_fn and skip_fn(fkey):
|
||||
return
|
||||
parent_table = fkey.column.table
|
||||
if parent_table in tables:
|
||||
child_table = fkey.parent.table
|
||||
if parent_table is not child_table:
|
||||
tuples.append((parent_table, child_table))
|
||||
|
||||
for table in tables:
|
||||
visitors.traverse(table,
|
||||
{'schema_visitor': True},
|
||||
{'foreign_key': visit_foreign_key})
|
||||
|
||||
tuples.extend(
|
||||
[parent, table] for parent in table._extra_dependencies
|
||||
)
|
||||
|
||||
return list(topological.sort(tuples, tables))
|
||||
|
||||
|
||||
def find_join_source(clauses, join_to):
|
||||
"""Given a list of FROM clauses and a selectable,
|
||||
return the first index and element from the list of
|
||||
clauses which can be joined against the selectable. returns
|
||||
None, None if no match is found.
|
||||
|
||||
e.g.::
|
||||
|
||||
clause1 = table1.join(table2)
|
||||
clause2 = table4.join(table5)
|
||||
|
||||
join_to = table2.join(table3)
|
||||
|
||||
find_join_source([clause1, clause2], join_to) == clause1
|
||||
|
||||
"""
|
||||
|
||||
selectables = list(expression._from_objects(join_to))
|
||||
for i, f in enumerate(clauses):
|
||||
for s in selectables:
|
||||
if f.is_derived_from(s):
|
||||
return i, f
|
||||
else:
|
||||
return None, None
|
||||
|
||||
|
||||
def visit_binary_product(fn, expr):
|
||||
"""Produce a traversal of the given expression, delivering
|
||||
column comparisons to the given function.
|
||||
|
||||
The function is of the form::
|
||||
|
||||
def my_fn(binary, left, right)
|
||||
|
||||
For each binary expression located which has a
|
||||
comparison operator, the product of "left" and
|
||||
"right" will be delivered to that function,
|
||||
in terms of that binary.
|
||||
|
||||
Hence an expression like::
|
||||
|
||||
and_(
|
||||
(a + b) == q + func.sum(e + f),
|
||||
j == r
|
||||
)
|
||||
|
||||
would have the traversal::
|
||||
|
||||
a <eq> q
|
||||
a <eq> e
|
||||
a <eq> f
|
||||
b <eq> q
|
||||
b <eq> e
|
||||
b <eq> f
|
||||
j <eq> r
|
||||
|
||||
That is, every combination of "left" and
|
||||
"right" that doesn't further contain
|
||||
a binary comparison is passed as pairs.
|
||||
|
||||
"""
|
||||
stack = []
|
||||
|
||||
def visit(element):
|
||||
if isinstance(element, (expression.ScalarSelect)):
|
||||
# we dont want to dig into correlated subqueries,
|
||||
# those are just column elements by themselves
|
||||
yield element
|
||||
elif element.__visit_name__ == 'binary' and \
|
||||
operators.is_comparison(element.operator):
|
||||
stack.insert(0, element)
|
||||
for l in visit(element.left):
|
||||
for r in visit(element.right):
|
||||
fn(stack[0], l, r)
|
||||
stack.pop(0)
|
||||
for elem in element.get_children():
|
||||
visit(elem)
|
||||
else:
|
||||
if isinstance(element, expression.ColumnClause):
|
||||
yield element
|
||||
for elem in element.get_children():
|
||||
for e in visit(elem):
|
||||
yield e
|
||||
list(visit(expr))
|
||||
|
||||
|
||||
def find_tables(clause, check_columns=False,
|
||||
include_aliases=False, include_joins=False,
|
||||
include_selects=False, include_crud=False):
|
||||
"""locate Table objects within the given expression."""
|
||||
|
||||
tables = []
|
||||
_visitors = {}
|
||||
|
||||
if include_selects:
|
||||
_visitors['select'] = _visitors['compound_select'] = tables.append
|
||||
|
||||
if include_joins:
|
||||
_visitors['join'] = tables.append
|
||||
|
||||
if include_aliases:
|
||||
_visitors['alias'] = tables.append
|
||||
|
||||
if include_crud:
|
||||
_visitors['insert'] = _visitors['update'] = \
|
||||
_visitors['delete'] = lambda ent: tables.append(ent.table)
|
||||
|
||||
if check_columns:
|
||||
def visit_column(column):
|
||||
tables.append(column.table)
|
||||
_visitors['column'] = visit_column
|
||||
|
||||
_visitors['table'] = tables.append
|
||||
|
||||
visitors.traverse(clause, {'column_collections': False}, _visitors)
|
||||
return tables
|
||||
|
||||
|
||||
def find_columns(clause):
|
||||
"""locate Column objects within the given expression."""
|
||||
|
||||
cols = util.column_set()
|
||||
visitors.traverse(clause, {}, {'column': cols.add})
|
||||
return cols
|
||||
|
||||
|
||||
def unwrap_order_by(clause):
|
||||
"""Break up an 'order by' expression into individual column-expressions,
|
||||
without DESC/ASC/NULLS FIRST/NULLS LAST"""
|
||||
|
||||
cols = util.column_set()
|
||||
stack = deque([clause])
|
||||
while stack:
|
||||
t = stack.popleft()
|
||||
if isinstance(t, expression.ColumnElement) and \
|
||||
(
|
||||
not isinstance(t, expression.UnaryExpression) or \
|
||||
not operators.is_ordering_modifier(t.modifier)
|
||||
):
|
||||
cols.add(t)
|
||||
else:
|
||||
for c in t.get_children():
|
||||
stack.append(c)
|
||||
return cols
|
||||
|
||||
|
||||
def clause_is_present(clause, search):
|
||||
"""Given a target clause and a second to search within, return True
|
||||
if the target is plainly present in the search without any
|
||||
subqueries or aliases involved.
|
||||
|
||||
Basically descends through Joins.
|
||||
|
||||
"""
|
||||
|
||||
stack = [search]
|
||||
while stack:
|
||||
elem = stack.pop()
|
||||
if clause == elem: # use == here so that Annotated's compare
|
||||
return True
|
||||
elif isinstance(elem, expression.Join):
|
||||
stack.extend((elem.left, elem.right))
|
||||
return False
|
||||
|
||||
|
||||
def bind_values(clause):
|
||||
"""Return an ordered list of "bound" values in the given clause.
|
||||
|
||||
E.g.::
|
||||
|
||||
>>> expr = and_(
|
||||
... table.c.foo==5, table.c.foo==7
|
||||
... )
|
||||
>>> bind_values(expr)
|
||||
[5, 7]
|
||||
"""
|
||||
|
||||
v = []
|
||||
|
||||
def visit_bindparam(bind):
|
||||
v.append(bind.effective_value)
|
||||
|
||||
visitors.traverse(clause, {}, {'bindparam': visit_bindparam})
|
||||
return v
|
||||
|
||||
|
||||
def _quote_ddl_expr(element):
|
||||
if isinstance(element, basestring):
|
||||
element = element.replace("'", "''")
|
||||
return "'%s'" % element
|
||||
else:
|
||||
return repr(element)
|
||||
|
||||
|
||||
class _repr_params(object):
|
||||
"""A string view of bound parameters, truncating
|
||||
display to the given number of 'multi' parameter sets.
|
||||
|
||||
"""
|
||||
def __init__(self, params, batches):
|
||||
self.params = params
|
||||
self.batches = batches
|
||||
|
||||
def __repr__(self):
|
||||
if isinstance(self.params, (list, tuple)) and \
|
||||
len(self.params) > self.batches and \
|
||||
isinstance(self.params[0], (list, dict, tuple)):
|
||||
msg = " ... displaying %i of %i total bound parameter sets ... "
|
||||
return ' '.join((
|
||||
repr(self.params[:self.batches - 2])[0:-1],
|
||||
msg % (self.batches, len(self.params)),
|
||||
repr(self.params[-2:])[1:]
|
||||
))
|
||||
else:
|
||||
return repr(self.params)
|
||||
|
||||
|
||||
def expression_as_ddl(clause):
|
||||
"""Given a SQL expression, convert for usage in DDL, such as
|
||||
CREATE INDEX and CHECK CONSTRAINT.
|
||||
|
||||
Converts bind params into quoted literals, column identifiers
|
||||
into detached column constructs so that the parent table
|
||||
identifier is not included.
|
||||
|
||||
.. deprecated:: this function is removed in 0.9.0.
|
||||
|
||||
"""
|
||||
def repl(element):
|
||||
if isinstance(element, expression.BindParameter):
|
||||
return expression.literal_column(_quote_ddl_expr(element.value))
|
||||
elif isinstance(element, expression.ColumnClause) and \
|
||||
element.table is not None:
|
||||
col = expression.column(element.name)
|
||||
col.quote = element.quote
|
||||
return col
|
||||
else:
|
||||
return None
|
||||
|
||||
return visitors.replacement_traverse(clause, {}, repl)
|
||||
|
||||
|
||||
def adapt_criterion_to_null(crit, nulls):
|
||||
"""given criterion containing bind params, convert selected elements
|
||||
to IS NULL.
|
||||
|
||||
"""
|
||||
|
||||
def visit_binary(binary):
|
||||
if isinstance(binary.left, expression.BindParameter) \
|
||||
and binary.left._identifying_key in nulls:
|
||||
# reverse order if the NULL is on the left side
|
||||
binary.left = binary.right
|
||||
binary.right = expression.null()
|
||||
binary.operator = operators.is_
|
||||
binary.negate = operators.isnot
|
||||
elif isinstance(binary.right, expression.BindParameter) \
|
||||
and binary.right._identifying_key in nulls:
|
||||
binary.right = expression.null()
|
||||
binary.operator = operators.is_
|
||||
binary.negate = operators.isnot
|
||||
|
||||
return visitors.cloned_traverse(crit, {}, {'binary': visit_binary})
|
||||
|
||||
|
||||
def join_condition(a, b, ignore_nonexistent_tables=False,
|
||||
a_subset=None,
|
||||
consider_as_foreign_keys=None):
|
||||
"""create a join condition between two tables or selectables.
|
||||
|
||||
e.g.::
|
||||
|
||||
join_condition(tablea, tableb)
|
||||
|
||||
would produce an expression along the lines of::
|
||||
|
||||
tablea.c.id==tableb.c.tablea_id
|
||||
|
||||
The join is determined based on the foreign key relationships
|
||||
between the two selectables. If there are multiple ways
|
||||
to join, or no way to join, an error is raised.
|
||||
|
||||
:param ignore_nonexistent_tables: Deprecated - this
|
||||
flag is no longer used. Only resolution errors regarding
|
||||
the two given tables are propagated.
|
||||
|
||||
:param a_subset: An optional expression that is a sub-component
|
||||
of ``a``. An attempt will be made to join to just this sub-component
|
||||
first before looking at the full ``a`` construct, and if found
|
||||
will be successful even if there are other ways to join to ``a``.
|
||||
This allows the "right side" of a join to be passed thereby
|
||||
providing a "natural join".
|
||||
|
||||
"""
|
||||
crit = []
|
||||
constraints = set()
|
||||
|
||||
for left in (a_subset, a):
|
||||
if left is None:
|
||||
continue
|
||||
for fk in sorted(
|
||||
b.foreign_keys,
|
||||
key=lambda fk: fk.parent._creation_order):
|
||||
if consider_as_foreign_keys is not None and \
|
||||
fk.parent not in consider_as_foreign_keys:
|
||||
continue
|
||||
try:
|
||||
col = fk.get_referent(left)
|
||||
except exc.NoReferenceError, nrte:
|
||||
if nrte.table_name == left.name:
|
||||
raise
|
||||
else:
|
||||
continue
|
||||
|
||||
if col is not None:
|
||||
crit.append(col == fk.parent)
|
||||
constraints.add(fk.constraint)
|
||||
if left is not b:
|
||||
for fk in sorted(
|
||||
left.foreign_keys,
|
||||
key=lambda fk: fk.parent._creation_order):
|
||||
if consider_as_foreign_keys is not None and \
|
||||
fk.parent not in consider_as_foreign_keys:
|
||||
continue
|
||||
try:
|
||||
col = fk.get_referent(b)
|
||||
except exc.NoReferenceError, nrte:
|
||||
if nrte.table_name == b.name:
|
||||
raise
|
||||
else:
|
||||
# this is totally covered. can't get
|
||||
# coverage to mark it.
|
||||
continue
|
||||
|
||||
if col is not None:
|
||||
crit.append(col == fk.parent)
|
||||
constraints.add(fk.constraint)
|
||||
if crit:
|
||||
break
|
||||
|
||||
if len(crit) == 0:
|
||||
if isinstance(b, expression.FromGrouping):
|
||||
hint = " Perhaps you meant to convert the right side to a "\
|
||||
"subquery using alias()?"
|
||||
else:
|
||||
hint = ""
|
||||
raise exc.NoForeignKeysError(
|
||||
"Can't find any foreign key relationships "
|
||||
"between '%s' and '%s'.%s" % (a.description, b.description, hint))
|
||||
elif len(constraints) > 1:
|
||||
raise exc.AmbiguousForeignKeysError(
|
||||
"Can't determine join between '%s' and '%s'; "
|
||||
"tables have more than one foreign key "
|
||||
"constraint relationship between them. "
|
||||
"Please specify the 'onclause' of this "
|
||||
"join explicitly." % (a.description, b.description))
|
||||
elif len(crit) == 1:
|
||||
return (crit[0])
|
||||
else:
|
||||
return sql.and_(*crit)
|
||||
|
||||
|
||||
class Annotated(object):
|
||||
"""clones a ClauseElement and applies an 'annotations' dictionary.
|
||||
|
||||
Unlike regular clones, this clone also mimics __hash__() and
|
||||
__cmp__() of the original element so that it takes its place
|
||||
in hashed collections.
|
||||
|
||||
A reference to the original element is maintained, for the important
|
||||
reason of keeping its hash value current. When GC'ed, the
|
||||
hash value may be reused, causing conflicts.
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, *args):
|
||||
if not args:
|
||||
# clone constructor
|
||||
return object.__new__(cls)
|
||||
else:
|
||||
element, values = args
|
||||
# pull appropriate subclass from registry of annotated
|
||||
# classes
|
||||
try:
|
||||
cls = annotated_classes[element.__class__]
|
||||
except KeyError:
|
||||
cls = annotated_classes[element.__class__] = type.__new__(type,
|
||||
"Annotated%s" % element.__class__.__name__,
|
||||
(cls, element.__class__), {})
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(self, element, values):
|
||||
# force FromClause to generate their internal
|
||||
# collections into __dict__
|
||||
if isinstance(element, expression.FromClause):
|
||||
element.c
|
||||
|
||||
self.__dict__ = element.__dict__.copy()
|
||||
expression.ColumnElement.comparator._reset(self)
|
||||
self.__element = element
|
||||
self._annotations = values
|
||||
|
||||
def _annotate(self, values):
|
||||
_values = self._annotations.copy()
|
||||
_values.update(values)
|
||||
return self._with_annotations(_values)
|
||||
|
||||
def _with_annotations(self, values):
|
||||
clone = self.__class__.__new__(self.__class__)
|
||||
clone.__dict__ = self.__dict__.copy()
|
||||
expression.ColumnElement.comparator._reset(clone)
|
||||
clone._annotations = values
|
||||
return clone
|
||||
|
||||
def _deannotate(self, values=None, clone=True):
|
||||
if values is None:
|
||||
return self.__element
|
||||
else:
|
||||
_values = self._annotations.copy()
|
||||
for v in values:
|
||||
_values.pop(v, None)
|
||||
return self._with_annotations(_values)
|
||||
|
||||
def _compiler_dispatch(self, visitor, **kw):
|
||||
return self.__element.__class__._compiler_dispatch(self, visitor, **kw)
|
||||
|
||||
@property
|
||||
def _constructor(self):
|
||||
return self.__element._constructor
|
||||
|
||||
def _clone(self):
|
||||
clone = self.__element._clone()
|
||||
if clone is self.__element:
|
||||
# detect immutable, don't change anything
|
||||
return self
|
||||
else:
|
||||
# update the clone with any changes that have occurred
|
||||
# to this object's __dict__.
|
||||
clone.__dict__.update(self.__dict__)
|
||||
return self.__class__(clone, self._annotations)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.__element)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(self.__element, expression.ColumnOperators):
|
||||
return self.__element.__class__.__eq__(self, other)
|
||||
else:
|
||||
return hash(other) == hash(self)
|
||||
|
||||
|
||||
class AnnotatedColumnElement(Annotated):
|
||||
def __init__(self, element, values):
|
||||
Annotated.__init__(self, element, values)
|
||||
for attr in ('name', 'key', 'table'):
|
||||
if self.__dict__.get(attr, False) is None:
|
||||
self.__dict__.pop(attr)
|
||||
|
||||
@util.memoized_property
|
||||
def name(self):
|
||||
"""pull 'name' from parent, if not present"""
|
||||
return self._Annotated__element.name
|
||||
|
||||
@util.memoized_property
|
||||
def table(self):
|
||||
"""pull 'table' from parent, if not present"""
|
||||
return self._Annotated__element.table
|
||||
|
||||
@util.memoized_property
|
||||
def key(self):
|
||||
"""pull 'key' from parent, if not present"""
|
||||
return self._Annotated__element.key
|
||||
|
||||
@util.memoized_property
|
||||
def info(self):
|
||||
return self._Annotated__element.info
|
||||
|
||||
# hard-generate Annotated subclasses. this technique
|
||||
# is used instead of on-the-fly types (i.e. type.__new__())
|
||||
# so that the resulting objects are pickleable.
|
||||
annotated_classes = {}
|
||||
|
||||
for cls in expression.__dict__.values() + [schema.Column, schema.Table]:
|
||||
if isinstance(cls, type) and issubclass(cls, expression.ClauseElement):
|
||||
if issubclass(cls, expression.ColumnElement):
|
||||
annotation_cls = "AnnotatedColumnElement"
|
||||
else:
|
||||
annotation_cls = "Annotated"
|
||||
exec "class Annotated%s(%s, cls):\n" \
|
||||
" pass" % (cls.__name__, annotation_cls) in locals()
|
||||
exec "annotated_classes[cls] = Annotated%s" % (cls.__name__,)
|
||||
|
||||
|
||||
def _deep_annotate(element, annotations, exclude=None):
|
||||
"""Deep copy the given ClauseElement, annotating each element
|
||||
with the given annotations dictionary.
|
||||
|
||||
Elements within the exclude collection will be cloned but not annotated.
|
||||
|
||||
"""
|
||||
def clone(elem):
|
||||
if exclude and \
|
||||
hasattr(elem, 'proxy_set') and \
|
||||
elem.proxy_set.intersection(exclude):
|
||||
newelem = elem._clone()
|
||||
elif annotations != elem._annotations:
|
||||
newelem = elem._annotate(annotations)
|
||||
else:
|
||||
newelem = elem
|
||||
newelem._copy_internals(clone=clone)
|
||||
return newelem
|
||||
|
||||
if element is not None:
|
||||
element = clone(element)
|
||||
return element
|
||||
|
||||
|
||||
def _deep_deannotate(element, values=None):
|
||||
"""Deep copy the given element, removing annotations."""
|
||||
|
||||
cloned = util.column_dict()
|
||||
|
||||
def clone(elem):
|
||||
# if a values dict is given,
|
||||
# the elem must be cloned each time it appears,
|
||||
# as there may be different annotations in source
|
||||
# elements that are remaining. if totally
|
||||
# removing all annotations, can assume the same
|
||||
# slate...
|
||||
if values or elem not in cloned:
|
||||
newelem = elem._deannotate(values=values, clone=True)
|
||||
newelem._copy_internals(clone=clone)
|
||||
if not values:
|
||||
cloned[elem] = newelem
|
||||
return newelem
|
||||
else:
|
||||
return cloned[elem]
|
||||
|
||||
if element is not None:
|
||||
element = clone(element)
|
||||
return element
|
||||
|
||||
|
||||
def _shallow_annotate(element, annotations):
|
||||
"""Annotate the given ClauseElement and copy its internals so that
|
||||
internal objects refer to the new annotated object.
|
||||
|
||||
Basically used to apply a "dont traverse" annotation to a
|
||||
selectable, without digging throughout the whole
|
||||
structure wasting time.
|
||||
"""
|
||||
element = element._annotate(annotations)
|
||||
element._copy_internals()
|
||||
return element
|
||||
|
||||
|
||||
def splice_joins(left, right, stop_on=None):
|
||||
if left is None:
|
||||
return right
|
||||
|
||||
stack = [(right, None)]
|
||||
|
||||
adapter = ClauseAdapter(left)
|
||||
ret = None
|
||||
while stack:
|
||||
(right, prevright) = stack.pop()
|
||||
if isinstance(right, expression.Join) and right is not stop_on:
|
||||
right = right._clone()
|
||||
right._reset_exported()
|
||||
right.onclause = adapter.traverse(right.onclause)
|
||||
stack.append((right.left, right))
|
||||
else:
|
||||
right = adapter.traverse(right)
|
||||
if prevright is not None:
|
||||
prevright.left = right
|
||||
if ret is None:
|
||||
ret = right
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def reduce_columns(columns, *clauses, **kw):
|
||||
"""given a list of columns, return a 'reduced' set based on natural
|
||||
equivalents.
|
||||
|
||||
the set is reduced to the smallest list of columns which have no natural
|
||||
equivalent present in the list. A "natural equivalent" means that two
|
||||
columns will ultimately represent the same value because they are related
|
||||
by a foreign key.
|
||||
|
||||
\*clauses is an optional list of join clauses which will be traversed
|
||||
to further identify columns that are "equivalent".
|
||||
|
||||
\**kw may specify 'ignore_nonexistent_tables' to ignore foreign keys
|
||||
whose tables are not yet configured, or columns that aren't yet present.
|
||||
|
||||
This function is primarily used to determine the most minimal "primary key"
|
||||
from a selectable, by reducing the set of primary key columns present
|
||||
in the the selectable to just those that are not repeated.
|
||||
|
||||
"""
|
||||
ignore_nonexistent_tables = kw.pop('ignore_nonexistent_tables', False)
|
||||
only_synonyms = kw.pop('only_synonyms', False)
|
||||
|
||||
columns = util.ordered_column_set(columns)
|
||||
|
||||
omit = util.column_set()
|
||||
for col in columns:
|
||||
for fk in chain(*[c.foreign_keys for c in col.proxy_set]):
|
||||
for c in columns:
|
||||
if c is col:
|
||||
continue
|
||||
try:
|
||||
fk_col = fk.column
|
||||
except exc.NoReferencedColumnError:
|
||||
# TODO: add specific coverage here
|
||||
# to test/sql/test_selectable ReduceTest
|
||||
if ignore_nonexistent_tables:
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
except exc.NoReferencedTableError:
|
||||
# TODO: add specific coverage here
|
||||
# to test/sql/test_selectable ReduceTest
|
||||
if ignore_nonexistent_tables:
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
if fk_col.shares_lineage(c) and \
|
||||
(not only_synonyms or \
|
||||
c.name == col.name):
|
||||
omit.add(col)
|
||||
break
|
||||
|
||||
if clauses:
|
||||
def visit_binary(binary):
|
||||
if binary.operator == operators.eq:
|
||||
cols = util.column_set(chain(*[c.proxy_set
|
||||
for c in columns.difference(omit)]))
|
||||
if binary.left in cols and binary.right in cols:
|
||||
for c in reversed(columns):
|
||||
if c.shares_lineage(binary.right) and \
|
||||
(not only_synonyms or \
|
||||
c.name == binary.left.name):
|
||||
omit.add(c)
|
||||
break
|
||||
for clause in clauses:
|
||||
if clause is not None:
|
||||
visitors.traverse(clause, {}, {'binary': visit_binary})
|
||||
|
||||
return expression.ColumnSet(columns.difference(omit))
|
||||
|
||||
|
||||
def criterion_as_pairs(expression, consider_as_foreign_keys=None,
|
||||
consider_as_referenced_keys=None, any_operator=False):
|
||||
"""traverse an expression and locate binary criterion pairs."""
|
||||
|
||||
if consider_as_foreign_keys and consider_as_referenced_keys:
|
||||
raise exc.ArgumentError("Can only specify one of "
|
||||
"'consider_as_foreign_keys' or "
|
||||
"'consider_as_referenced_keys'")
|
||||
|
||||
def col_is(a, b):
|
||||
#return a is b
|
||||
return a.compare(b)
|
||||
|
||||
def visit_binary(binary):
|
||||
if not any_operator and binary.operator is not operators.eq:
|
||||
return
|
||||
if not isinstance(binary.left, sql.ColumnElement) or \
|
||||
not isinstance(binary.right, sql.ColumnElement):
|
||||
return
|
||||
|
||||
if consider_as_foreign_keys:
|
||||
if binary.left in consider_as_foreign_keys and \
|
||||
(col_is(binary.right, binary.left) or
|
||||
binary.right not in consider_as_foreign_keys):
|
||||
pairs.append((binary.right, binary.left))
|
||||
elif binary.right in consider_as_foreign_keys and \
|
||||
(col_is(binary.left, binary.right) or
|
||||
binary.left not in consider_as_foreign_keys):
|
||||
pairs.append((binary.left, binary.right))
|
||||
elif consider_as_referenced_keys:
|
||||
if binary.left in consider_as_referenced_keys and \
|
||||
(col_is(binary.right, binary.left) or
|
||||
binary.right not in consider_as_referenced_keys):
|
||||
pairs.append((binary.left, binary.right))
|
||||
elif binary.right in consider_as_referenced_keys and \
|
||||
(col_is(binary.left, binary.right) or
|
||||
binary.left not in consider_as_referenced_keys):
|
||||
pairs.append((binary.right, binary.left))
|
||||
else:
|
||||
if isinstance(binary.left, schema.Column) and \
|
||||
isinstance(binary.right, schema.Column):
|
||||
if binary.left.references(binary.right):
|
||||
pairs.append((binary.right, binary.left))
|
||||
elif binary.right.references(binary.left):
|
||||
pairs.append((binary.left, binary.right))
|
||||
pairs = []
|
||||
visitors.traverse(expression, {}, {'binary': visit_binary})
|
||||
return pairs
|
||||
|
||||
|
||||
class AliasedRow(object):
|
||||
"""Wrap a RowProxy with a translation map.
|
||||
|
||||
This object allows a set of keys to be translated
|
||||
to those present in a RowProxy.
|
||||
|
||||
"""
|
||||
def __init__(self, row, map):
|
||||
# AliasedRow objects don't nest, so un-nest
|
||||
# if another AliasedRow was passed
|
||||
if isinstance(row, AliasedRow):
|
||||
self.row = row.row
|
||||
else:
|
||||
self.row = row
|
||||
self.map = map
|
||||
|
||||
def __contains__(self, key):
|
||||
return self.map[key] in self.row
|
||||
|
||||
def has_key(self, key):
|
||||
return key in self
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.row[self.map[key]]
|
||||
|
||||
def keys(self):
|
||||
return self.row.keys()
|
||||
|
||||
|
||||
class ClauseAdapter(visitors.ReplacingCloningVisitor):
|
||||
"""Clones and modifies clauses based on column correspondence.
|
||||
|
||||
E.g.::
|
||||
|
||||
table1 = Table('sometable', metadata,
|
||||
Column('col1', Integer),
|
||||
Column('col2', Integer)
|
||||
)
|
||||
table2 = Table('someothertable', metadata,
|
||||
Column('col1', Integer),
|
||||
Column('col2', Integer)
|
||||
)
|
||||
|
||||
condition = table1.c.col1 == table2.c.col1
|
||||
|
||||
make an alias of table1::
|
||||
|
||||
s = table1.alias('foo')
|
||||
|
||||
calling ``ClauseAdapter(s).traverse(condition)`` converts
|
||||
condition to read::
|
||||
|
||||
s.c.col1 == table2.c.col1
|
||||
|
||||
"""
|
||||
def __init__(self, selectable, equivalents=None,
|
||||
include=None, exclude=None,
|
||||
include_fn=None, exclude_fn=None,
|
||||
adapt_on_names=False):
|
||||
self.__traverse_options__ = {'stop_on': [selectable]}
|
||||
self.selectable = selectable
|
||||
if include:
|
||||
assert not include_fn
|
||||
self.include_fn = lambda e: e in include
|
||||
else:
|
||||
self.include_fn = include_fn
|
||||
if exclude:
|
||||
assert not exclude_fn
|
||||
self.exclude_fn = lambda e: e in exclude
|
||||
else:
|
||||
self.exclude_fn = exclude_fn
|
||||
self.equivalents = util.column_dict(equivalents or {})
|
||||
self.adapt_on_names = adapt_on_names
|
||||
|
||||
def _corresponding_column(self, col, require_embedded,
|
||||
_seen=util.EMPTY_SET):
|
||||
newcol = self.selectable.corresponding_column(
|
||||
col,
|
||||
require_embedded=require_embedded)
|
||||
if newcol is None and col in self.equivalents and col not in _seen:
|
||||
for equiv in self.equivalents[col]:
|
||||
newcol = self._corresponding_column(equiv,
|
||||
require_embedded=require_embedded,
|
||||
_seen=_seen.union([col]))
|
||||
if newcol is not None:
|
||||
return newcol
|
||||
if self.adapt_on_names and newcol is None:
|
||||
newcol = self.selectable.c.get(col.name)
|
||||
return newcol
|
||||
|
||||
def replace(self, col):
|
||||
if isinstance(col, expression.FromClause) and \
|
||||
self.selectable.is_derived_from(col):
|
||||
return self.selectable
|
||||
elif not isinstance(col, expression.ColumnElement):
|
||||
return None
|
||||
elif self.include_fn and not self.include_fn(col):
|
||||
return None
|
||||
elif self.exclude_fn and self.exclude_fn(col):
|
||||
return None
|
||||
else:
|
||||
return self._corresponding_column(col, True)
|
||||
|
||||
|
||||
class ColumnAdapter(ClauseAdapter):
|
||||
"""Extends ClauseAdapter with extra utility functions.
|
||||
|
||||
Provides the ability to "wrap" this ClauseAdapter
|
||||
around another, a columns dictionary which returns
|
||||
adapted elements given an original, and an
|
||||
adapted_row() factory.
|
||||
|
||||
"""
|
||||
def __init__(self, selectable, equivalents=None,
|
||||
chain_to=None, include=None,
|
||||
exclude=None, adapt_required=False):
|
||||
ClauseAdapter.__init__(self, selectable, equivalents, include, exclude)
|
||||
if chain_to:
|
||||
self.chain(chain_to)
|
||||
self.columns = util.populate_column_dict(self._locate_col)
|
||||
self.adapt_required = adapt_required
|
||||
|
||||
def wrap(self, adapter):
|
||||
ac = self.__class__.__new__(self.__class__)
|
||||
ac.__dict__ = self.__dict__.copy()
|
||||
ac._locate_col = ac._wrap(ac._locate_col, adapter._locate_col)
|
||||
ac.adapt_clause = ac._wrap(ac.adapt_clause, adapter.adapt_clause)
|
||||
ac.adapt_list = ac._wrap(ac.adapt_list, adapter.adapt_list)
|
||||
ac.columns = util.populate_column_dict(ac._locate_col)
|
||||
return ac
|
||||
|
||||
adapt_clause = ClauseAdapter.traverse
|
||||
adapt_list = ClauseAdapter.copy_and_process
|
||||
|
||||
def _wrap(self, local, wrapped):
|
||||
def locate(col):
|
||||
col = local(col)
|
||||
return wrapped(col)
|
||||
return locate
|
||||
|
||||
def _locate_col(self, col):
|
||||
c = self._corresponding_column(col, True)
|
||||
if c is None:
|
||||
c = self.adapt_clause(col)
|
||||
|
||||
# anonymize labels in case they have a hardcoded name
|
||||
if isinstance(c, expression.Label):
|
||||
c = c.label(None)
|
||||
|
||||
# adapt_required indicates that if we got the same column
|
||||
# back which we put in (i.e. it passed through),
|
||||
# it's not correct. this is used by eagerloading which
|
||||
# knows that all columns and expressions need to be adapted
|
||||
# to a result row, and a "passthrough" is definitely targeting
|
||||
# the wrong column.
|
||||
if self.adapt_required and c is col:
|
||||
return None
|
||||
|
||||
return c
|
||||
|
||||
def adapted_row(self, row):
|
||||
return AliasedRow(row, self.columns)
|
||||
|
||||
def __getstate__(self):
|
||||
d = self.__dict__.copy()
|
||||
del d['columns']
|
||||
return d
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.__dict__.update(state)
|
||||
self.columns = util.PopulateDict(self._locate_col)
|
||||
317
vendor/sqlalchemy/sql/visitors.py
vendored
Normal file
317
vendor/sqlalchemy/sql/visitors.py
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
# sql/visitors.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Visitor/traversal interface and library functions.
|
||||
|
||||
SQLAlchemy schema and expression constructs rely on a Python-centric
|
||||
version of the classic "visitor" pattern as the primary way in which
|
||||
they apply functionality. The most common use of this pattern
|
||||
is statement compilation, where individual expression classes match
|
||||
up to rendering methods that produce a string result. Beyond this,
|
||||
the visitor system is also used to inspect expressions for various
|
||||
information and patterns, as well as for usage in
|
||||
some kinds of expression transformation. Other kinds of transformation
|
||||
use a non-visitor traversal system.
|
||||
|
||||
For many examples of how the visit system is used, see the
|
||||
sqlalchemy.sql.util and the sqlalchemy.sql.compiler modules.
|
||||
For an introduction to clause adaption, see
|
||||
http://techspot.zzzeek.org/2008/01/23/expression-transformations/
|
||||
|
||||
"""
|
||||
|
||||
from collections import deque
|
||||
from .. import util
|
||||
import operator
|
||||
from .. import exc
|
||||
|
||||
__all__ = ['VisitableType', 'Visitable', 'ClauseVisitor',
|
||||
'CloningVisitor', 'ReplacingCloningVisitor', 'iterate',
|
||||
'iterate_depthfirst', 'traverse_using', 'traverse',
|
||||
'cloned_traverse', 'replacement_traverse']
|
||||
|
||||
|
||||
class VisitableType(type):
|
||||
"""Metaclass which assigns a `_compiler_dispatch` method to classes
|
||||
having a `__visit_name__` attribute.
|
||||
|
||||
The _compiler_dispatch attribute becomes an instance method which
|
||||
looks approximately like the following::
|
||||
|
||||
def _compiler_dispatch (self, visitor, **kw):
|
||||
'''Look for an attribute named "visit_" + self.__visit_name__
|
||||
on the visitor, and call it with the same kw params.'''
|
||||
visit_attr = 'visit_%s' % self.__visit_name__
|
||||
return getattr(visitor, visit_attr)(self, **kw)
|
||||
|
||||
Classes having no __visit_name__ attribute will remain unaffected.
|
||||
"""
|
||||
def __init__(cls, clsname, bases, clsdict):
|
||||
if cls.__name__ == 'Visitable' or not hasattr(cls, '__visit_name__'):
|
||||
super(VisitableType, cls).__init__(clsname, bases, clsdict)
|
||||
return
|
||||
|
||||
_generate_dispatch(cls)
|
||||
|
||||
super(VisitableType, cls).__init__(clsname, bases, clsdict)
|
||||
|
||||
|
||||
def _generate_dispatch(cls):
|
||||
"""Return an optimized visit dispatch function for the cls
|
||||
for use by the compiler.
|
||||
"""
|
||||
if '__visit_name__' in cls.__dict__:
|
||||
visit_name = cls.__visit_name__
|
||||
if isinstance(visit_name, str):
|
||||
# There is an optimization opportunity here because the
|
||||
# the string name of the class's __visit_name__ is known at
|
||||
# this early stage (import time) so it can be pre-constructed.
|
||||
getter = operator.attrgetter("visit_%s" % visit_name)
|
||||
|
||||
def _compiler_dispatch(self, visitor, **kw):
|
||||
try:
|
||||
meth = getter(visitor)
|
||||
except AttributeError:
|
||||
raise exc.UnsupportedCompilationError(visitor, cls)
|
||||
else:
|
||||
return meth(self, **kw)
|
||||
else:
|
||||
# The optimization opportunity is lost for this case because the
|
||||
# __visit_name__ is not yet a string. As a result, the visit
|
||||
# string has to be recalculated with each compilation.
|
||||
def _compiler_dispatch(self, visitor, **kw):
|
||||
visit_attr = 'visit_%s' % self.__visit_name__
|
||||
try:
|
||||
meth = getattr(visitor, visit_attr)
|
||||
except AttributeError:
|
||||
raise exc.UnsupportedCompilationError(visitor, cls)
|
||||
else:
|
||||
return meth(self, **kw)
|
||||
|
||||
_compiler_dispatch.__doc__ = \
|
||||
"""Look for an attribute named "visit_" + self.__visit_name__
|
||||
on the visitor, and call it with the same kw params.
|
||||
"""
|
||||
cls._compiler_dispatch = _compiler_dispatch
|
||||
|
||||
|
||||
class Visitable(object):
|
||||
"""Base class for visitable objects, applies the
|
||||
``VisitableType`` metaclass.
|
||||
|
||||
"""
|
||||
|
||||
__metaclass__ = VisitableType
|
||||
|
||||
|
||||
class ClauseVisitor(object):
|
||||
"""Base class for visitor objects which can traverse using
|
||||
the traverse() function.
|
||||
|
||||
"""
|
||||
|
||||
__traverse_options__ = {}
|
||||
|
||||
def traverse_single(self, obj, **kw):
|
||||
for v in self._visitor_iterator:
|
||||
meth = getattr(v, "visit_%s" % obj.__visit_name__, None)
|
||||
if meth:
|
||||
return meth(obj, **kw)
|
||||
|
||||
def iterate(self, obj):
|
||||
"""traverse the given expression structure, returning an iterator
|
||||
of all elements.
|
||||
|
||||
"""
|
||||
return iterate(obj, self.__traverse_options__)
|
||||
|
||||
def traverse(self, obj):
|
||||
"""traverse and visit the given expression structure."""
|
||||
|
||||
return traverse(obj, self.__traverse_options__, self._visitor_dict)
|
||||
|
||||
@util.memoized_property
|
||||
def _visitor_dict(self):
|
||||
visitors = {}
|
||||
|
||||
for name in dir(self):
|
||||
if name.startswith('visit_'):
|
||||
visitors[name[6:]] = getattr(self, name)
|
||||
return visitors
|
||||
|
||||
@property
|
||||
def _visitor_iterator(self):
|
||||
"""iterate through this visitor and each 'chained' visitor."""
|
||||
|
||||
v = self
|
||||
while v:
|
||||
yield v
|
||||
v = getattr(v, '_next', None)
|
||||
|
||||
def chain(self, visitor):
|
||||
"""'chain' an additional ClauseVisitor onto this ClauseVisitor.
|
||||
|
||||
the chained visitor will receive all visit events after this one.
|
||||
|
||||
"""
|
||||
tail = list(self._visitor_iterator)[-1]
|
||||
tail._next = visitor
|
||||
return self
|
||||
|
||||
|
||||
class CloningVisitor(ClauseVisitor):
|
||||
"""Base class for visitor objects which can traverse using
|
||||
the cloned_traverse() function.
|
||||
|
||||
"""
|
||||
|
||||
def copy_and_process(self, list_):
|
||||
"""Apply cloned traversal to the given list of elements, and return
|
||||
the new list.
|
||||
|
||||
"""
|
||||
return [self.traverse(x) for x in list_]
|
||||
|
||||
def traverse(self, obj):
|
||||
"""traverse and visit the given expression structure."""
|
||||
|
||||
return cloned_traverse(
|
||||
obj, self.__traverse_options__, self._visitor_dict)
|
||||
|
||||
|
||||
class ReplacingCloningVisitor(CloningVisitor):
|
||||
"""Base class for visitor objects which can traverse using
|
||||
the replacement_traverse() function.
|
||||
|
||||
"""
|
||||
|
||||
def replace(self, elem):
|
||||
"""receive pre-copied elements during a cloning traversal.
|
||||
|
||||
If the method returns a new element, the element is used
|
||||
instead of creating a simple copy of the element. Traversal
|
||||
will halt on the newly returned element if it is re-encountered.
|
||||
"""
|
||||
return None
|
||||
|
||||
def traverse(self, obj):
|
||||
"""traverse and visit the given expression structure."""
|
||||
|
||||
def replace(elem):
|
||||
for v in self._visitor_iterator:
|
||||
e = v.replace(elem)
|
||||
if e is not None:
|
||||
return e
|
||||
return replacement_traverse(obj, self.__traverse_options__, replace)
|
||||
|
||||
|
||||
def iterate(obj, opts):
|
||||
"""traverse the given expression structure, returning an iterator.
|
||||
|
||||
traversal is configured to be breadth-first.
|
||||
|
||||
"""
|
||||
stack = deque([obj])
|
||||
while stack:
|
||||
t = stack.popleft()
|
||||
yield t
|
||||
for c in t.get_children(**opts):
|
||||
stack.append(c)
|
||||
|
||||
|
||||
def iterate_depthfirst(obj, opts):
|
||||
"""traverse the given expression structure, returning an iterator.
|
||||
|
||||
traversal is configured to be depth-first.
|
||||
|
||||
"""
|
||||
stack = deque([obj])
|
||||
traversal = deque()
|
||||
while stack:
|
||||
t = stack.pop()
|
||||
traversal.appendleft(t)
|
||||
for c in t.get_children(**opts):
|
||||
stack.append(c)
|
||||
return iter(traversal)
|
||||
|
||||
|
||||
def traverse_using(iterator, obj, visitors):
|
||||
"""visit the given expression structure using the given iterator of
|
||||
objects.
|
||||
|
||||
"""
|
||||
for target in iterator:
|
||||
meth = visitors.get(target.__visit_name__, None)
|
||||
if meth:
|
||||
meth(target)
|
||||
return obj
|
||||
|
||||
|
||||
def traverse(obj, opts, visitors):
|
||||
"""traverse and visit the given expression structure using the default
|
||||
iterator.
|
||||
|
||||
"""
|
||||
return traverse_using(iterate(obj, opts), obj, visitors)
|
||||
|
||||
|
||||
def traverse_depthfirst(obj, opts, visitors):
|
||||
"""traverse and visit the given expression structure using the
|
||||
depth-first iterator.
|
||||
|
||||
"""
|
||||
return traverse_using(iterate_depthfirst(obj, opts), obj, visitors)
|
||||
|
||||
|
||||
def cloned_traverse(obj, opts, visitors):
|
||||
"""clone the given expression structure, allowing
|
||||
modifications by visitors."""
|
||||
|
||||
cloned = util.column_dict()
|
||||
stop_on = util.column_set(opts.get('stop_on', []))
|
||||
|
||||
def clone(elem):
|
||||
if elem in stop_on:
|
||||
return elem
|
||||
else:
|
||||
if id(elem) not in cloned:
|
||||
cloned[id(elem)] = newelem = elem._clone()
|
||||
newelem._copy_internals(clone=clone)
|
||||
meth = visitors.get(newelem.__visit_name__, None)
|
||||
if meth:
|
||||
meth(newelem)
|
||||
return cloned[id(elem)]
|
||||
|
||||
if obj is not None:
|
||||
obj = clone(obj)
|
||||
return obj
|
||||
|
||||
|
||||
def replacement_traverse(obj, opts, replace):
|
||||
"""clone the given expression structure, allowing element
|
||||
replacement by a given replacement function."""
|
||||
|
||||
cloned = util.column_dict()
|
||||
stop_on = util.column_set([id(x) for x in opts.get('stop_on', [])])
|
||||
|
||||
def clone(elem, **kw):
|
||||
if id(elem) in stop_on or \
|
||||
'no_replacement_traverse' in elem._annotations:
|
||||
return elem
|
||||
else:
|
||||
newelem = replace(elem)
|
||||
if newelem is not None:
|
||||
stop_on.add(id(newelem))
|
||||
return newelem
|
||||
else:
|
||||
if elem not in cloned:
|
||||
cloned[elem] = newelem = elem._clone()
|
||||
newelem._copy_internals(clone=clone, **kw)
|
||||
return cloned[elem]
|
||||
|
||||
if obj is not None:
|
||||
obj = clone(obj, **opts)
|
||||
return obj
|
||||
Reference in New Issue
Block a user