palgres 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +11 -5
- package/dist/chunks/{FAZVO74A.js → DNJLPMEG.js} +2 -2
- package/dist/chunks/FXAZO7GY.js +2 -0
- package/dist/chunks/{OBLAMOLO.js → OBMTT3YH.js} +2 -2
- package/dist/dsl.js +2 -2
- package/dist/index.js +2 -2
- package/dist/testing.js +2 -2
- package/package.json +2 -2
- package/types/palgres/src/dsl/columns.d.ts +234 -27
- package/types/palgres/src/dsl/foreign-key.d.ts +90 -0
- package/types/palgres/src/dsl/policy.d.ts +290 -1
- package/types/palgres/src/dsl/raw.d.ts +56 -0
- package/types/palgres/src/dsl/schema.d.ts +215 -69
- package/dist/chunks/7255GQJI.js +0 -2
|
@@ -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()
|
|
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
|
-
*
|
|
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
|
-
*
|
|
216
|
-
*
|
|
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
|
|
235
|
-
*
|
|
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
|
|
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
|
-
*
|
|
264
|
-
*
|
|
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
|
|
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
|
-
*
|
|
326
|
-
*
|
|
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;
|