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,263 @@
|
|
|
1
|
+
"""Holding a statement to its policies before any SQL exists.
|
|
2
|
+
|
|
3
|
+
Every way a statement can reach a table is walked: the table it selects from,
|
|
4
|
+
the tables it joins, the queries inside its subqueries and common tables, the
|
|
5
|
+
halves of a set operation, the table a collection gathers, and the target of a
|
|
6
|
+
write. A rule that only looked at the first of those would be walked around by
|
|
7
|
+
any of the rest.
|
|
8
|
+
|
|
9
|
+
Raw SQL is the one thing that cannot be walked, because there is no node to
|
|
10
|
+
read. It is refused unless a policy set says otherwise, which is a decision a
|
|
11
|
+
project makes once rather than a hole that is always open.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import replace
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
from pyoq.errors import PolicyViolationError
|
|
20
|
+
from pyoq.policies.model import Policies, TableReference
|
|
21
|
+
from pyoq.query.nodes import (
|
|
22
|
+
AggregateNode,
|
|
23
|
+
BinaryNode,
|
|
24
|
+
BoundValueNode,
|
|
25
|
+
CollectionNode,
|
|
26
|
+
ExtractNode,
|
|
27
|
+
FunctionNode,
|
|
28
|
+
RawNode,
|
|
29
|
+
UnaryNode,
|
|
30
|
+
VariadicNode,
|
|
31
|
+
VariadicOperator,
|
|
32
|
+
)
|
|
33
|
+
from pyoq.query.select_nodes import (
|
|
34
|
+
JoinNode,
|
|
35
|
+
SelectNode,
|
|
36
|
+
SetNode,
|
|
37
|
+
SubquerySourceNode,
|
|
38
|
+
TableSourceNode,
|
|
39
|
+
)
|
|
40
|
+
from pyoq.query.write_nodes import DeleteNode, InsertNode, UpdateNode
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from pyoq.query.nodes import ExpressionNode
|
|
44
|
+
from pyoq.query.select_nodes import SourceNode
|
|
45
|
+
from pyoq.query.write_nodes import WriteTargetNode
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def govern(node: object, policies: Policies, /, *, raw_sql: bool = False) -> object:
|
|
49
|
+
"""The statement a policy set allows, built from the one it was given."""
|
|
50
|
+
if not policies:
|
|
51
|
+
return node
|
|
52
|
+
return _Governing(policies, raw_sql=raw_sql).statement(node)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class _Governing:
|
|
56
|
+
"""One pass over one statement, carrying the policies it is held to."""
|
|
57
|
+
|
|
58
|
+
__slots__ = ("_policies", "_raw_sql")
|
|
59
|
+
|
|
60
|
+
def __init__(self, policies: Policies, *, raw_sql: bool) -> None:
|
|
61
|
+
self._policies = policies
|
|
62
|
+
self._raw_sql = raw_sql
|
|
63
|
+
|
|
64
|
+
def statement(self, node: object) -> object:
|
|
65
|
+
if isinstance(node, (SelectNode, SetNode)):
|
|
66
|
+
return self.query(node)
|
|
67
|
+
if isinstance(node, InsertNode):
|
|
68
|
+
return self._insert(node)
|
|
69
|
+
if isinstance(node, UpdateNode):
|
|
70
|
+
return self._scoped_write(node)
|
|
71
|
+
if isinstance(node, DeleteNode):
|
|
72
|
+
return self._scoped_write(node)
|
|
73
|
+
return node
|
|
74
|
+
|
|
75
|
+
def query(self, node: SelectNode | SetNode) -> SelectNode | SetNode:
|
|
76
|
+
if isinstance(node, SetNode):
|
|
77
|
+
return replace(
|
|
78
|
+
node,
|
|
79
|
+
left=self.query(node.left),
|
|
80
|
+
right=self.query(node.right),
|
|
81
|
+
)
|
|
82
|
+
return self._select(node)
|
|
83
|
+
|
|
84
|
+
def _select(self, node: SelectNode) -> SelectNode:
|
|
85
|
+
common = tuple(
|
|
86
|
+
replace(table, query=self.query(table.query))
|
|
87
|
+
for table in node.common_tables
|
|
88
|
+
)
|
|
89
|
+
source = self._source(node.source)
|
|
90
|
+
joins = tuple(
|
|
91
|
+
replace(join, source=self._joined(join.source)) for join in node.joins
|
|
92
|
+
)
|
|
93
|
+
tables = self._tables_of(node.source, node.joins)
|
|
94
|
+
condition = _all_of(
|
|
95
|
+
(
|
|
96
|
+
*(() if node.condition is None else (self.expression(node.condition),)),
|
|
97
|
+
*self._conditions(tables),
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
return replace(
|
|
101
|
+
node,
|
|
102
|
+
common_tables=common,
|
|
103
|
+
source=source,
|
|
104
|
+
joins=joins,
|
|
105
|
+
condition=condition,
|
|
106
|
+
projections=tuple(
|
|
107
|
+
replace(projection, expression=self.expression(projection.expression))
|
|
108
|
+
for projection in node.projections
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def _source(self, source: SourceNode | None) -> SourceNode | None:
|
|
113
|
+
if isinstance(source, TableSourceNode):
|
|
114
|
+
self._require_allowed(_reference_of(source))
|
|
115
|
+
return source
|
|
116
|
+
if isinstance(source, SubquerySourceNode):
|
|
117
|
+
return replace(source, query=self.query(source.query))
|
|
118
|
+
return source
|
|
119
|
+
|
|
120
|
+
def _joined(self, source: SourceNode) -> SourceNode:
|
|
121
|
+
"""A join always has a source, so it always gets one back."""
|
|
122
|
+
governed = self._source(source)
|
|
123
|
+
return source if governed is None else governed
|
|
124
|
+
|
|
125
|
+
def _tables_of(
|
|
126
|
+
self,
|
|
127
|
+
source: SourceNode | None,
|
|
128
|
+
joins: tuple[JoinNode, ...],
|
|
129
|
+
) -> tuple[TableReference, ...]:
|
|
130
|
+
"""Only the tables named here; a subquery answers for its own."""
|
|
131
|
+
sources = (source, *(join.source for join in joins))
|
|
132
|
+
return tuple(
|
|
133
|
+
_reference_of(one) for one in sources if isinstance(one, TableSourceNode)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def _conditions(
|
|
137
|
+
self,
|
|
138
|
+
tables: tuple[TableReference, ...],
|
|
139
|
+
) -> tuple[ExpressionNode, ...]:
|
|
140
|
+
return tuple(
|
|
141
|
+
condition
|
|
142
|
+
for table in tables
|
|
143
|
+
for condition in self._policies.conditions(table)
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def _insert(self, node: InsertNode) -> InsertNode:
|
|
147
|
+
table = _target_reference(node.target)
|
|
148
|
+
self._require_allowed(table)
|
|
149
|
+
assignments = self._policies.assignments(table)
|
|
150
|
+
if not assignments:
|
|
151
|
+
return node
|
|
152
|
+
return _with_assignments(node, assignments)
|
|
153
|
+
|
|
154
|
+
def _scoped_write(self, node: UpdateNode | DeleteNode) -> UpdateNode | DeleteNode:
|
|
155
|
+
table = _target_reference(node.target)
|
|
156
|
+
self._require_allowed(table)
|
|
157
|
+
conditions = self._policies.conditions(table)
|
|
158
|
+
if not conditions:
|
|
159
|
+
return node
|
|
160
|
+
existing = () if node.condition is None else (self.expression(node.condition),)
|
|
161
|
+
governed = _all_of((*existing, *conditions))
|
|
162
|
+
# A statement that said it meant every row now means every row a policy
|
|
163
|
+
# admits, which is what a policy is for.
|
|
164
|
+
return replace(node, condition=governed, unconditional=False)
|
|
165
|
+
|
|
166
|
+
def expression(self, node: ExpressionNode) -> ExpressionNode:
|
|
167
|
+
"""A collection reads a table of its own, and raw SQL reads anything."""
|
|
168
|
+
if isinstance(node, RawNode):
|
|
169
|
+
self._require_raw_sql()
|
|
170
|
+
return node
|
|
171
|
+
if isinstance(node, CollectionNode):
|
|
172
|
+
return self._collection(node)
|
|
173
|
+
return _rebuilt(node, self.expression)
|
|
174
|
+
|
|
175
|
+
def _collection(self, node: CollectionNode) -> CollectionNode:
|
|
176
|
+
table = TableReference(node.table, node.schema, node.catalog, node.alias)
|
|
177
|
+
self._require_allowed(table)
|
|
178
|
+
conditions = self._policies.conditions(table)
|
|
179
|
+
governed = _all_of((self.expression(node.condition), *conditions))
|
|
180
|
+
if governed is None: # pragma: no cover - a collection always correlates
|
|
181
|
+
return node
|
|
182
|
+
return replace(node, condition=governed)
|
|
183
|
+
|
|
184
|
+
def _require_allowed(self, table: TableReference) -> None:
|
|
185
|
+
reason = self._policies.refusal(table)
|
|
186
|
+
if reason is not None:
|
|
187
|
+
raise PolicyViolationError(reason)
|
|
188
|
+
|
|
189
|
+
def _require_raw_sql(self) -> None:
|
|
190
|
+
if self._raw_sql:
|
|
191
|
+
return
|
|
192
|
+
message = (
|
|
193
|
+
"raw SQL cannot be held to a policy, because there is no statement "
|
|
194
|
+
"to read; allow it deliberately if this connection may run it"
|
|
195
|
+
)
|
|
196
|
+
raise PolicyViolationError(message)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _reference_of(source: TableSourceNode) -> TableReference:
|
|
200
|
+
return TableReference(source.name, source.schema, source.catalog, source.alias)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _target_reference(target: WriteTargetNode) -> TableReference:
|
|
204
|
+
return TableReference(target.name, target.schema, target.catalog)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _all_of(conditions: tuple[ExpressionNode, ...]) -> ExpressionNode | None:
|
|
208
|
+
if not conditions:
|
|
209
|
+
return None
|
|
210
|
+
if len(conditions) == 1:
|
|
211
|
+
return conditions[0]
|
|
212
|
+
return VariadicNode(VariadicOperator.AND, conditions)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _with_assignments(
|
|
216
|
+
node: InsertNode,
|
|
217
|
+
assignments: tuple[tuple[str, object], ...],
|
|
218
|
+
) -> InsertNode:
|
|
219
|
+
"""Set what a policy requires, replacing whatever a caller gave for it."""
|
|
220
|
+
from pyoq.policies.model import family_for_value
|
|
221
|
+
|
|
222
|
+
columns = list(node.columns)
|
|
223
|
+
rows = [list(row) for row in node.rows]
|
|
224
|
+
for name, value in assignments:
|
|
225
|
+
bound = BoundValueNode(value, family_for_value(value))
|
|
226
|
+
if name in columns:
|
|
227
|
+
index = columns.index(name)
|
|
228
|
+
for row in rows:
|
|
229
|
+
row[index] = bound
|
|
230
|
+
continue
|
|
231
|
+
columns.append(name)
|
|
232
|
+
for row in rows:
|
|
233
|
+
row.append(bound)
|
|
234
|
+
return replace(
|
|
235
|
+
node,
|
|
236
|
+
columns=tuple(columns),
|
|
237
|
+
rows=tuple(tuple(row) for row in rows),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _rebuilt(
|
|
242
|
+
node: ExpressionNode,
|
|
243
|
+
visit: object,
|
|
244
|
+
) -> ExpressionNode:
|
|
245
|
+
"""Every expression that holds another one, rebuilt around the visited."""
|
|
246
|
+
if not callable(visit): # pragma: no cover - called with a method
|
|
247
|
+
return node
|
|
248
|
+
if isinstance(node, UnaryNode):
|
|
249
|
+
return replace(node, operand=visit(node.operand))
|
|
250
|
+
if isinstance(node, BinaryNode):
|
|
251
|
+
return replace(node, left=visit(node.left), right=visit(node.right))
|
|
252
|
+
if isinstance(node, VariadicNode):
|
|
253
|
+
return replace(node, operands=tuple(visit(one) for one in node.operands))
|
|
254
|
+
if isinstance(node, FunctionNode):
|
|
255
|
+
return replace(node, arguments=tuple(visit(one) for one in node.arguments))
|
|
256
|
+
if isinstance(node, AggregateNode):
|
|
257
|
+
return replace(node, arguments=tuple(visit(one) for one in node.arguments))
|
|
258
|
+
if isinstance(node, ExtractNode):
|
|
259
|
+
return replace(node, expression=visit(node.expression))
|
|
260
|
+
return node
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
__all__ = ("govern",)
|
pyoq/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
pyoq/query/__init__.py
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Public typed query construction interface."""
|
|
2
|
+
|
|
3
|
+
from pyoq.query.aggregates import (
|
|
4
|
+
AggregateExpression,
|
|
5
|
+
average,
|
|
6
|
+
count,
|
|
7
|
+
json_agg,
|
|
8
|
+
json_object_agg,
|
|
9
|
+
maximum,
|
|
10
|
+
minimum,
|
|
11
|
+
sum_,
|
|
12
|
+
)
|
|
13
|
+
from pyoq.query.arrays import array_of
|
|
14
|
+
from pyoq.query.casts import cast
|
|
15
|
+
from pyoq.query.choices import (
|
|
16
|
+
CaseBranches,
|
|
17
|
+
CaseBuilder,
|
|
18
|
+
case,
|
|
19
|
+
coalesce,
|
|
20
|
+
greatest,
|
|
21
|
+
least,
|
|
22
|
+
nullif,
|
|
23
|
+
)
|
|
24
|
+
from pyoq.query.documents import json_array, json_object
|
|
25
|
+
from pyoq.query.expressions import ComputedExpression, Condition, Expression
|
|
26
|
+
from pyoq.query.fields import Field, field
|
|
27
|
+
from pyoq.query.nodes import (
|
|
28
|
+
AggregateName,
|
|
29
|
+
AggregateNode,
|
|
30
|
+
BinaryNode,
|
|
31
|
+
BinaryOperator,
|
|
32
|
+
BoundValueNode,
|
|
33
|
+
CaseNode,
|
|
34
|
+
CastNode,
|
|
35
|
+
DatePart,
|
|
36
|
+
ExpressionNode,
|
|
37
|
+
ExtractNode,
|
|
38
|
+
FieldNode,
|
|
39
|
+
FunctionName,
|
|
40
|
+
FunctionNode,
|
|
41
|
+
RawNode,
|
|
42
|
+
RowValueNode,
|
|
43
|
+
ScalarFamily,
|
|
44
|
+
UnaryNode,
|
|
45
|
+
UnaryOperator,
|
|
46
|
+
VariadicNode,
|
|
47
|
+
VariadicOperator,
|
|
48
|
+
VendorFunctionNode,
|
|
49
|
+
WindowFunctionNode,
|
|
50
|
+
WindowName,
|
|
51
|
+
WindowNode,
|
|
52
|
+
)
|
|
53
|
+
from pyoq.query.projections import (
|
|
54
|
+
OrderTerm,
|
|
55
|
+
Projection,
|
|
56
|
+
alias,
|
|
57
|
+
ascending,
|
|
58
|
+
descending,
|
|
59
|
+
)
|
|
60
|
+
from pyoq.query.raw import NodeProvider, raw, raw_condition
|
|
61
|
+
from pyoq.query.recursion import (
|
|
62
|
+
RecursiveColumn,
|
|
63
|
+
RecursiveTable,
|
|
64
|
+
column,
|
|
65
|
+
recursive_table,
|
|
66
|
+
)
|
|
67
|
+
from pyoq.query.routines import Call, call, routine_output
|
|
68
|
+
from pyoq.query.rows import RowValue, row
|
|
69
|
+
from pyoq.query.select import (
|
|
70
|
+
JoinStep,
|
|
71
|
+
Query,
|
|
72
|
+
SelectQuery,
|
|
73
|
+
SemiJoinStep,
|
|
74
|
+
SetQuery,
|
|
75
|
+
exists,
|
|
76
|
+
not_exists,
|
|
77
|
+
select,
|
|
78
|
+
select_from,
|
|
79
|
+
)
|
|
80
|
+
from pyoq.query.select_nodes import (
|
|
81
|
+
CommonTableNode,
|
|
82
|
+
CommonTableSourceNode,
|
|
83
|
+
JoinKind,
|
|
84
|
+
JoinNode,
|
|
85
|
+
NullOrder,
|
|
86
|
+
OrderDirection,
|
|
87
|
+
OrderNode,
|
|
88
|
+
ProjectionNode,
|
|
89
|
+
SelectNode,
|
|
90
|
+
SetNode,
|
|
91
|
+
SetOperator,
|
|
92
|
+
SubquerySourceNode,
|
|
93
|
+
TableSourceNode,
|
|
94
|
+
)
|
|
95
|
+
from pyoq.query.sources import (
|
|
96
|
+
ELEMENT_COLUMN,
|
|
97
|
+
CommonTable,
|
|
98
|
+
Source,
|
|
99
|
+
Subquery,
|
|
100
|
+
common_table,
|
|
101
|
+
json_elements,
|
|
102
|
+
table_source,
|
|
103
|
+
)
|
|
104
|
+
from pyoq.query.values import BoundValue, bind
|
|
105
|
+
from pyoq.query.vendor import (
|
|
106
|
+
VendorFunction,
|
|
107
|
+
VendorPredicate,
|
|
108
|
+
vendor_function,
|
|
109
|
+
vendor_predicate,
|
|
110
|
+
)
|
|
111
|
+
from pyoq.query.windows import (
|
|
112
|
+
FrameEdge,
|
|
113
|
+
FrameExclusion,
|
|
114
|
+
Window,
|
|
115
|
+
WindowFunction,
|
|
116
|
+
WindowSpecification,
|
|
117
|
+
cume_dist,
|
|
118
|
+
current_row,
|
|
119
|
+
dense_rank,
|
|
120
|
+
first_value,
|
|
121
|
+
following,
|
|
122
|
+
lag,
|
|
123
|
+
last_value,
|
|
124
|
+
lead,
|
|
125
|
+
named_window,
|
|
126
|
+
nth_value,
|
|
127
|
+
ntile,
|
|
128
|
+
percent_rank,
|
|
129
|
+
preceding,
|
|
130
|
+
rank,
|
|
131
|
+
row_number,
|
|
132
|
+
unbounded_following,
|
|
133
|
+
unbounded_preceding,
|
|
134
|
+
)
|
|
135
|
+
from pyoq.query.write_nodes import (
|
|
136
|
+
AssignmentNode,
|
|
137
|
+
ConflictAction,
|
|
138
|
+
ConflictNode,
|
|
139
|
+
DeleteNode,
|
|
140
|
+
InsertNode,
|
|
141
|
+
UpdateNode,
|
|
142
|
+
WriteNode,
|
|
143
|
+
WriteTargetNode,
|
|
144
|
+
)
|
|
145
|
+
from pyoq.query.writes import (
|
|
146
|
+
ColumnBinding,
|
|
147
|
+
ColumnReference,
|
|
148
|
+
ConflictUpdate,
|
|
149
|
+
DeleteStatement,
|
|
150
|
+
InsertStatement,
|
|
151
|
+
MutationStatement,
|
|
152
|
+
ReturningStatement,
|
|
153
|
+
ScopedStatement,
|
|
154
|
+
UpdateStatement,
|
|
155
|
+
WriteColumn,
|
|
156
|
+
WriteStatement,
|
|
157
|
+
delete_from,
|
|
158
|
+
excluded,
|
|
159
|
+
insert_into,
|
|
160
|
+
update,
|
|
161
|
+
)
|
|
162
|
+
from pyoq.schema.models import JsonScalar, JsonValue
|
|
163
|
+
|
|
164
|
+
__all__ = (
|
|
165
|
+
"ELEMENT_COLUMN",
|
|
166
|
+
"AggregateExpression",
|
|
167
|
+
"AggregateName",
|
|
168
|
+
"AggregateNode",
|
|
169
|
+
"AssignmentNode",
|
|
170
|
+
"BinaryNode",
|
|
171
|
+
"BinaryOperator",
|
|
172
|
+
"BoundValue",
|
|
173
|
+
"BoundValueNode",
|
|
174
|
+
"Call",
|
|
175
|
+
"CaseBranches",
|
|
176
|
+
"CaseBuilder",
|
|
177
|
+
"CaseNode",
|
|
178
|
+
"CastNode",
|
|
179
|
+
"ColumnBinding",
|
|
180
|
+
"ColumnReference",
|
|
181
|
+
"CommonTable",
|
|
182
|
+
"CommonTableNode",
|
|
183
|
+
"CommonTableSourceNode",
|
|
184
|
+
"ComputedExpression",
|
|
185
|
+
"Condition",
|
|
186
|
+
"ConflictAction",
|
|
187
|
+
"ConflictNode",
|
|
188
|
+
"ConflictUpdate",
|
|
189
|
+
"DatePart",
|
|
190
|
+
"DeleteNode",
|
|
191
|
+
"DeleteStatement",
|
|
192
|
+
"Expression",
|
|
193
|
+
"ExpressionNode",
|
|
194
|
+
"ExtractNode",
|
|
195
|
+
"Field",
|
|
196
|
+
"FieldNode",
|
|
197
|
+
"FrameEdge",
|
|
198
|
+
"FrameExclusion",
|
|
199
|
+
"FunctionName",
|
|
200
|
+
"FunctionNode",
|
|
201
|
+
"InsertNode",
|
|
202
|
+
"InsertStatement",
|
|
203
|
+
"JoinKind",
|
|
204
|
+
"JoinNode",
|
|
205
|
+
"JoinStep",
|
|
206
|
+
"JsonScalar",
|
|
207
|
+
"JsonValue",
|
|
208
|
+
"MutationStatement",
|
|
209
|
+
"NodeProvider",
|
|
210
|
+
"NullOrder",
|
|
211
|
+
"OrderDirection",
|
|
212
|
+
"OrderNode",
|
|
213
|
+
"OrderTerm",
|
|
214
|
+
"Projection",
|
|
215
|
+
"ProjectionNode",
|
|
216
|
+
"Query",
|
|
217
|
+
"RawNode",
|
|
218
|
+
"RecursiveColumn",
|
|
219
|
+
"RecursiveTable",
|
|
220
|
+
"ReturningStatement",
|
|
221
|
+
"RowValue",
|
|
222
|
+
"RowValueNode",
|
|
223
|
+
"ScalarFamily",
|
|
224
|
+
"ScopedStatement",
|
|
225
|
+
"SelectNode",
|
|
226
|
+
"SelectQuery",
|
|
227
|
+
"SemiJoinStep",
|
|
228
|
+
"SetNode",
|
|
229
|
+
"SetOperator",
|
|
230
|
+
"SetQuery",
|
|
231
|
+
"Source",
|
|
232
|
+
"Subquery",
|
|
233
|
+
"SubquerySourceNode",
|
|
234
|
+
"TableSourceNode",
|
|
235
|
+
"UnaryNode",
|
|
236
|
+
"UnaryOperator",
|
|
237
|
+
"UpdateNode",
|
|
238
|
+
"UpdateStatement",
|
|
239
|
+
"VariadicNode",
|
|
240
|
+
"VariadicOperator",
|
|
241
|
+
"VendorFunction",
|
|
242
|
+
"VendorFunctionNode",
|
|
243
|
+
"VendorPredicate",
|
|
244
|
+
"Window",
|
|
245
|
+
"WindowFunction",
|
|
246
|
+
"WindowFunctionNode",
|
|
247
|
+
"WindowName",
|
|
248
|
+
"WindowNode",
|
|
249
|
+
"WindowSpecification",
|
|
250
|
+
"WriteColumn",
|
|
251
|
+
"WriteNode",
|
|
252
|
+
"WriteStatement",
|
|
253
|
+
"WriteTargetNode",
|
|
254
|
+
"alias",
|
|
255
|
+
"array_of",
|
|
256
|
+
"ascending",
|
|
257
|
+
"average",
|
|
258
|
+
"bind",
|
|
259
|
+
"call",
|
|
260
|
+
"case",
|
|
261
|
+
"cast",
|
|
262
|
+
"coalesce",
|
|
263
|
+
"column",
|
|
264
|
+
"common_table",
|
|
265
|
+
"count",
|
|
266
|
+
"cume_dist",
|
|
267
|
+
"current_row",
|
|
268
|
+
"delete_from",
|
|
269
|
+
"dense_rank",
|
|
270
|
+
"descending",
|
|
271
|
+
"excluded",
|
|
272
|
+
"exists",
|
|
273
|
+
"field",
|
|
274
|
+
"first_value",
|
|
275
|
+
"following",
|
|
276
|
+
"greatest",
|
|
277
|
+
"insert_into",
|
|
278
|
+
"json_agg",
|
|
279
|
+
"json_array",
|
|
280
|
+
"json_elements",
|
|
281
|
+
"json_object",
|
|
282
|
+
"json_object_agg",
|
|
283
|
+
"lag",
|
|
284
|
+
"last_value",
|
|
285
|
+
"lead",
|
|
286
|
+
"least",
|
|
287
|
+
"maximum",
|
|
288
|
+
"minimum",
|
|
289
|
+
"named_window",
|
|
290
|
+
"not_exists",
|
|
291
|
+
"nth_value",
|
|
292
|
+
"ntile",
|
|
293
|
+
"nullif",
|
|
294
|
+
"percent_rank",
|
|
295
|
+
"preceding",
|
|
296
|
+
"rank",
|
|
297
|
+
"raw",
|
|
298
|
+
"raw_condition",
|
|
299
|
+
"recursive_table",
|
|
300
|
+
"routine_output",
|
|
301
|
+
"row",
|
|
302
|
+
"row_number",
|
|
303
|
+
"select",
|
|
304
|
+
"select_from",
|
|
305
|
+
"sum_",
|
|
306
|
+
"table_source",
|
|
307
|
+
"unbounded_following",
|
|
308
|
+
"unbounded_preceding",
|
|
309
|
+
"update",
|
|
310
|
+
"vendor_function",
|
|
311
|
+
"vendor_predicate",
|
|
312
|
+
)
|