effect-qb 0.15.0 → 0.17.0
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/dist/mysql.js +1957 -595
- package/dist/postgres/metadata.js +2507 -182
- package/dist/postgres.js +9587 -8201
- package/dist/sqlite.js +8360 -0
- package/package.json +7 -2
- package/src/internal/column-state.ts +7 -0
- package/src/internal/column.ts +22 -0
- package/src/internal/derived-table.ts +29 -3
- package/src/internal/dialect.ts +14 -1
- package/src/internal/dsl-mutation-runtime.ts +173 -4
- package/src/internal/dsl-plan-runtime.ts +165 -20
- package/src/internal/dsl-query-runtime.ts +60 -6
- package/src/internal/dsl-transaction-ddl-runtime.ts +72 -2
- package/src/internal/executor.ts +62 -13
- package/src/internal/expression-ast.ts +3 -2
- package/src/internal/grouping-key.ts +141 -1
- package/src/internal/implication-runtime.ts +2 -1
- package/src/internal/json/types.ts +155 -40
- package/src/internal/predicate/analysis.ts +103 -1
- package/src/internal/predicate/atom.ts +7 -0
- package/src/internal/predicate/context.ts +170 -17
- package/src/internal/predicate/key.ts +64 -2
- package/src/internal/predicate/normalize.ts +115 -34
- package/src/internal/predicate/runtime.ts +144 -13
- package/src/internal/query.ts +563 -103
- package/src/internal/renderer.ts +39 -2
- package/src/internal/runtime/driver-value-mapping.ts +244 -0
- package/src/internal/runtime/normalize.ts +62 -38
- package/src/internal/runtime/schema.ts +5 -3
- package/src/internal/runtime/value.ts +153 -30
- package/src/internal/scalar.ts +11 -0
- package/src/internal/table-options.ts +108 -1
- package/src/internal/table.ts +87 -29
- package/src/mysql/column.ts +19 -2
- package/src/mysql/datatypes/index.ts +21 -0
- package/src/mysql/errors/catalog.ts +5 -5
- package/src/mysql/errors/normalize.ts +2 -2
- package/src/mysql/executor.ts +20 -5
- package/src/mysql/internal/dialect.ts +12 -6
- package/src/mysql/internal/dsl.ts +995 -263
- package/src/mysql/internal/renderer.ts +13 -3
- package/src/mysql/internal/sql-expression-renderer.ts +530 -128
- package/src/mysql/query.ts +9 -2
- package/src/mysql/renderer.ts +7 -2
- package/src/mysql/table.ts +38 -12
- package/src/postgres/cast.ts +22 -7
- package/src/postgres/column.ts +5 -2
- package/src/postgres/errors/normalize.ts +2 -2
- package/src/postgres/executor.ts +68 -10
- package/src/postgres/function/core.ts +19 -1
- package/src/postgres/internal/dialect.ts +12 -6
- package/src/postgres/internal/dsl.ts +958 -288
- package/src/postgres/internal/renderer.ts +13 -3
- package/src/postgres/internal/schema-ddl.ts +2 -1
- package/src/postgres/internal/schema-model.ts +6 -3
- package/src/postgres/internal/sql-expression-renderer.ts +477 -96
- package/src/postgres/json.ts +57 -17
- package/src/postgres/query.ts +9 -2
- package/src/postgres/renderer.ts +7 -2
- package/src/postgres/schema-management.ts +91 -4
- package/src/postgres/schema.ts +1 -1
- package/src/postgres/table.ts +189 -53
- package/src/postgres/type.ts +4 -0
- package/src/sqlite/column.ts +128 -0
- package/src/sqlite/datatypes/index.ts +79 -0
- package/src/sqlite/datatypes/spec.ts +98 -0
- package/src/sqlite/errors/catalog.ts +103 -0
- package/src/sqlite/errors/fields.ts +19 -0
- package/src/sqlite/errors/index.ts +19 -0
- package/src/sqlite/errors/normalize.ts +229 -0
- package/src/sqlite/errors/requirements.ts +71 -0
- package/src/sqlite/errors/types.ts +29 -0
- package/src/sqlite/executor.ts +227 -0
- package/src/sqlite/function/aggregate.ts +2 -0
- package/src/sqlite/function/core.ts +2 -0
- package/src/sqlite/function/index.ts +19 -0
- package/src/sqlite/function/string.ts +2 -0
- package/src/sqlite/function/temporal.ts +100 -0
- package/src/sqlite/function/window.ts +2 -0
- package/src/sqlite/internal/dialect.ts +37 -0
- package/src/sqlite/internal/dsl.ts +6926 -0
- package/src/sqlite/internal/renderer.ts +47 -0
- package/src/sqlite/internal/sql-expression-renderer.ts +1821 -0
- package/src/sqlite/json.ts +2 -0
- package/src/sqlite/query.ts +196 -0
- package/src/sqlite/renderer.ts +24 -0
- package/src/sqlite/table.ts +183 -0
- package/src/sqlite.ts +22 -0
|
@@ -23,50 +23,173 @@ const brandString = <BrandName extends string>(
|
|
|
23
23
|
Schema.brand(brand)
|
|
24
24
|
) as unknown as Schema.Schema<string & Brand.Brand<BrandName>>
|
|
25
25
|
|
|
26
|
-
export const
|
|
27
|
-
/^\d{4}-\d{2}-\d{2}$/,
|
|
28
|
-
"LocalDateString"
|
|
29
|
-
)
|
|
26
|
+
export const localDatePattern = /^(\d{4})-(\d{2})-(\d{2})$/
|
|
30
27
|
|
|
31
|
-
export const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
export const isValidLocalDateString = (value: string): boolean => {
|
|
29
|
+
const match = localDatePattern.exec(value)
|
|
30
|
+
if (match === null) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
const year = Number(match[1])
|
|
34
|
+
const month = Number(match[2])
|
|
35
|
+
const day = Number(match[3])
|
|
36
|
+
const parsed = new Date(Date.UTC(year, month - 1, day))
|
|
37
|
+
parsed.setUTCFullYear(year)
|
|
38
|
+
return parsed.getUTCFullYear() === year &&
|
|
39
|
+
parsed.getUTCMonth() === month - 1 &&
|
|
40
|
+
parsed.getUTCDate() === day
|
|
41
|
+
}
|
|
35
42
|
|
|
36
|
-
export const
|
|
37
|
-
/^\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/,
|
|
38
|
-
"OffsetTimeString"
|
|
39
|
-
)
|
|
43
|
+
export const localTimePattern = /^(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?$/
|
|
40
44
|
|
|
41
|
-
export const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
export const isValidLocalTimeString = (value: string): boolean => {
|
|
46
|
+
const match = localTimePattern.exec(value)
|
|
47
|
+
if (match === null) {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
const hour = Number(match[1])
|
|
51
|
+
const minute = Number(match[2])
|
|
52
|
+
const second = Number(match[3])
|
|
53
|
+
return hour >= 0 && hour <= 23 &&
|
|
54
|
+
minute >= 0 && minute <= 59 &&
|
|
55
|
+
second >= 0 && second <= 59
|
|
56
|
+
}
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
58
|
+
const offsetPattern = /^(?:Z|[+-](\d{2}):(\d{2}))$/
|
|
59
|
+
|
|
60
|
+
const isValidOffset = (value: string): boolean => {
|
|
61
|
+
const match = offsetPattern.exec(value)
|
|
62
|
+
if (match === null) {
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
if (value === "Z") {
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
const hour = Number(match[1])
|
|
69
|
+
const minute = Number(match[2])
|
|
70
|
+
return hour >= 0 && hour <= 23 &&
|
|
71
|
+
minute >= 0 && minute <= 59
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const offsetTimePattern = /^(\d{2}:\d{2}:\d{2}(?:\.\d+)?)(Z|[+-]\d{2}:\d{2})$/
|
|
75
|
+
|
|
76
|
+
export const isValidOffsetTimeString = (value: string): boolean => {
|
|
77
|
+
const match = offsetTimePattern.exec(value)
|
|
78
|
+
return match !== null &&
|
|
79
|
+
isValidLocalTimeString(match[1]!) &&
|
|
80
|
+
isValidOffset(match[2]!)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const localDateTimePattern = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2}(?:\.\d+)?)$/
|
|
84
|
+
|
|
85
|
+
export const isValidLocalDateTimeString = (value: string): boolean => {
|
|
86
|
+
const match = localDateTimePattern.exec(value)
|
|
87
|
+
return match !== null &&
|
|
88
|
+
isValidLocalDateString(match[1]!) &&
|
|
89
|
+
isValidLocalTimeString(match[2]!)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const instantPattern = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2}(?:\.\d+)?)(Z|[+-]\d{2}:\d{2})$/
|
|
93
|
+
|
|
94
|
+
export const isValidInstantString = (value: string): boolean => {
|
|
95
|
+
const match = instantPattern.exec(value)
|
|
96
|
+
return match !== null &&
|
|
97
|
+
isValidLocalDateString(match[1]!) &&
|
|
98
|
+
isValidLocalTimeString(match[2]!) &&
|
|
99
|
+
isValidOffset(match[3]!)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const LocalDateStringSchema = Schema.String.pipe(
|
|
103
|
+
Schema.pattern(localDatePattern),
|
|
104
|
+
Schema.filter(isValidLocalDateString),
|
|
105
|
+
Schema.brand("LocalDateString")
|
|
106
|
+
) as unknown as Schema.Schema<LocalDateString>
|
|
107
|
+
|
|
108
|
+
export const LocalTimeStringSchema = Schema.String.pipe(
|
|
109
|
+
Schema.pattern(localTimePattern),
|
|
110
|
+
Schema.filter(isValidLocalTimeString),
|
|
111
|
+
Schema.brand("LocalTimeString")
|
|
112
|
+
) as unknown as Schema.Schema<LocalTimeString>
|
|
113
|
+
|
|
114
|
+
export const OffsetTimeStringSchema = Schema.String.pipe(
|
|
115
|
+
Schema.pattern(offsetTimePattern),
|
|
116
|
+
Schema.filter(isValidOffsetTimeString),
|
|
117
|
+
Schema.brand("OffsetTimeString")
|
|
118
|
+
) as unknown as Schema.Schema<OffsetTimeString>
|
|
119
|
+
|
|
120
|
+
export const LocalDateTimeStringSchema = Schema.String.pipe(
|
|
121
|
+
Schema.pattern(localDateTimePattern),
|
|
122
|
+
Schema.filter(isValidLocalDateTimeString),
|
|
123
|
+
Schema.brand("LocalDateTimeString")
|
|
124
|
+
) as unknown as Schema.Schema<LocalDateTimeString>
|
|
125
|
+
|
|
126
|
+
export const InstantStringSchema = Schema.String.pipe(
|
|
127
|
+
Schema.pattern(instantPattern),
|
|
128
|
+
Schema.filter(isValidInstantString),
|
|
129
|
+
Schema.brand("InstantString")
|
|
130
|
+
) as unknown as Schema.Schema<InstantString>
|
|
50
131
|
|
|
51
132
|
export const YearStringSchema = brandString(
|
|
52
133
|
/^\d{4}$/,
|
|
53
134
|
"YearString"
|
|
54
135
|
)
|
|
55
136
|
|
|
56
|
-
export const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
137
|
+
export const canonicalizeBigIntString = (input: string): string => {
|
|
138
|
+
const trimmed = input.trim()
|
|
139
|
+
if (!/^-?\d+$/.test(trimmed)) {
|
|
140
|
+
throw new Error("Expected an integer-like bigint value")
|
|
141
|
+
}
|
|
142
|
+
return BigInt(trimmed).toString()
|
|
143
|
+
}
|
|
60
144
|
|
|
61
|
-
export const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
145
|
+
export const isCanonicalBigIntString = (value: string): boolean => {
|
|
146
|
+
try {
|
|
147
|
+
return canonicalizeBigIntString(value) === value
|
|
148
|
+
} catch {
|
|
149
|
+
return false
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const canonicalizeDecimalString = (input: string): string => {
|
|
154
|
+
const trimmed = input.trim()
|
|
155
|
+
const match = /^([+-]?)(\d+)(?:\.(\d+))?$/.exec(trimmed)
|
|
156
|
+
if (match === null) {
|
|
157
|
+
throw new Error("Expected a decimal string")
|
|
158
|
+
}
|
|
159
|
+
const sign = match[1] === "-" ? "-" : ""
|
|
160
|
+
const integer = match[2]!.replace(/^0+(?=\d)/, "") || "0"
|
|
161
|
+
const fraction = (match[3] ?? "").replace(/0+$/, "")
|
|
162
|
+
if (fraction.length === 0) {
|
|
163
|
+
if (integer === "0") {
|
|
164
|
+
return "0"
|
|
165
|
+
}
|
|
166
|
+
return `${sign}${integer}`
|
|
167
|
+
}
|
|
168
|
+
return `${sign}${integer}.${fraction}`
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export const isCanonicalDecimalString = (value: string): boolean => {
|
|
172
|
+
try {
|
|
173
|
+
return canonicalizeDecimalString(value) === value
|
|
174
|
+
} catch {
|
|
175
|
+
return false
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const BigIntStringSchema = Schema.String.pipe(
|
|
180
|
+
Schema.filter(isCanonicalBigIntString),
|
|
181
|
+
Schema.brand("BigIntString")
|
|
182
|
+
) as unknown as Schema.Schema<BigIntString>
|
|
183
|
+
|
|
184
|
+
export const DecimalStringSchema = Schema.String.pipe(
|
|
185
|
+
Schema.filter(isCanonicalDecimalString),
|
|
186
|
+
Schema.brand("DecimalString")
|
|
187
|
+
) as unknown as Schema.Schema<DecimalString>
|
|
65
188
|
|
|
66
189
|
export const JsonValueSchema: Schema.Schema<JsonValue> = Schema.suspend(() =>
|
|
67
190
|
Schema.Union(
|
|
68
191
|
Schema.String,
|
|
69
|
-
Schema.Number,
|
|
192
|
+
Schema.Number.pipe(Schema.finite()),
|
|
70
193
|
Schema.Boolean,
|
|
71
194
|
Schema.Null,
|
|
72
195
|
Schema.Array(JsonValueSchema),
|
|
@@ -79,7 +202,7 @@ export const JsonValueSchema: Schema.Schema<JsonValue> = Schema.suspend(() =>
|
|
|
79
202
|
|
|
80
203
|
export const JsonPrimitiveSchema: Schema.Schema<JsonPrimitive> = Schema.Union(
|
|
81
204
|
Schema.String,
|
|
82
|
-
Schema.Number,
|
|
205
|
+
Schema.Number.pipe(Schema.finite()),
|
|
83
206
|
Schema.Boolean,
|
|
84
207
|
Schema.Null
|
|
85
208
|
)
|
package/src/internal/scalar.ts
CHANGED
|
@@ -51,6 +51,7 @@ export declare namespace DbType {
|
|
|
51
51
|
readonly compareGroup?: string
|
|
52
52
|
readonly castTargets?: readonly string[]
|
|
53
53
|
readonly traits?: DatatypeTraits
|
|
54
|
+
readonly driverValueMapping?: DriverValueMapping
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/** JSON-like database type. */
|
|
@@ -135,6 +136,15 @@ export declare namespace DbType {
|
|
|
135
136
|
| Set<string, string>
|
|
136
137
|
}
|
|
137
138
|
|
|
139
|
+
export interface DriverValueMapping {
|
|
140
|
+
readonly fromDriver?: (value: unknown, dbType: DbType.Any) => unknown
|
|
141
|
+
readonly toDriver?: (value: unknown, dbType: DbType.Any) => unknown
|
|
142
|
+
readonly selectSql?: (sql: string, dbType: DbType.Any) => string
|
|
143
|
+
readonly jsonSelectSql?: (sql: string, dbType: DbType.Any) => string
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type DriverValueMappings = Readonly<Record<string, DriverValueMapping | undefined>>
|
|
147
|
+
|
|
138
148
|
/** Canonical static metadata stored on an expression. */
|
|
139
149
|
export interface State<
|
|
140
150
|
Runtime,
|
|
@@ -147,6 +157,7 @@ export interface State<
|
|
|
147
157
|
readonly runtime: Runtime
|
|
148
158
|
readonly dbType: Db
|
|
149
159
|
readonly runtimeSchema?: Schema.Schema.Any
|
|
160
|
+
readonly driverValueMapping?: DriverValueMapping
|
|
150
161
|
readonly nullability: Nullable
|
|
151
162
|
readonly dialect: Dialect
|
|
152
163
|
readonly kind: Kind
|
|
@@ -15,6 +15,30 @@ export type DdlExpressionLike = AnyExpression | AnySchemaExpression
|
|
|
15
15
|
|
|
16
16
|
export type ReferentialAction = "noAction" | "restrict" | "cascade" | "setNull" | "setDefault"
|
|
17
17
|
|
|
18
|
+
const referentialActionError = "Foreign key action must be noAction, restrict, cascade, setNull, or setDefault"
|
|
19
|
+
|
|
20
|
+
export const renderReferentialAction = (action: unknown): string => {
|
|
21
|
+
switch (action) {
|
|
22
|
+
case "noAction":
|
|
23
|
+
return "no action"
|
|
24
|
+
case "restrict":
|
|
25
|
+
return "restrict"
|
|
26
|
+
case "cascade":
|
|
27
|
+
return "cascade"
|
|
28
|
+
case "setNull":
|
|
29
|
+
return "set null"
|
|
30
|
+
case "setDefault":
|
|
31
|
+
return "set default"
|
|
32
|
+
}
|
|
33
|
+
throw new Error(referentialActionError)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const validateReferentialAction = (action: unknown): void => {
|
|
37
|
+
if (action !== undefined) {
|
|
38
|
+
renderReferentialAction(action)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
18
42
|
export type IndexKeySpec =
|
|
19
43
|
| {
|
|
20
44
|
readonly kind: "column"
|
|
@@ -107,6 +131,18 @@ type TupleFromColumns<Columns> = Columns extends readonly [infer Head extends st
|
|
|
107
131
|
? readonly [Columns]
|
|
108
132
|
: never
|
|
109
133
|
|
|
134
|
+
export type NonEmptyColumnInput<Columns extends string | readonly string[]> =
|
|
135
|
+
TupleFromColumns<Columns> extends never ? never : Columns
|
|
136
|
+
|
|
137
|
+
export type MatchingColumnArityInput<
|
|
138
|
+
Left extends string | readonly string[],
|
|
139
|
+
Right extends string | readonly string[]
|
|
140
|
+
> = TupleFromColumns<Left>["length"] extends TupleFromColumns<Right>["length"]
|
|
141
|
+
? TupleFromColumns<Right>["length"] extends TupleFromColumns<Left>["length"]
|
|
142
|
+
? unknown
|
|
143
|
+
: never
|
|
144
|
+
: never
|
|
145
|
+
|
|
110
146
|
type AssertKnownColumns<Fields extends TableFieldMap, Columns extends readonly string[]> = Exclude<
|
|
111
147
|
Columns[number],
|
|
112
148
|
ColumnNameUnion<Fields>
|
|
@@ -114,6 +150,47 @@ type AssertKnownColumns<Fields extends TableFieldMap, Columns extends readonly s
|
|
|
114
150
|
? Columns
|
|
115
151
|
: never
|
|
116
152
|
|
|
153
|
+
type IndexKeyColumnNames<Keys> = Keys extends readonly (infer Key)[]
|
|
154
|
+
? Key extends { readonly kind: "column"; readonly column: infer Column extends string }
|
|
155
|
+
? Column
|
|
156
|
+
: never
|
|
157
|
+
: never
|
|
158
|
+
|
|
159
|
+
type IndexOptionColumnNames<Spec> =
|
|
160
|
+
| (Spec extends { readonly columns: infer Columns extends readonly string[] } ? Columns[number] : never)
|
|
161
|
+
| (Spec extends { readonly include: infer Include extends readonly string[] } ? Include[number] : never)
|
|
162
|
+
| (Spec extends { readonly keys: infer Keys } ? IndexKeyColumnNames<Keys> : never)
|
|
163
|
+
|
|
164
|
+
type ForeignKeyReferencedColumnNames<Spec> = Spec extends { readonly references: () => infer Reference }
|
|
165
|
+
? Reference extends { readonly columns: infer Columns extends readonly string[] }
|
|
166
|
+
? Columns[number]
|
|
167
|
+
: never
|
|
168
|
+
: never
|
|
169
|
+
|
|
170
|
+
type ForeignKeyKnownReferencedColumnNames<Spec> = Spec extends { readonly references: () => infer Reference }
|
|
171
|
+
? Reference extends { readonly knownColumns: infer KnownColumns extends readonly string[] }
|
|
172
|
+
? KnownColumns[number]
|
|
173
|
+
: string
|
|
174
|
+
: string
|
|
175
|
+
|
|
176
|
+
type AssertKnownColumnNames<Fields extends TableFieldMap, Columns extends string> = [Columns] extends [never]
|
|
177
|
+
? true
|
|
178
|
+
: string extends Columns
|
|
179
|
+
? true
|
|
180
|
+
: Exclude<Columns, ColumnNameUnion<Fields>> extends never
|
|
181
|
+
? true
|
|
182
|
+
: false
|
|
183
|
+
|
|
184
|
+
type AssertKnownReferenceColumnNames<KnownColumns extends string, Columns extends string> = [Columns] extends [never]
|
|
185
|
+
? true
|
|
186
|
+
: string extends Columns
|
|
187
|
+
? true
|
|
188
|
+
: string extends KnownColumns
|
|
189
|
+
? true
|
|
190
|
+
: Exclude<Columns, KnownColumns> extends never
|
|
191
|
+
? true
|
|
192
|
+
: false
|
|
193
|
+
|
|
117
194
|
type AssertPrimaryKeyColumns<
|
|
118
195
|
Fields extends TableFieldMap,
|
|
119
196
|
Columns extends readonly string[]
|
|
@@ -159,6 +236,8 @@ export const collectInlineOptions = <Fields extends TableFieldMap>(
|
|
|
159
236
|
})
|
|
160
237
|
}
|
|
161
238
|
if (column.metadata.references) {
|
|
239
|
+
validateReferentialAction(column.metadata.references.onUpdate)
|
|
240
|
+
validateReferentialAction(column.metadata.references.onDelete)
|
|
162
241
|
const local = [columnName] as ColumnList
|
|
163
242
|
options.push({
|
|
164
243
|
kind: "foreignKey",
|
|
@@ -254,6 +333,8 @@ export const validateOptions = <Fields extends TableFieldMap>(
|
|
|
254
333
|
}
|
|
255
334
|
}
|
|
256
335
|
if (option.kind === "foreignKey") {
|
|
336
|
+
validateReferentialAction(option.onUpdate)
|
|
337
|
+
validateReferentialAction(option.onDelete)
|
|
257
338
|
const reference = option.references()
|
|
258
339
|
if (reference.columns.length !== columns.length) {
|
|
259
340
|
throw new Error(`Foreign key on table '${tableName}' must reference the same number of columns`)
|
|
@@ -278,7 +359,7 @@ export const validateOptions = <Fields extends TableFieldMap>(
|
|
|
278
359
|
throw new Error(`Unknown index key column '${key.column}' on table '${tableName}'`)
|
|
279
360
|
}
|
|
280
361
|
}
|
|
281
|
-
if (
|
|
362
|
+
if (columns.length === 0 && (option.keys === undefined || option.keys.length === 0)) {
|
|
282
363
|
throw new Error(`Index on table '${tableName}' requires at least one column or key`)
|
|
283
364
|
}
|
|
284
365
|
}
|
|
@@ -308,5 +389,31 @@ export type ValidatePrimaryKeyColumns<
|
|
|
308
389
|
Columns extends readonly string[]
|
|
309
390
|
> = AssertPrimaryKeyColumns<Fields, AssertKnownColumns<Fields, Columns>>
|
|
310
391
|
|
|
392
|
+
/** Compile-time validation that index columns, included columns, and column keys exist on the table. */
|
|
393
|
+
export type ValidateIndexOptionColumns<
|
|
394
|
+
Fields extends TableFieldMap,
|
|
395
|
+
Spec
|
|
396
|
+
> = AssertKnownColumnNames<Fields, IndexOptionColumnNames<Spec>> extends true ? Spec : never
|
|
397
|
+
|
|
398
|
+
/** Compile-time validation that foreign keys reference known local and target columns. */
|
|
399
|
+
export type ValidateForeignKeyOptionColumns<
|
|
400
|
+
Fields extends TableFieldMap,
|
|
401
|
+
Spec
|
|
402
|
+
> = Spec extends { readonly columns: infer Columns extends readonly string[] }
|
|
403
|
+
? AssertKnownColumns<Fields, Columns> extends never
|
|
404
|
+
? never
|
|
405
|
+
: AssertKnownReferenceColumnNames<
|
|
406
|
+
ForeignKeyKnownReferencedColumnNames<Spec>,
|
|
407
|
+
ForeignKeyReferencedColumnNames<Spec>
|
|
408
|
+
> extends true
|
|
409
|
+
? Spec
|
|
410
|
+
: never
|
|
411
|
+
: AssertKnownReferenceColumnNames<
|
|
412
|
+
ForeignKeyKnownReferencedColumnNames<Spec>,
|
|
413
|
+
ForeignKeyReferencedColumnNames<Spec>
|
|
414
|
+
> extends true
|
|
415
|
+
? Spec
|
|
416
|
+
: never
|
|
417
|
+
|
|
311
418
|
/** Normalizes a public column input into the internal tuple form. */
|
|
312
419
|
export type NormalizeColumns<Columns extends string | readonly string[]> = TupleFromColumns<Columns>
|
package/src/internal/table.ts
CHANGED
|
@@ -12,9 +12,13 @@ import {
|
|
|
12
12
|
resolvePrimaryKeyColumns,
|
|
13
13
|
type DdlExpressionLike,
|
|
14
14
|
type IndexKeySpec,
|
|
15
|
+
type MatchingColumnArityInput,
|
|
16
|
+
type NonEmptyColumnInput,
|
|
15
17
|
type NormalizeColumns,
|
|
16
18
|
type ReferentialAction,
|
|
17
19
|
type TableOptionSpec,
|
|
20
|
+
type ValidateForeignKeyOptionColumns,
|
|
21
|
+
type ValidateIndexOptionColumns,
|
|
18
22
|
type ValidateKnownColumns,
|
|
19
23
|
type ValidatePrimaryKeyColumns,
|
|
20
24
|
validateOptions
|
|
@@ -44,18 +48,32 @@ type InlinePrimaryKeyKeys<Fields extends TableFieldMap> = Extract<{
|
|
|
44
48
|
type TableDialect<Fields extends TableFieldMap> = Fields[keyof Fields][typeof import("./column-state.js").ColumnTypeId]["dbType"]["dialect"]
|
|
45
49
|
type TableKind = "schema" | "alias"
|
|
46
50
|
type DefaultSchemaName = "public"
|
|
47
|
-
type
|
|
51
|
+
type FieldColumnName<Fields extends TableFieldMap> = Extract<keyof Fields, string>
|
|
52
|
+
type FieldColumnList<Fields extends TableFieldMap> = readonly [FieldColumnName<Fields>, ...FieldColumnName<Fields>[]]
|
|
53
|
+
type FieldIndexKeySpec<Fields extends TableFieldMap> =
|
|
54
|
+
| (Extract<IndexKeySpec, { readonly kind: "column" }> & { readonly column: FieldColumnName<Fields> })
|
|
55
|
+
| Extract<IndexKeySpec, { readonly kind: "expression" }>
|
|
56
|
+
type ClassOptionSpec<Fields extends TableFieldMap = TableFieldMap> =
|
|
57
|
+
| (Omit<Extract<TableOptionSpec, { readonly kind: "index" }>, "columns" | "include" | "keys"> & {
|
|
58
|
+
readonly columns?: FieldColumnList<Fields>
|
|
59
|
+
readonly include?: readonly FieldColumnName<Fields>[]
|
|
60
|
+
readonly keys?: readonly [FieldIndexKeySpec<Fields>, ...FieldIndexKeySpec<Fields>[]]
|
|
61
|
+
})
|
|
62
|
+
| (Omit<Extract<TableOptionSpec, { readonly kind: "unique" }>, "columns"> & {
|
|
63
|
+
readonly columns: FieldColumnList<Fields>
|
|
64
|
+
})
|
|
65
|
+
| (Omit<Extract<TableOptionSpec, { readonly kind: "foreignKey" }>, "columns"> & {
|
|
66
|
+
readonly columns: FieldColumnList<Fields>
|
|
67
|
+
})
|
|
68
|
+
| Extract<TableOptionSpec, { readonly kind: "check" }>
|
|
48
69
|
interface TableOptionBuilderLike<
|
|
49
70
|
Spec extends TableOptionSpec = TableOptionSpec
|
|
50
71
|
> {
|
|
51
|
-
(
|
|
52
|
-
table: TableDefinition<any, any, any, "schema", any>
|
|
53
|
-
): TableDefinition<any, any, any, "schema", any>
|
|
54
72
|
readonly option: Spec
|
|
55
73
|
}
|
|
56
74
|
|
|
57
|
-
type ClassTableOption = TableOptionBuilderLike<ClassOptionSpec
|
|
58
|
-
type ClassDeclaredTableOptions = readonly ClassTableOption[]
|
|
75
|
+
type ClassTableOption<Fields extends TableFieldMap> = TableOptionBuilderLike<ClassOptionSpec<Fields>>
|
|
76
|
+
type ClassDeclaredTableOptions<Fields extends TableFieldMap> = readonly ClassTableOption<Fields>[]
|
|
59
77
|
|
|
60
78
|
type BuildPrimaryKey<
|
|
61
79
|
Table extends TableDefinition<any, any, any, "schema", any>,
|
|
@@ -69,9 +87,13 @@ type OptionInputTable<
|
|
|
69
87
|
Spec extends TableOptionSpec
|
|
70
88
|
> = Spec extends { readonly kind: "primaryKey"; readonly columns: infer Columns extends readonly string[] }
|
|
71
89
|
? ValidatePrimaryKeyColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : Table
|
|
72
|
-
: Spec extends { readonly
|
|
73
|
-
?
|
|
74
|
-
:
|
|
90
|
+
: Spec extends { readonly kind: "index" }
|
|
91
|
+
? ValidateIndexOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : Table
|
|
92
|
+
: Spec extends { readonly kind: "foreignKey" }
|
|
93
|
+
? ValidateForeignKeyOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : Table
|
|
94
|
+
: Spec extends { readonly columns: infer Columns extends readonly string[] }
|
|
95
|
+
? ValidateKnownColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : Table
|
|
96
|
+
: Table
|
|
75
97
|
|
|
76
98
|
type ApplyOption<
|
|
77
99
|
Table extends TableDefinition<any, any, any, "schema", any>,
|
|
@@ -90,6 +112,26 @@ type ApplyOption<
|
|
|
90
112
|
"schema"
|
|
91
113
|
>
|
|
92
114
|
|
|
115
|
+
export type ValidateDeclaredOptions<
|
|
116
|
+
Table extends TableDefinition<any, any, any, "schema", any>,
|
|
117
|
+
Options extends DeclaredTableOptions
|
|
118
|
+
> = {
|
|
119
|
+
readonly [K in keyof Options]: Options[K] extends TableOptionBuilderLike<infer Spec>
|
|
120
|
+
? OptionInputTable<Table, Spec> extends never ? never : Options[K]
|
|
121
|
+
: never
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type ApplyDeclaredOptions<
|
|
125
|
+
Table extends TableDefinition<any, any, any, "schema", any>,
|
|
126
|
+
Options extends DeclaredTableOptions
|
|
127
|
+
> = Options extends readonly [infer Head, ...infer Tail]
|
|
128
|
+
? Head extends TableOptionBuilderLike<infer Spec>
|
|
129
|
+
? Tail extends DeclaredTableOptions
|
|
130
|
+
? ApplyDeclaredOptions<ApplyOption<Table, Spec>, Tail>
|
|
131
|
+
: ApplyOption<Table, Spec>
|
|
132
|
+
: Table
|
|
133
|
+
: Table
|
|
134
|
+
|
|
93
135
|
export type MissingSelfGeneric = "Missing `Self` generic - use `class Self extends Table.Class<Self>(...) {}`"
|
|
94
136
|
|
|
95
137
|
/** Bound columns keyed by field name for a particular table. */
|
|
@@ -132,16 +174,17 @@ export interface TableSchemaNamespace<SchemaName extends string> {
|
|
|
132
174
|
readonly table: <
|
|
133
175
|
Name extends string,
|
|
134
176
|
Fields extends TableFieldMap,
|
|
177
|
+
const Options extends DeclaredTableOptions,
|
|
135
178
|
PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>
|
|
136
179
|
>(
|
|
137
180
|
name: Name,
|
|
138
181
|
fields: Fields,
|
|
139
|
-
...options:
|
|
140
|
-
) => TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>
|
|
182
|
+
...options: Options & ValidateDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
|
|
183
|
+
) => ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
|
|
141
184
|
}
|
|
142
185
|
|
|
143
186
|
export type DeclaredTableOptions = readonly TableOptionBuilderLike[]
|
|
144
|
-
export type { DdlExpressionLike, IndexKeySpec, NormalizeColumns, ReferentialAction } from "./table-options.js"
|
|
187
|
+
export type { DdlExpressionLike, IndexKeySpec, MatchingColumnArityInput, NonEmptyColumnInput, NormalizeColumns, ReferentialAction } from "./table-options.js"
|
|
145
188
|
|
|
146
189
|
export type TableDefinition<
|
|
147
190
|
Name extends string,
|
|
@@ -192,7 +235,7 @@ export type TableClassStatic<
|
|
|
192
235
|
>
|
|
193
236
|
readonly [OptionsSymbol]: readonly TableOptionSpec[]
|
|
194
237
|
readonly [DeclaredOptionsSymbol]?: readonly TableOptionSpec[]
|
|
195
|
-
readonly [options]?: ClassDeclaredTableOptions
|
|
238
|
+
readonly [options]?: ClassDeclaredTableOptions<Fields>
|
|
196
239
|
readonly tableName: Name
|
|
197
240
|
} & BoundColumns<Name, Fields> & Plan.RowSet<
|
|
198
241
|
BoundColumns<Name, Fields>,
|
|
@@ -204,6 +247,14 @@ export type TableClassStatic<
|
|
|
204
247
|
/** Minimal structural table-like contract used across helper APIs. */
|
|
205
248
|
export type AnyTable = TableDefinition<any, any, any, any, any> | TableClassStatic<any, any, any, any>
|
|
206
249
|
|
|
250
|
+
type FieldsOfAnyTable<Table extends AnyTable> = Table extends TableDefinition<any, infer Fields extends TableFieldMap, any, any, any>
|
|
251
|
+
? Fields
|
|
252
|
+
: Table extends TableClassStatic<any, infer Fields extends TableFieldMap, any, any>
|
|
253
|
+
? Fields
|
|
254
|
+
: never
|
|
255
|
+
|
|
256
|
+
type ColumnNamesOfAnyTable<Table extends AnyTable> = Extract<keyof FieldsOfAnyTable<Table>, string>
|
|
257
|
+
|
|
207
258
|
/** Public table-option builder type used by `Table.index`, `Table.primaryKey`, and friends. */
|
|
208
259
|
export type TableOption<
|
|
209
260
|
Spec extends TableOptionSpec = TableOptionSpec
|
|
@@ -341,7 +392,10 @@ const applyDeclaredOptions = <
|
|
|
341
392
|
return table
|
|
342
393
|
}
|
|
343
394
|
return declaredOptions.reduce<TableDefinition<any, any, any, "schema", any>>(
|
|
344
|
-
(current, option) =>
|
|
395
|
+
(current, option) =>
|
|
396
|
+
(option as unknown as (
|
|
397
|
+
table: TableDefinition<any, any, any, "schema", any>
|
|
398
|
+
) => TableDefinition<any, any, any, "schema", any>)(current),
|
|
345
399
|
table
|
|
346
400
|
) as unknown as Table
|
|
347
401
|
}
|
|
@@ -491,17 +545,17 @@ export function make<
|
|
|
491
545
|
*/
|
|
492
546
|
export const schema = <SchemaName extends string>(
|
|
493
547
|
schemaName: SchemaName
|
|
494
|
-
): TableSchemaNamespace<SchemaName> =>
|
|
495
|
-
|
|
496
|
-
table: <
|
|
548
|
+
): TableSchemaNamespace<SchemaName> => {
|
|
549
|
+
const table = <
|
|
497
550
|
Name extends string,
|
|
498
551
|
Fields extends TableFieldMap,
|
|
552
|
+
const Options extends DeclaredTableOptions,
|
|
499
553
|
PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>
|
|
500
554
|
>(
|
|
501
555
|
name: Name,
|
|
502
556
|
fields: Fields,
|
|
503
|
-
...options:
|
|
504
|
-
): TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName> =>
|
|
557
|
+
...options: Options & ValidateDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
|
|
558
|
+
): ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options> =>
|
|
505
559
|
applyDeclaredOptions(
|
|
506
560
|
makeTable(
|
|
507
561
|
name,
|
|
@@ -512,9 +566,13 @@ export const schema = <SchemaName extends string>(
|
|
|
512
566
|
schemaName,
|
|
513
567
|
"explicit"
|
|
514
568
|
) as TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>,
|
|
515
|
-
options
|
|
516
|
-
)
|
|
517
|
-
|
|
569
|
+
options as unknown as Options
|
|
570
|
+
) as ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
|
|
571
|
+
return {
|
|
572
|
+
schemaName,
|
|
573
|
+
table
|
|
574
|
+
} as unknown as TableSchemaNamespace<SchemaName>
|
|
575
|
+
}
|
|
518
576
|
|
|
519
577
|
/**
|
|
520
578
|
* Creates an aliased source from an existing table definition.
|
|
@@ -664,7 +722,7 @@ export function Class<
|
|
|
664
722
|
export const primaryKey = <
|
|
665
723
|
const Columns extends string | readonly string[]
|
|
666
724
|
>(
|
|
667
|
-
columns: Columns
|
|
725
|
+
columns: Columns & NonEmptyColumnInput<Columns>
|
|
668
726
|
): TableOption<{
|
|
669
727
|
readonly kind: "primaryKey"
|
|
670
728
|
readonly columns: NormalizeColumns<Columns>
|
|
@@ -677,7 +735,7 @@ export const primaryKey = <
|
|
|
677
735
|
export const unique = <
|
|
678
736
|
const Columns extends string | readonly string[]
|
|
679
737
|
>(
|
|
680
|
-
columns: Columns
|
|
738
|
+
columns: Columns & NonEmptyColumnInput<Columns>
|
|
681
739
|
): TableOption<{
|
|
682
740
|
readonly kind: "unique"
|
|
683
741
|
readonly columns: NormalizeColumns<Columns>
|
|
@@ -690,7 +748,7 @@ export const unique = <
|
|
|
690
748
|
export const index = <
|
|
691
749
|
const Columns extends string | readonly string[]
|
|
692
750
|
>(
|
|
693
|
-
columns: Columns
|
|
751
|
+
columns: Columns & NonEmptyColumnInput<Columns>
|
|
694
752
|
): TableOption<{
|
|
695
753
|
readonly kind: "index"
|
|
696
754
|
readonly columns: NormalizeColumns<Columns>
|
|
@@ -705,9 +763,9 @@ export const foreignKey = <
|
|
|
705
763
|
TargetTable extends AnyTable,
|
|
706
764
|
const TargetColumns extends string | readonly string[]
|
|
707
765
|
>(
|
|
708
|
-
columns: LocalColumns
|
|
766
|
+
columns: LocalColumns & NonEmptyColumnInput<LocalColumns>,
|
|
709
767
|
target: () => TargetTable,
|
|
710
|
-
referencedColumns: TargetColumns
|
|
768
|
+
referencedColumns: TargetColumns & NonEmptyColumnInput<TargetColumns> & MatchingColumnArityInput<LocalColumns, TargetColumns>
|
|
711
769
|
): TableOption<{
|
|
712
770
|
readonly kind: "foreignKey"
|
|
713
771
|
readonly columns: NormalizeColumns<LocalColumns>
|
|
@@ -715,7 +773,7 @@ export const foreignKey = <
|
|
|
715
773
|
readonly tableName: string
|
|
716
774
|
readonly schemaName?: string
|
|
717
775
|
readonly columns: NormalizeColumns<TargetColumns>
|
|
718
|
-
readonly knownColumns: readonly
|
|
776
|
+
readonly knownColumns: readonly ColumnNamesOfAnyTable<TargetTable>[]
|
|
719
777
|
}
|
|
720
778
|
}> => makeOption({
|
|
721
779
|
kind: "foreignKey",
|
|
@@ -724,7 +782,7 @@ export const foreignKey = <
|
|
|
724
782
|
tableName: target()[TypeId].baseName,
|
|
725
783
|
schemaName: target()[TypeId].schemaName,
|
|
726
784
|
columns: normalizeColumnList(referencedColumns) as NormalizeColumns<TargetColumns>,
|
|
727
|
-
knownColumns: Object.keys(target()[TypeId].fields)
|
|
785
|
+
knownColumns: Object.keys(target()[TypeId].fields) as unknown as readonly ColumnNamesOfAnyTable<TargetTable>[]
|
|
728
786
|
})
|
|
729
787
|
})
|
|
730
788
|
|
package/src/mysql/column.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema"
|
|
2
2
|
|
|
3
3
|
import * as BaseColumn from "../internal/column.js"
|
|
4
|
-
import { makeColumnDefinition, type ColumnDefinition } from "../internal/column-state.js"
|
|
4
|
+
import { makeColumnDefinition, type AnyColumnDefinition, type ColumnDefinition } from "../internal/column-state.js"
|
|
5
5
|
import type * as Expression from "../internal/scalar.js"
|
|
6
6
|
import {
|
|
7
7
|
DecimalStringSchema,
|
|
@@ -97,9 +97,26 @@ export const json = <SchemaType extends Schema.Schema.Any>(schema: SchemaType) =
|
|
|
97
97
|
export const nullable = BaseColumn.nullable
|
|
98
98
|
export const brand = BaseColumn.brand
|
|
99
99
|
export const primaryKey = BaseColumn.primaryKey
|
|
100
|
-
|
|
100
|
+
type UniqueColumn<Column extends AnyColumnDefinition> = ReturnType<typeof BaseColumn.unique<Column>>
|
|
101
|
+
|
|
102
|
+
type MysqlUniqueOptions = {
|
|
103
|
+
readonly name?: string
|
|
104
|
+
readonly nullsNotDistinct?: never
|
|
105
|
+
readonly deferrable?: never
|
|
106
|
+
readonly initiallyDeferred?: never
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type UniqueModifier = {
|
|
110
|
+
<Column extends AnyColumnDefinition>(column: Column): UniqueColumn<Column>
|
|
111
|
+
readonly options: <const Options extends MysqlUniqueOptions>(
|
|
112
|
+
options: Options
|
|
113
|
+
) => <Column extends AnyColumnDefinition>(column: Column) => UniqueColumn<Column>
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const unique = BaseColumn.unique as UniqueModifier
|
|
101
117
|
const default_ = BaseColumn.default_
|
|
102
118
|
export const generated = BaseColumn.generated
|
|
119
|
+
export const driverValueMapping = BaseColumn.driverValueMapping
|
|
103
120
|
export const references = BaseColumn.references
|
|
104
121
|
export const schema = BaseColumn.schema
|
|
105
122
|
export { default_ as default }
|