@typesql/pg 0.2.7 → 0.2.9

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.
@@ -5,33 +5,19 @@ import type {
5
5
  } from "./introspect.js";
6
6
 
7
7
  export interface GenerateOptions {
8
- /** Name of the exported schema interface. Default `"DB"`. */
9
8
  readonly typeName?: string;
10
- /** Module the column helpers are imported from. Default `"@typesql/core"`. */
11
9
  readonly importFrom?: string;
12
- /** Extra lines after the helper import, e.g. `import type { HistoryItem } from "./json.js";`. */
13
10
  readonly imports?: readonly string[];
14
- /**
15
- * TypeScript types of `json` / `jsonb` columns: a map keyed `"table.column"` (public) or
16
- * `"schema.table.column"`, or a function returning the TS type source for a column (or
17
- * `undefined`). Such a column is declared as `Json<T>`: still jsonb to the SQL, read as `T`,
18
- * written with `sql.json(value)`. Other columns are an error (use `column` for those).
19
- */
20
11
  readonly columnTypes?:
21
12
  | Readonly<Record<string, string>>
22
13
  | ((
23
14
  column: IntrospectedColumn,
24
15
  relation: IntrospectedRelation,
25
16
  ) => string | undefined);
26
- /**
27
- * Full control over one column: return the TypeScript source of its descriptor
28
- * (`Col<"jsonb">`, a helper of your own, ...) or `undefined` to keep the default.
29
- */
30
17
  readonly column?: (
31
18
  column: IntrospectedColumn,
32
19
  relation: IntrospectedRelation,
33
20
  ) => string | undefined;
34
- /** Emit table and column comments and notes as JSDoc. Default `true`. */
35
21
  readonly comments?: boolean;
36
22
  }
37
23
 
@@ -76,11 +62,6 @@ function columnNotes(c: IntrospectedColumn): string[] {
76
62
  return notes;
77
63
  }
78
64
 
79
- /**
80
- * TypeScript source declaring the TypeSQL schema of an introspected database: `public`
81
- * relations at the top level, other schemas nested (`DB["auth"]["sessions"]`), views and
82
- * materialized views as `View<…>`, enums under `$types`.
83
- */
84
65
  export function generateSource(
85
66
  db: IntrospectedDatabase,
86
67
  options: GenerateOptions = {},
@@ -91,7 +72,6 @@ export function generateSource(
91
72
  const used = new Set<string>();
92
73
  const usedKeys = new Set<string>();
93
74
 
94
- /** The TS type `columnTypes` gives a column, if any. */
95
75
  const customType = (
96
76
  rel: IntrospectedRelation,
97
77
  col: IntrospectedColumn,
@@ -101,7 +81,6 @@ export function generateSource(
101
81
  let ts: string | undefined;
102
82
  if (typeof columnTypes === "function") ts = columnTypes(col, rel);
103
83
  else {
104
- // The keys contain a dot, so no key is a property every object has.
105
84
  const k =
106
85
  rel.schema === "public" && !(qualified in columnTypes)
107
86
  ? short
@@ -117,7 +96,6 @@ export function generateSource(
117
96
  return ts;
118
97
  };
119
98
 
120
- /** `Json<T>`, `Json<T, { nullable: true }>`, `Json<T, {}, "json">`, … */
121
99
  const jsonDescriptor = (col: IntrospectedColumn, ts: string): string => {
122
100
  const flags = [
123
101
  ...(col.nullable ? ["nullable: true"] : []),
@@ -185,8 +163,6 @@ export function generateSource(
185
163
  }
186
164
 
187
165
  if (db.enums.length > 0) {
188
- // TypeSQL's enum registry is flat (`Col<"mood">`, schema qualifiers are dropped), so enums
189
- // of the same name in different schemas share an entry.
190
166
  const byName = new Map<
191
167
  string,
192
168
  { values: Set<string>; schemas: string[] }
@@ -237,7 +213,6 @@ export function generateSource(
237
213
  "}",
238
214
  "",
239
215
  ];
240
- // A function has no keys of its own.
241
216
  const unknownKeys = Object.keys(columnTypes).filter((k) => !usedKeys.has(k));
242
217
  if (unknownKeys.length > 0 && options.column === undefined) {
243
218
  throw new Error(
@@ -10,23 +10,13 @@ export type RelationKind =
10
10
 
11
11
  export interface IntrospectedColumn {
12
12
  readonly name: string;
13
- /**
14
- * Canonical Postgres type name as TypeSQL spells it: `int4`, `timestamptz`, `varchar`, an enum
15
- * name, `${T}[]` for arrays. Domains are replaced by their base type; `citext` is `text`.
16
- */
17
13
  readonly type: string;
18
- /** The column's type as Postgres prints it (`character varying(40)`, a domain name, ...). */
19
14
  readonly declaredType: string;
20
- /** The domain the column is declared with, if any (`public.positive`). */
21
15
  readonly domain: string | null;
22
16
  readonly nullable: boolean;
23
- /** Optional on INSERT: a DEFAULT, serial, identity or generated column. Always false for views. */
24
17
  readonly hasDefault: boolean;
25
- /** `GENERATED ALWAYS AS IDENTITY` / `GENERATED BY DEFAULT AS IDENTITY`. */
26
18
  readonly identity: "always" | "by default" | null;
27
- /** The expression of a `GENERATED ALWAYS AS (…) STORED` column. */
28
19
  readonly generated: string | null;
29
- /** The DEFAULT expression. */
30
20
  readonly default: string | null;
31
21
  readonly comment: string | null;
32
22
  }
@@ -42,32 +32,19 @@ export interface IntrospectedRelation {
42
32
  export interface IntrospectedEnum {
43
33
  readonly schema: string;
44
34
  readonly name: string;
45
- /** Labels in sort order. */
46
35
  readonly values: readonly string[];
47
36
  }
48
37
 
49
38
  export interface IntrospectedDatabase {
50
- /** `server_version`, e.g. `17.2`. */
51
39
  readonly version: string;
52
- /** Schemas that were read, in order. */
53
40
  readonly schemas: readonly string[];
54
41
  readonly relations: readonly IntrospectedRelation[];
55
- /** Enums of the read schemas and enums their columns use. */
56
42
  readonly enums: readonly IntrospectedEnum[];
57
43
  }
58
44
 
59
45
  export interface IntrospectOptions {
60
- /** Schemas to read. Default: every schema except `pg_catalog`, `information_schema` and `pg_*`. */
61
46
  readonly schemas?: readonly string[];
62
- /**
63
- * Relations to leave out: names (`kysely_migration`) or `schema.name`, with `*` matching any
64
- * run of characters (`kysely_migration*`, `audit.*`).
65
- */
66
47
  readonly exclude?: readonly string[];
67
- /**
68
- * View columns to treat as NOT NULL when the analysis cannot prove it, called with the view's
69
- * name and the column's name.
70
- */
71
48
  readonly viewNotNull?: NullabilityOptions["notNull"];
72
49
  }
73
50
 
@@ -87,7 +64,6 @@ const KINDS: Readonly<Record<string, RelationKind>> = {
87
64
  f: "foreign table",
88
65
  };
89
66
 
90
- /** Postgres type names TypeSQL spells differently. */
91
67
  const TYPE_ALIASES: Readonly<Record<string, string>> = { citext: "text" };
92
68
 
93
69
  interface TypeRow {
@@ -104,7 +80,6 @@ interface TypeRow {
104
80
  interface ResolvedType {
105
81
  readonly type: string;
106
82
  readonly notnull: boolean;
107
- /** The type under the domains and arrays: the enum the column uses, if it is one. */
108
83
  readonly scalarOid: string;
109
84
  readonly domain: string | null;
110
85
  }
@@ -112,7 +87,6 @@ interface ResolvedType {
112
87
  function resolver(types: ReadonlyMap<string, TypeRow>) {
113
88
  const resolve = (oid: string): ResolvedType => {
114
89
  const t = types.get(oid);
115
- // A type created after the types were read (a concurrent CREATE TYPE and CREATE TABLE)
116
90
  if (!t)
117
91
  return { type: "unknown", notnull: false, scalarOid: oid, domain: null };
118
92
  if (t.typtype === "d") {
@@ -123,8 +97,6 @@ function resolver(types: ReadonlyMap<string, TypeRow>) {
123
97
  domain: `${t.schema}.${t.name}`,
124
98
  };
125
99
  }
126
- // `typelem` is "0" (no type) for scalars; fixed-length types like `point` have one but are
127
- // not its array type.
128
100
  const elem = types.get(t.elem);
129
101
  if (elem && elem.array === t.oid) {
130
102
  const inner = resolve(elem.oid);
@@ -145,13 +117,8 @@ function resolver(types: ReadonlyMap<string, TypeRow>) {
145
117
  return resolve;
146
118
  }
147
119
 
148
- /** Default schema filter: everything but the system schemas. */
149
120
  const USER_SCHEMAS = `n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname NOT LIKE 'pg\\_%'`;
150
121
 
151
- /**
152
- * Read tables, views, materialized views, foreign tables and enums of a live database through
153
- * any node-postgres-style client (`Client`, `Pool`, `PoolClient`).
154
- */
155
122
  export async function introspect(
156
123
  client: PgQueryable,
157
124
  options: IntrospectOptions = {},
@@ -244,7 +211,6 @@ export async function introspect(
244
211
  ...(options.viewNotNull && { notNull: options.viewNotNull }),
245
212
  },
246
213
  );
247
- /** Types the columns use: their enums are read even outside `schemas`. */
248
214
  const usedTypes = new Set<string>();
249
215
  const relations: IntrospectedRelation[] = [];
250
216
  for (const rel of relRows) {
@@ -268,7 +234,6 @@ export async function introspect(
268
234
  declaredType: c.declared,
269
235
  domain: t.domain,
270
236
  nullable: viewNotNull ? !viewNotNull[i] : !(c.notnull || t.notnull),
271
- // A generated column's expression is its `default` row in pg_attrdef.
272
237
  hasDefault: !isView && (c.default !== null || identity !== null),
273
238
  identity,
274
239
  generated,
@@ -1,45 +1,20 @@
1
- /**
2
- * Nullability of view columns, derived from the view's query tree.
3
- *
4
- * Postgres records no NOT NULL information for view (or materialized view) columns. Its rewrite
5
- * rule (`pg_rewrite.ev_action`, the `_RETURN` rule) holds the parsed query, though, and from
6
- * PostgreSQL 16 every column reference in it carries `varnullingrels`, the outer joins that can
7
- * null it. A view column is NOT NULL here only when its expression provably cannot be NULL: a
8
- * NOT NULL table column that no outer join nulls, a non-NULL constant, a cast or a strict
9
- * operator over such values, a CASE whose every branch is NOT NULL, `count(*)`, ... Anything
10
- * else (function calls outside a short list, scalar subqueries, set-returning function columns)
11
- * is nullable. On PostgreSQL 15 and older every column reference counts as nullable.
12
- *
13
- * This module has no imports so that scripts/pg-catalog-from-pg.ts can load it with Node's
14
- * type stripping.
15
- */
16
-
17
- /** Runs a query: node-postgres' `client.query(text, values)` fits. */
18
1
  export type QueryFn = (
19
2
  text: string,
20
3
  values?: unknown[],
21
4
  ) => Promise<{ readonly rows: readonly any[] }>;
22
5
 
23
6
  export interface NullabilityOptions {
24
- /**
25
- * Columns known never to be NULL that the analysis cannot prove (typically columns of a
26
- * set-returning function). Called with the view's name and the column's name.
27
- */
28
7
  readonly notNull?: (view: string, column: string) => boolean;
29
8
  }
30
9
 
31
- /** A node of a parsed `pg_node_tree`: `{ _: "VAR", varno: "1", … }`. */
32
10
  export interface Node {
33
11
  readonly _: string;
34
12
  readonly [field: string]: NodeValue;
35
13
  }
36
14
  export type NodeValue = Node | readonly NodeValue[] | string | null;
37
15
 
38
- // ───────────────────────────── pg_node_tree reader ─────────────────────────────
39
-
40
16
  type Token = string | { readonly str: string };
41
17
 
42
- /** Tokens of the text form of a node tree (`outfuncs.c`). `[ … ]` datum bytes are skipped. */
43
18
  function tokenize(s: string): Token[] {
44
19
  const out: Token[] = [];
45
20
  let i = 0;
@@ -79,7 +54,6 @@ function tokenize(s: string): Token[] {
79
54
  return out;
80
55
  }
81
56
 
82
- /** Parse a node tree into plain values: nodes as objects, lists as arrays, `<>` as null. */
83
57
  export function parseNodeTree(text: string): NodeValue {
84
58
  const toks = tokenize(text);
85
59
  let p = 0;
@@ -91,7 +65,7 @@ export function parseNodeTree(text: string): NodeValue {
91
65
  const node: Record<string, NodeValue> = { _: toks[p++] as string };
92
66
  while (p < toks.length && toks[p] !== "}") {
93
67
  const label = toks[p++];
94
- if (!isLabel(label)) continue; // a datum's length before its skipped bytes
68
+ if (!isLabel(label)) continue;
95
69
  if (toks[p] === "}" || isLabel(toks[p])) {
96
70
  node[(label as string).slice(1)] = null;
97
71
  continue;
@@ -120,9 +94,6 @@ const node = (x: NodeValue | undefined): Node | null =>
120
94
  const str = (x: NodeValue | undefined): string | null =>
121
95
  typeof x === "string" ? x : null;
122
96
 
123
- // ───────────────────────────── function knowledge ─────────────────────────────
124
-
125
- /** `pg_catalog` functions that return a non-NULL value for non-NULL arguments. */
126
97
  const NON_NULL_FUNCTIONS = new Set([
127
98
  "current_database",
128
99
  "current_user",
@@ -167,7 +138,6 @@ const NON_NULL_FUNCTIONS = new Set([
167
138
  "age",
168
139
  ]);
169
140
 
170
- /** `pg_stat_get_*` accessors returning a number report 0 when no statistics exist, except these. */
171
141
  const NULLABLE_STAT_FUNCTIONS = new Set([
172
142
  "pg_stat_get_db_checksum_failures",
173
143
  "pg_stat_get_backend_pid",
@@ -179,9 +149,7 @@ const isNonNullStatFunction = (name: string, ret: string): boolean =>
179
149
  (ret === "int8" || ret === "int4" || ret === "float8") &&
180
150
  !NULLABLE_STAT_FUNCTIONS.has(name);
181
151
 
182
- /** Aggregates that never return NULL. */
183
152
  const NON_NULL_AGGREGATES = new Set(["count"]);
184
- /** Window functions that never return NULL. */
185
153
  const NON_NULL_WINDOW_FUNCTIONS = new Set([
186
154
  "count",
187
155
  "row_number",
@@ -191,12 +159,6 @@ const NON_NULL_WINDOW_FUNCTIONS = new Set([
191
159
  "cume_dist",
192
160
  ]);
193
161
 
194
- /**
195
- * Set-returning functions in FROM and their output columns (1-based) that are never NULL. A
196
- * strict set-returning function returns no rows for NULL arguments, so the arguments do not
197
- * matter. `jsonb_each` values are `jsonb` (a JSON null is `'null'::jsonb`, not SQL NULL);
198
- * the `_text` variants turn a JSON null into SQL NULL.
199
- */
200
162
  const NON_NULL_SRF_COLUMNS: Readonly<Record<string, readonly number[]>> = {
201
163
  generate_series: [1],
202
164
  generate_subscripts: [1],
@@ -212,11 +174,6 @@ const NON_NULL_SRF_COLUMNS: Readonly<Record<string, readonly number[]>> = {
212
174
  regexp_split_to_table: [1],
213
175
  };
214
176
 
215
- /**
216
- * `pg_catalog` operators that can return NULL for non-NULL operands: JSON field and path access
217
- * (a missing key) and jsonpath matches (an unknown result). Other built-in operators are strict
218
- * and return a value for non-NULL operands; operators outside `pg_catalog` count as nullable.
219
- */
220
177
  const NULLABLE_OPERATORS = new Set(["->", "->>", "#>", "#>>", "@@", "@?"]);
221
178
 
222
179
  interface Proc {
@@ -230,9 +187,6 @@ interface Att {
230
187
  readonly relkind: string;
231
188
  }
232
189
 
233
- // ───────────────────────────── analysis ─────────────────────────────
234
-
235
- /** Per-column NOT NULL of views and materialized views; caches what it reads. */
236
190
  export class ViewNullability {
237
191
  private readonly query: QueryFn;
238
192
  private readonly options: NullabilityOptions;
@@ -247,11 +201,8 @@ export class ViewNullability {
247
201
  this.options = options;
248
202
  }
249
203
 
250
- /** NOT NULL flags of the view's columns, in column order. `relid` is the view's `pg_class.oid`. */
251
204
  notNull(relid: string | number): Promise<boolean[]> {
252
205
  const key = String(relid);
253
- // CREATE OR REPLACE VIEW allows a view that reads itself through others: waiting for its own
254
- // analysis would never end
255
206
  if (this.analyzing.has(key)) return Promise.resolve([]);
256
207
  let cached = this.views.get(key);
257
208
  if (!cached) {
@@ -307,7 +258,6 @@ export class ViewNullability {
307
258
  return this.operators;
308
259
  }
309
260
 
310
- /** A built-in operator that returns a value for non-NULL operands. */
311
261
  private async strictOperator(e: Node): Promise<boolean> {
312
262
  const op = (await this.loadOperators()).get(String(e["opno"]));
313
263
  return op?.system === true && !NULLABLE_OPERATORS.has(op.name);
@@ -317,7 +267,6 @@ export class ViewNullability {
317
267
  let cached = this.atts.get(relid);
318
268
  if (!cached) {
319
269
  cached = this.query(
320
- // NOT NULL: the column's constraint, or a NOT NULL domain anywhere in its domain chain.
321
270
  `SELECT a.attnum, c.relkind,
322
271
  a.attnotnull OR coalesce((
323
272
  WITH RECURSIVE chain(oid, nn) AS (
@@ -335,7 +284,6 @@ export class ViewNullability {
335
284
  return cached;
336
285
  }
337
286
 
338
- /** Is output column `attno` (1-based) of a (sub)query NOT NULL? */
339
287
  private async queryColumn(
340
288
  value: NodeValue | undefined,
341
289
  attno: number,
@@ -343,8 +291,6 @@ export class ViewNullability {
343
291
  const query = node(value);
344
292
  if (!query) return false;
345
293
  if (query["setOperations"]) {
346
- // UNION / INTERSECT / EXCEPT: every arm must be NOT NULL. The range table of a set
347
- // operation holds its arms, as subqueries, and nothing else.
348
294
  for (const r of list(query["rtable"]))
349
295
  if (!(await this.queryColumn(node(r)?.["subquery"], attno)))
350
296
  return false;
@@ -358,17 +304,14 @@ export class ViewNullability {
358
304
  }
359
305
 
360
306
  private async variable(v: Node, query: Node): Promise<boolean> {
361
- // No varnullingrels (PostgreSQL ≤ 15): outer joins are not visible, so nothing is proven.
362
307
  if (!("varnullingrels" in v) || v["varlevelsup"] !== "0") return false;
363
- if (list(v["varnullingrels"]).length > 1) return false; // `(b 3)`: an outer join can null it
364
- // ROLLUP / CUBE / GROUPING SETS null the grouped columns (PostgreSQL ≤ 17 does not mark them).
308
+ if (list(v["varnullingrels"]).length > 1) return false;
365
309
  if (list(query["groupingSets"]).length > 0) return false;
366
310
  const rte = node(list(query["rtable"])[Number(v["varno"]) - 1]);
367
311
  const attno = Number(v["varattno"]);
368
312
  if (!rte || !(attno > 0)) return false;
369
313
  switch (rte["rtekind"]) {
370
314
  case "0": {
371
- // relation
372
315
  const relid = str(rte["relid"])!;
373
316
  const att = (await this.columns(relid)).find((a) => a.attnum === attno);
374
317
  if (!att) return false;
@@ -376,19 +319,18 @@ export class ViewNullability {
376
319
  return (await this.notNull(relid))[attno - 1] ?? false;
377
320
  return att.notnull;
378
321
  }
379
- case "1": // subquery
322
+ case "1":
380
323
  return this.queryColumn(rte["subquery"], attno);
381
- case "2": // join alias variable
324
+ case "2":
382
325
  return this.expr(list(rte["joinaliasvars"])[attno - 1], query);
383
- case "3": // function in FROM
326
+ case "3":
384
327
  return this.functionColumn(rte, attno);
385
- case "9": // PostgreSQL 18 GROUP BY expressions
328
+ case "9":
386
329
  return this.expr(list(rte["groupexprs"])[attno - 1], query);
387
330
  }
388
- return false; // VALUES, CTEs, table functions, ...
331
+ return false;
389
332
  }
390
333
 
391
- /** Column `attno` of a single set-returning function in FROM (`WITH ORDINALITY` adds a NOT NULL counter). */
392
334
  private async functionColumn(rte: Node, attno: number): Promise<boolean> {
393
335
  const functions = list(rte["functions"]);
394
336
  const only = node(functions[0]);
@@ -432,10 +374,8 @@ export class ViewNullability {
432
374
  case "FUNCEXPR": {
433
375
  if (e["funcretset"] !== "false") return false;
434
376
  const proc = (await this.loadProcs()).get(String(e["funcid"]));
435
- // format_type(type, typmod) is not strict: a NULL typmod still gives a name.
436
377
  if (proc?.system && proc.name === "format_type")
437
378
  return this.expr(list(e["args"])[0], query);
438
- // Explicit and implicit casts (funcformat 1, 2) keep a non-NULL input non-NULL.
439
379
  const castLike = e["funcformat"] === "1" || e["funcformat"] === "2";
440
380
  const known =
441
381
  proc?.system === true &&
@@ -448,8 +388,6 @@ export class ViewNullability {
448
388
  case "OPEXPR":
449
389
  return (await this.strictOperator(e)) && this.all(e["args"], query);
450
390
  case "SCALARARRAYOPEXPR": {
451
- // `x = ANY (arr)` is NULL when there is no match and `arr` holds a NULL: only an
452
- // `ARRAY[…]` of NOT NULL elements is safe.
453
391
  const [left, right] = list(e["args"]);
454
392
  const array = node(right);
455
393
  return (
@@ -474,7 +412,7 @@ export class ViewNullability {
474
412
  for (const w of list(e["args"]))
475
413
  if (!(await this.expr(node(w)?.["result"], query))) return false;
476
414
  return this.expr(e["defresult"], query);
477
- case "SUBLINK": // EXISTS (0) and ARRAY (6) sublinks are never NULL
415
+ case "SUBLINK":
478
416
  return e["subLinkType"] === "0" || e["subLinkType"] === "6";
479
417
  case "AGGREF": {
480
418
  const proc = (await this.loadProcs()).get(String(e["aggfnoid"]));
package/src/type-map.ts CHANGED
@@ -1,56 +1,9 @@
1
1
  import type { DefaultParamTypeMap, DefaultTypeMap } from "@typesql/core";
2
2
 
3
- /**
4
- * The object node-postgres returns for an `interval` column (package `postgres-interval` 1.x,
5
- * wired up by `pg-types`). Fields are only present when non-zero; `toPostgres()` renders the
6
- * value back to Postgres syntax and `toISO()`/`toISOString()` produce an ISO 8601 duration.
7
- *
8
- * Written inline in `PgTypeMap` (this alias only reads it back), so row types stay plain
9
- * object types: declaration emit in a package without a direct `@typesql/pg` dependency does
10
- * not need to name `PgInterval` (TS2883).
11
- */
12
3
  export type PgInterval = PgTypeMap["interval"];
13
4
 
14
- /**
15
- * Canonical Postgres type name → TS type as `pg` 8.x returns it with its default type
16
- * parsers (`pg-types` 2.x, text protocol). Verified against `pg-types/lib/textParsers.js`:
17
- *
18
- * number int2 (21), int4 (23), oid (26), float4 (700), float8 (701)
19
- * string int8 (20) — `parseBigInteger` keeps the digits as a string, numeric (1700),
20
- * money, text, varchar, bpchar, char, name, uuid, time, timetz, inet, cidr,
21
- * macaddr, macaddr8, xml, bit, varbit, tsvector, tsquery, every range type,
22
- * regclass/regtype/regproc, line/lseg/box/path/polygon — anything without a
23
- * registered parser comes back as the raw text representation
24
- * boolean bool (16)
25
- * Date date (1082), timestamp (1114), timestamptz (1184)
26
- * (`'infinity'`/`'-infinity'` come back as the numbers Infinity/-Infinity)
27
- * unknown json (114), jsonb (3802): `JSON.parse`d
28
- * Buffer bytea (17)
29
- * object interval (1186) → PgInterval, point (600) → { x, y }, circle (718) → { x, y, radius }
30
- *
31
- * `DefaultTypeMap` in core already describes pg; this interface only narrows the entries
32
- * that differ in detail (`Buffer` instead of `Uint8Array`, the interval object's methods,
33
- * `record`/`void` as their raw text).
34
- *
35
- * Arrays: core maps `T[]` element-wise (`TsTypeOf` checks the `[]` suffix before the map),
36
- * which matches pg for the array types that `pg-types` registers: bool[], bytea[], int2[],
37
- * int4[], int8[] (string[]), oid[], float4[], float8[], point[], bpchar[], varchar[], text[],
38
- * macaddr[], inet[], cidr[], date[], timestamp[], timestamptz[], interval[], json[], jsonb[],
39
- * uuid[], money[], time[], timetz[], numrange[]. Two kinds of mismatch exist and cannot be
40
- * expressed in this map:
41
- * - `numeric[]` (1231) is parsed with `parseFloat` → `number[]` at runtime, while the
42
- * element-wise type says `string[]`.
43
- * - Array types without a registered parser come back as the raw `{...}` string, not an
44
- * array: name[], "char"[], xml[], bit[], varbit[], tsvector[], tsquery[], circle[],
45
- * line[], lseg[], box[], path[], polygon[], int4range[], int8range[], tsrange[],
46
- * tstzrange[], daterange[], macaddr8[], regclass[]/regtype[]/regproc[], record[].
47
- * Register your own parser (`pg.types.setTypeParser`) or cast in SQL if you depend on these.
48
- * The two that `PgCatalog` columns use are declared here as the text pg returns: `aclitem[]`
49
- * (`relacl`, `datacl`, ...) and `"char"[]` (`proargmodes`).
50
- */
51
5
  export interface PgTypeMap
52
6
  extends Omit<DefaultTypeMap, "bytea" | "interval" | "record" | "void"> {
53
- /** What parameters accept on top of the row types. */
54
7
  $params: PgParamTypeMap;
55
8
  bytea: Buffer;
56
9
  interval: {
@@ -65,28 +18,10 @@ export interface PgTypeMap
65
18
  toISO(): string;
66
19
  toISOString(): string;
67
20
  };
68
- /** Anonymous `ROW(...)` results have no parser: raw text such as `"(1,foo)"`. */
69
21
  record: string;
70
- /** `void`-returning functions (`pg_sleep`) yield an empty string. */
71
22
  void: string;
72
- /** No array parser: the raw `{...}` text, e.g. `"{postgres=arwdDxtm/postgres}"`. */
73
23
  "aclitem[]": string;
74
- /** No array parser for `"char"[]`: the raw `{...}` text, e.g. `"{i,o}"`. */
75
24
  "char[]": string;
76
25
  }
77
26
 
78
- /**
79
- * TS types `pg` parameters accept on top of the row type (`PgTypeMap["$params"]`). Verified
80
- * against `pg/lib/utils.js` (`prepareValue`, 8.x): a `Date` is sent as a timestamp with the
81
- * local offset, `Buffer`/typed arrays as bytea, a `bigint` or number with `toString()`, and
82
- * objects with `toPostgres()` (such as `PgInterval`) through it; Postgres parses the text.
83
- *
84
- * int8, numeric, money number | bigint | string
85
- * date, timestamp, timestamptz Date | string
86
- * interval string, besides the `PgInterval` row type
87
- * bytea Uint8Array, besides `Buffer`
88
- *
89
- * Plain objects are `JSON.stringify`d and arrays become Postgres array literals, so neither is
90
- * listed for other types.
91
- */
92
27
  export interface PgParamTypeMap extends DefaultParamTypeMap {}