effect-qb 0.12.3
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.
- package/README.md +1294 -0
- package/dist/mysql.js +57575 -0
- package/dist/postgres.js +6303 -0
- package/package.json +42 -0
- package/src/internal/aggregation-validation.ts +57 -0
- package/src/internal/case-analysis.ts +50 -0
- package/src/internal/coercion-analysis.ts +30 -0
- package/src/internal/coercion-errors.ts +29 -0
- package/src/internal/coercion-kind.ts +32 -0
- package/src/internal/coercion-normalize.ts +7 -0
- package/src/internal/coercion-rules.ts +25 -0
- package/src/internal/column-state.ts +453 -0
- package/src/internal/column.ts +417 -0
- package/src/internal/datatypes/define.ts +44 -0
- package/src/internal/datatypes/lookup.ts +280 -0
- package/src/internal/datatypes/shape.ts +72 -0
- package/src/internal/derived-table.ts +149 -0
- package/src/internal/dialect.ts +30 -0
- package/src/internal/executor.ts +390 -0
- package/src/internal/expression-ast.ts +349 -0
- package/src/internal/expression.ts +325 -0
- package/src/internal/grouping-key.ts +82 -0
- package/src/internal/json/ast.ts +63 -0
- package/src/internal/json/errors.ts +13 -0
- package/src/internal/json/path.ts +227 -0
- package/src/internal/json/shape.ts +1 -0
- package/src/internal/json/types.ts +386 -0
- package/src/internal/mysql-dialect.ts +39 -0
- package/src/internal/mysql-renderer.ts +37 -0
- package/src/internal/plan.ts +64 -0
- package/src/internal/postgres-dialect.ts +34 -0
- package/src/internal/postgres-renderer.ts +40 -0
- package/src/internal/predicate-analysis.ts +71 -0
- package/src/internal/predicate-atom.ts +43 -0
- package/src/internal/predicate-branches.ts +40 -0
- package/src/internal/predicate-context.ts +279 -0
- package/src/internal/predicate-formula.ts +100 -0
- package/src/internal/predicate-key.ts +28 -0
- package/src/internal/predicate-nnf.ts +12 -0
- package/src/internal/predicate-normalize.ts +202 -0
- package/src/internal/projection-alias.ts +15 -0
- package/src/internal/projections.ts +101 -0
- package/src/internal/query-ast.ts +297 -0
- package/src/internal/query-factory.ts +6757 -0
- package/src/internal/query-requirements.ts +40 -0
- package/src/internal/query.ts +1590 -0
- package/src/internal/renderer.ts +102 -0
- package/src/internal/runtime-normalize.ts +344 -0
- package/src/internal/runtime-schema.ts +428 -0
- package/src/internal/runtime-value.ts +85 -0
- package/src/internal/schema-derivation.ts +131 -0
- package/src/internal/sql-expression-renderer.ts +1353 -0
- package/src/internal/table-options.ts +225 -0
- package/src/internal/table.ts +674 -0
- package/src/mysql/column.ts +30 -0
- package/src/mysql/datatypes/index.ts +6 -0
- package/src/mysql/datatypes/spec.ts +180 -0
- package/src/mysql/errors/catalog.ts +51662 -0
- package/src/mysql/errors/fields.ts +21 -0
- package/src/mysql/errors/index.ts +18 -0
- package/src/mysql/errors/normalize.ts +232 -0
- package/src/mysql/errors/requirements.ts +73 -0
- package/src/mysql/executor.ts +134 -0
- package/src/mysql/query.ts +189 -0
- package/src/mysql/renderer.ts +19 -0
- package/src/mysql/table.ts +157 -0
- package/src/mysql.ts +18 -0
- package/src/postgres/column.ts +20 -0
- package/src/postgres/datatypes/index.ts +8 -0
- package/src/postgres/datatypes/spec.ts +264 -0
- package/src/postgres/errors/catalog.ts +452 -0
- package/src/postgres/errors/fields.ts +48 -0
- package/src/postgres/errors/index.ts +4 -0
- package/src/postgres/errors/normalize.ts +209 -0
- package/src/postgres/errors/requirements.ts +65 -0
- package/src/postgres/errors/types.ts +38 -0
- package/src/postgres/executor.ts +131 -0
- package/src/postgres/query.ts +189 -0
- package/src/postgres/renderer.ts +29 -0
- package/src/postgres/table.ts +157 -0
- package/src/postgres.ts +18 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as Query from "./query.js"
|
|
2
|
+
import { type Projection, validateProjections } from "./projections.js"
|
|
3
|
+
import { renderPostgresPlan } from "./postgres-renderer.js"
|
|
4
|
+
|
|
5
|
+
/** Symbol used to attach rendered-query phantom row metadata. */
|
|
6
|
+
export const TypeId: unique symbol = Symbol.for("effect-qb/Renderer")
|
|
7
|
+
|
|
8
|
+
export type TypeId = typeof TypeId
|
|
9
|
+
|
|
10
|
+
/** Column projection metadata emitted by the renderer. */
|
|
11
|
+
export type { Projection }
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Rendered SQL plus phantom row typing.
|
|
15
|
+
*
|
|
16
|
+
* The rendered query exposes the SQL text, parameter values, target dialect,
|
|
17
|
+
* and projection metadata alongside the canonical row type implied by the
|
|
18
|
+
* source query plan.
|
|
19
|
+
*/
|
|
20
|
+
export interface RenderedQuery<Row, Dialect extends string = string> {
|
|
21
|
+
readonly sql: string
|
|
22
|
+
readonly params: readonly unknown[]
|
|
23
|
+
readonly dialect: Dialect
|
|
24
|
+
readonly projections: readonly Projection[]
|
|
25
|
+
readonly [TypeId]: {
|
|
26
|
+
readonly row: Row
|
|
27
|
+
readonly dialect: Dialect
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Extracts the row type carried by a rendered query. */
|
|
32
|
+
export type RowOf<Value extends RenderedQuery<any, any>> = Value[typeof TypeId]["row"]
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Public rendering contract.
|
|
36
|
+
*
|
|
37
|
+
* Renderers only accept complete, dialect-compatible plans. The returned
|
|
38
|
+
* `RenderedQuery` keeps the canonical `Query.ResultRow<...>` type attached for
|
|
39
|
+
* downstream executor layers, and the built-in renderer also performs a
|
|
40
|
+
* matching runtime aggregate-shape validation.
|
|
41
|
+
*/
|
|
42
|
+
export interface Renderer<Dialect extends string = string> {
|
|
43
|
+
readonly dialect: Dialect
|
|
44
|
+
render<PlanValue extends Query.QueryPlan<any, any, any, any, any, any, any, any, any>>(
|
|
45
|
+
plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
|
|
46
|
+
): RenderedQuery<any, Dialect>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type CustomRender<Dialect extends string> = <PlanValue extends Query.QueryPlan<any, any, any, any, any, any, any, any, any>>(
|
|
50
|
+
plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
|
|
51
|
+
) => {
|
|
52
|
+
readonly sql: string
|
|
53
|
+
readonly params?: readonly unknown[]
|
|
54
|
+
readonly projections?: readonly Projection[]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Constructs a renderer from a dialect and optional implementation callback.
|
|
59
|
+
*
|
|
60
|
+
* When no callback is provided, the library supplies a built-in renderer for
|
|
61
|
+
* `"postgres"` that consumes the query AST directly and produces SQL text plus
|
|
62
|
+
* parameter values.
|
|
63
|
+
*/
|
|
64
|
+
export function make(dialect: "postgres"): Renderer<"postgres">
|
|
65
|
+
export function make<Dialect extends string>(
|
|
66
|
+
dialect: Dialect,
|
|
67
|
+
render: CustomRender<Dialect>
|
|
68
|
+
): Renderer<Dialect>
|
|
69
|
+
export function make<Dialect extends string>(
|
|
70
|
+
dialect: Dialect,
|
|
71
|
+
render?: CustomRender<Dialect>
|
|
72
|
+
): Renderer<Dialect> {
|
|
73
|
+
const implementation = render ?? ((dialect === "postgres"
|
|
74
|
+
? renderPostgresPlan
|
|
75
|
+
: undefined) as CustomRender<Dialect> | undefined)
|
|
76
|
+
|
|
77
|
+
if (!implementation) {
|
|
78
|
+
throw new Error(`No built-in renderer for dialect: ${dialect}`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
dialect,
|
|
83
|
+
render(plan) {
|
|
84
|
+
const rendered = implementation(plan)
|
|
85
|
+
const projections = rendered.projections ?? []
|
|
86
|
+
validateProjections(projections)
|
|
87
|
+
return {
|
|
88
|
+
sql: rendered.sql,
|
|
89
|
+
params: rendered.params ?? [],
|
|
90
|
+
projections,
|
|
91
|
+
dialect,
|
|
92
|
+
[TypeId]: {
|
|
93
|
+
row: undefined as any,
|
|
94
|
+
dialect
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} as Renderer<Dialect>
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Built-in Postgres renderer backed by the current query AST. */
|
|
102
|
+
export const postgres = make("postgres")
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import type * as Expression from "./expression.js"
|
|
2
|
+
import { mysqlDatatypeKinds } from "../mysql/datatypes/spec.js"
|
|
3
|
+
import { postgresDatatypeKinds } from "../postgres/datatypes/spec.js"
|
|
4
|
+
import type { RuntimeTag } from "./datatypes/shape.js"
|
|
5
|
+
|
|
6
|
+
const stripParameterizedKind = (kind: string): string => {
|
|
7
|
+
const openParen = kind.indexOf("(")
|
|
8
|
+
return openParen === -1 ? kind : kind.slice(0, openParen)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const stripArrayKind = (kind: string): string => {
|
|
12
|
+
let current = kind
|
|
13
|
+
while (current.endsWith("[]")) {
|
|
14
|
+
current = current.slice(0, -2)
|
|
15
|
+
}
|
|
16
|
+
return current
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const baseKind = (kind: string): string => stripArrayKind(stripParameterizedKind(kind))
|
|
20
|
+
|
|
21
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
22
|
+
typeof value === "object" && value !== null && !Array.isArray(value)
|
|
23
|
+
|
|
24
|
+
const pad = (value: number, width = 2): string => value.toString().padStart(width, "0")
|
|
25
|
+
|
|
26
|
+
const formatLocalDate = (value: Date): string =>
|
|
27
|
+
`${value.getUTCFullYear()}-${pad(value.getUTCMonth() + 1)}-${pad(value.getUTCDate())}`
|
|
28
|
+
|
|
29
|
+
const formatLocalTime = (value: Date): string => {
|
|
30
|
+
const milliseconds = value.getUTCMilliseconds()
|
|
31
|
+
const base = `${pad(value.getUTCHours())}:${pad(value.getUTCMinutes())}:${pad(value.getUTCSeconds())}`
|
|
32
|
+
return milliseconds === 0 ? base : `${base}.${pad(milliseconds, 3)}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const formatLocalDateTime = (value: Date): string => {
|
|
36
|
+
const milliseconds = value.getUTCMilliseconds()
|
|
37
|
+
const base = `${formatLocalDate(value)}T${pad(value.getUTCHours())}:${pad(value.getUTCMinutes())}:${pad(value.getUTCSeconds())}`
|
|
38
|
+
return milliseconds === 0 ? base : `${base}.${pad(milliseconds, 3)}`
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const runtimeTagOfBaseDbType = (
|
|
42
|
+
dialect: string,
|
|
43
|
+
kind: string
|
|
44
|
+
): RuntimeTag | undefined => {
|
|
45
|
+
const normalizedKind = baseKind(kind)
|
|
46
|
+
if (dialect === "postgres") {
|
|
47
|
+
return postgresDatatypeKinds[normalizedKind as keyof typeof postgresDatatypeKinds]?.runtime
|
|
48
|
+
}
|
|
49
|
+
if (dialect === "mysql") {
|
|
50
|
+
return mysqlDatatypeKinds[normalizedKind as keyof typeof mysqlDatatypeKinds]?.runtime
|
|
51
|
+
}
|
|
52
|
+
return undefined
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const expectString = (value: unknown, label: string): string => {
|
|
56
|
+
if (typeof value === "string") {
|
|
57
|
+
return value
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`Expected ${label} as string`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const normalizeNumber = (value: unknown): number => {
|
|
63
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
64
|
+
return value
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
67
|
+
const parsed = Number(value)
|
|
68
|
+
if (Number.isFinite(parsed)) {
|
|
69
|
+
return parsed
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (typeof value === "bigint" && Number.isSafeInteger(Number(value))) {
|
|
73
|
+
return Number(value)
|
|
74
|
+
}
|
|
75
|
+
throw new Error("Expected a finite numeric value")
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const normalizeBoolean = (value: unknown): boolean => {
|
|
79
|
+
if (typeof value === "boolean") {
|
|
80
|
+
return value
|
|
81
|
+
}
|
|
82
|
+
if (typeof value === "number") {
|
|
83
|
+
if (value === 1) {
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
if (value === 0) {
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (typeof value === "string") {
|
|
91
|
+
const normalized = value.trim().toLowerCase()
|
|
92
|
+
if (normalized === "true" || normalized === "t" || normalized === "1") {
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
if (normalized === "false" || normalized === "f" || normalized === "0") {
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
throw new Error("Expected a boolean-like value")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const normalizeBigIntString = (value: unknown): string => {
|
|
103
|
+
if (typeof value === "bigint") {
|
|
104
|
+
return value.toString()
|
|
105
|
+
}
|
|
106
|
+
if (typeof value === "number" && Number.isSafeInteger(value)) {
|
|
107
|
+
return BigInt(value).toString()
|
|
108
|
+
}
|
|
109
|
+
if (typeof value === "string" && /^-?\d+$/.test(value.trim())) {
|
|
110
|
+
return BigInt(value.trim()).toString()
|
|
111
|
+
}
|
|
112
|
+
throw new Error("Expected an integer-like bigint value")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const canonicalizeDecimalString = (input: string): string => {
|
|
116
|
+
const trimmed = input.trim()
|
|
117
|
+
const match = /^([+-]?)(\d+)(?:\.(\d+))?$/.exec(trimmed)
|
|
118
|
+
if (match === null) {
|
|
119
|
+
throw new Error("Expected a decimal string")
|
|
120
|
+
}
|
|
121
|
+
const sign = match[1] === "-" ? "-" : ""
|
|
122
|
+
const integer = match[2]!.replace(/^0+(?=\d)/, "") || "0"
|
|
123
|
+
const fraction = (match[3] ?? "").replace(/0+$/, "")
|
|
124
|
+
if (fraction.length === 0) {
|
|
125
|
+
return `${sign}${integer}`
|
|
126
|
+
}
|
|
127
|
+
return `${sign}${integer}.${fraction}`
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const normalizeDecimalString = (value: unknown): string => {
|
|
131
|
+
if (typeof value === "string") {
|
|
132
|
+
return canonicalizeDecimalString(value)
|
|
133
|
+
}
|
|
134
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
135
|
+
const rendered = String(value)
|
|
136
|
+
if (/[eE]/.test(rendered)) {
|
|
137
|
+
throw new Error("Scientific notation is not a supported decimal runtime")
|
|
138
|
+
}
|
|
139
|
+
return canonicalizeDecimalString(rendered)
|
|
140
|
+
}
|
|
141
|
+
throw new Error("Expected a decimal-like value")
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const normalizeLocalDate = (value: unknown): string => {
|
|
145
|
+
if (value instanceof Date) {
|
|
146
|
+
return formatLocalDate(value)
|
|
147
|
+
}
|
|
148
|
+
const raw = expectString(value, "local date").trim()
|
|
149
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) {
|
|
150
|
+
return raw
|
|
151
|
+
}
|
|
152
|
+
const parsed = new Date(raw)
|
|
153
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
154
|
+
return formatLocalDate(parsed)
|
|
155
|
+
}
|
|
156
|
+
throw new Error("Expected a local-date value")
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const normalizeLocalTime = (value: unknown): string => {
|
|
160
|
+
if (value instanceof Date) {
|
|
161
|
+
return formatLocalTime(value)
|
|
162
|
+
}
|
|
163
|
+
const raw = expectString(value, "local time").trim()
|
|
164
|
+
if (/^\d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(raw)) {
|
|
165
|
+
return raw
|
|
166
|
+
}
|
|
167
|
+
throw new Error("Expected a local-time value")
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const normalizeOffsetTime = (value: unknown): string => {
|
|
171
|
+
if (value instanceof Date) {
|
|
172
|
+
return `${formatLocalTime(value)}Z`
|
|
173
|
+
}
|
|
174
|
+
const raw = expectString(value, "offset time").trim()
|
|
175
|
+
if (/^\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(raw)) {
|
|
176
|
+
return raw
|
|
177
|
+
}
|
|
178
|
+
throw new Error("Expected an offset-time value")
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const normalizeLocalDateTime = (value: unknown): string => {
|
|
182
|
+
if (value instanceof Date) {
|
|
183
|
+
return formatLocalDateTime(value)
|
|
184
|
+
}
|
|
185
|
+
const raw = expectString(value, "local datetime").trim()
|
|
186
|
+
if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(raw)) {
|
|
187
|
+
return raw.replace(" ", "T")
|
|
188
|
+
}
|
|
189
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(raw)) {
|
|
190
|
+
const parsed = new Date(raw)
|
|
191
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
192
|
+
return formatLocalDateTime(parsed)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
throw new Error("Expected a local-datetime value")
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const normalizeInstant = (value: unknown): string => {
|
|
199
|
+
if (value instanceof Date) {
|
|
200
|
+
return value.toISOString()
|
|
201
|
+
}
|
|
202
|
+
const raw = expectString(value, "instant").trim()
|
|
203
|
+
if (!/[zZ]|[+-]\d{2}:\d{2}$/.test(raw)) {
|
|
204
|
+
throw new Error("Instant values require a timezone offset")
|
|
205
|
+
}
|
|
206
|
+
const parsed = new Date(raw)
|
|
207
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
208
|
+
throw new Error("Expected an ISO instant value")
|
|
209
|
+
}
|
|
210
|
+
return parsed.toISOString()
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const normalizeYear = (value: unknown): string => {
|
|
214
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 9999) {
|
|
215
|
+
return pad(value, 4)
|
|
216
|
+
}
|
|
217
|
+
const raw = expectString(value, "year").trim()
|
|
218
|
+
if (/^\d{4}$/.test(raw)) {
|
|
219
|
+
return raw
|
|
220
|
+
}
|
|
221
|
+
throw new Error("Expected a four-digit year")
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const normalizeBytes = (value: unknown): Uint8Array => {
|
|
225
|
+
if (value instanceof Uint8Array) {
|
|
226
|
+
return new Uint8Array(value)
|
|
227
|
+
}
|
|
228
|
+
if (typeof Buffer !== "undefined" && value instanceof Buffer) {
|
|
229
|
+
return new Uint8Array(value)
|
|
230
|
+
}
|
|
231
|
+
throw new Error("Expected a byte array value")
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const isJsonValue = (value: unknown): boolean => {
|
|
235
|
+
if (value === null) {
|
|
236
|
+
return true
|
|
237
|
+
}
|
|
238
|
+
switch (typeof value) {
|
|
239
|
+
case "string":
|
|
240
|
+
case "number":
|
|
241
|
+
case "boolean":
|
|
242
|
+
return true
|
|
243
|
+
case "object":
|
|
244
|
+
if (Array.isArray(value)) {
|
|
245
|
+
return value.every(isJsonValue)
|
|
246
|
+
}
|
|
247
|
+
return isRecord(value) && Object.values(value).every(isJsonValue)
|
|
248
|
+
default:
|
|
249
|
+
return false
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const normalizeJson = (value: unknown): unknown => {
|
|
254
|
+
if (typeof value === "string") {
|
|
255
|
+
const parsed = JSON.parse(value)
|
|
256
|
+
if (isJsonValue(parsed)) {
|
|
257
|
+
return parsed
|
|
258
|
+
}
|
|
259
|
+
throw new Error("Parsed JSON value is not a valid JSON runtime")
|
|
260
|
+
}
|
|
261
|
+
if (isJsonValue(value)) {
|
|
262
|
+
return value
|
|
263
|
+
}
|
|
264
|
+
throw new Error("Expected a JSON value")
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const normalizeDbValue = (
|
|
268
|
+
dbType: Expression.DbType.Any,
|
|
269
|
+
value: unknown
|
|
270
|
+
): unknown => {
|
|
271
|
+
if (value === null) {
|
|
272
|
+
return null
|
|
273
|
+
}
|
|
274
|
+
if ("base" in dbType) {
|
|
275
|
+
return normalizeDbValue(dbType.base, value)
|
|
276
|
+
}
|
|
277
|
+
if ("element" in dbType) {
|
|
278
|
+
if (!Array.isArray(value)) {
|
|
279
|
+
throw new Error("Expected an array value")
|
|
280
|
+
}
|
|
281
|
+
return value.map((entry) => normalizeDbValue(dbType.element, entry))
|
|
282
|
+
}
|
|
283
|
+
if ("fields" in dbType) {
|
|
284
|
+
if (!isRecord(value)) {
|
|
285
|
+
throw new Error("Expected a record value")
|
|
286
|
+
}
|
|
287
|
+
const normalized: Record<string, unknown> = {}
|
|
288
|
+
for (const [key, fieldDbType] of Object.entries(dbType.fields)) {
|
|
289
|
+
if (key in value) {
|
|
290
|
+
normalized[key] = normalizeDbValue(fieldDbType, value[key])
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return normalized
|
|
294
|
+
}
|
|
295
|
+
if ("variant" in dbType && dbType.variant === "json") {
|
|
296
|
+
return normalizeJson(value)
|
|
297
|
+
}
|
|
298
|
+
if ("variant" in dbType && (dbType.variant === "enum" || dbType.variant === "set")) {
|
|
299
|
+
return expectString(value, "text")
|
|
300
|
+
}
|
|
301
|
+
switch (runtimeTagOfBaseDbType(dbType.dialect, dbType.kind)) {
|
|
302
|
+
case "string":
|
|
303
|
+
return expectString(value, "text")
|
|
304
|
+
case "number":
|
|
305
|
+
return normalizeNumber(value)
|
|
306
|
+
case "bigintString":
|
|
307
|
+
return normalizeBigIntString(value)
|
|
308
|
+
case "boolean":
|
|
309
|
+
return normalizeBoolean(value)
|
|
310
|
+
case "json":
|
|
311
|
+
return normalizeJson(value)
|
|
312
|
+
case "localDate":
|
|
313
|
+
return normalizeLocalDate(value)
|
|
314
|
+
case "localTime":
|
|
315
|
+
return normalizeLocalTime(value)
|
|
316
|
+
case "offsetTime":
|
|
317
|
+
return normalizeOffsetTime(value)
|
|
318
|
+
case "localDateTime":
|
|
319
|
+
return normalizeLocalDateTime(value)
|
|
320
|
+
case "instant":
|
|
321
|
+
return normalizeInstant(value)
|
|
322
|
+
case "year":
|
|
323
|
+
return normalizeYear(value)
|
|
324
|
+
case "decimalString":
|
|
325
|
+
return normalizeDecimalString(value)
|
|
326
|
+
case "bytes":
|
|
327
|
+
return normalizeBytes(value)
|
|
328
|
+
case "array":
|
|
329
|
+
if (!Array.isArray(value)) {
|
|
330
|
+
throw new Error("Expected an array value")
|
|
331
|
+
}
|
|
332
|
+
return value
|
|
333
|
+
case "record":
|
|
334
|
+
if (!isRecord(value)) {
|
|
335
|
+
throw new Error("Expected a record value")
|
|
336
|
+
}
|
|
337
|
+
return value
|
|
338
|
+
case "null":
|
|
339
|
+
return null
|
|
340
|
+
case "unknown":
|
|
341
|
+
case undefined:
|
|
342
|
+
return value
|
|
343
|
+
}
|
|
344
|
+
}
|