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.
- pyoq/__init__.py +10 -0
- pyoq/__main__.py +5 -0
- pyoq/_native.pyi +5 -0
- pyoq/cli/__init__.py +5 -0
- pyoq/cli/commands.py +270 -0
- pyoq/cli/defaults.py +98 -0
- pyoq/cli/services.py +97 -0
- pyoq/config/__init__.py +31 -0
- pyoq/config/connection.py +161 -0
- pyoq/config/loader.py +289 -0
- pyoq/config/models.py +245 -0
- pyoq/config/values.py +142 -0
- pyoq/descriptors.py +165 -0
- pyoq/diagnostics/__init__.py +68 -0
- pyoq/diagnostics/budget.py +136 -0
- pyoq/diagnostics/events.py +137 -0
- pyoq/diagnostics/fingerprint.py +267 -0
- pyoq/diagnostics/instrumented.py +237 -0
- pyoq/diagnostics/metrics.py +61 -0
- pyoq/diagnostics/observation.py +227 -0
- pyoq/diagnostics/scoped.py +103 -0
- pyoq/django/__init__.py +15 -0
- pyoq/django/apps.py +17 -0
- pyoq/django/execution.py +317 -0
- pyoq/django/generation.py +59 -0
- pyoq/django/management/__init__.py +0 -0
- pyoq/django/management/commands/__init__.py +0 -0
- pyoq/django/management/commands/makemigrations.py +53 -0
- pyoq/django/management/commands/pyoq_codegen.py +75 -0
- pyoq/django/parameters.py +101 -0
- pyoq/django/schema.py +379 -0
- pyoq/django/settings.py +87 -0
- pyoq/django/timeouts.py +105 -0
- pyoq/dsl/__init__.py +64 -0
- pyoq/dsl/aio/__init__.py +31 -0
- pyoq/dsl/aio/context.py +295 -0
- pyoq/dsl/aio/queries.py +335 -0
- pyoq/dsl/aio/writes.py +368 -0
- pyoq/dsl/context.py +326 -0
- pyoq/dsl/entry.py +37 -0
- pyoq/dsl/labels.py +36 -0
- pyoq/dsl/queries.py +339 -0
- pyoq/dsl/result.py +164 -0
- pyoq/dsl/writes.py +360 -0
- pyoq/errors.py +317 -0
- pyoq/fastapi/__init__.py +32 -0
- pyoq/fastapi/dependencies.py +167 -0
- pyoq/fastapi/lifespan.py +119 -0
- pyoq/fetching/__init__.py +55 -0
- pyoq/fetching/collections.py +136 -0
- pyoq/fetching/execution.py +587 -0
- pyoq/fetching/joined.py +79 -0
- pyoq/fetching/nesting.py +183 -0
- pyoq/fetching/plans.py +541 -0
- pyoq/fetching/select_in.py +149 -0
- pyoq/fetching/tables.py +110 -0
- pyoq/generation/__init__.py +54 -0
- pyoq/generation/cleanup.py +44 -0
- pyoq/generation/contracts.py +248 -0
- pyoq/generation/drift.py +169 -0
- pyoq/generation/lock.py +33 -0
- pyoq/generation/manifest.py +114 -0
- pyoq/generation/model.py +1001 -0
- pyoq/generation/pipeline.py +119 -0
- pyoq/generation/rendering/__init__.py +5 -0
- pyoq/generation/rendering/domains.py +51 -0
- pyoq/generation/rendering/enums.py +29 -0
- pyoq/generation/rendering/exports.py +70 -0
- pyoq/generation/rendering/imports.py +63 -0
- pyoq/generation/rendering/package.py +56 -0
- pyoq/generation/rendering/relations.py +133 -0
- pyoq/generation/rendering/routines.py +396 -0
- pyoq/generation/rendering/rows.py +79 -0
- pyoq/generation/rendering/source.py +121 -0
- pyoq/generation/rendering/tables.py +300 -0
- pyoq/generation/rendering/writes.py +514 -0
- pyoq/generation/validation.py +27 -0
- pyoq/generation/writer.py +184 -0
- pyoq/hydration/__init__.py +24 -0
- pyoq/hydration/engine.py +155 -0
- pyoq/hydration/identity.py +194 -0
- pyoq/hydration/plan.py +116 -0
- pyoq/migrations/__init__.py +9 -0
- pyoq/migrations/alembic.py +106 -0
- pyoq/migrations/hooks.py +75 -0
- pyoq/naming.py +261 -0
- pyoq/policies/__init__.py +47 -0
- pyoq/policies/bypass.py +122 -0
- pyoq/policies/governed.py +430 -0
- pyoq/policies/model.py +242 -0
- pyoq/policies/rewriting.py +263 -0
- pyoq/py.typed +1 -0
- pyoq/query/__init__.py +312 -0
- pyoq/query/aggregates.py +172 -0
- pyoq/query/arrays.py +65 -0
- pyoq/query/binding.py +52 -0
- pyoq/query/capabilities.py +317 -0
- pyoq/query/casts.py +73 -0
- pyoq/query/choices.py +185 -0
- pyoq/query/decoding.py +360 -0
- pyoq/query/documents.py +56 -0
- pyoq/query/execution/__init__.py +63 -0
- pyoq/query/execution/aio/__init__.py +31 -0
- pyoq/query/execution/aio/operations.py +228 -0
- pyoq/query/execution/aio/pooling.py +233 -0
- pyoq/query/execution/aio/streaming.py +161 -0
- pyoq/query/execution/aio/transactions.py +105 -0
- pyoq/query/execution/batch.py +96 -0
- pyoq/query/execution/binding_style.py +30 -0
- pyoq/query/execution/compilation.py +48 -0
- pyoq/query/execution/context.py +61 -0
- pyoq/query/execution/control.py +50 -0
- pyoq/query/execution/operations.py +224 -0
- pyoq/query/execution/planning.py +107 -0
- pyoq/query/execution/pooling.py +279 -0
- pyoq/query/execution/results.py +36 -0
- pyoq/query/execution/streaming.py +178 -0
- pyoq/query/execution/transactions.py +95 -0
- pyoq/query/expressions.py +1200 -0
- pyoq/query/fields.py +60 -0
- pyoq/query/mysql/__init__.py +59 -0
- pyoq/query/mysql/aio/__init__.py +38 -0
- pyoq/query/mysql/aio/commands.py +389 -0
- pyoq/query/mysql/aio/driver.py +196 -0
- pyoq/query/mysql/aio/executor.py +123 -0
- pyoq/query/mysql/aio/factory.py +26 -0
- pyoq/query/mysql/aio/operations.py +38 -0
- pyoq/query/mysql/aio/pool.py +53 -0
- pyoq/query/mysql/aio/transactions.py +313 -0
- pyoq/query/mysql/commands.py +354 -0
- pyoq/query/mysql/compiler.py +134 -0
- pyoq/query/mysql/context.py +20 -0
- pyoq/query/mysql/executor.py +126 -0
- pyoq/query/mysql/expressions.py +244 -0
- pyoq/query/mysql/factory.py +46 -0
- pyoq/query/mysql/health.py +66 -0
- pyoq/query/mysql/identifiers.py +9 -0
- pyoq/query/mysql/model.py +79 -0
- pyoq/query/mysql/operations.py +43 -0
- pyoq/query/mysql/parameters.py +69 -0
- pyoq/query/mysql/planning.py +20 -0
- pyoq/query/mysql/pool.py +67 -0
- pyoq/query/mysql/transactions.py +331 -0
- pyoq/query/mysql/writes.py +73 -0
- pyoq/query/nodes.py +750 -0
- pyoq/query/postgres/__init__.py +48 -0
- pyoq/query/postgres/aio/__init__.py +25 -0
- pyoq/query/postgres/aio/bulk.py +56 -0
- pyoq/query/postgres/aio/commands.py +264 -0
- pyoq/query/postgres/aio/executor.py +152 -0
- pyoq/query/postgres/aio/factory.py +26 -0
- pyoq/query/postgres/aio/operations.py +26 -0
- pyoq/query/postgres/aio/pool.py +40 -0
- pyoq/query/postgres/aio/transactions.py +295 -0
- pyoq/query/postgres/bulk.py +62 -0
- pyoq/query/postgres/commands.py +238 -0
- pyoq/query/postgres/compiler.py +114 -0
- pyoq/query/postgres/context.py +20 -0
- pyoq/query/postgres/executor.py +147 -0
- pyoq/query/postgres/expressions.py +311 -0
- pyoq/query/postgres/factory.py +24 -0
- pyoq/query/postgres/health.py +24 -0
- pyoq/query/postgres/identifiers.py +9 -0
- pyoq/query/postgres/model.py +81 -0
- pyoq/query/postgres/operations.py +25 -0
- pyoq/query/postgres/parameters.py +71 -0
- pyoq/query/postgres/planning.py +20 -0
- pyoq/query/postgres/pool.py +52 -0
- pyoq/query/postgres/transactions.py +295 -0
- pyoq/query/postgres/writes.py +37 -0
- pyoq/query/projections.py +105 -0
- pyoq/query/raw.py +90 -0
- pyoq/query/recursion.py +265 -0
- pyoq/query/rendering/__init__.py +1 -0
- pyoq/query/rendering/expressions.py +913 -0
- pyoq/query/rendering/identifiers.py +40 -0
- pyoq/query/rendering/projections.py +63 -0
- pyoq/query/rendering/queries.py +334 -0
- pyoq/query/rendering/sources.py +66 -0
- pyoq/query/rendering/writes.py +176 -0
- pyoq/query/results.py +459 -0
- pyoq/query/routines.py +196 -0
- pyoq/query/rows.py +156 -0
- pyoq/query/select.py +793 -0
- pyoq/query/select_nodes.py +277 -0
- pyoq/query/sources.py +236 -0
- pyoq/query/sqlite/__init__.py +43 -0
- pyoq/query/sqlite/commands.py +201 -0
- pyoq/query/sqlite/compiler.py +139 -0
- pyoq/query/sqlite/context.py +20 -0
- pyoq/query/sqlite/executor.py +119 -0
- pyoq/query/sqlite/expressions.py +224 -0
- pyoq/query/sqlite/factory.py +32 -0
- pyoq/query/sqlite/health.py +28 -0
- pyoq/query/sqlite/identifiers.py +9 -0
- pyoq/query/sqlite/model.py +73 -0
- pyoq/query/sqlite/operations.py +36 -0
- pyoq/query/sqlite/parameters.py +50 -0
- pyoq/query/sqlite/planning.py +20 -0
- pyoq/query/sqlite/pool.py +50 -0
- pyoq/query/sqlite/streaming.py +13 -0
- pyoq/query/sqlite/transactions.py +274 -0
- pyoq/query/sqlite/writes.py +35 -0
- pyoq/query/statements.py +27 -0
- pyoq/query/values.py +23 -0
- pyoq/query/vendor.py +162 -0
- pyoq/query/windows.py +424 -0
- pyoq/query/write_nodes.py +174 -0
- pyoq/query/writes.py +628 -0
- pyoq/relations/__init__.py +66 -0
- pyoq/relations/batching.py +219 -0
- pyoq/relations/derivation.py +111 -0
- pyoq/relations/fetching.py +355 -0
- pyoq/relations/graph.py +245 -0
- pyoq/relations/loading.py +74 -0
- pyoq/relations/model.py +75 -0
- pyoq/relations/planning.py +206 -0
- pyoq/runtime/__init__.py +9 -0
- pyoq/runtime/kernels.py +25 -0
- pyoq/runtime/python.py +43 -0
- pyoq/runtime/selection.py +73 -0
- pyoq/sanic/__init__.py +32 -0
- pyoq/sanic/scope.py +197 -0
- pyoq/sanic/workers.py +129 -0
- pyoq/schema/__init__.py +108 -0
- pyoq/schema/codec.py +711 -0
- pyoq/schema/models.py +604 -0
- pyoq/schema/mysql/__init__.py +16 -0
- pyoq/schema/mysql/connection.py +73 -0
- pyoq/schema/mysql/dsn.py +72 -0
- pyoq/schema/mysql/records.py +354 -0
- pyoq/schema/mysql/reflection.py +309 -0
- pyoq/schema/mysql/source.py +30 -0
- pyoq/schema/mysql/sql.py +128 -0
- pyoq/schema/mysql/types.py +105 -0
- pyoq/schema/postgres/__init__.py +13 -0
- pyoq/schema/postgres/connection.py +63 -0
- pyoq/schema/postgres/records.py +384 -0
- pyoq/schema/postgres/reflection.py +466 -0
- pyoq/schema/postgres/source.py +30 -0
- pyoq/schema/postgres/sql.py +246 -0
- pyoq/schema/postgres/types.py +98 -0
- pyoq/schema/registry.py +45 -0
- pyoq/schema/source.py +15 -0
- pyoq/schema/sqlite/__init__.py +6 -0
- pyoq/schema/sqlite/connection.py +54 -0
- pyoq/schema/sqlite/records.py +167 -0
- pyoq/schema/sqlite/reflection.py +393 -0
- pyoq/schema/sqlite/source.py +30 -0
- pyoq/schema/sqlite/sql.py +254 -0
- pyoq/schema/sqlite/types.py +74 -0
- pyoq/serving/__init__.py +23 -0
- pyoq/serving/databases.py +107 -0
- pyoq/serving/opening.py +331 -0
- pyoq/snapshots/__init__.py +20 -0
- pyoq/snapshots/drift.py +312 -0
- pyoq/snapshots/files.py +96 -0
- pyoq/snapshots/routing.py +40 -0
- pyoq/snapshots/source.py +33 -0
- pyoq/tracing/__init__.py +5 -0
- pyoq/tracing/spans.py +89 -0
- pyoq/unset.py +14 -0
- pyoq_sql-1.0.2.dist-info/METADATA +3050 -0
- pyoq_sql-1.0.2.dist-info/RECORD +267 -0
- pyoq_sql-1.0.2.dist-info/WHEEL +4 -0
- pyoq_sql-1.0.2.dist-info/entry_points.txt +3 -0
- 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
|
+
)
|