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
pyoq/fetching/nesting.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Building the collection one resolved fetch asks for.
|
|
2
|
+
|
|
3
|
+
A resolved plan says which relation is fetched, how, in what order, how many
|
|
4
|
+
rows of it to keep, and what each of those rows carries. This turns one of
|
|
5
|
+
those into the node a compiler renders, which is the only place that knows
|
|
6
|
+
both what a relation is and what a collection looks like.
|
|
7
|
+
|
|
8
|
+
Nothing here reaches a database. It builds a node, the way every other builder
|
|
9
|
+
in this toolkit does.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
from pyoq.errors import FetchPlanError
|
|
17
|
+
from pyoq.fetching.tables import TableBinding, TableNames
|
|
18
|
+
from pyoq.query.fields import family_for_type
|
|
19
|
+
from pyoq.query.nodes import (
|
|
20
|
+
BinaryNode,
|
|
21
|
+
BinaryOperator,
|
|
22
|
+
CollectionEntry,
|
|
23
|
+
CollectionJoinNode,
|
|
24
|
+
CollectionNode,
|
|
25
|
+
CollectionOrderNode,
|
|
26
|
+
VariadicNode,
|
|
27
|
+
VariadicOperator,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from pyoq.query.nodes import ExpressionNode, FieldNode
|
|
32
|
+
from pyoq.relations.fetching import FetchJoin
|
|
33
|
+
from pyoq.relations.model import TypedRelation
|
|
34
|
+
from pyoq.relations.planning import ResolvedFetch
|
|
35
|
+
from pyoq.schema.models import Identifier
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def collection_for(
|
|
39
|
+
fetch: ResolvedFetch,
|
|
40
|
+
carrying: tuple[Identifier, ...],
|
|
41
|
+
/,
|
|
42
|
+
*,
|
|
43
|
+
parent: TableBinding | None = None,
|
|
44
|
+
names: TableNames | None = None,
|
|
45
|
+
) -> CollectionNode:
|
|
46
|
+
"""The collection a resolved fetch describes, as a node a dialect renders.
|
|
47
|
+
|
|
48
|
+
``carrying`` names the columns of the related table each row holds. They
|
|
49
|
+
are given rather than derived because a caller asks for the columns it
|
|
50
|
+
wants, and a collection that carried every column would read more than was
|
|
51
|
+
asked for.
|
|
52
|
+
|
|
53
|
+
``parent`` is the appearance the collection correlates to, and ``names``
|
|
54
|
+
hands out the appearances it reads itself. A collection may read the table
|
|
55
|
+
its parent is reading, so both sides need names of their own or the
|
|
56
|
+
correlation compares a table with itself and gathers nothing.
|
|
57
|
+
"""
|
|
58
|
+
if not carrying:
|
|
59
|
+
message = (
|
|
60
|
+
"a collection carries at least one column of "
|
|
61
|
+
f"{fetch.relation.target.table.name.value!r}"
|
|
62
|
+
)
|
|
63
|
+
raise FetchPlanError(message)
|
|
64
|
+
source = parent or TableBinding(fetch.relation.source.table)
|
|
65
|
+
naming = names or TableNames(_named_by(fetch, source))
|
|
66
|
+
related = naming.next(fetch.relation.target.table)
|
|
67
|
+
entries = tuple(
|
|
68
|
+
CollectionEntry(name.value, _field(related, name)) for name in carrying
|
|
69
|
+
)
|
|
70
|
+
bound = tuple(
|
|
71
|
+
(join, naming.next(join.relation.target.table)) for join in fetch.joins
|
|
72
|
+
)
|
|
73
|
+
joined = tuple(_join(join, binding, related) for join, binding in bound)
|
|
74
|
+
carried = tuple(
|
|
75
|
+
CollectionEntry(_joined_key(binding, name), _field(binding, name))
|
|
76
|
+
for join, binding in bound
|
|
77
|
+
for name in join.carrying
|
|
78
|
+
)
|
|
79
|
+
return CollectionNode(
|
|
80
|
+
table=related.table.name.value,
|
|
81
|
+
entries=entries + carried,
|
|
82
|
+
condition=_correlation(fetch.relation, source, related),
|
|
83
|
+
orders=tuple(
|
|
84
|
+
CollectionOrderNode(_field(related, column), descending=descending)
|
|
85
|
+
for column, descending in _ordered(fetch)
|
|
86
|
+
),
|
|
87
|
+
schema=_named(related.table.schema),
|
|
88
|
+
catalog=_named(related.table.catalog),
|
|
89
|
+
alias=related.alias,
|
|
90
|
+
joins=joined,
|
|
91
|
+
limit=fetch.limit,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _ordered(fetch: ResolvedFetch) -> tuple[tuple[Identifier, bool], ...]:
|
|
96
|
+
"""The order terms, each of which has to name a column to be built.
|
|
97
|
+
|
|
98
|
+
A term ordered by an expression carries the name a plan reports it under
|
|
99
|
+
and not the expression itself, so nothing here can write one out. That is
|
|
100
|
+
a plan a caller resolves themselves rather than one this builds wrongly.
|
|
101
|
+
"""
|
|
102
|
+
written: list[tuple[Identifier, bool]] = []
|
|
103
|
+
for term in fetch.order_by:
|
|
104
|
+
if term.column is None:
|
|
105
|
+
message = (
|
|
106
|
+
f"the collection of {fetch.relation.target.table.name.value!r} "
|
|
107
|
+
f"is ordered by {term.name!r}, which is an expression rather "
|
|
108
|
+
"than a column, so this cannot build it"
|
|
109
|
+
)
|
|
110
|
+
raise FetchPlanError(message)
|
|
111
|
+
written.append((term.column, term.descending))
|
|
112
|
+
return tuple(written)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _named_by(fetch: ResolvedFetch, source: TableBinding) -> frozenset[str]:
|
|
116
|
+
"""What the tables of this collection are already called.
|
|
117
|
+
|
|
118
|
+
A caller who builds a collection on its own hands over no schema, so what
|
|
119
|
+
can be reserved is what the fetch itself names: the table it correlates
|
|
120
|
+
to, the table it reads, and the tables it reads alongside. A caller with a
|
|
121
|
+
schema passes the naming in, and that one reserves all of it.
|
|
122
|
+
"""
|
|
123
|
+
return frozenset(
|
|
124
|
+
{source.qualifier, fetch.relation.target.table.name.value}
|
|
125
|
+
| {join.relation.target.table.name.value for join in fetch.joins}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _joined_key(binding: TableBinding, column: Identifier) -> str:
|
|
130
|
+
"""What a joined column is called in a row of the collection.
|
|
131
|
+
|
|
132
|
+
Qualified by the appearance it came from rather than by its table, because
|
|
133
|
+
a collection may join one table twice and a row cannot carry one key
|
|
134
|
+
twice. This is how the collection encodes its row, not a name a caller
|
|
135
|
+
reads: what a caller reads is the row the executor hands back.
|
|
136
|
+
"""
|
|
137
|
+
return f"{binding.qualifier}.{column.value}"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _join(
|
|
141
|
+
join: FetchJoin,
|
|
142
|
+
binding: TableBinding,
|
|
143
|
+
parent: TableBinding,
|
|
144
|
+
) -> CollectionJoinNode:
|
|
145
|
+
return CollectionJoinNode(
|
|
146
|
+
table=binding.table.name.value,
|
|
147
|
+
condition=_correlation(join.relation, parent, binding),
|
|
148
|
+
schema=_named(binding.table.schema),
|
|
149
|
+
catalog=_named(binding.table.catalog),
|
|
150
|
+
alias=binding.alias,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _correlation(
|
|
155
|
+
relation: TypedRelation,
|
|
156
|
+
source: TableBinding,
|
|
157
|
+
target: TableBinding,
|
|
158
|
+
) -> ExpressionNode:
|
|
159
|
+
"""What ties one appearance of a relation to the other, column by column."""
|
|
160
|
+
matched = tuple(
|
|
161
|
+
BinaryNode(
|
|
162
|
+
BinaryOperator.EQUAL,
|
|
163
|
+
_field(target, target_column),
|
|
164
|
+
_field(source, source_column),
|
|
165
|
+
)
|
|
166
|
+
for source_column, target_column in zip(
|
|
167
|
+
relation.source.columns, relation.target.columns, strict=True
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
if len(matched) == 1:
|
|
171
|
+
return matched[0]
|
|
172
|
+
return VariadicNode(VariadicOperator.AND, matched)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _field(binding: TableBinding, column: Identifier) -> FieldNode:
|
|
176
|
+
return binding.field(column, family_for_type(object))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _named(value: Identifier | None) -> str | None:
|
|
180
|
+
return None if value is None else value.value
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
__all__ = ("collection_for",)
|