# This file is part of python-sql. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import warnings from array import array from sql import CombiningQuery, Expression, Flavor, Null, Select __all__ = ['And', 'Or', 'Not', 'Less', 'Greater', 'LessEqual', 'GreaterEqual', 'Equal', 'NotEqual', 'Between', 'NotBetween', 'IsDistinct', 'IsNotDistinct', 'Is', 'IsNot', 'Add', 'Sub', 'Mul', 'Div', 'FloorDiv', 'Mod', 'Pow', 'SquareRoot', 'CubeRoot', 'Factorial', 'Abs', 'BAnd', 'BOr', 'BXor', 'BNot', 'LShift', 'RShift', 'Concat', 'Like', 'NotLike', 'ILike', 'NotILike', 'In', 'NotIn', 'Exists', 'Any', 'Some', 'All'] class Operator(Expression): __slots__ = () @property def table(self): return '' @property def name(self): return '' @property def _operands(self): return () @property def params(self): def convert(operands): params = [] for operand in operands: if isinstance(operand, (Expression, Select, CombiningQuery)): params.extend(operand.params) elif isinstance(operand, (list, tuple)): params.extend(convert(operand)) elif isinstance(operand, array): params.extend(operand) else: params.append(operand) return params return tuple(convert(self._operands)) def _format(self, operand, param=None): if param is None: param = Flavor.get().param if isinstance(operand, Expression): return str(operand) elif isinstance(operand, (Select, CombiningQuery)): return '(%s)' % operand elif isinstance(operand, (list, tuple)): return '(' + ', '.join(self._format(o, param) for o in operand) + ')' elif isinstance(operand, array): return '(' + ', '.join((param,) * len(operand)) + ')' else: return param def __str__(self): raise NotImplementedError def __and__(self, other): if isinstance(other, And): return And([self] + other) else: return And((self, other)) def __or__(self, other): if isinstance(other, Or): return Or([self] + other) else: return Or((self, other)) class UnaryOperator(Operator): __slots__ = 'operand' _operator = '' def __init__(self, operand): self.operand = operand @property def _operands(self): return (self.operand,) def __str__(self): return '(%s %s)' % (self._operator, self._format(self.operand)) class BinaryOperator(Operator): __slots__ = ('left', 'right') _operator = '' def __init__(self, left, right): self.left = left self.right = right @property def _operands(self): return (self.left, self.right) def __str__(self): left, right = self._operands return '(%s %s %s)' % (self._format(left), self._operator, self._format(right)) def __invert__(self): return _INVERT[self.__class__](self.left, self.right) class NaryOperator(list, Operator): __slots__ = () _operator = '' @property def _operands(self): return self def __str__(self): return '(' + (' %s ' % self._operator).join( map(self._format, self)) + ')' class And(NaryOperator): __slots__ = () _operator = 'AND' class Or(NaryOperator): __slots__ = () _operator = 'OR' class Not(UnaryOperator): __slots__ = () _operator = 'NOT' class Neg(UnaryOperator): __slots__ = () _operator = '-' class Pos(UnaryOperator): __slots__ = () _operator = '+' class Less(BinaryOperator): __slots__ = () _operator = '<' class Greater(BinaryOperator): __slots__ = () _operator = '>' class LessEqual(BinaryOperator): __slots__ = () _operator = '<=' class GreaterEqual(BinaryOperator): __slots__ = () _operator = '>=' class Equal(BinaryOperator): __slots__ = () _operator = '=' @property def _operands(self): if self.left is Null: return (self.right,) elif self.right is Null: return (self.left,) return super(Equal, self)._operands def __str__(self): if self.left is Null: return '(%s IS NULL)' % self.right elif self.right is Null: return '(%s IS NULL)' % self.left return super(Equal, self).__str__() class NotEqual(Equal): __slots__ = () _operator = '!=' def __str__(self): if self.left is Null: return '(%s IS NOT NULL)' % self.right elif self.right is Null: return '(%s IS NOT NULL)' % self.left return super(Equal, self).__str__() class Between(Operator): __slots__ = ('operand', 'left', 'right', 'symmetric') _operator = 'BETWEEN' def __init__(self, operand, left, right, symmetric=False): self.operand = operand self.left = left self.right = right self.symmetric = symmetric @property def _operands(self): return (self.operand, self.left, self.right) def __str__(self): operator = self._operator if self.symmetric: operator += ' SYMMETRIC' return '(%s %s %s AND %s)' % ( self._format(self.operand), operator, self._format(self.left), self._format(self.right)) def __invert__(self): return _INVERT[self.__class__]( self.operand, self.left, self.right, self.symmetric) class NotBetween(Between): __slots__ = () _operator = 'NOT BETWEEN' class IsDistinct(BinaryOperator): __slots__ = () _operator = 'IS DISTINCT FROM' class IsNotDistinct(IsDistinct): __slots__ = () _operator = 'IS NOT DISTINCT FROM' class Is(BinaryOperator): __slots__ = () _operator = 'IS' def __init__(self, left, right): if right not in {None, True, False}: raise ValueError("invalid right: %r" % right) super(Is, self).__init__(left, right) @property def _operands(self): return (self.left,) def __str__(self): if self.right is None: return '(%s %s UNKNOWN)' % ( self._format(self.left), self._operator) elif self.right is True: return '(%s %s TRUE)' % (self._format(self.left), self._operator) elif self.right is False: return '(%s %s FALSE)' % (self._format(self.left), self._operator) class IsNot(Is): __slots__ = () _operator = 'IS NOT' class Add(BinaryOperator): __slots__ = () _operator = '+' class Sub(BinaryOperator): __slots__ = () _operator = '-' class Mul(BinaryOperator): __slots__ = () _operator = '*' class Div(BinaryOperator): __slots__ = () _operator = '/' # For backward compatibility class FloorDiv(BinaryOperator): __slots__ = () _operator = '/' def __init__(self, left, right): warnings.warn('FloorDiv operator is deprecated, use Div function', DeprecationWarning, stacklevel=2) super(FloorDiv, self).__init__(left, right) class Mod(BinaryOperator): __slots__ = () @property def _operator(self): # '%' must be escaped with format paramstyle if Flavor.get().paramstyle == 'format': return '%%' else: return '%' class Pow(BinaryOperator): __slots__ = () _operator = '^' class SquareRoot(UnaryOperator): __slots__ = () _operator = '|/' class CubeRoot(UnaryOperator): __slots__ = () _operator = '||/' class Factorial(UnaryOperator): __slots__ = () _operator = '!!' class Abs(UnaryOperator): __slots__ = () _operator = '@' class BAnd(BinaryOperator): __slots__ = () _operator = '&' class BOr(BinaryOperator): __slots__ = () _operator = '|' class BXor(BinaryOperator): __slots__ = () _operator = '#' class BNot(UnaryOperator): __slots__ = () _operator = '~' class LShift(BinaryOperator): __slots__ = () _operator = '<<' class RShift(BinaryOperator): __slots__ = () _operator = '>>' class Concat(BinaryOperator): __slots__ = () _operator = '||' class Like(BinaryOperator): __slots__ = 'escape' _operator = 'LIKE' def __init__(self, left, right, escape=None): super().__init__(left, right) if escape and len(escape) != 1: raise ValueError("invalid escape: %r" % escape) self.escape = escape @property def params(self): params = super().params if self.escape or Flavor().get().escape_empty: params += (self.escape or '',) return params def __str__(self): left, right = self._operands if self.escape or Flavor().get().escape_empty: return '(%s %s %s ESCAPE %s)' % ( self._format(left), self._operator, self._format(right), self._format(self.escape or '')) else: return '(%s %s %s)' % ( self._format(left), self._operator, self._format(right)) def __invert__(self): return _INVERT[self.__class__](self.left, self.right, self.escape) class NotLike(Like): __slots__ = () _operator = 'NOT LIKE' class ILike(Like): __slots__ = () @property def _operator(self): if Flavor.get().ilike: return 'ILIKE' else: return 'LIKE' @property def _operands(self): operands = super(ILike, self)._operands if not Flavor.get().ilike: from .functions import Upper operands = tuple(Upper(o) for o in operands) return operands class NotILike(ILike): __slots__ = () @property def _operator(self): if Flavor.get().ilike: return 'NOT ILIKE' else: return 'NOT LIKE' # TODO SIMILAR class In(BinaryOperator): __slots__ = () _operator = 'IN' class NotIn(BinaryOperator): __slots__ = () _operator = 'NOT IN' class Exists(UnaryOperator): __slots__ = () _operator = 'EXISTS' class Any(UnaryOperator): __slots__ = () _operator = 'ANY' Some = Any class All(UnaryOperator): __slots__ = () _operator = 'ALL' _INVERT = { Less: GreaterEqual, Greater: LessEqual, LessEqual: Greater, GreaterEqual: Less, Equal: NotEqual, NotEqual: Equal, Between: NotBetween, NotBetween: Between, IsDistinct: IsNotDistinct, IsNotDistinct: IsDistinct, Is: IsNot, IsNot: Is, Like: NotLike, NotLike: Like, ILike: NotILike, NotILike: ILike, In: NotIn, NotIn: In, }