ranges.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. # dialects/postgresql/ranges.py
  2. # Copyright (C) 2013-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. from __future__ import annotations
  8. import dataclasses
  9. from datetime import date
  10. from datetime import datetime
  11. from datetime import timedelta
  12. from decimal import Decimal
  13. from typing import Any
  14. from typing import cast
  15. from typing import Generic
  16. from typing import Optional
  17. from typing import overload
  18. from typing import Tuple
  19. from typing import Type
  20. from typing import TYPE_CHECKING
  21. from typing import TypeVar
  22. from typing import Union
  23. from .operators import ADJACENT_TO
  24. from .operators import CONTAINED_BY
  25. from .operators import CONTAINS
  26. from .operators import NOT_EXTEND_LEFT_OF
  27. from .operators import NOT_EXTEND_RIGHT_OF
  28. from .operators import OVERLAP
  29. from .operators import STRICTLY_LEFT_OF
  30. from .operators import STRICTLY_RIGHT_OF
  31. from ... import types as sqltypes
  32. from ...sql import operators
  33. from ...sql.type_api import TypeEngine
  34. from ...util import py310
  35. from ...util.typing import Literal
  36. if TYPE_CHECKING:
  37. from ...sql.elements import ColumnElement
  38. from ...sql.type_api import _TE
  39. from ...sql.type_api import TypeEngineMixin
  40. _T = TypeVar("_T", bound=Any)
  41. _BoundsType = Literal["()", "[)", "(]", "[]"]
  42. if py310:
  43. dc_slots = {"slots": True}
  44. dc_kwonly = {"kw_only": True}
  45. else:
  46. dc_slots = {}
  47. dc_kwonly = {}
  48. @dataclasses.dataclass(frozen=True, **dc_slots)
  49. class Range(Generic[_T]):
  50. """Represent a PostgreSQL range.
  51. E.g.::
  52. r = Range(10, 50, bounds="()")
  53. The calling style is similar to that of psycopg and psycopg2, in part
  54. to allow easier migration from previous SQLAlchemy versions that used
  55. these objects directly.
  56. :param lower: Lower bound value, or None
  57. :param upper: Upper bound value, or None
  58. :param bounds: keyword-only, optional string value that is one of
  59. ``"()"``, ``"[)"``, ``"(]"``, ``"[]"``. Defaults to ``"[)"``.
  60. :param empty: keyword-only, optional bool indicating this is an "empty"
  61. range
  62. .. versionadded:: 2.0
  63. """
  64. lower: Optional[_T] = None
  65. """the lower bound"""
  66. upper: Optional[_T] = None
  67. """the upper bound"""
  68. if TYPE_CHECKING:
  69. bounds: _BoundsType = dataclasses.field(default="[)")
  70. empty: bool = dataclasses.field(default=False)
  71. else:
  72. bounds: _BoundsType = dataclasses.field(default="[)", **dc_kwonly)
  73. empty: bool = dataclasses.field(default=False, **dc_kwonly)
  74. if not py310:
  75. def __init__(
  76. self,
  77. lower: Optional[_T] = None,
  78. upper: Optional[_T] = None,
  79. *,
  80. bounds: _BoundsType = "[)",
  81. empty: bool = False,
  82. ):
  83. # no __slots__ either so we can update dict
  84. self.__dict__.update(
  85. {
  86. "lower": lower,
  87. "upper": upper,
  88. "bounds": bounds,
  89. "empty": empty,
  90. }
  91. )
  92. def __bool__(self) -> bool:
  93. return not self.empty
  94. @property
  95. def isempty(self) -> bool:
  96. "A synonym for the 'empty' attribute."
  97. return self.empty
  98. @property
  99. def is_empty(self) -> bool:
  100. "A synonym for the 'empty' attribute."
  101. return self.empty
  102. @property
  103. def lower_inc(self) -> bool:
  104. """Return True if the lower bound is inclusive."""
  105. return self.bounds[0] == "["
  106. @property
  107. def lower_inf(self) -> bool:
  108. """Return True if this range is non-empty and lower bound is
  109. infinite."""
  110. return not self.empty and self.lower is None
  111. @property
  112. def upper_inc(self) -> bool:
  113. """Return True if the upper bound is inclusive."""
  114. return self.bounds[1] == "]"
  115. @property
  116. def upper_inf(self) -> bool:
  117. """Return True if this range is non-empty and the upper bound is
  118. infinite."""
  119. return not self.empty and self.upper is None
  120. @property
  121. def __sa_type_engine__(self) -> AbstractRange[Range[_T]]:
  122. return AbstractRange()
  123. def _contains_value(self, value: _T) -> bool:
  124. """Return True if this range contains the given value."""
  125. if self.empty:
  126. return False
  127. if self.lower is None:
  128. return self.upper is None or (
  129. value < self.upper
  130. if self.bounds[1] == ")"
  131. else value <= self.upper
  132. )
  133. if self.upper is None:
  134. return ( # type: ignore
  135. value > self.lower
  136. if self.bounds[0] == "("
  137. else value >= self.lower
  138. )
  139. return ( # type: ignore
  140. value > self.lower
  141. if self.bounds[0] == "("
  142. else value >= self.lower
  143. ) and (
  144. value < self.upper
  145. if self.bounds[1] == ")"
  146. else value <= self.upper
  147. )
  148. def _get_discrete_step(self) -> Any:
  149. "Determine the “step” for this range, if it is a discrete one."
  150. # See
  151. # https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-DISCRETE
  152. # for the rationale
  153. if isinstance(self.lower, int) or isinstance(self.upper, int):
  154. return 1
  155. elif isinstance(self.lower, datetime) or isinstance(
  156. self.upper, datetime
  157. ):
  158. # This is required, because a `isinstance(datetime.now(), date)`
  159. # is True
  160. return None
  161. elif isinstance(self.lower, date) or isinstance(self.upper, date):
  162. return timedelta(days=1)
  163. else:
  164. return None
  165. def _compare_edges(
  166. self,
  167. value1: Optional[_T],
  168. bound1: str,
  169. value2: Optional[_T],
  170. bound2: str,
  171. only_values: bool = False,
  172. ) -> int:
  173. """Compare two range bounds.
  174. Return -1, 0 or 1 respectively when `value1` is less than,
  175. equal to or greater than `value2`.
  176. When `only_value` is ``True``, do not consider the *inclusivity*
  177. of the edges, just their values.
  178. """
  179. value1_is_lower_bound = bound1 in {"[", "("}
  180. value2_is_lower_bound = bound2 in {"[", "("}
  181. # Infinite edges are equal when they are on the same side,
  182. # otherwise a lower edge is considered less than the upper end
  183. if value1 is value2 is None:
  184. if value1_is_lower_bound == value2_is_lower_bound:
  185. return 0
  186. else:
  187. return -1 if value1_is_lower_bound else 1
  188. elif value1 is None:
  189. return -1 if value1_is_lower_bound else 1
  190. elif value2 is None:
  191. return 1 if value2_is_lower_bound else -1
  192. # Short path for trivial case
  193. if bound1 == bound2 and value1 == value2:
  194. return 0
  195. value1_inc = bound1 in {"[", "]"}
  196. value2_inc = bound2 in {"[", "]"}
  197. step = self._get_discrete_step()
  198. if step is not None:
  199. # "Normalize" the two edges as '[)', to simplify successive
  200. # logic when the range is discrete: otherwise we would need
  201. # to handle the comparison between ``(0`` and ``[1`` that
  202. # are equal when dealing with integers while for floats the
  203. # former is lesser than the latter
  204. if value1_is_lower_bound:
  205. if not value1_inc:
  206. value1 += step
  207. value1_inc = True
  208. else:
  209. if value1_inc:
  210. value1 += step
  211. value1_inc = False
  212. if value2_is_lower_bound:
  213. if not value2_inc:
  214. value2 += step
  215. value2_inc = True
  216. else:
  217. if value2_inc:
  218. value2 += step
  219. value2_inc = False
  220. if value1 < value2: # type: ignore
  221. return -1
  222. elif value1 > value2: # type: ignore
  223. return 1
  224. elif only_values:
  225. return 0
  226. else:
  227. # Neither one is infinite but are equal, so we
  228. # need to consider the respective inclusive/exclusive
  229. # flag
  230. if value1_inc and value2_inc:
  231. return 0
  232. elif not value1_inc and not value2_inc:
  233. if value1_is_lower_bound == value2_is_lower_bound:
  234. return 0
  235. else:
  236. return 1 if value1_is_lower_bound else -1
  237. elif not value1_inc:
  238. return 1 if value1_is_lower_bound else -1
  239. elif not value2_inc:
  240. return -1 if value2_is_lower_bound else 1
  241. else:
  242. return 0
  243. def __eq__(self, other: Any) -> bool:
  244. """Compare this range to the `other` taking into account
  245. bounds inclusivity, returning ``True`` if they are equal.
  246. """
  247. if not isinstance(other, Range):
  248. return NotImplemented
  249. if self.empty and other.empty:
  250. return True
  251. elif self.empty != other.empty:
  252. return False
  253. slower = self.lower
  254. slower_b = self.bounds[0]
  255. olower = other.lower
  256. olower_b = other.bounds[0]
  257. supper = self.upper
  258. supper_b = self.bounds[1]
  259. oupper = other.upper
  260. oupper_b = other.bounds[1]
  261. return (
  262. self._compare_edges(slower, slower_b, olower, olower_b) == 0
  263. and self._compare_edges(supper, supper_b, oupper, oupper_b) == 0
  264. )
  265. def contained_by(self, other: Range[_T]) -> bool:
  266. "Determine whether this range is a contained by `other`."
  267. # Any range contains the empty one
  268. if self.empty:
  269. return True
  270. # An empty range does not contain any range except the empty one
  271. if other.empty:
  272. return False
  273. slower = self.lower
  274. slower_b = self.bounds[0]
  275. olower = other.lower
  276. olower_b = other.bounds[0]
  277. if self._compare_edges(slower, slower_b, olower, olower_b) < 0:
  278. return False
  279. supper = self.upper
  280. supper_b = self.bounds[1]
  281. oupper = other.upper
  282. oupper_b = other.bounds[1]
  283. if self._compare_edges(supper, supper_b, oupper, oupper_b) > 0:
  284. return False
  285. return True
  286. def contains(self, value: Union[_T, Range[_T]]) -> bool:
  287. "Determine whether this range contains `value`."
  288. if isinstance(value, Range):
  289. return value.contained_by(self)
  290. else:
  291. return self._contains_value(value)
  292. def overlaps(self, other: Range[_T]) -> bool:
  293. "Determine whether this range overlaps with `other`."
  294. # Empty ranges never overlap with any other range
  295. if self.empty or other.empty:
  296. return False
  297. slower = self.lower
  298. slower_b = self.bounds[0]
  299. supper = self.upper
  300. supper_b = self.bounds[1]
  301. olower = other.lower
  302. olower_b = other.bounds[0]
  303. oupper = other.upper
  304. oupper_b = other.bounds[1]
  305. # Check whether this lower bound is contained in the other range
  306. if (
  307. self._compare_edges(slower, slower_b, olower, olower_b) >= 0
  308. and self._compare_edges(slower, slower_b, oupper, oupper_b) <= 0
  309. ):
  310. return True
  311. # Check whether other lower bound is contained in this range
  312. if (
  313. self._compare_edges(olower, olower_b, slower, slower_b) >= 0
  314. and self._compare_edges(olower, olower_b, supper, supper_b) <= 0
  315. ):
  316. return True
  317. return False
  318. def strictly_left_of(self, other: Range[_T]) -> bool:
  319. "Determine whether this range is completely to the left of `other`."
  320. # Empty ranges are neither to left nor to the right of any other range
  321. if self.empty or other.empty:
  322. return False
  323. supper = self.upper
  324. supper_b = self.bounds[1]
  325. olower = other.lower
  326. olower_b = other.bounds[0]
  327. # Check whether this upper edge is less than other's lower end
  328. return self._compare_edges(supper, supper_b, olower, olower_b) < 0
  329. __lshift__ = strictly_left_of
  330. def strictly_right_of(self, other: Range[_T]) -> bool:
  331. "Determine whether this range is completely to the right of `other`."
  332. # Empty ranges are neither to left nor to the right of any other range
  333. if self.empty or other.empty:
  334. return False
  335. slower = self.lower
  336. slower_b = self.bounds[0]
  337. oupper = other.upper
  338. oupper_b = other.bounds[1]
  339. # Check whether this lower edge is greater than other's upper end
  340. return self._compare_edges(slower, slower_b, oupper, oupper_b) > 0
  341. __rshift__ = strictly_right_of
  342. def not_extend_left_of(self, other: Range[_T]) -> bool:
  343. "Determine whether this does not extend to the left of `other`."
  344. # Empty ranges are neither to left nor to the right of any other range
  345. if self.empty or other.empty:
  346. return False
  347. slower = self.lower
  348. slower_b = self.bounds[0]
  349. olower = other.lower
  350. olower_b = other.bounds[0]
  351. # Check whether this lower edge is not less than other's lower end
  352. return self._compare_edges(slower, slower_b, olower, olower_b) >= 0
  353. def not_extend_right_of(self, other: Range[_T]) -> bool:
  354. "Determine whether this does not extend to the right of `other`."
  355. # Empty ranges are neither to left nor to the right of any other range
  356. if self.empty or other.empty:
  357. return False
  358. supper = self.upper
  359. supper_b = self.bounds[1]
  360. oupper = other.upper
  361. oupper_b = other.bounds[1]
  362. # Check whether this upper edge is not greater than other's upper end
  363. return self._compare_edges(supper, supper_b, oupper, oupper_b) <= 0
  364. def _upper_edge_adjacent_to_lower(
  365. self,
  366. value1: Optional[_T],
  367. bound1: str,
  368. value2: Optional[_T],
  369. bound2: str,
  370. ) -> bool:
  371. """Determine whether an upper bound is immediately successive to a
  372. lower bound."""
  373. # Since we need a peculiar way to handle the bounds inclusivity,
  374. # just do a comparison by value here
  375. res = self._compare_edges(value1, bound1, value2, bound2, True)
  376. if res == -1:
  377. step = self._get_discrete_step()
  378. if step is None:
  379. return False
  380. if bound1 == "]":
  381. if bound2 == "[":
  382. return value1 == value2 - step # type: ignore
  383. else:
  384. return value1 == value2
  385. else:
  386. if bound2 == "[":
  387. return value1 == value2
  388. else:
  389. return value1 == value2 - step # type: ignore
  390. elif res == 0:
  391. # Cover cases like [0,0] -|- [1,] and [0,2) -|- (1,3]
  392. if (
  393. bound1 == "]"
  394. and bound2 == "["
  395. or bound1 == ")"
  396. and bound2 == "("
  397. ):
  398. step = self._get_discrete_step()
  399. if step is not None:
  400. return True
  401. return (
  402. bound1 == ")"
  403. and bound2 == "["
  404. or bound1 == "]"
  405. and bound2 == "("
  406. )
  407. else:
  408. return False
  409. def adjacent_to(self, other: Range[_T]) -> bool:
  410. "Determine whether this range is adjacent to the `other`."
  411. # Empty ranges are not adjacent to any other range
  412. if self.empty or other.empty:
  413. return False
  414. slower = self.lower
  415. slower_b = self.bounds[0]
  416. supper = self.upper
  417. supper_b = self.bounds[1]
  418. olower = other.lower
  419. olower_b = other.bounds[0]
  420. oupper = other.upper
  421. oupper_b = other.bounds[1]
  422. return self._upper_edge_adjacent_to_lower(
  423. supper, supper_b, olower, olower_b
  424. ) or self._upper_edge_adjacent_to_lower(
  425. oupper, oupper_b, slower, slower_b
  426. )
  427. def union(self, other: Range[_T]) -> Range[_T]:
  428. """Compute the union of this range with the `other`.
  429. This raises a ``ValueError`` exception if the two ranges are
  430. "disjunct", that is neither adjacent nor overlapping.
  431. """
  432. # Empty ranges are "additive identities"
  433. if self.empty:
  434. return other
  435. if other.empty:
  436. return self
  437. if not self.overlaps(other) and not self.adjacent_to(other):
  438. raise ValueError(
  439. "Adding non-overlapping and non-adjacent"
  440. " ranges is not implemented"
  441. )
  442. slower = self.lower
  443. slower_b = self.bounds[0]
  444. supper = self.upper
  445. supper_b = self.bounds[1]
  446. olower = other.lower
  447. olower_b = other.bounds[0]
  448. oupper = other.upper
  449. oupper_b = other.bounds[1]
  450. if self._compare_edges(slower, slower_b, olower, olower_b) < 0:
  451. rlower = slower
  452. rlower_b = slower_b
  453. else:
  454. rlower = olower
  455. rlower_b = olower_b
  456. if self._compare_edges(supper, supper_b, oupper, oupper_b) > 0:
  457. rupper = supper
  458. rupper_b = supper_b
  459. else:
  460. rupper = oupper
  461. rupper_b = oupper_b
  462. return Range(
  463. rlower, rupper, bounds=cast(_BoundsType, rlower_b + rupper_b)
  464. )
  465. def __add__(self, other: Range[_T]) -> Range[_T]:
  466. return self.union(other)
  467. def difference(self, other: Range[_T]) -> Range[_T]:
  468. """Compute the difference between this range and the `other`.
  469. This raises a ``ValueError`` exception if the two ranges are
  470. "disjunct", that is neither adjacent nor overlapping.
  471. """
  472. # Subtracting an empty range is a no-op
  473. if self.empty or other.empty:
  474. return self
  475. slower = self.lower
  476. slower_b = self.bounds[0]
  477. supper = self.upper
  478. supper_b = self.bounds[1]
  479. olower = other.lower
  480. olower_b = other.bounds[0]
  481. oupper = other.upper
  482. oupper_b = other.bounds[1]
  483. sl_vs_ol = self._compare_edges(slower, slower_b, olower, olower_b)
  484. su_vs_ou = self._compare_edges(supper, supper_b, oupper, oupper_b)
  485. if sl_vs_ol < 0 and su_vs_ou > 0:
  486. raise ValueError(
  487. "Subtracting a strictly inner range is not implemented"
  488. )
  489. sl_vs_ou = self._compare_edges(slower, slower_b, oupper, oupper_b)
  490. su_vs_ol = self._compare_edges(supper, supper_b, olower, olower_b)
  491. # If the ranges do not overlap, result is simply the first
  492. if sl_vs_ou > 0 or su_vs_ol < 0:
  493. return self
  494. # If this range is completely contained by the other, result is empty
  495. if sl_vs_ol >= 0 and su_vs_ou <= 0:
  496. return Range(None, None, empty=True)
  497. # If this range extends to the left of the other and ends in its
  498. # middle
  499. if sl_vs_ol <= 0 and su_vs_ol >= 0 and su_vs_ou <= 0:
  500. rupper_b = ")" if olower_b == "[" else "]"
  501. if (
  502. slower_b != "["
  503. and rupper_b != "]"
  504. and self._compare_edges(slower, slower_b, olower, rupper_b)
  505. == 0
  506. ):
  507. return Range(None, None, empty=True)
  508. else:
  509. return Range(
  510. slower,
  511. olower,
  512. bounds=cast(_BoundsType, slower_b + rupper_b),
  513. )
  514. # If this range starts in the middle of the other and extends to its
  515. # right
  516. if sl_vs_ol >= 0 and su_vs_ou >= 0 and sl_vs_ou <= 0:
  517. rlower_b = "(" if oupper_b == "]" else "["
  518. if (
  519. rlower_b != "["
  520. and supper_b != "]"
  521. and self._compare_edges(oupper, rlower_b, supper, supper_b)
  522. == 0
  523. ):
  524. return Range(None, None, empty=True)
  525. else:
  526. return Range(
  527. oupper,
  528. supper,
  529. bounds=cast(_BoundsType, rlower_b + supper_b),
  530. )
  531. assert False, f"Unhandled case computing {self} - {other}"
  532. def __sub__(self, other: Range[_T]) -> Range[_T]:
  533. return self.difference(other)
  534. def intersection(self, other: Range[_T]) -> Range[_T]:
  535. """Compute the intersection of this range with the `other`.
  536. .. versionadded:: 2.0.10
  537. """
  538. if self.empty or other.empty or not self.overlaps(other):
  539. return Range(None, None, empty=True)
  540. slower = self.lower
  541. slower_b = self.bounds[0]
  542. supper = self.upper
  543. supper_b = self.bounds[1]
  544. olower = other.lower
  545. olower_b = other.bounds[0]
  546. oupper = other.upper
  547. oupper_b = other.bounds[1]
  548. if self._compare_edges(slower, slower_b, olower, olower_b) < 0:
  549. rlower = olower
  550. rlower_b = olower_b
  551. else:
  552. rlower = slower
  553. rlower_b = slower_b
  554. if self._compare_edges(supper, supper_b, oupper, oupper_b) > 0:
  555. rupper = oupper
  556. rupper_b = oupper_b
  557. else:
  558. rupper = supper
  559. rupper_b = supper_b
  560. return Range(
  561. rlower,
  562. rupper,
  563. bounds=cast(_BoundsType, rlower_b + rupper_b),
  564. )
  565. def __mul__(self, other: Range[_T]) -> Range[_T]:
  566. return self.intersection(other)
  567. def __str__(self) -> str:
  568. return self._stringify()
  569. def _stringify(self) -> str:
  570. if self.empty:
  571. return "empty"
  572. l, r = self.lower, self.upper
  573. l = "" if l is None else l # type: ignore
  574. r = "" if r is None else r # type: ignore
  575. b0, b1 = cast("Tuple[str, str]", self.bounds)
  576. return f"{b0}{l},{r}{b1}"
  577. class AbstractRange(sqltypes.TypeEngine[Range[_T]]):
  578. """
  579. Base for PostgreSQL RANGE types.
  580. .. seealso::
  581. `PostgreSQL range functions <https://www.postgresql.org/docs/current/static/functions-range.html>`_
  582. """ # noqa: E501
  583. render_bind_cast = True
  584. __abstract__ = True
  585. @overload
  586. def adapt(self, cls: Type[_TE], **kw: Any) -> _TE:
  587. ...
  588. @overload
  589. def adapt(self, cls: Type[TypeEngineMixin], **kw: Any) -> TypeEngine[Any]:
  590. ...
  591. def adapt(
  592. self,
  593. cls: Type[Union[TypeEngine[Any], TypeEngineMixin]],
  594. **kw: Any,
  595. ) -> TypeEngine[Any]:
  596. """Dynamically adapt a range type to an abstract impl.
  597. For example ``INT4RANGE().adapt(_Psycopg2NumericRange)`` should
  598. produce a type that will have ``_Psycopg2NumericRange`` behaviors
  599. and also render as ``INT4RANGE`` in SQL and DDL.
  600. """
  601. if issubclass(cls, AbstractRangeImpl) and cls is not self.__class__:
  602. # two ways to do this are: 1. create a new type on the fly
  603. # or 2. have AbstractRangeImpl(visit_name) constructor and a
  604. # visit_abstract_range_impl() method in the PG compiler.
  605. # I'm choosing #1 as the resulting type object
  606. # will then make use of the same mechanics
  607. # as if we had made all these sub-types explicitly, and will
  608. # also look more obvious under pdb etc.
  609. # The adapt() operation here is cached per type-class-per-dialect,
  610. # so is not much of a performance concern
  611. visit_name = self.__visit_name__
  612. return type( # type: ignore
  613. f"{visit_name}RangeImpl",
  614. (cls, self.__class__),
  615. {"__visit_name__": visit_name},
  616. )()
  617. else:
  618. return super().adapt(cls)
  619. def _resolve_for_literal(self, value: Any) -> Any:
  620. spec = value.lower if value.lower is not None else value.upper
  621. if isinstance(spec, int):
  622. return INT8RANGE()
  623. elif isinstance(spec, (Decimal, float)):
  624. return NUMRANGE()
  625. elif isinstance(spec, datetime):
  626. return TSRANGE() if not spec.tzinfo else TSTZRANGE()
  627. elif isinstance(spec, date):
  628. return DATERANGE()
  629. else:
  630. # empty Range, SQL datatype can't be determined here
  631. return sqltypes.NULLTYPE
  632. class comparator_factory(TypeEngine.Comparator[Range[Any]]):
  633. """Define comparison operations for range types."""
  634. def contains(self, other: Any, **kw: Any) -> ColumnElement[bool]:
  635. """Boolean expression. Returns true if the right hand operand,
  636. which can be an element or a range, is contained within the
  637. column.
  638. kwargs may be ignored by this operator but are required for API
  639. conformance.
  640. """
  641. return self.expr.operate(CONTAINS, other)
  642. def contained_by(self, other: Any) -> ColumnElement[bool]:
  643. """Boolean expression. Returns true if the column is contained
  644. within the right hand operand.
  645. """
  646. return self.expr.operate(CONTAINED_BY, other)
  647. def overlaps(self, other: Any) -> ColumnElement[bool]:
  648. """Boolean expression. Returns true if the column overlaps
  649. (has points in common with) the right hand operand.
  650. """
  651. return self.expr.operate(OVERLAP, other)
  652. def strictly_left_of(self, other: Any) -> ColumnElement[bool]:
  653. """Boolean expression. Returns true if the column is strictly
  654. left of the right hand operand.
  655. """
  656. return self.expr.operate(STRICTLY_LEFT_OF, other)
  657. __lshift__ = strictly_left_of
  658. def strictly_right_of(self, other: Any) -> ColumnElement[bool]:
  659. """Boolean expression. Returns true if the column is strictly
  660. right of the right hand operand.
  661. """
  662. return self.expr.operate(STRICTLY_RIGHT_OF, other)
  663. __rshift__ = strictly_right_of
  664. def not_extend_right_of(self, other: Any) -> ColumnElement[bool]:
  665. """Boolean expression. Returns true if the range in the column
  666. does not extend right of the range in the operand.
  667. """
  668. return self.expr.operate(NOT_EXTEND_RIGHT_OF, other)
  669. def not_extend_left_of(self, other: Any) -> ColumnElement[bool]:
  670. """Boolean expression. Returns true if the range in the column
  671. does not extend left of the range in the operand.
  672. """
  673. return self.expr.operate(NOT_EXTEND_LEFT_OF, other)
  674. def adjacent_to(self, other: Any) -> ColumnElement[bool]:
  675. """Boolean expression. Returns true if the range in the column
  676. is adjacent to the range in the operand.
  677. """
  678. return self.expr.operate(ADJACENT_TO, other)
  679. def union(self, other: Any) -> ColumnElement[bool]:
  680. """Range expression. Returns the union of the two ranges.
  681. Will raise an exception if the resulting range is not
  682. contiguous.
  683. """
  684. return self.expr.operate(operators.add, other)
  685. def difference(self, other: Any) -> ColumnElement[bool]:
  686. """Range expression. Returns the union of the two ranges.
  687. Will raise an exception if the resulting range is not
  688. contiguous.
  689. """
  690. return self.expr.operate(operators.sub, other)
  691. def intersection(self, other: Any) -> ColumnElement[Range[_T]]:
  692. """Range expression. Returns the intersection of the two ranges.
  693. Will raise an exception if the resulting range is not
  694. contiguous.
  695. """
  696. return self.expr.operate(operators.mul, other)
  697. class AbstractRangeImpl(AbstractRange[Range[_T]]):
  698. """Marker for AbstractRange that will apply a subclass-specific
  699. adaptation"""
  700. class AbstractMultiRange(AbstractRange[Range[_T]]):
  701. """base for PostgreSQL MULTIRANGE types"""
  702. __abstract__ = True
  703. class AbstractMultiRangeImpl(
  704. AbstractRangeImpl[Range[_T]], AbstractMultiRange[Range[_T]]
  705. ):
  706. """Marker for AbstractRange that will apply a subclass-specific
  707. adaptation"""
  708. class INT4RANGE(AbstractRange[Range[int]]):
  709. """Represent the PostgreSQL INT4RANGE type."""
  710. __visit_name__ = "INT4RANGE"
  711. class INT8RANGE(AbstractRange[Range[int]]):
  712. """Represent the PostgreSQL INT8RANGE type."""
  713. __visit_name__ = "INT8RANGE"
  714. class NUMRANGE(AbstractRange[Range[Decimal]]):
  715. """Represent the PostgreSQL NUMRANGE type."""
  716. __visit_name__ = "NUMRANGE"
  717. class DATERANGE(AbstractRange[Range[date]]):
  718. """Represent the PostgreSQL DATERANGE type."""
  719. __visit_name__ = "DATERANGE"
  720. class TSRANGE(AbstractRange[Range[datetime]]):
  721. """Represent the PostgreSQL TSRANGE type."""
  722. __visit_name__ = "TSRANGE"
  723. class TSTZRANGE(AbstractRange[Range[datetime]]):
  724. """Represent the PostgreSQL TSTZRANGE type."""
  725. __visit_name__ = "TSTZRANGE"
  726. class INT4MULTIRANGE(AbstractMultiRange[Range[int]]):
  727. """Represent the PostgreSQL INT4MULTIRANGE type."""
  728. __visit_name__ = "INT4MULTIRANGE"
  729. class INT8MULTIRANGE(AbstractMultiRange[Range[int]]):
  730. """Represent the PostgreSQL INT8MULTIRANGE type."""
  731. __visit_name__ = "INT8MULTIRANGE"
  732. class NUMMULTIRANGE(AbstractMultiRange[Range[Decimal]]):
  733. """Represent the PostgreSQL NUMMULTIRANGE type."""
  734. __visit_name__ = "NUMMULTIRANGE"
  735. class DATEMULTIRANGE(AbstractMultiRange[Range[date]]):
  736. """Represent the PostgreSQL DATEMULTIRANGE type."""
  737. __visit_name__ = "DATEMULTIRANGE"
  738. class TSMULTIRANGE(AbstractMultiRange[Range[datetime]]):
  739. """Represent the PostgreSQL TSRANGE type."""
  740. __visit_name__ = "TSMULTIRANGE"
  741. class TSTZMULTIRANGE(AbstractMultiRange[Range[datetime]]):
  742. """Represent the PostgreSQL TSTZRANGE type."""
  743. __visit_name__ = "TSTZMULTIRANGE"