pyoq-sql 1.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. pyoq/__init__.py +10 -0
  2. pyoq/__main__.py +5 -0
  3. pyoq/_native.pyi +5 -0
  4. pyoq/cli/__init__.py +5 -0
  5. pyoq/cli/commands.py +270 -0
  6. pyoq/cli/defaults.py +98 -0
  7. pyoq/cli/services.py +97 -0
  8. pyoq/config/__init__.py +31 -0
  9. pyoq/config/connection.py +161 -0
  10. pyoq/config/loader.py +289 -0
  11. pyoq/config/models.py +245 -0
  12. pyoq/config/values.py +142 -0
  13. pyoq/descriptors.py +165 -0
  14. pyoq/diagnostics/__init__.py +68 -0
  15. pyoq/diagnostics/budget.py +136 -0
  16. pyoq/diagnostics/events.py +137 -0
  17. pyoq/diagnostics/fingerprint.py +267 -0
  18. pyoq/diagnostics/instrumented.py +237 -0
  19. pyoq/diagnostics/metrics.py +61 -0
  20. pyoq/diagnostics/observation.py +227 -0
  21. pyoq/diagnostics/scoped.py +103 -0
  22. pyoq/django/__init__.py +15 -0
  23. pyoq/django/apps.py +17 -0
  24. pyoq/django/execution.py +317 -0
  25. pyoq/django/generation.py +59 -0
  26. pyoq/django/management/__init__.py +0 -0
  27. pyoq/django/management/commands/__init__.py +0 -0
  28. pyoq/django/management/commands/makemigrations.py +53 -0
  29. pyoq/django/management/commands/pyoq_codegen.py +75 -0
  30. pyoq/django/parameters.py +101 -0
  31. pyoq/django/schema.py +379 -0
  32. pyoq/django/settings.py +87 -0
  33. pyoq/django/timeouts.py +105 -0
  34. pyoq/dsl/__init__.py +64 -0
  35. pyoq/dsl/aio/__init__.py +31 -0
  36. pyoq/dsl/aio/context.py +295 -0
  37. pyoq/dsl/aio/queries.py +335 -0
  38. pyoq/dsl/aio/writes.py +368 -0
  39. pyoq/dsl/context.py +326 -0
  40. pyoq/dsl/entry.py +37 -0
  41. pyoq/dsl/labels.py +36 -0
  42. pyoq/dsl/queries.py +339 -0
  43. pyoq/dsl/result.py +164 -0
  44. pyoq/dsl/writes.py +360 -0
  45. pyoq/errors.py +317 -0
  46. pyoq/fastapi/__init__.py +32 -0
  47. pyoq/fastapi/dependencies.py +167 -0
  48. pyoq/fastapi/lifespan.py +119 -0
  49. pyoq/fetching/__init__.py +55 -0
  50. pyoq/fetching/collections.py +136 -0
  51. pyoq/fetching/execution.py +587 -0
  52. pyoq/fetching/joined.py +79 -0
  53. pyoq/fetching/nesting.py +183 -0
  54. pyoq/fetching/plans.py +541 -0
  55. pyoq/fetching/select_in.py +149 -0
  56. pyoq/fetching/tables.py +110 -0
  57. pyoq/generation/__init__.py +54 -0
  58. pyoq/generation/cleanup.py +44 -0
  59. pyoq/generation/contracts.py +248 -0
  60. pyoq/generation/drift.py +169 -0
  61. pyoq/generation/lock.py +33 -0
  62. pyoq/generation/manifest.py +114 -0
  63. pyoq/generation/model.py +1001 -0
  64. pyoq/generation/pipeline.py +119 -0
  65. pyoq/generation/rendering/__init__.py +5 -0
  66. pyoq/generation/rendering/domains.py +51 -0
  67. pyoq/generation/rendering/enums.py +29 -0
  68. pyoq/generation/rendering/exports.py +70 -0
  69. pyoq/generation/rendering/imports.py +63 -0
  70. pyoq/generation/rendering/package.py +56 -0
  71. pyoq/generation/rendering/relations.py +133 -0
  72. pyoq/generation/rendering/routines.py +396 -0
  73. pyoq/generation/rendering/rows.py +79 -0
  74. pyoq/generation/rendering/source.py +121 -0
  75. pyoq/generation/rendering/tables.py +300 -0
  76. pyoq/generation/rendering/writes.py +514 -0
  77. pyoq/generation/validation.py +27 -0
  78. pyoq/generation/writer.py +184 -0
  79. pyoq/hydration/__init__.py +24 -0
  80. pyoq/hydration/engine.py +155 -0
  81. pyoq/hydration/identity.py +194 -0
  82. pyoq/hydration/plan.py +116 -0
  83. pyoq/migrations/__init__.py +9 -0
  84. pyoq/migrations/alembic.py +106 -0
  85. pyoq/migrations/hooks.py +75 -0
  86. pyoq/naming.py +261 -0
  87. pyoq/policies/__init__.py +47 -0
  88. pyoq/policies/bypass.py +122 -0
  89. pyoq/policies/governed.py +430 -0
  90. pyoq/policies/model.py +242 -0
  91. pyoq/policies/rewriting.py +263 -0
  92. pyoq/py.typed +1 -0
  93. pyoq/query/__init__.py +312 -0
  94. pyoq/query/aggregates.py +172 -0
  95. pyoq/query/arrays.py +65 -0
  96. pyoq/query/binding.py +52 -0
  97. pyoq/query/capabilities.py +317 -0
  98. pyoq/query/casts.py +73 -0
  99. pyoq/query/choices.py +185 -0
  100. pyoq/query/decoding.py +360 -0
  101. pyoq/query/documents.py +56 -0
  102. pyoq/query/execution/__init__.py +63 -0
  103. pyoq/query/execution/aio/__init__.py +31 -0
  104. pyoq/query/execution/aio/operations.py +228 -0
  105. pyoq/query/execution/aio/pooling.py +233 -0
  106. pyoq/query/execution/aio/streaming.py +161 -0
  107. pyoq/query/execution/aio/transactions.py +105 -0
  108. pyoq/query/execution/batch.py +96 -0
  109. pyoq/query/execution/binding_style.py +30 -0
  110. pyoq/query/execution/compilation.py +48 -0
  111. pyoq/query/execution/context.py +61 -0
  112. pyoq/query/execution/control.py +50 -0
  113. pyoq/query/execution/operations.py +224 -0
  114. pyoq/query/execution/planning.py +107 -0
  115. pyoq/query/execution/pooling.py +279 -0
  116. pyoq/query/execution/results.py +36 -0
  117. pyoq/query/execution/streaming.py +178 -0
  118. pyoq/query/execution/transactions.py +95 -0
  119. pyoq/query/expressions.py +1200 -0
  120. pyoq/query/fields.py +60 -0
  121. pyoq/query/mysql/__init__.py +59 -0
  122. pyoq/query/mysql/aio/__init__.py +38 -0
  123. pyoq/query/mysql/aio/commands.py +389 -0
  124. pyoq/query/mysql/aio/driver.py +196 -0
  125. pyoq/query/mysql/aio/executor.py +123 -0
  126. pyoq/query/mysql/aio/factory.py +26 -0
  127. pyoq/query/mysql/aio/operations.py +38 -0
  128. pyoq/query/mysql/aio/pool.py +53 -0
  129. pyoq/query/mysql/aio/transactions.py +313 -0
  130. pyoq/query/mysql/commands.py +354 -0
  131. pyoq/query/mysql/compiler.py +134 -0
  132. pyoq/query/mysql/context.py +20 -0
  133. pyoq/query/mysql/executor.py +126 -0
  134. pyoq/query/mysql/expressions.py +244 -0
  135. pyoq/query/mysql/factory.py +46 -0
  136. pyoq/query/mysql/health.py +66 -0
  137. pyoq/query/mysql/identifiers.py +9 -0
  138. pyoq/query/mysql/model.py +79 -0
  139. pyoq/query/mysql/operations.py +43 -0
  140. pyoq/query/mysql/parameters.py +69 -0
  141. pyoq/query/mysql/planning.py +20 -0
  142. pyoq/query/mysql/pool.py +67 -0
  143. pyoq/query/mysql/transactions.py +331 -0
  144. pyoq/query/mysql/writes.py +73 -0
  145. pyoq/query/nodes.py +750 -0
  146. pyoq/query/postgres/__init__.py +48 -0
  147. pyoq/query/postgres/aio/__init__.py +25 -0
  148. pyoq/query/postgres/aio/bulk.py +56 -0
  149. pyoq/query/postgres/aio/commands.py +264 -0
  150. pyoq/query/postgres/aio/executor.py +152 -0
  151. pyoq/query/postgres/aio/factory.py +26 -0
  152. pyoq/query/postgres/aio/operations.py +26 -0
  153. pyoq/query/postgres/aio/pool.py +40 -0
  154. pyoq/query/postgres/aio/transactions.py +295 -0
  155. pyoq/query/postgres/bulk.py +62 -0
  156. pyoq/query/postgres/commands.py +238 -0
  157. pyoq/query/postgres/compiler.py +114 -0
  158. pyoq/query/postgres/context.py +20 -0
  159. pyoq/query/postgres/executor.py +147 -0
  160. pyoq/query/postgres/expressions.py +311 -0
  161. pyoq/query/postgres/factory.py +24 -0
  162. pyoq/query/postgres/health.py +24 -0
  163. pyoq/query/postgres/identifiers.py +9 -0
  164. pyoq/query/postgres/model.py +81 -0
  165. pyoq/query/postgres/operations.py +25 -0
  166. pyoq/query/postgres/parameters.py +71 -0
  167. pyoq/query/postgres/planning.py +20 -0
  168. pyoq/query/postgres/pool.py +52 -0
  169. pyoq/query/postgres/transactions.py +295 -0
  170. pyoq/query/postgres/writes.py +37 -0
  171. pyoq/query/projections.py +105 -0
  172. pyoq/query/raw.py +90 -0
  173. pyoq/query/recursion.py +265 -0
  174. pyoq/query/rendering/__init__.py +1 -0
  175. pyoq/query/rendering/expressions.py +913 -0
  176. pyoq/query/rendering/identifiers.py +40 -0
  177. pyoq/query/rendering/projections.py +63 -0
  178. pyoq/query/rendering/queries.py +334 -0
  179. pyoq/query/rendering/sources.py +66 -0
  180. pyoq/query/rendering/writes.py +176 -0
  181. pyoq/query/results.py +459 -0
  182. pyoq/query/routines.py +196 -0
  183. pyoq/query/rows.py +156 -0
  184. pyoq/query/select.py +793 -0
  185. pyoq/query/select_nodes.py +277 -0
  186. pyoq/query/sources.py +236 -0
  187. pyoq/query/sqlite/__init__.py +43 -0
  188. pyoq/query/sqlite/commands.py +201 -0
  189. pyoq/query/sqlite/compiler.py +139 -0
  190. pyoq/query/sqlite/context.py +20 -0
  191. pyoq/query/sqlite/executor.py +119 -0
  192. pyoq/query/sqlite/expressions.py +224 -0
  193. pyoq/query/sqlite/factory.py +32 -0
  194. pyoq/query/sqlite/health.py +28 -0
  195. pyoq/query/sqlite/identifiers.py +9 -0
  196. pyoq/query/sqlite/model.py +73 -0
  197. pyoq/query/sqlite/operations.py +36 -0
  198. pyoq/query/sqlite/parameters.py +50 -0
  199. pyoq/query/sqlite/planning.py +20 -0
  200. pyoq/query/sqlite/pool.py +50 -0
  201. pyoq/query/sqlite/streaming.py +13 -0
  202. pyoq/query/sqlite/transactions.py +274 -0
  203. pyoq/query/sqlite/writes.py +35 -0
  204. pyoq/query/statements.py +27 -0
  205. pyoq/query/values.py +23 -0
  206. pyoq/query/vendor.py +162 -0
  207. pyoq/query/windows.py +424 -0
  208. pyoq/query/write_nodes.py +174 -0
  209. pyoq/query/writes.py +628 -0
  210. pyoq/relations/__init__.py +66 -0
  211. pyoq/relations/batching.py +219 -0
  212. pyoq/relations/derivation.py +111 -0
  213. pyoq/relations/fetching.py +355 -0
  214. pyoq/relations/graph.py +245 -0
  215. pyoq/relations/loading.py +74 -0
  216. pyoq/relations/model.py +75 -0
  217. pyoq/relations/planning.py +206 -0
  218. pyoq/runtime/__init__.py +9 -0
  219. pyoq/runtime/kernels.py +25 -0
  220. pyoq/runtime/python.py +43 -0
  221. pyoq/runtime/selection.py +73 -0
  222. pyoq/sanic/__init__.py +32 -0
  223. pyoq/sanic/scope.py +197 -0
  224. pyoq/sanic/workers.py +129 -0
  225. pyoq/schema/__init__.py +108 -0
  226. pyoq/schema/codec.py +711 -0
  227. pyoq/schema/models.py +604 -0
  228. pyoq/schema/mysql/__init__.py +16 -0
  229. pyoq/schema/mysql/connection.py +73 -0
  230. pyoq/schema/mysql/dsn.py +72 -0
  231. pyoq/schema/mysql/records.py +354 -0
  232. pyoq/schema/mysql/reflection.py +309 -0
  233. pyoq/schema/mysql/source.py +30 -0
  234. pyoq/schema/mysql/sql.py +128 -0
  235. pyoq/schema/mysql/types.py +105 -0
  236. pyoq/schema/postgres/__init__.py +13 -0
  237. pyoq/schema/postgres/connection.py +63 -0
  238. pyoq/schema/postgres/records.py +384 -0
  239. pyoq/schema/postgres/reflection.py +466 -0
  240. pyoq/schema/postgres/source.py +30 -0
  241. pyoq/schema/postgres/sql.py +246 -0
  242. pyoq/schema/postgres/types.py +98 -0
  243. pyoq/schema/registry.py +45 -0
  244. pyoq/schema/source.py +15 -0
  245. pyoq/schema/sqlite/__init__.py +6 -0
  246. pyoq/schema/sqlite/connection.py +54 -0
  247. pyoq/schema/sqlite/records.py +167 -0
  248. pyoq/schema/sqlite/reflection.py +393 -0
  249. pyoq/schema/sqlite/source.py +30 -0
  250. pyoq/schema/sqlite/sql.py +254 -0
  251. pyoq/schema/sqlite/types.py +74 -0
  252. pyoq/serving/__init__.py +23 -0
  253. pyoq/serving/databases.py +107 -0
  254. pyoq/serving/opening.py +331 -0
  255. pyoq/snapshots/__init__.py +20 -0
  256. pyoq/snapshots/drift.py +312 -0
  257. pyoq/snapshots/files.py +96 -0
  258. pyoq/snapshots/routing.py +40 -0
  259. pyoq/snapshots/source.py +33 -0
  260. pyoq/tracing/__init__.py +5 -0
  261. pyoq/tracing/spans.py +89 -0
  262. pyoq/unset.py +14 -0
  263. pyoq_sql-1.0.2.dist-info/METADATA +3050 -0
  264. pyoq_sql-1.0.2.dist-info/RECORD +267 -0
  265. pyoq_sql-1.0.2.dist-info/WHEEL +4 -0
  266. pyoq_sql-1.0.2.dist-info/entry_points.txt +3 -0
  267. pyoq_sql-1.0.2.dist-info/licenses/LICENSE +373 -0
@@ -0,0 +1,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
+ )