default_comparator.py 11 KB

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