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/schema/codec.py
ADDED
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
"""Versioned schema snapshot JSON codec."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from typing import TypeVar, cast
|
|
9
|
+
|
|
10
|
+
from pyoq.config import DatabaseDialect
|
|
11
|
+
from pyoq.errors import SchemaSerializationError, SchemaValidationError
|
|
12
|
+
from pyoq.schema import (
|
|
13
|
+
Capability,
|
|
14
|
+
CapabilitySet,
|
|
15
|
+
Catalog,
|
|
16
|
+
CheckConstraint,
|
|
17
|
+
Column,
|
|
18
|
+
Domain,
|
|
19
|
+
EnumType,
|
|
20
|
+
GeneratedKind,
|
|
21
|
+
GeneratedValue,
|
|
22
|
+
Identifier,
|
|
23
|
+
Index,
|
|
24
|
+
IndexTerm,
|
|
25
|
+
JsonValue,
|
|
26
|
+
Key,
|
|
27
|
+
KeyKind,
|
|
28
|
+
MetadataEntry,
|
|
29
|
+
ObjectReference,
|
|
30
|
+
ParameterMode,
|
|
31
|
+
ReferentialAction,
|
|
32
|
+
Relation,
|
|
33
|
+
Routine,
|
|
34
|
+
RoutineKind,
|
|
35
|
+
RoutineParameter,
|
|
36
|
+
Schema,
|
|
37
|
+
SchemaSnapshot,
|
|
38
|
+
SqlExpression,
|
|
39
|
+
SqlType,
|
|
40
|
+
Table,
|
|
41
|
+
TypeKind,
|
|
42
|
+
View,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
JsonObject = dict[str, JsonValue]
|
|
46
|
+
DecodedValue = TypeVar("DecodedValue")
|
|
47
|
+
EnumValue = TypeVar("EnumValue", bound=StrEnum)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def encode_snapshot(snapshot: SchemaSnapshot) -> str:
|
|
51
|
+
capabilities: list[JsonValue] = [
|
|
52
|
+
capability.value
|
|
53
|
+
for capability in sorted(
|
|
54
|
+
snapshot.capabilities.values, key=lambda item: item.value
|
|
55
|
+
)
|
|
56
|
+
]
|
|
57
|
+
payload: JsonObject = {
|
|
58
|
+
"capabilities": capabilities,
|
|
59
|
+
"catalogs": [_encode_catalog(catalog) for catalog in snapshot.catalogs],
|
|
60
|
+
"dialect": snapshot.dialect.value,
|
|
61
|
+
"format_version": snapshot.format_version,
|
|
62
|
+
}
|
|
63
|
+
return json.dumps(
|
|
64
|
+
payload,
|
|
65
|
+
ensure_ascii=False,
|
|
66
|
+
separators=(",", ":"),
|
|
67
|
+
sort_keys=True,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def decode_snapshot(value: str) -> SchemaSnapshot:
|
|
72
|
+
try:
|
|
73
|
+
decoded: object = json.loads(value)
|
|
74
|
+
return _decode_snapshot(_object(decoded, "snapshot"))
|
|
75
|
+
except SchemaSerializationError:
|
|
76
|
+
raise
|
|
77
|
+
except (json.JSONDecodeError, SchemaValidationError, ValueError) as error:
|
|
78
|
+
message = "serialized schema snapshot is invalid"
|
|
79
|
+
raise SchemaSerializationError(message) from error
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _encode_catalog(catalog: Catalog) -> JsonObject:
|
|
83
|
+
return {
|
|
84
|
+
"comment": catalog.comment,
|
|
85
|
+
"name": _encode_identifier(catalog.name),
|
|
86
|
+
"schemas": [_encode_schema(schema) for schema in catalog.schemas],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _encode_schema(schema: Schema) -> JsonObject:
|
|
91
|
+
return {
|
|
92
|
+
"comment": schema.comment,
|
|
93
|
+
"domains": [_encode_domain(domain) for domain in schema.domains],
|
|
94
|
+
"enums": [_encode_enum(enum) for enum in schema.enums],
|
|
95
|
+
"name": _encode_identifier(schema.name),
|
|
96
|
+
"routines": [_encode_routine(routine) for routine in schema.routines],
|
|
97
|
+
"tables": [_encode_table(table) for table in schema.tables],
|
|
98
|
+
"views": [_encode_view(view) for view in schema.views],
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _encode_domain(domain: Domain) -> JsonObject:
|
|
103
|
+
return {
|
|
104
|
+
"checks": [_encode_check(check) for check in domain.checks],
|
|
105
|
+
"comment": domain.comment,
|
|
106
|
+
"data-type": _encode_sql_type(domain.data_type),
|
|
107
|
+
"default": None if domain.default is None else domain.default.text,
|
|
108
|
+
"name": domain.name.value,
|
|
109
|
+
"nullable": domain.nullable,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _encode_routine(routine: Routine) -> JsonObject:
|
|
114
|
+
return {
|
|
115
|
+
"comment": routine.comment,
|
|
116
|
+
"kind": routine.kind.value,
|
|
117
|
+
"name": routine.name.value,
|
|
118
|
+
"overload": routine.overload,
|
|
119
|
+
"parameters": [
|
|
120
|
+
_encode_parameter(parameter) for parameter in routine.parameters
|
|
121
|
+
],
|
|
122
|
+
"returns": (
|
|
123
|
+
None if routine.returns is None else _encode_sql_type(routine.returns)
|
|
124
|
+
),
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _encode_parameter(parameter: RoutineParameter) -> JsonObject:
|
|
129
|
+
return {
|
|
130
|
+
"data-type": _encode_sql_type(parameter.data_type),
|
|
131
|
+
"has-default": parameter.has_default,
|
|
132
|
+
"mode": parameter.mode.value,
|
|
133
|
+
"name": _encode_identifier(parameter.name),
|
|
134
|
+
"nullable": parameter.nullable,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _encode_table(table: Table) -> JsonObject:
|
|
139
|
+
return {
|
|
140
|
+
"checks": [_encode_check(check) for check in table.checks],
|
|
141
|
+
"columns": [_encode_column(column) for column in table.columns],
|
|
142
|
+
"comment": table.comment,
|
|
143
|
+
"indexes": [_encode_index(index) for index in table.indexes],
|
|
144
|
+
"keys": [_encode_key(key) for key in table.keys],
|
|
145
|
+
"metadata": [_encode_metadata(entry) for entry in table.metadata],
|
|
146
|
+
"name": table.name.value,
|
|
147
|
+
"relations": [_encode_relation(relation) for relation in table.relations],
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _encode_view(view: View) -> JsonObject:
|
|
152
|
+
return {
|
|
153
|
+
"columns": [_encode_column(column) for column in view.columns],
|
|
154
|
+
"comment": view.comment,
|
|
155
|
+
"definition": _encode_expression(view.definition),
|
|
156
|
+
"materialized": view.materialized,
|
|
157
|
+
"metadata": [_encode_metadata(entry) for entry in view.metadata],
|
|
158
|
+
"name": view.name.value,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _encode_enum(enum: EnumType) -> JsonObject:
|
|
163
|
+
return {
|
|
164
|
+
"comment": enum.comment,
|
|
165
|
+
"name": enum.name.value,
|
|
166
|
+
"values": list(enum.values),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _encode_column(column: Column) -> JsonObject:
|
|
171
|
+
return {
|
|
172
|
+
"comment": column.comment,
|
|
173
|
+
"data_type": _encode_sql_type(column.data_type),
|
|
174
|
+
"default": _encode_expression(column.default),
|
|
175
|
+
"generated": _encode_generated(column.generated),
|
|
176
|
+
"metadata": [_encode_metadata(entry) for entry in column.metadata],
|
|
177
|
+
"name": column.name.value,
|
|
178
|
+
"nullable": column.nullable,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _encode_sql_type(data_type: SqlType) -> JsonObject:
|
|
183
|
+
return {
|
|
184
|
+
"element_type": (
|
|
185
|
+
None
|
|
186
|
+
if data_type.element_type is None
|
|
187
|
+
else _encode_sql_type(data_type.element_type)
|
|
188
|
+
),
|
|
189
|
+
"kind": data_type.kind.value,
|
|
190
|
+
"length": data_type.length,
|
|
191
|
+
"metadata": [_encode_metadata(entry) for entry in data_type.metadata],
|
|
192
|
+
"named_type": _encode_reference(data_type.named_type),
|
|
193
|
+
"precision": data_type.precision,
|
|
194
|
+
"scale": data_type.scale,
|
|
195
|
+
"source_name": data_type.source_name,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _encode_generated(value: GeneratedValue | None) -> JsonObject | None:
|
|
200
|
+
if value is None:
|
|
201
|
+
return None
|
|
202
|
+
return {
|
|
203
|
+
"expression": _encode_expression(value.expression),
|
|
204
|
+
"kind": value.kind.value,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _encode_key(key: Key) -> JsonObject:
|
|
209
|
+
return {
|
|
210
|
+
"columns": [column.value for column in key.columns],
|
|
211
|
+
"kind": key.kind.value,
|
|
212
|
+
"name": _encode_identifier(key.name),
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _encode_relation(relation: Relation) -> JsonObject:
|
|
217
|
+
return {
|
|
218
|
+
"columns": [column.value for column in relation.columns],
|
|
219
|
+
"name": _encode_identifier(relation.name),
|
|
220
|
+
"on_delete": relation.on_delete.value,
|
|
221
|
+
"on_update": relation.on_update.value,
|
|
222
|
+
"target": _encode_reference(relation.target),
|
|
223
|
+
"target_columns": [column.value for column in relation.target_columns],
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _encode_index(index: Index) -> JsonObject:
|
|
228
|
+
return {
|
|
229
|
+
"method": index.method,
|
|
230
|
+
"name": index.name.value,
|
|
231
|
+
"predicate": _encode_expression(index.predicate),
|
|
232
|
+
"terms": [_encode_index_term(term) for term in index.terms],
|
|
233
|
+
"unique": index.unique,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _encode_index_term(term: IndexTerm) -> JsonObject:
|
|
238
|
+
return {
|
|
239
|
+
"column": _encode_identifier(term.column),
|
|
240
|
+
"descending": term.descending,
|
|
241
|
+
"expression": _encode_expression(term.expression),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _encode_check(check: CheckConstraint) -> JsonObject:
|
|
246
|
+
return {
|
|
247
|
+
"expression": check.expression.text,
|
|
248
|
+
"name": _encode_identifier(check.name),
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _encode_reference(reference: ObjectReference | None) -> JsonObject | None:
|
|
253
|
+
if reference is None:
|
|
254
|
+
return None
|
|
255
|
+
return {
|
|
256
|
+
"catalog": _encode_identifier(reference.catalog),
|
|
257
|
+
"name": reference.name.value,
|
|
258
|
+
"schema": _encode_identifier(reference.schema),
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _encode_metadata(entry: MetadataEntry) -> JsonObject:
|
|
263
|
+
return {"key": entry.key, "value": entry.value}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _encode_identifier(identifier: Identifier | None) -> str | None:
|
|
267
|
+
return None if identifier is None else identifier.value
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _encode_expression(expression: SqlExpression | None) -> str | None:
|
|
271
|
+
return None if expression is None else expression.text
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _decode_snapshot(value: JsonObject) -> SchemaSnapshot:
|
|
275
|
+
location = "snapshot"
|
|
276
|
+
_require_fields(
|
|
277
|
+
value,
|
|
278
|
+
{"capabilities", "catalogs", "dialect", "format_version"},
|
|
279
|
+
location,
|
|
280
|
+
)
|
|
281
|
+
capabilities = frozenset(
|
|
282
|
+
_enum(Capability, item, f"{location}.capabilities")
|
|
283
|
+
for item in _array(value["capabilities"], f"{location}.capabilities")
|
|
284
|
+
)
|
|
285
|
+
catalogs = _decode_array(value["catalogs"], f"{location}.catalogs", _decode_catalog)
|
|
286
|
+
return SchemaSnapshot(
|
|
287
|
+
dialect=_enum(DatabaseDialect, value["dialect"], f"{location}.dialect"),
|
|
288
|
+
catalogs=catalogs,
|
|
289
|
+
capabilities=CapabilitySet(capabilities),
|
|
290
|
+
format_version=_integer(value["format_version"], f"{location}.format_version"),
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _decode_catalog(value: JsonValue, location: str) -> Catalog:
|
|
295
|
+
record = _record(value, {"comment", "name", "schemas"}, location)
|
|
296
|
+
return Catalog(
|
|
297
|
+
name=_optional_identifier(record["name"], f"{location}.name"),
|
|
298
|
+
schemas=_decode_array(record["schemas"], f"{location}.schemas", _decode_schema),
|
|
299
|
+
comment=_optional_string(record["comment"], f"{location}.comment"),
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _decode_schema(value: JsonValue, location: str) -> Schema:
|
|
304
|
+
record = _record(
|
|
305
|
+
value,
|
|
306
|
+
{"comment", "domains", "enums", "name", "routines", "tables", "views"},
|
|
307
|
+
location,
|
|
308
|
+
)
|
|
309
|
+
return Schema(
|
|
310
|
+
name=_optional_identifier(record["name"], f"{location}.name"),
|
|
311
|
+
tables=_decode_array(record["tables"], f"{location}.tables", _decode_table),
|
|
312
|
+
views=_decode_array(record["views"], f"{location}.views", _decode_view),
|
|
313
|
+
enums=_decode_array(record["enums"], f"{location}.enums", _decode_enum),
|
|
314
|
+
domains=_decode_array(record["domains"], f"{location}.domains", _decode_domain),
|
|
315
|
+
routines=_decode_array(
|
|
316
|
+
record["routines"], f"{location}.routines", _decode_routine
|
|
317
|
+
),
|
|
318
|
+
comment=_optional_string(record["comment"], f"{location}.comment"),
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _decode_domain(value: JsonValue, location: str) -> Domain:
|
|
323
|
+
record = _record(
|
|
324
|
+
value,
|
|
325
|
+
{"checks", "comment", "data-type", "default", "name", "nullable"},
|
|
326
|
+
location,
|
|
327
|
+
)
|
|
328
|
+
default = _optional_string(record["default"], f"{location}.default")
|
|
329
|
+
return Domain(
|
|
330
|
+
name=_identifier(record["name"], f"{location}.name"),
|
|
331
|
+
data_type=_decode_sql_type(record["data-type"], f"{location}.data-type"),
|
|
332
|
+
nullable=_boolean(record["nullable"], f"{location}.nullable"),
|
|
333
|
+
default=None if default is None else SqlExpression(default),
|
|
334
|
+
checks=_decode_array(record["checks"], f"{location}.checks", _decode_check),
|
|
335
|
+
comment=_optional_string(record["comment"], f"{location}.comment"),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _decode_routine(value: JsonValue, location: str) -> Routine:
|
|
340
|
+
record = _record(
|
|
341
|
+
value,
|
|
342
|
+
{"comment", "kind", "name", "overload", "parameters", "returns"},
|
|
343
|
+
location,
|
|
344
|
+
)
|
|
345
|
+
returns = record["returns"]
|
|
346
|
+
return Routine(
|
|
347
|
+
name=_identifier(record["name"], f"{location}.name"),
|
|
348
|
+
kind=_routine_kind(record["kind"], f"{location}.kind"),
|
|
349
|
+
parameters=_decode_array(
|
|
350
|
+
record["parameters"], f"{location}.parameters", _decode_parameter
|
|
351
|
+
),
|
|
352
|
+
returns=(
|
|
353
|
+
None
|
|
354
|
+
if returns is None
|
|
355
|
+
else _decode_sql_type(returns, f"{location}.returns")
|
|
356
|
+
),
|
|
357
|
+
overload=_optional_string(record["overload"], f"{location}.overload"),
|
|
358
|
+
comment=_optional_string(record["comment"], f"{location}.comment"),
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _decode_parameter(value: JsonValue, location: str) -> RoutineParameter:
|
|
363
|
+
record = _record(
|
|
364
|
+
value, {"data-type", "has-default", "mode", "name", "nullable"}, location
|
|
365
|
+
)
|
|
366
|
+
return RoutineParameter(
|
|
367
|
+
name=_optional_identifier(record["name"], f"{location}.name"),
|
|
368
|
+
data_type=_decode_sql_type(record["data-type"], f"{location}.data-type"),
|
|
369
|
+
mode=_parameter_mode(record["mode"], f"{location}.mode"),
|
|
370
|
+
has_default=_boolean(record["has-default"], f"{location}.has-default"),
|
|
371
|
+
nullable=_boolean(record["nullable"], f"{location}.nullable"),
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _routine_kind(value: JsonValue, location: str) -> RoutineKind:
|
|
376
|
+
return _enum(RoutineKind, value, location)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _parameter_mode(value: JsonValue, location: str) -> ParameterMode:
|
|
380
|
+
return _enum(ParameterMode, value, location)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _decode_table(value: JsonValue, location: str) -> Table:
|
|
384
|
+
record = _record(
|
|
385
|
+
value,
|
|
386
|
+
{
|
|
387
|
+
"checks",
|
|
388
|
+
"columns",
|
|
389
|
+
"comment",
|
|
390
|
+
"indexes",
|
|
391
|
+
"keys",
|
|
392
|
+
"metadata",
|
|
393
|
+
"name",
|
|
394
|
+
"relations",
|
|
395
|
+
},
|
|
396
|
+
location,
|
|
397
|
+
)
|
|
398
|
+
return Table(
|
|
399
|
+
name=_identifier(record["name"], f"{location}.name"),
|
|
400
|
+
columns=_decode_array(record["columns"], f"{location}.columns", _decode_column),
|
|
401
|
+
keys=_decode_array(record["keys"], f"{location}.keys", _decode_key),
|
|
402
|
+
relations=_decode_array(
|
|
403
|
+
record["relations"],
|
|
404
|
+
f"{location}.relations",
|
|
405
|
+
_decode_relation,
|
|
406
|
+
),
|
|
407
|
+
indexes=_decode_array(record["indexes"], f"{location}.indexes", _decode_index),
|
|
408
|
+
checks=_decode_array(record["checks"], f"{location}.checks", _decode_check),
|
|
409
|
+
comment=_optional_string(record["comment"], f"{location}.comment"),
|
|
410
|
+
metadata=_decode_array(
|
|
411
|
+
record["metadata"],
|
|
412
|
+
f"{location}.metadata",
|
|
413
|
+
_decode_metadata,
|
|
414
|
+
),
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _decode_view(value: JsonValue, location: str) -> View:
|
|
419
|
+
record = _record(
|
|
420
|
+
value,
|
|
421
|
+
{"columns", "comment", "definition", "materialized", "metadata", "name"},
|
|
422
|
+
location,
|
|
423
|
+
)
|
|
424
|
+
return View(
|
|
425
|
+
name=_identifier(record["name"], f"{location}.name"),
|
|
426
|
+
columns=_decode_array(record["columns"], f"{location}.columns", _decode_column),
|
|
427
|
+
definition=_optional_expression(record["definition"], f"{location}.definition"),
|
|
428
|
+
materialized=_boolean(record["materialized"], f"{location}.materialized"),
|
|
429
|
+
comment=_optional_string(record["comment"], f"{location}.comment"),
|
|
430
|
+
metadata=_decode_array(
|
|
431
|
+
record["metadata"],
|
|
432
|
+
f"{location}.metadata",
|
|
433
|
+
_decode_metadata,
|
|
434
|
+
),
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _decode_enum(value: JsonValue, location: str) -> EnumType:
|
|
439
|
+
record = _record(value, {"comment", "name", "values"}, location)
|
|
440
|
+
values = tuple(
|
|
441
|
+
_string(item, f"{location}.values")
|
|
442
|
+
for item in _array(record["values"], f"{location}.values")
|
|
443
|
+
)
|
|
444
|
+
return EnumType(
|
|
445
|
+
name=_identifier(record["name"], f"{location}.name"),
|
|
446
|
+
values=values,
|
|
447
|
+
comment=_optional_string(record["comment"], f"{location}.comment"),
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _decode_column(value: JsonValue, location: str) -> Column:
|
|
452
|
+
record = _record(
|
|
453
|
+
value,
|
|
454
|
+
{
|
|
455
|
+
"comment",
|
|
456
|
+
"data_type",
|
|
457
|
+
"default",
|
|
458
|
+
"generated",
|
|
459
|
+
"metadata",
|
|
460
|
+
"name",
|
|
461
|
+
"nullable",
|
|
462
|
+
},
|
|
463
|
+
location,
|
|
464
|
+
)
|
|
465
|
+
return Column(
|
|
466
|
+
name=_identifier(record["name"], f"{location}.name"),
|
|
467
|
+
data_type=_decode_sql_type(record["data_type"], f"{location}.data_type"),
|
|
468
|
+
nullable=_boolean(record["nullable"], f"{location}.nullable"),
|
|
469
|
+
default=_optional_expression(record["default"], f"{location}.default"),
|
|
470
|
+
generated=_decode_generated(record["generated"], f"{location}.generated"),
|
|
471
|
+
comment=_optional_string(record["comment"], f"{location}.comment"),
|
|
472
|
+
metadata=_decode_array(
|
|
473
|
+
record["metadata"],
|
|
474
|
+
f"{location}.metadata",
|
|
475
|
+
_decode_metadata,
|
|
476
|
+
),
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def _decode_sql_type(value: JsonValue, location: str) -> SqlType:
|
|
481
|
+
record = _record(
|
|
482
|
+
value,
|
|
483
|
+
{
|
|
484
|
+
"element_type",
|
|
485
|
+
"kind",
|
|
486
|
+
"length",
|
|
487
|
+
"metadata",
|
|
488
|
+
"named_type",
|
|
489
|
+
"precision",
|
|
490
|
+
"scale",
|
|
491
|
+
"source_name",
|
|
492
|
+
},
|
|
493
|
+
location,
|
|
494
|
+
)
|
|
495
|
+
element = record["element_type"]
|
|
496
|
+
named = record["named_type"]
|
|
497
|
+
return SqlType(
|
|
498
|
+
kind=_enum(TypeKind, record["kind"], f"{location}.kind"),
|
|
499
|
+
source_name=_string(record["source_name"], f"{location}.source_name"),
|
|
500
|
+
length=_optional_integer(record["length"], f"{location}.length"),
|
|
501
|
+
precision=_optional_integer(record["precision"], f"{location}.precision"),
|
|
502
|
+
scale=_optional_integer(record["scale"], f"{location}.scale"),
|
|
503
|
+
element_type=(
|
|
504
|
+
None
|
|
505
|
+
if element is None
|
|
506
|
+
else _decode_sql_type(element, f"{location}.element_type")
|
|
507
|
+
),
|
|
508
|
+
named_type=(
|
|
509
|
+
None
|
|
510
|
+
if named is None
|
|
511
|
+
else _decode_reference(named, f"{location}.named_type")
|
|
512
|
+
),
|
|
513
|
+
metadata=_decode_array(
|
|
514
|
+
record["metadata"],
|
|
515
|
+
f"{location}.metadata",
|
|
516
|
+
_decode_metadata,
|
|
517
|
+
),
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def _decode_generated(value: JsonValue, location: str) -> GeneratedValue | None:
|
|
522
|
+
if value is None:
|
|
523
|
+
return None
|
|
524
|
+
record = _record(value, {"expression", "kind"}, location)
|
|
525
|
+
return GeneratedValue(
|
|
526
|
+
kind=_enum(GeneratedKind, record["kind"], f"{location}.kind"),
|
|
527
|
+
expression=_optional_expression(record["expression"], f"{location}.expression"),
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def _decode_key(value: JsonValue, location: str) -> Key:
|
|
532
|
+
record = _record(value, {"columns", "kind", "name"}, location)
|
|
533
|
+
return Key(
|
|
534
|
+
kind=_enum(KeyKind, record["kind"], f"{location}.kind"),
|
|
535
|
+
columns=_decode_identifiers(record["columns"], f"{location}.columns"),
|
|
536
|
+
name=_optional_identifier(record["name"], f"{location}.name"),
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def _decode_relation(value: JsonValue, location: str) -> Relation:
|
|
541
|
+
record = _record(
|
|
542
|
+
value,
|
|
543
|
+
{"columns", "name", "on_delete", "on_update", "target", "target_columns"},
|
|
544
|
+
location,
|
|
545
|
+
)
|
|
546
|
+
return Relation(
|
|
547
|
+
columns=_decode_identifiers(record["columns"], f"{location}.columns"),
|
|
548
|
+
target=_decode_reference(record["target"], f"{location}.target"),
|
|
549
|
+
target_columns=_decode_identifiers(
|
|
550
|
+
record["target_columns"],
|
|
551
|
+
f"{location}.target_columns",
|
|
552
|
+
),
|
|
553
|
+
name=_optional_identifier(record["name"], f"{location}.name"),
|
|
554
|
+
on_update=_enum(
|
|
555
|
+
ReferentialAction,
|
|
556
|
+
record["on_update"],
|
|
557
|
+
f"{location}.on_update",
|
|
558
|
+
),
|
|
559
|
+
on_delete=_enum(
|
|
560
|
+
ReferentialAction,
|
|
561
|
+
record["on_delete"],
|
|
562
|
+
f"{location}.on_delete",
|
|
563
|
+
),
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _decode_index(value: JsonValue, location: str) -> Index:
|
|
568
|
+
record = _record(
|
|
569
|
+
value,
|
|
570
|
+
{"method", "name", "predicate", "terms", "unique"},
|
|
571
|
+
location,
|
|
572
|
+
)
|
|
573
|
+
return Index(
|
|
574
|
+
name=_identifier(record["name"], f"{location}.name"),
|
|
575
|
+
terms=_decode_array(record["terms"], f"{location}.terms", _decode_index_term),
|
|
576
|
+
unique=_boolean(record["unique"], f"{location}.unique"),
|
|
577
|
+
predicate=_optional_expression(record["predicate"], f"{location}.predicate"),
|
|
578
|
+
method=_optional_string(record["method"], f"{location}.method"),
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def _decode_index_term(value: JsonValue, location: str) -> IndexTerm:
|
|
583
|
+
record = _record(value, {"column", "descending", "expression"}, location)
|
|
584
|
+
return IndexTerm(
|
|
585
|
+
column=_optional_identifier(record["column"], f"{location}.column"),
|
|
586
|
+
expression=_optional_expression(record["expression"], f"{location}.expression"),
|
|
587
|
+
descending=_boolean(record["descending"], f"{location}.descending"),
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def _decode_check(value: JsonValue, location: str) -> CheckConstraint:
|
|
592
|
+
record = _record(value, {"expression", "name"}, location)
|
|
593
|
+
return CheckConstraint(
|
|
594
|
+
expression=SqlExpression(
|
|
595
|
+
_string(record["expression"], f"{location}.expression")
|
|
596
|
+
),
|
|
597
|
+
name=_optional_identifier(record["name"], f"{location}.name"),
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def _decode_reference(value: JsonValue, location: str) -> ObjectReference:
|
|
602
|
+
record = _record(value, {"catalog", "name", "schema"}, location)
|
|
603
|
+
return ObjectReference(
|
|
604
|
+
name=_identifier(record["name"], f"{location}.name"),
|
|
605
|
+
schema=_optional_identifier(record["schema"], f"{location}.schema"),
|
|
606
|
+
catalog=_optional_identifier(record["catalog"], f"{location}.catalog"),
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def _decode_metadata(value: JsonValue, location: str) -> MetadataEntry:
|
|
611
|
+
record = _record(value, {"key", "value"}, location)
|
|
612
|
+
return MetadataEntry(
|
|
613
|
+
key=_string(record["key"], f"{location}.key"),
|
|
614
|
+
value=_string(record["value"], f"{location}.value"),
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def _decode_identifiers(value: JsonValue, location: str) -> tuple[Identifier, ...]:
|
|
619
|
+
return tuple(_identifier(item, location) for item in _array(value, location))
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def _decode_array(
|
|
623
|
+
value: JsonValue,
|
|
624
|
+
location: str,
|
|
625
|
+
decoder: Callable[[JsonValue, str], DecodedValue],
|
|
626
|
+
) -> tuple[DecodedValue, ...]:
|
|
627
|
+
return tuple(
|
|
628
|
+
decoder(item, f"{location}[{index}]")
|
|
629
|
+
for index, item in enumerate(_array(value, location))
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def _record(value: JsonValue, fields: set[str], location: str) -> JsonObject:
|
|
634
|
+
record = _object(value, location)
|
|
635
|
+
_require_fields(record, fields, location)
|
|
636
|
+
return record
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def _object(value: object, location: str) -> JsonObject:
|
|
640
|
+
if not isinstance(value, dict):
|
|
641
|
+
message = f"{location} must be an object"
|
|
642
|
+
raise SchemaSerializationError(message)
|
|
643
|
+
return cast(JsonObject, value)
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def _array(value: JsonValue, location: str) -> list[JsonValue]:
|
|
647
|
+
if not isinstance(value, list):
|
|
648
|
+
message = f"{location} must be an array"
|
|
649
|
+
raise SchemaSerializationError(message)
|
|
650
|
+
return value
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def _require_fields(value: JsonObject, fields: set[str], location: str) -> None:
|
|
654
|
+
actual = set(value)
|
|
655
|
+
if actual != fields:
|
|
656
|
+
message = f"{location} fields do not match the schema format"
|
|
657
|
+
raise SchemaSerializationError(message)
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def _string(value: JsonValue, location: str) -> str:
|
|
661
|
+
if not isinstance(value, str):
|
|
662
|
+
message = f"{location} must be a string"
|
|
663
|
+
raise SchemaSerializationError(message)
|
|
664
|
+
return value
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def _optional_string(value: JsonValue, location: str) -> str | None:
|
|
668
|
+
return None if value is None else _string(value, location)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def _integer(value: JsonValue, location: str) -> int:
|
|
672
|
+
if type(value) is not int:
|
|
673
|
+
message = f"{location} must be an integer"
|
|
674
|
+
raise SchemaSerializationError(message)
|
|
675
|
+
return value
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
def _optional_integer(value: JsonValue, location: str) -> int | None:
|
|
679
|
+
return None if value is None else _integer(value, location)
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def _boolean(value: JsonValue, location: str) -> bool:
|
|
683
|
+
if type(value) is not bool:
|
|
684
|
+
message = f"{location} must be a boolean"
|
|
685
|
+
raise SchemaSerializationError(message)
|
|
686
|
+
return value
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def _identifier(value: JsonValue, location: str) -> Identifier:
|
|
690
|
+
return Identifier(_string(value, location))
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
def _optional_identifier(value: JsonValue, location: str) -> Identifier | None:
|
|
694
|
+
return None if value is None else _identifier(value, location)
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def _optional_expression(value: JsonValue, location: str) -> SqlExpression | None:
|
|
698
|
+
return None if value is None else SqlExpression(_string(value, location))
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _enum(
|
|
702
|
+
enum_type: type[EnumValue],
|
|
703
|
+
value: JsonValue,
|
|
704
|
+
location: str,
|
|
705
|
+
) -> EnumValue:
|
|
706
|
+
text = _string(value, location)
|
|
707
|
+
try:
|
|
708
|
+
return enum_type(text)
|
|
709
|
+
except ValueError as error:
|
|
710
|
+
message = f"{location} contains an unsupported value"
|
|
711
|
+
raise SchemaSerializationError(message) from error
|