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.

353 lines
11KB

  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. """Default implementation of SQL comparison operations.
  8. """
  9. from . import coercions
  10. from . import operators
  11. from . import roles
  12. from . import type_api
  13. from .elements import and_
  14. from .elements import BinaryExpression
  15. from .elements import ClauseList
  16. from .elements import collate
  17. from .elements import CollectionAggregate
  18. from .elements import False_
  19. from .elements import Null
  20. from .elements import or_
  21. from .elements import True_
  22. from .elements import UnaryExpression
  23. from .. import exc
  24. from .. import util
  25. def _boolean_compare(
  26. expr,
  27. op,
  28. obj,
  29. negate=None,
  30. reverse=False,
  31. _python_is_types=(util.NoneType, bool),
  32. result_type=None,
  33. **kwargs
  34. ):
  35. if result_type is None:
  36. result_type = type_api.BOOLEANTYPE
  37. if isinstance(obj, _python_is_types + (Null, True_, False_)):
  38. # allow x ==/!= True/False to be treated as a literal.
  39. # this comes out to "== / != true/false" or "1/0" if those
  40. # constants aren't supported and works on all platforms
  41. if op in (operators.eq, operators.ne) and isinstance(
  42. obj, (bool, True_, False_)
  43. ):
  44. return BinaryExpression(
  45. expr,
  46. coercions.expect(roles.ConstExprRole, obj),
  47. op,
  48. type_=result_type,
  49. negate=negate,
  50. modifiers=kwargs,
  51. )
  52. elif op in (
  53. operators.is_distinct_from,
  54. operators.is_not_distinct_from,
  55. ):
  56. return BinaryExpression(
  57. expr,
  58. coercions.expect(roles.ConstExprRole, obj),
  59. op,
  60. type_=result_type,
  61. negate=negate,
  62. modifiers=kwargs,
  63. )
  64. else:
  65. # all other None/True/False uses IS, IS NOT
  66. if op in (operators.eq, operators.is_):
  67. return BinaryExpression(
  68. expr,
  69. coercions.expect(roles.ConstExprRole, obj),
  70. operators.is_,
  71. negate=operators.is_not,
  72. type_=result_type,
  73. )
  74. elif op in (operators.ne, operators.is_not):
  75. return BinaryExpression(
  76. expr,
  77. coercions.expect(roles.ConstExprRole, obj),
  78. operators.is_not,
  79. negate=operators.is_,
  80. type_=result_type,
  81. )
  82. else:
  83. raise exc.ArgumentError(
  84. "Only '=', '!=', 'is_()', 'is_not()', "
  85. "'is_distinct_from()', 'is_not_distinct_from()' "
  86. "operators can be used with None/True/False"
  87. )
  88. else:
  89. obj = coercions.expect(
  90. roles.BinaryElementRole, element=obj, operator=op, expr=expr
  91. )
  92. if reverse:
  93. return BinaryExpression(
  94. obj, expr, op, type_=result_type, negate=negate, modifiers=kwargs
  95. )
  96. else:
  97. return BinaryExpression(
  98. expr, obj, op, type_=result_type, negate=negate, modifiers=kwargs
  99. )
  100. def _custom_op_operate(expr, op, obj, reverse=False, result_type=None, **kw):
  101. if result_type is None:
  102. if op.return_type:
  103. result_type = op.return_type
  104. elif op.is_comparison:
  105. result_type = type_api.BOOLEANTYPE
  106. return _binary_operate(
  107. expr, op, obj, reverse=reverse, result_type=result_type, **kw
  108. )
  109. def _binary_operate(expr, op, obj, reverse=False, result_type=None, **kw):
  110. obj = coercions.expect(
  111. roles.BinaryElementRole, obj, expr=expr, operator=op
  112. )
  113. if reverse:
  114. left, right = obj, expr
  115. else:
  116. left, right = expr, obj
  117. if result_type is None:
  118. op, result_type = left.comparator._adapt_expression(
  119. op, right.comparator
  120. )
  121. return BinaryExpression(left, right, op, type_=result_type, modifiers=kw)
  122. def _conjunction_operate(expr, op, other, **kw):
  123. if op is operators.and_:
  124. return and_(expr, other)
  125. elif op is operators.or_:
  126. return or_(expr, other)
  127. else:
  128. raise NotImplementedError()
  129. def _scalar(expr, op, fn, **kw):
  130. return fn(expr)
  131. def _in_impl(expr, op, seq_or_selectable, negate_op, **kw):
  132. seq_or_selectable = coercions.expect(
  133. roles.InElementRole, seq_or_selectable, expr=expr, operator=op
  134. )
  135. if "in_ops" in seq_or_selectable._annotations:
  136. op, negate_op = seq_or_selectable._annotations["in_ops"]
  137. return _boolean_compare(
  138. expr, op, seq_or_selectable, negate=negate_op, **kw
  139. )
  140. def _getitem_impl(expr, op, other, **kw):
  141. if isinstance(expr.type, type_api.INDEXABLE):
  142. other = coercions.expect(
  143. roles.BinaryElementRole, other, expr=expr, operator=op
  144. )
  145. return _binary_operate(expr, op, other, **kw)
  146. else:
  147. _unsupported_impl(expr, op, other, **kw)
  148. def _unsupported_impl(expr, op, *arg, **kw):
  149. raise NotImplementedError(
  150. "Operator '%s' is not supported on " "this expression" % op.__name__
  151. )
  152. def _inv_impl(expr, op, **kw):
  153. """See :meth:`.ColumnOperators.__inv__`."""
  154. # undocumented element currently used by the ORM for
  155. # relationship.contains()
  156. if hasattr(expr, "negation_clause"):
  157. return expr.negation_clause
  158. else:
  159. return expr._negate()
  160. def _neg_impl(expr, op, **kw):
  161. """See :meth:`.ColumnOperators.__neg__`."""
  162. return UnaryExpression(expr, operator=operators.neg, type_=expr.type)
  163. def _match_impl(expr, op, other, **kw):
  164. """See :meth:`.ColumnOperators.match`."""
  165. return _boolean_compare(
  166. expr,
  167. operators.match_op,
  168. coercions.expect(
  169. roles.BinaryElementRole,
  170. other,
  171. expr=expr,
  172. operator=operators.match_op,
  173. ),
  174. result_type=type_api.MATCHTYPE,
  175. negate=operators.not_match_op
  176. if op is operators.match_op
  177. else operators.match_op,
  178. **kw
  179. )
  180. def _distinct_impl(expr, op, **kw):
  181. """See :meth:`.ColumnOperators.distinct`."""
  182. return UnaryExpression(
  183. expr, operator=operators.distinct_op, type_=expr.type
  184. )
  185. def _between_impl(expr, op, cleft, cright, **kw):
  186. """See :meth:`.ColumnOperators.between`."""
  187. return BinaryExpression(
  188. expr,
  189. ClauseList(
  190. coercions.expect(
  191. roles.BinaryElementRole,
  192. cleft,
  193. expr=expr,
  194. operator=operators.and_,
  195. ),
  196. coercions.expect(
  197. roles.BinaryElementRole,
  198. cright,
  199. expr=expr,
  200. operator=operators.and_,
  201. ),
  202. operator=operators.and_,
  203. group=False,
  204. group_contents=False,
  205. ),
  206. op,
  207. negate=operators.not_between_op
  208. if op is operators.between_op
  209. else operators.between_op,
  210. modifiers=kw,
  211. )
  212. def _collate_impl(expr, op, other, **kw):
  213. return collate(expr, other)
  214. def _regexp_match_impl(expr, op, pattern, flags, **kw):
  215. if flags is not None:
  216. flags = coercions.expect(
  217. roles.BinaryElementRole,
  218. flags,
  219. expr=expr,
  220. operator=operators.regexp_replace_op,
  221. )
  222. return _boolean_compare(
  223. expr,
  224. op,
  225. pattern,
  226. flags=flags,
  227. negate=operators.not_regexp_match_op
  228. if op is operators.regexp_match_op
  229. else operators.regexp_match_op,
  230. **kw
  231. )
  232. def _regexp_replace_impl(expr, op, pattern, replacement, flags, **kw):
  233. replacement = coercions.expect(
  234. roles.BinaryElementRole,
  235. replacement,
  236. expr=expr,
  237. operator=operators.regexp_replace_op,
  238. )
  239. if flags is not None:
  240. flags = coercions.expect(
  241. roles.BinaryElementRole,
  242. flags,
  243. expr=expr,
  244. operator=operators.regexp_replace_op,
  245. )
  246. return _binary_operate(
  247. expr, op, pattern, replacement=replacement, flags=flags, **kw
  248. )
  249. # a mapping of operators with the method they use, along with
  250. # their negated operator for comparison operators
  251. operator_lookup = {
  252. "and_": (_conjunction_operate,),
  253. "or_": (_conjunction_operate,),
  254. "inv": (_inv_impl,),
  255. "add": (_binary_operate,),
  256. "mul": (_binary_operate,),
  257. "sub": (_binary_operate,),
  258. "div": (_binary_operate,),
  259. "mod": (_binary_operate,),
  260. "truediv": (_binary_operate,),
  261. "custom_op": (_custom_op_operate,),
  262. "json_path_getitem_op": (_binary_operate,),
  263. "json_getitem_op": (_binary_operate,),
  264. "concat_op": (_binary_operate,),
  265. "any_op": (_scalar, CollectionAggregate._create_any),
  266. "all_op": (_scalar, CollectionAggregate._create_all),
  267. "lt": (_boolean_compare, operators.ge),
  268. "le": (_boolean_compare, operators.gt),
  269. "ne": (_boolean_compare, operators.eq),
  270. "gt": (_boolean_compare, operators.le),
  271. "ge": (_boolean_compare, operators.lt),
  272. "eq": (_boolean_compare, operators.ne),
  273. "is_distinct_from": (_boolean_compare, operators.is_not_distinct_from),
  274. "is_not_distinct_from": (_boolean_compare, operators.is_distinct_from),
  275. "like_op": (_boolean_compare, operators.not_like_op),
  276. "ilike_op": (_boolean_compare, operators.not_ilike_op),
  277. "not_like_op": (_boolean_compare, operators.like_op),
  278. "not_ilike_op": (_boolean_compare, operators.ilike_op),
  279. "contains_op": (_boolean_compare, operators.not_contains_op),
  280. "startswith_op": (_boolean_compare, operators.not_startswith_op),
  281. "endswith_op": (_boolean_compare, operators.not_endswith_op),
  282. "desc_op": (_scalar, UnaryExpression._create_desc),
  283. "asc_op": (_scalar, UnaryExpression._create_asc),
  284. "nulls_first_op": (_scalar, UnaryExpression._create_nulls_first),
  285. "nulls_last_op": (_scalar, UnaryExpression._create_nulls_last),
  286. "in_op": (_in_impl, operators.not_in_op),
  287. "not_in_op": (_in_impl, operators.in_op),
  288. "is_": (_boolean_compare, operators.is_),
  289. "is_not": (_boolean_compare, operators.is_not),
  290. "collate": (_collate_impl,),
  291. "match_op": (_match_impl,),
  292. "not_match_op": (_match_impl,),
  293. "distinct_op": (_distinct_impl,),
  294. "between_op": (_between_impl,),
  295. "not_between_op": (_between_impl,),
  296. "neg": (_neg_impl,),
  297. "getitem": (_getitem_impl,),
  298. "lshift": (_unsupported_impl,),
  299. "rshift": (_unsupported_impl,),
  300. "contains": (_unsupported_impl,),
  301. "regexp_match_op": (_regexp_match_impl,),
  302. "not_regexp_match_op": (_regexp_match_impl,),
  303. "regexp_replace_op": (_regexp_replace_impl,),
  304. }