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
pyoq/dsl/context.py ADDED
@@ -0,0 +1,326 @@
1
+ """The entry point that ties a query to a database.
2
+
3
+ PyOQ's builders describe a statement and never reach a database, which is what
4
+ makes them safe to share, cache, and test. A context does not change that: it
5
+ holds the operations to run against and hands back builders that carry it, so a
6
+ chain can end in a fetch instead of being passed to an executor.
7
+
8
+ The unattached builders are still there and still cannot execute. Anything built
9
+ with them runs through `fetch` or `execute` here.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import TYPE_CHECKING, TypeVar, TypeVarTuple, cast, overload
15
+
16
+ from pyoq.dsl.labels import labels_of
17
+ from pyoq.dsl.queries import BoundSelect
18
+ from pyoq.dsl.result import Result
19
+ from pyoq.dsl.writes import BoundDelete, BoundInsert, BoundUpdate
20
+ from pyoq.query.select import SelectQuery, select, select_from
21
+ from pyoq.query.select_nodes import SelectNode
22
+ from pyoq.query.writes import (
23
+ InsertStatement,
24
+ ReturningStatement,
25
+ delete_from,
26
+ insert_into,
27
+ update,
28
+ )
29
+
30
+ if TYPE_CHECKING:
31
+ from collections.abc import Callable
32
+
33
+ from pyoq.query.execution import QueryOperations
34
+ from pyoq.query.expressions import Expression
35
+ from pyoq.query.projections import Projection
36
+ from pyoq.query.routines import Call
37
+ from pyoq.query.select import Query
38
+ from pyoq.query.sources import ColumnedTable, TableLike
39
+
40
+ ResultRow = TypeVar("ResultRow", bound=tuple[object, ...])
41
+ Values = TypeVarTuple("Values")
42
+ Value1 = TypeVar("Value1")
43
+ Value2 = TypeVar("Value2")
44
+ Value3 = TypeVar("Value3")
45
+ Value4 = TypeVar("Value4")
46
+ Value5 = TypeVar("Value5")
47
+ Value6 = TypeVar("Value6")
48
+ Value7 = TypeVar("Value7")
49
+ Value8 = TypeVar("Value8")
50
+
51
+
52
+ class DslContext:
53
+ """A database to build against."""
54
+
55
+ __slots__ = ("_operations",)
56
+ _operations: QueryOperations
57
+
58
+ def __init__(self, operations: QueryOperations) -> None:
59
+ object.__setattr__(self, "_operations", operations)
60
+
61
+ def __setattr__(self, name: str, value: object) -> None:
62
+ message = f"{type(self).__name__} is immutable"
63
+ raise AttributeError(message, name, value)
64
+
65
+ @property
66
+ def operations(self) -> QueryOperations:
67
+ return self._operations
68
+
69
+ @overload
70
+ def select(
71
+ self,
72
+ value1: Expression[Value1] | Projection[Value1],
73
+ /,
74
+ ) -> BoundSelect[tuple[Value1]]: ...
75
+
76
+ @overload
77
+ def select(
78
+ self,
79
+ value1: Expression[Value1] | Projection[Value1],
80
+ value2: Expression[Value2] | Projection[Value2],
81
+ /,
82
+ ) -> BoundSelect[tuple[Value1, Value2]]: ...
83
+
84
+ @overload
85
+ def select(
86
+ self,
87
+ value1: Expression[Value1] | Projection[Value1],
88
+ value2: Expression[Value2] | Projection[Value2],
89
+ value3: Expression[Value3] | Projection[Value3],
90
+ /,
91
+ ) -> BoundSelect[tuple[Value1, Value2, Value3]]: ...
92
+
93
+ @overload
94
+ def select(
95
+ self,
96
+ value1: Expression[Value1] | Projection[Value1],
97
+ value2: Expression[Value2] | Projection[Value2],
98
+ value3: Expression[Value3] | Projection[Value3],
99
+ value4: Expression[Value4] | Projection[Value4],
100
+ /,
101
+ ) -> BoundSelect[tuple[Value1, Value2, Value3, Value4]]: ...
102
+
103
+ @overload
104
+ def select(
105
+ self,
106
+ value1: Expression[Value1] | Projection[Value1],
107
+ value2: Expression[Value2] | Projection[Value2],
108
+ value3: Expression[Value3] | Projection[Value3],
109
+ value4: Expression[Value4] | Projection[Value4],
110
+ value5: Expression[Value5] | Projection[Value5],
111
+ /,
112
+ ) -> BoundSelect[tuple[Value1, Value2, Value3, Value4, Value5]]: ...
113
+
114
+ @overload
115
+ def select(
116
+ self,
117
+ value1: Expression[Value1] | Projection[Value1],
118
+ value2: Expression[Value2] | Projection[Value2],
119
+ value3: Expression[Value3] | Projection[Value3],
120
+ value4: Expression[Value4] | Projection[Value4],
121
+ value5: Expression[Value5] | Projection[Value5],
122
+ value6: Expression[Value6] | Projection[Value6],
123
+ /,
124
+ ) -> BoundSelect[tuple[Value1, Value2, Value3, Value4, Value5, Value6]]: ...
125
+
126
+ @overload
127
+ def select(
128
+ self,
129
+ value1: Expression[Value1] | Projection[Value1],
130
+ value2: Expression[Value2] | Projection[Value2],
131
+ value3: Expression[Value3] | Projection[Value3],
132
+ value4: Expression[Value4] | Projection[Value4],
133
+ value5: Expression[Value5] | Projection[Value5],
134
+ value6: Expression[Value6] | Projection[Value6],
135
+ value7: Expression[Value7] | Projection[Value7],
136
+ /,
137
+ ) -> BoundSelect[tuple[Value1, Value2, Value3, Value4, Value5, Value6, Value7]]: ...
138
+
139
+ @overload
140
+ def select(
141
+ self,
142
+ value1: Expression[Value1] | Projection[Value1],
143
+ value2: Expression[Value2] | Projection[Value2],
144
+ value3: Expression[Value3] | Projection[Value3],
145
+ value4: Expression[Value4] | Projection[Value4],
146
+ value5: Expression[Value5] | Projection[Value5],
147
+ value6: Expression[Value6] | Projection[Value6],
148
+ value7: Expression[Value7] | Projection[Value7],
149
+ value8: Expression[Value8] | Projection[Value8],
150
+ /,
151
+ ) -> BoundSelect[
152
+ tuple[Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8]
153
+ ]: ...
154
+
155
+ @overload
156
+ def select(
157
+ self,
158
+ value1: object,
159
+ value2: object,
160
+ value3: object,
161
+ value4: object,
162
+ value5: object,
163
+ value6: object,
164
+ value7: object,
165
+ value8: object,
166
+ /,
167
+ *values: object,
168
+ ) -> BoundSelect[tuple[object, ...]]:
169
+ """Wider than the overloads above name, where the element types stop.
170
+
171
+ A query is still a query past eight projections, and matching no
172
+ overload would take the whole chain down with it rather than just the
173
+ tuple. Past here a projection is checked when it is turned into one,
174
+ and the row is `object` per column, which is what is still known.
175
+ """
176
+
177
+ def select( # type: ignore[misc]
178
+ self,
179
+ *values: object,
180
+ ) -> BoundSelect[tuple[object, ...]]:
181
+ # `select` declares typed overloads for callers; this is the single
182
+ # implementation behind them, reached variadically. The overloads above
183
+ # are what a caller sees, and they carry the types across.
184
+ build = cast("Callable[..., SelectQuery[tuple[object, ...]]]", select)
185
+ return BoundSelect(self._operations, build(*values))
186
+
187
+ def select_from(self, table: ColumnedTable, /) -> BoundSelect[tuple[object, ...]]:
188
+ """Every column the table declares, without naming them one by one.
189
+
190
+ The columns are written into the SQL rather than left as a star, so
191
+ adding one later cannot silently change what a caller reads back.
192
+ Values arrive in declared order, which is the order the generated row
193
+ type takes its arguments, so `.into(TableRow)` builds it.
194
+ """
195
+ return BoundSelect(self._operations, select_from(table))
196
+
197
+ @overload
198
+ @overload
199
+ def insert_into(
200
+ self,
201
+ table: TableLike,
202
+ /,
203
+ column1: Expression[Value1],
204
+ ) -> BoundInsert[Value1]: ...
205
+
206
+ @overload
207
+ def insert_into(
208
+ self,
209
+ table: TableLike,
210
+ /,
211
+ column1: Expression[Value1],
212
+ column2: Expression[Value2],
213
+ ) -> BoundInsert[Value1, Value2]: ...
214
+
215
+ @overload
216
+ def insert_into(
217
+ self,
218
+ table: TableLike,
219
+ /,
220
+ column1: Expression[Value1],
221
+ column2: Expression[Value2],
222
+ column3: Expression[Value3],
223
+ ) -> BoundInsert[Value1, Value2, Value3]: ...
224
+
225
+ @overload
226
+ def insert_into(
227
+ self,
228
+ table: TableLike,
229
+ /,
230
+ column1: Expression[Value1],
231
+ column2: Expression[Value2],
232
+ column3: Expression[Value3],
233
+ column4: Expression[Value4],
234
+ ) -> BoundInsert[Value1, Value2, Value3, Value4]: ...
235
+
236
+ @overload
237
+ def insert_into(
238
+ self,
239
+ table: TableLike,
240
+ /,
241
+ column1: Expression[Value1],
242
+ column2: Expression[Value2],
243
+ column3: Expression[Value3],
244
+ column4: Expression[Value4],
245
+ column5: Expression[Value5],
246
+ ) -> BoundInsert[Value1, Value2, Value3, Value4, Value5]: ...
247
+
248
+ @overload
249
+ def insert_into(
250
+ self,
251
+ table: TableLike,
252
+ /,
253
+ column1: Expression[Value1],
254
+ column2: Expression[Value2],
255
+ column3: Expression[Value3],
256
+ column4: Expression[Value4],
257
+ column5: Expression[Value5],
258
+ column6: Expression[Value6],
259
+ ) -> BoundInsert[Value1, Value2, Value3, Value4, Value5, Value6]: ...
260
+
261
+ @overload
262
+ def insert_into(
263
+ self,
264
+ table: TableLike,
265
+ /,
266
+ column1: Expression[Value1],
267
+ column2: Expression[Value2],
268
+ column3: Expression[Value3],
269
+ column4: Expression[Value4],
270
+ column5: Expression[Value5],
271
+ column6: Expression[Value6],
272
+ column7: Expression[Value7],
273
+ ) -> BoundInsert[Value1, Value2, Value3, Value4, Value5, Value6, Value7]: ...
274
+
275
+ @overload
276
+ def insert_into(
277
+ self,
278
+ table: TableLike,
279
+ /,
280
+ column1: Expression[Value1],
281
+ column2: Expression[Value2],
282
+ column3: Expression[Value3],
283
+ column4: Expression[Value4],
284
+ column5: Expression[Value5],
285
+ column6: Expression[Value6],
286
+ column7: Expression[Value7],
287
+ column8: Expression[Value8],
288
+ ) -> BoundInsert[
289
+ Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8
290
+ ]: ...
291
+
292
+ def insert_into( # type: ignore[misc]
293
+ self,
294
+ table: TableLike,
295
+ /,
296
+ *columns: object,
297
+ ) -> BoundInsert[*tuple[object, ...]]:
298
+ build = cast("Callable[..., InsertStatement[*tuple[object, ...]]]", insert_into)
299
+ return BoundInsert(self._operations, build(table, *columns))
300
+
301
+ def update(self, table: TableLike, /) -> BoundUpdate:
302
+ return BoundUpdate(self._operations, update(table))
303
+
304
+ def delete_from(self, table: TableLike, /) -> BoundDelete:
305
+ return BoundDelete(self._operations, delete_from(table))
306
+
307
+ def fetch(
308
+ self,
309
+ query: Query[ResultRow] | ReturningStatement[ResultRow] | Call[ResultRow],
310
+ /,
311
+ ) -> Result[ResultRow]:
312
+ """Run something built the ordinary way, and read it the same way."""
313
+ rows = tuple(self._operations.many(query))
314
+ return Result(rows, _labels(query))
315
+
316
+
317
+ def _labels(
318
+ query: Query[ResultRow] | ReturningStatement[ResultRow] | Call[ResultRow],
319
+ ) -> tuple[str | None, ...]:
320
+ if isinstance(query, ReturningStatement):
321
+ return labels_of(query.node.returning)
322
+ node = query.node
323
+ return labels_of(node.projections) if isinstance(node, SelectNode) else ()
324
+
325
+
326
+ __all__ = ("DslContext",)
pyoq/dsl/entry.py ADDED
@@ -0,0 +1,37 @@
1
+ """Choosing a context from the database it is given.
2
+
3
+ There is one name for this because there is one question being asked. Whether
4
+ the fetches are awaited follows from the database, not from a second spelling.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING, overload
10
+
11
+ from pyoq.dsl.aio.context import AsyncDslContext
12
+ from pyoq.dsl.context import DslContext
13
+ from pyoq.query.execution.aio import AsyncQueryOperations
14
+
15
+ if TYPE_CHECKING:
16
+ from pyoq.query.execution import QueryOperations
17
+
18
+
19
+ @overload
20
+ def using(operations: QueryOperations, /) -> DslContext: ...
21
+
22
+
23
+ @overload
24
+ def using(operations: AsyncQueryOperations, /) -> AsyncDslContext: ...
25
+
26
+
27
+ def using(
28
+ operations: QueryOperations | AsyncQueryOperations,
29
+ /,
30
+ ) -> DslContext | AsyncDslContext:
31
+ """A context over any database PyOQ can run statements against."""
32
+ if isinstance(operations, AsyncQueryOperations):
33
+ return AsyncDslContext(operations)
34
+ return DslContext(operations)
35
+
36
+
37
+ __all__ = ("using",)
pyoq/dsl/labels.py ADDED
@@ -0,0 +1,36 @@
1
+ """The name a column comes back under.
2
+
3
+ Queries and writes both project through the same node, so the rule for naming a
4
+ column lives here rather than once in each place that needs it.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
10
+
11
+ from pyoq.query.nodes import FieldNode
12
+
13
+ if TYPE_CHECKING:
14
+ from pyoq.query.select_nodes import ProjectionNode
15
+
16
+
17
+ def labels_of(
18
+ projections: tuple[ProjectionNode, ...],
19
+ /,
20
+ ) -> tuple[str | None, ...]:
21
+ return tuple(_label(projection) for projection in projections)
22
+
23
+
24
+ def _label(projection: ProjectionNode) -> str | None:
25
+ """An alias if the query gave one, otherwise the column's own name.
26
+
27
+ Anything else has no name to report, and inventing one would report a fact
28
+ the database never stated.
29
+ """
30
+ if projection.alias is not None:
31
+ return projection.alias
32
+ expression = projection.expression
33
+ return expression.name if isinstance(expression, FieldNode) else None
34
+
35
+
36
+ __all__ = ("labels_of",)