]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- clean up default comparator which doesn't need to be a class,
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 4 Jan 2015 23:45:30 +0000 (18:45 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 4 Jan 2015 23:45:30 +0000 (18:45 -0500)
get PG stuff working

lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/dialects/postgresql/json.py
lib/sqlalchemy/sql/default_comparator.py
lib/sqlalchemy/sql/type_api.py

index d870dd2952c73ce72364ebee9bae2c295fbf5fb4..fa9a2cfd0537cfd00325d4ea18a9e691bb1fd91f 100644 (file)
@@ -483,7 +483,7 @@ import re
 
 from ... import sql, schema, exc, util
 from ...engine import default, reflection
-from ...sql import compiler, expression, operators
+from ...sql import compiler, expression, operators, default_comparator
 from ... import types as sqltypes
 
 try:
@@ -680,10 +680,10 @@ class _Slice(expression.ColumnElement):
     type = sqltypes.NULLTYPE
 
     def __init__(self, slice_, source_comparator):
-        self.start = source_comparator._check_literal(
+        self.start = default_comparator._check_literal(
             source_comparator.expr,
             operators.getitem, slice_.start)
-        self.stop = source_comparator._check_literal(
+        self.stop = default_comparator._check_literal(
             source_comparator.expr,
             operators.getitem, slice_.stop)
 
@@ -876,8 +876,9 @@ class ARRAY(sqltypes.Concatenable, sqltypes.TypeEngine):
                 index += shift_indexes
                 return_type = self.type.item_type
 
-            return self._binary_operate(self.expr, operators.getitem, index,
-                                        result_type=return_type)
+            return default_comparator._binary_operate(
+                self.expr, operators.getitem, index,
+                result_type=return_type)
 
         def any(self, other, operator=operators.eq):
             """Return ``other operator ANY (array)`` clause.
index 50176918e5bf9eef97ab0ea74cd43a8f738d1ae7..f38c4a56a23b6fa64f1d7e318834f05e921b97ce 100644 (file)
@@ -12,7 +12,7 @@ from .base import ischema_names
 from ... import types as sqltypes
 from ...sql.operators import custom_op
 from ... import sql
-from ...sql import elements
+from ...sql import elements, default_comparator
 from ... import util
 
 __all__ = ('JSON', 'JSONElement', 'JSONB')
@@ -46,7 +46,8 @@ class JSONElement(elements.BinaryExpression):
 
         self._json_opstring = opstring
         operator = custom_op(opstring, precedence=5)
-        right = left._check_literal(left, operator, right)
+        right = default_comparator._check_literal(
+            left, operator, right)
         super(JSONElement, self).__init__(
             left, right, operator, type_=result_type)
 
index c898b78d6af2a7c99c5d618a15a8fd4785d1be7d..bb9e53aaea8c6531058c87e9a4ef4bf1a3f4a88c 100644 (file)
@@ -18,267 +18,270 @@ from .elements import BindParameter, True_, False_, BinaryExpression, \
 from .selectable import SelectBase, Alias, Selectable, ScalarSelect
 
 
-class _DefaultColumnComparator(object):
-    """Defines comparison and math operations.
-
-    See :class:`.ColumnOperators` and :class:`.Operators` for descriptions
-    of all operations.
-
-    """
-
-    def _boolean_compare(self, expr, op, obj, negate=None, reverse=False,
-                         _python_is_types=(util.NoneType, bool),
-                         result_type = None,
-                         **kwargs):
-
-        if result_type is None:
-            result_type = type_api.BOOLEANTYPE
-
-        if isinstance(obj, _python_is_types + (Null, True_, False_)):
-
-            # allow x ==/!= True/False to be treated as a literal.
-            # this comes out to "== / != true/false" or "1/0" if those
-            # constants aren't supported and works on all platforms
-            if op in (operators.eq, operators.ne) and \
-                    isinstance(obj, (bool, True_, False_)):
-                return BinaryExpression(expr,
-                                        _literal_as_text(obj),
-                                        op,
-                                        type_=result_type,
-                                        negate=negate, modifiers=kwargs)
-            else:
-                # all other None/True/False uses IS, IS NOT
-                if op in (operators.eq, operators.is_):
-                    return BinaryExpression(expr, _const_expr(obj),
-                                            operators.is_,
-                                            negate=operators.isnot)
-                elif op in (operators.ne, operators.isnot):
-                    return BinaryExpression(expr, _const_expr(obj),
-                                            operators.isnot,
-                                            negate=operators.is_)
-                else:
-                    raise exc.ArgumentError(
-                        "Only '=', '!=', 'is_()', 'isnot()' operators can "
-                        "be used with None/True/False")
-        else:
-            obj = self._check_literal(expr, op, obj)
+def _boolean_compare(expr, op, obj, negate=None, reverse=False,
+                     _python_is_types=(util.NoneType, bool),
+                     result_type = None,
+                     **kwargs):
 
-        if reverse:
-            return BinaryExpression(obj,
-                                    expr,
-                                    op,
-                                    type_=result_type,
-                                    negate=negate, modifiers=kwargs)
-        else:
+    if result_type is None:
+        result_type = type_api.BOOLEANTYPE
+
+    if isinstance(obj, _python_is_types + (Null, True_, False_)):
+
+        # allow x ==/!= True/False to be treated as a literal.
+        # this comes out to "== / != true/false" or "1/0" if those
+        # constants aren't supported and works on all platforms
+        if op in (operators.eq, operators.ne) and \
+                isinstance(obj, (bool, True_, False_)):
             return BinaryExpression(expr,
-                                    obj,
+                                    _literal_as_text(obj),
                                     op,
                                     type_=result_type,
                                     negate=negate, modifiers=kwargs)
-
-    def _binary_operate(self, expr, op, obj, reverse=False, result_type=None,
-                        **kw):
-        obj = self._check_literal(expr, op, obj)
-
-        if reverse:
-            left, right = obj, expr
-        else:
-            left, right = expr, obj
-
-        if result_type is None:
-            op, result_type = left.comparator._adapt_expression(
-                op, right.comparator)
-
-        return BinaryExpression(
-            left, right, op, type_=result_type, modifiers=kw)
-
-    def _conjunction_operate(self, expr, op, other, **kw):
-        if op is operators.and_:
-            return and_(expr, other)
-        elif op is operators.or_:
-            return or_(expr, other)
         else:
-            raise NotImplementedError()
-
-    def _scalar(self, expr, op, fn, **kw):
-        return fn(expr)
-
-    def _in_impl(self, expr, op, seq_or_selectable, negate_op, **kw):
-        seq_or_selectable = _clause_element_as_expr(seq_or_selectable)
-
-        if isinstance(seq_or_selectable, ScalarSelect):
-            return self._boolean_compare(expr, op, seq_or_selectable,
-                                         negate=negate_op)
-        elif isinstance(seq_or_selectable, SelectBase):
-
-            # TODO: if we ever want to support (x, y, z) IN (select x,
-            # y, z from table), we would need a multi-column version of
-            # as_scalar() to produce a multi- column selectable that
-            # does not export itself as a FROM clause
-
-            return self._boolean_compare(
-                expr, op, seq_or_selectable.as_scalar(),
-                negate=negate_op, **kw)
-        elif isinstance(seq_or_selectable, (Selectable, TextClause)):
-            return self._boolean_compare(expr, op, seq_or_selectable,
-                                         negate=negate_op, **kw)
-        elif isinstance(seq_or_selectable, ClauseElement):
-            raise exc.InvalidRequestError(
-                'in_() accepts'
-                ' either a list of expressions '
-                'or a selectable: %r' % seq_or_selectable)
-
-        # Handle non selectable arguments as sequences
-        args = []
-        for o in seq_or_selectable:
-            if not _is_literal(o):
-                if not isinstance(o, operators.ColumnOperators):
-                    raise exc.InvalidRequestError(
-                        'in_() accepts'
-                        ' either a list of expressions '
-                        'or a selectable: %r' % o)
-            elif o is None:
-                o = Null()
+            # all other None/True/False uses IS, IS NOT
+            if op in (operators.eq, operators.is_):
+                return BinaryExpression(expr, _const_expr(obj),
+                                        operators.is_,
+                                        negate=operators.isnot)
+            elif op in (operators.ne, operators.isnot):
+                return BinaryExpression(expr, _const_expr(obj),
+                                        operators.isnot,
+                                        negate=operators.is_)
             else:
-                o = expr._bind_param(op, o)
-            args.append(o)
-        if len(args) == 0:
-
-            # Special case handling for empty IN's, behave like
-            # comparison against zero row selectable.  We use != to
-            # build the contradiction as it handles NULL values
-            # appropriately, i.e. "not (x IN ())" should not return NULL
-            # values for x.
-
-            util.warn('The IN-predicate on "%s" was invoked with an '
-                      'empty sequence. This results in a '
-                      'contradiction, which nonetheless can be '
-                      'expensive to evaluate.  Consider alternative '
-                      'strategies for improved performance.' % expr)
-            if op is operators.in_op:
-                return expr != expr
-            else:
-                return expr == expr
-
-        return self._boolean_compare(expr, op,
-                                     ClauseList(*args).self_group(against=op),
-                                     negate=negate_op)
-
-    def _unsupported_impl(self, expr, op, *arg, **kw):
-        raise NotImplementedError("Operator '%s' is not supported on "
-                                  "this expression" % op.__name__)
-
-    def _inv_impl(self, expr, op, **kw):
-        """See :meth:`.ColumnOperators.__inv__`."""
-        if hasattr(expr, 'negation_clause'):
-            return expr.negation_clause
+                raise exc.ArgumentError(
+                    "Only '=', '!=', 'is_()', 'isnot()' operators can "
+                    "be used with None/True/False")
+    else:
+        obj = _check_literal(expr, op, obj)
+
+    if reverse:
+        return BinaryExpression(obj,
+                                expr,
+                                op,
+                                type_=result_type,
+                                negate=negate, modifiers=kwargs)
+    else:
+        return BinaryExpression(expr,
+                                obj,
+                                op,
+                                type_=result_type,
+                                negate=negate, modifiers=kwargs)
+
+
+def _binary_operate(expr, op, obj, reverse=False, result_type=None,
+                    **kw):
+    obj = _check_literal(expr, op, obj)
+
+    if reverse:
+        left, right = obj, expr
+    else:
+        left, right = expr, obj
+
+    if result_type is None:
+        op, result_type = left.comparator._adapt_expression(
+            op, right.comparator)
+
+    return BinaryExpression(
+        left, right, op, type_=result_type, modifiers=kw)
+
+
+def _conjunction_operate(expr, op, other, **kw):
+    if op is operators.and_:
+        return and_(expr, other)
+    elif op is operators.or_:
+        return or_(expr, other)
+    else:
+        raise NotImplementedError()
+
+
+def _scalar(expr, op, fn, **kw):
+    return fn(expr)
+
+
+def _in_impl(expr, op, seq_or_selectable, negate_op, **kw):
+    seq_or_selectable = _clause_element_as_expr(seq_or_selectable)
+
+    if isinstance(seq_or_selectable, ScalarSelect):
+        return _boolean_compare(expr, op, seq_or_selectable,
+                                negate=negate_op)
+    elif isinstance(seq_or_selectable, SelectBase):
+
+        # TODO: if we ever want to support (x, y, z) IN (select x,
+        # y, z from table), we would need a multi-column version of
+        # as_scalar() to produce a multi- column selectable that
+        # does not export itself as a FROM clause
+
+        return _boolean_compare(
+            expr, op, seq_or_selectable.as_scalar(),
+            negate=negate_op, **kw)
+    elif isinstance(seq_or_selectable, (Selectable, TextClause)):
+        return _boolean_compare(expr, op, seq_or_selectable,
+                                negate=negate_op, **kw)
+    elif isinstance(seq_or_selectable, ClauseElement):
+        raise exc.InvalidRequestError(
+            'in_() accepts'
+            ' either a list of expressions '
+            'or a selectable: %r' % seq_or_selectable)
+
+    # Handle non selectable arguments as sequences
+    args = []
+    for o in seq_or_selectable:
+        if not _is_literal(o):
+            if not isinstance(o, operators.ColumnOperators):
+                raise exc.InvalidRequestError(
+                    'in_() accepts'
+                    ' either a list of expressions '
+                    'or a selectable: %r' % o)
+        elif o is None:
+            o = Null()
         else:
-            return expr._negate()
-
-    def _neg_impl(self, expr, op, **kw):
-        """See :meth:`.ColumnOperators.__neg__`."""
-        return UnaryExpression(expr, operator=operators.neg)
-
-    def _match_impl(self, expr, op, other, **kw):
-        """See :meth:`.ColumnOperators.match`."""
-
-        return self._boolean_compare(
-            expr, operators.match_op,
-            self._check_literal(
-                expr, operators.match_op, other),
-            result_type=type_api.MATCHTYPE,
-            negate=operators.notmatch_op
-            if op is operators.match_op else operators.match_op,
-            **kw
-        )
-
-    def _distinct_impl(self, expr, op, **kw):
-        """See :meth:`.ColumnOperators.distinct`."""
-        return UnaryExpression(expr, operator=operators.distinct_op,
-                               type_=expr.type)
-
-    def _between_impl(self, expr, op, cleft, cright, **kw):
-        """See :meth:`.ColumnOperators.between`."""
-        return BinaryExpression(
-            expr,
-            ClauseList(
-                self._check_literal(expr, operators.and_, cleft),
-                self._check_literal(expr, operators.and_, cright),
-                operator=operators.and_,
-                group=False, group_contents=False),
-            op,
-            negate=operators.notbetween_op
-            if op is operators.between_op
-            else operators.between_op,
-            modifiers=kw)
-
-    def _collate_impl(self, expr, op, other, **kw):
-        return collate(expr, other)
-
-    # a mapping of operators with the method they use, along with
-    # their negated operator for comparison operators
-    operators = {
-        "and_": (_conjunction_operate,),
-        "or_": (_conjunction_operate,),
-        "inv": (_inv_impl,),
-        "add": (_binary_operate,),
-        "mul": (_binary_operate,),
-        "sub": (_binary_operate,),
-        "div": (_binary_operate,),
-        "mod": (_binary_operate,),
-        "truediv": (_binary_operate,),
-        "custom_op": (_binary_operate,),
-        "concat_op": (_binary_operate,),
-        "lt": (_boolean_compare, operators.ge),
-        "le": (_boolean_compare, operators.gt),
-        "ne": (_boolean_compare, operators.eq),
-        "gt": (_boolean_compare, operators.le),
-        "ge": (_boolean_compare, operators.lt),
-        "eq": (_boolean_compare, operators.ne),
-        "like_op": (_boolean_compare, operators.notlike_op),
-        "ilike_op": (_boolean_compare, operators.notilike_op),
-        "notlike_op": (_boolean_compare, operators.like_op),
-        "notilike_op": (_boolean_compare, operators.ilike_op),
-        "contains_op": (_boolean_compare, operators.notcontains_op),
-        "startswith_op": (_boolean_compare, operators.notstartswith_op),
-        "endswith_op": (_boolean_compare, operators.notendswith_op),
-        "desc_op": (_scalar, UnaryExpression._create_desc),
-        "asc_op": (_scalar, UnaryExpression._create_asc),
-        "nullsfirst_op": (_scalar, UnaryExpression._create_nullsfirst),
-        "nullslast_op": (_scalar, UnaryExpression._create_nullslast),
-        "in_op": (_in_impl, operators.notin_op),
-        "notin_op": (_in_impl, operators.in_op),
-        "is_": (_boolean_compare, operators.is_),
-        "isnot": (_boolean_compare, operators.isnot),
-        "collate": (_collate_impl,),
-        "match_op": (_match_impl,),
-        "notmatch_op": (_match_impl,),
-        "distinct_op": (_distinct_impl,),
-        "between_op": (_between_impl, ),
-        "notbetween_op": (_between_impl, ),
-        "neg": (_neg_impl,),
-        "getitem": (_unsupported_impl,),
-        "lshift": (_unsupported_impl,),
-        "rshift": (_unsupported_impl,),
-    }
-
-    def _check_literal(self, expr, operator, other):
-        if isinstance(other, (ColumnElement, TextClause)):
-            if isinstance(other, BindParameter) and \
-                    other.type._isnull:
-                other = other._clone()
-                other.type = expr.type
-            return other
-        elif hasattr(other, '__clause_element__'):
-            other = other.__clause_element__()
-        elif isinstance(other, type_api.TypeEngine.Comparator):
-            other = other.expr
-
-        if isinstance(other, (SelectBase, Alias)):
-            return other.as_scalar()
-        elif not isinstance(other, (ColumnElement, TextClause)):
-            return expr._bind_param(operator, other)
+            o = expr._bind_param(op, o)
+        args.append(o)
+    if len(args) == 0:
+
+        # Special case handling for empty IN's, behave like
+        # comparison against zero row selectable.  We use != to
+        # build the contradiction as it handles NULL values
+        # appropriately, i.e. "not (x IN ())" should not return NULL
+        # values for x.
+
+        util.warn('The IN-predicate on "%s" was invoked with an '
+                  'empty sequence. This results in a '
+                  'contradiction, which nonetheless can be '
+                  'expensive to evaluate.  Consider alternative '
+                  'strategies for improved performance.' % expr)
+        if op is operators.in_op:
+            return expr != expr
         else:
-            return other
+            return expr == expr
+
+    return _boolean_compare(expr, op,
+                            ClauseList(*args).self_group(against=op),
+                            negate=negate_op)
+
+
+def _unsupported_impl(expr, op, *arg, **kw):
+    raise NotImplementedError("Operator '%s' is not supported on "
+                              "this expression" % op.__name__)
+
+
+def _inv_impl(expr, op, **kw):
+    """See :meth:`.ColumnOperators.__inv__`."""
+    if hasattr(expr, 'negation_clause'):
+        return expr.negation_clause
+    else:
+        return expr._negate()
+
+
+def _neg_impl(expr, op, **kw):
+    """See :meth:`.ColumnOperators.__neg__`."""
+    return UnaryExpression(expr, operator=operators.neg)
+
+
+def _match_impl(expr, op, other, **kw):
+    """See :meth:`.ColumnOperators.match`."""
+
+    return _boolean_compare(
+        expr, operators.match_op,
+        _check_literal(
+            expr, operators.match_op, other),
+        result_type=type_api.MATCHTYPE,
+        negate=operators.notmatch_op
+        if op is operators.match_op else operators.match_op,
+        **kw
+    )
+
+
+def _distinct_impl(expr, op, **kw):
+    """See :meth:`.ColumnOperators.distinct`."""
+    return UnaryExpression(expr, operator=operators.distinct_op,
+                           type_=expr.type)
+
+
+def _between_impl(expr, op, cleft, cright, **kw):
+    """See :meth:`.ColumnOperators.between`."""
+    return BinaryExpression(
+        expr,
+        ClauseList(
+            _check_literal(expr, operators.and_, cleft),
+            _check_literal(expr, operators.and_, cright),
+            operator=operators.and_,
+            group=False, group_contents=False),
+        op,
+        negate=operators.notbetween_op
+        if op is operators.between_op
+        else operators.between_op,
+        modifiers=kw)
+
+
+def _collate_impl(expr, op, other, **kw):
+    return collate(expr, other)
+
+# a mapping of operators with the method they use, along with
+# their negated operator for comparison operators
+operator_lookup = {
+    "and_": (_conjunction_operate,),
+    "or_": (_conjunction_operate,),
+    "inv": (_inv_impl,),
+    "add": (_binary_operate,),
+    "mul": (_binary_operate,),
+    "sub": (_binary_operate,),
+    "div": (_binary_operate,),
+    "mod": (_binary_operate,),
+    "truediv": (_binary_operate,),
+    "custom_op": (_binary_operate,),
+    "concat_op": (_binary_operate,),
+    "lt": (_boolean_compare, operators.ge),
+    "le": (_boolean_compare, operators.gt),
+    "ne": (_boolean_compare, operators.eq),
+    "gt": (_boolean_compare, operators.le),
+    "ge": (_boolean_compare, operators.lt),
+    "eq": (_boolean_compare, operators.ne),
+    "like_op": (_boolean_compare, operators.notlike_op),
+    "ilike_op": (_boolean_compare, operators.notilike_op),
+    "notlike_op": (_boolean_compare, operators.like_op),
+    "notilike_op": (_boolean_compare, operators.ilike_op),
+    "contains_op": (_boolean_compare, operators.notcontains_op),
+    "startswith_op": (_boolean_compare, operators.notstartswith_op),
+    "endswith_op": (_boolean_compare, operators.notendswith_op),
+    "desc_op": (_scalar, UnaryExpression._create_desc),
+    "asc_op": (_scalar, UnaryExpression._create_asc),
+    "nullsfirst_op": (_scalar, UnaryExpression._create_nullsfirst),
+    "nullslast_op": (_scalar, UnaryExpression._create_nullslast),
+    "in_op": (_in_impl, operators.notin_op),
+    "notin_op": (_in_impl, operators.in_op),
+    "is_": (_boolean_compare, operators.is_),
+    "isnot": (_boolean_compare, operators.isnot),
+    "collate": (_collate_impl,),
+    "match_op": (_match_impl,),
+    "notmatch_op": (_match_impl,),
+    "distinct_op": (_distinct_impl,),
+    "between_op": (_between_impl, ),
+    "notbetween_op": (_between_impl, ),
+    "neg": (_neg_impl,),
+    "getitem": (_unsupported_impl,),
+    "lshift": (_unsupported_impl,),
+    "rshift": (_unsupported_impl,),
+}
+
+
+def _check_literal(expr, operator, other):
+    if isinstance(other, (ColumnElement, TextClause)):
+        if isinstance(other, BindParameter) and \
+                other.type._isnull:
+            other = other._clone()
+            other.type = expr.type
+        return other
+    elif hasattr(other, '__clause_element__'):
+        other = other.__clause_element__()
+    elif isinstance(other, type_api.TypeEngine.Comparator):
+        other = other.expr
+
+    if isinstance(other, (SelectBase, Alias)):
+        return other.as_scalar()
+    elif not isinstance(other, (ColumnElement, TextClause)):
+        return expr._bind_param(operator, other)
+    else:
+        return other
 
-the_comparator = _DefaultColumnComparator()
index 834640928bc7ad9efa34192530f83138e80a6fa9..bff497800992f5ae9771623e5cedf65a39dc1549 100644 (file)
@@ -48,21 +48,21 @@ class TypeEngine(Visitable):
         """
         __slots__ = 'expr', 'type'
 
+        default_comparator = None
+
         def __init__(self, expr):
             self.expr = expr
             self.type = expr.type
 
         @util.dependencies('sqlalchemy.sql.default_comparator')
         def operate(self, default_comparator, op, *other, **kwargs):
-            comp = default_comparator.the_comparator
-            o = comp.operators[op.__name__]
-            return o[0](comp, self.expr, op, *(other + o[1:]), **kwargs)
+            o = default_comparator.operator_lookup[op.__name__]
+            return o[0](self.expr, op, *(other + o[1:]), **kwargs)
 
         @util.dependencies('sqlalchemy.sql.default_comparator')
         def reverse_operate(self, default_comparator, op, other, **kwargs):
-            comp = default_comparator.the_comparator
-            o = comp.operators[op.__name__]
-            return o[0](comp, self.expr, op, other,
+            o = default_comparator.operator_lookup[op.__name__]
+            return o[0](self.expr, op, other,
                         reverse=True, *o[1:], **kwargs)
 
         def _adapt_expression(self, op, other_comparator):