1# sql/default_comparator.py 
    2# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors 
    3# <see AUTHORS file> 
    4# 
    5# This module is part of SQLAlchemy and is released under 
    6# the MIT License: http://www.opensource.org/licenses/mit-license.php 
    7 
    8"""Default implementation of SQL comparison operations. 
    9""" 
    10 
    11from . import operators 
    12from . import type_api 
    13from .elements import _clause_element_as_expr 
    14from .elements import _const_expr 
    15from .elements import _is_literal 
    16from .elements import _literal_as_text 
    17from .elements import and_ 
    18from .elements import BinaryExpression 
    19from .elements import BindParameter 
    20from .elements import ClauseElement 
    21from .elements import ClauseList 
    22from .elements import collate 
    23from .elements import CollectionAggregate 
    24from .elements import ColumnElement 
    25from .elements import False_ 
    26from .elements import Null 
    27from .elements import or_ 
    28from .elements import TextClause 
    29from .elements import True_ 
    30from .elements import Tuple 
    31from .elements import UnaryExpression 
    32from .elements import Visitable 
    33from .selectable import Alias 
    34from .selectable import ScalarSelect 
    35from .selectable import Selectable 
    36from .selectable import SelectBase 
    37from .. import exc 
    38from .. import util 
    39 
    40 
    41def _boolean_compare( 
    42    expr, 
    43    op, 
    44    obj, 
    45    negate=None, 
    46    reverse=False, 
    47    _python_is_types=(util.NoneType, bool), 
    48    result_type=None, 
    49    **kwargs 
    50): 
    51 
    52    if result_type is None: 
    53        result_type = type_api.BOOLEANTYPE 
    54 
    55    if isinstance(obj, _python_is_types + (Null, True_, False_)): 
    56 
    57        # allow x ==/!= True/False to be treated as a literal. 
    58        # this comes out to "== / != true/false" or "1/0" if those 
    59        # constants aren't supported and works on all platforms 
    60        if op in (operators.eq, operators.ne) and isinstance( 
    61            obj, (bool, True_, False_) 
    62        ): 
    63            return BinaryExpression( 
    64                expr, 
    65                _literal_as_text(obj), 
    66                op, 
    67                type_=result_type, 
    68                negate=negate, 
    69                modifiers=kwargs, 
    70            ) 
    71        elif op in (operators.is_distinct_from, operators.isnot_distinct_from): 
    72            return BinaryExpression( 
    73                expr, 
    74                _literal_as_text(obj), 
    75                op, 
    76                type_=result_type, 
    77                negate=negate, 
    78                modifiers=kwargs, 
    79            ) 
    80        else: 
    81            # all other None/True/False uses IS, IS NOT 
    82            if op in (operators.eq, operators.is_): 
    83                return BinaryExpression( 
    84                    expr, 
    85                    _const_expr(obj), 
    86                    operators.is_, 
    87                    negate=operators.isnot, 
    88                    type_=result_type, 
    89                ) 
    90            elif op in (operators.ne, operators.isnot): 
    91                return BinaryExpression( 
    92                    expr, 
    93                    _const_expr(obj), 
    94                    operators.isnot, 
    95                    negate=operators.is_, 
    96                    type_=result_type, 
    97                ) 
    98            else: 
    99                raise exc.ArgumentError( 
    100                    "Only '=', '!=', 'is_()', 'isnot()', " 
    101                    "'is_distinct_from()', 'isnot_distinct_from()' " 
    102                    "operators can be used with None/True/False" 
    103                ) 
    104    else: 
    105        obj = _check_literal(expr, op, obj) 
    106 
    107    if reverse: 
    108        return BinaryExpression( 
    109            obj, expr, op, type_=result_type, negate=negate, modifiers=kwargs 
    110        ) 
    111    else: 
    112        return BinaryExpression( 
    113            expr, obj, op, type_=result_type, negate=negate, modifiers=kwargs 
    114        ) 
    115 
    116 
    117def _custom_op_operate(expr, op, obj, reverse=False, result_type=None, **kw): 
    118    if result_type is None: 
    119        if op.return_type: 
    120            result_type = op.return_type 
    121        elif op.is_comparison: 
    122            result_type = type_api.BOOLEANTYPE 
    123 
    124    return _binary_operate( 
    125        expr, op, obj, reverse=reverse, result_type=result_type, **kw 
    126    ) 
    127 
    128 
    129def _binary_operate(expr, op, obj, reverse=False, result_type=None, **kw): 
    130    obj = _check_literal(expr, op, obj) 
    131 
    132    if reverse: 
    133        left, right = obj, expr 
    134    else: 
    135        left, right = expr, obj 
    136 
    137    if result_type is None: 
    138        op, result_type = left.comparator._adapt_expression( 
    139            op, right.comparator 
    140        ) 
    141 
    142    return BinaryExpression(left, right, op, type_=result_type, modifiers=kw) 
    143 
    144 
    145def _conjunction_operate(expr, op, other, **kw): 
    146    if op is operators.and_: 
    147        return and_(expr, other) 
    148    elif op is operators.or_: 
    149        return or_(expr, other) 
    150    else: 
    151        raise NotImplementedError() 
    152 
    153 
    154def _scalar(expr, op, fn, **kw): 
    155    return fn(expr) 
    156 
    157 
    158def _in_impl(expr, op, seq_or_selectable, negate_op, **kw): 
    159    seq_or_selectable = _clause_element_as_expr(seq_or_selectable) 
    160 
    161    if isinstance(seq_or_selectable, ScalarSelect): 
    162        return _boolean_compare(expr, op, seq_or_selectable, negate=negate_op) 
    163    elif isinstance(seq_or_selectable, SelectBase): 
    164 
    165        # TODO: if we ever want to support (x, y, z) IN (select x, 
    166        # y, z from table), we would need a multi-column version of 
    167        # as_scalar() to produce a multi- column selectable that 
    168        # does not export itself as a FROM clause 
    169 
    170        return _boolean_compare( 
    171            expr, op, seq_or_selectable.as_scalar(), negate=negate_op, **kw 
    172        ) 
    173    elif isinstance(seq_or_selectable, (Selectable, TextClause)): 
    174        return _boolean_compare( 
    175            expr, op, seq_or_selectable, negate=negate_op, **kw 
    176        ) 
    177    elif isinstance(seq_or_selectable, ClauseElement): 
    178        if ( 
    179            isinstance(seq_or_selectable, BindParameter) 
    180            and seq_or_selectable.expanding 
    181        ): 
    182 
    183            if isinstance(expr, Tuple): 
    184                seq_or_selectable = seq_or_selectable._with_expanding_in_types( 
    185                    [elem.type for elem in expr] 
    186                ) 
    187 
    188            return _boolean_compare( 
    189                expr, op, seq_or_selectable, negate=negate_op 
    190            ) 
    191        else: 
    192            raise exc.InvalidRequestError( 
    193                "in_() accepts" 
    194                " either a list of expressions, " 
    195                'a selectable, or an "expanding" bound parameter: %r' 
    196                % seq_or_selectable 
    197            ) 
    198 
    199    # Handle non selectable arguments as sequences 
    200    args = [] 
    201    for o in seq_or_selectable: 
    202        if not _is_literal(o): 
    203            if not isinstance(o, operators.ColumnOperators): 
    204                raise exc.InvalidRequestError( 
    205                    "in_() accepts" 
    206                    " either a list of expressions, " 
    207                    'a selectable, or an "expanding" bound parameter: %r' % o 
    208                ) 
    209        elif o is None: 
    210            o = Null() 
    211        else: 
    212            o = expr._bind_param(op, o) 
    213        args.append(o) 
    214 
    215    if len(args) == 0: 
    216        op, negate_op = ( 
    217            (operators.empty_in_op, operators.empty_notin_op) 
    218            if op is operators.in_op 
    219            else (operators.empty_notin_op, operators.empty_in_op) 
    220        ) 
    221 
    222    return _boolean_compare( 
    223        expr, 
    224        op, 
    225        ClauseList(_tuple_values=isinstance(expr, Tuple), *args).self_group( 
    226            against=op 
    227        ), 
    228        negate=negate_op, 
    229    ) 
    230 
    231 
    232def _getitem_impl(expr, op, other, **kw): 
    233    if isinstance(expr.type, type_api.INDEXABLE): 
    234        other = _check_literal(expr, op, other) 
    235        return _binary_operate(expr, op, other, **kw) 
    236    else: 
    237        _unsupported_impl(expr, op, other, **kw) 
    238 
    239 
    240def _unsupported_impl(expr, op, *arg, **kw): 
    241    raise NotImplementedError( 
    242        "Operator '%s' is not supported on " "this expression" % op.__name__ 
    243    ) 
    244 
    245 
    246def _inv_impl(expr, op, **kw): 
    247    """See :meth:`.ColumnOperators.__inv__`.""" 
    248    if hasattr(expr, "negation_clause"): 
    249        return expr.negation_clause 
    250    else: 
    251        return expr._negate() 
    252 
    253 
    254def _neg_impl(expr, op, **kw): 
    255    """See :meth:`.ColumnOperators.__neg__`.""" 
    256    return UnaryExpression(expr, operator=operators.neg, type_=expr.type) 
    257 
    258 
    259def _match_impl(expr, op, other, **kw): 
    260    """See :meth:`.ColumnOperators.match`.""" 
    261 
    262    return _boolean_compare( 
    263        expr, 
    264        operators.match_op, 
    265        _check_literal(expr, operators.match_op, other), 
    266        result_type=type_api.MATCHTYPE, 
    267        negate=operators.notmatch_op 
    268        if op is operators.match_op 
    269        else operators.match_op, 
    270        **kw 
    271    ) 
    272 
    273 
    274def _distinct_impl(expr, op, **kw): 
    275    """See :meth:`.ColumnOperators.distinct`.""" 
    276    return UnaryExpression( 
    277        expr, operator=operators.distinct_op, type_=expr.type 
    278    ) 
    279 
    280 
    281def _between_impl(expr, op, cleft, cright, **kw): 
    282    """See :meth:`.ColumnOperators.between`.""" 
    283    return BinaryExpression( 
    284        expr, 
    285        ClauseList( 
    286            _check_literal(expr, operators.and_, cleft), 
    287            _check_literal(expr, operators.and_, cright), 
    288            operator=operators.and_, 
    289            group=False, 
    290            group_contents=False, 
    291        ), 
    292        op, 
    293        negate=operators.notbetween_op 
    294        if op is operators.between_op 
    295        else operators.between_op, 
    296        modifiers=kw, 
    297    ) 
    298 
    299 
    300def _collate_impl(expr, op, other, **kw): 
    301    return collate(expr, other) 
    302 
    303 
    304# a mapping of operators with the method they use, along with 
    305# their negated operator for comparison operators 
    306operator_lookup = { 
    307    "and_": (_conjunction_operate,), 
    308    "or_": (_conjunction_operate,), 
    309    "inv": (_inv_impl,), 
    310    "add": (_binary_operate,), 
    311    "mul": (_binary_operate,), 
    312    "sub": (_binary_operate,), 
    313    "div": (_binary_operate,), 
    314    "mod": (_binary_operate,), 
    315    "truediv": (_binary_operate,), 
    316    "custom_op": (_custom_op_operate,), 
    317    "json_path_getitem_op": (_binary_operate,), 
    318    "json_getitem_op": (_binary_operate,), 
    319    "concat_op": (_binary_operate,), 
    320    "any_op": (_scalar, CollectionAggregate._create_any), 
    321    "all_op": (_scalar, CollectionAggregate._create_all), 
    322    "lt": (_boolean_compare, operators.ge), 
    323    "le": (_boolean_compare, operators.gt), 
    324    "ne": (_boolean_compare, operators.eq), 
    325    "gt": (_boolean_compare, operators.le), 
    326    "ge": (_boolean_compare, operators.lt), 
    327    "eq": (_boolean_compare, operators.ne), 
    328    "is_distinct_from": (_boolean_compare, operators.isnot_distinct_from), 
    329    "isnot_distinct_from": (_boolean_compare, operators.is_distinct_from), 
    330    "like_op": (_boolean_compare, operators.notlike_op), 
    331    "ilike_op": (_boolean_compare, operators.notilike_op), 
    332    "notlike_op": (_boolean_compare, operators.like_op), 
    333    "notilike_op": (_boolean_compare, operators.ilike_op), 
    334    "contains_op": (_boolean_compare, operators.notcontains_op), 
    335    "startswith_op": (_boolean_compare, operators.notstartswith_op), 
    336    "endswith_op": (_boolean_compare, operators.notendswith_op), 
    337    "desc_op": (_scalar, UnaryExpression._create_desc), 
    338    "asc_op": (_scalar, UnaryExpression._create_asc), 
    339    "nullsfirst_op": (_scalar, UnaryExpression._create_nullsfirst), 
    340    "nullslast_op": (_scalar, UnaryExpression._create_nullslast), 
    341    "in_op": (_in_impl, operators.notin_op), 
    342    "notin_op": (_in_impl, operators.in_op), 
    343    "is_": (_boolean_compare, operators.is_), 
    344    "isnot": (_boolean_compare, operators.isnot), 
    345    "collate": (_collate_impl,), 
    346    "match_op": (_match_impl,), 
    347    "notmatch_op": (_match_impl,), 
    348    "distinct_op": (_distinct_impl,), 
    349    "between_op": (_between_impl,), 
    350    "notbetween_op": (_between_impl,), 
    351    "neg": (_neg_impl,), 
    352    "getitem": (_getitem_impl,), 
    353    "lshift": (_unsupported_impl,), 
    354    "rshift": (_unsupported_impl,), 
    355    "contains": (_unsupported_impl,), 
    356} 
    357 
    358 
    359def _check_literal(expr, operator, other, bindparam_type=None): 
    360    if isinstance(other, (ColumnElement, TextClause)): 
    361        if isinstance(other, BindParameter) and other.type._isnull: 
    362            other = other._clone() 
    363            other.type = expr.type 
    364        return other 
    365    elif hasattr(other, "__clause_element__"): 
    366        other = other.__clause_element__() 
    367    elif isinstance(other, type_api.TypeEngine.Comparator): 
    368        other = other.expr 
    369 
    370    if isinstance(other, (SelectBase, Alias)): 
    371        return other.as_scalar() 
    372    elif not isinstance(other, Visitable): 
    373        return expr._bind_param(operator, other, type_=bindparam_type) 
    374    else: 
    375        return other