You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1095 lines
						
					
					
						
							34 KiB
						
					
					
				
			
		
		
	
	
							1095 lines
						
					
					
						
							34 KiB
						
					
					
				# sql/coercions.py
 | 
						|
# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
 | 
						|
# <see AUTHORS file>
 | 
						|
#
 | 
						|
# This module is part of SQLAlchemy and is released under
 | 
						|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
 | 
						|
 | 
						|
import numbers
 | 
						|
import re
 | 
						|
 | 
						|
from . import operators
 | 
						|
from . import roles
 | 
						|
from . import visitors
 | 
						|
from .base import ExecutableOption
 | 
						|
from .base import Options
 | 
						|
from .traversals import HasCacheKey
 | 
						|
from .visitors import Visitable
 | 
						|
from .. import exc
 | 
						|
from .. import inspection
 | 
						|
from .. import util
 | 
						|
from ..util import collections_abc
 | 
						|
 | 
						|
 | 
						|
elements = None
 | 
						|
lambdas = None
 | 
						|
schema = None
 | 
						|
selectable = None
 | 
						|
sqltypes = None
 | 
						|
traversals = None
 | 
						|
 | 
						|
 | 
						|
def _is_literal(element):
 | 
						|
    """Return whether or not the element is a "literal" in the context
 | 
						|
    of a SQL expression construct.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    return (
 | 
						|
        not isinstance(
 | 
						|
            element,
 | 
						|
            (Visitable, schema.SchemaEventTarget),
 | 
						|
        )
 | 
						|
        and not hasattr(element, "__clause_element__")
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def _deep_is_literal(element):
 | 
						|
    """Return whether or not the element is a "literal" in the context
 | 
						|
    of a SQL expression construct.
 | 
						|
 | 
						|
    does a deeper more esoteric check than _is_literal.   is used
 | 
						|
    for lambda elements that have to distinguish values that would
 | 
						|
    be bound vs. not without any context.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    if isinstance(element, collections_abc.Sequence) and not isinstance(
 | 
						|
        element, str
 | 
						|
    ):
 | 
						|
        for elem in element:
 | 
						|
            if not _deep_is_literal(elem):
 | 
						|
                return False
 | 
						|
        else:
 | 
						|
            return True
 | 
						|
 | 
						|
    return (
 | 
						|
        not isinstance(
 | 
						|
            element,
 | 
						|
            (
 | 
						|
                Visitable,
 | 
						|
                schema.SchemaEventTarget,
 | 
						|
                HasCacheKey,
 | 
						|
                Options,
 | 
						|
                util.langhelpers._symbol,
 | 
						|
            ),
 | 
						|
        )
 | 
						|
        and not hasattr(element, "__clause_element__")
 | 
						|
        and (
 | 
						|
            not isinstance(element, type)
 | 
						|
            or not issubclass(element, HasCacheKey)
 | 
						|
        )
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def _document_text_coercion(paramname, meth_rst, param_rst):
 | 
						|
    return util.add_parameter_text(
 | 
						|
        paramname,
 | 
						|
        (
 | 
						|
            ".. warning:: "
 | 
						|
            "The %s argument to %s can be passed as a Python string argument, "
 | 
						|
            "which will be treated "
 | 
						|
            "as **trusted SQL text** and rendered as given.  **DO NOT PASS "
 | 
						|
            "UNTRUSTED INPUT TO THIS PARAMETER**."
 | 
						|
        )
 | 
						|
        % (param_rst, meth_rst),
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def _expression_collection_was_a_list(attrname, fnname, args):
 | 
						|
    if args and isinstance(args[0], (list, set, dict)) and len(args) == 1:
 | 
						|
        if isinstance(args[0], list):
 | 
						|
            util.warn_deprecated_20(
 | 
						|
                'The "%s" argument to %s(), when referring to a sequence '
 | 
						|
                "of items, is now passed as a series of positional "
 | 
						|
                "elements, rather than as a list. " % (attrname, fnname)
 | 
						|
            )
 | 
						|
        return args[0]
 | 
						|
    else:
 | 
						|
        return args
 | 
						|
 | 
						|
 | 
						|
def expect(
 | 
						|
    role,
 | 
						|
    element,
 | 
						|
    apply_propagate_attrs=None,
 | 
						|
    argname=None,
 | 
						|
    post_inspect=False,
 | 
						|
    **kw
 | 
						|
):
 | 
						|
    if (
 | 
						|
        role.allows_lambda
 | 
						|
        # note callable() will not invoke a __getattr__() method, whereas
 | 
						|
        # hasattr(obj, "__call__") will. by keeping the callable() check here
 | 
						|
        # we prevent most needless calls to hasattr()  and therefore
 | 
						|
        # __getattr__(), which is present on ColumnElement.
 | 
						|
        and callable(element)
 | 
						|
        and hasattr(element, "__code__")
 | 
						|
    ):
 | 
						|
        return lambdas.LambdaElement(
 | 
						|
            element,
 | 
						|
            role,
 | 
						|
            lambdas.LambdaOptions(**kw),
 | 
						|
            apply_propagate_attrs=apply_propagate_attrs,
 | 
						|
        )
 | 
						|
 | 
						|
    # major case is that we are given a ClauseElement already, skip more
 | 
						|
    # elaborate logic up front if possible
 | 
						|
    impl = _impl_lookup[role]
 | 
						|
 | 
						|
    original_element = element
 | 
						|
 | 
						|
    if not isinstance(
 | 
						|
        element,
 | 
						|
        (elements.ClauseElement, schema.SchemaItem, schema.FetchedValue),
 | 
						|
    ):
 | 
						|
        resolved = None
 | 
						|
 | 
						|
        if impl._resolve_literal_only:
 | 
						|
            resolved = impl._literal_coercion(element, **kw)
 | 
						|
        else:
 | 
						|
 | 
						|
            original_element = element
 | 
						|
 | 
						|
            is_clause_element = False
 | 
						|
 | 
						|
            # this is a special performance optimization for ORM
 | 
						|
            # joins used by JoinTargetImpl that we don't go through the
 | 
						|
            # work of creating __clause_element__() when we only need the
 | 
						|
            # original QueryableAttribute, as the former will do clause
 | 
						|
            # adaption and all that which is just thrown away here.
 | 
						|
            if (
 | 
						|
                impl._skip_clauseelement_for_target_match
 | 
						|
                and isinstance(element, role)
 | 
						|
                and hasattr(element, "__clause_element__")
 | 
						|
            ):
 | 
						|
                is_clause_element = True
 | 
						|
            else:
 | 
						|
                while hasattr(element, "__clause_element__"):
 | 
						|
                    is_clause_element = True
 | 
						|
 | 
						|
                    if not getattr(element, "is_clause_element", False):
 | 
						|
                        element = element.__clause_element__()
 | 
						|
                    else:
 | 
						|
                        break
 | 
						|
 | 
						|
            if not is_clause_element:
 | 
						|
                if impl._use_inspection:
 | 
						|
                    insp = inspection.inspect(element, raiseerr=False)
 | 
						|
                    if insp is not None:
 | 
						|
                        if post_inspect:
 | 
						|
                            insp._post_inspect
 | 
						|
                        try:
 | 
						|
                            resolved = insp.__clause_element__()
 | 
						|
                        except AttributeError:
 | 
						|
                            impl._raise_for_expected(original_element, argname)
 | 
						|
 | 
						|
                if resolved is None:
 | 
						|
                    resolved = impl._literal_coercion(
 | 
						|
                        element, argname=argname, **kw
 | 
						|
                    )
 | 
						|
            else:
 | 
						|
                resolved = element
 | 
						|
    else:
 | 
						|
        resolved = element
 | 
						|
    if (
 | 
						|
        apply_propagate_attrs is not None
 | 
						|
        and not apply_propagate_attrs._propagate_attrs
 | 
						|
        and resolved._propagate_attrs
 | 
						|
    ):
 | 
						|
        apply_propagate_attrs._propagate_attrs = resolved._propagate_attrs
 | 
						|
 | 
						|
    if impl._role_class in resolved.__class__.__mro__:
 | 
						|
        if impl._post_coercion:
 | 
						|
            resolved = impl._post_coercion(
 | 
						|
                resolved,
 | 
						|
                argname=argname,
 | 
						|
                original_element=original_element,
 | 
						|
                **kw
 | 
						|
            )
 | 
						|
        return resolved
 | 
						|
    else:
 | 
						|
        return impl._implicit_coercions(
 | 
						|
            original_element, resolved, argname=argname, **kw
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
def expect_as_key(role, element, **kw):
 | 
						|
    kw["as_key"] = True
 | 
						|
    return expect(role, element, **kw)
 | 
						|
 | 
						|
 | 
						|
def expect_col_expression_collection(role, expressions):
 | 
						|
    for expr in expressions:
 | 
						|
        strname = None
 | 
						|
        column = None
 | 
						|
 | 
						|
        resolved = expect(role, expr)
 | 
						|
        if isinstance(resolved, util.string_types):
 | 
						|
            strname = resolved = expr
 | 
						|
        else:
 | 
						|
            cols = []
 | 
						|
            visitors.traverse(resolved, {}, {"column": cols.append})
 | 
						|
            if cols:
 | 
						|
                column = cols[0]
 | 
						|
        add_element = column if column is not None else strname
 | 
						|
        yield resolved, column, strname, add_element
 | 
						|
 | 
						|
 | 
						|
class RoleImpl(object):
 | 
						|
    __slots__ = ("_role_class", "name", "_use_inspection")
 | 
						|
 | 
						|
    def _literal_coercion(self, element, **kw):
 | 
						|
        raise NotImplementedError()
 | 
						|
 | 
						|
    _post_coercion = None
 | 
						|
    _resolve_literal_only = False
 | 
						|
    _skip_clauseelement_for_target_match = False
 | 
						|
 | 
						|
    def __init__(self, role_class):
 | 
						|
        self._role_class = role_class
 | 
						|
        self.name = role_class._role_name
 | 
						|
        self._use_inspection = issubclass(role_class, roles.UsesInspection)
 | 
						|
 | 
						|
    def _implicit_coercions(self, element, resolved, argname=None, **kw):
 | 
						|
        self._raise_for_expected(element, argname, resolved)
 | 
						|
 | 
						|
    def _raise_for_expected(
 | 
						|
        self,
 | 
						|
        element,
 | 
						|
        argname=None,
 | 
						|
        resolved=None,
 | 
						|
        advice=None,
 | 
						|
        code=None,
 | 
						|
        err=None,
 | 
						|
    ):
 | 
						|
        if resolved is not None and resolved is not element:
 | 
						|
            got = "%r object resolved from %r object" % (resolved, element)
 | 
						|
        else:
 | 
						|
            got = repr(element)
 | 
						|
 | 
						|
        if argname:
 | 
						|
            msg = "%s expected for argument %r; got %s." % (
 | 
						|
                self.name,
 | 
						|
                argname,
 | 
						|
                got,
 | 
						|
            )
 | 
						|
        else:
 | 
						|
            msg = "%s expected, got %s." % (self.name, got)
 | 
						|
 | 
						|
        if advice:
 | 
						|
            msg += " " + advice
 | 
						|
 | 
						|
        util.raise_(exc.ArgumentError(msg, code=code), replace_context=err)
 | 
						|
 | 
						|
 | 
						|
class _Deannotate(object):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _post_coercion(self, resolved, **kw):
 | 
						|
        from .util import _deep_deannotate
 | 
						|
 | 
						|
        return _deep_deannotate(resolved)
 | 
						|
 | 
						|
 | 
						|
class _StringOnly(object):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    _resolve_literal_only = True
 | 
						|
 | 
						|
 | 
						|
class _ReturnsStringKey(object):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if isinstance(original_element, util.string_types):
 | 
						|
            return original_element
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
    def _literal_coercion(self, element, **kw):
 | 
						|
        return element
 | 
						|
 | 
						|
 | 
						|
class _ColumnCoercions(object):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _warn_for_scalar_subquery_coercion(self):
 | 
						|
        util.warn(
 | 
						|
            "implicitly coercing SELECT object to scalar subquery; "
 | 
						|
            "please use the .scalar_subquery() method to produce a scalar "
 | 
						|
            "subquery.",
 | 
						|
        )
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if not getattr(resolved, "is_clause_element", False):
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
        elif resolved._is_select_statement:
 | 
						|
            self._warn_for_scalar_subquery_coercion()
 | 
						|
            return resolved.scalar_subquery()
 | 
						|
        elif resolved._is_from_clause and isinstance(
 | 
						|
            resolved, selectable.Subquery
 | 
						|
        ):
 | 
						|
            self._warn_for_scalar_subquery_coercion()
 | 
						|
            return resolved.element.scalar_subquery()
 | 
						|
        elif self._role_class.allows_lambda and resolved._is_lambda_element:
 | 
						|
            return resolved
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
 | 
						|
def _no_text_coercion(
 | 
						|
    element, argname=None, exc_cls=exc.ArgumentError, extra=None, err=None
 | 
						|
):
 | 
						|
    util.raise_(
 | 
						|
        exc_cls(
 | 
						|
            "%(extra)sTextual SQL expression %(expr)r %(argname)sshould be "
 | 
						|
            "explicitly declared as text(%(expr)r)"
 | 
						|
            % {
 | 
						|
                "expr": util.ellipses_string(element),
 | 
						|
                "argname": "for argument %s" % (argname,) if argname else "",
 | 
						|
                "extra": "%s " % extra if extra else "",
 | 
						|
            }
 | 
						|
        ),
 | 
						|
        replace_context=err,
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
class _NoTextCoercion(object):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _literal_coercion(self, element, argname=None, **kw):
 | 
						|
        if isinstance(element, util.string_types) and issubclass(
 | 
						|
            elements.TextClause, self._role_class
 | 
						|
        ):
 | 
						|
            _no_text_coercion(element, argname)
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(element, argname)
 | 
						|
 | 
						|
 | 
						|
class _CoerceLiterals(object):
 | 
						|
    __slots__ = ()
 | 
						|
    _coerce_consts = False
 | 
						|
    _coerce_star = False
 | 
						|
    _coerce_numerics = False
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None):
 | 
						|
        return _no_text_coercion(element, argname)
 | 
						|
 | 
						|
    def _literal_coercion(self, element, argname=None, **kw):
 | 
						|
        if isinstance(element, util.string_types):
 | 
						|
            if self._coerce_star and element == "*":
 | 
						|
                return elements.ColumnClause("*", is_literal=True)
 | 
						|
            else:
 | 
						|
                return self._text_coercion(element, argname, **kw)
 | 
						|
 | 
						|
        if self._coerce_consts:
 | 
						|
            if element is None:
 | 
						|
                return elements.Null()
 | 
						|
            elif element is False:
 | 
						|
                return elements.False_()
 | 
						|
            elif element is True:
 | 
						|
                return elements.True_()
 | 
						|
 | 
						|
        if self._coerce_numerics and isinstance(element, (numbers.Number)):
 | 
						|
            return elements.ColumnClause(str(element), is_literal=True)
 | 
						|
 | 
						|
        self._raise_for_expected(element, argname)
 | 
						|
 | 
						|
 | 
						|
class LiteralValueImpl(RoleImpl):
 | 
						|
    _resolve_literal_only = True
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, element, resolved, argname, type_=None, **kw
 | 
						|
    ):
 | 
						|
        if not _is_literal(resolved):
 | 
						|
            self._raise_for_expected(
 | 
						|
                element, resolved=resolved, argname=argname, **kw
 | 
						|
            )
 | 
						|
 | 
						|
        return elements.BindParameter(None, element, type_=type_, unique=True)
 | 
						|
 | 
						|
    def _literal_coercion(self, element, argname=None, type_=None, **kw):
 | 
						|
        return element
 | 
						|
 | 
						|
 | 
						|
class _SelectIsNotFrom(object):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _raise_for_expected(self, element, argname=None, resolved=None, **kw):
 | 
						|
        if isinstance(element, roles.SelectStatementRole) or isinstance(
 | 
						|
            resolved, roles.SelectStatementRole
 | 
						|
        ):
 | 
						|
            advice = (
 | 
						|
                "To create a "
 | 
						|
                "FROM clause from a %s object, use the .subquery() method."
 | 
						|
                % (resolved.__class__ if resolved is not None else element,)
 | 
						|
            )
 | 
						|
            code = "89ve"
 | 
						|
        else:
 | 
						|
            advice = code = None
 | 
						|
 | 
						|
        return super(_SelectIsNotFrom, self)._raise_for_expected(
 | 
						|
            element,
 | 
						|
            argname=argname,
 | 
						|
            resolved=resolved,
 | 
						|
            advice=advice,
 | 
						|
            code=code,
 | 
						|
            **kw
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class HasCacheKeyImpl(RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if isinstance(original_element, traversals.HasCacheKey):
 | 
						|
            return original_element
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
    def _literal_coercion(self, element, **kw):
 | 
						|
        return element
 | 
						|
 | 
						|
 | 
						|
class ExecutableOptionImpl(RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if isinstance(original_element, ExecutableOption):
 | 
						|
            return original_element
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
    def _literal_coercion(self, element, **kw):
 | 
						|
        return element
 | 
						|
 | 
						|
 | 
						|
class ExpressionElementImpl(_ColumnCoercions, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _literal_coercion(
 | 
						|
        self, element, name=None, type_=None, argname=None, is_crud=False, **kw
 | 
						|
    ):
 | 
						|
        if (
 | 
						|
            element is None
 | 
						|
            and not is_crud
 | 
						|
            and (type_ is None or not type_.should_evaluate_none)
 | 
						|
        ):
 | 
						|
            # TODO: there's no test coverage now for the
 | 
						|
            # "should_evaluate_none" part of this, as outside of "crud" this
 | 
						|
            # codepath is not normally used except in some special cases
 | 
						|
            return elements.Null()
 | 
						|
        else:
 | 
						|
            try:
 | 
						|
                return elements.BindParameter(
 | 
						|
                    name, element, type_, unique=True, _is_crud=is_crud
 | 
						|
                )
 | 
						|
            except exc.ArgumentError as err:
 | 
						|
                self._raise_for_expected(element, err=err)
 | 
						|
 | 
						|
    def _raise_for_expected(self, element, argname=None, resolved=None, **kw):
 | 
						|
        if isinstance(element, roles.AnonymizedFromClauseRole):
 | 
						|
            advice = (
 | 
						|
                "To create a "
 | 
						|
                "column expression from a FROM clause row "
 | 
						|
                "as a whole, use the .table_valued() method."
 | 
						|
            )
 | 
						|
        else:
 | 
						|
            advice = None
 | 
						|
 | 
						|
        return super(ExpressionElementImpl, self)._raise_for_expected(
 | 
						|
            element, argname=argname, resolved=resolved, advice=advice, **kw
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class BinaryElementImpl(ExpressionElementImpl, RoleImpl):
 | 
						|
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _literal_coercion(
 | 
						|
        self, element, expr, operator, bindparam_type=None, argname=None, **kw
 | 
						|
    ):
 | 
						|
        try:
 | 
						|
            return expr._bind_param(operator, element, type_=bindparam_type)
 | 
						|
        except exc.ArgumentError as err:
 | 
						|
            self._raise_for_expected(element, err=err)
 | 
						|
 | 
						|
    def _post_coercion(self, resolved, expr, **kw):
 | 
						|
        if resolved.type._isnull and not expr.type._isnull:
 | 
						|
            resolved = resolved._with_binary_element_type(expr.type)
 | 
						|
        return resolved
 | 
						|
 | 
						|
 | 
						|
class InElementImpl(RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if resolved._is_from_clause:
 | 
						|
            if (
 | 
						|
                isinstance(resolved, selectable.Alias)
 | 
						|
                and resolved.element._is_select_statement
 | 
						|
            ):
 | 
						|
                self._warn_for_implicit_coercion(resolved)
 | 
						|
                return self._post_coercion(resolved.element, **kw)
 | 
						|
            else:
 | 
						|
                self._warn_for_implicit_coercion(resolved)
 | 
						|
                return self._post_coercion(resolved.select(), **kw)
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
    def _warn_for_implicit_coercion(self, elem):
 | 
						|
        util.warn(
 | 
						|
            "Coercing %s object into a select() for use in IN(); "
 | 
						|
            "please pass a select() construct explicitly"
 | 
						|
            % (elem.__class__.__name__)
 | 
						|
        )
 | 
						|
 | 
						|
    def _literal_coercion(self, element, expr, operator, **kw):
 | 
						|
        if isinstance(element, collections_abc.Iterable) and not isinstance(
 | 
						|
            element, util.string_types
 | 
						|
        ):
 | 
						|
            non_literal_expressions = {}
 | 
						|
            element = list(element)
 | 
						|
            for o in element:
 | 
						|
                if not _is_literal(o):
 | 
						|
                    if not isinstance(o, operators.ColumnOperators):
 | 
						|
                        self._raise_for_expected(element, **kw)
 | 
						|
                    else:
 | 
						|
                        non_literal_expressions[o] = o
 | 
						|
                elif o is None:
 | 
						|
                    non_literal_expressions[o] = elements.Null()
 | 
						|
 | 
						|
            if non_literal_expressions:
 | 
						|
                return elements.ClauseList(
 | 
						|
                    *[
 | 
						|
                        non_literal_expressions[o]
 | 
						|
                        if o in non_literal_expressions
 | 
						|
                        else expr._bind_param(operator, o)
 | 
						|
                        for o in element
 | 
						|
                    ]
 | 
						|
                )
 | 
						|
            else:
 | 
						|
                return expr._bind_param(operator, element, expanding=True)
 | 
						|
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(element, **kw)
 | 
						|
 | 
						|
    def _post_coercion(self, element, expr, operator, **kw):
 | 
						|
        if element._is_select_statement:
 | 
						|
            # for IN, we are doing scalar_subquery() coercion without
 | 
						|
            # a warning
 | 
						|
            return element.scalar_subquery()
 | 
						|
        elif isinstance(element, elements.ClauseList):
 | 
						|
            assert not len(element.clauses) == 0
 | 
						|
            return element.self_group(against=operator)
 | 
						|
 | 
						|
        elif isinstance(element, elements.BindParameter):
 | 
						|
            element = element._clone(maintain_key=True)
 | 
						|
            element.expanding = True
 | 
						|
            element.expand_op = operator
 | 
						|
 | 
						|
            return element
 | 
						|
        else:
 | 
						|
            return element
 | 
						|
 | 
						|
 | 
						|
class OnClauseImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    _coerce_consts = True
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, legacy=False, **kw
 | 
						|
    ):
 | 
						|
        if legacy and isinstance(resolved, str):
 | 
						|
            return resolved
 | 
						|
        else:
 | 
						|
            return super(OnClauseImpl, self)._implicit_coercions(
 | 
						|
                original_element,
 | 
						|
                resolved,
 | 
						|
                argname=argname,
 | 
						|
                legacy=legacy,
 | 
						|
                **kw
 | 
						|
            )
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None, legacy=False):
 | 
						|
        if legacy and isinstance(element, str):
 | 
						|
            util.warn_deprecated_20(
 | 
						|
                "Using strings to indicate relationship names in "
 | 
						|
                "Query.join() is deprecated and will be removed in "
 | 
						|
                "SQLAlchemy 2.0.  Please use the class-bound attribute "
 | 
						|
                "directly."
 | 
						|
            )
 | 
						|
            return element
 | 
						|
 | 
						|
        return super(OnClauseImpl, self)._text_coercion(element, argname)
 | 
						|
 | 
						|
    def _post_coercion(self, resolved, original_element=None, **kw):
 | 
						|
        # this is a hack right now as we want to use coercion on an
 | 
						|
        # ORM InstrumentedAttribute, but we want to return the object
 | 
						|
        # itself if it is one, not its clause element.
 | 
						|
        # ORM context _join and _legacy_join() would need to be improved
 | 
						|
        # to look for annotations in a clause element form.
 | 
						|
        if isinstance(original_element, roles.JoinTargetRole):
 | 
						|
            return original_element
 | 
						|
        return resolved
 | 
						|
 | 
						|
 | 
						|
class WhereHavingImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    _coerce_consts = True
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None):
 | 
						|
        return _no_text_coercion(element, argname)
 | 
						|
 | 
						|
 | 
						|
class StatementOptionImpl(_CoerceLiterals, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    _coerce_consts = True
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None):
 | 
						|
        return elements.TextClause(element)
 | 
						|
 | 
						|
 | 
						|
class ColumnArgumentImpl(_NoTextCoercion, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
 | 
						|
class ColumnArgumentOrKeyImpl(_ReturnsStringKey, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
 | 
						|
class StrAsPlainColumnImpl(_CoerceLiterals, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None):
 | 
						|
        return elements.ColumnClause(element)
 | 
						|
 | 
						|
 | 
						|
class ByOfImpl(_CoerceLiterals, _ColumnCoercions, RoleImpl, roles.ByOfRole):
 | 
						|
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    _coerce_consts = True
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None):
 | 
						|
        return elements._textual_label_reference(element)
 | 
						|
 | 
						|
 | 
						|
class OrderByImpl(ByOfImpl, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _post_coercion(self, resolved, **kw):
 | 
						|
        if (
 | 
						|
            isinstance(resolved, self._role_class)
 | 
						|
            and resolved._order_by_label_element is not None
 | 
						|
        ):
 | 
						|
            return elements._label_reference(resolved)
 | 
						|
        else:
 | 
						|
            return resolved
 | 
						|
 | 
						|
 | 
						|
class GroupByImpl(ByOfImpl, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if isinstance(resolved, roles.StrictFromClauseRole):
 | 
						|
            return elements.ClauseList(*resolved.c)
 | 
						|
        else:
 | 
						|
            return resolved
 | 
						|
 | 
						|
 | 
						|
class DMLColumnImpl(_ReturnsStringKey, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _post_coercion(self, element, as_key=False, **kw):
 | 
						|
        if as_key:
 | 
						|
            return element.key
 | 
						|
        else:
 | 
						|
            return element
 | 
						|
 | 
						|
 | 
						|
class ConstExprImpl(RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _literal_coercion(self, element, argname=None, **kw):
 | 
						|
        if element is None:
 | 
						|
            return elements.Null()
 | 
						|
        elif element is False:
 | 
						|
            return elements.False_()
 | 
						|
        elif element is True:
 | 
						|
            return elements.True_()
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(element, argname)
 | 
						|
 | 
						|
 | 
						|
class TruncatedLabelImpl(_StringOnly, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if isinstance(original_element, util.string_types):
 | 
						|
            return resolved
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
    def _literal_coercion(self, element, argname=None, **kw):
 | 
						|
        """coerce the given value to :class:`._truncated_label`.
 | 
						|
 | 
						|
        Existing :class:`._truncated_label` and
 | 
						|
        :class:`._anonymous_label` objects are passed
 | 
						|
        unchanged.
 | 
						|
        """
 | 
						|
 | 
						|
        if isinstance(element, elements._truncated_label):
 | 
						|
            return element
 | 
						|
        else:
 | 
						|
            return elements._truncated_label(element)
 | 
						|
 | 
						|
 | 
						|
class DDLExpressionImpl(_Deannotate, _CoerceLiterals, RoleImpl):
 | 
						|
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    _coerce_consts = True
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None):
 | 
						|
        # see #5754 for why we can't easily deprecate this coercion.
 | 
						|
        # essentially expressions like postgresql_where would have to be
 | 
						|
        # text() as they come back from reflection and we don't want to
 | 
						|
        # have text() elements wired into the inspection dictionaries.
 | 
						|
        return elements.TextClause(element)
 | 
						|
 | 
						|
 | 
						|
class DDLConstraintColumnImpl(_Deannotate, _ReturnsStringKey, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
 | 
						|
class DDLReferredColumnImpl(DDLConstraintColumnImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
 | 
						|
class LimitOffsetImpl(RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(self, element, resolved, argname=None, **kw):
 | 
						|
        if resolved is None:
 | 
						|
            return None
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(element, argname, resolved)
 | 
						|
 | 
						|
    def _literal_coercion(self, element, name, type_, **kw):
 | 
						|
        if element is None:
 | 
						|
            return None
 | 
						|
        else:
 | 
						|
            value = util.asint(element)
 | 
						|
            return selectable._OffsetLimitParam(
 | 
						|
                name, value, type_=type_, unique=True
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
class LabeledColumnExprImpl(ExpressionElementImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if isinstance(resolved, roles.ExpressionElementRole):
 | 
						|
            return resolved.label(None)
 | 
						|
        else:
 | 
						|
            new = super(LabeledColumnExprImpl, self)._implicit_coercions(
 | 
						|
                original_element, resolved, argname=argname, **kw
 | 
						|
            )
 | 
						|
            if isinstance(new, roles.ExpressionElementRole):
 | 
						|
                return new.label(None)
 | 
						|
            else:
 | 
						|
                self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
 | 
						|
class ColumnsClauseImpl(_SelectIsNotFrom, _CoerceLiterals, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    _coerce_consts = True
 | 
						|
    _coerce_numerics = True
 | 
						|
    _coerce_star = True
 | 
						|
 | 
						|
    _guess_straight_column = re.compile(r"^\w\S*$", re.I)
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None):
 | 
						|
        element = str(element)
 | 
						|
 | 
						|
        guess_is_literal = not self._guess_straight_column.match(element)
 | 
						|
        raise exc.ArgumentError(
 | 
						|
            "Textual column expression %(column)r %(argname)sshould be "
 | 
						|
            "explicitly declared with text(%(column)r), "
 | 
						|
            "or use %(literal_column)s(%(column)r) "
 | 
						|
            "for more specificity"
 | 
						|
            % {
 | 
						|
                "column": util.ellipses_string(element),
 | 
						|
                "argname": "for argument %s" % (argname,) if argname else "",
 | 
						|
                "literal_column": "literal_column"
 | 
						|
                if guess_is_literal
 | 
						|
                else "column",
 | 
						|
            }
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class ReturnsRowsImpl(RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
 | 
						|
class StatementImpl(_CoerceLiterals, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _post_coercion(self, resolved, original_element, argname=None, **kw):
 | 
						|
        if resolved is not original_element and not isinstance(
 | 
						|
            original_element, util.string_types
 | 
						|
        ):
 | 
						|
            # use same method as Connection uses; this will later raise
 | 
						|
            # ObjectNotExecutableError
 | 
						|
            try:
 | 
						|
                original_element._execute_on_connection
 | 
						|
            except AttributeError:
 | 
						|
                util.warn_deprecated(
 | 
						|
                    "Object %r should not be used directly in a SQL statement "
 | 
						|
                    "context, such as passing to methods such as "
 | 
						|
                    "session.execute().  This usage will be disallowed in a "
 | 
						|
                    "future release.  "
 | 
						|
                    "Please use Core select() / update() / delete() etc. "
 | 
						|
                    "with Session.execute() and other statement execution "
 | 
						|
                    "methods." % original_element,
 | 
						|
                    "1.4",
 | 
						|
                )
 | 
						|
 | 
						|
        return resolved
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if resolved._is_lambda_element:
 | 
						|
            return resolved
 | 
						|
        else:
 | 
						|
            return super(StatementImpl, self)._implicit_coercions(
 | 
						|
                original_element, resolved, argname=argname, **kw
 | 
						|
            )
 | 
						|
 | 
						|
    def _text_coercion(self, element, argname=None):
 | 
						|
        util.warn_deprecated_20(
 | 
						|
            "Using plain strings to indicate SQL statements without using "
 | 
						|
            "the text() construct is  "
 | 
						|
            "deprecated and will be removed in version 2.0.  Ensure plain "
 | 
						|
            "SQL statements are passed using the text() construct."
 | 
						|
        )
 | 
						|
        return elements.TextClause(element)
 | 
						|
 | 
						|
 | 
						|
class SelectStatementImpl(_NoTextCoercion, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if resolved._is_text_clause:
 | 
						|
            return resolved.columns()
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
 | 
						|
class HasCTEImpl(ReturnsRowsImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
 | 
						|
class IsCTEImpl(RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
 | 
						|
class JoinTargetImpl(RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    _skip_clauseelement_for_target_match = True
 | 
						|
 | 
						|
    def _literal_coercion(self, element, legacy=False, **kw):
 | 
						|
        if isinstance(element, str):
 | 
						|
            return element
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, legacy=False, **kw
 | 
						|
    ):
 | 
						|
        if isinstance(original_element, roles.JoinTargetRole):
 | 
						|
            # note that this codepath no longer occurs as of
 | 
						|
            # #6550, unless JoinTargetImpl._skip_clauseelement_for_target_match
 | 
						|
            # were set to False.
 | 
						|
            return original_element
 | 
						|
        elif legacy and isinstance(resolved, str):
 | 
						|
            util.warn_deprecated_20(
 | 
						|
                "Using strings to indicate relationship names in "
 | 
						|
                "Query.join() is deprecated and will be removed in "
 | 
						|
                "SQLAlchemy 2.0.  Please use the class-bound attribute "
 | 
						|
                "directly."
 | 
						|
            )
 | 
						|
            return resolved
 | 
						|
        elif legacy and isinstance(resolved, roles.WhereHavingRole):
 | 
						|
            return resolved
 | 
						|
        elif legacy and resolved._is_select_statement:
 | 
						|
            util.warn_deprecated(
 | 
						|
                "Implicit coercion of SELECT and textual SELECT "
 | 
						|
                "constructs into FROM clauses is deprecated; please call "
 | 
						|
                ".subquery() on any Core select or ORM Query object in "
 | 
						|
                "order to produce a subquery object.",
 | 
						|
                version="1.4",
 | 
						|
            )
 | 
						|
            # TODO: doing _implicit_subquery here causes tests to fail,
 | 
						|
            # how was this working before?  probably that ORM
 | 
						|
            # join logic treated it as a select and subquery would happen
 | 
						|
            # in _ORMJoin->Join
 | 
						|
            return resolved
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
 | 
						|
class FromClauseImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self,
 | 
						|
        original_element,
 | 
						|
        resolved,
 | 
						|
        argname=None,
 | 
						|
        explicit_subquery=False,
 | 
						|
        allow_select=True,
 | 
						|
        **kw
 | 
						|
    ):
 | 
						|
        if resolved._is_select_statement:
 | 
						|
            if explicit_subquery:
 | 
						|
                return resolved.subquery()
 | 
						|
            elif allow_select:
 | 
						|
                util.warn_deprecated(
 | 
						|
                    "Implicit coercion of SELECT and textual SELECT "
 | 
						|
                    "constructs into FROM clauses is deprecated; please call "
 | 
						|
                    ".subquery() on any Core select or ORM Query object in "
 | 
						|
                    "order to produce a subquery object.",
 | 
						|
                    version="1.4",
 | 
						|
                )
 | 
						|
                return resolved._implicit_subquery
 | 
						|
        elif resolved._is_text_clause:
 | 
						|
            return resolved
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
    def _post_coercion(self, element, deannotate=False, **kw):
 | 
						|
        if deannotate:
 | 
						|
            return element._deannotate()
 | 
						|
        else:
 | 
						|
            return element
 | 
						|
 | 
						|
 | 
						|
class StrictFromClauseImpl(FromClauseImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self,
 | 
						|
        original_element,
 | 
						|
        resolved,
 | 
						|
        argname=None,
 | 
						|
        allow_select=False,
 | 
						|
        **kw
 | 
						|
    ):
 | 
						|
        if resolved._is_select_statement and allow_select:
 | 
						|
            util.warn_deprecated(
 | 
						|
                "Implicit coercion of SELECT and textual SELECT constructs "
 | 
						|
                "into FROM clauses is deprecated; please call .subquery() "
 | 
						|
                "on any Core select or ORM Query object in order to produce a "
 | 
						|
                "subquery object.",
 | 
						|
                version="1.4",
 | 
						|
            )
 | 
						|
            return resolved._implicit_subquery
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
 | 
						|
class AnonymizedFromClauseImpl(StrictFromClauseImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _post_coercion(self, element, flat=False, name=None, **kw):
 | 
						|
        assert name is None
 | 
						|
 | 
						|
        return element._anonymous_fromclause(flat=flat)
 | 
						|
 | 
						|
 | 
						|
class DMLTableImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _post_coercion(self, element, **kw):
 | 
						|
        if "dml_table" in element._annotations:
 | 
						|
            return element._annotations["dml_table"]
 | 
						|
        else:
 | 
						|
            return element
 | 
						|
 | 
						|
 | 
						|
class DMLSelectImpl(_NoTextCoercion, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _implicit_coercions(
 | 
						|
        self, original_element, resolved, argname=None, **kw
 | 
						|
    ):
 | 
						|
        if resolved._is_from_clause:
 | 
						|
            if (
 | 
						|
                isinstance(resolved, selectable.Alias)
 | 
						|
                and resolved.element._is_select_statement
 | 
						|
            ):
 | 
						|
                return resolved.element
 | 
						|
            else:
 | 
						|
                return resolved.select()
 | 
						|
        else:
 | 
						|
            self._raise_for_expected(original_element, argname, resolved)
 | 
						|
 | 
						|
 | 
						|
class CompoundElementImpl(_NoTextCoercion, RoleImpl):
 | 
						|
    __slots__ = ()
 | 
						|
 | 
						|
    def _raise_for_expected(self, element, argname=None, resolved=None, **kw):
 | 
						|
        if isinstance(element, roles.FromClauseRole):
 | 
						|
            if element._is_subquery:
 | 
						|
                advice = (
 | 
						|
                    "Use the plain select() object without "
 | 
						|
                    "calling .subquery() or .alias()."
 | 
						|
                )
 | 
						|
            else:
 | 
						|
                advice = (
 | 
						|
                    "To SELECT from any FROM clause, use the .select() method."
 | 
						|
                )
 | 
						|
        else:
 | 
						|
            advice = None
 | 
						|
        return super(CompoundElementImpl, self)._raise_for_expected(
 | 
						|
            element, argname=argname, resolved=resolved, advice=advice, **kw
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
_impl_lookup = {}
 | 
						|
 | 
						|
 | 
						|
for name in dir(roles):
 | 
						|
    cls = getattr(roles, name)
 | 
						|
    if name.endswith("Role"):
 | 
						|
        name = name.replace("Role", "Impl")
 | 
						|
        if name in globals():
 | 
						|
            impl = globals()[name](cls)
 | 
						|
            _impl_lookup[cls] = impl
 |