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,384 @@
1
+ """Typed PostgreSQL catalog row records."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import cast
7
+
8
+ from pyoq.errors import SchemaInspectionError
9
+ from pyoq.schema.models import (
10
+ GeneratedKind,
11
+ Identifier,
12
+ KeyKind,
13
+ ObjectReference,
14
+ ReferentialAction,
15
+ )
16
+
17
+ _IDENTITY_KINDS = {
18
+ "a": GeneratedKind.IDENTITY_ALWAYS,
19
+ "d": GeneratedKind.IDENTITY_BY_DEFAULT,
20
+ }
21
+ _COMPUTED_KINDS = {
22
+ "s": GeneratedKind.COMPUTED_STORED,
23
+ "v": GeneratedKind.COMPUTED_VIRTUAL,
24
+ }
25
+ _KEY_KINDS = {"p": KeyKind.PRIMARY, "u": KeyKind.UNIQUE}
26
+ _REFERENTIAL_ACTIONS = {
27
+ "a": ReferentialAction.NO_ACTION,
28
+ "r": ReferentialAction.RESTRICT,
29
+ "c": ReferentialAction.CASCADE,
30
+ "n": ReferentialAction.SET_NULL,
31
+ "d": ReferentialAction.SET_DEFAULT,
32
+ }
33
+ _DESCENDING_OPTION = 1
34
+
35
+
36
+ @dataclass(frozen=True, slots=True)
37
+ class ObjectRecord:
38
+ schema: str
39
+ name: str
40
+ kind: str
41
+ comment: str | None
42
+ definition: str | None
43
+
44
+ @property
45
+ def is_table(self) -> bool:
46
+ return self.kind in {"r", "p"}
47
+
48
+ @property
49
+ def is_materialized(self) -> bool:
50
+ return self.kind == "m"
51
+
52
+
53
+ @dataclass(frozen=True, slots=True)
54
+ class ColumnRecord:
55
+ schema: str
56
+ table: str
57
+ name: str
58
+ data_type: str
59
+ nullable: bool
60
+ type_kind: str
61
+ """What the column's own type is: a base type, a domain, or an enum."""
62
+
63
+ type_schema: str
64
+ type_name: str
65
+ domain_base: str | None
66
+ default: str | None
67
+ identity: str
68
+ computed: str
69
+ comment: str | None
70
+
71
+ @property
72
+ def generated_kind(self) -> GeneratedKind | None:
73
+ identity = _IDENTITY_KINDS.get(self.identity.strip())
74
+ if identity is not None:
75
+ return identity
76
+ return _COMPUTED_KINDS.get(self.computed.strip())
77
+
78
+
79
+ @dataclass(frozen=True, slots=True)
80
+ class ConstraintRecord:
81
+ schema: str
82
+ table: str
83
+ name: str
84
+ kind: str
85
+ definition: str
86
+ columns: tuple[str, ...]
87
+ target_schema: str | None
88
+ target_table: str | None
89
+ target_columns: tuple[str, ...]
90
+ on_update: str
91
+ on_delete: str
92
+
93
+ @property
94
+ def key_kind(self) -> KeyKind | None:
95
+ return _KEY_KINDS.get(self.kind)
96
+
97
+ @property
98
+ def target_reference(self) -> ObjectReference | None:
99
+ if self.target_schema is None or self.target_table is None:
100
+ return None
101
+ return ObjectReference(
102
+ Identifier(self.target_table),
103
+ Identifier(self.target_schema),
104
+ )
105
+
106
+ @property
107
+ def update_action(self) -> ReferentialAction:
108
+ return _referential_action(self.on_update)
109
+
110
+ @property
111
+ def delete_action(self) -> ReferentialAction:
112
+ return _referential_action(self.on_delete)
113
+
114
+
115
+ @dataclass(frozen=True, slots=True)
116
+ class IndexRecord:
117
+ schema: str
118
+ table: str
119
+ name: str
120
+ unique: bool
121
+ method: str
122
+ predicate: str | None
123
+ attribute_numbers: tuple[int, ...]
124
+ column_names: tuple[str | None, ...]
125
+ definitions: tuple[str, ...]
126
+ options: tuple[int, ...]
127
+
128
+ def descending(self, position: int) -> bool:
129
+ return bool(self.options[position] & _DESCENDING_OPTION)
130
+
131
+
132
+ def _referential_action(code: str) -> ReferentialAction:
133
+ action = _REFERENTIAL_ACTIONS.get(code.strip())
134
+ if action is None:
135
+ message = f"unsupported PostgreSQL referential action: {code!r}"
136
+ raise SchemaInspectionError(message)
137
+ return action
138
+
139
+
140
+ def read_object(row: tuple[object, ...]) -> ObjectRecord:
141
+ values = _row(row, 5)
142
+ return ObjectRecord(
143
+ _text(values[0]),
144
+ _text(values[1]),
145
+ _text(values[2]),
146
+ _optional_text(values[3]),
147
+ _optional_text(values[4]),
148
+ )
149
+
150
+
151
+ @dataclass(frozen=True, slots=True)
152
+ class EnumRecord:
153
+ schema: str
154
+ name: str
155
+ label: str
156
+ comment: str | None
157
+
158
+
159
+ def read_enum(row: tuple[object, ...]) -> EnumRecord:
160
+ values = _row(row, 4)
161
+ return EnumRecord(
162
+ _text(values[0]),
163
+ _text(values[1]),
164
+ _text(values[2]),
165
+ _optional_text(values[3]),
166
+ )
167
+
168
+
169
+ @dataclass(frozen=True, slots=True)
170
+ class DomainRecord:
171
+ schema: str
172
+ name: str
173
+ base_type: str
174
+ not_null: bool
175
+ default: str | None
176
+ comment: str | None
177
+
178
+
179
+ @dataclass(frozen=True, slots=True)
180
+ class DomainCheckRecord:
181
+ schema: str
182
+ domain: str
183
+ name: str
184
+ definition: str
185
+
186
+
187
+ @dataclass(frozen=True, slots=True)
188
+ class RoutineRecord:
189
+ schema: str
190
+ name: str
191
+ kind: str
192
+ identifier: int
193
+ """What the database calls this routine, which separates two of a name."""
194
+
195
+ returns: str | None
196
+ position: int | None
197
+ parameter_name: str | None
198
+ mode: str
199
+ parameter_type: str | None
200
+ return_type_schema: str | None
201
+ return_type_name: str | None
202
+ return_type_kind: str | None
203
+ return_base_type: str | None
204
+ return_is_array: bool
205
+ parameter_type_schema: str | None
206
+ parameter_type_name: str | None
207
+ parameter_type_kind: str | None
208
+ parameter_base_type: str | None
209
+ parameter_is_array: bool
210
+ parameter_forbids_null: bool = False
211
+ """Whether the parameter's own type refuses a null, which a domain can."""
212
+ default_count: int = 0
213
+
214
+
215
+ def read_domain(row: tuple[object, ...]) -> DomainRecord:
216
+ values = _row(row, 6)
217
+ return DomainRecord(
218
+ _text(values[0]),
219
+ _text(values[1]),
220
+ _text(values[2]),
221
+ _flag(values[3]),
222
+ _optional_text(values[4]),
223
+ _optional_text(values[5]),
224
+ )
225
+
226
+
227
+ def read_domain_check(row: tuple[object, ...]) -> DomainCheckRecord:
228
+ values = _row(row, 4)
229
+ return DomainCheckRecord(
230
+ _text(values[0]),
231
+ _text(values[1]),
232
+ _text(values[2]),
233
+ _text(values[3]),
234
+ )
235
+
236
+
237
+ def read_routine(row: tuple[object, ...]) -> RoutineRecord:
238
+ values = _row(row, 21)
239
+ return RoutineRecord(
240
+ _text(values[0]),
241
+ _text(values[1]),
242
+ _text(values[2]),
243
+ _number(values[3]),
244
+ _optional_text(values[4]),
245
+ None if values[5] is None else _number(values[5]),
246
+ _optional_text(values[6]),
247
+ _text(values[7]),
248
+ _optional_text(values[8]),
249
+ _optional_text(values[9]),
250
+ _optional_text(values[10]),
251
+ _optional_text(values[11]),
252
+ _optional_text(values[12]),
253
+ _flag(values[13]),
254
+ _optional_text(values[14]),
255
+ _optional_text(values[15]),
256
+ _optional_text(values[16]),
257
+ _optional_text(values[17]),
258
+ _flag(values[18]),
259
+ _flag(values[19]),
260
+ _number(values[20]),
261
+ )
262
+
263
+
264
+ def read_column(row: tuple[object, ...]) -> ColumnRecord:
265
+ values = _row(row, 13)
266
+ return ColumnRecord(
267
+ _text(values[0]),
268
+ _text(values[1]),
269
+ _text(values[2]),
270
+ _text(values[3]),
271
+ _flag(values[4]),
272
+ _text(values[5]),
273
+ _text(values[6]),
274
+ _text(values[7]),
275
+ _optional_text(values[8]),
276
+ _optional_text(values[9]),
277
+ _text(values[10]),
278
+ _text(values[11]),
279
+ _optional_text(values[12]),
280
+ )
281
+
282
+
283
+ def read_constraint(row: tuple[object, ...]) -> ConstraintRecord:
284
+ values = _row(row, 11)
285
+ return ConstraintRecord(
286
+ _text(values[0]),
287
+ _text(values[1]),
288
+ _text(values[2]),
289
+ _text(values[3]),
290
+ _text(values[4]),
291
+ _names(values[5]),
292
+ _optional_text(values[6]),
293
+ _optional_text(values[7]),
294
+ _names(values[8]),
295
+ _text(values[9]),
296
+ _text(values[10]),
297
+ )
298
+
299
+
300
+ def read_index(row: tuple[object, ...]) -> IndexRecord:
301
+ values = _row(row, 10)
302
+ return IndexRecord(
303
+ _text(values[0]),
304
+ _text(values[1]),
305
+ _text(values[2]),
306
+ _flag(values[3]),
307
+ _text(values[4]),
308
+ _optional_text(values[5]),
309
+ _numbers(values[6]),
310
+ _optional_names(values[7]),
311
+ _names(values[8]),
312
+ _numbers(values[9]),
313
+ )
314
+
315
+
316
+ def _row(row: tuple[object, ...], width: int) -> tuple[object, ...]:
317
+ if len(row) != width:
318
+ message = "PostgreSQL catalog row has an unexpected shape"
319
+ raise SchemaInspectionError(message)
320
+ return row
321
+
322
+
323
+ def _text(value: object) -> str:
324
+ if not isinstance(value, str):
325
+ message = f"PostgreSQL catalog value is not text: {value!r}"
326
+ raise SchemaInspectionError(message)
327
+ return value
328
+
329
+
330
+ def _optional_text(value: object) -> str | None:
331
+ return None if value is None else _text(value)
332
+
333
+
334
+ def _flag(value: object) -> bool:
335
+ if not isinstance(value, bool):
336
+ message = f"PostgreSQL catalog value is not a flag: {value!r}"
337
+ raise SchemaInspectionError(message)
338
+ return value
339
+
340
+
341
+ def _names(value: object) -> tuple[str, ...]:
342
+ return tuple(_text(item) for item in _sequence(value))
343
+
344
+
345
+ def _optional_names(value: object) -> tuple[str | None, ...]:
346
+ return tuple(_optional_text(item) for item in _sequence(value))
347
+
348
+
349
+ def _numbers(value: object) -> tuple[int, ...]:
350
+ return tuple(_number(item) for item in _sequence(value))
351
+
352
+
353
+ def _number(value: object) -> int:
354
+ if isinstance(value, bool) or not isinstance(value, int):
355
+ message = f"PostgreSQL catalog value is not a number: {value!r}"
356
+ raise SchemaInspectionError(message)
357
+ return value
358
+
359
+
360
+ def _sequence(value: object) -> list[object]:
361
+ if not isinstance(value, list):
362
+ message = f"PostgreSQL catalog value is not an array: {value!r}"
363
+ raise SchemaInspectionError(message)
364
+ return cast("list[object]", value)
365
+
366
+
367
+ __all__ = (
368
+ "ColumnRecord",
369
+ "ConstraintRecord",
370
+ "DomainCheckRecord",
371
+ "DomainRecord",
372
+ "EnumRecord",
373
+ "IndexRecord",
374
+ "ObjectRecord",
375
+ "RoutineRecord",
376
+ "read_column",
377
+ "read_constraint",
378
+ "read_domain",
379
+ "read_domain_check",
380
+ "read_enum",
381
+ "read_index",
382
+ "read_object",
383
+ "read_routine",
384
+ )