pyoq-sql 1.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. pyoq/__init__.py +10 -0
  2. pyoq/__main__.py +5 -0
  3. pyoq/_native.pyi +5 -0
  4. pyoq/cli/__init__.py +5 -0
  5. pyoq/cli/commands.py +270 -0
  6. pyoq/cli/defaults.py +98 -0
  7. pyoq/cli/services.py +97 -0
  8. pyoq/config/__init__.py +31 -0
  9. pyoq/config/connection.py +161 -0
  10. pyoq/config/loader.py +289 -0
  11. pyoq/config/models.py +245 -0
  12. pyoq/config/values.py +142 -0
  13. pyoq/descriptors.py +165 -0
  14. pyoq/diagnostics/__init__.py +68 -0
  15. pyoq/diagnostics/budget.py +136 -0
  16. pyoq/diagnostics/events.py +137 -0
  17. pyoq/diagnostics/fingerprint.py +267 -0
  18. pyoq/diagnostics/instrumented.py +237 -0
  19. pyoq/diagnostics/metrics.py +61 -0
  20. pyoq/diagnostics/observation.py +227 -0
  21. pyoq/diagnostics/scoped.py +103 -0
  22. pyoq/django/__init__.py +15 -0
  23. pyoq/django/apps.py +17 -0
  24. pyoq/django/execution.py +317 -0
  25. pyoq/django/generation.py +59 -0
  26. pyoq/django/management/__init__.py +0 -0
  27. pyoq/django/management/commands/__init__.py +0 -0
  28. pyoq/django/management/commands/makemigrations.py +53 -0
  29. pyoq/django/management/commands/pyoq_codegen.py +75 -0
  30. pyoq/django/parameters.py +101 -0
  31. pyoq/django/schema.py +379 -0
  32. pyoq/django/settings.py +87 -0
  33. pyoq/django/timeouts.py +105 -0
  34. pyoq/dsl/__init__.py +64 -0
  35. pyoq/dsl/aio/__init__.py +31 -0
  36. pyoq/dsl/aio/context.py +295 -0
  37. pyoq/dsl/aio/queries.py +335 -0
  38. pyoq/dsl/aio/writes.py +368 -0
  39. pyoq/dsl/context.py +326 -0
  40. pyoq/dsl/entry.py +37 -0
  41. pyoq/dsl/labels.py +36 -0
  42. pyoq/dsl/queries.py +339 -0
  43. pyoq/dsl/result.py +164 -0
  44. pyoq/dsl/writes.py +360 -0
  45. pyoq/errors.py +317 -0
  46. pyoq/fastapi/__init__.py +32 -0
  47. pyoq/fastapi/dependencies.py +167 -0
  48. pyoq/fastapi/lifespan.py +119 -0
  49. pyoq/fetching/__init__.py +55 -0
  50. pyoq/fetching/collections.py +136 -0
  51. pyoq/fetching/execution.py +587 -0
  52. pyoq/fetching/joined.py +79 -0
  53. pyoq/fetching/nesting.py +183 -0
  54. pyoq/fetching/plans.py +541 -0
  55. pyoq/fetching/select_in.py +149 -0
  56. pyoq/fetching/tables.py +110 -0
  57. pyoq/generation/__init__.py +54 -0
  58. pyoq/generation/cleanup.py +44 -0
  59. pyoq/generation/contracts.py +248 -0
  60. pyoq/generation/drift.py +169 -0
  61. pyoq/generation/lock.py +33 -0
  62. pyoq/generation/manifest.py +114 -0
  63. pyoq/generation/model.py +1001 -0
  64. pyoq/generation/pipeline.py +119 -0
  65. pyoq/generation/rendering/__init__.py +5 -0
  66. pyoq/generation/rendering/domains.py +51 -0
  67. pyoq/generation/rendering/enums.py +29 -0
  68. pyoq/generation/rendering/exports.py +70 -0
  69. pyoq/generation/rendering/imports.py +63 -0
  70. pyoq/generation/rendering/package.py +56 -0
  71. pyoq/generation/rendering/relations.py +133 -0
  72. pyoq/generation/rendering/routines.py +396 -0
  73. pyoq/generation/rendering/rows.py +79 -0
  74. pyoq/generation/rendering/source.py +121 -0
  75. pyoq/generation/rendering/tables.py +300 -0
  76. pyoq/generation/rendering/writes.py +514 -0
  77. pyoq/generation/validation.py +27 -0
  78. pyoq/generation/writer.py +184 -0
  79. pyoq/hydration/__init__.py +24 -0
  80. pyoq/hydration/engine.py +155 -0
  81. pyoq/hydration/identity.py +194 -0
  82. pyoq/hydration/plan.py +116 -0
  83. pyoq/migrations/__init__.py +9 -0
  84. pyoq/migrations/alembic.py +106 -0
  85. pyoq/migrations/hooks.py +75 -0
  86. pyoq/naming.py +261 -0
  87. pyoq/policies/__init__.py +47 -0
  88. pyoq/policies/bypass.py +122 -0
  89. pyoq/policies/governed.py +430 -0
  90. pyoq/policies/model.py +242 -0
  91. pyoq/policies/rewriting.py +263 -0
  92. pyoq/py.typed +1 -0
  93. pyoq/query/__init__.py +312 -0
  94. pyoq/query/aggregates.py +172 -0
  95. pyoq/query/arrays.py +65 -0
  96. pyoq/query/binding.py +52 -0
  97. pyoq/query/capabilities.py +317 -0
  98. pyoq/query/casts.py +73 -0
  99. pyoq/query/choices.py +185 -0
  100. pyoq/query/decoding.py +360 -0
  101. pyoq/query/documents.py +56 -0
  102. pyoq/query/execution/__init__.py +63 -0
  103. pyoq/query/execution/aio/__init__.py +31 -0
  104. pyoq/query/execution/aio/operations.py +228 -0
  105. pyoq/query/execution/aio/pooling.py +233 -0
  106. pyoq/query/execution/aio/streaming.py +161 -0
  107. pyoq/query/execution/aio/transactions.py +105 -0
  108. pyoq/query/execution/batch.py +96 -0
  109. pyoq/query/execution/binding_style.py +30 -0
  110. pyoq/query/execution/compilation.py +48 -0
  111. pyoq/query/execution/context.py +61 -0
  112. pyoq/query/execution/control.py +50 -0
  113. pyoq/query/execution/operations.py +224 -0
  114. pyoq/query/execution/planning.py +107 -0
  115. pyoq/query/execution/pooling.py +279 -0
  116. pyoq/query/execution/results.py +36 -0
  117. pyoq/query/execution/streaming.py +178 -0
  118. pyoq/query/execution/transactions.py +95 -0
  119. pyoq/query/expressions.py +1200 -0
  120. pyoq/query/fields.py +60 -0
  121. pyoq/query/mysql/__init__.py +59 -0
  122. pyoq/query/mysql/aio/__init__.py +38 -0
  123. pyoq/query/mysql/aio/commands.py +389 -0
  124. pyoq/query/mysql/aio/driver.py +196 -0
  125. pyoq/query/mysql/aio/executor.py +123 -0
  126. pyoq/query/mysql/aio/factory.py +26 -0
  127. pyoq/query/mysql/aio/operations.py +38 -0
  128. pyoq/query/mysql/aio/pool.py +53 -0
  129. pyoq/query/mysql/aio/transactions.py +313 -0
  130. pyoq/query/mysql/commands.py +354 -0
  131. pyoq/query/mysql/compiler.py +134 -0
  132. pyoq/query/mysql/context.py +20 -0
  133. pyoq/query/mysql/executor.py +126 -0
  134. pyoq/query/mysql/expressions.py +244 -0
  135. pyoq/query/mysql/factory.py +46 -0
  136. pyoq/query/mysql/health.py +66 -0
  137. pyoq/query/mysql/identifiers.py +9 -0
  138. pyoq/query/mysql/model.py +79 -0
  139. pyoq/query/mysql/operations.py +43 -0
  140. pyoq/query/mysql/parameters.py +69 -0
  141. pyoq/query/mysql/planning.py +20 -0
  142. pyoq/query/mysql/pool.py +67 -0
  143. pyoq/query/mysql/transactions.py +331 -0
  144. pyoq/query/mysql/writes.py +73 -0
  145. pyoq/query/nodes.py +750 -0
  146. pyoq/query/postgres/__init__.py +48 -0
  147. pyoq/query/postgres/aio/__init__.py +25 -0
  148. pyoq/query/postgres/aio/bulk.py +56 -0
  149. pyoq/query/postgres/aio/commands.py +264 -0
  150. pyoq/query/postgres/aio/executor.py +152 -0
  151. pyoq/query/postgres/aio/factory.py +26 -0
  152. pyoq/query/postgres/aio/operations.py +26 -0
  153. pyoq/query/postgres/aio/pool.py +40 -0
  154. pyoq/query/postgres/aio/transactions.py +295 -0
  155. pyoq/query/postgres/bulk.py +62 -0
  156. pyoq/query/postgres/commands.py +238 -0
  157. pyoq/query/postgres/compiler.py +114 -0
  158. pyoq/query/postgres/context.py +20 -0
  159. pyoq/query/postgres/executor.py +147 -0
  160. pyoq/query/postgres/expressions.py +311 -0
  161. pyoq/query/postgres/factory.py +24 -0
  162. pyoq/query/postgres/health.py +24 -0
  163. pyoq/query/postgres/identifiers.py +9 -0
  164. pyoq/query/postgres/model.py +81 -0
  165. pyoq/query/postgres/operations.py +25 -0
  166. pyoq/query/postgres/parameters.py +71 -0
  167. pyoq/query/postgres/planning.py +20 -0
  168. pyoq/query/postgres/pool.py +52 -0
  169. pyoq/query/postgres/transactions.py +295 -0
  170. pyoq/query/postgres/writes.py +37 -0
  171. pyoq/query/projections.py +105 -0
  172. pyoq/query/raw.py +90 -0
  173. pyoq/query/recursion.py +265 -0
  174. pyoq/query/rendering/__init__.py +1 -0
  175. pyoq/query/rendering/expressions.py +913 -0
  176. pyoq/query/rendering/identifiers.py +40 -0
  177. pyoq/query/rendering/projections.py +63 -0
  178. pyoq/query/rendering/queries.py +334 -0
  179. pyoq/query/rendering/sources.py +66 -0
  180. pyoq/query/rendering/writes.py +176 -0
  181. pyoq/query/results.py +459 -0
  182. pyoq/query/routines.py +196 -0
  183. pyoq/query/rows.py +156 -0
  184. pyoq/query/select.py +793 -0
  185. pyoq/query/select_nodes.py +277 -0
  186. pyoq/query/sources.py +236 -0
  187. pyoq/query/sqlite/__init__.py +43 -0
  188. pyoq/query/sqlite/commands.py +201 -0
  189. pyoq/query/sqlite/compiler.py +139 -0
  190. pyoq/query/sqlite/context.py +20 -0
  191. pyoq/query/sqlite/executor.py +119 -0
  192. pyoq/query/sqlite/expressions.py +224 -0
  193. pyoq/query/sqlite/factory.py +32 -0
  194. pyoq/query/sqlite/health.py +28 -0
  195. pyoq/query/sqlite/identifiers.py +9 -0
  196. pyoq/query/sqlite/model.py +73 -0
  197. pyoq/query/sqlite/operations.py +36 -0
  198. pyoq/query/sqlite/parameters.py +50 -0
  199. pyoq/query/sqlite/planning.py +20 -0
  200. pyoq/query/sqlite/pool.py +50 -0
  201. pyoq/query/sqlite/streaming.py +13 -0
  202. pyoq/query/sqlite/transactions.py +274 -0
  203. pyoq/query/sqlite/writes.py +35 -0
  204. pyoq/query/statements.py +27 -0
  205. pyoq/query/values.py +23 -0
  206. pyoq/query/vendor.py +162 -0
  207. pyoq/query/windows.py +424 -0
  208. pyoq/query/write_nodes.py +174 -0
  209. pyoq/query/writes.py +628 -0
  210. pyoq/relations/__init__.py +66 -0
  211. pyoq/relations/batching.py +219 -0
  212. pyoq/relations/derivation.py +111 -0
  213. pyoq/relations/fetching.py +355 -0
  214. pyoq/relations/graph.py +245 -0
  215. pyoq/relations/loading.py +74 -0
  216. pyoq/relations/model.py +75 -0
  217. pyoq/relations/planning.py +206 -0
  218. pyoq/runtime/__init__.py +9 -0
  219. pyoq/runtime/kernels.py +25 -0
  220. pyoq/runtime/python.py +43 -0
  221. pyoq/runtime/selection.py +73 -0
  222. pyoq/sanic/__init__.py +32 -0
  223. pyoq/sanic/scope.py +197 -0
  224. pyoq/sanic/workers.py +129 -0
  225. pyoq/schema/__init__.py +108 -0
  226. pyoq/schema/codec.py +711 -0
  227. pyoq/schema/models.py +604 -0
  228. pyoq/schema/mysql/__init__.py +16 -0
  229. pyoq/schema/mysql/connection.py +73 -0
  230. pyoq/schema/mysql/dsn.py +72 -0
  231. pyoq/schema/mysql/records.py +354 -0
  232. pyoq/schema/mysql/reflection.py +309 -0
  233. pyoq/schema/mysql/source.py +30 -0
  234. pyoq/schema/mysql/sql.py +128 -0
  235. pyoq/schema/mysql/types.py +105 -0
  236. pyoq/schema/postgres/__init__.py +13 -0
  237. pyoq/schema/postgres/connection.py +63 -0
  238. pyoq/schema/postgres/records.py +384 -0
  239. pyoq/schema/postgres/reflection.py +466 -0
  240. pyoq/schema/postgres/source.py +30 -0
  241. pyoq/schema/postgres/sql.py +246 -0
  242. pyoq/schema/postgres/types.py +98 -0
  243. pyoq/schema/registry.py +45 -0
  244. pyoq/schema/source.py +15 -0
  245. pyoq/schema/sqlite/__init__.py +6 -0
  246. pyoq/schema/sqlite/connection.py +54 -0
  247. pyoq/schema/sqlite/records.py +167 -0
  248. pyoq/schema/sqlite/reflection.py +393 -0
  249. pyoq/schema/sqlite/source.py +30 -0
  250. pyoq/schema/sqlite/sql.py +254 -0
  251. pyoq/schema/sqlite/types.py +74 -0
  252. pyoq/serving/__init__.py +23 -0
  253. pyoq/serving/databases.py +107 -0
  254. pyoq/serving/opening.py +331 -0
  255. pyoq/snapshots/__init__.py +20 -0
  256. pyoq/snapshots/drift.py +312 -0
  257. pyoq/snapshots/files.py +96 -0
  258. pyoq/snapshots/routing.py +40 -0
  259. pyoq/snapshots/source.py +33 -0
  260. pyoq/tracing/__init__.py +5 -0
  261. pyoq/tracing/spans.py +89 -0
  262. pyoq/unset.py +14 -0
  263. pyoq_sql-1.0.2.dist-info/METADATA +3050 -0
  264. pyoq_sql-1.0.2.dist-info/RECORD +267 -0
  265. pyoq_sql-1.0.2.dist-info/WHEEL +4 -0
  266. pyoq_sql-1.0.2.dist-info/entry_points.txt +3 -0
  267. pyoq_sql-1.0.2.dist-info/licenses/LICENSE +373 -0
@@ -0,0 +1,1200 @@
1
+ """Typed immutable scalar expressions and predicates."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import date, datetime, time, timedelta
6
+ from decimal import Decimal
7
+ from typing import TYPE_CHECKING, Generic, TypeAlias, TypeVar, cast, overload
8
+
9
+ from pyoq.errors import ExpressionTypeError
10
+ from pyoq.query.nodes import (
11
+ ArrayDimensionsNode,
12
+ ArrayElementNode,
13
+ ArrayLengthNode,
14
+ ArrayNode,
15
+ ArrayOperation,
16
+ BinaryNode,
17
+ BinaryOperator,
18
+ BoundValueNode,
19
+ DatePart,
20
+ ExpressionNode,
21
+ ExtractNode,
22
+ FunctionName,
23
+ FunctionNode,
24
+ JsonContainsNode,
25
+ JsonKeysNode,
26
+ JsonNode,
27
+ JsonOperation,
28
+ JsonWrite,
29
+ JsonWriteNode,
30
+ NodeProvider,
31
+ ScalarFamily,
32
+ UnaryNode,
33
+ UnaryOperator,
34
+ VariadicNode,
35
+ VariadicOperator,
36
+ )
37
+ from pyoq.query.results import expression_family
38
+ from pyoq.schema.models import JsonValue
39
+
40
+ if TYPE_CHECKING:
41
+ from pyoq.query.projections import OrderTerm, Projection
42
+ from pyoq.query.results import ResultDescriptor
43
+
44
+ Value = TypeVar("Value")
45
+ Operand = TypeVar("Operand")
46
+ OrderedValue = TypeVar(
47
+ "OrderedValue",
48
+ bound=int | float | Decimal | str | date | datetime | time | timedelta | None,
49
+ )
50
+ TemporalValue = TypeVar("TemporalValue", bound=date | datetime | time | None)
51
+ HelperValue = TypeVar("HelperValue")
52
+ Element = TypeVar("Element")
53
+
54
+
55
+ _WHOLE_DOCUMENT_WRITES = frozenset({JsonWrite.MERGE, JsonWrite.CONCAT})
56
+ """Writes that take one document into another rather than reaching a member."""
57
+
58
+
59
+ class Expression(Generic[Value]):
60
+ __slots__ = ("_expression_family", "_expression_node")
61
+ _expression_family: ScalarFamily
62
+ _expression_node: ExpressionNode
63
+
64
+ def __init__(self, node: ExpressionNode, family: ScalarFamily) -> None:
65
+ self._initialize_expression(node, family)
66
+
67
+ def _initialize_expression(
68
+ self,
69
+ node: ExpressionNode,
70
+ family: ScalarFamily,
71
+ ) -> None:
72
+ object.__setattr__(self, "_expression_node", node)
73
+ object.__setattr__(self, "_expression_family", family)
74
+
75
+ def __setattr__(self, name: str, value: object) -> None:
76
+ message = "expressions are immutable"
77
+ raise AttributeError(message)
78
+
79
+ @property
80
+ def node(self) -> ExpressionNode:
81
+ return self._expression_node
82
+
83
+ @property
84
+ def family(self) -> ScalarFamily:
85
+ return self._expression_family
86
+
87
+ @property
88
+ def result(self) -> ResultDescriptor:
89
+ """What this answers with: its kind, its Python type, its nullability.
90
+
91
+ Derived from the tree rather than stored, so an expression built out
92
+ of others cannot disagree with what it was built from.
93
+ """
94
+ from pyoq.query.results import result_descriptor
95
+
96
+ return result_descriptor(self.node)
97
+
98
+ def as_(self, name: str, /) -> Projection[Value]:
99
+ from pyoq.query.projections import alias
100
+
101
+ return alias(self, name)
102
+
103
+ def asc(self) -> OrderTerm:
104
+ from pyoq.query.projections import ascending
105
+
106
+ return ascending(self)
107
+
108
+ def desc(self) -> OrderTerm:
109
+ from pyoq.query.projections import descending
110
+
111
+ return descending(self)
112
+
113
+ def eq(
114
+ self: Expression[Operand],
115
+ other: Operand | Expression[Operand],
116
+ /,
117
+ ) -> Condition:
118
+ if other is None:
119
+ return self.is_null()
120
+ return _comparison(BinaryOperator.EQUAL, self, other)
121
+
122
+ def ne(
123
+ self: Expression[Operand],
124
+ other: Operand | Expression[Operand],
125
+ /,
126
+ ) -> Condition:
127
+ if other is None:
128
+ return self.is_not_null()
129
+ return _comparison(BinaryOperator.NOT_EQUAL, self, other)
130
+
131
+ def is_distinct_from(
132
+ self: Expression[Operand],
133
+ other: Operand | Expression[Operand],
134
+ /,
135
+ ) -> Condition:
136
+ return _comparison(BinaryOperator.IS_DISTINCT_FROM, self, other)
137
+
138
+ def is_not_distinct_from(
139
+ self: Expression[Operand],
140
+ other: Operand | Expression[Operand],
141
+ /,
142
+ ) -> Condition:
143
+ return _comparison(BinaryOperator.IS_NOT_DISTINCT_FROM, self, other)
144
+
145
+ def has(
146
+ self: Expression[tuple[Element, ...]],
147
+ value: Element,
148
+ /,
149
+ ) -> Condition:
150
+ """Whether this array holds that value."""
151
+ return Condition(self._array_node(ArrayOperation.HAS, (value,)))
152
+
153
+ def lacks(
154
+ self: Expression[tuple[Element, ...]],
155
+ value: Element,
156
+ /,
157
+ ) -> Condition:
158
+ """Whether this array holds that value nowhere."""
159
+ return Condition(self._array_node(ArrayOperation.LACKS, (value,)))
160
+
161
+ def contains_all(
162
+ self: Expression[tuple[Element, ...]],
163
+ values: tuple[Element, ...],
164
+ /,
165
+ ) -> Condition:
166
+ """Whether this array holds every one of those values."""
167
+ return Condition(self._array_node(ArrayOperation.CONTAINS, values))
168
+
169
+ def contained_by(
170
+ self: Expression[tuple[Element, ...]],
171
+ values: tuple[Element, ...],
172
+ /,
173
+ ) -> Condition:
174
+ """Whether every value of this array is among those."""
175
+ return Condition(self._array_node(ArrayOperation.CONTAINED_BY, values))
176
+
177
+ def overlaps(
178
+ self: Expression[tuple[Element, ...]],
179
+ values: tuple[Element, ...],
180
+ /,
181
+ ) -> Condition:
182
+ """Whether this array and those values share anything at all."""
183
+ return Condition(self._array_node(ArrayOperation.OVERLAPS, values))
184
+
185
+ def append(
186
+ self: Expression[tuple[Element, ...]],
187
+ value: Element,
188
+ /,
189
+ ) -> ComputedExpression[tuple[Element, ...]]:
190
+ """This array with that value on the end."""
191
+ return self._array_result(ArrayOperation.APPEND, (value,))
192
+
193
+ def prepend(
194
+ self: Expression[tuple[Element, ...]],
195
+ value: Element,
196
+ /,
197
+ ) -> ComputedExpression[tuple[Element, ...]]:
198
+ """This array with that value on the front."""
199
+ return self._array_result(ArrayOperation.PREPEND, (value,))
200
+
201
+ def concat_array(
202
+ self: Expression[tuple[Element, ...]],
203
+ values: tuple[Element, ...],
204
+ /,
205
+ ) -> ComputedExpression[tuple[Element, ...]]:
206
+ """This array with those values after it."""
207
+ return self._array_result(ArrayOperation.CONCAT, values)
208
+
209
+ def without(
210
+ self: Expression[tuple[Element, ...]],
211
+ value: Element,
212
+ /,
213
+ ) -> ComputedExpression[tuple[Element, ...]]:
214
+ """This array with every copy of that value gone."""
215
+ return self._array_result(ArrayOperation.REMOVE, (value,))
216
+
217
+ def _array_result(
218
+ self: Expression[tuple[Element, ...]],
219
+ operation: ArrayOperation,
220
+ values: tuple[object, ...],
221
+ ) -> ComputedExpression[tuple[Element, ...]]:
222
+ return ComputedExpression[tuple[Element, ...]](
223
+ self._array_node(operation, values), ScalarFamily.OTHER
224
+ )
225
+
226
+ def replacing(
227
+ self: Expression[tuple[Element, ...]],
228
+ value: Element,
229
+ replacement: Element,
230
+ /,
231
+ ) -> ComputedExpression[tuple[Element, ...]]:
232
+ """This array with every copy of that value replaced by another."""
233
+ return ComputedExpression[tuple[Element, ...]](
234
+ ArrayNode(
235
+ ArrayOperation.REPLACE,
236
+ self.node,
237
+ (
238
+ operand(value, ScalarFamily.OTHER),
239
+ operand(replacement, ScalarFamily.OTHER),
240
+ ),
241
+ ),
242
+ ScalarFamily.OTHER,
243
+ )
244
+
245
+ def element(
246
+ self: Expression[tuple[Element, ...]],
247
+ *position: int,
248
+ ) -> ComputedExpression[Element | None]:
249
+ """The element at this position, counted from zero.
250
+
251
+ A column of an array is read back as a tuple and a tuple counts from
252
+ zero, so this counts from zero too and the dialect is told what it
253
+ counts from. Past the end there is no element, so the answer is null.
254
+
255
+ An array of arrays is read at one position per dimension, together,
256
+ because a dialect that stores one does not hand back a row of it.
257
+ """
258
+ for index in position:
259
+ if index < 0:
260
+ message = "an array is counted from zero, forwards"
261
+ raise ValueError(message)
262
+ return ComputedExpression[Element | None](
263
+ ArrayElementNode(
264
+ self.node,
265
+ tuple(operand(index, ScalarFamily.NUMERIC) for index in position),
266
+ ),
267
+ ScalarFamily.OTHER,
268
+ )
269
+
270
+ def length_along(
271
+ self: Expression[tuple[Element, ...]],
272
+ dimension: int,
273
+ /,
274
+ ) -> ComputedExpression[int | None]:
275
+ """How many elements this array holds along one of its dimensions.
276
+
277
+ Counted from one, the way a dialect numbers dimensions. The whole
278
+ count across every dimension is what length answers with.
279
+ """
280
+ if dimension < 1:
281
+ message = "the dimensions of an array are counted from one"
282
+ raise ValueError(message)
283
+ return ComputedExpression[int | None](
284
+ ArrayLengthNode(self.node, dimension), ScalarFamily.NUMERIC
285
+ )
286
+
287
+ def dimensions(
288
+ self: Expression[tuple[Element, ...]],
289
+ ) -> ComputedExpression[int | None]:
290
+ """How many dimensions this array has, which an empty array does not say."""
291
+ return ComputedExpression[int | None](
292
+ ArrayDimensionsNode(self.node), ScalarFamily.NUMERIC
293
+ )
294
+
295
+ def _array_node(
296
+ self,
297
+ operation: ArrayOperation,
298
+ values: tuple[object, ...],
299
+ ) -> ArrayNode:
300
+ one = operation in _ONE_VALUE
301
+ value = values[0] if one else values
302
+ return ArrayNode(operation, self.node, (operand(value, ScalarFamily.OTHER),))
303
+
304
+ def json_get(
305
+ self: Expression[HelperValue],
306
+ *path: str | int,
307
+ ) -> ComputedExpression[JsonValue]:
308
+ """The JSON value at this path, as JSON.
309
+
310
+ A path is steps rather than text because the dialects do not agree on
311
+ how one is written, and PostgreSQL reads another's spelling as a
312
+ member that is simply absent. A string step is a member and an
313
+ integer step is an element.
314
+ """
315
+ return ComputedExpression[JsonValue](
316
+ self._json_node(JsonOperation.VALUE, path), ScalarFamily.JSON
317
+ )
318
+
319
+ def json_keys(
320
+ self: Expression[HelperValue],
321
+ ) -> ComputedExpression[JsonValue]:
322
+ """The members this object has, as a JSON array of their names.
323
+
324
+ The dialects do not agree on the order the names come back in, because
325
+ each one stores an object the way it stores an object.
326
+ """
327
+ return ComputedExpression[JsonValue](JsonKeysNode(self.node), ScalarFamily.JSON)
328
+
329
+ def json_text(
330
+ self: Expression[HelperValue],
331
+ *path: str | int,
332
+ ) -> ComputedExpression[str | None]:
333
+ """The JSON value at this path, as text rather than as JSON."""
334
+ return ComputedExpression[str | None](
335
+ self._json_node(JsonOperation.TEXT, path), ScalarFamily.STRING
336
+ )
337
+
338
+ def json_length(
339
+ self: Expression[HelperValue],
340
+ *path: str | int,
341
+ ) -> ComputedExpression[int | None]:
342
+ """How many elements the array at this path holds."""
343
+ return ComputedExpression[int | None](
344
+ self._json_node(JsonOperation.LENGTH, path), ScalarFamily.NUMERIC
345
+ )
346
+
347
+ def json_has(self: Expression[HelperValue], *path: str | int) -> Condition:
348
+ """Whether anything is at this path at all."""
349
+ if not path:
350
+ message = "asking whether a path is there needs a path"
351
+ raise ValueError(message)
352
+ return Condition(self._json_node(JsonOperation.EXISTS, path))
353
+
354
+ def json_set(
355
+ self: Expression[HelperValue],
356
+ *path: str | int,
357
+ to: JsonValue | NodeProvider,
358
+ ) -> ComputedExpression[JsonValue]:
359
+ """The document with this path set, whether or not it was there."""
360
+ return self._json_written(JsonWrite.SET, path, to)
361
+
362
+ def json_insert(
363
+ self: Expression[HelperValue],
364
+ *path: str | int,
365
+ to: JsonValue | NodeProvider,
366
+ ) -> ComputedExpression[JsonValue]:
367
+ """The document with this path set, only where it was not there."""
368
+ return self._json_written(JsonWrite.INSERT, path, to)
369
+
370
+ def json_replace(
371
+ self: Expression[HelperValue],
372
+ *path: str | int,
373
+ to: JsonValue | NodeProvider,
374
+ ) -> ComputedExpression[JsonValue]:
375
+ """The document with this path set, only where it was there already."""
376
+ return self._json_written(JsonWrite.REPLACE, path, to)
377
+
378
+ def json_merge(
379
+ self: Expression[HelperValue],
380
+ value: JsonValue | NodeProvider,
381
+ /,
382
+ ) -> ComputedExpression[JsonValue]:
383
+ """The document with that one merged into it, all the way down.
384
+
385
+ This is merge-patch: a member given as null is removed rather than set
386
+ to null, and two objects at the same member are merged rather than one
387
+ replacing the other. PostgreSQL has no merge-patch of its own and its
388
+ concatenation is a different operation, so it is refused there and
389
+ ``json_concat`` says the other thing plainly.
390
+ """
391
+ return self._json_written(JsonWrite.MERGE, (), value)
392
+
393
+ def json_concat(
394
+ self: Expression[HelperValue],
395
+ value: JsonValue | NodeProvider,
396
+ /,
397
+ ) -> ComputedExpression[JsonValue]:
398
+ """The document with that one written over its members, one level deep.
399
+
400
+ A member of the second document replaces the first whole, an object is
401
+ not merged into an object, and a null is stored as a null rather than
402
+ removing anything.
403
+ """
404
+ return self._json_written(JsonWrite.CONCAT, (), value)
405
+
406
+ def json_remove(
407
+ self: Expression[HelperValue],
408
+ *path: str | int,
409
+ ) -> ComputedExpression[JsonValue]:
410
+ """The document without whatever was at this path."""
411
+ if not path:
412
+ message = "removing something from a document needs a path"
413
+ raise ValueError(message)
414
+ return ComputedExpression[JsonValue](
415
+ self._json_node(JsonOperation.REMOVE, path), ScalarFamily.JSON
416
+ )
417
+
418
+ def _json_written(
419
+ self,
420
+ operation: JsonWrite,
421
+ path: tuple[str | int, ...],
422
+ value: JsonValue | NodeProvider,
423
+ ) -> ComputedExpression[JsonValue]:
424
+ """A value or an expression, because a member can be set from a column."""
425
+ _require_family(self.family, ScalarFamily.JSON)
426
+ if operation not in _WHOLE_DOCUMENT_WRITES and not path:
427
+ message = "writing into a document needs a path"
428
+ raise ValueError(message)
429
+ written = (
430
+ value.node
431
+ if isinstance(value, NodeProvider)
432
+ else operand(value, ScalarFamily.JSON)
433
+ )
434
+ return ComputedExpression[JsonValue](
435
+ JsonWriteNode(operation, self.node, written, path), ScalarFamily.JSON
436
+ )
437
+
438
+ def json_contains(
439
+ self: Expression[HelperValue],
440
+ value: JsonValue,
441
+ /,
442
+ ) -> Condition:
443
+ """Whether this JSON value holds that one."""
444
+ _require_family(self.family, ScalarFamily.JSON)
445
+ return Condition(JsonContainsNode(self.node, operand(value, ScalarFamily.JSON)))
446
+
447
+ def _json_node(
448
+ self,
449
+ operation: JsonOperation,
450
+ path: tuple[str | int, ...],
451
+ ) -> JsonNode:
452
+ _require_family(self.family, ScalarFamily.JSON)
453
+ return JsonNode(operation, self.node, path)
454
+
455
+ def is_null(self) -> Condition:
456
+ return Condition(UnaryNode(UnaryOperator.IS_NULL, self.node))
457
+
458
+ def is_not_null(self) -> Condition:
459
+ return Condition(UnaryNode(UnaryOperator.IS_NOT_NULL, self.node))
460
+
461
+ def in_(
462
+ self: Expression[Operand],
463
+ *values: Operand | Expression[Operand],
464
+ ) -> Condition:
465
+ return _membership(VariadicOperator.IN, self, values)
466
+
467
+ def not_in(
468
+ self: Expression[Operand],
469
+ *values: Operand | Expression[Operand],
470
+ ) -> Condition:
471
+ return _membership(VariadicOperator.NOT_IN, self, values)
472
+
473
+ def between(
474
+ self: Expression[OrderedValue],
475
+ lower: OrderedValue | Expression[OrderedValue],
476
+ upper: OrderedValue | Expression[OrderedValue],
477
+ /,
478
+ ) -> Condition:
479
+ _require_ordered(self.family)
480
+ return _range_condition(VariadicOperator.BETWEEN, self, lower, upper)
481
+
482
+ def not_between(
483
+ self: Expression[OrderedValue],
484
+ lower: OrderedValue | Expression[OrderedValue],
485
+ upper: OrderedValue | Expression[OrderedValue],
486
+ /,
487
+ ) -> Condition:
488
+ _require_ordered(self.family)
489
+ return _range_condition(VariadicOperator.NOT_BETWEEN, self, lower, upper)
490
+
491
+ def lt(
492
+ self: Expression[OrderedValue],
493
+ other: OrderedValue | Expression[OrderedValue],
494
+ /,
495
+ ) -> Condition:
496
+ _require_ordered(self.family)
497
+ return _comparison(BinaryOperator.LESS_THAN, self, other)
498
+
499
+ def le(
500
+ self: Expression[OrderedValue],
501
+ other: OrderedValue | Expression[OrderedValue],
502
+ /,
503
+ ) -> Condition:
504
+ _require_ordered(self.family)
505
+ return _comparison(BinaryOperator.LESS_OR_EQUAL, self, other)
506
+
507
+ def gt(
508
+ self: Expression[OrderedValue],
509
+ other: OrderedValue | Expression[OrderedValue],
510
+ /,
511
+ ) -> Condition:
512
+ _require_ordered(self.family)
513
+ return _comparison(BinaryOperator.GREATER_THAN, self, other)
514
+
515
+ def ge(
516
+ self: Expression[OrderedValue],
517
+ other: OrderedValue | Expression[OrderedValue],
518
+ /,
519
+ ) -> Condition:
520
+ _require_ordered(self.family)
521
+ return _comparison(BinaryOperator.GREATER_OR_EQUAL, self, other)
522
+
523
+ @overload
524
+ def add(
525
+ self: Expression[int], other: int | Expression[int], /
526
+ ) -> ComputedExpression[int]: ...
527
+
528
+ @overload
529
+ def add(
530
+ self: Expression[float], other: float | Expression[float], /
531
+ ) -> ComputedExpression[float]: ...
532
+
533
+ @overload
534
+ def add(
535
+ self: Expression[Decimal], other: Decimal | Expression[Decimal], /
536
+ ) -> ComputedExpression[Decimal]: ...
537
+
538
+ @overload
539
+ def add(
540
+ self: Expression[int | None], other: int | Expression[int], /
541
+ ) -> ComputedExpression[int | None]: ...
542
+
543
+ @overload
544
+ def add(
545
+ self: Expression[float | None], other: float | Expression[float], /
546
+ ) -> NullableFloatResult: ...
547
+
548
+ @overload
549
+ def add(
550
+ self: Expression[Decimal | None], other: Decimal | Expression[Decimal], /
551
+ ) -> ComputedExpression[Decimal | None]: ...
552
+
553
+ def add(self, other: object, /) -> NumericResult:
554
+ return cast(NumericResult, _numeric(BinaryOperator.ADD, self, other))
555
+
556
+ @overload
557
+ def subtract(
558
+ self: Expression[int], other: int | Expression[int], /
559
+ ) -> ComputedExpression[int]: ...
560
+
561
+ @overload
562
+ def subtract(
563
+ self: Expression[float], other: float | Expression[float], /
564
+ ) -> ComputedExpression[float]: ...
565
+
566
+ @overload
567
+ def subtract(
568
+ self: Expression[Decimal], other: Decimal | Expression[Decimal], /
569
+ ) -> ComputedExpression[Decimal]: ...
570
+
571
+ @overload
572
+ def subtract(
573
+ self: Expression[int | None], other: int | Expression[int], /
574
+ ) -> ComputedExpression[int | None]: ...
575
+
576
+ @overload
577
+ def subtract(
578
+ self: Expression[float | None], other: float | Expression[float], /
579
+ ) -> NullableFloatResult: ...
580
+
581
+ @overload
582
+ def subtract(
583
+ self: Expression[Decimal | None], other: Decimal | Expression[Decimal], /
584
+ ) -> ComputedExpression[Decimal | None]: ...
585
+
586
+ def subtract(self, other: object, /) -> NumericResult:
587
+ return cast(NumericResult, _numeric(BinaryOperator.SUBTRACT, self, other))
588
+
589
+ @overload
590
+ def multiply(
591
+ self: Expression[int], other: int | Expression[int], /
592
+ ) -> ComputedExpression[int]: ...
593
+
594
+ @overload
595
+ def multiply(
596
+ self: Expression[float], other: float | Expression[float], /
597
+ ) -> ComputedExpression[float]: ...
598
+
599
+ @overload
600
+ def multiply(
601
+ self: Expression[Decimal], other: Decimal | Expression[Decimal], /
602
+ ) -> ComputedExpression[Decimal]: ...
603
+
604
+ @overload
605
+ def multiply(
606
+ self: Expression[int | None], other: int | Expression[int], /
607
+ ) -> ComputedExpression[int | None]: ...
608
+
609
+ @overload
610
+ def multiply(
611
+ self: Expression[float | None], other: float | Expression[float], /
612
+ ) -> NullableFloatResult: ...
613
+
614
+ @overload
615
+ def multiply(
616
+ self: Expression[Decimal | None], other: Decimal | Expression[Decimal], /
617
+ ) -> ComputedExpression[Decimal | None]: ...
618
+
619
+ def multiply(self, other: object, /) -> NumericResult:
620
+ return cast(NumericResult, _numeric(BinaryOperator.MULTIPLY, self, other))
621
+
622
+ @overload
623
+ def divide(
624
+ self: Expression[int], other: int | Expression[int], /
625
+ ) -> ComputedExpression[int]: ...
626
+
627
+ @overload
628
+ def divide(
629
+ self: Expression[float], other: float | Expression[float], /
630
+ ) -> ComputedExpression[float]: ...
631
+
632
+ @overload
633
+ def divide(
634
+ self: Expression[Decimal], other: Decimal | Expression[Decimal], /
635
+ ) -> ComputedExpression[Decimal]: ...
636
+
637
+ @overload
638
+ def divide(
639
+ self: Expression[int | None], other: int | Expression[int], /
640
+ ) -> ComputedExpression[int | None]: ...
641
+
642
+ @overload
643
+ def divide(
644
+ self: Expression[float | None], other: float | Expression[float], /
645
+ ) -> NullableFloatResult: ...
646
+
647
+ @overload
648
+ def divide(
649
+ self: Expression[Decimal | None], other: Decimal | Expression[Decimal], /
650
+ ) -> ComputedExpression[Decimal | None]: ...
651
+
652
+ def divide(self, other: object, /) -> NumericResult:
653
+ return cast(NumericResult, _numeric(BinaryOperator.DIVIDE, self, other))
654
+
655
+ @overload
656
+ def modulo(
657
+ self: Expression[int], other: int | Expression[int], /
658
+ ) -> ComputedExpression[int]: ...
659
+
660
+ @overload
661
+ def modulo(
662
+ self: Expression[float], other: float | Expression[float], /
663
+ ) -> ComputedExpression[float]: ...
664
+
665
+ @overload
666
+ def modulo(
667
+ self: Expression[Decimal], other: Decimal | Expression[Decimal], /
668
+ ) -> ComputedExpression[Decimal]: ...
669
+
670
+ @overload
671
+ def modulo(
672
+ self: Expression[int | None], other: int | Expression[int], /
673
+ ) -> ComputedExpression[int | None]: ...
674
+
675
+ @overload
676
+ def modulo(
677
+ self: Expression[float | None], other: float | Expression[float], /
678
+ ) -> NullableFloatResult: ...
679
+
680
+ @overload
681
+ def modulo(
682
+ self: Expression[Decimal | None], other: Decimal | Expression[Decimal], /
683
+ ) -> ComputedExpression[Decimal | None]: ...
684
+
685
+ def modulo(self, other: object, /) -> NumericResult:
686
+ return cast(NumericResult, _numeric(BinaryOperator.MODULO, self, other))
687
+
688
+ def negate(self: Expression[Operand]) -> ComputedExpression[Operand]:
689
+ _require_family(self.family, ScalarFamily.NUMERIC)
690
+ return ComputedExpression(
691
+ UnaryNode(UnaryOperator.NEGATE, self.node), self.family
692
+ )
693
+
694
+ @overload
695
+ def concat(
696
+ self: Expression[str], other: str | Expression[str], /
697
+ ) -> ComputedExpression[str]: ...
698
+
699
+ @overload
700
+ def concat(
701
+ self: Expression[str | None], other: str | Expression[str], /
702
+ ) -> ComputedExpression[str | None]: ...
703
+
704
+ def concat(self, other: object, /) -> StringResult:
705
+ return cast(StringResult, _string_binary(BinaryOperator.CONCAT, self, other))
706
+
707
+ @overload
708
+ def like(self: Expression[str], pattern: str | Expression[str], /) -> Condition: ...
709
+
710
+ @overload
711
+ def like(
712
+ self: Expression[str | None], pattern: str | Expression[str], /
713
+ ) -> Condition: ...
714
+
715
+ def like(self, pattern: str | Expression[str], /) -> Condition:
716
+ return _string_condition(BinaryOperator.LIKE, self, pattern)
717
+
718
+ @overload
719
+ def not_like(
720
+ self: Expression[str], pattern: str | Expression[str], /
721
+ ) -> Condition: ...
722
+
723
+ @overload
724
+ def not_like(
725
+ self: Expression[str | None], pattern: str | Expression[str], /
726
+ ) -> Condition: ...
727
+
728
+ def not_like(self, pattern: str | Expression[str], /) -> Condition:
729
+ return _string_condition(BinaryOperator.NOT_LIKE, self, pattern)
730
+
731
+ @overload
732
+ def contains(
733
+ self: Expression[str], value: str | Expression[str], /
734
+ ) -> Condition: ...
735
+
736
+ @overload
737
+ def contains(
738
+ self: Expression[str | None], value: str | Expression[str], /
739
+ ) -> Condition: ...
740
+
741
+ def contains(self, value: str | Expression[str], /) -> Condition:
742
+ return _string_function(FunctionName.CONTAINS, self, value)
743
+
744
+ @overload
745
+ def starts_with(
746
+ self: Expression[str], value: str | Expression[str], /
747
+ ) -> Condition: ...
748
+
749
+ @overload
750
+ def starts_with(
751
+ self: Expression[str | None], value: str | Expression[str], /
752
+ ) -> Condition: ...
753
+
754
+ def starts_with(self, value: str | Expression[str], /) -> Condition:
755
+ return _string_function(FunctionName.STARTS_WITH, self, value)
756
+
757
+ @overload
758
+ def ends_with(
759
+ self: Expression[str], value: str | Expression[str], /
760
+ ) -> Condition: ...
761
+
762
+ @overload
763
+ def ends_with(
764
+ self: Expression[str | None], value: str | Expression[str], /
765
+ ) -> Condition: ...
766
+
767
+ def ends_with(self, value: str | Expression[str], /) -> Condition:
768
+ return _string_function(FunctionName.ENDS_WITH, self, value)
769
+
770
+ @overload
771
+ def is_true(self: Expression[bool]) -> Condition: ...
772
+
773
+ @overload
774
+ def is_true(self: Expression[bool | None]) -> Condition: ...
775
+
776
+ def is_true(self) -> Condition:
777
+ _require_family(self.family, ScalarFamily.BOOLEAN)
778
+ return Condition(UnaryNode(UnaryOperator.IS_TRUE, self.node))
779
+
780
+ @overload
781
+ def is_false(self: Expression[bool]) -> Condition: ...
782
+
783
+ @overload
784
+ def is_false(self: Expression[bool | None]) -> Condition: ...
785
+
786
+ def is_false(self) -> Condition:
787
+ _require_family(self.family, ScalarFamily.BOOLEAN)
788
+ return Condition(UnaryNode(UnaryOperator.IS_FALSE, self.node))
789
+
790
+ @overload
791
+ def lower(self: Expression[str]) -> ComputedExpression[str]: ...
792
+
793
+ @overload
794
+ def lower(self: Expression[str | None]) -> ComputedExpression[str | None]: ...
795
+
796
+ def lower(self) -> StringResult:
797
+ return cast(StringResult, _string_transform(FunctionName.LOWER, self))
798
+
799
+ @overload
800
+ def upper(self: Expression[str]) -> ComputedExpression[str]: ...
801
+
802
+ @overload
803
+ def upper(self: Expression[str | None]) -> ComputedExpression[str | None]: ...
804
+
805
+ def upper(self) -> StringResult:
806
+ return cast(StringResult, _string_transform(FunctionName.UPPER, self))
807
+
808
+ @overload
809
+ def length(self: Expression[str]) -> ComputedExpression[int]: ...
810
+
811
+ @overload
812
+ def length(self: Expression[str | None]) -> ComputedExpression[int | None]: ...
813
+
814
+ @overload
815
+ def length(
816
+ self: Expression[tuple[Element, ...]],
817
+ ) -> ComputedExpression[int | None]: ...
818
+
819
+ def length(self) -> LengthResult:
820
+ """How long this is: characters of text, or elements of an array."""
821
+ if self.family is ScalarFamily.OTHER:
822
+ return cast(
823
+ LengthResult,
824
+ ComputedExpression[int | None](
825
+ ArrayLengthNode(self.node), ScalarFamily.NUMERIC
826
+ ),
827
+ )
828
+ _require_family(self.family, ScalarFamily.STRING)
829
+ return cast(
830
+ LengthResult,
831
+ ComputedExpression[int](
832
+ FunctionNode(FunctionName.LENGTH, (self.node,)),
833
+ ScalarFamily.NUMERIC,
834
+ ),
835
+ )
836
+
837
+ @overload
838
+ def extract(
839
+ self: Expression[date], part: DatePart, /
840
+ ) -> ComputedExpression[int]: ...
841
+
842
+ @overload
843
+ def extract(
844
+ self: Expression[date | None], part: DatePart, /
845
+ ) -> ComputedExpression[int | None]: ...
846
+
847
+ @overload
848
+ def extract(
849
+ self: Expression[datetime], part: DatePart, /
850
+ ) -> ComputedExpression[int]: ...
851
+
852
+ @overload
853
+ def extract(
854
+ self: Expression[datetime | None], part: DatePart, /
855
+ ) -> ComputedExpression[int | None]: ...
856
+
857
+ @overload
858
+ def extract(
859
+ self: Expression[time], part: DatePart, /
860
+ ) -> ComputedExpression[int]: ...
861
+
862
+ @overload
863
+ def extract(
864
+ self: Expression[time | None], part: DatePart, /
865
+ ) -> ComputedExpression[int | None]: ...
866
+
867
+ def extract(self, part: DatePart, /) -> ExtractResult:
868
+ _require_family(self.family, ScalarFamily.TEMPORAL)
869
+ return cast(
870
+ ExtractResult,
871
+ ComputedExpression[int](ExtractNode(part, self.node), ScalarFamily.NUMERIC),
872
+ )
873
+
874
+ def add_duration(
875
+ self: Expression[TemporalValue],
876
+ duration: timedelta | Expression[timedelta],
877
+ /,
878
+ ) -> ComputedExpression[TemporalValue]:
879
+ return _temporal(BinaryOperator.ADD, self, duration)
880
+
881
+ def subtract_duration(
882
+ self: Expression[TemporalValue],
883
+ duration: timedelta | Expression[timedelta],
884
+ /,
885
+ ) -> ComputedExpression[TemporalValue]:
886
+ return _temporal(BinaryOperator.SUBTRACT, self, duration)
887
+
888
+ @overload
889
+ def difference(
890
+ self: Expression[date],
891
+ other: date | Expression[date],
892
+ /,
893
+ ) -> ComputedExpression[timedelta]: ...
894
+
895
+ @overload
896
+ def difference(
897
+ self: Expression[date | None],
898
+ other: date | Expression[date],
899
+ /,
900
+ ) -> ComputedExpression[timedelta | None]: ...
901
+
902
+ @overload
903
+ def difference(
904
+ self: Expression[datetime],
905
+ other: datetime | Expression[datetime],
906
+ /,
907
+ ) -> ComputedExpression[timedelta]: ...
908
+
909
+ @overload
910
+ def difference(
911
+ self: Expression[datetime | None],
912
+ other: datetime | Expression[datetime],
913
+ /,
914
+ ) -> ComputedExpression[timedelta | None]: ...
915
+
916
+ @overload
917
+ def difference(
918
+ self: Expression[time],
919
+ other: time | Expression[time],
920
+ /,
921
+ ) -> ComputedExpression[timedelta]: ...
922
+
923
+ @overload
924
+ def difference(
925
+ self: Expression[time | None],
926
+ other: time | Expression[time],
927
+ /,
928
+ ) -> ComputedExpression[timedelta | None]: ...
929
+
930
+ def difference(self, other: object, /) -> DifferenceResult:
931
+ _require_family(self.family, ScalarFamily.TEMPORAL)
932
+ return cast(
933
+ DifferenceResult,
934
+ ComputedExpression[timedelta](
935
+ BinaryNode(
936
+ BinaryOperator.SUBTRACT,
937
+ self.node,
938
+ operand(other, ScalarFamily.TEMPORAL),
939
+ ),
940
+ ScalarFamily.TEMPORAL,
941
+ ),
942
+ )
943
+
944
+
945
+ class ComputedExpression(Expression[Value], Generic[Value]):
946
+ __slots__ = ()
947
+
948
+
949
+ LengthResult: TypeAlias = ComputedExpression[int] | ComputedExpression[int | None]
950
+ ExtractResult: TypeAlias = ComputedExpression[int] | ComputedExpression[int | None]
951
+ NumericResult: TypeAlias = (
952
+ ComputedExpression[int]
953
+ | ComputedExpression[float]
954
+ | ComputedExpression[Decimal]
955
+ | ComputedExpression[int | None]
956
+ | ComputedExpression[float | None]
957
+ | ComputedExpression[Decimal | None]
958
+ )
959
+ NullableFloatResult: TypeAlias = (
960
+ ComputedExpression[int | None] | ComputedExpression[float | None]
961
+ )
962
+ StringResult: TypeAlias = ComputedExpression[str] | ComputedExpression[str | None]
963
+ DifferenceResult: TypeAlias = (
964
+ ComputedExpression[timedelta] | ComputedExpression[timedelta | None]
965
+ )
966
+
967
+
968
+ class Condition(Expression[bool]):
969
+ __slots__ = ()
970
+
971
+ def __init__(self, node: ExpressionNode) -> None:
972
+ super().__init__(node, ScalarFamily.BOOLEAN)
973
+
974
+ def and_(self, other: Expression[bool], /) -> Condition:
975
+ return Condition(_combine(VariadicOperator.AND, self.node, other.node))
976
+
977
+ def or_(self, other: Expression[bool], /) -> Condition:
978
+ return Condition(_combine(VariadicOperator.OR, self.node, other.node))
979
+
980
+ def not_(self) -> Condition:
981
+ return Condition(UnaryNode(UnaryOperator.NOT, self.node))
982
+
983
+ def __and__(self, other: Expression[bool], /) -> Condition:
984
+ return self.and_(other)
985
+
986
+ def __or__(self, other: Expression[bool], /) -> Condition:
987
+ return self.or_(other)
988
+
989
+ def __invert__(self) -> Condition:
990
+ return self.not_()
991
+
992
+
993
+ _ONE_VALUE = frozenset(
994
+ {
995
+ ArrayOperation.HAS,
996
+ ArrayOperation.LACKS,
997
+ ArrayOperation.APPEND,
998
+ ArrayOperation.PREPEND,
999
+ ArrayOperation.REMOVE,
1000
+ }
1001
+ )
1002
+ """The ways of asking about an array that take one value, not a set of them."""
1003
+
1004
+
1005
+ def require_boolean(value: object, clause: str) -> None:
1006
+ if not isinstance(value, Expression) or value.family is not ScalarFamily.BOOLEAN:
1007
+ message = f"{clause} requires a boolean expression"
1008
+ raise TypeError(message)
1009
+
1010
+
1011
+ def _comparison(
1012
+ operator: BinaryOperator,
1013
+ left: Expression[HelperValue],
1014
+ right: object,
1015
+ ) -> Condition:
1016
+ right_node = operand(right, left.family)
1017
+ return Condition(BinaryNode(operator, left.node, right_node))
1018
+
1019
+
1020
+ def _membership(
1021
+ operator: VariadicOperator,
1022
+ expression: Expression[HelperValue],
1023
+ values: tuple[object, ...],
1024
+ ) -> Condition:
1025
+ operands = (
1026
+ expression.node,
1027
+ *(operand(value, expression.family) for value in values),
1028
+ )
1029
+ return Condition(VariadicNode(operator, operands))
1030
+
1031
+
1032
+ def _range_condition(
1033
+ operator: VariadicOperator,
1034
+ expression: Expression[HelperValue],
1035
+ lower: object,
1036
+ upper: object,
1037
+ ) -> Condition:
1038
+ operands = (
1039
+ expression.node,
1040
+ operand(lower, expression.family),
1041
+ operand(upper, expression.family),
1042
+ )
1043
+ return Condition(VariadicNode(operator, operands))
1044
+
1045
+
1046
+ def _numeric(
1047
+ operator: BinaryOperator,
1048
+ left: Expression[HelperValue],
1049
+ right: object,
1050
+ ) -> ComputedExpression[HelperValue]:
1051
+ _require_family(left.family, ScalarFamily.NUMERIC)
1052
+ return ComputedExpression[HelperValue](
1053
+ BinaryNode(operator, left.node, operand(right, ScalarFamily.NUMERIC)),
1054
+ ScalarFamily.NUMERIC,
1055
+ )
1056
+
1057
+
1058
+ def _string_binary(
1059
+ operator: BinaryOperator,
1060
+ left: Expression[HelperValue],
1061
+ right: object,
1062
+ ) -> ComputedExpression[HelperValue]:
1063
+ _require_family(left.family, ScalarFamily.STRING)
1064
+ return ComputedExpression[HelperValue](
1065
+ BinaryNode(operator, left.node, operand(right, ScalarFamily.STRING)),
1066
+ ScalarFamily.STRING,
1067
+ )
1068
+
1069
+
1070
+ def _temporal(
1071
+ operator: BinaryOperator,
1072
+ left: Expression[TemporalValue],
1073
+ right: timedelta | Expression[timedelta],
1074
+ ) -> ComputedExpression[TemporalValue]:
1075
+ _require_family(left.family, ScalarFamily.TEMPORAL)
1076
+ return ComputedExpression(
1077
+ BinaryNode(operator, left.node, operand(right, ScalarFamily.TEMPORAL)),
1078
+ ScalarFamily.TEMPORAL,
1079
+ )
1080
+
1081
+
1082
+ def _string_condition(
1083
+ operator: BinaryOperator,
1084
+ left: Expression[HelperValue],
1085
+ right: object,
1086
+ ) -> Condition:
1087
+ _require_family(left.family, ScalarFamily.STRING)
1088
+ return Condition(
1089
+ BinaryNode(operator, left.node, operand(right, ScalarFamily.STRING))
1090
+ )
1091
+
1092
+
1093
+ def _string_function(
1094
+ name: FunctionName,
1095
+ expression: Expression[HelperValue],
1096
+ value: object,
1097
+ ) -> Condition:
1098
+ _require_family(expression.family, ScalarFamily.STRING)
1099
+ arguments = (expression.node, operand(value, ScalarFamily.STRING))
1100
+ return Condition(FunctionNode(name, arguments))
1101
+
1102
+
1103
+ def _string_transform(
1104
+ name: FunctionName,
1105
+ expression: Expression[HelperValue],
1106
+ ) -> ComputedExpression[HelperValue]:
1107
+ _require_family(expression.family, ScalarFamily.STRING)
1108
+ return ComputedExpression[HelperValue](
1109
+ FunctionNode(name, (expression.node,)), ScalarFamily.STRING
1110
+ )
1111
+
1112
+
1113
+ def operand(value: object, expected: ScalarFamily) -> ExpressionNode:
1114
+ """Turn a value or expression into a node, refusing an incompatible one.
1115
+
1116
+ What an expression is judged by is what its own node answers with, so an
1117
+ expression built out of others is bound as the thing it became rather
1118
+ than as the thing it was constructed with.
1119
+ """
1120
+ if isinstance(value, Expression):
1121
+ _require_compatible(expected, expression_family(value.node))
1122
+ return value.node
1123
+ family = family_for_value(value)
1124
+ _require_compatible(expected, family)
1125
+ return BoundValueNode(value, family)
1126
+
1127
+
1128
+ def family_for_value(value: object) -> ScalarFamily:
1129
+ if value is None:
1130
+ return ScalarFamily.NULL
1131
+ if isinstance(value, bool):
1132
+ return ScalarFamily.BOOLEAN
1133
+ if isinstance(value, (int, float, Decimal)):
1134
+ return ScalarFamily.NUMERIC
1135
+ if isinstance(value, str):
1136
+ return ScalarFamily.STRING
1137
+ if isinstance(value, bytes):
1138
+ return ScalarFamily.BINARY
1139
+ if isinstance(value, (date, time, timedelta)):
1140
+ return ScalarFamily.TEMPORAL
1141
+ if isinstance(value, (list, dict)):
1142
+ return ScalarFamily.JSON
1143
+ return ScalarFamily.OTHER
1144
+
1145
+
1146
+ def _require_compatible(expected: ScalarFamily, actual: ScalarFamily) -> None:
1147
+ if actual is ScalarFamily.NULL or expected is ScalarFamily.OTHER:
1148
+ return
1149
+ json_families = {
1150
+ ScalarFamily.BOOLEAN,
1151
+ ScalarFamily.JSON,
1152
+ ScalarFamily.NUMERIC,
1153
+ ScalarFamily.STRING,
1154
+ }
1155
+ if expected is ScalarFamily.JSON and actual in json_families:
1156
+ return
1157
+ if actual is expected:
1158
+ return
1159
+ message = f"incompatible expression families: {expected.value} and {actual.value}"
1160
+ raise ExpressionTypeError(message)
1161
+
1162
+
1163
+ def _require_family(actual: ScalarFamily, expected: ScalarFamily) -> None:
1164
+ if actual is not expected:
1165
+ message = f"operation requires {expected.value}, received {actual.value}"
1166
+ raise ExpressionTypeError(message)
1167
+
1168
+
1169
+ def _require_ordered(family: ScalarFamily) -> None:
1170
+ if family not in {ScalarFamily.NUMERIC, ScalarFamily.STRING, ScalarFamily.TEMPORAL}:
1171
+ message = f"operation requires an ordered expression, received {family.value}"
1172
+ raise ExpressionTypeError(message)
1173
+
1174
+
1175
+ def _combine(
1176
+ operator: VariadicOperator,
1177
+ left: ExpressionNode,
1178
+ right: ExpressionNode,
1179
+ ) -> VariadicNode:
1180
+ left_operands = (
1181
+ left.operands
1182
+ if isinstance(left, VariadicNode) and left.operator is operator
1183
+ else (left,)
1184
+ )
1185
+ right_operands = (
1186
+ right.operands
1187
+ if isinstance(right, VariadicNode) and right.operator is operator
1188
+ else (right,)
1189
+ )
1190
+ return VariadicNode(operator, (*left_operands, *right_operands))
1191
+
1192
+
1193
+ __all__ = (
1194
+ "ComputedExpression",
1195
+ "Condition",
1196
+ "Expression",
1197
+ "family_for_value",
1198
+ "operand",
1199
+ "require_boolean",
1200
+ )