palgres 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,9 +7,21 @@ export type OnUpdateAction = OnDeleteAction;
7
7
  * or forget the user (`set null`). Nothing else: a deletion is never blocked.
8
8
  */
9
9
  export type AuthUserOnDelete = Extract<OnDeleteAction, 'cascade' | 'set null'>;
10
+ /**
11
+ * The column types `.array()` accepts as elements: `text().array()` is a
12
+ * `text[]` column. `jsonb` is left out on purpose: a JSON array is already a
13
+ * `jsonb` value, so keep it in a `jsonb()` column.
14
+ */
10
15
  export type ArrayElementType = 'uuid' | 'text' | 'integer' | 'bigint' | 'numeric' | 'boolean' | 'timestamp' | 'date' | 'enum';
11
16
  /** Column type identifiers. */
12
17
  export type ColumnType = 'uuid' | 'text' | 'integer' | 'bigint' | 'numeric' | 'boolean' | 'timestamp' | 'date' | 'jsonb' | 'enum' | 'sql' | `${ArrayElementType}[]`;
18
+ /**
19
+ * Whether the application writes a column. `'readonly'` is a column whose
20
+ * value only the database sets, such as an `.identity()` that is ALWAYS or a
21
+ * `.generated()` column: it is on the row type and on no insert or update
22
+ * type. The default is the union of both, which reads as "writable", so a type
23
+ * that does not name this parameter still matches every column.
24
+ */
13
25
  export type ColumnWrite = 'write' | 'readonly';
14
26
  /** Base column definition shared by all column types. */
15
27
  export interface ColumnDef {
@@ -64,9 +76,26 @@ export interface ColumnDef {
64
76
  * declared. Without it, renaming a column loses its data.
65
77
  */
66
78
  renamedFrom?: string;
79
+ /**
80
+ * This release's PROMISE that it does not use this column: it neither reads
81
+ * nor writes it, and never names it in a filter, a sort or a SET. Set by
82
+ * `.ignored()`.
83
+ *
84
+ * Removing a column from a table that holds rows takes two deploys: mark it
85
+ * ignored and ship, then remove it. A deploy that drops such a column is
86
+ * refused unless the release already serving carries this mark (a column of
87
+ * an empty table is simply dropped). While the mark is on, the SQL the engine
88
+ * writes for you keeps the promise: no projection, `RETURNING` or `with`
89
+ * object names the column, and the generated row type leaves it out. So the
90
+ * release still serving does not break when the column is dropped.
91
+ *
92
+ * The word is `ignored` and not `deprecated` deliberately: RFC 9745 defines
93
+ * deprecation as changing NO behaviour, and this changes what a deploy will
94
+ * accept. Rails calls the same thing `ignored_columns`.
95
+ */
67
96
  ignored?: boolean;
68
97
  /**
69
- * The per-row fill of a NOT NULL column (`.fillWith()`, D-1): one SQL scalar
98
+ * The per-row fill of a NOT NULL column (`.fillWith()`): one SQL scalar
70
99
  * expression over this row's columns. The deploy fills every row that does not
71
100
  * supply the column with it — the rows already in the table and every write of
72
101
  * a release that does not know the column yet — and only then makes it NOT
@@ -75,6 +104,10 @@ export interface ColumnDef {
75
104
  fill?: string;
76
105
  onDeleteAction?: OnDeleteAction;
77
106
  onUpdateAction?: OnUpdateAction;
107
+ /**
108
+ * Set only when a foreign key explicitly says `index: false`: it turns off
109
+ * the index created for this foreign key column by default.
110
+ */
78
111
  index?: boolean;
79
112
  enumName?: string;
80
113
  enumValues?: string[];
@@ -90,23 +123,72 @@ export interface ColumnDef {
90
123
  * its own data.
91
124
  */
92
125
  dbAssigned?: boolean;
126
+ /** numeric({ precision, scale }): the typmod, which is part of the column's TYPE. */
93
127
  precision?: number;
94
128
  scale?: number;
129
+ /** sqlType("…"): the Postgres type, verbatim. The deploy asks Postgres
130
+ * how it spells the type and refuses any other spelling. */
95
131
  sqlType?: string;
132
+ /** .identity(): a sequence numbers the column. `'always'` refuses any value
133
+ * the application sends; `'byDefault'` fills it only when none is sent. */
96
134
  identity?: 'always' | 'byDefault';
97
135
  /** .generated(expr): the column is `expr` over the row — computed when read (virtual) or on write (stored). */
98
136
  generated?: {
99
137
  expr: string;
100
138
  stored: boolean;
101
139
  };
140
+ /**
141
+ * The named codec this column declares (`.asNumber()` or `.asDecimal()`).
142
+ * Unlike `transform`, the NAME is kept in the runtime schema object, so the
143
+ * generated types can use the matching TypeScript type instead of falling
144
+ * back to the storage type. Not part of the DDL: the column's Postgres type
145
+ * is unchanged.
146
+ */
102
147
  codec?: CodecName;
148
+ /**
149
+ * How the stored value is converted on its way in and out of the
150
+ * application (`.transform()`).
151
+ *
152
+ * NOT part of the DDL: the column's Postgres type is unchanged and this pair
153
+ * is never written into a migration. It exists so the row type can hand back
154
+ * the type the application actually works with.
155
+ */
103
156
  transform?: ColumnTransform;
104
157
  }
158
+ /**
159
+ * The read/write pair a column may declare with `.transform()`.
160
+ *
161
+ * `fromDb` takes whatever the driver produced for this column and returns the
162
+ * value the application sees; `toDb` is its inverse on the way out. Not
163
+ * exported on purpose: a column declares one inline, so nobody needs to name
164
+ * the shape.
165
+ */
105
166
  interface ColumnTransform<T = unknown> {
106
167
  fromDb: (value: unknown) => T;
107
168
  toDb: (value: T) => unknown;
108
169
  }
170
+ /**
171
+ * The closed set of named column codecs: `'number'` (`.asNumber()`) and
172
+ * `'decimal'` (`.asDecimal()`).
173
+ *
174
+ * WHY A CLOSED SET rather than `.transform<T>()`: a transform's target type is
175
+ * a TYPE parameter, erased at runtime, so the generator that reads the bundled
176
+ * schema object cannot learn it. The generated types would say `string` while
177
+ * the application is handed a number.
178
+ *
179
+ * A codec is NAMED instead of typed: the name is stored on the column
180
+ * (`_def.codec`), travels with the runtime schema object to every reader, and
181
+ * BOTH the conversion and the generated TypeScript type come from it. One
182
+ * declaration, one truth, and no way for the two to disagree.
183
+ */
109
184
  export type CodecName = 'number' | 'decimal';
185
+ /**
186
+ * What each named codec does: `fromDb` turns the value read from Postgres into
187
+ * the one the application sees, `toDb` turns it back for a write, and `tsType`
188
+ * is the TypeScript type the generated row types use. The `'number'` codec
189
+ * throws on a whole number outside the safe integer range, and on `NaN` and
190
+ * `Infinity`, instead of storing a different number.
191
+ */
110
192
  export declare const CODECS: Record<CodecName, {
111
193
  fromDb: (value: unknown) => unknown;
112
194
  toDb: (value: unknown) => unknown;
@@ -175,7 +257,35 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
175
257
  * This is for a value something ELSE writes — a trigger in a raw() unit.
176
258
  */
177
259
  dbAssigned(): ColumnBuilder<K, N, true, E, P, T, W>;
260
+ /**
261
+ * An array of this type: `text().array()` is `text[]`, surfaced as `string[]`;
262
+ * `enumType('Role', [...]).array()` is `"Role"[]`, with the enum's values as
263
+ * the element type (what Prisma's `roles Role[]` makes). The only default an
264
+ * array takes is the empty one: `text().array().default([])`.
265
+ *
266
+ * Call it before `.default()`, and not after `.asNumber()`, `.asDecimal()` or
267
+ * `.transform()`: those convert one value, not a list of them.
268
+ *
269
+ * Filter with `contains` (every given element is in the array) and
270
+ * `overlaps` (any of them is).
271
+ */
178
272
  array(): ColumnBuilder<`${K & ArrayElementType}[]`, N, D, K extends 'enum' ? E : never, P, T, W>;
273
+ /**
274
+ * The database numbers this column from a sequence: `GENERATED ALWAYS AS
275
+ * IDENTITY`. Only on integer(), bigint() and sqlType('smallint'); the column
276
+ * is NOT NULL and takes no default and no fill.
277
+ *
278
+ * By default ALWAYS: the column is on no insert or update type, and Postgres
279
+ * refuses a value the application sends. `{ always: false }` is
280
+ * `BY DEFAULT`: the sequence fills it only when an insert leaves it out.
281
+ *
282
+ * A column's identity does not change after it is created: adding or removing
283
+ * it on an existing column is refused, because the release still serving
284
+ * writes the column too — add a new column instead. A NEW identity column on
285
+ * a table that already holds rows is refused as well (Postgres would rewrite
286
+ * the table under a lock to number them): add a bigint column with a default
287
+ * and fill it.
288
+ */
179
289
  identity(): ColumnBuilder<K, false, true, E, P, T, 'readonly'>;
180
290
  identity(opts: {
181
291
  always: true;
@@ -195,8 +305,23 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
195
305
  generated(expr: string, opts?: {
196
306
  stored?: boolean;
197
307
  }): ColumnBuilder<K, N, true, E, P, T, 'readonly'>;
308
+ /**
309
+ * Fill this NOT NULL column for every row that does not supply it: the rows
310
+ * already in the table AND every write of the release that is still serving,
311
+ * which does not know the column yet. So one push can add a NOT NULL column,
312
+ * or make a column NOT NULL, on a table that holds rows, and nothing is
313
+ * blocked while it runs.
314
+ *
315
+ * `expr` is ONE SQL scalar expression over this row's columns. A table,
316
+ * function or type it names must be schema-qualified (`public.accounts`): it
317
+ * runs with a pinned search_path. It reads BYPASSING row-level security, and
318
+ * what it writes is visible to anyone who can read the row.
319
+ *
320
+ * Only on a NOT NULL column with no default that is not `.ignored()`;
321
+ * `defineTable` refuses any other column and names it. For a constant value,
322
+ * use `.default()`.
323
+ */
198
324
  fillWith(expr: string): ColumnBuilder<K, N, D, E, P, T, W>;
199
- /** Add a foreign key reference. */
200
325
  /**
201
326
  * Declares that this column used to be called `previous`.
202
327
  *
@@ -210,20 +335,11 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
210
335
  */
211
336
  renamedFrom(previous: string): ColumnBuilder<K, N, D, E, P, T, W>;
212
337
  /**
213
- * See {@link ColumnDef.ignored}.
338
+ * Marks this column as no longer used by this release: the first step of
339
+ * removing it from a table that holds rows. See {@link ColumnDef.ignored}.
214
340
  *
215
- * COPIES the def rather than mutating it. The constructor takes an existing
216
- * def BY REFERENCE, so every builder derived from another shares one object —
217
- * `const a = slug.unique()` leaves `a._def === slug._def`. An in-place
218
- * `ignored = true` therefore marks every column sharing that def, including
219
- * one another table actively reads, and the gate would let THAT column be
220
- * dropped. Measured before this copy existed.
221
- *
222
- * The aliasing is older than this method and other fields leak through it too.
223
- * The reason this one cannot wait: every other leak produces a VISIBLE schema
224
- * difference — the plan shows it, the DDL shows it. This one is invisible by
225
- * design (no DDL, no diff, no plan line), so its only effect is to disarm a
226
- * safety gate in silence.
341
+ * It returns a new column and leaves the one it was called on unmarked, so a
342
+ * column built from a shared base does not mark the others built from it.
227
343
  */
228
344
  ignored(): ColumnBuilder<K, N, D, E, P, T, W>;
229
345
  /**
@@ -231,13 +347,13 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
231
347
  *
232
348
  * The target is a THUNK, not a direct reference. In a cycle (`x → y`, `y → x`)
233
349
  * the second table does not exist yet when the first is built; a direct
234
- * reference makes TypeScript chase its own tail (TS7022 — measured, and making
235
- * the return type independent of the target does NOT help). The thunk is
350
+ * reference makes TypeScript report a circular type (TS7022), and making the
351
+ * return type independent of the target does not help. The thunk is
236
352
  * invoked in `defineSchema`, where every binding exists and every table
237
353
  * already knows its name.
238
354
  *
239
355
  * In a cycle, ONE side needs an explicit return type:
240
- * `references((): AnyColumn => y.id)`. One side is enough — measured.
356
+ * `references((): AnyColumn => y.id)`. One side is enough.
241
357
  * For a self-reference use `selfReferences(column)`: no thunk, no annotation.
242
358
  *
243
359
  * `as` names the FORWARD relation (`author_id` → `author` by default);
@@ -252,6 +368,18 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
252
368
  onDelete?: OnDeleteAction;
253
369
  /** ON UPDATE — what Prisma writes for every relation (cascade). Default: no action. */
254
370
  onUpdate?: OnUpdateAction;
371
+ /**
372
+ * Whether an index is created for this foreign key column. On by default.
373
+ *
374
+ * Postgres does not index a foreign key by itself; only the target side
375
+ * has its unique constraint. Without an index, every JOIN over the
376
+ * foreign key and every `ON DELETE CASCADE` pays for it: deleting a
377
+ * parent scans the whole child table.
378
+ *
379
+ * Turn it off (`false`) only for a real need: when the foreign key column
380
+ * is also updated OFTEN, the index breaks HOT updates. HOT breaks when a
381
+ * column that CHANGES is indexed, not because the table has indexes.
382
+ */
255
383
  index?: boolean;
256
384
  }): ColumnBuilder<K, N, D, E, P, T, W>;
257
385
  /**
@@ -260,8 +388,8 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
260
388
  *
261
389
  * No thunk and no type annotation: the target table is the one being declared,
262
390
  * so there is nothing to defer and nothing for TypeScript to chase in a circle.
263
- * Drizzle forces an explicit `(): AnyPgColumn =>` here because its reference
264
- * always goes through a callback; measured, we do not need one.
391
+ * Unlike a `references()` callback, it needs no `(): AnyColumn =>`
392
+ * annotation.
265
393
  */
266
394
  selfReferences(column: string, opts?: {
267
395
  as?: string;
@@ -271,6 +399,32 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
271
399
  onDelete(action: OnDeleteAction): ColumnBuilder<K, N, D, E, P, T, W>;
272
400
  /** Add a single-column UNIQUE constraint. */
273
401
  unique(): ColumnBuilder<K, N, D, E, P, T, W>;
402
+ /**
403
+ * Surface this exact-precision column (`bigint()` or `numeric()`) as a JS
404
+ * `number`.
405
+ *
406
+ * `numeric`/`bigint` arrive as strings because a JS number cannot hold their
407
+ * full range. That is correct, and exactly why this exists: application code
408
+ * that does arithmetic on the column otherwise repeats the same
409
+ * `Number(row.x)` / `String(v)` pair in every caller, and each copy is a
410
+ * place the two directions can drift apart.
411
+ *
412
+ * A fraction is written as the number JavaScript holds (`9.99` → `'9.99'`)
413
+ * and Postgres rounds it to the column's scale; a value read back is a JS
414
+ * number, so a decimal with more significant digits than a double holds
415
+ * comes back rounded. Whole numbers are safe below 2^53: writing one past
416
+ * it throws instead of storing a different number, as do `NaN` and
417
+ * `Infinity`. For values a double cannot hold, use `.asDecimal()`. A column
418
+ * has one codec, and not together with `.transform()`.
419
+ */
420
+ asNumber(): ColumnBuilder<K, N, D, E, P, number, W>;
421
+ /**
422
+ * Surface this exact-precision column (`bigint()` or `numeric()`) as a
423
+ * `string`: the value Postgres sent, DECLARED rather than left as the
424
+ * default.
425
+ */
426
+ asDecimal(): ColumnBuilder<K, N, D, E, P, string, W>;
427
+ private withCodec;
274
428
  /**
275
429
  * Declare how this column's value is projected in and out of the process.
276
430
  *
@@ -289,12 +443,29 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
289
443
  * A transform is a PROJECTION, never a constraint: it lives only in this
290
444
  * process, so it can neither validate nor migrate what is stored.
291
445
  */
292
- asNumber(): ColumnBuilder<K, N, D, E, P, number, W>;
293
- asDecimal(): ColumnBuilder<K, N, D, E, P, string, W>;
294
- private withCodec;
295
446
  transform<Target>(fns: ColumnTransform<Target>): ColumnBuilder<K, N, D, E, P, Target, W>;
296
447
  }
448
+ /**
449
+ * Extracts the TypeScript value type for a column, respecting nullability.
450
+ * - "uuid" | "text" | "timestamp" | "bigint" | "numeric" | "date" → string (or string | null when N = true)
451
+ * - "sql" → the value its sqlType() declares ({@link SqlTypeValue}: string, number or unknown)
452
+ * - "<type>[]" → an array of the element's value
453
+ * Note: bigint/numeric surface as string — JS number loses precision past 2^53,
454
+ * and the driver hands int8/numeric over as strings. App code uses
455
+ * BigInt(row.amount) for bigint, or a decimal lib for numeric.
456
+ * - "integer" → number
457
+ * - "boolean" → boolean
458
+ * - "jsonb" → P (the dev-supplied payload shape from jsonb<T>(), else unknown)
459
+ * - "enum" → E (the union of literal values)
460
+ *
461
+ * A declared `.transform<T>()` OVERRIDES the table above: the column then
462
+ * surfaces as T (or T | null when nullable), because that is the value the
463
+ * application is handed. Nullability is still the column's, not the
464
+ * transform's — `fromDb` is not called for a NULL.
465
+ */
297
466
  export type ColValue<C> = C extends ColumnBuilder<ColumnType, infer N, boolean, unknown, unknown, infer T> ? [unknown] extends [T] ? ColStoredValue<C> : N extends true ? T | null : T : never;
467
+ /** An array column's element, as the row type hands it over: the same value
468
+ * as the scalar column of that type, element by element. */
298
469
  type ArrayElementValue<K, E> = K extends 'integer[]' ? number : K extends 'boolean[]' ? boolean : K extends 'enum[]' ? E : string;
299
470
  /** The value as the DATABASE hands it over — the branch table above, before any
300
471
  * transform. This is what a column's `fromDb` receives. */
@@ -314,24 +485,60 @@ export declare function integer(): ColumnBuilder<'integer', false, false, never>
314
485
  /**
315
486
  * Create a BIGINT column (Postgres int8, max ~9.2×10^18).
316
487
  * Surfaces as `string` in row/insert types — JS number loses precision past 2^53
317
- * and pgx/PostgREST serialize int8 as a JSON string. Use BigInt(row.column) in app code.
488
+ * and the driver hands int8 over as a string. Use BigInt(row.column) in app code,
489
+ * or `.asNumber()` below 2^53.
318
490
  */
319
491
  export declare function bigint(): ColumnBuilder<'bigint', false, false, never>;
320
492
  /**
321
493
  * Create a NUMERIC column (Postgres `numeric`/`decimal`, arbitrary precision).
322
494
  * For exact fractional values (money with cents as a decimal, rates, weights)
323
495
  * where int4/int8 don't fit. Surfaces as `string` in row/insert types — JS
324
- * number can't hold arbitrary-precision decimals without rounding, and
325
- * pgx/PostgREST serialize numeric as a JSON string. Parse with a decimal lib
326
- * (or BigInt for scaled integers) in app code.
496
+ * number can't hold arbitrary-precision decimals without rounding, and the
497
+ * driver hands numeric over as a string. Parse with a decimal lib (or BigInt
498
+ * for scaled integers) in app code, or declare `.asNumber()` when a JS number
499
+ * is precise enough.
327
500
  */
328
501
  export declare function numeric(opts?: {
329
502
  precision: number;
330
503
  scale?: number;
331
504
  }): ColumnBuilder<'numeric', false, false, never>;
505
+ /**
506
+ * Create a DATE column: a calendar day, surfaced as `"YYYY-MM-DD"`.
507
+ *
508
+ * A string and not a JS `Date`: a `Date` is an instant, and turning a day into
509
+ * one picks a time zone — `new Date("2026-09-28")` is the 27th in New York.
510
+ * `.defaultNow()` is today (`CURRENT_DATE`).
511
+ */
332
512
  export declare function date(): ColumnBuilder<'date', false, false, never>;
513
+ /**
514
+ * A column of a Postgres type the DSL does not offer: `inet`, `interval`,
515
+ * `citext`, `tsrange`, `character varying(20)`, `double precision`… The
516
+ * escape hatch: the value is surfaced as the text Postgres prints for it (what
517
+ * psql shows), except `smallint`/`real`/`double precision`, which are numbers,
518
+ * and `json`, which is the JSON value. `timestamp without time zone` and
519
+ * `bytea` are refused: use `timestamp()` (a `timestamptz`), and keep bytes in
520
+ * Storage.
521
+ *
522
+ * A type the DSL has its own builder for (`text`, `int8`, `jsonb`, …) is
523
+ * refused too: declare it with that builder, so each type has one spelling.
524
+ *
525
+ * Spell the type the way Postgres does (`character varying(20)`, not
526
+ * `varchar(20)`): the deploy asks Postgres and refuses any other spelling,
527
+ * telling you the right one. Two spellings would read as a type change on
528
+ * every push.
529
+ */
333
530
  export declare function sqlType<const S extends string>(type: S): ColumnBuilder<'sql', false, false, never, SqlTypeValue<S>>;
531
+ /**
532
+ * The Postgres types an sqlType() column hands over as a JS NUMBER, as the
533
+ * driver reads them. Every sqlType() that is not here
534
+ * or in {@link SQL_JSON_TYPES} is the text Postgres prints for the value. The
535
+ * names are the canonical spellings the deploy insists on.
536
+ */
334
537
  export declare const SQL_NUMBER_TYPES: ReadonlySet<string>;
538
+ /**
539
+ * The Postgres types an sqlType() column hands over as a JSON value rather
540
+ * than as text. The names are the canonical spellings the deploy insists on.
541
+ */
335
542
  export declare const SQL_JSON_TYPES: ReadonlySet<string>;
336
543
  /** What an sqlType() column surfaces as, from its Postgres type. */
337
544
  export type SqlTypeValue<S extends string> = S extends 'smallint' | 'real' | 'double precision' ? number : S extends 'json' ? unknown : string;
@@ -1,8 +1,33 @@
1
1
  import type { PolicyExprRef } from "./policy.js";
2
2
  import type { ColumnMap, TableHandle } from "./schema.js";
3
+ /**
4
+ * The symbol key under which a reference keeps its target table object (or a
5
+ * callback that returns it). It is never serialized.
6
+ *
7
+ * The target's name is known when the reference is declared, but its schema is
8
+ * not: `defineSchema` assigns schemas, and it runs after the `defineTable`
9
+ * callbacks. So the table object itself is carried along, and `defineSchema`
10
+ * writes the schema-qualified name from it, by the same rule it uses for a
11
+ * column's `.references()`.
12
+ *
13
+ * It is a symbol key because `JSON.stringify` skips symbol keys, so the object
14
+ * never appears in the serialized schema.
15
+ */
3
16
  export declare const REF_TARGET: unique symbol;
17
+ /** An `ON UPDATE` / `ON DELETE` action: the closed set Postgres accepts. */
4
18
  export type FkAction = "cascade" | "restrict" | "set null" | "set default" | "no action";
19
+ /** A foreign key's `MATCH` type. `partial` is left out because Postgres does not implement it yet. */
5
20
  export type FkMatch = "simple" | "full";
21
+ /**
22
+ * A table-level FOREIGN KEY, as `defineTable` stores it: what
23
+ * `foreignKey(name)` builds, or a plain object you can write under a table's
24
+ * `foreignKeys` yourself.
25
+ *
26
+ * `columns` and `refColumns` pair up by position and must have the same
27
+ * length. `refTable` is the target table's key: bare for a table in `public`,
28
+ * `schema.table` otherwise. `onUpdate` and `onDelete` default to
29
+ * `"no action"`, `match` to `"simple"`.
30
+ */
6
31
  export interface ForeignKeyDef {
7
32
  name: string;
8
33
  columns: string[];
@@ -14,13 +39,59 @@ export interface ForeignKeyDef {
14
39
  /** Only `false`: no index derived for a one-column key (the column's references() has the same option). */
15
40
  index?: false;
16
41
  }
42
+ /**
43
+ * Builds one table-level FOREIGN KEY, which can span several columns. Start it
44
+ * with `foreignKey(name)` inside a table's `foreignKeys` callback.
45
+ *
46
+ * ```ts
47
+ * foreignKeys: (c) => [
48
+ * foreignKey("settlement_entry_household")
49
+ * .on(c.col("household_id"), c.col("entry_id"))
50
+ * .references(entries, "household_id", "id")
51
+ * .onDelete("cascade"),
52
+ * ]
53
+ * ```
54
+ *
55
+ * Why a column's `.references()` is not enough: it covers one column and one
56
+ * target. What multi-tenant integrity needs is
57
+ * `FOREIGN KEY (household_id, entry_id) REFERENCES entries (household_id, id)`:
58
+ * the child's parent must not only exist, it must belong to the SAME tenant. A
59
+ * per-column reference cannot express that.
60
+ *
61
+ * The two can be used together and neither drops the other: a key declared
62
+ * here is tracked by its own name, a column's by `<table>_<column>_fkey`. One
63
+ * table can have both.
64
+ */
17
65
  export declare class ForeignKeyBuilder {
18
66
  readonly _def: ForeignKeyDef;
19
67
  constructor(name: string);
68
+ /**
69
+ * The local columns, as `c.col(...)` references. Their order is kept: they
70
+ * pair up one-to-one with the target columns given to `.references()`. An
71
+ * expression cannot be part of a foreign key.
72
+ */
20
73
  on(...cols: PolicyExprRef[]): this;
74
+ /**
75
+ * The target table and its columns. Pass the table itself, or a callback
76
+ * that returns it (`() => entries`) for a table declared later in the file
77
+ * or one that points back at this table.
78
+ *
79
+ * Give at least one target column, and exactly as many as `.on()` has. The
80
+ * count is checked when the table is declared, so a mismatched key fails as
81
+ * soon as the schema file runs and never reaches a deploy.
82
+ */
21
83
  references<C extends ColumnMap, N extends string>(table: TableHandle<C, N> | (() => TableHandle<C, N>), ...cols: (Extract<keyof C, string>)[]): this;
84
+ /** The `ON UPDATE` action. Defaults to `"no action"`. */
22
85
  onUpdate(action: FkAction): this;
86
+ /** The `ON DELETE` action. Defaults to `"no action"`. */
23
87
  onDelete(action: FkAction): this;
88
+ /**
89
+ * Sets the `MATCH` type. With `"full"` (`MATCH FULL`) the constraint is
90
+ * checked unless ALL referencing columns are NULL. With the default
91
+ * `"simple"` (`MATCH SIMPLE`) a single NULL column is enough to skip the
92
+ * check. That can be relied on deliberately, but in an ordinary foreign key
93
+ * it is usually not what you want.
94
+ */
24
95
  match(m: FkMatch): this;
25
96
  /**
26
97
  * `.index(false)`: Palgres indexes a one-column foreign key's column unasked
@@ -29,5 +100,24 @@ export declare class ForeignKeyBuilder {
29
100
  */
30
101
  index(on: false): this;
31
102
  }
103
+ /**
104
+ * Starts a table-level foreign key named `name`, for a table's `foreignKeys`
105
+ * callback. Constraints are matched by name across deploys.
106
+ *
107
+ * @param name The constraint name. It must not be empty.
108
+ * @returns A {@link ForeignKeyBuilder}: chain `.on()` and `.references()`, and optionally `.onDelete()`, `.onUpdate()`, `.match()`, `.index(false)`.
109
+ */
32
110
  export declare function foreignKey(name: string): ForeignKeyBuilder;
111
+ /**
112
+ * Turns a `foreignKey(...)` builder, or a plain {@link ForeignKeyDef}, into a
113
+ * `ForeignKeyDef` and checks that it is consistent. It throws when the key has
114
+ * no local column (`.on()`), no target (`.references()`), or a different
115
+ * number of local and target columns.
116
+ *
117
+ * `defineTable` calls it for every entry of `foreignKeys`. A deploy refuses a
118
+ * column-count mismatch as well, but only once it runs against the live
119
+ * database; here the mistake fails as soon as the schema file runs, and the
120
+ * broken declaration never leaves your machine. Both checks are kept: one for
121
+ * the author, one for the deploy.
122
+ */
33
123
  export declare function toForeignKeyDef(b: ForeignKeyBuilder | ForeignKeyDef): ForeignKeyDef;