default_comparator.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. # sql/default_comparator.py
  2. # Copyright (C) 2005-2024 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 __future__ import annotations
  10. import typing
  11. from typing import Any
  12. from typing import Callable
  13. from typing import Dict
  14. from typing import NoReturn
  15. from typing import Optional
  16. from typing import Tuple
  17. from typing import Type
  18. from typing import Union
  19. from . import coercions
  20. from . import operators
  21. from . import roles
  22. from . import type_api
  23. from .elements import and_
  24. from .elements import BinaryExpression
  25. from .elements import ClauseElement
  26. from .elements import CollationClause
  27. from .elements import CollectionAggregate
  28. from .elements import ExpressionClauseList
  29. from .elements import False_
  30. from .elements import Null
  31. from .elements import OperatorExpression
  32. from .elements import or_
  33. from .elements import True_
  34. from .elements import UnaryExpression
  35. from .operators import OperatorType
  36. from .. import exc
  37. from .. import util
  38. _T = typing.TypeVar("_T", bound=Any)
  39. if typing.TYPE_CHECKING:
  40. from .elements import ColumnElement
  41. from .operators import custom_op
  42. from .type_api import TypeEngine
  43. def _boolean_compare(
  44. expr: ColumnElement[Any],
  45. op: OperatorType,
  46. obj: Any,
  47. *,
  48. negate_op: Optional[OperatorType] = None,
  49. reverse: bool = False,
  50. _python_is_types: Tuple[Type[Any], ...] = (type(None), bool),
  51. result_type: Optional[TypeEngine[bool]] = None,
  52. **kwargs: Any,
  53. ) -> OperatorExpression[bool]:
  54. if result_type is None:
  55. result_type = type_api.BOOLEANTYPE
  56. if isinstance(obj, _python_is_types + (Null, True_, False_)):
  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 OperatorExpression._construct_for_op(
  64. expr,
  65. coercions.expect(roles.ConstExprRole, obj),
  66. op,
  67. type_=result_type,
  68. negate=negate_op,
  69. modifiers=kwargs,
  70. )
  71. elif op in (
  72. operators.is_distinct_from,
  73. operators.is_not_distinct_from,
  74. ):
  75. return OperatorExpression._construct_for_op(
  76. expr,
  77. coercions.expect(roles.ConstExprRole, obj),
  78. op,
  79. type_=result_type,
  80. negate=negate_op,
  81. modifiers=kwargs,
  82. )
  83. elif expr._is_collection_aggregate:
  84. obj = coercions.expect(
  85. roles.ConstExprRole, element=obj, operator=op, expr=expr
  86. )
  87. else:
  88. # all other None uses IS, IS NOT
  89. if op in (operators.eq, operators.is_):
  90. return OperatorExpression._construct_for_op(
  91. expr,
  92. coercions.expect(roles.ConstExprRole, obj),
  93. operators.is_,
  94. negate=operators.is_not,
  95. type_=result_type,
  96. )
  97. elif op in (operators.ne, operators.is_not):
  98. return OperatorExpression._construct_for_op(
  99. expr,
  100. coercions.expect(roles.ConstExprRole, obj),
  101. operators.is_not,
  102. negate=operators.is_,
  103. type_=result_type,
  104. )
  105. else:
  106. raise exc.ArgumentError(
  107. "Only '=', '!=', 'is_()', 'is_not()', "
  108. "'is_distinct_from()', 'is_not_distinct_from()' "
  109. "operators can be used with None/True/False"
  110. )
  111. else:
  112. obj = coercions.expect(
  113. roles.BinaryElementRole, element=obj, operator=op, expr=expr
  114. )
  115. if reverse:
  116. return OperatorExpression._construct_for_op(
  117. obj,
  118. expr,
  119. op,
  120. type_=result_type,
  121. negate=negate_op,
  122. modifiers=kwargs,
  123. )
  124. else:
  125. return OperatorExpression._construct_for_op(
  126. expr,
  127. obj,
  128. op,
  129. type_=result_type,
  130. negate=negate_op,
  131. modifiers=kwargs,
  132. )
  133. def _custom_op_operate(
  134. expr: ColumnElement[Any],
  135. op: custom_op[Any],
  136. obj: Any,
  137. reverse: bool = False,
  138. result_type: Optional[TypeEngine[Any]] = None,
  139. **kw: Any,
  140. ) -> ColumnElement[Any]:
  141. if result_type is None:
  142. if op.return_type:
  143. result_type = op.return_type
  144. elif op.is_comparison:
  145. result_type = type_api.BOOLEANTYPE
  146. return _binary_operate(
  147. expr, op, obj, reverse=reverse, result_type=result_type, **kw
  148. )
  149. def _binary_operate(
  150. expr: ColumnElement[Any],
  151. op: OperatorType,
  152. obj: roles.BinaryElementRole[Any],
  153. *,
  154. reverse: bool = False,
  155. result_type: Optional[TypeEngine[_T]] = None,
  156. **kw: Any,
  157. ) -> OperatorExpression[_T]:
  158. coerced_obj = coercions.expect(
  159. roles.BinaryElementRole, obj, expr=expr, operator=op
  160. )
  161. if reverse:
  162. left, right = coerced_obj, expr
  163. else:
  164. left, right = expr, coerced_obj
  165. if result_type is None:
  166. op, result_type = left.comparator._adapt_expression(
  167. op, right.comparator
  168. )
  169. return OperatorExpression._construct_for_op(
  170. left, right, op, type_=result_type, modifiers=kw
  171. )
  172. def _conjunction_operate(
  173. expr: ColumnElement[Any], op: OperatorType, other: Any, **kw: Any
  174. ) -> ColumnElement[Any]:
  175. if op is operators.and_:
  176. return and_(expr, other)
  177. elif op is operators.or_:
  178. return or_(expr, other)
  179. else:
  180. raise NotImplementedError()
  181. def _scalar(
  182. expr: ColumnElement[Any],
  183. op: OperatorType,
  184. fn: Callable[[ColumnElement[Any]], ColumnElement[Any]],
  185. **kw: Any,
  186. ) -> ColumnElement[Any]:
  187. return fn(expr)
  188. def _in_impl(
  189. expr: ColumnElement[Any],
  190. op: OperatorType,
  191. seq_or_selectable: ClauseElement,
  192. negate_op: OperatorType,
  193. **kw: Any,
  194. ) -> ColumnElement[Any]:
  195. seq_or_selectable = coercions.expect(
  196. roles.InElementRole, seq_or_selectable, expr=expr, operator=op
  197. )
  198. if "in_ops" in seq_or_selectable._annotations:
  199. op, negate_op = seq_or_selectable._annotations["in_ops"]
  200. return _boolean_compare(
  201. expr, op, seq_or_selectable, negate_op=negate_op, **kw
  202. )
  203. def _getitem_impl(
  204. expr: ColumnElement[Any], op: OperatorType, other: Any, **kw: Any
  205. ) -> ColumnElement[Any]:
  206. if (
  207. isinstance(expr.type, type_api.INDEXABLE)
  208. or isinstance(expr.type, type_api.TypeDecorator)
  209. and isinstance(expr.type.impl_instance, type_api.INDEXABLE)
  210. ):
  211. other = coercions.expect(
  212. roles.BinaryElementRole, other, expr=expr, operator=op
  213. )
  214. return _binary_operate(expr, op, other, **kw)
  215. else:
  216. _unsupported_impl(expr, op, other, **kw)
  217. def _unsupported_impl(
  218. expr: ColumnElement[Any], op: OperatorType, *arg: Any, **kw: Any
  219. ) -> NoReturn:
  220. raise NotImplementedError(
  221. "Operator '%s' is not supported on " "this expression" % op.__name__
  222. )
  223. def _inv_impl(
  224. expr: ColumnElement[Any], op: OperatorType, **kw: Any
  225. ) -> ColumnElement[Any]:
  226. """See :meth:`.ColumnOperators.__inv__`."""
  227. # undocumented element currently used by the ORM for
  228. # relationship.contains()
  229. if hasattr(expr, "negation_clause"):
  230. return expr.negation_clause
  231. else:
  232. return expr._negate()
  233. def _neg_impl(
  234. expr: ColumnElement[Any], op: OperatorType, **kw: Any
  235. ) -> ColumnElement[Any]:
  236. """See :meth:`.ColumnOperators.__neg__`."""
  237. return UnaryExpression(expr, operator=operators.neg, type_=expr.type)
  238. def _bitwise_not_impl(
  239. expr: ColumnElement[Any], op: OperatorType, **kw: Any
  240. ) -> ColumnElement[Any]:
  241. """See :meth:`.ColumnOperators.bitwise_not`."""
  242. return UnaryExpression(
  243. expr, operator=operators.bitwise_not_op, type_=expr.type
  244. )
  245. def _match_impl(
  246. expr: ColumnElement[Any], op: OperatorType, other: Any, **kw: Any
  247. ) -> ColumnElement[Any]:
  248. """See :meth:`.ColumnOperators.match`."""
  249. return _boolean_compare(
  250. expr,
  251. operators.match_op,
  252. coercions.expect(
  253. roles.BinaryElementRole,
  254. other,
  255. expr=expr,
  256. operator=operators.match_op,
  257. ),
  258. result_type=type_api.MATCHTYPE,
  259. negate_op=operators.not_match_op
  260. if op is operators.match_op
  261. else operators.match_op,
  262. **kw,
  263. )
  264. def _distinct_impl(
  265. expr: ColumnElement[Any], op: OperatorType, **kw: Any
  266. ) -> ColumnElement[Any]:
  267. """See :meth:`.ColumnOperators.distinct`."""
  268. return UnaryExpression(
  269. expr, operator=operators.distinct_op, type_=expr.type
  270. )
  271. def _between_impl(
  272. expr: ColumnElement[Any],
  273. op: OperatorType,
  274. cleft: Any,
  275. cright: Any,
  276. **kw: Any,
  277. ) -> ColumnElement[Any]:
  278. """See :meth:`.ColumnOperators.between`."""
  279. return BinaryExpression(
  280. expr,
  281. ExpressionClauseList._construct_for_list(
  282. operators.and_,
  283. type_api.NULLTYPE,
  284. coercions.expect(
  285. roles.BinaryElementRole,
  286. cleft,
  287. expr=expr,
  288. operator=operators.and_,
  289. ),
  290. coercions.expect(
  291. roles.BinaryElementRole,
  292. cright,
  293. expr=expr,
  294. operator=operators.and_,
  295. ),
  296. group=False,
  297. ),
  298. op,
  299. negate=operators.not_between_op
  300. if op is operators.between_op
  301. else operators.between_op,
  302. modifiers=kw,
  303. )
  304. def _collate_impl(
  305. expr: ColumnElement[str], op: OperatorType, collation: str, **kw: Any
  306. ) -> ColumnElement[str]:
  307. return CollationClause._create_collation_expression(expr, collation)
  308. def _regexp_match_impl(
  309. expr: ColumnElement[str],
  310. op: OperatorType,
  311. pattern: Any,
  312. flags: Optional[str],
  313. **kw: Any,
  314. ) -> ColumnElement[Any]:
  315. return BinaryExpression(
  316. expr,
  317. coercions.expect(
  318. roles.BinaryElementRole,
  319. pattern,
  320. expr=expr,
  321. operator=operators.comma_op,
  322. ),
  323. op,
  324. negate=operators.not_regexp_match_op,
  325. modifiers={"flags": flags},
  326. )
  327. def _regexp_replace_impl(
  328. expr: ColumnElement[Any],
  329. op: OperatorType,
  330. pattern: Any,
  331. replacement: Any,
  332. flags: Optional[str],
  333. **kw: Any,
  334. ) -> ColumnElement[Any]:
  335. return BinaryExpression(
  336. expr,
  337. ExpressionClauseList._construct_for_list(
  338. operators.comma_op,
  339. type_api.NULLTYPE,
  340. coercions.expect(
  341. roles.BinaryElementRole,
  342. pattern,
  343. expr=expr,
  344. operator=operators.comma_op,
  345. ),
  346. coercions.expect(
  347. roles.BinaryElementRole,
  348. replacement,
  349. expr=expr,
  350. operator=operators.comma_op,
  351. ),
  352. group=False,
  353. ),
  354. op,
  355. modifiers={"flags": flags},
  356. )
  357. # a mapping of operators with the method they use, along with
  358. # additional keyword arguments to be passed
  359. operator_lookup: Dict[
  360. str,
  361. Tuple[
  362. Callable[..., ColumnElement[Any]],
  363. util.immutabledict[
  364. str, Union[OperatorType, Callable[..., ColumnElement[Any]]]
  365. ],
  366. ],
  367. ] = {
  368. "and_": (_conjunction_operate, util.EMPTY_DICT),
  369. "or_": (_conjunction_operate, util.EMPTY_DICT),
  370. "inv": (_inv_impl, util.EMPTY_DICT),
  371. "add": (_binary_operate, util.EMPTY_DICT),
  372. "mul": (_binary_operate, util.EMPTY_DICT),
  373. "sub": (_binary_operate, util.EMPTY_DICT),
  374. "div": (_binary_operate, util.EMPTY_DICT),
  375. "mod": (_binary_operate, util.EMPTY_DICT),
  376. "bitwise_xor_op": (_binary_operate, util.EMPTY_DICT),
  377. "bitwise_or_op": (_binary_operate, util.EMPTY_DICT),
  378. "bitwise_and_op": (_binary_operate, util.EMPTY_DICT),
  379. "bitwise_not_op": (_bitwise_not_impl, util.EMPTY_DICT),
  380. "bitwise_lshift_op": (_binary_operate, util.EMPTY_DICT),
  381. "bitwise_rshift_op": (_binary_operate, util.EMPTY_DICT),
  382. "truediv": (_binary_operate, util.EMPTY_DICT),
  383. "floordiv": (_binary_operate, util.EMPTY_DICT),
  384. "custom_op": (_custom_op_operate, util.EMPTY_DICT),
  385. "json_path_getitem_op": (_binary_operate, util.EMPTY_DICT),
  386. "json_getitem_op": (_binary_operate, util.EMPTY_DICT),
  387. "concat_op": (_binary_operate, util.EMPTY_DICT),
  388. "any_op": (
  389. _scalar,
  390. util.immutabledict({"fn": CollectionAggregate._create_any}),
  391. ),
  392. "all_op": (
  393. _scalar,
  394. util.immutabledict({"fn": CollectionAggregate._create_all}),
  395. ),
  396. "lt": (_boolean_compare, util.immutabledict({"negate_op": operators.ge})),
  397. "le": (_boolean_compare, util.immutabledict({"negate_op": operators.gt})),
  398. "ne": (_boolean_compare, util.immutabledict({"negate_op": operators.eq})),
  399. "gt": (_boolean_compare, util.immutabledict({"negate_op": operators.le})),
  400. "ge": (_boolean_compare, util.immutabledict({"negate_op": operators.lt})),
  401. "eq": (_boolean_compare, util.immutabledict({"negate_op": operators.ne})),
  402. "is_distinct_from": (
  403. _boolean_compare,
  404. util.immutabledict({"negate_op": operators.is_not_distinct_from}),
  405. ),
  406. "is_not_distinct_from": (
  407. _boolean_compare,
  408. util.immutabledict({"negate_op": operators.is_distinct_from}),
  409. ),
  410. "like_op": (
  411. _boolean_compare,
  412. util.immutabledict({"negate_op": operators.not_like_op}),
  413. ),
  414. "ilike_op": (
  415. _boolean_compare,
  416. util.immutabledict({"negate_op": operators.not_ilike_op}),
  417. ),
  418. "not_like_op": (
  419. _boolean_compare,
  420. util.immutabledict({"negate_op": operators.like_op}),
  421. ),
  422. "not_ilike_op": (
  423. _boolean_compare,
  424. util.immutabledict({"negate_op": operators.ilike_op}),
  425. ),
  426. "contains_op": (
  427. _boolean_compare,
  428. util.immutabledict({"negate_op": operators.not_contains_op}),
  429. ),
  430. "icontains_op": (
  431. _boolean_compare,
  432. util.immutabledict({"negate_op": operators.not_icontains_op}),
  433. ),
  434. "startswith_op": (
  435. _boolean_compare,
  436. util.immutabledict({"negate_op": operators.not_startswith_op}),
  437. ),
  438. "istartswith_op": (
  439. _boolean_compare,
  440. util.immutabledict({"negate_op": operators.not_istartswith_op}),
  441. ),
  442. "endswith_op": (
  443. _boolean_compare,
  444. util.immutabledict({"negate_op": operators.not_endswith_op}),
  445. ),
  446. "iendswith_op": (
  447. _boolean_compare,
  448. util.immutabledict({"negate_op": operators.not_iendswith_op}),
  449. ),
  450. "desc_op": (
  451. _scalar,
  452. util.immutabledict({"fn": UnaryExpression._create_desc}),
  453. ),
  454. "asc_op": (
  455. _scalar,
  456. util.immutabledict({"fn": UnaryExpression._create_asc}),
  457. ),
  458. "nulls_first_op": (
  459. _scalar,
  460. util.immutabledict({"fn": UnaryExpression._create_nulls_first}),
  461. ),
  462. "nulls_last_op": (
  463. _scalar,
  464. util.immutabledict({"fn": UnaryExpression._create_nulls_last}),
  465. ),
  466. "in_op": (
  467. _in_impl,
  468. util.immutabledict({"negate_op": operators.not_in_op}),
  469. ),
  470. "not_in_op": (
  471. _in_impl,
  472. util.immutabledict({"negate_op": operators.in_op}),
  473. ),
  474. "is_": (
  475. _boolean_compare,
  476. util.immutabledict({"negate_op": operators.is_}),
  477. ),
  478. "is_not": (
  479. _boolean_compare,
  480. util.immutabledict({"negate_op": operators.is_not}),
  481. ),
  482. "collate": (_collate_impl, util.EMPTY_DICT),
  483. "match_op": (_match_impl, util.EMPTY_DICT),
  484. "not_match_op": (_match_impl, util.EMPTY_DICT),
  485. "distinct_op": (_distinct_impl, util.EMPTY_DICT),
  486. "between_op": (_between_impl, util.EMPTY_DICT),
  487. "not_between_op": (_between_impl, util.EMPTY_DICT),
  488. "neg": (_neg_impl, util.EMPTY_DICT),
  489. "getitem": (_getitem_impl, util.EMPTY_DICT),
  490. "lshift": (_unsupported_impl, util.EMPTY_DICT),
  491. "rshift": (_unsupported_impl, util.EMPTY_DICT),
  492. "contains": (_unsupported_impl, util.EMPTY_DICT),
  493. "regexp_match_op": (_regexp_match_impl, util.EMPTY_DICT),
  494. "not_regexp_match_op": (_regexp_match_impl, util.EMPTY_DICT),
  495. "regexp_replace_op": (_regexp_replace_impl, util.EMPTY_DICT),
  496. }