effect-qb 0.13.0 → 0.14.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 (54) hide show
  1. package/README.md +6 -1431
  2. package/dist/mysql.js +1678 -355
  3. package/dist/postgres/metadata.js +2724 -0
  4. package/dist/postgres.js +7197 -5433
  5. package/package.json +8 -10
  6. package/src/internal/column-state.ts +84 -10
  7. package/src/internal/column.ts +556 -34
  8. package/src/internal/datatypes/define.ts +0 -30
  9. package/src/internal/executor.ts +45 -11
  10. package/src/internal/expression-ast.ts +4 -0
  11. package/src/internal/expression.ts +1 -1
  12. package/src/internal/implication-runtime.ts +171 -0
  13. package/src/internal/mysql-query.ts +7173 -0
  14. package/src/internal/mysql-renderer.ts +2 -2
  15. package/src/internal/plan.ts +14 -4
  16. package/src/internal/{query-factory.ts → postgres-query.ts} +619 -167
  17. package/src/internal/postgres-renderer.ts +2 -2
  18. package/src/internal/postgres-schema-model.ts +144 -0
  19. package/src/internal/predicate-analysis.ts +10 -0
  20. package/src/internal/predicate-context.ts +112 -36
  21. package/src/internal/predicate-formula.ts +31 -19
  22. package/src/internal/predicate-normalize.ts +177 -106
  23. package/src/internal/predicate-runtime.ts +676 -0
  24. package/src/internal/query.ts +455 -39
  25. package/src/internal/renderer.ts +2 -2
  26. package/src/internal/runtime-schema.ts +74 -20
  27. package/src/internal/schema-ddl.ts +55 -0
  28. package/src/internal/schema-derivation.ts +93 -21
  29. package/src/internal/schema-expression.ts +44 -0
  30. package/src/internal/sql-expression-renderer.ts +95 -31
  31. package/src/internal/table-options.ts +87 -7
  32. package/src/internal/table.ts +104 -41
  33. package/src/mysql/column.ts +1 -0
  34. package/src/mysql/datatypes/index.ts +17 -2
  35. package/src/mysql/function/core.ts +1 -0
  36. package/src/mysql/function/index.ts +1 -0
  37. package/src/mysql/private/query.ts +1 -13
  38. package/src/mysql/query.ts +5 -0
  39. package/src/postgres/cast.ts +31 -0
  40. package/src/postgres/column.ts +26 -0
  41. package/src/postgres/datatypes/index.ts +40 -5
  42. package/src/postgres/function/core.ts +12 -0
  43. package/src/postgres/function/index.ts +2 -1
  44. package/src/postgres/function/json.ts +499 -2
  45. package/src/postgres/metadata.ts +31 -0
  46. package/src/postgres/private/query.ts +1 -13
  47. package/src/postgres/query.ts +5 -2
  48. package/src/postgres/schema-expression.ts +16 -0
  49. package/src/postgres/schema-management.ts +204 -0
  50. package/src/postgres/schema.ts +35 -0
  51. package/src/postgres/table.ts +307 -41
  52. package/src/postgres/type.ts +4 -0
  53. package/src/postgres.ts +14 -0
  54. package/CHANGELOG.md +0 -134
@@ -1,22 +1,36 @@
1
+ import type * as Brand from "effect/Brand"
1
2
  import * as Schema from "effect/Schema"
2
3
 
3
4
  import * as Expression from "./expression.js"
5
+ import type { CanCastDbType } from "./datatypes/lookup.js"
4
6
  import {
7
+ BigIntStringSchema,
8
+ InstantStringSchema,
5
9
  LocalDateStringSchema,
6
10
  DecimalStringSchema,
7
11
  LocalDateTimeStringSchema,
12
+ LocalTimeStringSchema,
13
+ OffsetTimeStringSchema,
8
14
  type LocalDateString,
15
+ type LocalTimeString,
16
+ type OffsetTimeString,
17
+ type InstantString,
9
18
  type DecimalString,
10
- type LocalDateTimeString
19
+ type LocalDateTimeString,
20
+ type BigIntString
11
21
  } from "./runtime-value.js"
12
22
  import {
13
23
  type AnyBoundColumn,
14
24
  type AnyColumnDefinition,
15
25
  type BaseSelectType,
16
26
  type BoundColumn,
27
+ BoundColumnTypeId,
17
28
  ColumnTypeId,
29
+ type DdlExpression,
18
30
  type ColumnDefinition,
31
+ type ColumnUniqueOptions,
19
32
  type ColumnReference,
33
+ type ColumnIndexOptions,
20
34
  type HasDefault,
21
35
  type InsertType,
22
36
  type IsGenerated,
@@ -29,6 +43,8 @@ import {
29
43
  type UpdateType
30
44
  } from "./column-state.js"
31
45
 
46
+ type ReferentialAction = "noAction" | "restrict" | "cascade" | "setNull" | "setDefault"
47
+
32
48
  type CompatibleReference<
33
49
  Self extends AnyColumnDefinition,
34
50
  Target extends AnyBoundColumn
@@ -51,7 +67,7 @@ type NullableColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
51
67
  IsPrimaryKey<Column>,
52
68
  Column[typeof ColumnTypeId]["unique"],
53
69
  ReferencesOf<Column>
54
- >
70
+ > & PreserveBrand<Column>
55
71
 
56
72
  type PrimaryKeyColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
57
73
  SelectType<Column>,
@@ -64,7 +80,7 @@ type PrimaryKeyColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
64
80
  true,
65
81
  true,
66
82
  ReferencesOf<Column>
67
- >
83
+ > & PreserveBrand<Column>
68
84
 
69
85
  type UniqueColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
70
86
  SelectType<Column>,
@@ -77,7 +93,9 @@ type UniqueColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
77
93
  IsPrimaryKey<Column>,
78
94
  true,
79
95
  ReferencesOf<Column>
80
- >
96
+ > & PreserveBrand<Column>
97
+
98
+ type IndexedColumn<Column extends AnyColumnDefinition> = Column
81
99
 
82
100
  type HasDefaultColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
83
101
  SelectType<Column>,
@@ -90,7 +108,20 @@ type HasDefaultColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
90
108
  IsPrimaryKey<Column>,
91
109
  Column[typeof ColumnTypeId]["unique"],
92
110
  ReferencesOf<Column>
93
- >
111
+ > & PreserveBrand<Column>
112
+
113
+ type DdlTypedColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
114
+ SelectType<Column>,
115
+ InsertType<Column>,
116
+ UpdateType<Column>,
117
+ Column[typeof ColumnTypeId]["dbType"],
118
+ IsNullable<Column>,
119
+ HasDefault<Column>,
120
+ IsGenerated<Column>,
121
+ IsPrimaryKey<Column>,
122
+ Column[typeof ColumnTypeId]["unique"],
123
+ ReferencesOf<Column>
124
+ > & PreserveBrand<Column>
94
125
 
95
126
  type GeneratedColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
96
127
  SelectType<Column>,
@@ -103,12 +134,55 @@ type GeneratedColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
103
134
  IsPrimaryKey<Column>,
104
135
  Column[typeof ColumnTypeId]["unique"],
105
136
  ReferencesOf<Column>
106
- >
137
+ > & PreserveBrand<Column>
138
+
139
+ type ByDefaultIdentityColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
140
+ SelectType<Column>,
141
+ InsertType<Column>,
142
+ UpdateType<Column>,
143
+ Column[typeof ColumnTypeId]["dbType"],
144
+ IsNullable<Column>,
145
+ true,
146
+ false,
147
+ IsPrimaryKey<Column>,
148
+ Column[typeof ColumnTypeId]["unique"],
149
+ ReferencesOf<Column>
150
+ > & PreserveBrand<Column>
151
+
152
+ type AlwaysIdentityColumn<Column extends AnyColumnDefinition> = ColumnDefinition<
153
+ SelectType<Column>,
154
+ InsertType<Column>,
155
+ UpdateType<Column>,
156
+ Column[typeof ColumnTypeId]["dbType"],
157
+ IsNullable<Column>,
158
+ false,
159
+ true,
160
+ IsPrimaryKey<Column>,
161
+ Column[typeof ColumnTypeId]["unique"],
162
+ ReferencesOf<Column>
163
+ > & PreserveBrand<Column>
107
164
 
108
165
  type CompatibleColumnExpression<
109
166
  Column extends AnyColumnDefinition,
110
167
  Value extends Expression.Any
111
- > = [Expression.RuntimeOf<Value>] extends [SelectType<Column>] ? Column : never
168
+ > = [Expression.RuntimeOf<Value>] extends [SelectType<Column>]
169
+ ? Column
170
+ : CanCastDbType<
171
+ Expression.DbTypeOf<Value>,
172
+ Column[typeof ColumnTypeId]["dbType"],
173
+ Column[typeof ColumnTypeId]["dbType"]["dialect"]
174
+ > extends true
175
+ ? Column
176
+ : never
177
+
178
+ type CompatibleDdlExpression<
179
+ Column extends AnyColumnDefinition,
180
+ Value extends DdlExpression
181
+ > = Value extends Expression.Any ? CompatibleColumnExpression<Column, Value> : Column
182
+
183
+ type BaseInsertType<Column extends AnyColumnDefinition> = NonNullable<InsertType<Column>>
184
+
185
+ type BaseUpdateType<Column extends AnyColumnDefinition> = NonNullable<UpdateType<Column>>
112
186
 
113
187
  type ReferencingColumn<
114
188
  Column extends AnyColumnDefinition,
@@ -124,7 +198,16 @@ type ReferencingColumn<
124
198
  IsPrimaryKey<Column>,
125
199
  Column[typeof ColumnTypeId]["unique"],
126
200
  ColumnReference<Target>
127
- >
201
+ > & PreserveBrand<Column>
202
+
203
+ type ForeignKeyOptions<Target extends AnyBoundColumn> = {
204
+ readonly target: () => Target
205
+ readonly name?: string
206
+ readonly onUpdate?: ReferentialAction
207
+ readonly onDelete?: ReferentialAction
208
+ readonly deferrable?: boolean
209
+ readonly initiallyDeferred?: boolean
210
+ }
128
211
 
129
212
  type SchemaCompatibleColumn<
130
213
  Column extends AnyColumnDefinition,
@@ -156,18 +239,130 @@ type ColumnWithSchema<
156
239
  ReferencesOf<Column>,
157
240
  Column[typeof ColumnTypeId]["source"],
158
241
  Column[typeof ColumnTypeId]["dependencies"]
159
- >
242
+ > & PreserveBrand<Column>
243
+
244
+ type BrandNameOf<Column extends AnyBoundColumn> =
245
+ `${Column[typeof BoundColumnTypeId]["tableName"]}.${Column[typeof BoundColumnTypeId]["columnName"]}`
246
+
247
+ type BrandedValue<
248
+ Value,
249
+ BrandName extends string
250
+ > = [Extract<Value, null | undefined>] extends [never]
251
+ ? Value & Brand.Brand<BrandName>
252
+ : Exclude<Value, null | undefined> & Brand.Brand<BrandName> | Extract<Value, null | undefined>
253
+
254
+ type BrandMetadata<Column extends AnyColumnDefinition> = {
255
+ readonly metadata: Column["metadata"] & { readonly brand: true }
256
+ }
257
+
258
+ type PreserveBrand<Column extends AnyColumnDefinition> = Column["metadata"]["brand"] extends true
259
+ ? BrandMetadata<Column>
260
+ : {}
261
+
262
+ type BrandedColumn<
263
+ Column extends AnyBoundColumn
264
+ > = ColumnDefinition<
265
+ BrandedValue<SelectType<Column>, BrandNameOf<Column>>,
266
+ BrandedValue<InsertType<Column>, BrandNameOf<Column>>,
267
+ BrandedValue<UpdateType<Column>, BrandNameOf<Column>>,
268
+ Column[typeof ColumnTypeId]["dbType"],
269
+ IsNullable<Column>,
270
+ HasDefault<Column>,
271
+ IsGenerated<Column>,
272
+ IsPrimaryKey<Column>,
273
+ Column[typeof ColumnTypeId]["unique"],
274
+ ReferencesOf<Column>,
275
+ Column[typeof ColumnTypeId]["source"],
276
+ Column[typeof ColumnTypeId]["dependencies"]
277
+ > & BrandMetadata<Column>
278
+
279
+ type BrandMarkedColumn<
280
+ Column extends AnyColumnDefinition
281
+ > = ColumnDefinition<
282
+ SelectType<Column>,
283
+ InsertType<Column>,
284
+ UpdateType<Column>,
285
+ Column[typeof ColumnTypeId]["dbType"],
286
+ IsNullable<Column>,
287
+ HasDefault<Column>,
288
+ IsGenerated<Column>,
289
+ IsPrimaryKey<Column>,
290
+ Column[typeof ColumnTypeId]["unique"],
291
+ ReferencesOf<Column>,
292
+ Column[typeof ColumnTypeId]["source"],
293
+ Column[typeof ColumnTypeId]["dependencies"]
294
+ > & BrandMetadata<Column>
295
+
296
+ export interface ArrayOptions {
297
+ readonly nullableElements?: boolean
298
+ }
299
+
300
+ export type NumericOptions =
301
+ | {
302
+ readonly precision?: undefined
303
+ readonly scale?: undefined
304
+ }
305
+ | {
306
+ readonly precision: number
307
+ readonly scale?: number
308
+ }
309
+
310
+ type ArrayElementSelect<
311
+ Column extends AnyColumnDefinition,
312
+ Options extends ArrayOptions | undefined
313
+ > = Options extends { readonly nullableElements: true }
314
+ ? NullableSelect<BaseSelectType<Column>>
315
+ : BaseSelectType<Column>
316
+
317
+ type ArrayElementInsert<
318
+ Column extends AnyColumnDefinition,
319
+ Options extends ArrayOptions | undefined
320
+ > = Options extends { readonly nullableElements: true }
321
+ ? NullableSelect<BaseInsertType<Column>>
322
+ : BaseInsertType<Column>
323
+
324
+ type ArrayElementUpdate<
325
+ Column extends AnyColumnDefinition,
326
+ Options extends ArrayOptions | undefined
327
+ > = Options extends { readonly nullableElements: true }
328
+ ? NullableSelect<BaseUpdateType<Column>>
329
+ : BaseUpdateType<Column>
330
+
331
+ type ArrayColumn<
332
+ Column extends AnyColumnDefinition,
333
+ Options extends ArrayOptions | undefined = undefined
334
+ > = ColumnDefinition<
335
+ ReadonlyArray<ArrayElementSelect<Column, Options>>,
336
+ ReadonlyArray<ArrayElementInsert<Column, Options>>,
337
+ ReadonlyArray<ArrayElementUpdate<Column, Options>>,
338
+ Expression.DbType.Array<
339
+ Column[typeof ColumnTypeId]["dbType"]["dialect"],
340
+ Column[typeof ColumnTypeId]["dbType"],
341
+ `${Column[typeof ColumnTypeId]["dbType"]["kind"]}[]`
342
+ >,
343
+ IsNullable<Column>,
344
+ HasDefault<Column>,
345
+ IsGenerated<Column>,
346
+ IsPrimaryKey<Column>,
347
+ Column[typeof ColumnTypeId]["unique"],
348
+ ReferencesOf<Column>,
349
+ Column[typeof ColumnTypeId]["source"],
350
+ Column[typeof ColumnTypeId]["dependencies"]
351
+ > & PreserveBrand<Column>
160
352
 
161
353
  const mapColumn = <
162
354
  Column extends AnyColumnDefinition,
163
355
  Next extends AnyColumnDefinition
164
356
  >(
165
357
  column: Column,
166
- metadata: Next["metadata"]
358
+ metadata: AnyColumnDefinition["metadata"]
167
359
  ): Next => remapColumnDefinition(column as any, {
168
360
  metadata
169
361
  }) as Next
170
362
 
363
+ const isColumnDefinitionValue = (value: unknown): value is AnyColumnDefinition =>
364
+ typeof value === "object" && value !== null && ColumnTypeId in value
365
+
171
366
  const primitive = <Type, Db extends Expression.DbType.Any>(
172
367
  schema: Schema.Schema<Type, any, any>,
173
368
  dbType: Db
@@ -214,7 +409,7 @@ type ColumnModule<
214
409
  readonly uuid: () => ColumnDefinition<string, string, string, Expression.DbType.Base<Dialect, UuidKind>, false, false, false, false, false, undefined>
215
410
  readonly text: () => ColumnDefinition<string, string, string, Expression.DbType.Base<Dialect, TextKind>, false, false, false, false, false, undefined>
216
411
  readonly int: () => ColumnDefinition<number, number, number, Expression.DbType.Base<Dialect, IntKind>, false, false, false, false, false, undefined>
217
- readonly number: () => ColumnDefinition<DecimalString, DecimalString, DecimalString, Expression.DbType.Base<Dialect, NumberKind>, false, false, false, false, false, undefined>
412
+ readonly number: (options?: NumericOptions) => ColumnDefinition<DecimalString, DecimalString, DecimalString, Expression.DbType.Base<Dialect, NumberKind>, false, false, false, false, false, undefined>
218
413
  readonly boolean: () => ColumnDefinition<boolean, boolean, boolean, Expression.DbType.Base<Dialect, BooleanKind>, false, false, false, false, false, undefined>
219
414
  readonly date: () => ColumnDefinition<LocalDateString, LocalDateString, LocalDateString, Expression.DbType.Base<Dialect, DateKind>, false, false, false, false, false, undefined>
220
415
  readonly timestamp: () => ColumnDefinition<LocalDateTimeString, LocalDateTimeString, LocalDateTimeString, Expression.DbType.Base<Dialect, TimestampKind>, false, false, false, false, false, undefined>
@@ -234,12 +429,71 @@ type ColumnModule<
234
429
  >
235
430
  }
236
431
 
432
+ type PostgresColumnModule = ColumnModule<
433
+ "postgres",
434
+ "uuid",
435
+ "text",
436
+ "int4",
437
+ "numeric",
438
+ "bool",
439
+ "date",
440
+ "timestamp",
441
+ "json"
442
+ > & {
443
+ readonly int2: () => ColumnDefinition<number, number, number, Expression.DbType.Base<"postgres", "int2">, false, false, false, false, false, undefined>
444
+ readonly int8: () => ColumnDefinition<BigIntString, BigIntString, BigIntString, Expression.DbType.Base<"postgres", "int8">, false, false, false, false, false, undefined>
445
+ readonly float4: () => ColumnDefinition<number, number, number, Expression.DbType.Base<"postgres", "float4">, false, false, false, false, false, undefined>
446
+ readonly float8: () => ColumnDefinition<number, number, number, Expression.DbType.Base<"postgres", "float8">, false, false, false, false, false, undefined>
447
+ readonly char: (length?: number) => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "char">, false, false, false, false, false, undefined>
448
+ readonly varchar: (length?: number) => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "varchar">, false, false, false, false, false, undefined>
449
+ readonly time: () => ColumnDefinition<LocalTimeString, LocalTimeString, LocalTimeString, Expression.DbType.Base<"postgres", "time">, false, false, false, false, false, undefined>
450
+ readonly timetz: () => ColumnDefinition<OffsetTimeString, OffsetTimeString, OffsetTimeString, Expression.DbType.Base<"postgres", "timetz">, false, false, false, false, false, undefined>
451
+ readonly timestamptz: () => ColumnDefinition<InstantString, InstantString, InstantString, Expression.DbType.Base<"postgres", "timestamptz">, false, false, false, false, false, undefined>
452
+ readonly interval: () => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "interval">, false, false, false, false, false, undefined>
453
+ readonly bytea: () => ColumnDefinition<Uint8Array, Uint8Array, Uint8Array, Expression.DbType.Base<"postgres", "bytea">, false, false, false, false, false, undefined>
454
+ readonly name: () => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "name">, false, false, false, false, false, undefined>
455
+ readonly oid: () => ColumnDefinition<number, number, number, Expression.DbType.Base<"postgres", "oid">, false, false, false, false, false, undefined>
456
+ readonly regclass: () => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "regclass">, false, false, false, false, false, undefined>
457
+ readonly bit: () => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "bit">, false, false, false, false, false, undefined>
458
+ readonly varbit: () => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "varbit">, false, false, false, false, false, undefined>
459
+ readonly xml: () => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "xml">, false, false, false, false, false, undefined>
460
+ readonly pg_lsn: () => ColumnDefinition<string, string, string, Expression.DbType.Base<"postgres", "pg_lsn">, false, false, false, false, false, undefined>
461
+ readonly jsonb: <SchemaType extends Schema.Schema.Any>(
462
+ schema: SchemaType
463
+ ) => ColumnDefinition<
464
+ Schema.Schema.Type<SchemaType>,
465
+ Schema.Schema.Type<SchemaType>,
466
+ Schema.Schema.Type<SchemaType>,
467
+ Expression.DbType.Json<"postgres", "jsonb">,
468
+ false,
469
+ false,
470
+ false,
471
+ false,
472
+ false,
473
+ undefined
474
+ >
475
+ }
476
+
237
477
  const typeFactory = <Dialect extends string>(dialect: Dialect) =>
238
478
  <Kind extends string>(kind: Kind): Expression.DbType.Base<Dialect, Kind> => ({
239
479
  dialect,
240
480
  kind
241
481
  })
242
482
 
483
+ const postgresType = typeFactory("postgres")
484
+
485
+ const renderNumericDdlType = (
486
+ kind: string,
487
+ options?: NumericOptions
488
+ ): string | undefined => {
489
+ if (options === undefined || options.precision === undefined) {
490
+ return undefined
491
+ }
492
+ return options.scale === undefined
493
+ ? `${kind}(${options.precision})`
494
+ : `${kind}(${options.precision},${options.scale})`
495
+ }
496
+
243
497
  const makeColumnModule = <
244
498
  Dialect extends string,
245
499
  UuidKind extends string,
@@ -276,12 +530,25 @@ const makeColumnModule = <
276
530
  generated: false,
277
531
  primaryKey: false,
278
532
  unique: false,
279
- references: undefined
533
+ references: undefined,
534
+ ddlType: undefined,
535
+ identity: undefined
280
536
  }),
281
537
  uuid: () => primitive(Schema.UUID, dialectType(kinds.uuid)),
282
538
  text: () => primitive(Schema.String, dialectType(kinds.text)),
283
539
  int: () => primitive(Schema.Int, dialectType(kinds.int)),
284
- number: () => primitive(DecimalStringSchema, dialectType(kinds.number)),
540
+ number: (options?: NumericOptions) =>
541
+ makeColumnDefinition(DecimalStringSchema, {
542
+ dbType: dialectType(kinds.number),
543
+ nullable: false,
544
+ hasDefault: false,
545
+ generated: false,
546
+ primaryKey: false,
547
+ unique: false,
548
+ references: undefined,
549
+ ddlType: renderNumericDdlType(kinds.number, options),
550
+ identity: undefined
551
+ }),
285
552
  boolean: () => primitive(Schema.Boolean, dialectType(kinds.boolean)),
286
553
  date: () => primitive(LocalDateStringSchema, dialectType(kinds.date)),
287
554
  timestamp: () => primitive(LocalDateTimeStringSchema, dialectType(kinds.timestamp)),
@@ -296,13 +563,14 @@ const makeColumnModule = <
296
563
  generated: false,
297
564
  primaryKey: false,
298
565
  unique: false,
299
- references: undefined
566
+ references: undefined,
567
+ ddlType: undefined,
568
+ identity: undefined
300
569
  })
301
570
  }
302
571
  }
303
572
 
304
- /** Postgres-specialized column constructors. */
305
- export const postgres = makeColumnModule("postgres", {
573
+ const postgresBase = makeColumnModule("postgres", {
306
574
  uuid: "uuid",
307
575
  text: "text",
308
576
  int: "int4",
@@ -313,6 +581,66 @@ export const postgres = makeColumnModule("postgres", {
313
581
  json: "json"
314
582
  })
315
583
 
584
+ /** Postgres-specialized column constructors. */
585
+ export const postgres: PostgresColumnModule = {
586
+ ...postgresBase,
587
+ int2: () => primitive(Schema.Int, postgresType("int2")),
588
+ int8: () => primitive(BigIntStringSchema, postgresType("int8")),
589
+ float4: () => primitive(Schema.Number, postgresType("float4")),
590
+ float8: () => primitive(Schema.Number, postgresType("float8")),
591
+ char: (length = 1) =>
592
+ makeColumnDefinition(Schema.String, {
593
+ dbType: postgresType("char"),
594
+ nullable: false,
595
+ hasDefault: false,
596
+ generated: false,
597
+ primaryKey: false,
598
+ unique: false,
599
+ references: undefined,
600
+ ddlType: `char(${length})`,
601
+ identity: undefined
602
+ }),
603
+ varchar: (length?: number) =>
604
+ makeColumnDefinition(Schema.String, {
605
+ dbType: postgresType("varchar"),
606
+ nullable: false,
607
+ hasDefault: false,
608
+ generated: false,
609
+ primaryKey: false,
610
+ unique: false,
611
+ references: undefined,
612
+ ddlType: length === undefined ? "varchar" : `varchar(${length})`,
613
+ identity: undefined
614
+ }),
615
+ time: () => primitive(LocalTimeStringSchema, postgresType("time")),
616
+ timetz: () => primitive(OffsetTimeStringSchema, postgresType("timetz")),
617
+ timestamptz: () => primitive(InstantStringSchema, postgresType("timestamptz")),
618
+ interval: () => primitive(Schema.String, postgresType("interval")),
619
+ bytea: () => primitive(Schema.Uint8ArrayFromSelf, postgresType("bytea")),
620
+ name: () => primitive(Schema.String, postgresType("name")),
621
+ oid: () => primitive(Schema.Int, postgresType("oid")),
622
+ regclass: () => primitive(Schema.String, postgresType("regclass")),
623
+ bit: () => primitive(Schema.String, postgresType("bit")),
624
+ varbit: () => primitive(Schema.String, postgresType("varbit")),
625
+ xml: () => primitive(Schema.String, postgresType("xml")),
626
+ pg_lsn: () => primitive(Schema.String, postgresType("pg_lsn")),
627
+ jsonb: <SchemaType extends Schema.Schema.Any>(schema: SchemaType) =>
628
+ makeColumnDefinition(schema as unknown as Schema.Schema<NonNullable<Schema.Schema.Type<SchemaType>>, any, any>, {
629
+ dbType: {
630
+ ...postgresType("jsonb"),
631
+ variant: "jsonb"
632
+ } as Expression.DbType.Json<"postgres", "jsonb">,
633
+ nullable: false,
634
+ hasDefault: false,
635
+ generated: false,
636
+ primaryKey: false,
637
+ unique: false,
638
+ references: undefined,
639
+ ddlType: undefined,
640
+ identity: undefined
641
+ })
642
+ }
643
+
316
644
  /** MySQL-specialized column constructors. */
317
645
  export const mysql = makeColumnModule("mysql", {
318
646
  uuid: "uuid",
@@ -331,17 +659,55 @@ export const uuid = postgres.uuid
331
659
  export const text = postgres.text
332
660
  /** Creates a Postgres `int4` column. */
333
661
  export const int = postgres.int
662
+ /** Creates a Postgres `int2` column. */
663
+ export const int2 = postgres.int2
664
+ /** Creates a Postgres `int8` column. */
665
+ export const int8 = postgres.int8
334
666
  /** Creates a Postgres `numeric` column decoded as `DecimalString`. */
335
667
  export const number = postgres.number
668
+ /** Creates a Postgres `float4` column. */
669
+ export const float4 = postgres.float4
670
+ /** Creates a Postgres `float8` column. */
671
+ export const float8 = postgres.float8
336
672
  /** Creates a Postgres `bool` column. */
337
673
  export const boolean = postgres.boolean
338
674
  /** Creates a Postgres `date` column decoded as `LocalDateString`. */
339
675
  export const date = postgres.date
340
676
  /** Creates a Postgres `timestamp` column decoded as `LocalDateTimeString`. */
341
677
  export const timestamp = postgres.timestamp
678
+ /** Creates a Postgres `time` column decoded as `LocalTimeString`. */
679
+ export const time = postgres.time
680
+ /** Creates a Postgres `timetz` column decoded as `OffsetTimeString`. */
681
+ export const timetz = postgres.timetz
682
+ /** Creates a Postgres `timestamptz` column decoded as `InstantString`. */
683
+ export const timestamptz = postgres.timestamptz
684
+ /** Creates a Postgres `char` column. */
685
+ export const char = postgres.char
686
+ /** Creates a Postgres `varchar` column. */
687
+ export const varchar = postgres.varchar
688
+ /** Creates a Postgres `interval` column. */
689
+ export const interval = postgres.interval
690
+ /** Creates a Postgres `bytea` column. */
691
+ export const bytea = postgres.bytea
692
+ /** Creates a Postgres `name` column. */
693
+ export const name = postgres.name
694
+ /** Creates a Postgres `oid` column. */
695
+ export const oid = postgres.oid
696
+ /** Creates a Postgres `regclass` column. */
697
+ export const regclass = postgres.regclass
698
+ /** Creates a Postgres `bit` column. */
699
+ export const bit = postgres.bit
700
+ /** Creates a Postgres `varbit` column. */
701
+ export const varbit = postgres.varbit
702
+ /** Creates a Postgres `xml` column. */
703
+ export const xml = postgres.xml
704
+ /** Creates a Postgres `pg_lsn` column. */
705
+ export const pg_lsn = postgres.pg_lsn
342
706
 
343
707
  /** Creates a Postgres `json` column backed by an arbitrary Effect schema. */
344
708
  export const json = postgres.json
709
+ /** Creates a Postgres `jsonb` column backed by an arbitrary Effect schema. */
710
+ export const jsonb = postgres.jsonb
345
711
  /** Creates a Postgres column backed by an arbitrary SQL type and Effect schema. */
346
712
  export const custom = postgres.custom
347
713
 
@@ -354,6 +720,33 @@ export const schema = <SchemaType extends Schema.Schema.Any>(nextSchema: SchemaT
354
720
  schema: nextSchema
355
721
  }) as ColumnWithSchema<Column, SchemaType>
356
722
 
723
+ type BrandResult<Column extends AnyColumnDefinition> = Column extends AnyBoundColumn
724
+ ? BrandedColumn<Column>
725
+ : BrandMarkedColumn<Column>
726
+
727
+ /** Brands a column with its `table.column` provenance. */
728
+ export const brand = <Column extends AnyColumnDefinition>(
729
+ column: Column
730
+ ): BrandResult<Column> => {
731
+ if (BoundColumnTypeId in column) {
732
+ const boundColumn = column as unknown as AnyBoundColumn
733
+ const brandName = `${boundColumn[BoundColumnTypeId].tableName}.${boundColumn[BoundColumnTypeId].columnName}`
734
+ return remapColumnDefinition(boundColumn, {
735
+ schema: Schema.brand(brandName)(boundColumn.schema),
736
+ metadata: {
737
+ ...boundColumn.metadata,
738
+ brand: true
739
+ }
740
+ }) as BrandResult<Column>
741
+ }
742
+ return remapColumnDefinition(column, {
743
+ metadata: {
744
+ ...column.metadata,
745
+ brand: true
746
+ }
747
+ }) as BrandResult<Column>
748
+ }
749
+
357
750
  /** Marks a column as nullable. Nullable columns decode as `T | null`. */
358
751
  export const nullable = <Column extends AnyColumnDefinition>(
359
752
  column: Column[typeof ColumnTypeId]["primaryKey"] extends true ? never : Column
@@ -374,53 +767,182 @@ export const primaryKey = <Column extends AnyColumnDefinition>(
374
767
  unique: true
375
768
  })
376
769
 
770
+ type UniqueModifier = {
771
+ <Column extends AnyColumnDefinition>(column: Column): UniqueColumn<Column>
772
+ readonly options: <const Options extends ColumnUniqueOptions>(
773
+ options: Options
774
+ ) => <Column extends AnyColumnDefinition>(column: Column) => UniqueColumn<Column>
775
+ }
776
+
377
777
  /** Marks a column as unique. */
378
- export const unique = <Column extends AnyColumnDefinition>(
379
- column: Column
380
- ): UniqueColumn<Column> =>
381
- mapColumn(column, {
382
- ...column.metadata,
383
- unique: true
384
- })
778
+ export const unique: UniqueModifier = Object.assign(
779
+ <Column extends AnyColumnDefinition>(column: Column): UniqueColumn<Column> =>
780
+ mapColumn(column, {
781
+ ...column.metadata,
782
+ unique: true
783
+ }),
784
+ {
785
+ options: <const Options extends ColumnUniqueOptions>(options: Options) =>
786
+ <Column extends AnyColumnDefinition>(column: Column): UniqueColumn<Column> =>
787
+ mapColumn(column, {
788
+ ...column.metadata,
789
+ unique: true,
790
+ uniqueConstraint: options
791
+ })
792
+ }
793
+ )
385
794
 
386
795
  /** Marks a column as having a database default expression and therefore optional on insert. */
387
- export const default_ = <Value extends Expression.Any>(value: Value) =>
796
+ export const default_ = <Value extends DdlExpression>(value: Value) =>
388
797
  <Column extends AnyColumnDefinition>(
389
- column: Column[typeof ColumnTypeId]["generated"] extends true ? never : CompatibleColumnExpression<Column, Value>
798
+ column: Column[typeof ColumnTypeId]["generated"] extends true ? never : CompatibleDdlExpression<Column, Value>
390
799
  ): HasDefaultColumn<Column> =>
391
800
  mapColumn(column, {
392
801
  ...column.metadata,
393
802
  hasDefault: true,
394
803
  defaultValue: value,
395
- generatedValue: undefined
804
+ generatedValue: undefined,
805
+ identity: undefined
396
806
  })
397
807
 
398
808
  /** Marks a column as generated by the database expression and omitted from insert/update. */
399
- export const generated = <Value extends Expression.Any>(value: Value) =>
809
+ export const generated = <Value extends DdlExpression>(value: Value) =>
400
810
  <Column extends AnyColumnDefinition>(
401
- column: Column[typeof ColumnTypeId]["hasDefault"] extends true ? never : CompatibleColumnExpression<Column, Value>
811
+ column: Column[typeof ColumnTypeId]["hasDefault"] extends true ? never : CompatibleDdlExpression<Column, Value>
402
812
  ): GeneratedColumn<Column> =>
403
813
  mapColumn(column, {
404
814
  ...column.metadata,
405
815
  generated: true,
406
816
  hasDefault: false,
407
817
  defaultValue: undefined,
408
- generatedValue: value
818
+ generatedValue: value,
819
+ identity: undefined
820
+ })
821
+
822
+ /** Preserves the exact SQL type used for DDL rendering. */
823
+ export const ddlType = <SqlType extends string>(sqlType: SqlType) =>
824
+ <Column extends AnyColumnDefinition>(column: Column): DdlTypedColumn<Column> =>
825
+ mapColumn(column, {
826
+ ...column.metadata,
827
+ ddlType: sqlType
828
+ })
829
+
830
+ /** Marks a column as a Postgres array type. */
831
+ export const array = <Options extends ArrayOptions | undefined = undefined>(
832
+ options?: Options
833
+ ) =>
834
+ <Column extends AnyColumnDefinition>(
835
+ column: Column
836
+ ): ArrayColumn<Column, Options> =>
837
+ remapColumnDefinition(column as AnyColumnDefinition, {
838
+ schema: Schema.Array(options?.nullableElements ? Schema.NullOr(column.schema) : column.schema),
839
+ metadata: {
840
+ ...column.metadata,
841
+ dbType: {
842
+ dialect: column.metadata.dbType.dialect,
843
+ kind: `${column.metadata.dbType.kind}[]`,
844
+ element: column.metadata.dbType
845
+ } as Expression.DbType.Array<
846
+ Column[typeof ColumnTypeId]["dbType"]["dialect"],
847
+ Column[typeof ColumnTypeId]["dbType"],
848
+ `${Column[typeof ColumnTypeId]["dbType"]["kind"]}[]`
849
+ >,
850
+ ddlType: `${column.metadata.ddlType ?? column.metadata.dbType.kind}[]`
851
+ }
852
+ }) as ArrayColumn<Column, Options>
853
+
854
+ /** Marks a column as indexed. */
855
+ export function index<Column extends AnyColumnDefinition>(
856
+ column: Column
857
+ ): IndexedColumn<Column>
858
+ export function index<const Options extends ColumnIndexOptions>(
859
+ options: Options
860
+ ): <Column extends AnyColumnDefinition>(column: Column) => IndexedColumn<Column>
861
+ export function index(arg: unknown): unknown {
862
+ if (isColumnDefinitionValue(arg)) {
863
+ return mapColumn(arg, {
864
+ ...arg.metadata,
865
+ index: arg.metadata.index ?? {}
409
866
  })
867
+ }
868
+ const options = (arg ?? {}) as ColumnIndexOptions
869
+ return <Column extends AnyColumnDefinition>(
870
+ column: Column
871
+ ): IndexedColumn<Column> =>
872
+ mapColumn(column, {
873
+ ...column.metadata,
874
+ index: options
875
+ })
876
+ }
877
+
878
+ /** Marks a column as `generated by default as identity`. */
879
+ export const identityByDefault = <Column extends AnyColumnDefinition>(
880
+ column: Column[typeof ColumnTypeId]["generated"] extends true ? never : Column
881
+ ): ByDefaultIdentityColumn<Column> =>
882
+ mapColumn(column, {
883
+ ...column.metadata,
884
+ hasDefault: true,
885
+ generated: false,
886
+ defaultValue: undefined,
887
+ generatedValue: undefined,
888
+ identity: {
889
+ generation: "byDefault"
890
+ }
891
+ })
892
+
893
+ /** Marks a column as `generated always as identity`. */
894
+ export const identityAlways = <Column extends AnyColumnDefinition>(
895
+ column: Column[typeof ColumnTypeId]["hasDefault"] extends true ? never : Column
896
+ ): AlwaysIdentityColumn<Column> =>
897
+ mapColumn(column, {
898
+ ...column.metadata,
899
+ hasDefault: false,
900
+ generated: true,
901
+ defaultValue: undefined,
902
+ generatedValue: undefined,
903
+ identity: {
904
+ generation: "always"
905
+ }
906
+ })
410
907
 
411
908
  /**
412
909
  * Attaches a lazy foreign-key reference to another bound column.
413
910
  *
414
911
  * The base, non-null select types must match.
415
912
  */
416
- export const references = <Target extends AnyBoundColumn>(target: () => Target) =>
417
- <Column extends AnyColumnDefinition>(
418
- column: CompatibleReference<Column, Target>
419
- ): ReferencingColumn<Column, Target> =>
913
+ export function foreignKey<Target extends AnyBoundColumn>(
914
+ target: () => Target
915
+ ): <Column extends AnyColumnDefinition>(
916
+ column: CompatibleReference<Column, Target>
917
+ ) => ReferencingColumn<Column, Target>
918
+ export function foreignKey<const Options extends ForeignKeyOptions<AnyBoundColumn>>(
919
+ options: Options
920
+ ): <Column extends AnyColumnDefinition>(
921
+ column: CompatibleReference<Column, ReturnType<Options["target"]>>
922
+ ) => ReferencingColumn<Column, ReturnType<Options["target"]>>
923
+ export function foreignKey(arg: unknown): unknown {
924
+ if (typeof arg === "function") {
925
+ const target = arg as () => AnyBoundColumn
926
+ return <Column extends AnyColumnDefinition>(
927
+ column: CompatibleReference<Column, AnyBoundColumn>
928
+ ): ReferencingColumn<Column, AnyBoundColumn> =>
929
+ mapColumn(column, {
930
+ ...column.metadata,
931
+ references: { target }
932
+ })
933
+ }
934
+ const options = arg as ForeignKeyOptions<AnyBoundColumn>
935
+ return <Column extends AnyColumnDefinition>(
936
+ column: CompatibleReference<Column, ReturnType<typeof options.target>>
937
+ ): ReferencingColumn<Column, ReturnType<typeof options.target>> =>
420
938
  mapColumn(column, {
421
939
  ...column.metadata,
422
- references: { target }
940
+ references: options
423
941
  })
942
+ }
943
+
944
+ export const references = <Target extends AnyBoundColumn>(target: () => Target) =>
945
+ foreignKey(target)
424
946
 
425
947
  /** Convenience alias for any column definition. */
426
948
  export type Any = AnyColumnDefinition