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,674 @@
|
|
|
1
|
+
import { pipeArguments, type Pipeable } from "effect/Pipeable"
|
|
2
|
+
import * as Schema from "effect/Schema"
|
|
3
|
+
|
|
4
|
+
import * as Plan from "./plan.js"
|
|
5
|
+
import type { BoundColumnFrom } from "./column-state.js"
|
|
6
|
+
import { bindColumn, type AnyColumnDefinition } from "./column-state.js"
|
|
7
|
+
import {
|
|
8
|
+
collectInlineOptions,
|
|
9
|
+
normalizeColumnList,
|
|
10
|
+
resolvePrimaryKeyColumns,
|
|
11
|
+
type DeclaredTableOptions as InternalDeclaredTableOptions,
|
|
12
|
+
type NormalizeColumns,
|
|
13
|
+
type TableOptionSpec,
|
|
14
|
+
type ValidateKnownColumns,
|
|
15
|
+
type ValidatePrimaryKeyColumns,
|
|
16
|
+
validateOptions
|
|
17
|
+
} from "./table-options.js"
|
|
18
|
+
import {
|
|
19
|
+
deriveSchemas,
|
|
20
|
+
type InsertRow,
|
|
21
|
+
type SelectRow,
|
|
22
|
+
type TableFieldMap,
|
|
23
|
+
type UpdateRow
|
|
24
|
+
} from "./schema-derivation.js"
|
|
25
|
+
|
|
26
|
+
/** Symbol used to attach table-definition metadata. */
|
|
27
|
+
export const TypeId: unique symbol = Symbol.for("effect-qb/Table")
|
|
28
|
+
/** Symbol for the normalized table option list. */
|
|
29
|
+
export const OptionsSymbol: unique symbol = Symbol.for("effect-qb/Table/normalizedOptions")
|
|
30
|
+
/** Symbol used by `Table.Class` to declare table-level options. */
|
|
31
|
+
export const options: unique symbol = Symbol.for("effect-qb/Table/declaredOptions")
|
|
32
|
+
|
|
33
|
+
const CacheSymbol: unique symbol = Symbol.for("effect-qb/Table/cache")
|
|
34
|
+
const DeclaredOptionsSymbol: unique symbol = Symbol.for("effect-qb/Table/factoryDeclaredOptions")
|
|
35
|
+
|
|
36
|
+
type InlinePrimaryKeyKeys<Fields extends TableFieldMap> = Extract<{
|
|
37
|
+
[K in keyof Fields]: Fields[K]["metadata"]["primaryKey"] extends true ? K : never
|
|
38
|
+
}[keyof Fields], string>
|
|
39
|
+
|
|
40
|
+
type TableDialect<Fields extends TableFieldMap> = Fields[keyof Fields][typeof import("./column-state.js").ColumnTypeId]["dbType"]["dialect"]
|
|
41
|
+
type TableKind = "schema" | "alias"
|
|
42
|
+
type DefaultSchemaName = "public"
|
|
43
|
+
type ClassOptionSpec = Exclude<TableOptionSpec, { readonly kind: "primaryKey" }>
|
|
44
|
+
type ClassTableOption = TableOption<ClassOptionSpec>
|
|
45
|
+
type ClassDeclaredTableOptions = readonly ClassTableOption[]
|
|
46
|
+
|
|
47
|
+
type BuildPrimaryKey<
|
|
48
|
+
Table extends TableDefinition<any, any, any, "schema", any>,
|
|
49
|
+
Spec extends TableOptionSpec
|
|
50
|
+
> = Spec extends { readonly kind: "primaryKey"; readonly columns: infer Columns extends readonly string[] }
|
|
51
|
+
? Columns[number] & keyof Table[typeof TypeId]["fields"] & string
|
|
52
|
+
: Table[typeof TypeId]["primaryKey"][number]
|
|
53
|
+
|
|
54
|
+
type OptionInputTable<
|
|
55
|
+
Table extends TableDefinition<any, any, any, "schema", any>,
|
|
56
|
+
Spec extends TableOptionSpec
|
|
57
|
+
> = Spec extends { readonly kind: "primaryKey"; readonly columns: infer Columns extends readonly string[] }
|
|
58
|
+
? ValidatePrimaryKeyColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : Table
|
|
59
|
+
: Spec extends { readonly columns: infer Columns extends readonly string[] }
|
|
60
|
+
? ValidateKnownColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : Table
|
|
61
|
+
: Table
|
|
62
|
+
|
|
63
|
+
type ApplyOption<
|
|
64
|
+
Table extends TableDefinition<any, any, any, "schema", any>,
|
|
65
|
+
Spec extends TableOptionSpec
|
|
66
|
+
> = TableDefinition<
|
|
67
|
+
Table[typeof TypeId]["name"],
|
|
68
|
+
Table[typeof TypeId]["fields"],
|
|
69
|
+
BuildPrimaryKey<Table, Spec>,
|
|
70
|
+
"schema"
|
|
71
|
+
>
|
|
72
|
+
|
|
73
|
+
export type MissingSelfGeneric = "Missing `Self` generic - use `class Self extends Table.Class<Self>(...) {}`"
|
|
74
|
+
|
|
75
|
+
/** Bound columns keyed by field name for a particular table. */
|
|
76
|
+
export type BoundColumns<
|
|
77
|
+
Name extends string,
|
|
78
|
+
Fields extends TableFieldMap
|
|
79
|
+
> = {
|
|
80
|
+
readonly [K in keyof Fields]: BoundColumnFrom<Fields[K], Name, Extract<K, string>>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Derived runtime schemas exposed by a table definition. */
|
|
84
|
+
export interface TableSchemas<
|
|
85
|
+
Fields extends TableFieldMap,
|
|
86
|
+
PrimaryKeyColumns extends keyof Fields & string
|
|
87
|
+
> {
|
|
88
|
+
readonly select: Schema.Schema<SelectRow<Fields>>
|
|
89
|
+
readonly insert: Schema.Schema<InsertRow<Fields>>
|
|
90
|
+
readonly update: Schema.Schema<UpdateRow<Fields, PrimaryKeyColumns>>
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface TableState<
|
|
94
|
+
Name extends string,
|
|
95
|
+
Fields extends TableFieldMap,
|
|
96
|
+
PrimaryKeyColumns extends keyof Fields & string,
|
|
97
|
+
Kind extends TableKind = "schema",
|
|
98
|
+
SchemaName extends string | undefined = DefaultSchemaName
|
|
99
|
+
> {
|
|
100
|
+
readonly name: Name
|
|
101
|
+
readonly baseName: string
|
|
102
|
+
readonly schemaName: SchemaName
|
|
103
|
+
readonly fields: Fields
|
|
104
|
+
readonly primaryKey: readonly PrimaryKeyColumns[]
|
|
105
|
+
readonly kind: Kind
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Namespace-scoped table builder. */
|
|
109
|
+
export interface TableSchemaNamespace<SchemaName extends string> {
|
|
110
|
+
readonly schemaName: SchemaName
|
|
111
|
+
readonly table: <
|
|
112
|
+
Name extends string,
|
|
113
|
+
Fields extends TableFieldMap,
|
|
114
|
+
PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>
|
|
115
|
+
>(
|
|
116
|
+
name: Name,
|
|
117
|
+
fields: Fields,
|
|
118
|
+
...options: InternalDeclaredTableOptions
|
|
119
|
+
) => TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type DeclaredTableOptions = InternalDeclaredTableOptions
|
|
123
|
+
|
|
124
|
+
export type TableDefinition<
|
|
125
|
+
Name extends string,
|
|
126
|
+
Fields extends TableFieldMap,
|
|
127
|
+
PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>,
|
|
128
|
+
Kind extends TableKind = "schema",
|
|
129
|
+
SchemaName extends string | undefined = DefaultSchemaName
|
|
130
|
+
> = Pipeable & {
|
|
131
|
+
readonly name: Name
|
|
132
|
+
readonly columns: BoundColumns<Name, Fields>
|
|
133
|
+
readonly schemas: TableSchemas<Fields, PrimaryKeyColumns>
|
|
134
|
+
readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, Kind, SchemaName>
|
|
135
|
+
readonly [Plan.TypeId]: Plan.State<
|
|
136
|
+
BoundColumns<Name, Fields>,
|
|
137
|
+
never,
|
|
138
|
+
Record<Name, Plan.Source<Name>>,
|
|
139
|
+
TableDialect<Fields>
|
|
140
|
+
>
|
|
141
|
+
readonly [OptionsSymbol]: readonly TableOptionSpec[]
|
|
142
|
+
readonly [DeclaredOptionsSymbol]: readonly TableOptionSpec[]
|
|
143
|
+
} & BoundColumns<Name, Fields> & Plan.Plan<
|
|
144
|
+
BoundColumns<Name, Fields>,
|
|
145
|
+
never,
|
|
146
|
+
Record<Name, Plan.Source<Name>>,
|
|
147
|
+
TableDialect<Fields>
|
|
148
|
+
>
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Static class-based table definition.
|
|
152
|
+
*
|
|
153
|
+
* The class object itself acts as the table definition, exposing static bound
|
|
154
|
+
* columns, derived schemas, and plan metadata.
|
|
155
|
+
*/
|
|
156
|
+
export type TableClassStatic<
|
|
157
|
+
Name extends string,
|
|
158
|
+
Fields extends TableFieldMap,
|
|
159
|
+
PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>,
|
|
160
|
+
SchemaName extends string | undefined = DefaultSchemaName
|
|
161
|
+
> = (abstract new (...args: any[]) => any) & Pipeable & {
|
|
162
|
+
readonly columns: BoundColumns<Name, Fields>
|
|
163
|
+
readonly schemas: TableSchemas<Fields, PrimaryKeyColumns>
|
|
164
|
+
readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>
|
|
165
|
+
readonly [Plan.TypeId]: Plan.State<
|
|
166
|
+
BoundColumns<Name, Fields>,
|
|
167
|
+
never,
|
|
168
|
+
Record<Name, Plan.Source<Name>>,
|
|
169
|
+
TableDialect<Fields>
|
|
170
|
+
>
|
|
171
|
+
readonly [OptionsSymbol]: readonly TableOptionSpec[]
|
|
172
|
+
readonly [DeclaredOptionsSymbol]?: readonly TableOptionSpec[]
|
|
173
|
+
readonly [options]?: ClassDeclaredTableOptions
|
|
174
|
+
readonly tableName: Name
|
|
175
|
+
} & BoundColumns<Name, Fields> & Plan.Plan<
|
|
176
|
+
BoundColumns<Name, Fields>,
|
|
177
|
+
never,
|
|
178
|
+
Record<Name, Plan.Source<Name>>,
|
|
179
|
+
TableDialect<Fields>
|
|
180
|
+
>
|
|
181
|
+
|
|
182
|
+
/** Minimal structural table-like contract used across helper APIs. */
|
|
183
|
+
export type AnyTable = TableDefinition<any, any, any, any, any> | TableClassStatic<any, any, any, any>
|
|
184
|
+
|
|
185
|
+
/** Public table-option builder type used by `Table.index`, `Table.primaryKey`, and friends. */
|
|
186
|
+
export type TableOption<
|
|
187
|
+
Spec extends TableOptionSpec = TableOptionSpec
|
|
188
|
+
> = {
|
|
189
|
+
<
|
|
190
|
+
Name extends string,
|
|
191
|
+
Fields extends TableFieldMap,
|
|
192
|
+
PrimaryKeyColumns extends keyof Fields & string
|
|
193
|
+
>(
|
|
194
|
+
table: OptionInputTable<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", any>, Spec>
|
|
195
|
+
): ApplyOption<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", any>, Spec>
|
|
196
|
+
readonly option: Spec
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const TableProto = {
|
|
200
|
+
pipe(this: unknown) {
|
|
201
|
+
return pipeArguments(this, arguments)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
type BuildArtifacts<
|
|
206
|
+
Name extends string,
|
|
207
|
+
Fields extends TableFieldMap,
|
|
208
|
+
PrimaryKeyColumns extends keyof Fields & string
|
|
209
|
+
> = {
|
|
210
|
+
readonly columns: BoundColumns<Name, Fields>
|
|
211
|
+
readonly schemas: TableSchemas<Fields, PrimaryKeyColumns>
|
|
212
|
+
readonly normalizedOptions: readonly TableOptionSpec[]
|
|
213
|
+
readonly primaryKey: readonly PrimaryKeyColumns[]
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const buildArtifacts = <
|
|
217
|
+
Name extends string,
|
|
218
|
+
Fields extends TableFieldMap,
|
|
219
|
+
SchemaName extends string | undefined
|
|
220
|
+
>(
|
|
221
|
+
name: Name,
|
|
222
|
+
fields: Fields,
|
|
223
|
+
declaredOptions: readonly TableOptionSpec[],
|
|
224
|
+
schemaName: SchemaName
|
|
225
|
+
): BuildArtifacts<Name, Fields, keyof Fields & string> => {
|
|
226
|
+
const normalizedOptions = [...collectInlineOptions(fields), ...declaredOptions]
|
|
227
|
+
validateFieldDialects(name, fields)
|
|
228
|
+
validateOptions(name, fields, declaredOptions)
|
|
229
|
+
const primaryKey = resolvePrimaryKeyColumns(fields, declaredOptions) as readonly (keyof Fields & string)[]
|
|
230
|
+
const columns = Object.fromEntries(
|
|
231
|
+
Object.entries(fields).map(([key, column]) => [key, bindColumn(name, key, column, name, schemaName)])
|
|
232
|
+
) as BoundColumns<Name, Fields>
|
|
233
|
+
const schemas = deriveSchemas(fields, primaryKey)
|
|
234
|
+
return {
|
|
235
|
+
columns,
|
|
236
|
+
schemas,
|
|
237
|
+
normalizedOptions,
|
|
238
|
+
primaryKey
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const makeTable = <
|
|
243
|
+
Name extends string,
|
|
244
|
+
Fields extends TableFieldMap,
|
|
245
|
+
PrimaryKeyColumns extends keyof Fields & string,
|
|
246
|
+
Kind extends TableKind = "schema",
|
|
247
|
+
SchemaName extends string | undefined = DefaultSchemaName
|
|
248
|
+
>(
|
|
249
|
+
name: Name,
|
|
250
|
+
fields: Fields,
|
|
251
|
+
declaredOptions: readonly TableOptionSpec[],
|
|
252
|
+
baseName: string = name,
|
|
253
|
+
kind: Kind = "schema" as Kind,
|
|
254
|
+
schemaName?: SchemaName,
|
|
255
|
+
schemaMode: "default" | "explicit" = "default"
|
|
256
|
+
): TableDefinition<Name, Fields, PrimaryKeyColumns, Kind, SchemaName> => {
|
|
257
|
+
const resolvedSchemaName = schemaMode === "explicit"
|
|
258
|
+
? schemaName
|
|
259
|
+
: ("public" as SchemaName)
|
|
260
|
+
const artifacts = buildArtifacts(name, fields, declaredOptions, resolvedSchemaName)
|
|
261
|
+
const dialect = resolveFieldDialect(fields)
|
|
262
|
+
const table = Object.create(TableProto)
|
|
263
|
+
table.name = name
|
|
264
|
+
table.columns = artifacts.columns
|
|
265
|
+
table.schemas = artifacts.schemas
|
|
266
|
+
table[TypeId] = {
|
|
267
|
+
name,
|
|
268
|
+
baseName,
|
|
269
|
+
schemaName: resolvedSchemaName,
|
|
270
|
+
fields,
|
|
271
|
+
primaryKey: artifacts.primaryKey,
|
|
272
|
+
kind
|
|
273
|
+
}
|
|
274
|
+
table[Plan.TypeId] = {
|
|
275
|
+
selection: artifacts.columns,
|
|
276
|
+
required: undefined as never,
|
|
277
|
+
available: {
|
|
278
|
+
[name]: {
|
|
279
|
+
name,
|
|
280
|
+
mode: "required",
|
|
281
|
+
baseName
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
dialect
|
|
285
|
+
}
|
|
286
|
+
table[OptionsSymbol] = artifacts.normalizedOptions
|
|
287
|
+
table[DeclaredOptionsSymbol] = declaredOptions
|
|
288
|
+
for (const [key, value] of Object.entries(artifacts.columns)) {
|
|
289
|
+
Object.defineProperty(table, key, {
|
|
290
|
+
enumerable: true,
|
|
291
|
+
value
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
return table
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const extractDeclaredOptions = (
|
|
298
|
+
declaredOptions: DeclaredTableOptions | undefined
|
|
299
|
+
): readonly TableOptionSpec[] => declaredOptions?.map((option) => option.option) ?? []
|
|
300
|
+
|
|
301
|
+
const validateClassOptions = (declaredOptions: readonly TableOptionSpec[]): void => {
|
|
302
|
+
for (const option of declaredOptions) {
|
|
303
|
+
if (option.kind === "primaryKey") {
|
|
304
|
+
throw new Error("Table.Class does not support table-level primary keys; declare primary keys inline on columns")
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const resolveFieldDialect = (fields: TableFieldMap): string => {
|
|
310
|
+
const dialects = [...new Set(Object.values(fields).map((field) => field.metadata.dbType.dialect))]
|
|
311
|
+
if (dialects.length === 0) {
|
|
312
|
+
return "postgres"
|
|
313
|
+
}
|
|
314
|
+
if (dialects.length > 1) {
|
|
315
|
+
throw new Error(`Mixed table dialects are not supported: ${dialects.join(", ")}`)
|
|
316
|
+
}
|
|
317
|
+
return dialects[0]!
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const validateFieldDialects = (tableName: string, fields: TableFieldMap): void => {
|
|
321
|
+
try {
|
|
322
|
+
resolveFieldDialect(fields)
|
|
323
|
+
} catch (error) {
|
|
324
|
+
throw new Error(`Invalid dialects for table '${tableName}': ${(error as Error).message}`)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const ensureClassArtifacts = <
|
|
329
|
+
Name extends string,
|
|
330
|
+
Fields extends TableFieldMap,
|
|
331
|
+
PrimaryKeyColumns extends keyof Fields & string
|
|
332
|
+
>(
|
|
333
|
+
self: TableClassStatic<Name, Fields, PrimaryKeyColumns> & {
|
|
334
|
+
readonly [CacheSymbol]?: BuildArtifacts<Name, Fields, PrimaryKeyColumns>
|
|
335
|
+
}
|
|
336
|
+
): BuildArtifacts<Name, Fields, PrimaryKeyColumns> => {
|
|
337
|
+
const cached = self[CacheSymbol]
|
|
338
|
+
if (cached) {
|
|
339
|
+
return cached
|
|
340
|
+
}
|
|
341
|
+
const state = self[TypeId]
|
|
342
|
+
const declaredOptions = extractDeclaredOptions(self[options])
|
|
343
|
+
validateClassOptions(declaredOptions)
|
|
344
|
+
const artifacts = buildArtifacts(
|
|
345
|
+
state.name,
|
|
346
|
+
state.fields,
|
|
347
|
+
declaredOptions,
|
|
348
|
+
state.schemaName
|
|
349
|
+
) as BuildArtifacts<Name, Fields, PrimaryKeyColumns>
|
|
350
|
+
Object.defineProperty(self, CacheSymbol, {
|
|
351
|
+
configurable: true,
|
|
352
|
+
value: artifacts
|
|
353
|
+
})
|
|
354
|
+
return artifacts
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const appendOption = <
|
|
358
|
+
Table extends TableDefinition<any, any, any, "schema", any>,
|
|
359
|
+
Spec extends TableOptionSpec
|
|
360
|
+
>(
|
|
361
|
+
table: Table,
|
|
362
|
+
option: Spec
|
|
363
|
+
): ApplyOption<Table, Spec> => {
|
|
364
|
+
const state = table[TypeId]
|
|
365
|
+
if (state.kind !== "schema") {
|
|
366
|
+
throw new Error("Table options can only be applied to schema tables, not aliased query sources")
|
|
367
|
+
}
|
|
368
|
+
return makeTable(
|
|
369
|
+
state.name,
|
|
370
|
+
state.fields,
|
|
371
|
+
[...table[DeclaredOptionsSymbol], option],
|
|
372
|
+
state.baseName,
|
|
373
|
+
state.kind,
|
|
374
|
+
state.schemaName,
|
|
375
|
+
"explicit"
|
|
376
|
+
) as unknown as ApplyOption<Table, Spec>
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const makeOption = <Spec extends TableOptionSpec>(option: Spec): TableOption<Spec> => {
|
|
380
|
+
const builder = ((table: TableDefinition<any, any, any, "schema", any>) =>
|
|
381
|
+
appendOption(table, option)) as unknown as TableOption<Spec>
|
|
382
|
+
;(builder as { option: Spec }).option = option
|
|
383
|
+
return builder
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** Creates a table definition from a name and field map. */
|
|
387
|
+
export function make<
|
|
388
|
+
Name extends string,
|
|
389
|
+
Fields extends TableFieldMap,
|
|
390
|
+
SchemaName extends string | undefined = DefaultSchemaName
|
|
391
|
+
>(
|
|
392
|
+
name: Name,
|
|
393
|
+
fields: Fields,
|
|
394
|
+
schemaName?: SchemaName
|
|
395
|
+
): TableDefinition<Name, Fields, InlinePrimaryKeyKeys<Fields>, "schema", SchemaName> {
|
|
396
|
+
const resolvedSchemaName = arguments.length >= 3
|
|
397
|
+
? schemaName
|
|
398
|
+
: ("public" as SchemaName)
|
|
399
|
+
return makeTable(
|
|
400
|
+
name,
|
|
401
|
+
fields,
|
|
402
|
+
[],
|
|
403
|
+
name,
|
|
404
|
+
"schema",
|
|
405
|
+
resolvedSchemaName,
|
|
406
|
+
arguments.length >= 3 ? "explicit" : "default"
|
|
407
|
+
)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Creates a namespace-scoped builder for a concrete SQL schema/database.
|
|
412
|
+
*/
|
|
413
|
+
export const schema = <SchemaName extends string>(
|
|
414
|
+
schemaName: SchemaName
|
|
415
|
+
): TableSchemaNamespace<SchemaName> => ({
|
|
416
|
+
schemaName,
|
|
417
|
+
table: <
|
|
418
|
+
Name extends string,
|
|
419
|
+
Fields extends TableFieldMap,
|
|
420
|
+
PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>
|
|
421
|
+
>(
|
|
422
|
+
name: Name,
|
|
423
|
+
fields: Fields,
|
|
424
|
+
...options: InternalDeclaredTableOptions
|
|
425
|
+
): TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName> =>
|
|
426
|
+
makeTable(
|
|
427
|
+
name,
|
|
428
|
+
fields,
|
|
429
|
+
extractDeclaredOptions(options),
|
|
430
|
+
name,
|
|
431
|
+
"schema",
|
|
432
|
+
schemaName,
|
|
433
|
+
"explicit"
|
|
434
|
+
) as TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Creates an aliased source from an existing table definition.
|
|
439
|
+
*
|
|
440
|
+
* The alias becomes the logical source identity used by the query layer while
|
|
441
|
+
* the original physical table name is retained in bound-column provenance for
|
|
442
|
+
* downstream SQL rendering work.
|
|
443
|
+
*/
|
|
444
|
+
export const alias = <
|
|
445
|
+
Name extends string,
|
|
446
|
+
Fields extends TableFieldMap,
|
|
447
|
+
PrimaryKeyColumns extends keyof Fields & string,
|
|
448
|
+
SchemaName extends string,
|
|
449
|
+
AliasName extends string
|
|
450
|
+
>(
|
|
451
|
+
table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, SchemaName> | TableClassStatic<Name, Fields, PrimaryKeyColumns, SchemaName>,
|
|
452
|
+
aliasName: AliasName
|
|
453
|
+
): TableDefinition<
|
|
454
|
+
AliasName,
|
|
455
|
+
Fields,
|
|
456
|
+
PrimaryKeyColumns,
|
|
457
|
+
"alias",
|
|
458
|
+
SchemaName
|
|
459
|
+
> => {
|
|
460
|
+
const state = table[TypeId]
|
|
461
|
+
const columns = Object.fromEntries(
|
|
462
|
+
Object.entries(state.fields).map(([key, column]) => [key, bindColumn(aliasName, key, column as AnyColumnDefinition, state.baseName, state.schemaName)])
|
|
463
|
+
) as BoundColumns<AliasName, Fields>
|
|
464
|
+
const aliased = Object.create(TableProto)
|
|
465
|
+
aliased.name = aliasName
|
|
466
|
+
aliased.columns = columns
|
|
467
|
+
aliased.schemas = table.schemas
|
|
468
|
+
aliased[TypeId] = {
|
|
469
|
+
name: aliasName,
|
|
470
|
+
baseName: state.baseName,
|
|
471
|
+
schemaName: state.schemaName,
|
|
472
|
+
fields: state.fields,
|
|
473
|
+
primaryKey: state.primaryKey,
|
|
474
|
+
kind: "alias"
|
|
475
|
+
}
|
|
476
|
+
aliased[Plan.TypeId] = {
|
|
477
|
+
selection: columns,
|
|
478
|
+
required: undefined as never,
|
|
479
|
+
available: {
|
|
480
|
+
[aliasName]: {
|
|
481
|
+
name: aliasName,
|
|
482
|
+
mode: "required",
|
|
483
|
+
baseName: state.baseName
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
dialect: table[Plan.TypeId].dialect
|
|
487
|
+
}
|
|
488
|
+
aliased[OptionsSymbol] = table[OptionsSymbol]
|
|
489
|
+
aliased[DeclaredOptionsSymbol] = table[DeclaredOptionsSymbol]
|
|
490
|
+
for (const [key, value] of Object.entries(columns)) {
|
|
491
|
+
Object.defineProperty(aliased, key, {
|
|
492
|
+
enumerable: true,
|
|
493
|
+
value
|
|
494
|
+
})
|
|
495
|
+
}
|
|
496
|
+
return aliased
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Class-based table constructor mirroring `Schema.Class`.
|
|
501
|
+
*
|
|
502
|
+
* The returned base class can be extended and configured with
|
|
503
|
+
* `static readonly [Table.options]`.
|
|
504
|
+
*/
|
|
505
|
+
export function Class<
|
|
506
|
+
Self = never,
|
|
507
|
+
SchemaName extends string | undefined = DefaultSchemaName
|
|
508
|
+
>(
|
|
509
|
+
name: string,
|
|
510
|
+
schemaName?: SchemaName
|
|
511
|
+
) {
|
|
512
|
+
const resolvedSchemaName = arguments.length >= 2
|
|
513
|
+
? schemaName
|
|
514
|
+
: ("public" as SchemaName)
|
|
515
|
+
return <Fields extends TableFieldMap>(fields: Fields): [Self] extends [never]
|
|
516
|
+
? MissingSelfGeneric
|
|
517
|
+
: TableClassStatic<typeof name, Fields, InlinePrimaryKeyKeys<Fields>, SchemaName> => {
|
|
518
|
+
abstract class TableClassBase {
|
|
519
|
+
static readonly tableName = name
|
|
520
|
+
|
|
521
|
+
static get columns() {
|
|
522
|
+
return ensureClassArtifacts(this as any).columns
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
static get schemas() {
|
|
526
|
+
return ensureClassArtifacts(this as any).schemas
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
static get [TypeId]() {
|
|
530
|
+
const declaredOptions = extractDeclaredOptions((this as unknown as TableClassStatic<typeof name, Fields>)[options])
|
|
531
|
+
validateClassOptions(declaredOptions)
|
|
532
|
+
return {
|
|
533
|
+
name,
|
|
534
|
+
baseName: name,
|
|
535
|
+
schemaName: resolvedSchemaName,
|
|
536
|
+
fields,
|
|
537
|
+
primaryKey: resolvePrimaryKeyColumns(fields, collectInlineOptions(fields)),
|
|
538
|
+
kind: "schema"
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
static get [Plan.TypeId]() {
|
|
543
|
+
const artifacts = ensureClassArtifacts(this as any)
|
|
544
|
+
return {
|
|
545
|
+
selection: artifacts.columns,
|
|
546
|
+
required: undefined as never,
|
|
547
|
+
available: {
|
|
548
|
+
[name]: {
|
|
549
|
+
name,
|
|
550
|
+
mode: "required",
|
|
551
|
+
baseName: name
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
dialect: resolveFieldDialect(fields)
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
static get [OptionsSymbol]() {
|
|
559
|
+
return ensureClassArtifacts(this as any).normalizedOptions
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
static pipe(this: unknown) {
|
|
563
|
+
return pipeArguments(this, arguments)
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
for (const key of Object.keys(fields)) {
|
|
568
|
+
Object.defineProperty(TableClassBase, key, {
|
|
569
|
+
enumerable: true,
|
|
570
|
+
configurable: true,
|
|
571
|
+
get() {
|
|
572
|
+
return (ensureClassArtifacts(this as any).columns as unknown as Record<string, AnyColumnDefinition>)[key]
|
|
573
|
+
}
|
|
574
|
+
})
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return TableClassBase as any
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/** Declares a table-level primary key. */
|
|
582
|
+
export const primaryKey = <
|
|
583
|
+
Columns extends string | readonly string[]
|
|
584
|
+
>(
|
|
585
|
+
columns: Columns
|
|
586
|
+
): TableOption<{
|
|
587
|
+
readonly kind: "primaryKey"
|
|
588
|
+
readonly columns: NormalizeColumns<Columns>
|
|
589
|
+
}> => makeOption({
|
|
590
|
+
kind: "primaryKey",
|
|
591
|
+
columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
/** Declares a table-level unique constraint. */
|
|
595
|
+
export const unique = <
|
|
596
|
+
Columns extends string | readonly string[]
|
|
597
|
+
>(
|
|
598
|
+
columns: Columns
|
|
599
|
+
): TableOption<{
|
|
600
|
+
readonly kind: "unique"
|
|
601
|
+
readonly columns: NormalizeColumns<Columns>
|
|
602
|
+
}> => makeOption({
|
|
603
|
+
kind: "unique",
|
|
604
|
+
columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
/** Declares a table-level index. */
|
|
608
|
+
export const index = <
|
|
609
|
+
Columns extends string | readonly string[]
|
|
610
|
+
>(
|
|
611
|
+
columns: Columns
|
|
612
|
+
): TableOption<{
|
|
613
|
+
readonly kind: "index"
|
|
614
|
+
readonly columns: NormalizeColumns<Columns>
|
|
615
|
+
}> => makeOption({
|
|
616
|
+
kind: "index",
|
|
617
|
+
columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
|
|
618
|
+
})
|
|
619
|
+
|
|
620
|
+
/** Declares a table-level foreign key. */
|
|
621
|
+
export const foreignKey = <
|
|
622
|
+
LocalColumns extends string | readonly string[],
|
|
623
|
+
TargetTable extends AnyTable,
|
|
624
|
+
TargetColumns extends string | readonly string[]
|
|
625
|
+
>(
|
|
626
|
+
columns: LocalColumns,
|
|
627
|
+
target: () => TargetTable,
|
|
628
|
+
referencedColumns: TargetColumns
|
|
629
|
+
): TableOption<{
|
|
630
|
+
readonly kind: "foreignKey"
|
|
631
|
+
readonly columns: NormalizeColumns<LocalColumns>
|
|
632
|
+
readonly references: () => {
|
|
633
|
+
readonly tableName: string
|
|
634
|
+
readonly schemaName?: string
|
|
635
|
+
readonly columns: NormalizeColumns<TargetColumns>
|
|
636
|
+
readonly knownColumns: readonly string[]
|
|
637
|
+
}
|
|
638
|
+
}> => makeOption({
|
|
639
|
+
kind: "foreignKey",
|
|
640
|
+
columns: normalizeColumnList(columns) as NormalizeColumns<LocalColumns>,
|
|
641
|
+
references: () => ({
|
|
642
|
+
tableName: target()[TypeId].baseName,
|
|
643
|
+
schemaName: target()[TypeId].schemaName,
|
|
644
|
+
columns: normalizeColumnList(referencedColumns) as NormalizeColumns<TargetColumns>,
|
|
645
|
+
knownColumns: Object.keys(target()[TypeId].fields)
|
|
646
|
+
})
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
/** Declares a metadata-only check constraint placeholder. */
|
|
650
|
+
export const check = <Name extends string>(
|
|
651
|
+
name: Name,
|
|
652
|
+
predicate: unknown
|
|
653
|
+
): TableOption<{
|
|
654
|
+
readonly kind: "check"
|
|
655
|
+
readonly name: Name
|
|
656
|
+
readonly predicate: unknown
|
|
657
|
+
}> => makeOption({
|
|
658
|
+
kind: "check",
|
|
659
|
+
name,
|
|
660
|
+
predicate
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
/** Extracts the row type of a table's select schema. */
|
|
664
|
+
export type SelectOf<Table extends { readonly schemas: { readonly select: Schema.Schema<any> } }> = Schema.Schema.Type<
|
|
665
|
+
Table["schemas"]["select"]
|
|
666
|
+
>
|
|
667
|
+
/** Extracts the payload type of a table's insert schema. */
|
|
668
|
+
export type InsertOf<Table extends { readonly schemas: { readonly insert: Schema.Schema<any> } }> = Schema.Schema.Type<
|
|
669
|
+
Table["schemas"]["insert"]
|
|
670
|
+
>
|
|
671
|
+
/** Extracts the payload type of a table's update schema. */
|
|
672
|
+
export type UpdateOf<Table extends { readonly schemas: { readonly update: Schema.Schema<any> } }> = Schema.Schema.Type<
|
|
673
|
+
Table["schemas"]["update"]
|
|
674
|
+
>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as BaseColumn from "../internal/column.js"
|
|
2
|
+
import * as Expression from "../internal/expression.js"
|
|
3
|
+
import { LocalDateTimeStringSchema } from "../internal/runtime-value.js"
|
|
4
|
+
|
|
5
|
+
/** MySQL-specialized column-definition DSL. */
|
|
6
|
+
export const uuid = BaseColumn.mysql.uuid
|
|
7
|
+
export const text = BaseColumn.mysql.text
|
|
8
|
+
export const int = BaseColumn.mysql.int
|
|
9
|
+
export const number = BaseColumn.mysql.number
|
|
10
|
+
export const boolean = BaseColumn.mysql.boolean
|
|
11
|
+
export const date = BaseColumn.mysql.date
|
|
12
|
+
export const datetime = () =>
|
|
13
|
+
BaseColumn.mysql.custom(
|
|
14
|
+
LocalDateTimeStringSchema,
|
|
15
|
+
{ dialect: "mysql", kind: "datetime" } as Expression.DbType.MySqlDatetime
|
|
16
|
+
)
|
|
17
|
+
export const timestamp = BaseColumn.mysql.timestamp
|
|
18
|
+
export const json = BaseColumn.mysql.json
|
|
19
|
+
export const custom = BaseColumn.mysql.custom
|
|
20
|
+
|
|
21
|
+
export const nullable = BaseColumn.nullable
|
|
22
|
+
export const primaryKey = BaseColumn.primaryKey
|
|
23
|
+
export const unique = BaseColumn.unique
|
|
24
|
+
export const hasDefault = BaseColumn.hasDefault
|
|
25
|
+
export const generated = BaseColumn.generated
|
|
26
|
+
export const references = BaseColumn.references
|
|
27
|
+
export const schema = BaseColumn.schema
|
|
28
|
+
|
|
29
|
+
export type Any = BaseColumn.Any
|
|
30
|
+
export type AnyBound = BaseColumn.AnyBound
|