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.
- package/README.md +2 -2
- package/dist/analytics.js +35 -33
- package/dist/api.js +34 -34
- package/dist/auth.js +50 -44
- package/dist/cli.js +2 -2
- package/dist/commands/auth.js +58 -52
- package/dist/commands/bootstrap.js +115 -157
- package/dist/commands/branches.js +154 -147
- package/dist/commands/bucket.js +124 -118
- package/dist/commands/checkout.js +49 -49
- package/dist/commands/config.js +212 -88
- package/dist/commands/connection_string.js +62 -62
- package/dist/commands/data_api.js +96 -96
- package/dist/commands/databases.js +23 -23
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/dev.js +114 -114
- package/dist/commands/env.js +43 -43
- package/dist/commands/functions.js +97 -98
- package/dist/commands/index.js +26 -26
- package/dist/commands/init.js +23 -22
- package/dist/commands/ip_allow.js +29 -29
- package/dist/commands/link.js +223 -166
- package/dist/commands/neon_auth.js +381 -363
- package/dist/commands/operations.js +11 -11
- package/dist/commands/orgs.js +8 -8
- package/dist/commands/projects.js +101 -99
- package/dist/commands/psql.js +31 -31
- package/dist/commands/roles.js +21 -21
- package/dist/commands/schema_diff.js +23 -23
- package/dist/commands/set_context.js +17 -17
- package/dist/commands/status.js +17 -17
- package/dist/commands/user.js +5 -5
- package/dist/commands/vpc_endpoints.js +50 -50
- package/dist/config.js +7 -7
- package/dist/config_format.js +5 -5
- package/dist/context.js +23 -16
- package/dist/current_branch_fast_path.js +6 -6
- package/dist/dev/env.js +33 -33
- package/dist/dev/functions.js +4 -4
- package/dist/dev/inputs.js +6 -6
- package/dist/dev/runtime.js +25 -25
- package/dist/env.js +14 -14
- package/dist/env_file.js +13 -13
- package/dist/errors.js +19 -19
- package/dist/functions_api.js +10 -10
- package/dist/help.js +15 -15
- package/dist/index.js +94 -92
- package/dist/log.js +2 -2
- package/dist/pkg.js +5 -5
- package/dist/psql/cli.js +4 -2
- package/dist/psql/command/cmd_cond.js +61 -61
- package/dist/psql/command/cmd_connect.js +159 -154
- package/dist/psql/command/cmd_copy.js +107 -97
- package/dist/psql/command/cmd_describe.js +368 -363
- package/dist/psql/command/cmd_format.js +276 -263
- package/dist/psql/command/cmd_io.js +269 -263
- package/dist/psql/command/cmd_lo.js +74 -66
- package/dist/psql/command/cmd_meta.js +148 -148
- package/dist/psql/command/cmd_misc.js +17 -17
- package/dist/psql/command/cmd_pipeline.js +142 -135
- package/dist/psql/command/cmd_restrict.js +25 -25
- package/dist/psql/command/cmd_show.js +183 -168
- package/dist/psql/command/dispatch.js +26 -26
- package/dist/psql/command/shared.js +14 -14
- package/dist/psql/complete/filenames.js +16 -16
- package/dist/psql/complete/index.js +4 -4
- package/dist/psql/complete/matcher.js +33 -32
- package/dist/psql/complete/psqlVars.js +173 -173
- package/dist/psql/complete/queries.js +5 -3
- package/dist/psql/complete/rules.js +900 -863
- package/dist/psql/core/common.js +136 -133
- package/dist/psql/core/help.js +343 -343
- package/dist/psql/core/mainloop.js +160 -153
- package/dist/psql/core/prompt.js +126 -123
- package/dist/psql/core/settings.js +111 -111
- package/dist/psql/core/sqlHelp.js +150 -150
- package/dist/psql/core/startup.js +211 -205
- package/dist/psql/core/syncVars.js +14 -14
- package/dist/psql/core/variables.js +24 -24
- package/dist/psql/describe/formatters.js +302 -289
- package/dist/psql/describe/processNamePattern.js +28 -28
- package/dist/psql/describe/queries.js +656 -651
- package/dist/psql/index.js +436 -411
- package/dist/psql/io/history.js +36 -36
- package/dist/psql/io/input.js +15 -15
- package/dist/psql/io/lineEditor/buffer.js +27 -25
- package/dist/psql/io/lineEditor/complete.js +15 -15
- package/dist/psql/io/lineEditor/filename.js +22 -22
- package/dist/psql/io/lineEditor/index.js +65 -62
- package/dist/psql/io/lineEditor/keymap.js +325 -318
- package/dist/psql/io/lineEditor/vt100.js +60 -60
- package/dist/psql/io/pgpass.js +18 -18
- package/dist/psql/io/pgservice.js +14 -14
- package/dist/psql/io/psqlrc.js +46 -46
- package/dist/psql/print/aligned.js +175 -166
- package/dist/psql/print/asciidoc.js +51 -51
- package/dist/psql/print/crosstab.js +34 -31
- package/dist/psql/print/csv.js +25 -22
- package/dist/psql/print/html.js +54 -54
- package/dist/psql/print/json.js +12 -12
- package/dist/psql/print/latex.js +118 -118
- package/dist/psql/print/pager.js +28 -26
- package/dist/psql/print/troff.js +48 -48
- package/dist/psql/print/unaligned.js +15 -14
- package/dist/psql/print/units.js +17 -17
- package/dist/psql/scanner/slash.js +48 -46
- package/dist/psql/scanner/sql.js +88 -84
- package/dist/psql/scanner/stringutils.js +21 -17
- package/dist/psql/types/index.js +7 -7
- package/dist/psql/types/scanner.js +8 -8
- package/dist/psql/wire/connection.js +341 -327
- package/dist/psql/wire/copy.js +7 -7
- package/dist/psql/wire/pipeline.js +26 -24
- package/dist/psql/wire/protocol.js +102 -102
- package/dist/psql/wire/sasl.js +62 -62
- package/dist/psql/wire/tls.js +79 -73
- package/dist/storage_api.js +15 -15
- package/dist/test_utils/fixtures.js +34 -31
- package/dist/test_utils/oauth_server.js +5 -5
- package/dist/utils/api_enums.js +13 -13
- package/dist/utils/branch_notice.js +5 -5
- package/dist/utils/branch_picker.js +26 -26
- package/dist/utils/compute_units.js +4 -4
- package/dist/utils/enrichers.js +20 -15
- package/dist/utils/esbuild.js +28 -28
- package/dist/utils/formats.js +1 -1
- package/dist/utils/middlewares.js +3 -3
- package/dist/utils/package_manager.js +68 -0
- package/dist/utils/point_in_time.js +12 -12
- package/dist/utils/psql.js +30 -30
- package/dist/utils/string.js +2 -2
- package/dist/utils/ui.js +9 -9
- package/dist/utils/zip.js +1 -1
- package/dist/writer.js +17 -17
- 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 {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import { fetchForeignTableInfo, fetchInheritedBy, fetchInherits, fetchPartitionKey, fetchPartitionOf, fetchPerColumnFdwOptions, fetchPolicies, fetchStatisticsObjects, fetchTableInfo, fetchTablePublications, fetchTableSubscriptions, } from
|
|
49
|
-
import {
|
|
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
|
|
64
|
-
case
|
|
61
|
+
case "aligned":
|
|
62
|
+
case "wrapped":
|
|
65
63
|
return alignedPrinter;
|
|
66
|
-
case
|
|
64
|
+
case "unaligned":
|
|
67
65
|
return unalignedPrinter;
|
|
68
|
-
case
|
|
66
|
+
case "csv":
|
|
69
67
|
return csvPrinter;
|
|
70
|
-
case
|
|
68
|
+
case "json":
|
|
71
69
|
return jsonPrinter;
|
|
72
|
-
case
|
|
70
|
+
case "html":
|
|
73
71
|
return htmlPrinter;
|
|
74
|
-
case
|
|
72
|
+
case "asciidoc":
|
|
75
73
|
return asciidocPrinter;
|
|
76
|
-
case
|
|
74
|
+
case "latex":
|
|
77
75
|
return latexPrinter;
|
|
78
|
-
case
|
|
76
|
+
case "latex-longtable":
|
|
79
77
|
return latexLongtablePrinter;
|
|
80
|
-
case
|
|
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 ===
|
|
91
|
+
return "";
|
|
92
|
+
if (typeof v === "string")
|
|
95
93
|
return v;
|
|
96
94
|
if (Buffer.isBuffer(v))
|
|
97
|
-
return v.toString(
|
|
98
|
-
if (typeof v ===
|
|
99
|
-
typeof v ===
|
|
100
|
-
typeof v ===
|
|
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) =>
|
|
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 ===
|
|
123
|
+
if (typeof chunk === "string") {
|
|
126
124
|
buf += chunk;
|
|
127
125
|
}
|
|
128
126
|
else {
|
|
129
|
-
buf += chunk.toString(
|
|
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 ===
|
|
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:
|
|
216
|
-
schemavar:
|
|
217
|
-
visibilityrule:
|
|
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 =
|
|
234
|
-
|
|
235
|
-
|
|
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(
|
|
238
|
-
sql +=
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
292
|
-
relkind ===
|
|
293
|
-
relkind ===
|
|
294
|
-
relkind ===
|
|
295
|
-
relkind ===
|
|
296
|
-
relkind ===
|
|
297
|
-
relkind ===
|
|
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 ===
|
|
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 ===
|
|
315
|
-
relkind ===
|
|
316
|
-
relkind ===
|
|
317
|
-
relkind ===
|
|
318
|
-
relkind ===
|
|
319
|
-
relkind ===
|
|
320
|
-
const colSql =
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
?
|
|
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
|
-
|
|
335
|
+
" END AS attstorage" +
|
|
338
336
|
(includeCompression
|
|
339
|
-
?
|
|
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
|
-
|
|
345
|
-
:
|
|
342
|
+
" END AS attcompression"
|
|
343
|
+
: "") +
|
|
346
344
|
(includeStatsTarget
|
|
347
|
-
?
|
|
348
|
-
:
|
|
349
|
-
|
|
350
|
-
:
|
|
351
|
-
|
|
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
|
-
|
|
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 ===
|
|
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(
|
|
364
|
+
const fields = [fakeField("Column"), fakeField("Type")];
|
|
367
365
|
if (!isToast) {
|
|
368
|
-
fields.push(fakeField(
|
|
369
|
-
fields.push(fakeField(
|
|
370
|
-
fields.push(fakeField(
|
|
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(
|
|
371
|
+
fields.push(fakeField("Storage"));
|
|
374
372
|
if (includeCompression)
|
|
375
|
-
fields.push(fakeField(
|
|
373
|
+
fields.push(fakeField("Compression"));
|
|
376
374
|
if (includeStatsTarget)
|
|
377
|
-
fields.push(fakeField(
|
|
378
|
-
fields.push(fakeField(
|
|
375
|
+
fields.push(fakeField("Stats target"));
|
|
376
|
+
fields.push(fakeField("Description"));
|
|
379
377
|
}
|
|
380
378
|
if (hasAnyFdwOptions)
|
|
381
|
-
fields.push(fakeField(
|
|
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]) ===
|
|
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 ?
|
|
391
|
-
let dflt = colDefault ??
|
|
392
|
-
if (identity ===
|
|
393
|
-
dflt =
|
|
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 ===
|
|
396
|
-
dflt =
|
|
393
|
+
else if (identity === "d") {
|
|
394
|
+
dflt = "generated by default as identity";
|
|
397
395
|
}
|
|
398
|
-
else if (generated ===
|
|
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 ===
|
|
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 ??
|
|
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 ?
|
|
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 ?
|
|
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:
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
484
|
-
relkind ===
|
|
485
|
-
relkind ===
|
|
486
|
-
relkind ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
530
|
-
relkind ===
|
|
531
|
-
relkind ===
|
|
532
|
-
relkind ===
|
|
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 ===
|
|
537
|
-
relkind ===
|
|
538
|
-
relkind ===
|
|
539
|
-
relkind ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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:
|
|
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] ??
|
|
611
|
+
relreplident: cellToString(r[2] ?? "d") || "d",
|
|
611
612
|
relispartition: parseBool(r[3]),
|
|
612
|
-
reltablespace: Number(cellToString(r[4] ??
|
|
613
|
-
relam: Number(cellToString(r[5] ??
|
|
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 ===
|
|
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 ?
|
|
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 ?
|
|
655
|
-
if (constraintdef ===
|
|
656
|
-
out.write(
|
|
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 =
|
|
677
|
+
header = "Policies:";
|
|
677
678
|
}
|
|
678
679
|
else if (rowsecurity && forcerowsecurity && tuples > 0) {
|
|
679
|
-
header =
|
|
680
|
+
header = "Policies (forced row security enabled):";
|
|
680
681
|
}
|
|
681
682
|
else if (rowsecurity && !forcerowsecurity && tuples === 0) {
|
|
682
|
-
header =
|
|
683
|
+
header = "Policies (row security enabled): (none)";
|
|
683
684
|
}
|
|
684
685
|
else if (rowsecurity && forcerowsecurity && tuples === 0) {
|
|
685
|
-
header =
|
|
686
|
+
header = "Policies (forced row security enabled): (none)";
|
|
686
687
|
}
|
|
687
688
|
else if (!rowsecurity && tuples > 0) {
|
|
688
|
-
header =
|
|
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 +=
|
|
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 ?
|
|
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 =
|
|
744
|
-
const indent =
|
|
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 ===
|
|
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(
|
|
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 ?
|
|
786
|
-
const indent =
|
|
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 ?
|
|
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 ===
|
|
797
|
-
line +=
|
|
798
|
-
else if (childKind ===
|
|
799
|
-
line +=
|
|
797
|
+
if (childKind === "p" || childKind === "I")
|
|
798
|
+
line += ", PARTITIONED";
|
|
799
|
+
else if (childKind === "f")
|
|
800
|
+
line += ", FOREIGN";
|
|
800
801
|
if (detached)
|
|
801
|
-
line +=
|
|
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 ===
|
|
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 ===
|
|
823
|
-
if (!isCatalog && ri ===
|
|
823
|
+
const isCatalog = schema === "pg_catalog";
|
|
824
|
+
if (!isCatalog && ri === "d")
|
|
824
825
|
return;
|
|
825
|
-
if (isCatalog && ri ===
|
|
826
|
+
if (isCatalog && ri === "n")
|
|
826
827
|
return;
|
|
827
|
-
const label = ri ===
|
|
828
|
-
?
|
|
829
|
-
: ri ===
|
|
830
|
-
?
|
|
831
|
-
: ri ===
|
|
832
|
-
?
|
|
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 ===
|
|
843
|
-
relkind ===
|
|
844
|
-
relkind ===
|
|
845
|
-
relkind ===
|
|
846
|
-
relkind ===
|
|
847
|
-
relkind ===
|
|
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 =
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
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
|
-
|
|
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(
|
|
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]) ===
|
|
894
|
-
const isUnique = String(r[2]) ===
|
|
895
|
-
const isClustered = String(r[3]) ===
|
|
896
|
-
const isValid = String(r[4]) ===
|
|
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 ?
|
|
900
|
-
const condeferrable = String(r[8]) ===
|
|
901
|
-
const condeferred = String(r[9]) ===
|
|
902
|
-
const isReplIdent = String(r[10]) ===
|
|
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(
|
|
907
|
+
const usingPos = indexdef.indexOf(" USING ");
|
|
907
908
|
const tail = usingPos >= 0 ? indexdef.slice(usingPos + 7) : indexdef;
|
|
908
|
-
if (contype ===
|
|
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 +=
|
|
920
|
+
line += " PRIMARY KEY,";
|
|
920
921
|
}
|
|
921
922
|
else if (isUnique) {
|
|
922
|
-
line += contype ===
|
|
923
|
+
line += contype === "u" ? " UNIQUE CONSTRAINT," : " UNIQUE,";
|
|
923
924
|
}
|
|
924
925
|
line += ` ${tail}`;
|
|
925
926
|
if (condeferrable)
|
|
926
|
-
line +=
|
|
927
|
+
line += " DEFERRABLE";
|
|
927
928
|
if (condeferred)
|
|
928
|
-
line +=
|
|
929
|
+
line += " INITIALLY DEFERRED";
|
|
929
930
|
}
|
|
930
931
|
if (isClustered)
|
|
931
|
-
line +=
|
|
932
|
+
line += " CLUSTER";
|
|
932
933
|
if (!isValid)
|
|
933
|
-
line +=
|
|
934
|
+
line += " INVALID";
|
|
934
935
|
if (isReplIdent)
|
|
935
|
-
line +=
|
|
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 =
|
|
944
|
-
|
|
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
|
-
|
|
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(
|
|
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({
|
|
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(
|
|
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 +=
|
|
982
|
+
line += " NO INHERIT";
|
|
979
983
|
else if (!isLocal)
|
|
980
|
-
line +=
|
|
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 =
|
|
989
|
-
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
1023
|
-
|
|
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
|
-
|
|
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(
|
|
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({
|
|
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(
|
|
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(
|
|
1064
|
+
kinds.push("ndistinct");
|
|
1058
1065
|
if (deps)
|
|
1059
|
-
kinds.push(
|
|
1066
|
+
kinds.push("dependencies");
|
|
1060
1067
|
if (mcv)
|
|
1061
|
-
kinds.push(
|
|
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({
|
|
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(
|
|
1086
|
+
out.write("Publications:\n");
|
|
1077
1087
|
for (const r of rs.rows) {
|
|
1078
|
-
out.write(` "${cellToString(r[0] ??
|
|
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({
|
|
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(
|
|
1112
|
+
out.write("Subscriptions:\n");
|
|
1100
1113
|
for (const r of rs.rows) {
|
|
1101
|
-
out.write(` "${cellToString(r[0] ??
|
|
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 !==
|
|
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 =
|
|
1135
|
-
|
|
1136
|
-
|
|
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
|
-
|
|
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 !==
|
|
1169
|
+
if (err === null || typeof err !== "object")
|
|
1157
1170
|
return false;
|
|
1158
1171
|
const code = err.code;
|
|
1159
|
-
if (typeof code ===
|
|
1172
|
+
if (typeof code === "string" && code === "42501")
|
|
1160
1173
|
return true;
|
|
1161
1174
|
const message = err.message;
|
|
1162
|
-
if (typeof 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
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
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(
|
|
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,
|
|
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
|
|
1252
|
+
case "r":
|
|
1240
1253
|
return `Table "${schema}.${name}"`;
|
|
1241
|
-
case
|
|
1254
|
+
case "v":
|
|
1242
1255
|
return `View "${schema}.${name}"`;
|
|
1243
|
-
case
|
|
1256
|
+
case "m":
|
|
1244
1257
|
return `Materialized view "${schema}.${name}"`;
|
|
1245
|
-
case
|
|
1258
|
+
case "S":
|
|
1246
1259
|
return `Sequence "${schema}.${name}"`;
|
|
1247
|
-
case
|
|
1260
|
+
case "i":
|
|
1248
1261
|
return `Index "${schema}.${name}"`;
|
|
1249
|
-
case
|
|
1262
|
+
case "I":
|
|
1250
1263
|
return `Partitioned index "${schema}.${name}"`;
|
|
1251
|
-
case
|
|
1264
|
+
case "p":
|
|
1252
1265
|
return `Partitioned table "${schema}.${name}"`;
|
|
1253
|
-
case
|
|
1266
|
+
case "f":
|
|
1254
1267
|
return `Foreign table "${schema}.${name}"`;
|
|
1255
|
-
case
|
|
1268
|
+
case "t":
|
|
1256
1269
|
return `TOAST table "${schema}.${name}"`;
|
|
1257
|
-
case
|
|
1270
|
+
case "c":
|
|
1258
1271
|
return `Composite type "${schema}.${name}"`;
|
|
1259
1272
|
default:
|
|
1260
1273
|
return `Relation "${schema}.${name}"`;
|