effect-qb 0.17.0 → 0.20.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.
Files changed (137) hide show
  1. package/README.md +11 -1
  2. package/dist/index.js +9376 -0
  3. package/dist/mysql.js +4092 -2671
  4. package/dist/postgres/metadata.js +2589 -1402
  5. package/dist/postgres.js +3953 -3583
  6. package/dist/sqlite.js +5527 -4088
  7. package/dist/standard.js +9330 -0
  8. package/package.json +9 -4
  9. package/src/casing.ts +71 -0
  10. package/src/index.ts +2 -0
  11. package/src/internal/casing.ts +89 -0
  12. package/src/internal/coercion/rules.ts +13 -1
  13. package/src/internal/column-state.d.ts +3 -3
  14. package/src/internal/column-state.ts +24 -18
  15. package/src/internal/column.ts +52 -15
  16. package/src/internal/datatypes/define.ts +7 -1
  17. package/src/internal/datatypes/enrich.ts +23 -0
  18. package/src/internal/datatypes/lookup.ts +81 -25
  19. package/src/internal/datatypes/matrix.ts +903 -0
  20. package/src/internal/datatypes/shape.ts +2 -0
  21. package/src/internal/derived-table.ts +4 -36
  22. package/src/{mysql/internal/sql-expression-renderer.ts → internal/dialect-renderers/mysql.ts} +552 -361
  23. package/src/{postgres/internal/sql-expression-renderer.ts → internal/dialect-renderers/postgres.ts} +657 -400
  24. package/src/{sqlite/internal/sql-expression-renderer.ts → internal/dialect-renderers/sqlite.ts} +505 -347
  25. package/src/internal/dialect.ts +36 -1
  26. package/src/internal/dsl-mutation-runtime.ts +12 -162
  27. package/src/internal/dsl-plan-runtime.ts +10 -138
  28. package/src/internal/dsl-query-runtime.ts +5 -79
  29. package/src/internal/dsl-transaction-ddl-runtime.ts +41 -65
  30. package/src/internal/executor.ts +66 -49
  31. package/src/internal/grouping-key.ts +87 -20
  32. package/src/internal/implication-runtime.ts +1 -1
  33. package/src/internal/json/path-access.ts +351 -0
  34. package/src/internal/predicate/runtime.ts +3 -0
  35. package/src/internal/query.d.ts +39 -12
  36. package/src/internal/query.ts +65 -26
  37. package/src/internal/renderer.ts +26 -14
  38. package/src/internal/runtime/driver-value-mapping.ts +3 -3
  39. package/src/internal/runtime/normalize.ts +12 -5
  40. package/src/internal/runtime/schema.ts +28 -38
  41. package/src/internal/runtime/value.ts +20 -23
  42. package/src/internal/scalar.d.ts +1 -1
  43. package/src/internal/scalar.ts +8 -2
  44. package/src/internal/schema-derivation.d.ts +12 -61
  45. package/src/internal/schema-derivation.ts +95 -43
  46. package/src/internal/schema-expression.ts +2 -2
  47. package/src/internal/sql-expression-renderer.ts +19 -0
  48. package/src/internal/standard-dsl.ts +6978 -0
  49. package/src/internal/table-options.ts +126 -66
  50. package/src/internal/table.ts +819 -237
  51. package/src/mysql/column-extension.ts +3 -0
  52. package/src/mysql/column.ts +14 -15
  53. package/src/mysql/datatypes/index.ts +4 -2
  54. package/src/mysql/datatypes/spec.ts +6 -176
  55. package/src/mysql/errors/normalize.ts +0 -1
  56. package/src/mysql/executor.ts +11 -11
  57. package/src/mysql/function/temporal.ts +1 -1
  58. package/src/mysql/internal/dialect.ts +9 -4
  59. package/src/mysql/internal/dsl.ts +334 -170
  60. package/src/mysql/internal/renderer.ts +6 -2
  61. package/src/mysql/json.ts +7 -2
  62. package/src/mysql/query-extension.ts +16 -0
  63. package/src/mysql/renderer.ts +37 -3
  64. package/src/mysql/type.ts +60 -0
  65. package/src/mysql.ts +7 -13
  66. package/src/postgres/check.ts +1 -0
  67. package/src/postgres/column-extension.ts +28 -0
  68. package/src/postgres/column.ts +13 -19
  69. package/src/postgres/datatypes/index.d.ts +2 -1
  70. package/src/postgres/datatypes/index.ts +4 -2
  71. package/src/postgres/datatypes/spec.ts +6 -260
  72. package/src/postgres/errors/normalize.ts +0 -1
  73. package/src/postgres/executor.ts +11 -11
  74. package/src/postgres/foreign-key.ts +24 -0
  75. package/src/postgres/function/core.ts +1 -3
  76. package/src/postgres/function/index.ts +1 -17
  77. package/src/postgres/function/temporal.ts +1 -1
  78. package/src/postgres/index.ts +1 -0
  79. package/src/postgres/internal/dialect.ts +9 -4
  80. package/src/postgres/internal/dsl.ts +328 -179
  81. package/src/postgres/internal/renderer.ts +6 -2
  82. package/src/postgres/internal/schema-ddl.ts +22 -10
  83. package/src/postgres/internal/schema-model.ts +238 -7
  84. package/src/postgres/json-extension.ts +7 -0
  85. package/src/postgres/json.ts +762 -173
  86. package/src/postgres/jsonb.ts +37 -0
  87. package/src/postgres/primary-key.ts +24 -0
  88. package/src/postgres/query-extension.ts +2 -0
  89. package/src/postgres/renderer.ts +37 -3
  90. package/src/postgres/schema-management.ts +13 -13
  91. package/src/postgres/schema.ts +106 -15
  92. package/src/postgres/table.ts +205 -538
  93. package/src/postgres/type.ts +93 -10
  94. package/src/postgres/unique.ts +32 -0
  95. package/src/postgres.ts +20 -16
  96. package/src/sqlite/column-extension.ts +3 -0
  97. package/src/sqlite/column.ts +14 -15
  98. package/src/sqlite/datatypes/index.ts +4 -2
  99. package/src/sqlite/datatypes/spec.ts +6 -94
  100. package/src/sqlite/errors/normalize.ts +0 -1
  101. package/src/sqlite/executor.ts +11 -11
  102. package/src/sqlite/function/temporal.ts +1 -1
  103. package/src/sqlite/internal/dialect.ts +9 -4
  104. package/src/sqlite/internal/dsl.ts +307 -159
  105. package/src/sqlite/internal/renderer.ts +6 -2
  106. package/src/sqlite/json.ts +8 -2
  107. package/src/sqlite/query-extension.ts +2 -0
  108. package/src/sqlite/renderer.ts +37 -3
  109. package/src/sqlite/type.ts +40 -0
  110. package/src/sqlite.ts +7 -13
  111. package/src/standard/cast.ts +113 -0
  112. package/src/standard/check.ts +17 -0
  113. package/src/standard/column.ts +163 -0
  114. package/src/standard/datatypes/index.ts +83 -0
  115. package/src/standard/datatypes/spec.ts +12 -0
  116. package/src/standard/dialect.ts +40 -0
  117. package/src/standard/foreign-key.ts +37 -0
  118. package/src/standard/function/aggregate.ts +2 -0
  119. package/src/standard/function/core.ts +2 -0
  120. package/src/standard/function/index.ts +18 -0
  121. package/src/standard/function/string.ts +2 -0
  122. package/src/standard/function/temporal.ts +78 -0
  123. package/src/standard/function/window.ts +2 -0
  124. package/src/standard/index.ts +17 -0
  125. package/src/standard/internal/renderer.ts +45 -0
  126. package/src/standard/json.ts +883 -0
  127. package/src/standard/primary-key.ts +17 -0
  128. package/src/standard/query.ts +152 -0
  129. package/src/standard/renderer.ts +49 -0
  130. package/src/standard/table.ts +151 -0
  131. package/src/standard/unique.ts +17 -0
  132. package/src/standard.ts +32 -0
  133. package/src/internal/aggregation-validation.ts +0 -57
  134. package/src/internal/table.d.ts +0 -173
  135. package/src/mysql/table.ts +0 -183
  136. package/src/postgres/cast.ts +0 -45
  137. package/src/sqlite/table.ts +0 -183
@@ -5,16 +5,15 @@ import * as Plan from "./row-set.js"
5
5
  import type { Any as AnyExpression } from "./scalar.js"
6
6
  import type { TrueFormula } from "./predicate/formula.js"
7
7
  import type { BoundColumnFrom } from "./column-state.js"
8
- import { bindColumn, type AnyColumnDefinition } from "./column-state.js"
8
+ import { bindColumn, BoundColumnTypeId, type AnyBoundColumn, type AnyColumnDefinition } from "./column-state.js"
9
9
  import {
10
10
  collectInlineOptions,
11
- normalizeColumnList,
12
11
  resolvePrimaryKeyColumns,
12
+ type ColumnList,
13
13
  type DdlExpressionLike,
14
14
  type IndexKeySpec,
15
- type MatchingColumnArityInput,
16
- type NonEmptyColumnInput,
17
- type NormalizeColumns,
15
+ type LiteralStringInput,
16
+ type NonEmptyStringInput,
18
17
  type ReferentialAction,
19
18
  type TableOptionSpec,
20
19
  type ValidateForeignKeyOptionColumns,
@@ -24,12 +23,16 @@ import {
24
23
  validateOptions
25
24
  } from "./table-options.js"
26
25
  import {
27
- deriveSchemas,
26
+ deriveInsertSchema,
27
+ deriveSelectSchema,
28
+ deriveUpdateSchema,
28
29
  type InsertRow,
29
30
  type SelectRow,
30
31
  type TableFieldMap,
32
+ type TableSchemaVariant,
31
33
  type UpdateRow
32
34
  } from "./schema-derivation.js"
35
+ import * as Casing from "./casing.js"
33
36
 
34
37
  /** Symbol used to attach table-definition metadata. */
35
38
  export const TypeId: unique symbol = Symbol.for("effect-qb/Table")
@@ -39,17 +42,62 @@ export const OptionsSymbol: unique symbol = Symbol.for("effect-qb/Table/normaliz
39
42
  export const options: unique symbol = Symbol.for("effect-qb/Table/declaredOptions")
40
43
 
41
44
  const CacheSymbol: unique symbol = Symbol.for("effect-qb/Table/cache")
45
+ const SchemaCacheSymbol: unique symbol = Symbol.for("effect-qb/Table/schemaCache")
42
46
  const DeclaredOptionsSymbol: unique symbol = Symbol.for("effect-qb/Table/factoryDeclaredOptions")
47
+ const ResolveOptionSymbol: unique symbol = Symbol.for("effect-qb/Table/resolveOption")
43
48
 
44
49
  type InlinePrimaryKeyKeys<Fields extends TableFieldMap> = Extract<{
45
50
  [K in keyof Fields]: Fields[K]["metadata"]["primaryKey"] extends true ? K : never
46
51
  }[keyof Fields], string>
47
52
 
48
- type TableDialect<Fields extends TableFieldMap> = Fields[keyof Fields][typeof import("./column-state.js").ColumnTypeId]["dbType"]["dialect"]
53
+ type FieldDialects<Fields extends TableFieldMap> = Fields[keyof Fields][typeof import("./column-state.js").ColumnTypeId]["dbType"]["dialect"]
54
+ type ConcreteFieldDialects<Fields extends TableFieldMap> = Exclude<FieldDialects<Fields>, "standard">
55
+ type TableDialect<Fields extends TableFieldMap> = [ConcreteFieldDialects<Fields>] extends [never]
56
+ ? "standard"
57
+ : ConcreteFieldDialects<Fields>
49
58
  type TableKind = "schema" | "alias"
50
59
  type DefaultSchemaName = "public"
60
+ type NonEmptyFieldMap<Fields extends TableFieldMap> =
61
+ string extends keyof Fields ? Fields : "" extends keyof Fields ? never : Fields
51
62
  type FieldColumnName<Fields extends TableFieldMap> = Extract<keyof Fields, string>
52
63
  type FieldColumnList<Fields extends TableFieldMap> = readonly [FieldColumnName<Fields>, ...FieldColumnName<Fields>[]]
64
+ export type SchemaTableDefinition = TableDefinition<any, any, any, "schema", any, any>
65
+ type LooseTableSelection = any
66
+ type ConcreteSelector<
67
+ Table extends SchemaTableDefinition,
68
+ Selection extends AnyColumnSelection
69
+ > = SchemaTableDefinition extends Table ? never : (table: Table) => Selection
70
+ type ColumnNameOfBound<Column> = Column extends {
71
+ readonly [BoundColumnTypeId]: {
72
+ readonly columnName: infer ColumnName extends string
73
+ }
74
+ } ? ColumnName : never
75
+ type BoundColumnTupleNames<Columns extends readonly AnyBoundColumn[]> =
76
+ Columns extends readonly [infer Head extends AnyBoundColumn, ...infer Tail extends AnyBoundColumn[]]
77
+ ? readonly [ColumnNameOfBound<Head>, ...BoundColumnTupleNames<Tail>]
78
+ : readonly []
79
+ type ColumnSelectionNames<Selection> =
80
+ Selection extends AnyBoundColumn
81
+ ? readonly [ColumnNameOfBound<Selection>]
82
+ : Selection extends readonly [infer Head extends AnyBoundColumn, ...infer Tail extends AnyBoundColumn[]]
83
+ ? readonly [ColumnNameOfBound<Head>, ...BoundColumnTupleNames<Tail>]
84
+ : never
85
+ export type TableColumnSelection<Table extends SchemaTableDefinition> =
86
+ TableColumn<Table> | readonly [TableColumn<Table>, ...TableColumn<Table>[]]
87
+ export type TableColumn<Table extends SchemaTableDefinition> =
88
+ SchemaTableDefinition extends Table ? AnyBoundColumn : Table extends TableDefinition<infer Name, infer Fields, any, "schema", any>
89
+ ? BoundColumns<Name, Fields>[Extract<keyof Fields, string>]
90
+ : never
91
+ export type AnyColumnSelection = AnyBoundColumn | readonly [AnyBoundColumn, ...AnyBoundColumn[]]
92
+ export type SelectedColumns<Selection extends AnyColumnSelection> = ColumnSelectionNames<Selection>
93
+ type MatchingSelectionArityInput<
94
+ Left extends AnyColumnSelection,
95
+ Right extends AnyColumnSelection
96
+ > = SelectedColumns<Left>["length"] extends SelectedColumns<Right>["length"]
97
+ ? SelectedColumns<Right>["length"] extends SelectedColumns<Left>["length"]
98
+ ? unknown
99
+ : never
100
+ : never
53
101
  type FieldIndexKeySpec<Fields extends TableFieldMap> =
54
102
  | (Extract<IndexKeySpec, { readonly kind: "column" }> & { readonly column: FieldColumnName<Fields> })
55
103
  | Extract<IndexKeySpec, { readonly kind: "expression" }>
@@ -67,53 +115,162 @@ type ClassOptionSpec<Fields extends TableFieldMap = TableFieldMap> =
67
115
  })
68
116
  | Extract<TableOptionSpec, { readonly kind: "check" }>
69
117
  interface TableOptionBuilderLike<
70
- Spec extends TableOptionSpec = TableOptionSpec
118
+ Spec extends TableOptionSpec = TableOptionSpec,
119
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
71
120
  > {
72
121
  readonly option: Spec
73
122
  }
74
123
 
75
- type ClassTableOption<Fields extends TableFieldMap> = TableOptionBuilderLike<ClassOptionSpec<Fields>>
124
+ type ClassTableOption<Fields extends TableFieldMap> = TableOption<
125
+ ClassOptionSpec<Fields>,
126
+ TableDefinition<string, Fields, keyof Fields & string, "schema", any>
127
+ >
76
128
  type ClassDeclaredTableOptions<Fields extends TableFieldMap> = readonly ClassTableOption<Fields>[]
129
+ type TableNameOf<Table extends TableDefinition<any, any, any, "schema", any>> =
130
+ Table extends TableDefinition<infer Name, any, any, "schema", any> ? Name : never
131
+ type TableFieldsOf<Table extends TableDefinition<any, any, any, "schema", any>> =
132
+ Table extends TableDefinition<any, infer Fields, any, "schema", any> ? Fields : never
133
+ type TablePrimaryKeyOf<Table extends TableDefinition<any, any, any, "schema", any>> =
134
+ Table extends TableDefinition<any, any, infer PrimaryKeyColumns, "schema", any> ? PrimaryKeyColumns : never
135
+ type TableSchemaNameOf<Table extends TableDefinition<any, any, any, "schema", any>> =
136
+ Table extends TableDefinition<any, any, any, "schema", infer SchemaName> ? SchemaName : never
137
+ type HasBroadColumns<Columns extends readonly string[]> = string extends Columns[number] ? true : false
138
+
139
+ export type ConflictArbiterScope = "unconditional" | "partial"
140
+ export type ConflictArbiter<
141
+ Columns extends ColumnList = ColumnList,
142
+ Scope extends ConflictArbiterScope = ConflictArbiterScope,
143
+ Name extends string | undefined = string | undefined,
144
+ Constraint extends boolean = boolean
145
+ > = {
146
+ readonly columns: Columns
147
+ readonly scope: Scope
148
+ readonly name?: Name
149
+ readonly constraint: Constraint
150
+ }
151
+
152
+ type InlineConflictArbiters<Fields extends TableFieldMap> = Extract<{
153
+ [K in keyof Fields]: Fields[K] extends AnyColumnDefinition
154
+ ? Fields[K]["metadata"]["primaryKey"] extends true
155
+ ? ConflictArbiter<readonly [Extract<K, string>], "unconditional", undefined, true>
156
+ : Fields[K]["metadata"]["unique"] extends true
157
+ ? ConflictArbiter<
158
+ readonly [Extract<K, string>],
159
+ "unconditional",
160
+ Fields[K]["metadata"]["uniqueConstraint"] extends { readonly name?: infer Name extends string } ? Name : undefined,
161
+ true
162
+ >
163
+ : never
164
+ : never
165
+ }[keyof Fields], ConflictArbiter>
166
+
167
+ type TableConflictArbitersOf<Table extends TableDefinition<any, any, any, any, any, any>> =
168
+ Table extends TableDefinition<any, any, any, any, any, infer ConflictArbiters> ? ConflictArbiters : never
169
+
170
+ type NormalizeArbiterColumns<Columns> =
171
+ Columns extends readonly [infer Head extends string, ...infer Tail extends string[]]
172
+ ? readonly [Head, ...Tail]
173
+ : Columns extends string
174
+ ? readonly [Columns]
175
+ : never
176
+
177
+ type OptionColumns<Spec extends TableOptionSpec> =
178
+ Spec extends { readonly columns?: infer Columns } ? NormalizeArbiterColumns<Columns> : never
179
+
180
+ type OptionName<Spec extends TableOptionSpec> =
181
+ Spec extends { readonly name: infer Name extends string } ? Name : undefined
182
+
183
+ type ConflictArbiterFromOption<Spec extends TableOptionSpec> =
184
+ OptionColumns<Spec> extends infer Columns extends ColumnList
185
+ ? HasBroadColumns<Columns> extends true
186
+ ? never
187
+ : Spec extends { readonly kind: "primaryKey" | "unique" }
188
+ ? ConflictArbiter<Columns, "unconditional", OptionName<Spec>, true>
189
+ : Spec extends { readonly kind: "index"; readonly unique: true }
190
+ ? ConflictArbiter<Columns, Spec extends { readonly predicate: DdlExpressionLike } ? "partial" : "unconditional", OptionName<Spec>, false>
191
+ : never
192
+ : never
77
193
 
78
194
  type BuildPrimaryKey<
79
- Table extends TableDefinition<any, any, any, "schema", any>,
195
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
80
196
  Spec extends TableOptionSpec
81
197
  > = Spec extends { readonly kind: "primaryKey"; readonly columns: infer Columns extends readonly string[] }
82
- ? Columns[number] & keyof Table[typeof TypeId]["fields"] & string
83
- : Table[typeof TypeId]["primaryKey"][number]
198
+ ? HasBroadColumns<Columns> extends true
199
+ ? TablePrimaryKeyOf<Table>
200
+ : Columns[number] & keyof TableFieldsOf<Table> & string
201
+ : TablePrimaryKeyOf<Table>
84
202
 
85
- type OptionInputTable<
86
- Table extends TableDefinition<any, any, any, "schema", any>,
203
+ type OptionInputConstraint<
204
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
87
205
  Spec extends TableOptionSpec
88
206
  > = Spec extends { readonly kind: "primaryKey"; readonly columns: infer Columns extends readonly string[] }
89
- ? ValidatePrimaryKeyColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : Table
207
+ ? HasBroadColumns<Columns> extends true ? unknown : ValidatePrimaryKeyColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : unknown
90
208
  : Spec extends { readonly kind: "index" }
91
- ? ValidateIndexOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : Table
209
+ ? Spec extends { readonly columns: infer Columns extends readonly string[] }
210
+ ? HasBroadColumns<Columns> extends true ? unknown : ValidateIndexOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : unknown
211
+ : ValidateIndexOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : unknown
92
212
  : Spec extends { readonly kind: "foreignKey" }
93
- ? ValidateForeignKeyOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : Table
213
+ ? Spec extends { readonly columns: infer Columns extends readonly string[] }
214
+ ? HasBroadColumns<Columns> extends true ? unknown : ValidateForeignKeyOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : unknown
215
+ : ValidateForeignKeyOptionColumns<Table[typeof TypeId]["fields"], Spec> extends never ? never : unknown
94
216
  : Spec extends { readonly columns: infer Columns extends readonly string[] }
95
- ? ValidateKnownColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : Table
96
- : Table
217
+ ? HasBroadColumns<Columns> extends true ? unknown : ValidateKnownColumns<Table[typeof TypeId]["fields"], Columns> extends never ? never : unknown
218
+ : unknown
219
+
220
+ type OptionInputTable<
221
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
222
+ Spec extends TableOptionSpec
223
+ > = Table & OptionInputConstraint<Table, Spec>
224
+
225
+ type OptionValidationArgs<
226
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
227
+ Spec extends TableOptionSpec
228
+ > = OptionInputConstraint<Table, Spec> extends never ? [never] : []
97
229
 
98
230
  type ApplyOption<
99
- Table extends TableDefinition<any, any, any, "schema", any>,
231
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
100
232
  Spec extends TableOptionSpec
101
233
  > = Spec extends { readonly kind: "primaryKey" }
102
234
  ? TableDefinition<
103
- Table[typeof TypeId]["name"],
104
- Table[typeof TypeId]["fields"],
235
+ TableNameOf<Table>,
236
+ TableFieldsOf<Table>,
105
237
  BuildPrimaryKey<Table, Spec>,
106
- "schema"
238
+ "schema",
239
+ TableSchemaNameOf<Table>,
240
+ TableConflictArbitersOf<Table> | ConflictArbiterFromOption<Spec>
107
241
  >
108
242
  : TableDefinition<
109
- Table[typeof TypeId]["name"],
110
- Table[typeof TypeId]["fields"],
111
- Table[typeof TypeId]["primaryKey"][number],
112
- "schema"
243
+ TableNameOf<Table>,
244
+ TableFieldsOf<Table>,
245
+ TablePrimaryKeyOf<Table>,
246
+ "schema",
247
+ TableSchemaNameOf<Table>,
248
+ TableConflictArbitersOf<Table> | ConflictArbiterFromOption<Spec>
113
249
  >
114
250
 
251
+ type ApplyTableOption<
252
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
253
+ Spec extends TableOptionSpec
254
+ > = ApplyOption<Table, Spec>
255
+
256
+ type TableOptionPipe<
257
+ Name extends string,
258
+ Fields extends TableFieldMap,
259
+ PrimaryKeyColumns extends keyof Fields & string,
260
+ Kind extends TableKind,
261
+ SchemaName extends string | undefined,
262
+ ConflictArbiters extends ConflictArbiter
263
+ > = Kind extends "schema"
264
+ ? {
265
+ pipe<Spec extends TableOptionSpec>(
266
+ option: TableOption<Spec, TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName, ConflictArbiters>>,
267
+ ...validation: OptionValidationArgs<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName, ConflictArbiters>, Spec>
268
+ ): ApplyTableOption<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName, ConflictArbiters>, Spec>
269
+ }
270
+ : {}
271
+
115
272
  export type ValidateDeclaredOptions<
116
- Table extends TableDefinition<any, any, any, "schema", any>,
273
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
117
274
  Options extends DeclaredTableOptions
118
275
  > = {
119
276
  readonly [K in keyof Options]: Options[K] extends TableOptionBuilderLike<infer Spec>
@@ -122,7 +279,7 @@ export type ValidateDeclaredOptions<
122
279
  }
123
280
 
124
281
  export type ApplyDeclaredOptions<
125
- Table extends TableDefinition<any, any, any, "schema", any>,
282
+ Table extends TableDefinition<any, any, any, "schema", any, any>,
126
283
  Options extends DeclaredTableOptions
127
284
  > = Options extends readonly [infer Head, ...infer Tail]
128
285
  ? Head extends TableOptionBuilderLike<infer Spec>
@@ -148,9 +305,23 @@ export interface TableSchemas<
148
305
  Fields extends TableFieldMap,
149
306
  PrimaryKeyColumns extends keyof Fields & string
150
307
  > {
151
- readonly select: Schema.Schema<SelectRow<Name, Fields>>
152
- readonly insert: Schema.Schema<InsertRow<Name, Fields>>
153
- readonly update: Schema.Schema<UpdateRow<Name, Fields, PrimaryKeyColumns>>
308
+ readonly select: Schema.ConstraintDecoder<SelectRow<Name, Fields>, never>
309
+ readonly insert: Schema.ConstraintDecoder<InsertRow<Name, Fields>, never>
310
+ readonly update: Schema.ConstraintDecoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never>
311
+ }
312
+
313
+ type AnyTableSchemas = {
314
+ readonly select: Schema.Schema<any>
315
+ readonly insert: Schema.Schema<any>
316
+ readonly update: Schema.Schema<any>
317
+ }
318
+
319
+ type TableSchemaCache<
320
+ Name extends string,
321
+ Fields extends TableFieldMap,
322
+ PrimaryKeyColumns extends keyof Fields & string
323
+ > = Partial<TableSchemas<Name, Fields, PrimaryKeyColumns>> & {
324
+ schemas?: TableSchemas<Name, Fields, PrimaryKeyColumns>
154
325
  }
155
326
 
156
327
  interface TableState<
@@ -158,45 +329,37 @@ interface TableState<
158
329
  Fields extends TableFieldMap,
159
330
  PrimaryKeyColumns extends keyof Fields & string,
160
331
  Kind extends TableKind = "schema",
161
- SchemaName extends string | undefined = DefaultSchemaName
332
+ SchemaName extends string | undefined = DefaultSchemaName,
333
+ ConflictArbiters extends ConflictArbiter = InlineConflictArbiters<Fields>
162
334
  > {
163
335
  readonly name: Name
164
336
  readonly baseName: string
165
337
  readonly schemaName: SchemaName
166
338
  readonly fields: Fields
167
339
  readonly primaryKey: readonly PrimaryKeyColumns[]
340
+ readonly conflictArbiters: readonly ConflictArbiters[]
168
341
  readonly kind: Kind
169
- }
170
-
171
- /** Namespace-scoped table builder. */
172
- export interface TableSchemaNamespace<SchemaName extends string> {
173
- readonly schemaName: SchemaName
174
- readonly table: <
175
- Name extends string,
176
- Fields extends TableFieldMap,
177
- const Options extends DeclaredTableOptions,
178
- PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>
179
- >(
180
- name: Name,
181
- fields: Fields,
182
- ...options: Options & ValidateDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
183
- ) => ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
342
+ readonly casing?: Casing.Options
184
343
  }
185
344
 
186
345
  export type DeclaredTableOptions = readonly TableOptionBuilderLike[]
187
- export type { DdlExpressionLike, IndexKeySpec, MatchingColumnArityInput, NonEmptyColumnInput, NormalizeColumns, ReferentialAction } from "./table-options.js"
346
+ export type { DdlExpressionLike, IndexKeySpec, LiteralStringInput, MatchingColumnArityInput, NonEmptyColumnInput, NonEmptyStringInput, NormalizeColumns, ReferentialAction } from "./table-options.js"
347
+ export type { NonEmptyFieldMap }
348
+ export type NonEmptySchemaNameInput<Value extends string | undefined> =
349
+ Value extends string ? NonEmptyStringInput<Value> : Value
188
350
 
189
351
  export type TableDefinition<
190
352
  Name extends string,
191
353
  Fields extends TableFieldMap,
192
354
  PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>,
193
355
  Kind extends TableKind = "schema",
194
- SchemaName extends string | undefined = DefaultSchemaName
195
- > = Pipeable & {
356
+ SchemaName extends string | undefined = DefaultSchemaName,
357
+ ConflictArbiters extends ConflictArbiter = InlineConflictArbiters<Fields>
358
+ > = Pipeable & TableOptionPipe<Name, Fields, PrimaryKeyColumns, Kind, SchemaName, ConflictArbiters> & {
196
359
  readonly name: Name
197
360
  readonly columns: BoundColumns<Name, Fields>
198
- readonly schemas: TableSchemas<Name, Fields, PrimaryKeyColumns>
199
- readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, Kind, SchemaName>
361
+ readonly schemas: TableSchemas<Name, Fields, PrimaryKeyColumns> & AnyTableSchemas
362
+ readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, Kind, SchemaName, ConflictArbiters>
200
363
  readonly [Plan.TypeId]: Plan.State<
201
364
  BoundColumns<Name, Fields>,
202
365
  never,
@@ -222,11 +385,12 @@ export type TableClassStatic<
222
385
  Name extends string,
223
386
  Fields extends TableFieldMap,
224
387
  PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>,
225
- SchemaName extends string | undefined = DefaultSchemaName
388
+ SchemaName extends string | undefined = DefaultSchemaName,
389
+ ConflictArbiters extends ConflictArbiter = InlineConflictArbiters<Fields>
226
390
  > = (abstract new (...args: any[]) => any) & Pipeable & {
227
391
  readonly columns: BoundColumns<Name, Fields>
228
392
  readonly schemas: TableSchemas<Name, Fields, PrimaryKeyColumns>
229
- readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>
393
+ readonly [TypeId]: TableState<Name, Fields, PrimaryKeyColumns, "schema", SchemaName, ConflictArbiters>
230
394
  readonly [Plan.TypeId]: Plan.State<
231
395
  BoundColumns<Name, Fields>,
232
396
  never,
@@ -245,32 +409,38 @@ export type TableClassStatic<
245
409
  >
246
410
 
247
411
  /** Minimal structural table-like contract used across helper APIs. */
248
- export type AnyTable = TableDefinition<any, any, any, any, any> | TableClassStatic<any, any, any, any>
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
412
+ export type AnyTable<Dialect extends string = string> = {
413
+ readonly [TypeId]: TableState<string, TableFieldMap, string, TableKind, string | undefined, ConflictArbiter>
414
+ readonly [OptionsSymbol]: readonly TableOptionSpec[]
415
+ } & Plan.RowSet<any, any, Record<string, Plan.AnySource>, Dialect>
255
416
 
256
- type ColumnNamesOfAnyTable<Table extends AnyTable> = Extract<keyof FieldsOfAnyTable<Table>, string>
417
+ type CheckPredicateTable = any
418
+ type CheckPredicate = (table: CheckPredicateTable) => DdlExpressionLike
257
419
 
258
420
  /** Public table-option builder type used by `Table.index`, `Table.primaryKey`, and friends. */
259
421
  export type TableOption<
260
- Spec extends TableOptionSpec = TableOptionSpec
261
- > = {
262
- <
263
- Name extends string,
264
- Fields extends TableFieldMap,
265
- PrimaryKeyColumns extends keyof Fields & string
266
- >(
267
- table: OptionInputTable<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", any>, Spec>
268
- ): ApplyOption<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", any>, Spec>
422
+ Spec extends TableOptionSpec = TableOptionSpec,
423
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
424
+ > = Pipeable & {
425
+ readonly pipe: Pipeable["pipe"]
269
426
  readonly option: Spec
270
- }
427
+ readonly [ResolveOptionSymbol]?: (table: TableDefinition<any, any, any, "schema", any>) => Spec
428
+ } & (Spec extends { readonly kind: "primaryKey" }
429
+ ? {
430
+ <Table extends TableContext>(
431
+ table: Table,
432
+ ...validation: OptionValidationArgs<Table, Spec>
433
+ ): ApplyOption<Table, Spec>
434
+ }
435
+ : {
436
+ <Table extends TableContext>(
437
+ table: Table,
438
+ ...validation: OptionValidationArgs<Table, Spec>
439
+ ): ApplyOption<Table, Spec>
440
+ })
271
441
 
272
442
  const TableProto = {
273
- pipe(this: unknown) {
443
+ pipe(this: Pipeable) {
274
444
  return pipeArguments(this, arguments)
275
445
  }
276
446
  }
@@ -279,7 +449,7 @@ const attachPipe = <Value extends object>(value: Value): Value => {
279
449
  Object.defineProperty(value, "pipe", {
280
450
  configurable: true,
281
451
  writable: true,
282
- value: function(this: unknown) {
452
+ value: function(this: Value) {
283
453
  return pipeArguments(value, arguments)
284
454
  }
285
455
  })
@@ -292,11 +462,44 @@ type BuildArtifacts<
292
462
  PrimaryKeyColumns extends keyof Fields & string
293
463
  > = {
294
464
  readonly columns: BoundColumns<Name, Fields>
295
- readonly schemas: TableSchemas<Name, Fields, PrimaryKeyColumns>
296
465
  readonly normalizedOptions: readonly TableOptionSpec[]
297
466
  readonly primaryKey: readonly PrimaryKeyColumns[]
467
+ readonly conflictArbiters: readonly ConflictArbiter[]
298
468
  }
299
469
 
470
+ const conflictArbitersFromOptions = (
471
+ options: readonly TableOptionSpec[]
472
+ ): readonly ConflictArbiter[] =>
473
+ options.flatMap((option): readonly ConflictArbiter[] => {
474
+ if (typeof option !== "object" || option === null) {
475
+ return []
476
+ }
477
+ if (!("columns" in option) || !Array.isArray(option.columns) || option.columns.length === 0) {
478
+ return []
479
+ }
480
+ switch (option.kind) {
481
+ case "primaryKey":
482
+ case "unique":
483
+ return [{
484
+ columns: option.columns as ColumnList,
485
+ scope: "unconditional",
486
+ name: option.name,
487
+ constraint: true
488
+ }]
489
+ case "index":
490
+ return option.unique === true
491
+ ? [{
492
+ columns: option.columns as ColumnList,
493
+ scope: option.predicate === undefined ? "unconditional" : "partial",
494
+ name: option.name,
495
+ constraint: false
496
+ }]
497
+ : []
498
+ default:
499
+ return []
500
+ }
501
+ })
502
+
300
503
  const buildArtifacts = <
301
504
  Name extends string,
302
505
  Fields extends TableFieldMap,
@@ -305,22 +508,159 @@ const buildArtifacts = <
305
508
  name: Name,
306
509
  fields: Fields,
307
510
  declaredOptions: readonly TableOptionSpec[],
308
- schemaName: SchemaName
511
+ schemaName: SchemaName,
512
+ casing?: Casing.Options
309
513
  ): BuildArtifacts<Name, Fields, keyof Fields & string> => {
310
514
  const normalizedOptions = [...collectInlineOptions(fields), ...declaredOptions]
311
515
  validateFieldDialects(name, fields)
312
516
  validateOptions(name, fields, declaredOptions)
313
517
  const primaryKey = resolvePrimaryKeyColumns(fields, declaredOptions) as readonly (keyof Fields & string)[]
518
+ const conflictArbiters = conflictArbitersFromOptions(normalizedOptions)
314
519
  const columns = Object.fromEntries(
315
- Object.entries(fields).map(([key, column]) => [key, bindColumn(name, key, column, name, schemaName)])
520
+ Object.entries(fields).map(([key, column]) => [key, bindColumn(name, key, column, name, schemaName, casing)])
316
521
  ) as BoundColumns<Name, Fields>
317
- const schemas = deriveSchemas(name, fields, primaryKey)
318
522
  return {
319
523
  columns,
320
- schemas,
321
524
  normalizedOptions,
322
- primaryKey
525
+ primaryKey,
526
+ conflictArbiters
527
+ }
528
+ }
529
+
530
+ const getSchemaCache = <
531
+ Name extends string,
532
+ Fields extends TableFieldMap,
533
+ PrimaryKeyColumns extends keyof Fields & string
534
+ >(
535
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
536
+ ): TableSchemaCache<Name, Fields, PrimaryKeyColumns> => {
537
+ const target = table as {
538
+ [SchemaCacheSymbol]?: TableSchemaCache<Name, Fields, PrimaryKeyColumns>
539
+ }
540
+ if (target[SchemaCacheSymbol] !== undefined) {
541
+ return target[SchemaCacheSymbol]
542
+ }
543
+ const cache: TableSchemaCache<Name, Fields, PrimaryKeyColumns> = {}
544
+ Object.defineProperty(table, SchemaCacheSymbol, {
545
+ configurable: true,
546
+ value: cache
547
+ })
548
+ return cache
549
+ }
550
+
551
+ const deriveTableSchema = <
552
+ Variant extends TableSchemaVariant,
553
+ Name extends string,
554
+ Fields extends TableFieldMap,
555
+ PrimaryKeyColumns extends keyof Fields & string
556
+ >(
557
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>,
558
+ variant: Variant
559
+ ): TableSchemas<Name, Fields, PrimaryKeyColumns>[Variant] => {
560
+ const state = table[TypeId]
561
+ switch (variant) {
562
+ case "select":
563
+ return deriveSelectSchema(state.name, state.fields, state.primaryKey) as TableSchemas<Name, Fields, PrimaryKeyColumns>[Variant]
564
+ case "insert":
565
+ return deriveInsertSchema(state.name, state.fields, state.primaryKey) as TableSchemas<Name, Fields, PrimaryKeyColumns>[Variant]
566
+ case "update":
567
+ return deriveUpdateSchema(state.name, state.fields, state.primaryKey) as TableSchemas<Name, Fields, PrimaryKeyColumns>[Variant]
568
+ }
569
+ }
570
+
571
+ const schemaFor = <
572
+ Variant extends TableSchemaVariant,
573
+ Name extends string,
574
+ Fields extends TableFieldMap,
575
+ PrimaryKeyColumns extends keyof Fields & string
576
+ >(
577
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>,
578
+ variant: Variant
579
+ ): TableSchemas<Name, Fields, PrimaryKeyColumns>[Variant] => {
580
+ const cache = getSchemaCache(table)
581
+ const cached = cache[variant]
582
+ if (cached !== undefined) {
583
+ return cached as TableSchemas<Name, Fields, PrimaryKeyColumns>[Variant]
584
+ }
585
+ const schema = deriveTableSchema(table, variant)
586
+ cache[variant] = schema as any
587
+ return schema
588
+ }
589
+
590
+ export function selectSchema<
591
+ Name extends string,
592
+ Fields extends TableFieldMap,
593
+ PrimaryKeyColumns extends keyof Fields & string
594
+ >(
595
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
596
+ ): Schema.ConstraintDecoder<SelectRow<Name, Fields>, never> {
597
+ return schemaFor(table, "select") as Schema.ConstraintDecoder<SelectRow<Name, Fields>, never>
598
+ }
599
+
600
+ export function insertSchema<
601
+ Name extends string,
602
+ Fields extends TableFieldMap,
603
+ PrimaryKeyColumns extends keyof Fields & string
604
+ >(
605
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
606
+ ): Schema.ConstraintDecoder<InsertRow<Name, Fields>, never> {
607
+ return schemaFor(table, "insert") as Schema.ConstraintDecoder<InsertRow<Name, Fields>, never>
608
+ }
609
+
610
+ export function updateSchema<
611
+ Name extends string,
612
+ Fields extends TableFieldMap,
613
+ PrimaryKeyColumns extends keyof Fields & string
614
+ >(
615
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
616
+ ): Schema.ConstraintDecoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never> {
617
+ return schemaFor(table, "update") as Schema.ConstraintDecoder<UpdateRow<Name, Fields, PrimaryKeyColumns>, never>
618
+ }
619
+
620
+ const schemasFor = <
621
+ Name extends string,
622
+ Fields extends TableFieldMap,
623
+ PrimaryKeyColumns extends keyof Fields & string
624
+ >(
625
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any> | TableClassStatic<Name, Fields, PrimaryKeyColumns, any>
626
+ ): TableSchemas<Name, Fields, PrimaryKeyColumns> => {
627
+ const cache = getSchemaCache(table)
628
+ if (cache.schemas !== undefined) {
629
+ return cache.schemas
323
630
  }
631
+ const schemas = {} as TableSchemas<Name, Fields, PrimaryKeyColumns>
632
+ Object.defineProperties(schemas, {
633
+ select: {
634
+ enumerable: true,
635
+ get: () => selectSchema(table)
636
+ },
637
+ insert: {
638
+ enumerable: true,
639
+ get: () => insertSchema(table)
640
+ },
641
+ update: {
642
+ enumerable: true,
643
+ get: () => updateSchema(table)
644
+ }
645
+ })
646
+ cache.schemas = schemas
647
+ return schemas
648
+ }
649
+
650
+ const defineSchemasGetter = <
651
+ Name extends string,
652
+ Fields extends TableFieldMap,
653
+ PrimaryKeyColumns extends keyof Fields & string
654
+ >(
655
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, any>
656
+ ): void => {
657
+ Object.defineProperty(table, "schemas", {
658
+ configurable: true,
659
+ enumerable: true,
660
+ get() {
661
+ return schemasFor(table)
662
+ }
663
+ })
324
664
  }
325
665
 
326
666
  const makeTable = <
@@ -328,7 +668,8 @@ const makeTable = <
328
668
  Fields extends TableFieldMap,
329
669
  PrimaryKeyColumns extends keyof Fields & string,
330
670
  Kind extends TableKind = "schema",
331
- SchemaName extends string | undefined = DefaultSchemaName
671
+ SchemaName extends string | undefined = DefaultSchemaName,
672
+ ConflictArbiters extends ConflictArbiter = InlineConflictArbiters<Fields>
332
673
  >(
333
674
  name: Name,
334
675
  fields: Fields,
@@ -336,24 +677,27 @@ const makeTable = <
336
677
  baseName: string = name,
337
678
  kind: Kind = "schema" as Kind,
338
679
  schemaName?: SchemaName,
339
- schemaMode: "default" | "explicit" = "default"
340
- ): TableDefinition<Name, Fields, PrimaryKeyColumns, Kind, SchemaName> => {
680
+ schemaMode: "default" | "explicit" = "default",
681
+ casing?: Casing.Options
682
+ ): TableDefinition<Name, Fields, PrimaryKeyColumns, Kind, SchemaName, ConflictArbiters> => {
341
683
  const resolvedSchemaName = schemaMode === "explicit"
342
684
  ? schemaName
343
685
  : ("public" as SchemaName)
344
- const artifacts = buildArtifacts(name, fields, declaredOptions, resolvedSchemaName)
686
+ const artifacts = buildArtifacts(name, fields, declaredOptions, resolvedSchemaName, casing)
345
687
  const dialect = resolveFieldDialect(fields)
346
688
  const table = attachPipe(Object.create(TableProto))
347
689
  table.name = name
348
690
  table.columns = artifacts.columns
349
- table.schemas = artifacts.schemas
691
+ defineSchemasGetter(table)
350
692
  table[TypeId] = {
351
693
  name,
352
694
  baseName,
353
695
  schemaName: resolvedSchemaName,
354
696
  fields,
355
697
  primaryKey: artifacts.primaryKey,
356
- kind
698
+ conflictArbiters: artifacts.conflictArbiters as readonly ConflictArbiters[],
699
+ kind,
700
+ casing
357
701
  }
358
702
  table[Plan.TypeId] = {
359
703
  selection: artifacts.columns,
@@ -400,23 +744,17 @@ const applyDeclaredOptions = <
400
744
  ) as unknown as Table
401
745
  }
402
746
 
403
- const validateClassOptions = (declaredOptions: readonly TableOptionSpec[]): void => {
404
- for (const option of declaredOptions) {
405
- if (option.kind === "primaryKey") {
406
- throw new Error("Table.Class does not support table-level primary keys; declare primary keys inline on columns")
407
- }
408
- }
409
- }
410
-
411
747
  const resolveFieldDialect = (fields: TableFieldMap): string => {
412
748
  const dialects = [...new Set(Object.values(fields).map((field) => field.metadata.dbType.dialect))]
413
749
  if (dialects.length === 0) {
414
750
  throw new Error("Cannot infer table dialect from an empty field set")
415
751
  }
416
- if (dialects.length > 1) {
752
+ const concreteDialects = dialects.filter((dialect) => dialect !== "standard")
753
+ const uniqueConcreteDialects = [...new Set(concreteDialects)]
754
+ if (uniqueConcreteDialects.length > 1) {
417
755
  throw new Error(`Mixed table dialects are not supported: ${dialects.join(", ")}`)
418
756
  }
419
- return dialects[0]!
757
+ return uniqueConcreteDialects[0] ?? "standard"
420
758
  }
421
759
 
422
760
  const validateFieldDialects = (tableName: string, fields: TableFieldMap): void => {
@@ -442,7 +780,6 @@ const ensureClassArtifacts = <
442
780
  }
443
781
  const state = self[TypeId]
444
782
  const classOptions = self[options]
445
- validateClassOptions(extractDeclaredOptions(classOptions))
446
783
  const table = applyDeclaredOptions(
447
784
  makeTable(
448
785
  state.name,
@@ -451,15 +788,16 @@ const ensureClassArtifacts = <
451
788
  state.name,
452
789
  "schema",
453
790
  state.schemaName,
454
- state.schemaName === undefined || state.schemaName === "public" ? "default" : "explicit"
791
+ state.schemaName === undefined || state.schemaName === "public" ? "default" : "explicit",
792
+ state.casing
455
793
  ) as TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", typeof state.schemaName>,
456
794
  classOptions
457
795
  )
458
796
  const artifacts = {
459
797
  columns: table.columns,
460
- schemas: table.schemas,
461
798
  normalizedOptions: table[OptionsSymbol],
462
- primaryKey: table[TypeId].primaryKey as readonly PrimaryKeyColumns[]
799
+ primaryKey: table[TypeId].primaryKey as readonly PrimaryKeyColumns[],
800
+ conflictArbiters: table[TypeId].conflictArbiters
463
801
  } satisfies BuildArtifacts<Name, Fields, PrimaryKeyColumns>
464
802
  Object.defineProperty(self, CacheSymbol, {
465
803
  configurable: true,
@@ -476,9 +814,6 @@ const appendOption = <
476
814
  option: Spec
477
815
  ): ApplyOption<Table, Spec> => {
478
816
  const state = table[TypeId]
479
- if (state.kind !== "schema") {
480
- throw new Error("Table options can only be applied to schema tables, not aliased query sources")
481
- }
482
817
  return makeTable(
483
818
  state.name,
484
819
  state.fields,
@@ -486,49 +821,113 @@ const appendOption = <
486
821
  state.baseName,
487
822
  state.kind,
488
823
  state.schemaName,
489
- "explicit"
824
+ "explicit",
825
+ state.casing
490
826
  ) as unknown as ApplyOption<Table, Spec>
491
827
  }
492
828
 
493
- const makeOption = <Spec extends TableOptionSpec>(option: Spec): TableOption<Spec> => {
494
- const builder = ((table: TableDefinition<any, any, any, "schema", any>) =>
495
- appendOption(table, option)) as unknown as TableOption<Spec>
496
- ;(builder as { option: Spec }).option = option
497
- return builder
829
+ const makeOption = <
830
+ Spec extends TableOptionSpec,
831
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
832
+ >(option: Spec): TableOption<Spec, TableContext> => {
833
+ return attachPipe(Object.assign(
834
+ <Table extends TableDefinition<any, any, any, "schema", any>>(
835
+ table: Table,
836
+ ..._validation: OptionValidationArgs<Table, Spec>
837
+ ): ApplyTableOption<Table, Spec> =>
838
+ appendOption(table, option) as unknown as ApplyTableOption<Table, Spec>,
839
+ { option }
840
+ )) as unknown as TableOption<Spec, TableContext>
498
841
  }
499
842
 
500
- const makeResolvedOption = <Spec extends TableOptionSpec>(
843
+ const makeResolvedOption = <
844
+ Spec extends TableOptionSpec,
845
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
846
+ >(
501
847
  option: Spec,
502
848
  resolve: (table: TableDefinition<any, any, any, "schema", any>) => Spec
503
- ): TableOption<Spec> => {
504
- const builder = ((table: TableDefinition<any, any, any, "schema", any>) =>
505
- appendOption(table, resolve(table))) as unknown as TableOption<Spec>
506
- ;(builder as { option: Spec }).option = option
507
- return builder
849
+ ): TableOption<Spec, TableContext> => {
850
+ return attachPipe(Object.assign(
851
+ <Table extends TableDefinition<any, any, any, "schema", any>>(
852
+ table: Table,
853
+ ..._validation: OptionValidationArgs<Table, Spec>
854
+ ): ApplyTableOption<Table, Spec> =>
855
+ appendOption(
856
+ table,
857
+ resolve(table)
858
+ ) as unknown as ApplyTableOption<Table, Spec>,
859
+ {
860
+ option,
861
+ [ResolveOptionSymbol]: resolve
862
+ }
863
+ )) as unknown as TableOption<Spec, TableContext>
508
864
  }
509
865
 
510
866
  export const option = <Spec extends TableOptionSpec>(spec: Spec): TableOption<Spec> =>
511
867
  makeOption(spec)
512
868
 
513
- export const optionFromTable = <Spec extends TableOptionSpec>(
869
+ export const optionFromTable = <
870
+ Spec extends TableOptionSpec,
871
+ TableContext extends SchemaTableDefinition = SchemaTableDefinition
872
+ >(
514
873
  spec: Spec,
515
874
  resolve: (table: TableDefinition<any, any, any, "schema", any>) => Spec
516
- ): TableOption<Spec> =>
875
+ ): TableOption<Spec, TableContext> =>
517
876
  makeResolvedOption(spec, resolve)
518
877
 
878
+ export const resolveOption = <
879
+ Spec extends TableOptionSpec,
880
+ TableContext extends SchemaTableDefinition
881
+ >(
882
+ option: TableOption<Spec, TableContext>,
883
+ table: TableContext
884
+ ): Spec => {
885
+ const resolve = option[ResolveOptionSymbol]
886
+ return resolve === undefined ? option.option : resolve(table)
887
+ }
888
+
889
+ export const mapOption = <
890
+ Spec extends TableOptionSpec,
891
+ Next extends TableOptionSpec,
892
+ TableContext extends SchemaTableDefinition
893
+ >(
894
+ option: TableOption<Spec, TableContext>,
895
+ map: (spec: Spec) => Next
896
+ ): TableOption<Next, TableContext> => {
897
+ const resolve = option[ResolveOptionSymbol]
898
+ return resolve === undefined
899
+ ? makeOption<Next, TableContext>(map(option.option))
900
+ : makeResolvedOption<Next, TableContext>(
901
+ map(option.option),
902
+ (table) => map(resolve(table))
903
+ )
904
+ }
905
+
519
906
  /** Creates a table definition from a name and field map. */
907
+ export function make<
908
+ Name extends string,
909
+ Fields extends TableFieldMap
910
+ >(
911
+ name: NonEmptyStringInput<Name>,
912
+ fields: Fields & NonEmptyFieldMap<Fields>
913
+ ): TableDefinition<Name, Fields, InlinePrimaryKeyKeys<Fields>, "schema", DefaultSchemaName>
520
914
  export function make<
521
915
  Name extends string,
522
916
  Fields extends TableFieldMap,
523
- SchemaName extends string | undefined = DefaultSchemaName
917
+ const SchemaName extends string
524
918
  >(
525
- name: Name,
526
- fields: Fields,
527
- schemaName?: SchemaName
528
- ): TableDefinition<Name, Fields, InlinePrimaryKeyKeys<Fields>, "schema", SchemaName> {
919
+ name: NonEmptyStringInput<Name>,
920
+ fields: Fields & NonEmptyFieldMap<Fields>,
921
+ schemaName: NonEmptySchemaNameInput<SchemaName>
922
+ ): TableDefinition<Name, Fields, InlinePrimaryKeyKeys<Fields>, "schema", SchemaName>
923
+ export function make(
924
+ name: string,
925
+ fields: TableFieldMap,
926
+ schemaName?: string
927
+ ): any {
529
928
  const resolvedSchemaName = arguments.length >= 3
530
929
  ? schemaName
531
- : ("public" as SchemaName)
930
+ : "public"
532
931
  return makeTable(
533
932
  name,
534
933
  fields,
@@ -540,38 +939,58 @@ export function make<
540
939
  )
541
940
  }
542
941
 
543
- /**
544
- * Creates a namespace-scoped builder for a concrete SQL schema/database.
545
- */
546
- export const schema = <SchemaName extends string>(
547
- schemaName: SchemaName
548
- ): TableSchemaNamespace<SchemaName> => {
549
- const table = <
550
- Name extends string,
551
- Fields extends TableFieldMap,
552
- const Options extends DeclaredTableOptions,
553
- PrimaryKeyColumns extends keyof Fields & string = InlinePrimaryKeyKeys<Fields>
554
- >(
555
- name: Name,
556
- fields: Fields,
557
- ...options: Options & ValidateDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
558
- ): ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options> =>
559
- applyDeclaredOptions(
560
- makeTable(
561
- name,
562
- fields,
563
- [],
564
- name,
565
- "schema",
566
- schemaName,
567
- "explicit"
568
- ) as TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>,
569
- options as unknown as Options
570
- ) as ApplyDeclaredOptions<TableDefinition<Name, Fields, PrimaryKeyColumns, "schema", SchemaName>, Options>
571
- return {
942
+ export const withCasing = <
943
+ Table extends TableDefinition<any, any, any, any, any>
944
+ >(
945
+ table: Table,
946
+ casing: Casing.Options
947
+ ): Table => {
948
+ const state = table[TypeId]
949
+ return makeTable(
950
+ state.name,
951
+ state.fields,
952
+ table[DeclaredOptionsSymbol],
953
+ state.baseName,
954
+ state.kind,
955
+ state.schemaName,
956
+ "explicit",
957
+ Casing.merge(state.casing, casing)
958
+ ) as Table
959
+ }
960
+
961
+ export const withSchema = <
962
+ Table extends TableDefinition<any, any, any, any, any>,
963
+ SchemaName extends string
964
+ >(
965
+ table: Table,
966
+ schemaName: SchemaName,
967
+ schemaCasing?: Casing.Options
968
+ ): TableDefinition<
969
+ Table[typeof TypeId]["name"],
970
+ Table[typeof TypeId]["fields"],
971
+ Table[typeof TypeId]["primaryKey"][number],
972
+ Table[typeof TypeId]["kind"],
973
+ SchemaName,
974
+ Table[typeof TypeId]["conflictArbiters"][number]
975
+ > => {
976
+ const state = table[TypeId]
977
+ return makeTable(
978
+ state.name,
979
+ state.fields,
980
+ table[DeclaredOptionsSymbol],
981
+ state.baseName,
982
+ state.kind,
572
983
  schemaName,
573
- table
574
- } as unknown as TableSchemaNamespace<SchemaName>
984
+ "explicit",
985
+ Casing.merge(schemaCasing, state.casing)
986
+ ) as TableDefinition<
987
+ Table[typeof TypeId]["name"],
988
+ Table[typeof TypeId]["fields"],
989
+ Table[typeof TypeId]["primaryKey"][number],
990
+ Table[typeof TypeId]["kind"],
991
+ SchemaName,
992
+ Table[typeof TypeId]["conflictArbiters"][number]
993
+ >
575
994
  }
576
995
 
577
996
  /**
@@ -586,39 +1005,43 @@ export const alias = <
586
1005
  Fields extends TableFieldMap,
587
1006
  PrimaryKeyColumns extends keyof Fields & string,
588
1007
  SchemaName extends string,
1008
+ ConflictArbiters extends ConflictArbiter,
589
1009
  AliasName extends string
590
1010
  >(
591
- table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, SchemaName> | TableClassStatic<Name, Fields, PrimaryKeyColumns, SchemaName>,
592
- aliasName: AliasName
1011
+ table: TableDefinition<Name, Fields, PrimaryKeyColumns, any, SchemaName, ConflictArbiters> | TableClassStatic<Name, Fields, PrimaryKeyColumns, SchemaName, ConflictArbiters>,
1012
+ aliasName: LiteralStringInput<AliasName>
593
1013
  ): TableDefinition<
594
1014
  AliasName,
595
1015
  Fields,
596
1016
  PrimaryKeyColumns,
597
1017
  "alias",
598
- SchemaName
1018
+ SchemaName,
1019
+ ConflictArbiters
599
1020
  > => {
600
1021
  const state = table[TypeId]
601
1022
  const columns = Object.fromEntries(
602
- Object.entries(state.fields).map(([key, column]) => [key, bindColumn(aliasName, key, column as AnyColumnDefinition, state.baseName, state.schemaName)])
1023
+ Object.entries(state.fields).map(([key, column]) => [key, bindColumn(aliasName as AliasName, key, column as AnyColumnDefinition, state.baseName, state.schemaName, state.casing)])
603
1024
  ) as BoundColumns<AliasName, Fields>
604
1025
  const aliased = attachPipe(Object.create(TableProto))
605
- aliased.name = aliasName
1026
+ aliased.name = aliasName as AliasName
606
1027
  aliased.columns = columns
607
- aliased.schemas = deriveSchemas(aliasName, state.fields, state.primaryKey)
1028
+ defineSchemasGetter(aliased)
608
1029
  aliased[TypeId] = {
609
- name: aliasName,
1030
+ name: aliasName as AliasName,
610
1031
  baseName: state.baseName,
611
1032
  schemaName: state.schemaName,
612
1033
  fields: state.fields,
613
1034
  primaryKey: state.primaryKey,
614
- kind: "alias"
1035
+ conflictArbiters: state.conflictArbiters,
1036
+ kind: "alias",
1037
+ casing: state.casing
615
1038
  }
616
1039
  aliased[Plan.TypeId] = {
617
1040
  selection: columns,
618
1041
  required: undefined as never,
619
1042
  available: {
620
1043
  [aliasName]: {
621
- name: aliasName,
1044
+ name: aliasName as AliasName,
622
1045
  mode: "required",
623
1046
  baseName: state.baseName
624
1047
  }
@@ -642,19 +1065,38 @@ export const alias = <
642
1065
  * The returned base class can be extended and configured with
643
1066
  * `static readonly [Table.options]`.
644
1067
  */
1068
+ export function Class<Self = never>(
1069
+ name: "",
1070
+ schemaName?: string | undefined
1071
+ ): never
1072
+ export function Class<Self = never>(
1073
+ name: string,
1074
+ schemaName: ""
1075
+ ): never
1076
+ export function Class<
1077
+ Self = never,
1078
+ const SchemaName extends string | undefined = DefaultSchemaName,
1079
+ const Name extends string = string
1080
+ >(
1081
+ name: NonEmptyStringInput<Name>,
1082
+ schemaName?: NonEmptySchemaNameInput<SchemaName>
1083
+ ): <Fields extends TableFieldMap>(fields: Fields & NonEmptyFieldMap<Fields>) => [Self] extends [never]
1084
+ ? MissingSelfGeneric
1085
+ : TableClassStatic<Name, Fields, InlinePrimaryKeyKeys<Fields>, SchemaName>
645
1086
  export function Class<
646
1087
  Self = never,
647
- SchemaName extends string | undefined = DefaultSchemaName
1088
+ SchemaName extends string | undefined = DefaultSchemaName,
1089
+ Name extends string = string
648
1090
  >(
649
1091
  name: string,
650
1092
  schemaName?: SchemaName
651
- ) {
1093
+ ): any {
652
1094
  const resolvedSchemaName = arguments.length >= 2
653
1095
  ? schemaName
654
1096
  : ("public" as SchemaName)
655
- return <Fields extends TableFieldMap>(fields: Fields): [Self] extends [never]
1097
+ return <Fields extends TableFieldMap>(fields: Fields & NonEmptyFieldMap<Fields>): [Self] extends [never]
656
1098
  ? MissingSelfGeneric
657
- : TableClassStatic<typeof name, Fields, InlinePrimaryKeyKeys<Fields>, SchemaName> => {
1099
+ : TableClassStatic<Name, Fields, InlinePrimaryKeyKeys<Fields>, SchemaName> => {
658
1100
  abstract class TableClassBase {
659
1101
  static readonly tableName = name
660
1102
 
@@ -663,18 +1105,19 @@ export function Class<
663
1105
  }
664
1106
 
665
1107
  static get schemas() {
666
- return ensureClassArtifacts(this as any).schemas
1108
+ return schemasFor(this as any)
667
1109
  }
668
1110
 
669
1111
  static get [TypeId]() {
670
- const declaredOptions = extractDeclaredOptions((this as unknown as TableClassStatic<typeof name, Fields>)[options])
671
- validateClassOptions(declaredOptions)
1112
+ const declaredOptions = extractDeclaredOptions((this as unknown as TableClassStatic<Name, Fields>)[options])
1113
+ const normalizedOptions = [...collectInlineOptions(fields), ...declaredOptions]
672
1114
  return {
673
1115
  name,
674
1116
  baseName: name,
675
1117
  schemaName: resolvedSchemaName,
676
1118
  fields,
677
1119
  primaryKey: resolvePrimaryKeyColumns(fields, collectInlineOptions(fields)),
1120
+ conflictArbiters: conflictArbitersFromOptions(normalizedOptions),
678
1121
  kind: "schema"
679
1122
  }
680
1123
  }
@@ -699,7 +1142,7 @@ export function Class<
699
1142
  return ensureClassArtifacts(this as any).normalizedOptions
700
1143
  }
701
1144
 
702
- static pipe(this: unknown) {
1145
+ static pipe(this: Pipeable) {
703
1146
  return pipeArguments(this, arguments)
704
1147
  }
705
1148
  }
@@ -718,97 +1161,236 @@ export function Class<
718
1161
  }
719
1162
  }
720
1163
 
1164
+ const selectionArray = (selection: AnyColumnSelection): readonly AnyBoundColumn[] =>
1165
+ (Array.isArray(selection) ? selection : [selection]) as readonly AnyBoundColumn[]
1166
+
1167
+ export const selectedColumnList = <Selection extends AnyColumnSelection>(
1168
+ selection: Selection
1169
+ ): SelectedColumns<Selection> =>
1170
+ selectionArray(selection).map((column) => column[BoundColumnTypeId].columnName) as unknown as SelectedColumns<Selection>
1171
+
1172
+ const referenceFromSelection = <Selection extends AnyColumnSelection>(selection: Selection) => {
1173
+ const columns = selectionArray(selection)
1174
+ const first = columns[0]!
1175
+ const bound = first[BoundColumnTypeId]
1176
+ return {
1177
+ tableName: bound.baseTableName,
1178
+ schemaName: bound.schemaName,
1179
+ casing: bound.casing,
1180
+ columns: columns.map((column) => column[BoundColumnTypeId].columnName) as unknown as SelectedColumns<Selection>
1181
+ }
1182
+ }
1183
+
721
1184
  /** Declares a table-level primary key. */
722
- export const primaryKey = <
723
- const Columns extends string | readonly string[]
1185
+ export function primaryKey<
1186
+ Table extends SchemaTableDefinition,
1187
+ Selection extends TableColumnSelection<Table> = TableColumnSelection<Table>
1188
+ >(
1189
+ columns: ConcreteSelector<Table, Selection>
1190
+ ): TableOption<{
1191
+ readonly kind: "primaryKey"
1192
+ readonly columns: SelectedColumns<Selection>
1193
+ }, Table>
1194
+ export function primaryKey<
1195
+ Selection extends AnyColumnSelection
724
1196
  >(
725
- columns: Columns & NonEmptyColumnInput<Columns>
1197
+ columns: (table: LooseTableSelection) => Selection
1198
+ ): TableOption<{
1199
+ readonly kind: "primaryKey"
1200
+ readonly columns: SelectedColumns<Selection>
1201
+ }>
1202
+ export function primaryKey(
1203
+ columns: (table: any) => AnyColumnSelection
726
1204
  ): TableOption<{
727
1205
  readonly kind: "primaryKey"
728
- readonly columns: NormalizeColumns<Columns>
729
- }> => makeOption({
730
- kind: "primaryKey",
731
- columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
732
- })
1206
+ readonly columns: readonly [string, ...string[]]
1207
+ }> {
1208
+ return makeResolvedOption({
1209
+ kind: "primaryKey",
1210
+ columns: [] as unknown as readonly [string, ...string[]]
1211
+ }, (table) => ({
1212
+ kind: "primaryKey",
1213
+ columns: selectedColumnList(columns(table))
1214
+ }))
1215
+ }
733
1216
 
734
1217
  /** Declares a table-level unique constraint. */
735
- export const unique = <
736
- const Columns extends string | readonly string[]
1218
+ export function unique<
1219
+ Table extends SchemaTableDefinition,
1220
+ Selection extends TableColumnSelection<Table> = TableColumnSelection<Table>
1221
+ >(
1222
+ columns: ConcreteSelector<Table, Selection>
1223
+ ): TableOption<{
1224
+ readonly kind: "unique"
1225
+ readonly columns: SelectedColumns<Selection>
1226
+ }, Table>
1227
+ export function unique<
1228
+ Selection extends AnyColumnSelection
737
1229
  >(
738
- columns: Columns & NonEmptyColumnInput<Columns>
1230
+ columns: (table: LooseTableSelection) => Selection
739
1231
  ): TableOption<{
740
1232
  readonly kind: "unique"
741
- readonly columns: NormalizeColumns<Columns>
742
- }> => makeOption({
743
- kind: "unique",
744
- columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
745
- })
1233
+ readonly columns: SelectedColumns<Selection>
1234
+ }>
1235
+ export function unique(
1236
+ columns: (table: any) => AnyColumnSelection
1237
+ ): TableOption<{
1238
+ readonly kind: "unique"
1239
+ readonly columns: readonly [string, ...string[]]
1240
+ }> {
1241
+ return makeResolvedOption({
1242
+ kind: "unique",
1243
+ columns: [] as unknown as readonly [string, ...string[]]
1244
+ }, (table) => ({
1245
+ kind: "unique",
1246
+ columns: selectedColumnList(columns(table))
1247
+ }))
1248
+ }
746
1249
 
747
1250
  /** Declares a table-level index. */
748
- export const index = <
749
- const Columns extends string | readonly string[]
1251
+ export function index<
1252
+ Table extends SchemaTableDefinition,
1253
+ Selection extends TableColumnSelection<Table> = TableColumnSelection<Table>
750
1254
  >(
751
- columns: Columns & NonEmptyColumnInput<Columns>
1255
+ columns: ConcreteSelector<Table, Selection>
752
1256
  ): TableOption<{
753
1257
  readonly kind: "index"
754
- readonly columns: NormalizeColumns<Columns>
755
- }> => makeOption({
756
- kind: "index",
757
- columns: normalizeColumnList(columns) as NormalizeColumns<Columns>
758
- })
1258
+ readonly columns: SelectedColumns<Selection>
1259
+ }, Table>
1260
+ export function index<
1261
+ Selection extends AnyColumnSelection
1262
+ >(
1263
+ columns: (table: LooseTableSelection) => Selection
1264
+ ): TableOption<{
1265
+ readonly kind: "index"
1266
+ readonly columns: SelectedColumns<Selection>
1267
+ }>
1268
+ export function index(
1269
+ columns: (table: any) => AnyColumnSelection
1270
+ ): TableOption<{
1271
+ readonly kind: "index"
1272
+ readonly columns: readonly [string, ...string[]]
1273
+ }> {
1274
+ return makeResolvedOption({
1275
+ kind: "index",
1276
+ columns: [] as unknown as readonly [string, ...string[]]
1277
+ }, (table) => ({
1278
+ kind: "index",
1279
+ columns: selectedColumnList(columns(table))
1280
+ }))
1281
+ }
759
1282
 
760
1283
  /** Declares a table-level foreign key. */
761
- export const foreignKey = <
762
- const LocalColumns extends string | readonly string[],
763
- TargetTable extends AnyTable,
764
- const TargetColumns extends string | readonly string[]
765
- >(
766
- columns: LocalColumns & NonEmptyColumnInput<LocalColumns>,
767
- target: () => TargetTable,
768
- referencedColumns: TargetColumns & NonEmptyColumnInput<TargetColumns> & MatchingColumnArityInput<LocalColumns, TargetColumns>
1284
+ export function foreignKey<
1285
+ Table extends SchemaTableDefinition,
1286
+ LocalSelection extends TableColumnSelection<Table> = TableColumnSelection<Table>,
1287
+ TargetSelection extends AnyColumnSelection = AnyColumnSelection
1288
+ >(
1289
+ columns: ConcreteSelector<Table, LocalSelection>,
1290
+ target: () => TargetSelection & MatchingSelectionArityInput<LocalSelection, TargetSelection>
769
1291
  ): TableOption<{
770
1292
  readonly kind: "foreignKey"
771
- readonly columns: NormalizeColumns<LocalColumns>
1293
+ readonly columns: SelectedColumns<LocalSelection>
772
1294
  readonly references: () => {
773
1295
  readonly tableName: string
774
1296
  readonly schemaName?: string
775
- readonly columns: NormalizeColumns<TargetColumns>
776
- readonly knownColumns: readonly ColumnNamesOfAnyTable<TargetTable>[]
1297
+ readonly casing?: Casing.Options
1298
+ readonly columns: SelectedColumns<TargetSelection>
777
1299
  }
778
- }> => makeOption({
779
- kind: "foreignKey",
780
- columns: normalizeColumnList(columns) as NormalizeColumns<LocalColumns>,
781
- references: () => ({
782
- tableName: target()[TypeId].baseName,
783
- schemaName: target()[TypeId].schemaName,
784
- columns: normalizeColumnList(referencedColumns) as NormalizeColumns<TargetColumns>,
785
- knownColumns: Object.keys(target()[TypeId].fields) as unknown as readonly ColumnNamesOfAnyTable<TargetTable>[]
786
- })
787
- })
1300
+ }, Table>
1301
+ export function foreignKey<
1302
+ LocalSelection extends AnyColumnSelection,
1303
+ TargetSelection extends AnyColumnSelection
1304
+ >(
1305
+ columns: (table: LooseTableSelection) => LocalSelection,
1306
+ target: () => TargetSelection
1307
+ ): TableOption<{
1308
+ readonly kind: "foreignKey"
1309
+ readonly columns: SelectedColumns<LocalSelection>
1310
+ readonly references: () => {
1311
+ readonly tableName: string
1312
+ readonly schemaName?: string
1313
+ readonly casing?: Casing.Options
1314
+ readonly columns: SelectedColumns<TargetSelection>
1315
+ }
1316
+ }>
1317
+ export function foreignKey(
1318
+ columns: (table: any) => AnyColumnSelection,
1319
+ target: () => AnyColumnSelection
1320
+ ): TableOption<{
1321
+ readonly kind: "foreignKey"
1322
+ readonly columns: readonly [string, ...string[]]
1323
+ readonly references: () => {
1324
+ readonly tableName: string
1325
+ readonly schemaName?: string
1326
+ readonly casing?: Casing.Options
1327
+ readonly columns: readonly [string, ...string[]]
1328
+ }
1329
+ }> {
1330
+ return makeResolvedOption({
1331
+ kind: "foreignKey",
1332
+ columns: [] as unknown as readonly [string, ...string[]],
1333
+ references: () => referenceFromSelection(target())
1334
+ }, (table) => ({
1335
+ kind: "foreignKey",
1336
+ columns: selectedColumnList(columns(table)),
1337
+ references: () => referenceFromSelection(target())
1338
+ }))
1339
+ }
788
1340
 
789
1341
  /** Declares a check constraint expression. */
790
- export const check = <Name extends string>(
791
- name: Name,
1342
+ export function check<const Name extends string>(
1343
+ name: NonEmptyStringInput<Name>,
792
1344
  predicate: DdlExpressionLike
793
1345
  ): TableOption<{
794
1346
  readonly kind: "check"
795
1347
  readonly name: Name
796
1348
  readonly predicate: DdlExpressionLike
797
- }> => makeOption({
798
- kind: "check",
799
- name,
800
- predicate
801
- })
802
-
803
- /** Extracts the row type of a table's select schema. */
804
- export type SelectOf<Table extends { readonly schemas: { readonly select: Schema.Schema<any> } }> = Schema.Schema.Type<
805
- Table["schemas"]["select"]
806
- >
807
- /** Extracts the payload type of a table's insert schema. */
808
- export type InsertOf<Table extends { readonly schemas: { readonly insert: Schema.Schema<any> } }> = Schema.Schema.Type<
809
- Table["schemas"]["insert"]
810
- >
811
- /** Extracts the payload type of a table's update schema. */
812
- export type UpdateOf<Table extends { readonly schemas: { readonly update: Schema.Schema<any> } }> = Schema.Schema.Type<
813
- Table["schemas"]["update"]
814
- >
1349
+ }>
1350
+ export function check<const Name extends string>(
1351
+ name: NonEmptyStringInput<Name>,
1352
+ predicate: CheckPredicate
1353
+ ): TableOption<{
1354
+ readonly kind: "check"
1355
+ readonly name: Name
1356
+ readonly predicate: DdlExpressionLike
1357
+ }>
1358
+ export function check(
1359
+ name: string,
1360
+ predicate: DdlExpressionLike | CheckPredicate
1361
+ ): TableOption<{
1362
+ readonly kind: "check"
1363
+ readonly name: string
1364
+ readonly predicate: DdlExpressionLike
1365
+ }> {
1366
+ const spec = {
1367
+ kind: "check",
1368
+ name,
1369
+ predicate: predicate as DdlExpressionLike
1370
+ } as const
1371
+ return typeof predicate === "function"
1372
+ ? makeResolvedOption(spec, (table) => ({
1373
+ ...spec,
1374
+ predicate: predicate(table as CheckPredicateTable)
1375
+ }))
1376
+ : makeOption(spec)
1377
+ }
1378
+
1379
+ /** Extracts the row type produced by `selectSchema(table)`. */
1380
+ export type SelectOf<Table extends AnyTable> = Table[typeof TypeId] extends {
1381
+ readonly name: infer Name extends string
1382
+ readonly fields: infer Fields extends TableFieldMap
1383
+ } ? SelectRow<Name, Fields> : never
1384
+
1385
+ /** Extracts the payload type produced by `insertSchema(table)`. */
1386
+ export type InsertOf<Table extends AnyTable> = Table[typeof TypeId] extends {
1387
+ readonly name: infer Name extends string
1388
+ readonly fields: infer Fields extends TableFieldMap
1389
+ } ? InsertRow<Name, Fields> : never
1390
+
1391
+ /** Extracts the payload type produced by `updateSchema(table)`. */
1392
+ export type UpdateOf<Table extends AnyTable> = Table[typeof TypeId] extends {
1393
+ readonly name: infer Name extends string
1394
+ readonly fields: infer Fields extends TableFieldMap
1395
+ readonly primaryKey: readonly (infer PrimaryKeyColumns)[]
1396
+ } ? UpdateRow<Name, Fields, Extract<PrimaryKeyColumns, keyof Fields & string>> : never