neonctl 2.28.0 → 2.29.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 (135) hide show
  1. package/README.md +2 -2
  2. package/dist/analytics.js +35 -33
  3. package/dist/api.js +34 -34
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +2 -2
  6. package/dist/commands/auth.js +58 -52
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +154 -147
  9. package/dist/commands/bucket.js +124 -118
  10. package/dist/commands/checkout.js +49 -49
  11. package/dist/commands/config.js +212 -88
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +96 -96
  14. package/dist/commands/databases.js +23 -23
  15. package/dist/commands/deploy.js +12 -12
  16. package/dist/commands/dev.js +114 -114
  17. package/dist/commands/env.js +43 -43
  18. package/dist/commands/functions.js +97 -98
  19. package/dist/commands/index.js +26 -26
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +223 -166
  23. package/dist/commands/neon_auth.js +381 -363
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +101 -99
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +21 -21
  29. package/dist/commands/schema_diff.js +23 -23
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +17 -17
  32. package/dist/commands/user.js +5 -5
  33. package/dist/commands/vpc_endpoints.js +50 -50
  34. package/dist/config.js +7 -7
  35. package/dist/config_format.js +5 -5
  36. package/dist/context.js +23 -16
  37. package/dist/current_branch_fast_path.js +6 -6
  38. package/dist/dev/env.js +33 -33
  39. package/dist/dev/functions.js +4 -4
  40. package/dist/dev/inputs.js +6 -6
  41. package/dist/dev/runtime.js +25 -25
  42. package/dist/env.js +14 -14
  43. package/dist/env_file.js +13 -13
  44. package/dist/errors.js +19 -19
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +94 -92
  48. package/dist/log.js +2 -2
  49. package/dist/pkg.js +5 -5
  50. package/dist/psql/cli.js +4 -2
  51. package/dist/psql/command/cmd_cond.js +61 -61
  52. package/dist/psql/command/cmd_connect.js +159 -154
  53. package/dist/psql/command/cmd_copy.js +107 -97
  54. package/dist/psql/command/cmd_describe.js +368 -363
  55. package/dist/psql/command/cmd_format.js +276 -263
  56. package/dist/psql/command/cmd_io.js +269 -263
  57. package/dist/psql/command/cmd_lo.js +74 -66
  58. package/dist/psql/command/cmd_meta.js +148 -148
  59. package/dist/psql/command/cmd_misc.js +17 -17
  60. package/dist/psql/command/cmd_pipeline.js +142 -135
  61. package/dist/psql/command/cmd_restrict.js +25 -25
  62. package/dist/psql/command/cmd_show.js +183 -168
  63. package/dist/psql/command/dispatch.js +26 -26
  64. package/dist/psql/command/shared.js +14 -14
  65. package/dist/psql/complete/filenames.js +16 -16
  66. package/dist/psql/complete/index.js +4 -4
  67. package/dist/psql/complete/matcher.js +33 -32
  68. package/dist/psql/complete/psqlVars.js +173 -173
  69. package/dist/psql/complete/queries.js +5 -3
  70. package/dist/psql/complete/rules.js +900 -863
  71. package/dist/psql/core/common.js +136 -133
  72. package/dist/psql/core/help.js +343 -343
  73. package/dist/psql/core/mainloop.js +160 -153
  74. package/dist/psql/core/prompt.js +126 -123
  75. package/dist/psql/core/settings.js +111 -111
  76. package/dist/psql/core/sqlHelp.js +150 -150
  77. package/dist/psql/core/startup.js +211 -205
  78. package/dist/psql/core/syncVars.js +14 -14
  79. package/dist/psql/core/variables.js +24 -24
  80. package/dist/psql/describe/formatters.js +302 -289
  81. package/dist/psql/describe/processNamePattern.js +28 -28
  82. package/dist/psql/describe/queries.js +656 -651
  83. package/dist/psql/index.js +436 -411
  84. package/dist/psql/io/history.js +36 -36
  85. package/dist/psql/io/input.js +15 -15
  86. package/dist/psql/io/lineEditor/buffer.js +27 -25
  87. package/dist/psql/io/lineEditor/complete.js +15 -15
  88. package/dist/psql/io/lineEditor/filename.js +22 -22
  89. package/dist/psql/io/lineEditor/index.js +65 -62
  90. package/dist/psql/io/lineEditor/keymap.js +325 -318
  91. package/dist/psql/io/lineEditor/vt100.js +60 -60
  92. package/dist/psql/io/pgpass.js +18 -18
  93. package/dist/psql/io/pgservice.js +14 -14
  94. package/dist/psql/io/psqlrc.js +46 -46
  95. package/dist/psql/print/aligned.js +175 -166
  96. package/dist/psql/print/asciidoc.js +51 -51
  97. package/dist/psql/print/crosstab.js +34 -31
  98. package/dist/psql/print/csv.js +25 -22
  99. package/dist/psql/print/html.js +54 -54
  100. package/dist/psql/print/json.js +12 -12
  101. package/dist/psql/print/latex.js +118 -118
  102. package/dist/psql/print/pager.js +28 -26
  103. package/dist/psql/print/troff.js +48 -48
  104. package/dist/psql/print/unaligned.js +15 -14
  105. package/dist/psql/print/units.js +17 -17
  106. package/dist/psql/scanner/slash.js +48 -46
  107. package/dist/psql/scanner/sql.js +88 -84
  108. package/dist/psql/scanner/stringutils.js +21 -17
  109. package/dist/psql/types/index.js +7 -7
  110. package/dist/psql/types/scanner.js +8 -8
  111. package/dist/psql/wire/connection.js +341 -327
  112. package/dist/psql/wire/copy.js +7 -7
  113. package/dist/psql/wire/pipeline.js +26 -24
  114. package/dist/psql/wire/protocol.js +102 -102
  115. package/dist/psql/wire/sasl.js +62 -62
  116. package/dist/psql/wire/tls.js +79 -73
  117. package/dist/storage_api.js +15 -15
  118. package/dist/test_utils/fixtures.js +34 -31
  119. package/dist/test_utils/oauth_server.js +5 -5
  120. package/dist/utils/api_enums.js +13 -13
  121. package/dist/utils/branch_notice.js +5 -5
  122. package/dist/utils/branch_picker.js +26 -26
  123. package/dist/utils/compute_units.js +4 -4
  124. package/dist/utils/enrichers.js +20 -15
  125. package/dist/utils/esbuild.js +28 -28
  126. package/dist/utils/formats.js +1 -1
  127. package/dist/utils/middlewares.js +3 -3
  128. package/dist/utils/package_manager.js +68 -0
  129. package/dist/utils/point_in_time.js +12 -12
  130. package/dist/utils/psql.js +30 -30
  131. package/dist/utils/string.js +2 -2
  132. package/dist/utils/ui.js +9 -9
  133. package/dist/utils/zip.js +1 -1
  134. package/dist/writer.js +17 -17
  135. package/package.json +6 -7
@@ -36,19 +36,17 @@
36
36
  * query down the wire. See {@link processSQLNamePattern} for the
37
37
  * pattern parser.
38
38
  */
39
- import { processSQLNamePattern } from './processNamePattern.js';
40
- import { alignedPrinter } from '../print/aligned.js';
41
- import { asciidocPrinter } from '../print/asciidoc.js';
42
- import { csvPrinter } from '../print/csv.js';
43
- import { htmlPrinter } from '../print/html.js';
44
- import { jsonPrinter } from '../print/json.js';
45
- import { latexLongtablePrinter, latexPrinter } from '../print/latex.js';
46
- import { troffMsPrinter } from '../print/troff.js';
47
- import { unalignedPrinter } from '../print/unaligned.js';
48
- import { fetchForeignTableInfo, fetchInheritedBy, fetchInherits, fetchPartitionKey, fetchPartitionOf, fetchPerColumnFdwOptions, fetchPolicies, fetchStatisticsObjects, fetchTableInfo, fetchTablePublications, fetchTableSubscriptions, } from './queries.js';
49
- import { applyPattern } from './processNamePattern.js';
50
- import { fetchNotNullConstraints } from './queries.js';
51
- import { serverAtLeast, PG_14 } from './versionGate.js';
39
+ import { alignedPrinter } from "../print/aligned.js";
40
+ import { asciidocPrinter } from "../print/asciidoc.js";
41
+ import { csvPrinter } from "../print/csv.js";
42
+ import { htmlPrinter } from "../print/html.js";
43
+ import { jsonPrinter } from "../print/json.js";
44
+ import { latexLongtablePrinter, latexPrinter } from "../print/latex.js";
45
+ import { troffMsPrinter } from "../print/troff.js";
46
+ import { unalignedPrinter } from "../print/unaligned.js";
47
+ import { applyPattern, processSQLNamePattern, } from "./processNamePattern.js";
48
+ import { fetchForeignTableInfo, fetchInheritedBy, fetchInherits, fetchNotNullConstraints, fetchPartitionKey, fetchPartitionOf, fetchPerColumnFdwOptions, fetchPolicies, fetchStatisticsObjects, fetchTableInfo, fetchTablePublications, fetchTableSubscriptions, } from "./queries.js";
49
+ import { PG_14, serverAtLeast } from "./versionGate.js";
52
50
  /**
53
51
  * Pick the printer for the active output format. Mirrors `pickPrinter`
54
52
  * in `core/common.ts`, but operates off `PrintQueryOpts.topt.format`
@@ -60,24 +58,24 @@ import { serverAtLeast, PG_14 } from './versionGate.js';
60
58
  */
61
59
  const pickPrinterForFormat = (opts) => {
62
60
  switch (opts.topt.format) {
63
- case 'aligned':
64
- case 'wrapped':
61
+ case "aligned":
62
+ case "wrapped":
65
63
  return alignedPrinter;
66
- case 'unaligned':
64
+ case "unaligned":
67
65
  return unalignedPrinter;
68
- case 'csv':
66
+ case "csv":
69
67
  return csvPrinter;
70
- case 'json':
68
+ case "json":
71
69
  return jsonPrinter;
72
- case 'html':
70
+ case "html":
73
71
  return htmlPrinter;
74
- case 'asciidoc':
72
+ case "asciidoc":
75
73
  return asciidocPrinter;
76
- case 'latex':
74
+ case "latex":
77
75
  return latexPrinter;
78
- case 'latex-longtable':
76
+ case "latex-longtable":
79
77
  return latexLongtablePrinter;
80
- case 'troff-ms':
78
+ case "troff-ms":
81
79
  return troffMsPrinter;
82
80
  default:
83
81
  return alignedPrinter;
@@ -90,14 +88,14 @@ const pickPrinterForFormat = (opts) => {
90
88
  */
91
89
  const cellToString = (v) => {
92
90
  if (v === null || v === undefined)
93
- return '';
94
- if (typeof v === 'string')
91
+ return "";
92
+ if (typeof v === "string")
95
93
  return v;
96
94
  if (Buffer.isBuffer(v))
97
- return v.toString('utf-8');
98
- if (typeof v === 'number' ||
99
- typeof v === 'boolean' ||
100
- typeof v === 'bigint') {
95
+ return v.toString("utf-8");
96
+ if (typeof v === "number" ||
97
+ typeof v === "boolean" ||
98
+ typeof v === "bigint") {
101
99
  return String(v);
102
100
  }
103
101
  // Non-primitive fallback: encode JSON. This branch shouldn't be hit
@@ -107,7 +105,7 @@ const cellToString = (v) => {
107
105
  return JSON.stringify(v);
108
106
  }
109
107
  catch {
110
- return '';
108
+ return "";
111
109
  }
112
110
  };
113
111
  /**
@@ -117,16 +115,16 @@ const cellToString = (v) => {
117
115
  */
118
116
  const coerceResultSet = (rs) => ({
119
117
  ...rs,
120
- rows: rs.rows.map((row) => row.map((c) => (c === null || c === undefined ? null : cellToString(c)))),
118
+ rows: rs.rows.map((row) => row.map((c) => c === null || c === undefined ? null : cellToString(c))),
121
119
  });
122
120
  const makeSectionBuffer = () => {
123
- let buf = '';
121
+ let buf = "";
124
122
  const write = (chunk) => {
125
- if (typeof chunk === 'string') {
123
+ if (typeof chunk === "string") {
126
124
  buf += chunk;
127
125
  }
128
126
  else {
129
- buf += chunk.toString('utf-8');
127
+ buf += chunk.toString("utf-8");
130
128
  }
131
129
  return true;
132
130
  };
@@ -162,8 +160,8 @@ const makeSectionBuffer = () => {
162
160
  const captureSection = async (fn) => {
163
161
  const buf = makeSectionBuffer();
164
162
  await fn(buf);
165
- const text = buf.toString().replace(/\n+$/, '');
166
- return text === '' ? null : text;
163
+ const text = buf.toString().replace(/\n+$/, "");
164
+ return text === "" ? null : text;
167
165
  };
168
166
  /**
169
167
  * Run a list-style describe query and write its result via the aligned
@@ -195,7 +193,7 @@ export const lookupRelations = async (conn, query, patternResult) => {
195
193
  oid: Number(cellToString(row[0])),
196
194
  nspname: cellToString(row[1]),
197
195
  relname: cellToString(row[2]),
198
- relkind: cellToString(row[3] ?? ''),
196
+ relkind: cellToString(row[3] ?? ""),
199
197
  }));
200
198
  };
201
199
  /**
@@ -212,9 +210,9 @@ export const lookupOneRelation = async (conn, pattern) => {
212
210
  // `^(name)$` interpolation matched neither (review item #22).
213
211
  const np = processSQLNamePattern({
214
212
  pattern,
215
- namevar: 'c.relname',
216
- schemavar: 'n.nspname',
217
- visibilityrule: 'pg_catalog.pg_table_is_visible(c.oid)',
213
+ namevar: "c.relname",
214
+ schemavar: "n.nspname",
215
+ visibilityrule: "pg_catalog.pg_table_is_visible(c.oid)",
218
216
  });
219
217
  // A db-qualified pattern (3+ dotted components → dotCount > 1) is a
220
218
  // cross-database reference that this single-DB detail short-circuit cannot
@@ -230,12 +228,12 @@ export const lookupOneRelation = async (conn, pattern) => {
230
228
  ...np.nameConditions,
231
229
  ...np.visibilityConditions,
232
230
  ];
233
- let sql = 'SELECT c.oid, n.nspname, c.relname, c.relkind\n' +
234
- 'FROM pg_catalog.pg_class c\n' +
235
- ' LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n';
231
+ let sql = "SELECT c.oid, n.nspname, c.relname, c.relkind\n" +
232
+ "FROM pg_catalog.pg_class c\n" +
233
+ " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n";
236
234
  if (conds.length > 0)
237
- sql += `WHERE ${conds.join('\n AND ')}\n`;
238
- sql += 'ORDER BY 2, 3 LIMIT 1;';
235
+ sql += `WHERE ${conds.join("\n AND ")}\n`;
236
+ sql += "ORDER BY 2, 3 LIMIT 1;";
239
237
  const rs = await conn.query(sql, np.params);
240
238
  if (rs.rows.length === 0)
241
239
  return null;
@@ -271,14 +269,14 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
271
269
  // matview-inline form is also gated by `HIDE_TABLEAM` so the user can
272
270
  // opt out of access-method noise.
273
271
  const baseTitle = headerForRelkind(relkind, schema, name);
274
- const title = !hideTableam && relkind === 'm' && relInfo.relam !== 0 && relInfo.amname
272
+ const title = !hideTableam && relkind === "m" && relInfo.relam !== 0 && relInfo.amname
275
273
  ? `${baseTitle}\nAccess method: ${relInfo.amname}`
276
274
  : baseTitle;
277
275
  // ----- Pre-fetch per-column FDW options (foreign tables only) so we
278
276
  // can fold them into each column row. Upstream renders these
279
277
  // inline as a trailing "FDW options: (k 'v', ...)" annotation
280
278
  // rather than a separate footer section.
281
- const fdwOptionsByColumn = relkind === 'f'
279
+ const fdwOptionsByColumn = relkind === "f"
282
280
  ? await fetchPerColumnFdwOptionsMap(conn, oid)
283
281
  : new Map();
284
282
  // ----- Columns -----
@@ -288,13 +286,13 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
288
286
  // `describeOneTableDetails` gates the verbose column block on `verbose`
289
287
  // alone, not on relkind).
290
288
  const verboseCols = verbose &&
291
- (relkind === 'r' ||
292
- relkind === 'm' ||
293
- relkind === 'p' ||
294
- relkind === 'f' ||
295
- relkind === 'v' ||
296
- relkind === 'I' ||
297
- relkind === 'i');
289
+ (relkind === "r" ||
290
+ relkind === "m" ||
291
+ relkind === "p" ||
292
+ relkind === "f" ||
293
+ relkind === "v" ||
294
+ relkind === "I" ||
295
+ relkind === "i");
298
296
  // Compression column (upstream `\d+`): present when the server is
299
297
  // PG 14+, the `HIDE_TOAST_COMPRESSION` var is off, and the relkind is
300
298
  // a regular table / partitioned table / materialized view (describe.c
@@ -305,52 +303,52 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
305
303
  const includeCompression = verboseCols &&
306
304
  serverAtLeast(conn.serverVersion, PG_14) &&
307
305
  !hideCompression &&
308
- (relkind === 'r' || relkind === 'p' || relkind === 'm');
306
+ (relkind === "r" || relkind === "p" || relkind === "m");
309
307
  // Stats target column (upstream `\d+`): every verbose relkind EXCEPT a
310
308
  // plain view — views have no per-column statistics targets (describe.c
311
309
  // ~1964: RELATION, INDEX, PARTITIONED_INDEX, MATVIEW, FOREIGN_TABLE,
312
310
  // PARTITIONED_TABLE).
313
311
  const includeStatsTarget = verboseCols &&
314
- (relkind === 'r' ||
315
- relkind === 'i' ||
316
- relkind === 'I' ||
317
- relkind === 'm' ||
318
- relkind === 'f' ||
319
- relkind === 'p');
320
- const colSql = 'SELECT a.attname,\n' +
321
- ' pg_catalog.format_type(a.atttypid, a.atttypmod),\n' +
322
- ' (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)\n' +
323
- ' FROM pg_catalog.pg_attrdef d\n' +
324
- ' WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),\n' +
325
- ' a.attnotnull,\n' +
326
- ' (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n' +
327
- ' WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation,\n' +
328
- ' a.attidentity,\n' +
329
- ' a.attgenerated' +
312
+ (relkind === "r" ||
313
+ relkind === "i" ||
314
+ relkind === "I" ||
315
+ relkind === "m" ||
316
+ relkind === "f" ||
317
+ relkind === "p");
318
+ const colSql = "SELECT a.attname,\n" +
319
+ " pg_catalog.format_type(a.atttypid, a.atttypmod),\n" +
320
+ " (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)\n" +
321
+ " FROM pg_catalog.pg_attrdef d\n" +
322
+ " WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),\n" +
323
+ " a.attnotnull,\n" +
324
+ " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n" +
325
+ " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation,\n" +
326
+ " a.attidentity,\n" +
327
+ " a.attgenerated" +
330
328
  (verboseCols
331
- ? ',\n CASE a.attstorage' +
329
+ ? ",\n CASE a.attstorage" +
332
330
  " WHEN 'p' THEN 'plain'" +
333
331
  " WHEN 'e' THEN 'external'" +
334
332
  " WHEN 'm' THEN 'main'" +
335
333
  " WHEN 'x' THEN 'extended'" +
336
334
  " ELSE '???'" +
337
- ' END AS attstorage' +
335
+ " END AS attstorage" +
338
336
  (includeCompression
339
- ? ',\n CASE a.attcompression' +
337
+ ? ",\n CASE a.attcompression" +
340
338
  " WHEN 'p' THEN 'pglz'" +
341
339
  " WHEN 'l' THEN 'lz4'" +
342
340
  " WHEN '' THEN ''" +
343
341
  " ELSE '???'" +
344
- ' END AS attcompression'
345
- : '') +
342
+ " END AS attcompression"
343
+ : "") +
346
344
  (includeStatsTarget
347
- ? ',\n CASE WHEN a.attstattarget = -1 THEN NULL ELSE a.attstattarget::text END AS attstattarget'
348
- : '') +
349
- ',\n pg_catalog.col_description(a.attrelid, a.attnum)'
350
- : '') +
351
- '\nFROM pg_catalog.pg_attribute a\n' +
345
+ ? ",\n CASE WHEN a.attstattarget = -1 THEN NULL ELSE a.attstattarget::text END AS attstattarget"
346
+ : "") +
347
+ ",\n pg_catalog.col_description(a.attrelid, a.attnum)"
348
+ : "") +
349
+ "\nFROM pg_catalog.pg_attribute a\n" +
352
350
  `WHERE a.attrelid = '${oid}' AND a.attnum > 0 AND NOT a.attisdropped\n` +
353
- 'ORDER BY a.attnum;';
351
+ "ORDER BY a.attnum;";
354
352
  const colsRs = await conn.query(colSql, []);
355
353
  // Foreign tables get an extra "FDW options" column when at least one
356
354
  // attribute actually has options set (matches upstream — the column
@@ -360,78 +358,78 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
360
358
  // Collation/Nullable/Default (those are uniformly empty for the three
361
359
  // fixed columns chunk_id/chunk_seq/chunk_data). Matches upstream's
362
360
  // `\d <toast>` output.
363
- const isToast = relkind === 't';
361
+ const isToast = relkind === "t";
364
362
  // Synthesize a printable result set: Column, Type[, Collation, Nullable,
365
363
  // Default[, Storage[, Compression], Stats target, Description]][, FDW options].
366
- const fields = [fakeField('Column'), fakeField('Type')];
364
+ const fields = [fakeField("Column"), fakeField("Type")];
367
365
  if (!isToast) {
368
- fields.push(fakeField('Collation'));
369
- fields.push(fakeField('Nullable'));
370
- fields.push(fakeField('Default'));
366
+ fields.push(fakeField("Collation"));
367
+ fields.push(fakeField("Nullable"));
368
+ fields.push(fakeField("Default"));
371
369
  }
372
370
  if (verboseCols) {
373
- fields.push(fakeField('Storage'));
371
+ fields.push(fakeField("Storage"));
374
372
  if (includeCompression)
375
- fields.push(fakeField('Compression'));
373
+ fields.push(fakeField("Compression"));
376
374
  if (includeStatsTarget)
377
- fields.push(fakeField('Stats target'));
378
- fields.push(fakeField('Description'));
375
+ fields.push(fakeField("Stats target"));
376
+ fields.push(fakeField("Description"));
379
377
  }
380
378
  if (hasAnyFdwOptions)
381
- fields.push(fakeField('FDW options'));
379
+ fields.push(fakeField("FDW options"));
382
380
  const rows = colsRs.rows.map((r) => {
383
381
  const colName = cellToString(r[0]);
384
382
  const colType = cellToString(r[1]);
385
383
  const colDefault = r[2] === null ? null : cellToString(r[2]);
386
- const notnull = String(r[3]) === 't' || r[3] === true;
384
+ const notnull = String(r[3]) === "t" || r[3] === true;
387
385
  const collation = r[4] === null ? null : cellToString(r[4]);
388
- const identity = cellToString(r[5] ?? '');
389
- const generated = cellToString(r[6] ?? '');
390
- const nullable = notnull ? 'not null' : '';
391
- let dflt = colDefault ?? '';
392
- if (identity === 'a') {
393
- dflt = 'generated always as identity';
386
+ const identity = cellToString(r[5] ?? "");
387
+ const generated = cellToString(r[6] ?? "");
388
+ const nullable = notnull ? "not null" : "";
389
+ let dflt = colDefault ?? "";
390
+ if (identity === "a") {
391
+ dflt = "generated always as identity";
394
392
  }
395
- else if (identity === 'd') {
396
- dflt = 'generated by default as identity';
393
+ else if (identity === "d") {
394
+ dflt = "generated by default as identity";
397
395
  }
398
- else if (generated === 's') {
396
+ else if (generated === "s") {
399
397
  // STORED generated column (PG 12+).
400
- dflt = dflt ? `generated always as (${dflt}) stored` : '';
398
+ dflt = dflt ? `generated always as (${dflt}) stored` : "";
401
399
  }
402
- else if (generated === 'v') {
400
+ else if (generated === "v") {
403
401
  // VIRTUAL generated column (PG 18+). Same expression rendering as
404
402
  // STORED but without the trailing keyword.
405
- dflt = dflt ? `generated always as (${dflt})` : '';
403
+ dflt = dflt ? `generated always as (${dflt})` : "";
406
404
  }
407
405
  const row = isToast
408
406
  ? [colName, colType]
409
- : [colName, colType, collation ?? '', nullable, dflt];
407
+ : [colName, colType, collation ?? "", nullable, dflt];
410
408
  if (verboseCols) {
411
409
  // Slot offsets: 7 = storage, [8 = compression if PG14+], stats, desc.
412
410
  let idx = 7;
413
- const storage = cellToString(r[idx++] ?? '');
411
+ const storage = cellToString(r[idx++] ?? "");
414
412
  row.push(storage);
415
413
  if (includeCompression) {
416
- const compression = cellToString(r[idx++] ?? '');
414
+ const compression = cellToString(r[idx++] ?? "");
417
415
  row.push(compression);
418
416
  }
419
417
  if (includeStatsTarget) {
420
- const statsTarget = r[idx] === null ? '' : cellToString(r[idx] ?? '');
418
+ const statsTarget = r[idx] === null ? "" : cellToString(r[idx] ?? "");
421
419
  idx++;
422
420
  row.push(statsTarget);
423
421
  }
424
- const description = r[idx] === null ? '' : cellToString(r[idx] ?? '');
422
+ const description = r[idx] === null ? "" : cellToString(r[idx] ?? "");
425
423
  row.push(description);
426
424
  }
427
425
  if (hasAnyFdwOptions) {
428
426
  const opts = fdwOptionsByColumn.get(colName);
429
- row.push(opts ? `(${opts})` : '');
427
+ row.push(opts ? `(${opts})` : "");
430
428
  }
431
429
  return row;
432
430
  });
433
431
  const colsResult = {
434
- command: 'SELECT',
432
+ command: "SELECT",
435
433
  rowCount: rows.length,
436
434
  oid: null,
437
435
  fields,
@@ -459,14 +457,14 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
459
457
  // Upstream attaches this as a table FOOTER (describe.c ~3175), so it
460
458
  // renders flush against the column rows with the single trailing
461
459
  // blank line the footer machinery adds — not as a separate block.
462
- if ((relkind === 'v' || relkind === 'm') && verbose) {
460
+ if ((relkind === "v" || relkind === "m") && verbose) {
463
461
  const vrs = await conn.query(`SELECT pg_catalog.pg_get_viewdef('${oid}'::pg_catalog.oid, true);`, []);
464
462
  if (vrs.rows.length > 0) {
465
463
  push(`View definition:\n${cellToString(vrs.rows[0][0])}`);
466
464
  }
467
465
  }
468
466
  // ----- Partition-key (partitioned-table parent only) -----
469
- if (relkind === 'p') {
467
+ if (relkind === "p") {
470
468
  push(await captureSection((b) => renderPartitionKeySection(conn, oid, b)));
471
469
  }
472
470
  // ----- Partition-of (child partition only) -----
@@ -476,18 +474,18 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
476
474
  // ----- Owning table (TOAST tables only — printed before Indexes).
477
475
  // Upstream `describeOneTableDetails` adds the owning-table footer
478
476
  // prior to attaching the indexes footer for `RELKIND_TOASTVALUE`.
479
- if (relkind === 't') {
477
+ if (relkind === "t") {
480
478
  push(await captureSection((b) => renderToastOwningTableFooter(conn, oid, b)));
481
479
  }
482
480
  // ----- Indexes (tables / matviews / partitioned tables / TOAST) -----
483
- if (relkind === 'r' ||
484
- relkind === 'm' ||
485
- relkind === 'p' ||
486
- relkind === 't') {
481
+ if (relkind === "r" ||
482
+ relkind === "m" ||
483
+ relkind === "p" ||
484
+ relkind === "t") {
487
485
  push(await captureSection((b) => renderIndexesSection(conn, oid, b)));
488
486
  }
489
487
  // ----- Check constraints -----
490
- if (relkind === 'r' || relkind === 'p' || relkind === 'f') {
488
+ if (relkind === "r" || relkind === "p" || relkind === "f") {
491
489
  push(await captureSection((b) => renderCheckConstraintsSection(conn, oid, b)));
492
490
  }
493
491
  // ----- Not-null constraints (PG 18+ named NOT NULL constraints) -----
@@ -495,59 +493,62 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
495
493
  // constraints and Foreign-key constraints (describe.c ~3104).
496
494
  // The query returns empty on pre-PG-18 servers (no contype = 'n'
497
495
  // rows), so the section is naturally absent there.
498
- if (verbose && (relkind === 'r' || relkind === 'p' || relkind === 'f')) {
496
+ if (verbose && (relkind === "r" || relkind === "p" || relkind === "f")) {
499
497
  push(await captureSection((b) => renderNotNullConstraintsSection(conn, oid, b)));
500
498
  }
501
499
  // ----- Foreign-key constraints -----
502
- if (relkind === 'r' || relkind === 'p') {
500
+ if (relkind === "r" || relkind === "p") {
503
501
  push(await captureSection((b) => renderForeignKeyConstraintsSection(conn, oid, b)));
504
502
  push(await captureSection((b) => renderReferencedBySection(conn, oid, b)));
505
503
  }
506
504
  // ----- Triggers -----
507
- if (relkind === 'r' || relkind === 'p' || relkind === 'v') {
505
+ if (relkind === "r" || relkind === "p" || relkind === "v") {
508
506
  push(await captureSection((b) => renderTriggersSection(conn, oid, b)));
509
507
  }
510
508
  // ----- RLS policies (regular + partitioned tables) -----
511
- if (relkind === 'r' || relkind === 'p') {
509
+ if (relkind === "r" || relkind === "p") {
512
510
  push(await captureSection((b) => renderPoliciesSection(conn, oid, relInfo, b)));
513
511
  }
514
512
  // ----- Foreign-table footer: Server + FDW options -----
515
513
  // Per-column FDW options are rendered inline within the columns
516
514
  // table (see fdwOptionsByColumn above); no separate footer here.
517
- if (relkind === 'f') {
515
+ if (relkind === "f") {
518
516
  push(await captureSection((b) => renderForeignTableFooter(conn, oid, b)));
519
517
  }
520
518
  // ----- Inherits: (parents) — for tables, partitioned tables, foreign -----
521
- if (relkind === 'r' || relkind === 'p' || relkind === 'f') {
519
+ if (relkind === "r" || relkind === "p" || relkind === "f") {
522
520
  push(await captureSection((b) => renderInheritsSection(conn, oid, b)));
523
521
  }
524
522
  // ----- Inherited by / Partitions / Number of [child tables|partitions] -----
525
- if (relkind === 'r' || relkind === 'p' || relkind === 'f') {
523
+ if (relkind === "r" || relkind === "p" || relkind === "f") {
526
524
  push(await captureSection((b) => renderInheritedBySection(conn, oid, relkind, verbose, b)));
527
525
  }
528
526
  // ----- Publications (any publishable relkind) -----
529
- if (relkind === 'r' ||
530
- relkind === 'p' ||
531
- relkind === 'm' ||
532
- relkind === 'f') {
527
+ if (relkind === "r" ||
528
+ relkind === "p" ||
529
+ relkind === "m" ||
530
+ relkind === "f") {
533
531
  push(await captureSection((b) => renderPublicationsSection(conn, oid, b)));
534
532
  }
535
533
  // ----- Subscriptions (any publishable relkind; permission-denied silent) -----
536
- if (relkind === 'r' ||
537
- relkind === 'p' ||
538
- relkind === 'm' ||
539
- relkind === 'f') {
534
+ if (relkind === "r" ||
535
+ relkind === "p" ||
536
+ relkind === "m" ||
537
+ relkind === "f") {
540
538
  push(await captureSection((b) => renderSubscriptionsSection(conn, oid, b)));
541
539
  }
542
540
  // ----- Statistics objects (verbose; r/m/p/f) -----
543
541
  if (verbose &&
544
- (relkind === 'r' || relkind === 'm' || relkind === 'p' || relkind === 'f')) {
542
+ (relkind === "r" ||
543
+ relkind === "m" ||
544
+ relkind === "p" ||
545
+ relkind === "f")) {
545
546
  push(await captureSection((b) => renderStatisticsObjectsSection(conn, oid, b)));
546
547
  }
547
548
  // ----- Replica Identity (verbose, non-default, regular & matview).
548
549
  // INDEX mode is rendered inline within Indexes:, so the footer
549
550
  // is only emitted for FULL / NOTHING.
550
- if (verbose && (relkind === 'r' || relkind === 'm')) {
551
+ if (verbose && (relkind === "r" || relkind === "m")) {
551
552
  push(await captureSection((b) => {
552
553
  renderReplicaIdentitySection(schema, relInfo, b);
553
554
  }));
@@ -563,7 +564,7 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
563
564
  // so we don't double up here. Gated by `HIDE_TABLEAM` to mirror
564
565
  // upstream — the per-test psql.sql toggles the variable to
565
566
  // suppress access-method noise.
566
- if (!hideTableam && verbose && (relkind === 'r' || relkind === 'p')) {
567
+ if (!hideTableam && verbose && (relkind === "r" || relkind === "p")) {
567
568
  push(await captureSection((b) => {
568
569
  renderAccessMethodFooter(relInfo, b);
569
570
  }));
@@ -595,7 +596,7 @@ const fetchRelationInfo = async (conn, oid) => {
595
596
  return {
596
597
  rowsecurity: false,
597
598
  forcerowsecurity: false,
598
- relreplident: 'd',
599
+ relreplident: "d",
599
600
  relispartition: false,
600
601
  reltablespace: 0,
601
602
  relam: 0,
@@ -607,16 +608,16 @@ const fetchRelationInfo = async (conn, oid) => {
607
608
  return {
608
609
  rowsecurity: parseBool(r[0]),
609
610
  forcerowsecurity: parseBool(r[1]),
610
- relreplident: cellToString(r[2] ?? 'd') || 'd',
611
+ relreplident: cellToString(r[2] ?? "d") || "d",
611
612
  relispartition: parseBool(r[3]),
612
- reltablespace: Number(cellToString(r[4] ?? '0')) || 0,
613
- relam: Number(cellToString(r[5] ?? '0')) || 0,
613
+ reltablespace: Number(cellToString(r[4] ?? "0")) || 0,
614
+ relam: Number(cellToString(r[5] ?? "0")) || 0,
614
615
  spcname: r[6] === null || r[6] === undefined ? null : cellToString(r[6]),
615
616
  amname: r[7] === null || r[7] === undefined ? null : cellToString(r[7]),
616
617
  };
617
618
  };
618
619
  /** Coerce a Postgres "t"/"f" text-mode boolean (or a real bool) to JS. */
619
- const parseBool = (v) => v === true || (typeof v === 'string' && (v === 't' || v === 'true'));
620
+ const parseBool = (v) => v === true || (typeof v === "string" && (v === "t" || v === "true"));
620
621
  /**
621
622
  * Render `Partition key: <partkeydef>` for partitioned-table parents.
622
623
  */
@@ -625,8 +626,8 @@ const renderPartitionKeySection = async (conn, oid, out) => {
625
626
  const rs = await conn.query(q.sql, q.params);
626
627
  if (rs.rows.length === 0)
627
628
  return;
628
- const def = cellToString(rs.rows[0][0] ?? '');
629
- if (def === '')
629
+ const def = cellToString(rs.rows[0][0] ?? "");
630
+ if (def === "")
630
631
  return;
631
632
  out.write(`Partition key: ${def}\n`);
632
633
  };
@@ -645,15 +646,15 @@ const renderPartitionOfSection = async (conn, oid, verbose, out) => {
645
646
  if (rs.rows.length === 0)
646
647
  return;
647
648
  const row = rs.rows[0];
648
- const parent = cellToString(row[0] ?? '');
649
- const bound = cellToString(row[1] ?? '');
649
+ const parent = cellToString(row[0] ?? "");
650
+ const bound = cellToString(row[1] ?? "");
650
651
  const detached = parseBool(row[2]);
651
- const tail = detached ? ' DETACH PENDING' : '';
652
+ const tail = detached ? " DETACH PENDING" : "";
652
653
  out.write(`Partition of: ${parent} ${bound}${tail}\n`);
653
654
  if (verbose) {
654
- const constraintdef = row[3] === null || row[3] === undefined ? '' : cellToString(row[3]);
655
- if (constraintdef === '') {
656
- out.write('No partition constraint\n');
655
+ const constraintdef = row[3] === null || row[3] === undefined ? "" : cellToString(row[3]);
656
+ if (constraintdef === "") {
657
+ out.write("No partition constraint\n");
657
658
  }
658
659
  else {
659
660
  out.write(`Partition constraint: ${constraintdef}\n`);
@@ -673,19 +674,19 @@ const renderPoliciesSection = async (conn, oid, relInfo, out) => {
673
674
  const { rowsecurity, forcerowsecurity } = relInfo;
674
675
  let header = null;
675
676
  if (rowsecurity && !forcerowsecurity && tuples > 0) {
676
- header = 'Policies:';
677
+ header = "Policies:";
677
678
  }
678
679
  else if (rowsecurity && forcerowsecurity && tuples > 0) {
679
- header = 'Policies (forced row security enabled):';
680
+ header = "Policies (forced row security enabled):";
680
681
  }
681
682
  else if (rowsecurity && !forcerowsecurity && tuples === 0) {
682
- header = 'Policies (row security enabled): (none)';
683
+ header = "Policies (row security enabled): (none)";
683
684
  }
684
685
  else if (rowsecurity && forcerowsecurity && tuples === 0) {
685
- header = 'Policies (forced row security enabled): (none)';
686
+ header = "Policies (forced row security enabled): (none)";
686
687
  }
687
688
  else if (!rowsecurity && tuples > 0) {
688
- header = 'Policies (row security disabled):';
689
+ header = "Policies (row security disabled):";
689
690
  }
690
691
  if (header === null)
691
692
  return;
@@ -699,8 +700,8 @@ const renderPoliciesSection = async (conn, oid, relInfo, out) => {
699
700
  const cmd = r[5] === null || r[5] === undefined ? null : cellToString(r[5]);
700
701
  let line = ` POLICY "${polname}"`;
701
702
  if (!permissive)
702
- line += ' AS RESTRICTIVE';
703
- if (cmd !== null && cmd !== '')
703
+ line += " AS RESTRICTIVE";
704
+ if (cmd !== null && cmd !== "")
704
705
  line += ` FOR ${cmd}`;
705
706
  if (roles !== null)
706
707
  line += `\n TO ${roles}`;
@@ -723,11 +724,11 @@ const renderForeignTableFooter = async (conn, oid, out) => {
723
724
  if (rs.rows.length === 0)
724
725
  return;
725
726
  const row = rs.rows[0];
726
- const server = cellToString(row[0] ?? '');
727
- const ftoptions = row[1] === null || row[1] === undefined ? '' : cellToString(row[1]);
728
- if (server !== '')
727
+ const server = cellToString(row[0] ?? "");
728
+ const ftoptions = row[1] === null || row[1] === undefined ? "" : cellToString(row[1]);
729
+ if (server !== "")
729
730
  out.write(`Server: ${server}\n`);
730
- if (ftoptions !== '')
731
+ if (ftoptions !== "")
731
732
  out.write(`FDW options: (${ftoptions})\n`);
732
733
  };
733
734
  /**
@@ -740,12 +741,12 @@ const renderInheritsSection = async (conn, oid, out) => {
740
741
  const rs = await conn.query(q.sql, q.params);
741
742
  if (rs.rows.length === 0)
742
743
  return;
743
- const label = 'Inherits';
744
- const indent = ' '.repeat(label.length);
744
+ const label = "Inherits";
745
+ const indent = " ".repeat(label.length);
745
746
  rs.rows.forEach((r, idx) => {
746
747
  const parent = cellToString(r[0]);
747
748
  const prefix = idx === 0 ? `${label}: ` : `${indent} `;
748
- const trailing = idx < rs.rows.length - 1 ? ',' : '';
749
+ const trailing = idx < rs.rows.length - 1 ? "," : "";
749
750
  out.write(`${prefix}${parent}${trailing}\n`);
750
751
  });
751
752
  };
@@ -761,12 +762,12 @@ const renderInheritsSection = async (conn, oid, out) => {
761
762
  * list (verbose).
762
763
  */
763
764
  const renderInheritedBySection = async (conn, oid, relkind, verbose, out) => {
764
- const isPartitioned = relkind === 'p' || relkind === 'I';
765
+ const isPartitioned = relkind === "p" || relkind === "I";
765
766
  const q = fetchInheritedBy({ oid, serverVersion: conn.serverVersion });
766
767
  const rs = await conn.query(q.sql, q.params);
767
768
  const tuples = rs.rows.length;
768
769
  if (isPartitioned && tuples === 0) {
769
- out.write('Number of partitions: 0\n');
770
+ out.write("Number of partitions: 0\n");
770
771
  return;
771
772
  }
772
773
  if (!verbose) {
@@ -782,25 +783,25 @@ const renderInheritedBySection = async (conn, oid, relkind, verbose, out) => {
782
783
  }
783
784
  // Verbose mode: list each child with its bound (for partitions) and
784
785
  // child-relkind annotations.
785
- const label = isPartitioned ? 'Partitions' : 'Child tables';
786
- const indent = ' '.repeat(label.length);
786
+ const label = isPartitioned ? "Partitions" : "Child tables";
787
+ const indent = " ".repeat(label.length);
787
788
  rs.rows.forEach((r, idx) => {
788
789
  const relname = cellToString(r[0]);
789
- const childKind = cellToString(r[1] ?? '');
790
+ const childKind = cellToString(r[1] ?? "");
790
791
  const detached = parseBool(r[2]);
791
- const bound = r[3] === null || r[3] === undefined ? '' : cellToString(r[3]);
792
+ const bound = r[3] === null || r[3] === undefined ? "" : cellToString(r[3]);
792
793
  const prefix = idx === 0 ? `${label}: ` : `${indent} `;
793
794
  let line = `${prefix}${relname}`;
794
- if (bound !== '')
795
+ if (bound !== "")
795
796
  line += ` ${bound}`;
796
- if (childKind === 'p' || childKind === 'I')
797
- line += ', PARTITIONED';
798
- else if (childKind === 'f')
799
- line += ', FOREIGN';
797
+ if (childKind === "p" || childKind === "I")
798
+ line += ", PARTITIONED";
799
+ else if (childKind === "f")
800
+ line += ", FOREIGN";
800
801
  if (detached)
801
- line += ' (DETACH PENDING)';
802
+ line += " (DETACH PENDING)";
802
803
  if (idx < rs.rows.length - 1)
803
- line += ',';
804
+ line += ",";
804
805
  out.write(`${line}\n`);
805
806
  });
806
807
  };
@@ -815,22 +816,22 @@ const renderInheritedBySection = async (conn, oid, relkind, verbose, out) => {
815
816
  const renderReplicaIdentitySection = (schema, relInfo, out) => {
816
817
  const ri = relInfo.relreplident;
817
818
  // INDEX mode is rendered inline on the matching index — no footer.
818
- if (ri === 'i')
819
+ if (ri === "i")
819
820
  return;
820
821
  // pg_catalog relations default to 'n', user relations to 'd' — both
821
822
  // suppress the footer when the value matches the schema default.
822
- const isCatalog = schema === 'pg_catalog';
823
- if (!isCatalog && ri === 'd')
823
+ const isCatalog = schema === "pg_catalog";
824
+ if (!isCatalog && ri === "d")
824
825
  return;
825
- if (isCatalog && ri === 'n')
826
+ if (isCatalog && ri === "n")
826
827
  return;
827
- const label = ri === 'f'
828
- ? 'FULL'
829
- : ri === 'd'
830
- ? 'NOTHING'
831
- : ri === 'n'
832
- ? 'NOTHING'
833
- : '???';
828
+ const label = ri === "f"
829
+ ? "FULL"
830
+ : ri === "d"
831
+ ? "NOTHING"
832
+ : ri === "n"
833
+ ? "NOTHING"
834
+ : "???";
834
835
  out.write(`Replica Identity: ${label}\n`);
835
836
  };
836
837
  /**
@@ -839,12 +840,12 @@ const renderReplicaIdentitySection = (schema, relInfo, out) => {
839
840
  * tablespaces — caller enforces the relkind filter.
840
841
  */
841
842
  const renderTablespaceFooter = (relkind, relInfo, out) => {
842
- const tsSupported = relkind === 'r' ||
843
- relkind === 'm' ||
844
- relkind === 'i' ||
845
- relkind === 'I' ||
846
- relkind === 'p' ||
847
- relkind === 't';
843
+ const tsSupported = relkind === "r" ||
844
+ relkind === "m" ||
845
+ relkind === "i" ||
846
+ relkind === "I" ||
847
+ relkind === "p" ||
848
+ relkind === "t";
848
849
  if (!tsSupported)
849
850
  return;
850
851
  if (relInfo.reltablespace === 0 || !relInfo.spcname)
@@ -873,39 +874,39 @@ const renderAccessMethodFooter = (relInfo, out) => {
873
874
  * it, so no follow-up footer is needed for INDEX-mode RI.
874
875
  */
875
876
  const renderIndexesSection = async (conn, oid, out) => {
876
- const sql = 'SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered,\n' +
877
- ' i.indisvalid,\n' +
878
- ' pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n' +
879
- ' pg_catalog.pg_get_constraintdef(con.oid, true),\n' +
880
- ' contype, condeferrable, condeferred,\n' +
881
- ' i.indisreplident,\n' +
882
- ' c2.reltablespace\n' +
883
- 'FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n' +
877
+ const sql = "SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered,\n" +
878
+ " i.indisvalid,\n" +
879
+ " pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n" +
880
+ " pg_catalog.pg_get_constraintdef(con.oid, true),\n" +
881
+ " contype, condeferrable, condeferred,\n" +
882
+ " i.indisreplident,\n" +
883
+ " c2.reltablespace\n" +
884
+ "FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n" +
884
885
  ` LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n` +
885
886
  `WHERE c.oid = '${oid}' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n` +
886
- 'ORDER BY i.indisprimary DESC, c2.relname;';
887
+ "ORDER BY i.indisprimary DESC, c2.relname;";
887
888
  const rs = await conn.query(sql, []);
888
889
  if (rs.rows.length === 0)
889
890
  return;
890
- out.write('Indexes:\n');
891
+ out.write("Indexes:\n");
891
892
  for (const r of rs.rows) {
892
893
  const idxName = cellToString(r[0]);
893
- const isPrimary = String(r[1]) === 't' || r[1] === true;
894
- const isUnique = String(r[2]) === 't' || r[2] === true;
895
- const isClustered = String(r[3]) === 't' || r[3] === true;
896
- const isValid = String(r[4]) === 't' || r[4] === true;
894
+ const isPrimary = String(r[1]) === "t" || r[1] === true;
895
+ const isUnique = String(r[2]) === "t" || r[2] === true;
896
+ const isClustered = String(r[3]) === "t" || r[3] === true;
897
+ const isValid = String(r[4]) === "t" || r[4] === true;
897
898
  const indexdef = cellToString(r[5]);
898
- const constrDef = r[6] !== null ? cellToString(r[6]) : '';
899
- const contype = r[7] === null ? '' : cellToString(r[7]);
900
- const condeferrable = String(r[8]) === 't' || r[8] === true;
901
- const condeferred = String(r[9]) === 't' || r[9] === true;
902
- const isReplIdent = String(r[10]) === 't' || r[10] === true;
899
+ const constrDef = r[6] !== null ? cellToString(r[6]) : "";
900
+ const contype = r[7] === null ? "" : cellToString(r[7]);
901
+ const condeferrable = String(r[8]) === "t" || r[8] === true;
902
+ const condeferred = String(r[9]) === "t" || r[9] === true;
903
+ const isReplIdent = String(r[10]) === "t" || r[10] === true;
903
904
  let line = ` "${idxName}"`;
904
905
  // Strip everything up through " USING " from the indexdef so we get
905
906
  // the trailing `btree (...)` clause.
906
- const usingPos = indexdef.indexOf(' USING ');
907
+ const usingPos = indexdef.indexOf(" USING ");
907
908
  const tail = usingPos >= 0 ? indexdef.slice(usingPos + 7) : indexdef;
908
- if (contype === 'x') {
909
+ if (contype === "x") {
909
910
  // Exclusion constraint: emit constraintdef verbatim, no tail.
910
911
  line += ` ${constrDef}`;
911
912
  }
@@ -916,23 +917,23 @@ const renderIndexesSection = async (conn, oid, out) => {
916
917
  // indisunique -> " UNIQUE,"
917
918
  // No prefix for plain non-unique indexes.
918
919
  if (isPrimary) {
919
- line += ' PRIMARY KEY,';
920
+ line += " PRIMARY KEY,";
920
921
  }
921
922
  else if (isUnique) {
922
- line += contype === 'u' ? ' UNIQUE CONSTRAINT,' : ' UNIQUE,';
923
+ line += contype === "u" ? " UNIQUE CONSTRAINT," : " UNIQUE,";
923
924
  }
924
925
  line += ` ${tail}`;
925
926
  if (condeferrable)
926
- line += ' DEFERRABLE';
927
+ line += " DEFERRABLE";
927
928
  if (condeferred)
928
- line += ' INITIALLY DEFERRED';
929
+ line += " INITIALLY DEFERRED";
929
930
  }
930
931
  if (isClustered)
931
- line += ' CLUSTER';
932
+ line += " CLUSTER";
932
933
  if (!isValid)
933
- line += ' INVALID';
934
+ line += " INVALID";
934
935
  if (isReplIdent)
935
- line += ' REPLICA IDENTITY';
936
+ line += " REPLICA IDENTITY";
936
937
  out.write(`${line}\n`);
937
938
  }
938
939
  };
@@ -940,14 +941,14 @@ const renderIndexesSection = async (conn, oid, out) => {
940
941
  * Render `Check constraints:\n "name" CHECK (expr)` list.
941
942
  */
942
943
  const renderCheckConstraintsSection = async (conn, oid, out) => {
943
- const sql = 'SELECT r.conname, pg_catalog.pg_get_constraintdef(r.oid, true)\n' +
944
- 'FROM pg_catalog.pg_constraint r\n' +
944
+ const sql = "SELECT r.conname, pg_catalog.pg_get_constraintdef(r.oid, true)\n" +
945
+ "FROM pg_catalog.pg_constraint r\n" +
945
946
  `WHERE r.conrelid = '${oid}' AND r.contype = 'c'\n` +
946
- 'ORDER BY 1;';
947
+ "ORDER BY 1;";
947
948
  const rs = await conn.query(sql, []);
948
949
  if (rs.rows.length === 0)
949
950
  return;
950
- out.write('Check constraints:\n');
951
+ out.write("Check constraints:\n");
951
952
  for (const r of rs.rows) {
952
953
  out.write(` "${cellToString(r[0])}" ${cellToString(r[1])}\n`);
953
954
  }
@@ -963,11 +964,14 @@ const renderCheckConstraintsSection = async (conn, oid, out) => {
963
964
  * query returns no rows, so the whole section is suppressed.
964
965
  */
965
966
  const renderNotNullConstraintsSection = async (conn, oid, out) => {
966
- const q = fetchNotNullConstraints({ oid, serverVersion: conn.serverVersion });
967
+ const q = fetchNotNullConstraints({
968
+ oid,
969
+ serverVersion: conn.serverVersion,
970
+ });
967
971
  const rs = await conn.query(q.sql, q.params);
968
972
  if (rs.rows.length === 0)
969
973
  return;
970
- out.write('Not-null constraints:\n');
974
+ out.write("Not-null constraints:\n");
971
975
  for (const r of rs.rows) {
972
976
  const conname = cellToString(r[0]);
973
977
  const attname = cellToString(r[1]);
@@ -975,9 +979,9 @@ const renderNotNullConstraintsSection = async (conn, oid, out) => {
975
979
  const isLocal = parseBool(r[3]);
976
980
  let line = ` "${conname}" NOT NULL "${attname}"`;
977
981
  if (noInherit)
978
- line += ' NO INHERIT';
982
+ line += " NO INHERIT";
979
983
  else if (!isLocal)
980
- line += ' (inherited)';
984
+ line += " (inherited)";
981
985
  out.write(`${line}\n`);
982
986
  }
983
987
  };
@@ -985,14 +989,14 @@ const renderNotNullConstraintsSection = async (conn, oid, out) => {
985
989
  * Render `Foreign-key constraints:\n "name" FOREIGN KEY ...` list.
986
990
  */
987
991
  const renderForeignKeyConstraintsSection = async (conn, oid, out) => {
988
- const sql = 'SELECT conname, pg_catalog.pg_get_constraintdef(oid, true) AS condef\n' +
989
- 'FROM pg_catalog.pg_constraint\n' +
992
+ const sql = "SELECT conname, pg_catalog.pg_get_constraintdef(oid, true) AS condef\n" +
993
+ "FROM pg_catalog.pg_constraint\n" +
990
994
  `WHERE conrelid = '${oid}' AND contype = 'f'\n` +
991
- 'ORDER BY conname;';
995
+ "ORDER BY conname;";
992
996
  const rs = await conn.query(sql, []);
993
997
  if (rs.rows.length === 0)
994
998
  return;
995
- out.write('Foreign-key constraints:\n');
999
+ out.write("Foreign-key constraints:\n");
996
1000
  for (const r of rs.rows) {
997
1001
  out.write(` "${cellToString(r[0])}" ${cellToString(r[1])}\n`);
998
1002
  }
@@ -1002,15 +1006,15 @@ const renderForeignKeyConstraintsSection = async (conn, oid, out) => {
1002
1006
  * (incoming FKs from other tables).
1003
1007
  */
1004
1008
  const renderReferencedBySection = async (conn, oid, out) => {
1005
- const sql = 'SELECT conname, conrelid::pg_catalog.regclass,\n' +
1006
- ' pg_catalog.pg_get_constraintdef(oid, true) AS condef\n' +
1007
- 'FROM pg_catalog.pg_constraint\n' +
1009
+ const sql = "SELECT conname, conrelid::pg_catalog.regclass,\n" +
1010
+ " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n" +
1011
+ "FROM pg_catalog.pg_constraint\n" +
1008
1012
  `WHERE confrelid = '${oid}' AND contype = 'f'\n` +
1009
- 'ORDER BY conname;';
1013
+ "ORDER BY conname;";
1010
1014
  const rs = await conn.query(sql, []);
1011
1015
  if (rs.rows.length === 0)
1012
1016
  return;
1013
- out.write('Referenced by:\n');
1017
+ out.write("Referenced by:\n");
1014
1018
  for (const r of rs.rows) {
1015
1019
  out.write(` TABLE "${cellToString(r[1])}" CONSTRAINT "${cellToString(r[0])}" ${cellToString(r[2])}\n`);
1016
1020
  }
@@ -1019,14 +1023,14 @@ const renderReferencedBySection = async (conn, oid, out) => {
1019
1023
  * Render `Triggers:\n name AFTER ... EXECUTE ...` list.
1020
1024
  */
1021
1025
  const renderTriggersSection = async (conn, oid, out) => {
1022
- const sql = 'SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid, true) AS tgdef, t.tgenabled\n' +
1023
- 'FROM pg_catalog.pg_trigger t\n' +
1026
+ const sql = "SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid, true) AS tgdef, t.tgenabled\n" +
1027
+ "FROM pg_catalog.pg_trigger t\n" +
1024
1028
  `WHERE t.tgrelid = '${oid}' AND NOT t.tgisinternal\n` +
1025
- 'ORDER BY 1;';
1029
+ "ORDER BY 1;";
1026
1030
  const rs = await conn.query(sql, []);
1027
1031
  if (rs.rows.length === 0)
1028
1032
  return;
1029
- out.write('Triggers:\n');
1033
+ out.write("Triggers:\n");
1030
1034
  for (const r of rs.rows) {
1031
1035
  out.write(` ${cellToString(r[1])}\n`);
1032
1036
  }
@@ -1039,27 +1043,30 @@ const renderTriggersSection = async (conn, oid, out) => {
1039
1043
  * inside parentheses; we preserve insertion order matching upstream.
1040
1044
  */
1041
1045
  const renderStatisticsObjectsSection = async (conn, oid, out) => {
1042
- const q = fetchStatisticsObjects({ oid, serverVersion: conn.serverVersion });
1046
+ const q = fetchStatisticsObjects({
1047
+ oid,
1048
+ serverVersion: conn.serverVersion,
1049
+ });
1043
1050
  const rs = await conn.query(q.sql, q.params);
1044
1051
  if (rs.rows.length === 0)
1045
1052
  return;
1046
- out.write('Statistics objects:\n');
1053
+ out.write("Statistics objects:\n");
1047
1054
  for (const r of rs.rows) {
1048
- const nsp = cellToString(r[0] ?? '');
1049
- const name = cellToString(r[1] ?? '');
1055
+ const nsp = cellToString(r[0] ?? "");
1056
+ const name = cellToString(r[1] ?? "");
1050
1057
  const ndist = parseBool(r[2]);
1051
1058
  const deps = parseBool(r[3]);
1052
1059
  const mcv = parseBool(r[4]);
1053
- const columns = cellToString(r[5] ?? '');
1054
- const relname = cellToString(r[6] ?? '');
1060
+ const columns = cellToString(r[5] ?? "");
1061
+ const relname = cellToString(r[6] ?? "");
1055
1062
  const kinds = [];
1056
1063
  if (ndist)
1057
- kinds.push('ndistinct');
1064
+ kinds.push("ndistinct");
1058
1065
  if (deps)
1059
- kinds.push('dependencies');
1066
+ kinds.push("dependencies");
1060
1067
  if (mcv)
1061
- kinds.push('mcv');
1062
- const kindStr = kinds.length > 0 ? ` (${kinds.join(', ')})` : '';
1068
+ kinds.push("mcv");
1069
+ const kindStr = kinds.length > 0 ? ` (${kinds.join(", ")})` : "";
1063
1070
  out.write(` "${nsp}"."${name}"${kindStr} ON ${columns} FROM ${relname}\n`);
1064
1071
  }
1065
1072
  };
@@ -1069,13 +1076,16 @@ const renderStatisticsObjectsSection = async (conn, oid, out) => {
1069
1076
  * IN SCHEMA). No-op when the result set is empty.
1070
1077
  */
1071
1078
  const renderPublicationsSection = async (conn, oid, out) => {
1072
- const q = fetchTablePublications({ oid, serverVersion: conn.serverVersion });
1079
+ const q = fetchTablePublications({
1080
+ oid,
1081
+ serverVersion: conn.serverVersion,
1082
+ });
1073
1083
  const rs = await conn.query(q.sql, q.params);
1074
1084
  if (rs.rows.length === 0)
1075
1085
  return;
1076
- out.write('Publications:\n');
1086
+ out.write("Publications:\n");
1077
1087
  for (const r of rs.rows) {
1078
- out.write(` "${cellToString(r[0] ?? '')}"\n`);
1088
+ out.write(` "${cellToString(r[0] ?? "")}"\n`);
1079
1089
  }
1080
1090
  };
1081
1091
  /**
@@ -1084,7 +1094,10 @@ const renderPublicationsSection = async (conn, oid, out) => {
1084
1094
  * error, the section is silently omitted (mirroring upstream behaviour).
1085
1095
  */
1086
1096
  const renderSubscriptionsSection = async (conn, oid, out) => {
1087
- const q = fetchTableSubscriptions({ oid, serverVersion: conn.serverVersion });
1097
+ const q = fetchTableSubscriptions({
1098
+ oid,
1099
+ serverVersion: conn.serverVersion,
1100
+ });
1088
1101
  let rs;
1089
1102
  try {
1090
1103
  rs = await conn.query(q.sql, q.params);
@@ -1096,9 +1109,9 @@ const renderSubscriptionsSection = async (conn, oid, out) => {
1096
1109
  }
1097
1110
  if (rs.rows.length === 0)
1098
1111
  return;
1099
- out.write('Subscriptions:\n');
1112
+ out.write("Subscriptions:\n");
1100
1113
  for (const r of rs.rows) {
1101
- out.write(` "${cellToString(r[0] ?? '')}"\n`);
1114
+ out.write(` "${cellToString(r[0] ?? "")}"\n`);
1102
1115
  }
1103
1116
  };
1104
1117
  /**
@@ -1112,9 +1125,9 @@ const fetchPerColumnFdwOptionsMap = async (conn, oid) => {
1112
1125
  const rs = await conn.query(q.sql, q.params);
1113
1126
  const m = new Map();
1114
1127
  for (const r of rs.rows) {
1115
- const attname = cellToString(r[0] ?? '');
1116
- const opts = cellToString(r[1] ?? '');
1117
- if (attname !== '' && opts !== '')
1128
+ const attname = cellToString(r[0] ?? "");
1129
+ const opts = cellToString(r[1] ?? "");
1130
+ if (attname !== "" && opts !== "")
1118
1131
  m.set(attname, opts);
1119
1132
  }
1120
1133
  return m;
@@ -1131,17 +1144,17 @@ const renderToastOwningTableFooter = async (conn, oid, out) => {
1131
1144
  // would drop the `pg_catalog.` prefix for pg_catalog parents). Look
1132
1145
  // up the parent's schema + relname directly so we can render the
1133
1146
  // schema-qualified form unconditionally.
1134
- const sql = 'SELECT n.nspname, c.relname\n' +
1135
- 'FROM pg_catalog.pg_class c\n' +
1136
- 'JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n' +
1147
+ const sql = "SELECT n.nspname, c.relname\n" +
1148
+ "FROM pg_catalog.pg_class c\n" +
1149
+ "JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" +
1137
1150
  `WHERE c.reltoastrelid = '${oid}'\n` +
1138
- 'LIMIT 1;';
1151
+ "LIMIT 1;";
1139
1152
  const rs = await conn.query(sql, []);
1140
1153
  if (rs.rows.length === 0)
1141
1154
  return;
1142
- const nspname = cellToString(rs.rows[0][0] ?? '');
1143
- const relname = cellToString(rs.rows[0][1] ?? '');
1144
- if (relname === '')
1155
+ const nspname = cellToString(rs.rows[0][0] ?? "");
1156
+ const relname = cellToString(rs.rows[0][1] ?? "");
1157
+ if (relname === "")
1145
1158
  return;
1146
1159
  out.write(`Owning table: "${nspname}.${relname}"\n`);
1147
1160
  };
@@ -1153,13 +1166,13 @@ const renderToastOwningTableFooter = async (conn, oid, out) => {
1153
1166
  * errors, not arbitrary failures.
1154
1167
  */
1155
1168
  const isPermissionDeniedError = (err) => {
1156
- if (err === null || typeof err !== 'object')
1169
+ if (err === null || typeof err !== "object")
1157
1170
  return false;
1158
1171
  const code = err.code;
1159
- if (typeof code === 'string' && code === '42501')
1172
+ if (typeof code === "string" && code === "42501")
1160
1173
  return true;
1161
1174
  const message = err.message;
1162
- if (typeof message === 'string' && /permission denied/i.test(message)) {
1175
+ if (typeof message === "string" && /permission denied/i.test(message)) {
1163
1176
  return true;
1164
1177
  }
1165
1178
  return false;
@@ -1183,10 +1196,10 @@ export const describeOneSequence = async (conn, oid, schema, name, out, popt) =>
1183
1196
  // `Owned by:` AS a footer of the printed table — not as a separate
1184
1197
  // post-table line.
1185
1198
  const ownedSql = "SELECT pg_catalog.quote_ident(nspname) || '.' || pg_catalog.quote_ident(relname) || '.' || pg_catalog.quote_ident(attname)\n" +
1186
- 'FROM pg_catalog.pg_class c\n' +
1187
- 'JOIN pg_catalog.pg_depend d ON c.oid = d.refobjid\n' +
1188
- 'JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n' +
1189
- 'JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid AND a.attnum = d.refobjsubid\n' +
1199
+ "FROM pg_catalog.pg_class c\n" +
1200
+ "JOIN pg_catalog.pg_depend d ON c.oid = d.refobjid\n" +
1201
+ "JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" +
1202
+ "JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid AND a.attnum = d.refobjsubid\n" +
1190
1203
  `WHERE d.classid = 'pg_catalog.pg_class'::regclass AND d.refclassid = 'pg_catalog.pg_class'::regclass\n` +
1191
1204
  ` AND d.objid = '${oid}' AND d.deptype IN ('a', 'i');`;
1192
1205
  const ownRs = await conn.query(ownedSql, []);
@@ -1214,7 +1227,7 @@ export const describeOneFunctionDetails = async (conn, oid, out) => {
1214
1227
  const rs = await conn.query(sql, []);
1215
1228
  if (rs.rows.length > 0) {
1216
1229
  out.write(cellToString(rs.rows[0][0]));
1217
- out.write('\n');
1230
+ out.write("\n");
1218
1231
  }
1219
1232
  };
1220
1233
  /**
@@ -1224,7 +1237,7 @@ export const describeOneViewDetails = async (conn, oid, schema, name, out, popt,
1224
1237
  // Use the table renderer for columns first (views have columns). In
1225
1238
  // verbose mode this also adds the Storage / Stats target / Description
1226
1239
  // columns, matching upstream `\d+ <view>`.
1227
- await describeOneTableDetails(conn, oid, schema, name, 'v', verbose, out, popt, false, hideCompression);
1240
+ await describeOneTableDetails(conn, oid, schema, name, "v", verbose, out, popt, false, hideCompression);
1228
1241
  // The "View definition:" footer (verbose-only) is emitted by
1229
1242
  // describeOneTableDetails as a table footer so it renders flush with the
1230
1243
  // column rows and gets the single trailing blank — matching upstream
@@ -1236,25 +1249,25 @@ export const describeOneViewDetails = async (conn, oid, schema, name, out, popt,
1236
1249
  */
1237
1250
  const headerForRelkind = (relkind, schema, name) => {
1238
1251
  switch (relkind) {
1239
- case 'r':
1252
+ case "r":
1240
1253
  return `Table "${schema}.${name}"`;
1241
- case 'v':
1254
+ case "v":
1242
1255
  return `View "${schema}.${name}"`;
1243
- case 'm':
1256
+ case "m":
1244
1257
  return `Materialized view "${schema}.${name}"`;
1245
- case 'S':
1258
+ case "S":
1246
1259
  return `Sequence "${schema}.${name}"`;
1247
- case 'i':
1260
+ case "i":
1248
1261
  return `Index "${schema}.${name}"`;
1249
- case 'I':
1262
+ case "I":
1250
1263
  return `Partitioned index "${schema}.${name}"`;
1251
- case 'p':
1264
+ case "p":
1252
1265
  return `Partitioned table "${schema}.${name}"`;
1253
- case 'f':
1266
+ case "f":
1254
1267
  return `Foreign table "${schema}.${name}"`;
1255
- case 't':
1268
+ case "t":
1256
1269
  return `TOAST table "${schema}.${name}"`;
1257
- case 'c':
1270
+ case "c":
1258
1271
  return `Composite type "${schema}.${name}"`;
1259
1272
  default:
1260
1273
  return `Relation "${schema}.${name}"`;