neonctl 2.22.2 → 2.23.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 +84 -0
- package/analytics.js +5 -2
- package/commands/branches.js +9 -1
- package/commands/connection_string.js +9 -1
- package/commands/functions.js +277 -0
- package/commands/index.js +4 -0
- package/commands/neon_auth.js +1013 -0
- package/commands/projects.js +9 -1
- package/commands/psql.js +6 -1
- package/functions_api.js +44 -0
- package/package.json +15 -5
- package/psql/cli.js +51 -0
- package/psql/command/cmd_cond.js +437 -0
- package/psql/command/cmd_connect.js +815 -0
- package/psql/command/cmd_copy.js +1025 -0
- package/psql/command/cmd_describe.js +1810 -0
- package/psql/command/cmd_format.js +909 -0
- package/psql/command/cmd_io.js +2187 -0
- package/psql/command/cmd_lo.js +385 -0
- package/psql/command/cmd_meta.js +970 -0
- package/psql/command/cmd_misc.js +187 -0
- package/psql/command/cmd_pipeline.js +1141 -0
- package/psql/command/cmd_restrict.js +171 -0
- package/psql/command/cmd_show.js +751 -0
- package/psql/command/dispatch.js +343 -0
- package/psql/command/inputQueue.js +42 -0
- package/psql/command/shared.js +71 -0
- package/psql/complete/filenames.js +139 -0
- package/psql/complete/index.js +104 -0
- package/psql/complete/matcher.js +314 -0
- package/psql/complete/psqlVars.js +247 -0
- package/psql/complete/queries.js +491 -0
- package/psql/complete/rules.js +2387 -0
- package/psql/core/common.js +1250 -0
- package/psql/core/help.js +576 -0
- package/psql/core/mainloop.js +1353 -0
- package/psql/core/prompt.js +437 -0
- package/psql/core/settings.js +684 -0
- package/psql/core/sqlHelp.js +1066 -0
- package/psql/core/startup.js +840 -0
- package/psql/core/syncVars.js +116 -0
- package/psql/core/variables.js +287 -0
- package/psql/describe/formatters.js +1277 -0
- package/psql/describe/processNamePattern.js +270 -0
- package/psql/describe/queries.js +2373 -0
- package/psql/describe/versionGate.js +43 -0
- package/psql/index.js +2005 -0
- package/psql/io/history.js +299 -0
- package/psql/io/input.js +120 -0
- package/psql/io/lineEditor/buffer.js +323 -0
- package/psql/io/lineEditor/complete.js +227 -0
- package/psql/io/lineEditor/filename.js +159 -0
- package/psql/io/lineEditor/index.js +891 -0
- package/psql/io/lineEditor/keymap.js +738 -0
- package/psql/io/lineEditor/vt100.js +363 -0
- package/psql/io/pgpass.js +202 -0
- package/psql/io/pgservice.js +194 -0
- package/psql/io/psqlrc.js +422 -0
- package/psql/print/aligned.js +1756 -0
- package/psql/print/asciidoc.js +248 -0
- package/psql/print/crosstab.js +460 -0
- package/psql/print/csv.js +92 -0
- package/psql/print/html.js +258 -0
- package/psql/print/json.js +96 -0
- package/psql/print/latex.js +396 -0
- package/psql/print/pager.js +265 -0
- package/psql/print/troff.js +258 -0
- package/psql/print/unaligned.js +118 -0
- package/psql/print/units.js +135 -0
- package/psql/scanner/slash.js +513 -0
- package/psql/scanner/sql.js +910 -0
- package/psql/scanner/stringutils.js +390 -0
- package/psql/types/backslash.js +1 -0
- package/psql/types/connection.js +1 -0
- package/psql/types/index.js +7 -0
- package/psql/types/printer.js +1 -0
- package/psql/types/repl.js +1 -0
- package/psql/types/scanner.js +24 -0
- package/psql/types/settings.js +1 -0
- package/psql/types/variables.js +1 -0
- package/psql/wire/connection.js +2844 -0
- package/psql/wire/copy.js +108 -0
- package/psql/wire/notify.js +59 -0
- package/psql/wire/pipeline.js +519 -0
- package/psql/wire/protocol.js +466 -0
- package/psql/wire/sasl.js +296 -0
- package/psql/wire/tls.js +596 -0
- package/test_utils/fixtures.js +1 -0
- package/utils/esbuild.js +147 -0
- package/utils/psql.js +107 -11
- package/utils/zip.js +4 -0
- package/writer.js +1 -1
- package/commands/auth.test.js +0 -211
- package/commands/branches.test.js +0 -460
- package/commands/checkout.test.js +0 -170
- package/commands/connection_string.test.js +0 -196
- package/commands/data_api.test.js +0 -169
- package/commands/databases.test.js +0 -39
- package/commands/help.test.js +0 -9
- package/commands/init.test.js +0 -56
- package/commands/ip_allow.test.js +0 -59
- package/commands/link.test.js +0 -381
- package/commands/operations.test.js +0 -7
- package/commands/orgs.test.js +0 -7
- package/commands/projects.test.js +0 -144
- package/commands/psql.test.js +0 -49
- package/commands/roles.test.js +0 -37
- package/commands/set_context.test.js +0 -159
- package/commands/vpc_endpoints.test.js +0 -69
- package/context.test.js +0 -119
- package/env.test.js +0 -55
- package/utils/formats.test.js +0 -32
- package/writer.test.js +0 -104
|
@@ -0,0 +1,1810 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* psql `\d*` describe-command dispatchers.
|
|
3
|
+
*
|
|
4
|
+
* Each command takes an optional pattern, parses verbose / show-system
|
|
5
|
+
* suffixes from the command name, runs the appropriate SQL template
|
|
6
|
+
* from {@link '../describe/queries.js'} through the pattern parser, and
|
|
7
|
+
* prints the result via the aligned printer.
|
|
8
|
+
*
|
|
9
|
+
* Command name suffix decoding:
|
|
10
|
+
*
|
|
11
|
+
* - Trailing `+` → `verbose=true` (extra columns)
|
|
12
|
+
* - Trailing `S` → `showSystem=true` (include `pg_*` schemas)
|
|
13
|
+
* - Both may combine: `\dtS+`, `\dt+S`, etc.
|
|
14
|
+
*
|
|
15
|
+
* Dispatch model: every `\d*` family registers itself as a separate
|
|
16
|
+
* primary name (e.g. `dt`, `dt+`, `dtS`, `dtS+`). That's not how psql
|
|
17
|
+
* itself does it — psql parses a single `\d` token and then peels off
|
|
18
|
+
* letter-by-letter — but our `BackslashRegistry` is keyed by full
|
|
19
|
+
* command name and is much easier to read this way. The semantic
|
|
20
|
+
* outcome is identical.
|
|
21
|
+
*
|
|
22
|
+
* For `\d <name>` (the bare describe), we look the relation up via
|
|
23
|
+
* {@link lookupOneRelation} and dispatch to the right `describeOne*`
|
|
24
|
+
* renderer based on relkind:
|
|
25
|
+
*
|
|
26
|
+
* - 'r' / 'p' / 'f' (regular, partitioned, foreign) →
|
|
27
|
+
* {@link describeOneTableDetails}
|
|
28
|
+
* - 'v' → {@link describeOneViewDetails}
|
|
29
|
+
* - 'S' → {@link describeOneSequence}
|
|
30
|
+
* - 'i' / 'I' (index, partitioned index) → table-detail with index header
|
|
31
|
+
* - 'm' → table-detail with materialized-view header
|
|
32
|
+
*
|
|
33
|
+
* Connection guard: every command checks `ctx.settings.db` is non-null
|
|
34
|
+
* and emits `\<cmd>: no current connection` to stderr on miss.
|
|
35
|
+
*/
|
|
36
|
+
import { describeOneSequence, describeOneTableDetails, describeOneViewDetails, lookupOneRelation, runListQuery, } from '../describe/formatters.js';
|
|
37
|
+
import { describeAccessMethods, describeAggregates, describeConfigurationParameters, describeFunctions, describeOperators, describeRoleGrants, describeRoles, describeSubscriptions, describeTableDetails, describeTablespaces, describeTypes, listAllDbs, listCasts, listCollations, listConversions, listDbRoleSettings, listDefaultACLs, listDomains, listEventTriggers, listExtendedStats, listExtensions, listForeignDataWrappers, listForeignServers, listForeignTables, listLanguages, listLargeObjects, listOpFamilyFunctions, listOpFamilyOperators, listOperatorClasses, listOperatorFamilies, listPartitionedTables, listPublications, listSchemas, listTSConfigs, listTSDictionaries, listTSParsers, listTSTemplates, listTables, listUserMappings, objectDescription, permissionsList, } from '../describe/queries.js';
|
|
38
|
+
import { processSQLNamePattern, } from '../describe/processNamePattern.js';
|
|
39
|
+
import { writeErr } from './shared.js';
|
|
40
|
+
/**
|
|
41
|
+
* Helper: split a `\dXY+` command name into base, verbose, showSystem.
|
|
42
|
+
*
|
|
43
|
+
* "dtS+" → { base: "dt", verbose: true, showSystem: true }
|
|
44
|
+
* "df+" → { base: "df", verbose: true, showSystem: false }
|
|
45
|
+
* "dnS" → { base: "dn", verbose: false, showSystem: true }
|
|
46
|
+
*/
|
|
47
|
+
const decodeSuffix = (cmdName, base) => {
|
|
48
|
+
const tail = cmdName.slice(base.length);
|
|
49
|
+
let verbose = false;
|
|
50
|
+
let showSystem = false;
|
|
51
|
+
// Suffix order is unrestricted in psql.
|
|
52
|
+
for (const ch of tail) {
|
|
53
|
+
if (ch === '+')
|
|
54
|
+
verbose = true;
|
|
55
|
+
else if (ch === 'S')
|
|
56
|
+
showSystem = true;
|
|
57
|
+
}
|
|
58
|
+
return { verbose, showSystem };
|
|
59
|
+
};
|
|
60
|
+
/** Resolve the current connection or null. */
|
|
61
|
+
const conn = (ctx) => ctx.settings.db;
|
|
62
|
+
/** Emit "no current connection" error. */
|
|
63
|
+
const noConn = (ctx) => {
|
|
64
|
+
writeErr(`\\${ctx.cmdName}: no current connection\n`);
|
|
65
|
+
return { status: 'error', errorWritten: true };
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Read the connection's current database. Mirrors upstream's
|
|
69
|
+
* `PQdb(pset.db)` — used by the cross-database check. PgConnection
|
|
70
|
+
* exposes `database` as a getter; mocks typically pass it as a record
|
|
71
|
+
* property. Returns `''` on miss; callers compare case-sensitively
|
|
72
|
+
* (matching upstream's `strcmp`).
|
|
73
|
+
*/
|
|
74
|
+
const currentDb = (c) => {
|
|
75
|
+
const meta = c;
|
|
76
|
+
return typeof meta.database === 'string' ? meta.database : '';
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Validate a `processSQLNamePattern` result against the command's max
|
|
80
|
+
* dotted-name budget. Mirrors the dot-count check upstream's
|
|
81
|
+
* `processSQLNamePattern` performs after parsing:
|
|
82
|
+
*
|
|
83
|
+
* - `dotCount > maxDots` → "improper qualified name (too many dotted names)"
|
|
84
|
+
* - `dotCount == 2 && maxDots == 2 && dbLiteral != current_db` →
|
|
85
|
+
* "cross-database references are not implemented"
|
|
86
|
+
*
|
|
87
|
+
* Pass `maxDots = 0` for commands that don't accept schema-qualified
|
|
88
|
+
* patterns (`\dA`, `\dx`, `\dn`, `\db`, `\des`, …). Pass `maxDots = 2`
|
|
89
|
+
* for the schema-qualified-with-db family (`\dt`, `\df`, `\dD`, …) so
|
|
90
|
+
* the 2-dot case emits the dedicated cross-database error rather than
|
|
91
|
+
* a generic "too many" message.
|
|
92
|
+
*
|
|
93
|
+
* Returns `null` on success; the formatted error string otherwise.
|
|
94
|
+
*/
|
|
95
|
+
const validatePattern = (pattern, result, maxDots, curDb) => {
|
|
96
|
+
if (pattern === null)
|
|
97
|
+
return null;
|
|
98
|
+
if (result.dotCount > maxDots) {
|
|
99
|
+
return `improper qualified name (too many dotted names): ${pattern}`;
|
|
100
|
+
}
|
|
101
|
+
if (maxDots >= 1 &&
|
|
102
|
+
result.dotCount === maxDots &&
|
|
103
|
+
result.dbLiteral !== null &&
|
|
104
|
+
result.dbLiteral !== curDb) {
|
|
105
|
+
return `cross-database references are not implemented: ${pattern}`;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
};
|
|
109
|
+
const runWithPattern = async (ctx, pattern, query, patternOpts, maxDots = 2, overrides = {}) => {
|
|
110
|
+
const c = conn(ctx);
|
|
111
|
+
if (!c)
|
|
112
|
+
return noConn(ctx);
|
|
113
|
+
const result = processSQLNamePattern({ ...patternOpts, pattern });
|
|
114
|
+
const dotErr = validatePattern(pattern, result, maxDots, currentDb(c));
|
|
115
|
+
if (dotErr !== null) {
|
|
116
|
+
writeErr(`${dotErr}\n`);
|
|
117
|
+
return { status: 'error', errorWritten: true };
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const basePopt = ctx.settings.popt;
|
|
121
|
+
const popt = overrides.suppressDefaultFooter
|
|
122
|
+
? { ...basePopt, topt: { ...basePopt.topt, defaultFooter: false } }
|
|
123
|
+
: basePopt;
|
|
124
|
+
await runListQuery(c, query, result, process.stdout, popt);
|
|
125
|
+
return { status: 'ok' };
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
writeErr(`\\${ctx.cmdName}: ${errMsg(err)}\n`);
|
|
129
|
+
return { status: 'error', errorWritten: true };
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Apply two distinct {@link NamePatternResult}s to a query that emits two
|
|
134
|
+
* placeholder occurrences (one per pattern). Mirrors `applyPattern` but
|
|
135
|
+
* threads each result into a single occurrence so they can carry
|
|
136
|
+
* different column targets (e.g. `\dAc <am> <type>` filters
|
|
137
|
+
* `am.amname` first and `t.typname` second). When a slot's result
|
|
138
|
+
* carries no conditions we substitute the `true` tautology so the
|
|
139
|
+
* surrounding `AND`/`WHERE` chain stays well-formed.
|
|
140
|
+
*
|
|
141
|
+
* Returns a query string with the placeholders replaced and a unified
|
|
142
|
+
* parameter list renumbered so `$N` references stay distinct.
|
|
143
|
+
*/
|
|
144
|
+
const applyTwoPatterns = (sql, baseParams, results) => {
|
|
145
|
+
const placeholder = 'true /* TODO(WP-20): pattern matching */';
|
|
146
|
+
let rendered = sql;
|
|
147
|
+
const params = [...baseParams];
|
|
148
|
+
for (const result of results) {
|
|
149
|
+
const idx = rendered.indexOf(placeholder);
|
|
150
|
+
if (idx < 0)
|
|
151
|
+
break;
|
|
152
|
+
const conds = [
|
|
153
|
+
...result.schemaConditions,
|
|
154
|
+
...result.nameConditions,
|
|
155
|
+
...result.visibilityConditions,
|
|
156
|
+
];
|
|
157
|
+
const slotOffset = params.length;
|
|
158
|
+
const renumbered = conds.map((c) => c.replace(/\$(\d+)/g, (_, n) => `$${Number(n) + slotOffset}`));
|
|
159
|
+
params.push(...result.params);
|
|
160
|
+
const replacement = renumbered.length === 0 ? 'true' : `(${renumbered.join(' AND ')})`;
|
|
161
|
+
rendered =
|
|
162
|
+
rendered.slice(0, idx) +
|
|
163
|
+
replacement +
|
|
164
|
+
rendered.slice(idx + placeholder.length);
|
|
165
|
+
}
|
|
166
|
+
return { sql: rendered, params };
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Run a list query with two distinct pattern slots. Builds the final
|
|
170
|
+
* SQL via {@link applyTwoPatterns}, hands it to {@link runListQuery}
|
|
171
|
+
* with an empty pattern result (so the second-pass `applyPattern` is a
|
|
172
|
+
* no-op — all placeholders are already resolved).
|
|
173
|
+
*
|
|
174
|
+
* Note: we discard the query builder's `params` because the two-pattern
|
|
175
|
+
* builders (`listOperatorClasses`, `listDbRoleSettings`) emit the raw
|
|
176
|
+
* user strings there as a courtesy for single-pattern callers. The
|
|
177
|
+
* regex-converted values come from `results[*].params` instead, so
|
|
178
|
+
* threading the originals would introduce unreferenced bind values.
|
|
179
|
+
*/
|
|
180
|
+
const runDualPatternList = async (ctx, query, results) => {
|
|
181
|
+
const c = conn(ctx);
|
|
182
|
+
if (!c)
|
|
183
|
+
return noConn(ctx);
|
|
184
|
+
const { sql, params } = applyTwoPatterns(query.sql, [], results);
|
|
185
|
+
const finalQuery = {
|
|
186
|
+
...query,
|
|
187
|
+
sql,
|
|
188
|
+
params,
|
|
189
|
+
};
|
|
190
|
+
const empty = {
|
|
191
|
+
schemaConditions: [],
|
|
192
|
+
nameConditions: [],
|
|
193
|
+
visibilityConditions: [],
|
|
194
|
+
params: [],
|
|
195
|
+
dotCount: 0,
|
|
196
|
+
dbLiteral: null,
|
|
197
|
+
};
|
|
198
|
+
try {
|
|
199
|
+
await runListQuery(c, finalQuery, empty, process.stdout, ctx.settings.popt);
|
|
200
|
+
return { status: 'ok' };
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
writeErr(`\\${ctx.cmdName}: ${errMsg(err)}\n`);
|
|
204
|
+
return { status: 'error', errorWritten: true };
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Apply per-argument type patterns into the `ARG_PATTERN_<i>`
|
|
209
|
+
* placeholders emitted by {@link describeFunctions} and
|
|
210
|
+
* {@link describeOperators}. Each non-`-` arg pattern produces a set of
|
|
211
|
+
* conditions against the `t<i>` / `nt<i>` join, matched against
|
|
212
|
+
* `typname`, `nt<i>.nspname`, and the formatted-type expression — same
|
|
213
|
+
* semantics as upstream's `validateSQLNamePattern(... ft, tiv, ...)`
|
|
214
|
+
* call in `describe.c`. `-` slots emit a literal `typname IS NULL`
|
|
215
|
+
* check at SQL build time and don't generate any conditions here.
|
|
216
|
+
*
|
|
217
|
+
* Returns the rewritten SQL plus the merged parameter list (renumbered
|
|
218
|
+
* so `$N` slots from each arg pattern don't collide with prior slots).
|
|
219
|
+
*/
|
|
220
|
+
const applyArgPatterns = (sql, baseParams, argResults) => {
|
|
221
|
+
let rendered = sql;
|
|
222
|
+
const params = [...baseParams];
|
|
223
|
+
for (let i = 0; i < argResults.length; i++) {
|
|
224
|
+
const placeholder = `true /* ARG_PATTERN_${i} */`;
|
|
225
|
+
const idx = rendered.indexOf(placeholder);
|
|
226
|
+
if (idx < 0)
|
|
227
|
+
continue;
|
|
228
|
+
const r = argResults[i];
|
|
229
|
+
let replacement = 'true';
|
|
230
|
+
if (r !== null) {
|
|
231
|
+
const conds = [
|
|
232
|
+
...r.schemaConditions,
|
|
233
|
+
...r.nameConditions,
|
|
234
|
+
...r.visibilityConditions,
|
|
235
|
+
];
|
|
236
|
+
if (conds.length > 0) {
|
|
237
|
+
const slotOffset = params.length;
|
|
238
|
+
const renumbered = conds.map((c) => c.replace(/\$(\d+)/g, (_, n) => `$${Number(n) + slotOffset}`));
|
|
239
|
+
params.push(...r.params);
|
|
240
|
+
replacement = `(${renumbered.join(' AND ')})`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
rendered =
|
|
244
|
+
rendered.slice(0, idx) +
|
|
245
|
+
replacement +
|
|
246
|
+
rendered.slice(idx + placeholder.length);
|
|
247
|
+
}
|
|
248
|
+
return { sql: rendered, params };
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Upstream `map_typename_pattern()`: a few aliases the user can type
|
|
252
|
+
* (`int`, `float`, `decimal`, …) get rewritten to the canonical type
|
|
253
|
+
* names that appear in `pg_type.typname` / `format_type()`. Mirrors
|
|
254
|
+
* `describe.c::map_typename_pattern`. Pattern matching is otherwise
|
|
255
|
+
* literal, so this is the only place the user's input gets rewritten.
|
|
256
|
+
*/
|
|
257
|
+
const TYPENAME_ALIASES = {
|
|
258
|
+
decimal: 'numeric',
|
|
259
|
+
float: 'double precision',
|
|
260
|
+
int: 'integer',
|
|
261
|
+
'bool[]': 'boolean[]',
|
|
262
|
+
'decimal[]': 'numeric[]',
|
|
263
|
+
'float[]': 'double precision[]',
|
|
264
|
+
'float4[]': 'real[]',
|
|
265
|
+
'float8[]': 'double precision[]',
|
|
266
|
+
'int[]': 'integer[]',
|
|
267
|
+
'int2[]': 'smallint[]',
|
|
268
|
+
'int4[]': 'integer[]',
|
|
269
|
+
'int8[]': 'bigint[]',
|
|
270
|
+
'time[]': 'time without time zone[]',
|
|
271
|
+
'timetz[]': 'time with time zone[]',
|
|
272
|
+
'timestamp[]': 'timestamp without time zone[]',
|
|
273
|
+
'timestamptz[]': 'timestamp with time zone[]',
|
|
274
|
+
'varbit[]': 'bit varying[]',
|
|
275
|
+
'varchar[]': 'character varying[]',
|
|
276
|
+
};
|
|
277
|
+
const mapTypenamePattern = (pattern) => TYPENAME_ALIASES[pattern.toLowerCase()] ?? pattern;
|
|
278
|
+
/**
|
|
279
|
+
* Process a per-argument type pattern slot for `\df` / `\do`. `-`
|
|
280
|
+
* returns null (the SQL builder handles the literal `IS NULL` check);
|
|
281
|
+
* any other value goes through {@link processSQLNamePattern} configured
|
|
282
|
+
* with the `t<i>` / `nt<i>` join columns and the formatted-type
|
|
283
|
+
* altnamevar so `\df foo int4` matches `oid -> integer` correctly.
|
|
284
|
+
*/
|
|
285
|
+
const processArgPattern = (slot, raw) => {
|
|
286
|
+
if (raw === '-')
|
|
287
|
+
return null;
|
|
288
|
+
const mapped = mapTypenamePattern(raw);
|
|
289
|
+
return processSQLNamePattern({
|
|
290
|
+
pattern: mapped,
|
|
291
|
+
schemavar: `nt${slot}.nspname`,
|
|
292
|
+
namevar: `t${slot}.typname`,
|
|
293
|
+
altnamevar: `pg_catalog.format_type(t${slot}.oid, NULL)`,
|
|
294
|
+
visibilityrule: `pg_catalog.pg_type_is_visible(t${slot}.oid)`,
|
|
295
|
+
});
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* Drive a `\df` / `\do` style command: collect the main pattern, then
|
|
299
|
+
* any extra args as per-arg type filters, splice them all into the
|
|
300
|
+
* query, and print. Returns the collected arg patterns so the SQL
|
|
301
|
+
* builder upstream can mirror them as joins.
|
|
302
|
+
*/
|
|
303
|
+
const collectArgPatterns = (ctx) => {
|
|
304
|
+
const args = [];
|
|
305
|
+
// Upstream caps `\do` at 2 args internally but reads all of them
|
|
306
|
+
// from the scanner; describeFunctions caps at FUNC_MAX_ARGS (100).
|
|
307
|
+
// We let the caller decide what to do with extras (mostly: ignore).
|
|
308
|
+
for (;;) {
|
|
309
|
+
const arg = ctx.nextArg('normal');
|
|
310
|
+
if (arg === null)
|
|
311
|
+
break;
|
|
312
|
+
args.push(arg);
|
|
313
|
+
}
|
|
314
|
+
return args;
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Run a `\df` / `\do` query: handles the main pattern AND the per-arg
|
|
318
|
+
* type filters in one go. Mirrors {@link runWithPattern}, but also
|
|
319
|
+
* threads per-arg `NamePatternResult`s into the `ARG_PATTERN_<i>`
|
|
320
|
+
* placeholders before handing off to {@link runListQuery}.
|
|
321
|
+
*/
|
|
322
|
+
const runFunctionOrOperatorQuery = async (ctx, pattern, argPatternResults, query, patternOpts) => {
|
|
323
|
+
const c = conn(ctx);
|
|
324
|
+
if (!c)
|
|
325
|
+
return noConn(ctx);
|
|
326
|
+
const result = processSQLNamePattern({ ...patternOpts, pattern });
|
|
327
|
+
const dotErr = validatePattern(pattern, result, 2, currentDb(c));
|
|
328
|
+
if (dotErr !== null) {
|
|
329
|
+
writeErr(`${dotErr}\n`);
|
|
330
|
+
return { status: 'error', errorWritten: true };
|
|
331
|
+
}
|
|
332
|
+
// Substitute the arg-pattern placeholders first so the params they
|
|
333
|
+
// contribute precede the main pattern's `$N` allocations.
|
|
334
|
+
const { sql, params } = applyArgPatterns(query.sql, query.params, argPatternResults);
|
|
335
|
+
const finalQuery = {
|
|
336
|
+
...query,
|
|
337
|
+
sql,
|
|
338
|
+
params,
|
|
339
|
+
};
|
|
340
|
+
try {
|
|
341
|
+
await runListQuery(c, finalQuery, result, process.stdout, ctx.settings.popt);
|
|
342
|
+
return { status: 'ok' };
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
writeErr(`\\${ctx.cmdName}: ${errMsg(err)}\n`);
|
|
346
|
+
return { status: 'error', errorWritten: true };
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
const errMsg = (err) => err instanceof Error ? err.message : String(err);
|
|
350
|
+
// ===========================================================================
|
|
351
|
+
// Individual command specs
|
|
352
|
+
// ===========================================================================
|
|
353
|
+
// ---- \d / \d <name> / \dt / \di / \dv / \dm / \ds / \dE -------------------
|
|
354
|
+
/** Bare `\d [pattern]` — list-or-detail. */
|
|
355
|
+
const makeDescribeCmd = (baseName) => ({
|
|
356
|
+
name: baseName,
|
|
357
|
+
run: async (ctx) => {
|
|
358
|
+
const pattern = ctx.nextArg('normal');
|
|
359
|
+
const c = conn(ctx);
|
|
360
|
+
if (!c)
|
|
361
|
+
return noConn(ctx);
|
|
362
|
+
const { verbose, showSystem } = decodeSuffix(ctx.cmdName, baseName);
|
|
363
|
+
// If pattern looks like a single concrete name (no wildcards),
|
|
364
|
+
// try a per-relation detail; otherwise list.
|
|
365
|
+
if (pattern && !/[*?]/.test(pattern)) {
|
|
366
|
+
// Pass the raw pattern straight through — lookupOneRelation folds /
|
|
367
|
+
// dequotes / splits schema.name via processSQLNamePattern.
|
|
368
|
+
try {
|
|
369
|
+
const rel = await lookupOneRelation(c, pattern);
|
|
370
|
+
if (rel) {
|
|
371
|
+
await dispatchDetail(ctx, c, rel, verbose);
|
|
372
|
+
return { status: 'ok' };
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch (err) {
|
|
376
|
+
writeErr(`\\${ctx.cmdName}: ${errMsg(err)}\n`);
|
|
377
|
+
return { status: 'error', errorWritten: true };
|
|
378
|
+
}
|
|
379
|
+
// Fall through to list (mirrors upstream behaviour: if no exact
|
|
380
|
+
// relation, treat the name as a list pattern).
|
|
381
|
+
}
|
|
382
|
+
// `\d`-family `x` suffix toggles expanded mode for the LIST view
|
|
383
|
+
// only — upstream `command.c` declines to apply it when a pattern is
|
|
384
|
+
// present and would dispatch to per-relation detail rendering.
|
|
385
|
+
const forceExpanded = ctx.cmdName.slice(baseName.length).includes('x') && pattern === null;
|
|
386
|
+
const savedPopt = ctx.settings.popt;
|
|
387
|
+
if (forceExpanded) {
|
|
388
|
+
ctx.settings.popt = {
|
|
389
|
+
...savedPopt,
|
|
390
|
+
topt: { ...savedPopt.topt, expanded: 'on' },
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
// List mode — use either describeTableDetails (for bare \d) or
|
|
395
|
+
// listTables(tabtypes=...) for the typed variants.
|
|
396
|
+
return await runTypedList(ctx, c, baseName, pattern, verbose, showSystem);
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
ctx.settings.popt = savedPopt;
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
const dispatchDetail = async (ctx, c, rel, verbose) => {
|
|
404
|
+
const popt = ctx.settings.popt;
|
|
405
|
+
switch (rel.relkind) {
|
|
406
|
+
case 'S':
|
|
407
|
+
await describeOneSequence(c, rel.oid, rel.nspname, rel.relname, process.stdout, popt);
|
|
408
|
+
return;
|
|
409
|
+
case 'v':
|
|
410
|
+
await describeOneViewDetails(c, rel.oid, rel.nspname, rel.relname, process.stdout, popt, verbose, ctx.settings.hideCompression);
|
|
411
|
+
return;
|
|
412
|
+
default:
|
|
413
|
+
await describeOneTableDetails(c, rel.oid, rel.nspname, rel.relname, rel.relkind, verbose, process.stdout, popt, ctx.settings.hideTableam, ctx.settings.hideCompression);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const runTypedList = async (ctx, c, baseName, pattern, verbose, showSystem) => {
|
|
417
|
+
const serverVersion = c.serverVersion;
|
|
418
|
+
const hideTableam = ctx.settings.hideTableam;
|
|
419
|
+
let query;
|
|
420
|
+
let tabtypes = '';
|
|
421
|
+
switch (baseName) {
|
|
422
|
+
case 'd':
|
|
423
|
+
query = describeTableDetails({
|
|
424
|
+
pattern: pattern ?? undefined,
|
|
425
|
+
verbose,
|
|
426
|
+
showSystem,
|
|
427
|
+
serverVersion,
|
|
428
|
+
hideTableam,
|
|
429
|
+
});
|
|
430
|
+
break;
|
|
431
|
+
case 'dt':
|
|
432
|
+
tabtypes = 't';
|
|
433
|
+
query = listTables({
|
|
434
|
+
pattern: pattern ?? undefined,
|
|
435
|
+
verbose,
|
|
436
|
+
showSystem,
|
|
437
|
+
serverVersion,
|
|
438
|
+
tabtypes,
|
|
439
|
+
hideTableam,
|
|
440
|
+
});
|
|
441
|
+
break;
|
|
442
|
+
case 'di':
|
|
443
|
+
tabtypes = 'i';
|
|
444
|
+
query = listTables({
|
|
445
|
+
pattern: pattern ?? undefined,
|
|
446
|
+
verbose,
|
|
447
|
+
showSystem,
|
|
448
|
+
serverVersion,
|
|
449
|
+
tabtypes,
|
|
450
|
+
hideTableam,
|
|
451
|
+
});
|
|
452
|
+
break;
|
|
453
|
+
case 'dv':
|
|
454
|
+
tabtypes = 'v';
|
|
455
|
+
query = listTables({
|
|
456
|
+
pattern: pattern ?? undefined,
|
|
457
|
+
verbose,
|
|
458
|
+
showSystem,
|
|
459
|
+
serverVersion,
|
|
460
|
+
tabtypes,
|
|
461
|
+
hideTableam,
|
|
462
|
+
});
|
|
463
|
+
break;
|
|
464
|
+
case 'dm':
|
|
465
|
+
tabtypes = 'm';
|
|
466
|
+
query = listTables({
|
|
467
|
+
pattern: pattern ?? undefined,
|
|
468
|
+
verbose,
|
|
469
|
+
showSystem,
|
|
470
|
+
serverVersion,
|
|
471
|
+
tabtypes,
|
|
472
|
+
hideTableam,
|
|
473
|
+
});
|
|
474
|
+
break;
|
|
475
|
+
case 'ds':
|
|
476
|
+
tabtypes = 's';
|
|
477
|
+
query = listTables({
|
|
478
|
+
pattern: pattern ?? undefined,
|
|
479
|
+
verbose,
|
|
480
|
+
showSystem,
|
|
481
|
+
serverVersion,
|
|
482
|
+
tabtypes,
|
|
483
|
+
hideTableam,
|
|
484
|
+
});
|
|
485
|
+
break;
|
|
486
|
+
case 'dE':
|
|
487
|
+
tabtypes = 'E';
|
|
488
|
+
query = listTables({
|
|
489
|
+
pattern: pattern ?? undefined,
|
|
490
|
+
verbose,
|
|
491
|
+
showSystem,
|
|
492
|
+
serverVersion,
|
|
493
|
+
tabtypes,
|
|
494
|
+
hideTableam,
|
|
495
|
+
});
|
|
496
|
+
break;
|
|
497
|
+
default:
|
|
498
|
+
return { status: 'error' };
|
|
499
|
+
}
|
|
500
|
+
const visibility = baseName === 'd'
|
|
501
|
+
? 'pg_catalog.pg_table_is_visible(c.oid)'
|
|
502
|
+
: 'pg_catalog.pg_table_is_visible(c.oid)';
|
|
503
|
+
return runWithPattern(ctx, pattern, query, {
|
|
504
|
+
namevar: 'c.relname',
|
|
505
|
+
schemavar: 'n.nspname',
|
|
506
|
+
visibilityrule: visibility,
|
|
507
|
+
});
|
|
508
|
+
};
|
|
509
|
+
// Register all the relation-list-style commands with all suffix combos.
|
|
510
|
+
const RELATION_BASES = ['d', 'dt', 'di', 'dv', 'dm', 'ds', 'dE'];
|
|
511
|
+
// Standard suffix matrix. `x` is added separately for the bases that
|
|
512
|
+
// accept the expanded-mode toggle (currently `\d`/`\d+`).
|
|
513
|
+
const SUFFIX_COMBOS = ['', '+', 'S', 'S+', '+S'];
|
|
514
|
+
// Extended suffix matrix for `\d` only — adds the `x` (expanded) suffix
|
|
515
|
+
// in the same combinations regress exercises.
|
|
516
|
+
const DESCRIBE_X_SUFFIXES = ['x', '+x', 'x+', 'Sx', 'xS', '+Sx', '+xS'];
|
|
517
|
+
// ---- \df / \df+ / \dfS / \dfa / \dfn / \dfp / \dft / \dfw / \dfx -------
|
|
518
|
+
// Upstream's `command.c::exec_command_df` accepts a free-form suffix after
|
|
519
|
+
// `\df`: `+` for verbose, `S` to include system schemas, `x` to force
|
|
520
|
+
// expanded mode (when no pattern), and {a,n,p,t,w} to filter by function
|
|
521
|
+
// kind (aggregate / normal / procedure / trigger / window). Multiple may
|
|
522
|
+
// combine and order is unrestricted (`\dfax+`, `\df+xn`, etc.).
|
|
523
|
+
const cmdDescribeFunctions = (cmdName) => ({
|
|
524
|
+
name: cmdName,
|
|
525
|
+
run: async (ctx) => {
|
|
526
|
+
const pattern = ctx.nextArg('normal');
|
|
527
|
+
const c = conn(ctx);
|
|
528
|
+
if (!c)
|
|
529
|
+
return noConn(ctx);
|
|
530
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, 'df');
|
|
531
|
+
let functypes = '';
|
|
532
|
+
const tail = cmdName.slice(2);
|
|
533
|
+
for (const ch of tail) {
|
|
534
|
+
if (ch === 'a' || ch === 'n' || ch === 'p' || ch === 't' || ch === 'w') {
|
|
535
|
+
functypes += ch;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
// Per-argument type patterns: only collected when the main pattern
|
|
539
|
+
// is non-null (upstream: "Collect argument-type patterns too /
|
|
540
|
+
// otherwise it was just \df"). Each extra arg filters one slot of
|
|
541
|
+
// `proargtypes`; `-` means "no type in that slot".
|
|
542
|
+
const argPatterns = [];
|
|
543
|
+
if (pattern !== null) {
|
|
544
|
+
argPatterns.push(...collectArgPatterns(ctx));
|
|
545
|
+
}
|
|
546
|
+
const argPatternResults = argPatterns.map((a, i) => processArgPattern(i, a));
|
|
547
|
+
// `x` enables expanded mode for the printed result. For `\df` upstream
|
|
548
|
+
// applies the toggle whether or not a pattern is present (unlike `\d`,
|
|
549
|
+
// which only toggles when no pattern is given). Override popt locally
|
|
550
|
+
// so we don't leak the change to subsequent commands.
|
|
551
|
+
const forceExpanded = tail.includes('x');
|
|
552
|
+
const savedPopt = ctx.settings.popt;
|
|
553
|
+
if (forceExpanded) {
|
|
554
|
+
ctx.settings.popt = {
|
|
555
|
+
...savedPopt,
|
|
556
|
+
topt: { ...savedPopt.topt, expanded: 'on' },
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
try {
|
|
560
|
+
const query = describeFunctions({
|
|
561
|
+
pattern: pattern ?? undefined,
|
|
562
|
+
verbose,
|
|
563
|
+
showSystem,
|
|
564
|
+
functypes,
|
|
565
|
+
argPatterns,
|
|
566
|
+
serverVersion: c.serverVersion,
|
|
567
|
+
});
|
|
568
|
+
return await runFunctionOrOperatorQuery(ctx, pattern, argPatternResults, query, {
|
|
569
|
+
namevar: 'p.proname',
|
|
570
|
+
schemavar: 'n.nspname',
|
|
571
|
+
visibilityrule: 'pg_catalog.pg_function_is_visible(p.oid)',
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
finally {
|
|
575
|
+
ctx.settings.popt = savedPopt;
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
// ---- \da ----------------------------------------------------------------
|
|
580
|
+
const cmdDescribeAggregates = (cmdName) => ({
|
|
581
|
+
name: cmdName,
|
|
582
|
+
run: async (ctx) => {
|
|
583
|
+
const pattern = ctx.nextArg('normal');
|
|
584
|
+
const c = conn(ctx);
|
|
585
|
+
if (!c)
|
|
586
|
+
return noConn(ctx);
|
|
587
|
+
const { showSystem } = decodeSuffix(cmdName, 'da');
|
|
588
|
+
const query = describeAggregates({
|
|
589
|
+
pattern: pattern ?? undefined,
|
|
590
|
+
showSystem,
|
|
591
|
+
serverVersion: c.serverVersion,
|
|
592
|
+
});
|
|
593
|
+
return runWithPattern(ctx, pattern, query, {
|
|
594
|
+
namevar: 'p.proname',
|
|
595
|
+
schemavar: 'n.nspname',
|
|
596
|
+
visibilityrule: 'pg_catalog.pg_function_is_visible(p.oid)',
|
|
597
|
+
});
|
|
598
|
+
},
|
|
599
|
+
});
|
|
600
|
+
// ---- \dA / \dA+ / \dAm / \dAm+ -----------------------------------------
|
|
601
|
+
// `\dAm[+]` is a Neon extension: it lists access methods with their
|
|
602
|
+
// handler and description columns always present, equivalent to upstream
|
|
603
|
+
// `\dA+`. Upstream psql doesn't accept this spelling; we register it as
|
|
604
|
+
// an alias so users who reach for "access methods" by full name get the
|
|
605
|
+
// verbose view (Name, Type, Handler, Description) by default.
|
|
606
|
+
const cmdDescribeAccessMethods = (cmdName) => ({
|
|
607
|
+
name: cmdName,
|
|
608
|
+
run: async (ctx) => {
|
|
609
|
+
const pattern = ctx.nextArg('normal');
|
|
610
|
+
const c = conn(ctx);
|
|
611
|
+
if (!c)
|
|
612
|
+
return noConn(ctx);
|
|
613
|
+
const isAlias = cmdName.startsWith('dAm');
|
|
614
|
+
const base = isAlias ? 'dAm' : 'dA';
|
|
615
|
+
const { verbose } = decodeSuffix(cmdName, base);
|
|
616
|
+
// `\dAm[+]` always displays the verbose columns; trailing `+` is
|
|
617
|
+
// accepted for syntactic parity but doesn't change output.
|
|
618
|
+
const query = describeAccessMethods({
|
|
619
|
+
pattern: pattern ?? undefined,
|
|
620
|
+
verbose: isAlias ? true : verbose,
|
|
621
|
+
serverVersion: c.serverVersion,
|
|
622
|
+
});
|
|
623
|
+
// Access methods are global, never schema-qualified; first dot is
|
|
624
|
+
// "too many dotted names".
|
|
625
|
+
const result = await runWithPattern(ctx, pattern, query, { namevar: 'amname' }, 0);
|
|
626
|
+
// Upstream `exec_command_d` drains any args past the first AFTER
|
|
627
|
+
// running the query and writing the result, emitting one warning
|
|
628
|
+
// per leftover token via `pg_log_warning`. Mirror that ordering so
|
|
629
|
+
// `\dA foo bar` prints the (empty) result first, then
|
|
630
|
+
// `\dA: extra argument "bar" ignored` to stderr.
|
|
631
|
+
for (let extra = ctx.nextArg('normal'); extra !== null;) {
|
|
632
|
+
writeErr(`\\${base}: extra argument "${extra}" ignored\n`);
|
|
633
|
+
extra = ctx.nextArg('normal');
|
|
634
|
+
}
|
|
635
|
+
return result;
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
// ---- \dAc / \dAc+ ------------------------------------------------------
|
|
639
|
+
// `\dAc [AM-pattern [TYPE-pattern]]` — list operator classes. Two-pattern
|
|
640
|
+
// command: the first arg filters by access-method name, the second by
|
|
641
|
+
// input-type schema/name. Mirrors upstream's `case 'A' / cmd[2] == 'c'`
|
|
642
|
+
// dispatch in `command.c::exec_command_d`.
|
|
643
|
+
const cmdListOperatorClasses = (cmdName) => ({
|
|
644
|
+
name: cmdName,
|
|
645
|
+
run: async (ctx) => {
|
|
646
|
+
const amPat = ctx.nextArg('normal');
|
|
647
|
+
const typePat = amPat ? ctx.nextArg('normal') : null;
|
|
648
|
+
const c = conn(ctx);
|
|
649
|
+
if (!c)
|
|
650
|
+
return noConn(ctx);
|
|
651
|
+
const { verbose } = decodeSuffix(cmdName, 'dAc');
|
|
652
|
+
const query = listOperatorClasses({
|
|
653
|
+
amPattern: amPat ?? undefined,
|
|
654
|
+
typePattern: typePat ?? undefined,
|
|
655
|
+
verbose,
|
|
656
|
+
serverVersion: c.serverVersion,
|
|
657
|
+
});
|
|
658
|
+
const results = [];
|
|
659
|
+
const curDb = currentDb(c);
|
|
660
|
+
if (amPat !== null) {
|
|
661
|
+
const r = processSQLNamePattern({
|
|
662
|
+
namevar: 'am.amname',
|
|
663
|
+
pattern: amPat,
|
|
664
|
+
});
|
|
665
|
+
// Access method names are flat — first dot is "too many".
|
|
666
|
+
const err = validatePattern(amPat, r, 0, curDb);
|
|
667
|
+
if (err !== null) {
|
|
668
|
+
writeErr(`${err}\n`);
|
|
669
|
+
return { status: 'error', errorWritten: true };
|
|
670
|
+
}
|
|
671
|
+
results.push(r);
|
|
672
|
+
}
|
|
673
|
+
if (typePat !== null) {
|
|
674
|
+
const r = processSQLNamePattern({
|
|
675
|
+
namevar: 't.typname',
|
|
676
|
+
schemavar: 'tn.nspname',
|
|
677
|
+
pattern: typePat,
|
|
678
|
+
});
|
|
679
|
+
// Type pattern accepts db.schema.name; cross-db check on 2-dot.
|
|
680
|
+
const err = validatePattern(typePat, r, 2, curDb);
|
|
681
|
+
if (err !== null) {
|
|
682
|
+
writeErr(`${err}\n`);
|
|
683
|
+
return { status: 'error', errorWritten: true };
|
|
684
|
+
}
|
|
685
|
+
results.push(r);
|
|
686
|
+
}
|
|
687
|
+
return runDualPatternList(ctx, query, results);
|
|
688
|
+
},
|
|
689
|
+
});
|
|
690
|
+
// ---- \dAf / \dAf+ ------------------------------------------------------
|
|
691
|
+
// `\dAf [AM-pattern [TYPE-pattern]]` — list operator families. Same
|
|
692
|
+
// two-pattern shape as `\dAc`. Routes through `listOperatorFamilies`
|
|
693
|
+
// from queries.ts.
|
|
694
|
+
const cmdListOperatorFamilies = (cmdName) => ({
|
|
695
|
+
name: cmdName,
|
|
696
|
+
run: async (ctx) => {
|
|
697
|
+
const amPat = ctx.nextArg('normal');
|
|
698
|
+
const typePat = amPat ? ctx.nextArg('normal') : null;
|
|
699
|
+
const c = conn(ctx);
|
|
700
|
+
if (!c)
|
|
701
|
+
return noConn(ctx);
|
|
702
|
+
const { verbose } = decodeSuffix(cmdName, 'dAf');
|
|
703
|
+
const query = listOperatorFamilies({
|
|
704
|
+
amPattern: amPat ?? undefined,
|
|
705
|
+
typePattern: typePat ?? undefined,
|
|
706
|
+
verbose,
|
|
707
|
+
serverVersion: c.serverVersion,
|
|
708
|
+
});
|
|
709
|
+
const results = [];
|
|
710
|
+
const curDb = currentDb(c);
|
|
711
|
+
if (amPat !== null) {
|
|
712
|
+
const r = processSQLNamePattern({
|
|
713
|
+
namevar: 'am.amname',
|
|
714
|
+
pattern: amPat,
|
|
715
|
+
});
|
|
716
|
+
const err = validatePattern(amPat, r, 0, curDb);
|
|
717
|
+
if (err !== null) {
|
|
718
|
+
writeErr(`${err}\n`);
|
|
719
|
+
return { status: 'error', errorWritten: true };
|
|
720
|
+
}
|
|
721
|
+
results.push(r);
|
|
722
|
+
}
|
|
723
|
+
if (typePat !== null) {
|
|
724
|
+
const r = processSQLNamePattern({
|
|
725
|
+
namevar: 't.typname',
|
|
726
|
+
schemavar: 'tn.nspname',
|
|
727
|
+
pattern: typePat,
|
|
728
|
+
});
|
|
729
|
+
const err = validatePattern(typePat, r, 2, curDb);
|
|
730
|
+
if (err !== null) {
|
|
731
|
+
writeErr(`${err}\n`);
|
|
732
|
+
return { status: 'error', errorWritten: true };
|
|
733
|
+
}
|
|
734
|
+
results.push(r);
|
|
735
|
+
}
|
|
736
|
+
return runDualPatternList(ctx, query, results);
|
|
737
|
+
},
|
|
738
|
+
});
|
|
739
|
+
// ---- \dAo / \dAo+ ------------------------------------------------------
|
|
740
|
+
// `\dAo [AM-pattern [OPFAMILY-pattern]]` — list operators in an opfamily.
|
|
741
|
+
// Routes through `listOpFamilyOperators` from queries.ts.
|
|
742
|
+
const cmdListOpFamilyOperators = (cmdName) => ({
|
|
743
|
+
name: cmdName,
|
|
744
|
+
run: async (ctx) => {
|
|
745
|
+
const amPat = ctx.nextArg('normal');
|
|
746
|
+
const familyPat = amPat ? ctx.nextArg('normal') : null;
|
|
747
|
+
const c = conn(ctx);
|
|
748
|
+
if (!c)
|
|
749
|
+
return noConn(ctx);
|
|
750
|
+
const { verbose } = decodeSuffix(cmdName, 'dAo');
|
|
751
|
+
const query = listOpFamilyOperators({
|
|
752
|
+
amPattern: amPat ?? undefined,
|
|
753
|
+
familyPattern: familyPat ?? undefined,
|
|
754
|
+
verbose,
|
|
755
|
+
serverVersion: c.serverVersion,
|
|
756
|
+
});
|
|
757
|
+
const results = [];
|
|
758
|
+
const curDb = currentDb(c);
|
|
759
|
+
if (amPat !== null) {
|
|
760
|
+
const r = processSQLNamePattern({
|
|
761
|
+
namevar: 'am.amname',
|
|
762
|
+
pattern: amPat,
|
|
763
|
+
});
|
|
764
|
+
const err = validatePattern(amPat, r, 0, curDb);
|
|
765
|
+
if (err !== null) {
|
|
766
|
+
writeErr(`${err}\n`);
|
|
767
|
+
return { status: 'error', errorWritten: true };
|
|
768
|
+
}
|
|
769
|
+
results.push(r);
|
|
770
|
+
}
|
|
771
|
+
if (familyPat !== null) {
|
|
772
|
+
const r = processSQLNamePattern({
|
|
773
|
+
namevar: 'of.opfname',
|
|
774
|
+
schemavar: 'nsf.nspname',
|
|
775
|
+
pattern: familyPat,
|
|
776
|
+
});
|
|
777
|
+
const err = validatePattern(familyPat, r, 2, curDb);
|
|
778
|
+
if (err !== null) {
|
|
779
|
+
writeErr(`${err}\n`);
|
|
780
|
+
return { status: 'error', errorWritten: true };
|
|
781
|
+
}
|
|
782
|
+
results.push(r);
|
|
783
|
+
}
|
|
784
|
+
return runDualPatternList(ctx, query, results);
|
|
785
|
+
},
|
|
786
|
+
});
|
|
787
|
+
// ---- \dAp / \dAp+ ------------------------------------------------------
|
|
788
|
+
// `\dAp [AM-pattern [OPFAMILY-pattern]]` — list support functions of an
|
|
789
|
+
// opfamily. Routes through `listOpFamilyFunctions` from queries.ts.
|
|
790
|
+
const cmdListOpFamilyFunctions = (cmdName) => ({
|
|
791
|
+
name: cmdName,
|
|
792
|
+
run: async (ctx) => {
|
|
793
|
+
const amPat = ctx.nextArg('normal');
|
|
794
|
+
const familyPat = amPat ? ctx.nextArg('normal') : null;
|
|
795
|
+
const c = conn(ctx);
|
|
796
|
+
if (!c)
|
|
797
|
+
return noConn(ctx);
|
|
798
|
+
const { verbose } = decodeSuffix(cmdName, 'dAp');
|
|
799
|
+
// `\dApx[+]` — expanded toggle, same convention as `\df`/`\z`.
|
|
800
|
+
const forceExpanded = cmdName.slice(3).includes('x');
|
|
801
|
+
const savedPopt = ctx.settings.popt;
|
|
802
|
+
if (forceExpanded) {
|
|
803
|
+
ctx.settings.popt = {
|
|
804
|
+
...savedPopt,
|
|
805
|
+
topt: { ...savedPopt.topt, expanded: 'on' },
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
try {
|
|
809
|
+
const query = listOpFamilyFunctions({
|
|
810
|
+
amPattern: amPat ?? undefined,
|
|
811
|
+
familyPattern: familyPat ?? undefined,
|
|
812
|
+
verbose,
|
|
813
|
+
serverVersion: c.serverVersion,
|
|
814
|
+
});
|
|
815
|
+
const results = [];
|
|
816
|
+
const curDb = currentDb(c);
|
|
817
|
+
if (amPat !== null) {
|
|
818
|
+
const r = processSQLNamePattern({
|
|
819
|
+
namevar: 'am.amname',
|
|
820
|
+
pattern: amPat,
|
|
821
|
+
});
|
|
822
|
+
const err = validatePattern(amPat, r, 0, curDb);
|
|
823
|
+
if (err !== null) {
|
|
824
|
+
writeErr(`${err}\n`);
|
|
825
|
+
return { status: 'error', errorWritten: true };
|
|
826
|
+
}
|
|
827
|
+
results.push(r);
|
|
828
|
+
}
|
|
829
|
+
if (familyPat !== null) {
|
|
830
|
+
const r = processSQLNamePattern({
|
|
831
|
+
namevar: 'of.opfname',
|
|
832
|
+
schemavar: 'ns.nspname',
|
|
833
|
+
pattern: familyPat,
|
|
834
|
+
});
|
|
835
|
+
const err = validatePattern(familyPat, r, 2, curDb);
|
|
836
|
+
if (err !== null) {
|
|
837
|
+
writeErr(`${err}\n`);
|
|
838
|
+
return { status: 'error', errorWritten: true };
|
|
839
|
+
}
|
|
840
|
+
results.push(r);
|
|
841
|
+
}
|
|
842
|
+
return await runDualPatternList(ctx, query, results);
|
|
843
|
+
}
|
|
844
|
+
finally {
|
|
845
|
+
ctx.settings.popt = savedPopt;
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
});
|
|
849
|
+
// ---- \dT / \dT+ / \dTS --------------------------------------------------
|
|
850
|
+
const cmdDescribeTypes = (cmdName) => ({
|
|
851
|
+
name: cmdName,
|
|
852
|
+
run: async (ctx) => {
|
|
853
|
+
const pattern = ctx.nextArg('normal');
|
|
854
|
+
const c = conn(ctx);
|
|
855
|
+
if (!c)
|
|
856
|
+
return noConn(ctx);
|
|
857
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, 'dT');
|
|
858
|
+
const query = describeTypes({
|
|
859
|
+
pattern: pattern ?? undefined,
|
|
860
|
+
verbose,
|
|
861
|
+
showSystem,
|
|
862
|
+
serverVersion: c.serverVersion,
|
|
863
|
+
});
|
|
864
|
+
return runWithPattern(ctx, pattern, query, {
|
|
865
|
+
namevar: 't.typname',
|
|
866
|
+
schemavar: 'n.nspname',
|
|
867
|
+
visibilityrule: 'pg_catalog.pg_type_is_visible(t.oid)',
|
|
868
|
+
});
|
|
869
|
+
},
|
|
870
|
+
});
|
|
871
|
+
// ---- \do / \do+ ---------------------------------------------------------
|
|
872
|
+
const cmdDescribeOperators = (cmdName) => ({
|
|
873
|
+
name: cmdName,
|
|
874
|
+
run: async (ctx) => {
|
|
875
|
+
const pattern = ctx.nextArg('normal');
|
|
876
|
+
const c = conn(ctx);
|
|
877
|
+
if (!c)
|
|
878
|
+
return noConn(ctx);
|
|
879
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, 'do');
|
|
880
|
+
// Upstream: same arg-collection rule as `\df` (only when main
|
|
881
|
+
// pattern is non-null). describeOperators caps at 2 args; we still
|
|
882
|
+
// forward the user's input so the SQL builder can decide which
|
|
883
|
+
// joins to emit.
|
|
884
|
+
let argPatterns = [];
|
|
885
|
+
if (pattern !== null) {
|
|
886
|
+
argPatterns = collectArgPatterns(ctx);
|
|
887
|
+
}
|
|
888
|
+
if (argPatterns.length > 2)
|
|
889
|
+
argPatterns = argPatterns.slice(0, 2);
|
|
890
|
+
const argPatternResults = argPatterns.map((a, i) => processArgPattern(i, a));
|
|
891
|
+
const query = describeOperators({
|
|
892
|
+
pattern: pattern ?? undefined,
|
|
893
|
+
verbose,
|
|
894
|
+
showSystem,
|
|
895
|
+
argPatterns,
|
|
896
|
+
serverVersion: c.serverVersion,
|
|
897
|
+
});
|
|
898
|
+
return runFunctionOrOperatorQuery(ctx, pattern, argPatternResults, query, {
|
|
899
|
+
namevar: 'o.oprname',
|
|
900
|
+
schemavar: 'n.nspname',
|
|
901
|
+
visibilityrule: 'pg_catalog.pg_operator_is_visible(o.oid)',
|
|
902
|
+
});
|
|
903
|
+
},
|
|
904
|
+
});
|
|
905
|
+
// ---- \du / \dg / \dg+ / \du+ -------------------------------------------
|
|
906
|
+
const cmdDescribeRoles = (cmdName) => ({
|
|
907
|
+
name: cmdName,
|
|
908
|
+
run: async (ctx) => {
|
|
909
|
+
const pattern = ctx.nextArg('normal');
|
|
910
|
+
const c = conn(ctx);
|
|
911
|
+
if (!c)
|
|
912
|
+
return noConn(ctx);
|
|
913
|
+
const base = cmdName.startsWith('du') ? 'du' : 'dg';
|
|
914
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, base);
|
|
915
|
+
const query = describeRoles({
|
|
916
|
+
pattern: pattern ?? undefined,
|
|
917
|
+
verbose,
|
|
918
|
+
showSystem,
|
|
919
|
+
serverVersion: c.serverVersion,
|
|
920
|
+
});
|
|
921
|
+
// Roles are global — first dot is "too many". Upstream calls
|
|
922
|
+
// `printQuery` with `default_footer = false` (describe.c
|
|
923
|
+
// `describeRoles`), so suppress the `(N rows)` counter for both
|
|
924
|
+
// populated and empty results to match `psql -E \du`.
|
|
925
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'r.rolname' }, 0, {
|
|
926
|
+
suppressDefaultFooter: true,
|
|
927
|
+
});
|
|
928
|
+
},
|
|
929
|
+
});
|
|
930
|
+
// ---- \drds -------------------------------------------------------------
|
|
931
|
+
// `\drds [role-pattern [database-pattern]]` — list role-/database-level
|
|
932
|
+
// configuration settings (`pg_db_role_setting`). Two-pattern command:
|
|
933
|
+
// first arg filters by role, second by database. Empty results print a
|
|
934
|
+
// stderr notice ("Did not find any settings…") when not in quiet mode,
|
|
935
|
+
// mirroring upstream `describe.c::listDbRoleSettings`.
|
|
936
|
+
const cmdListDbRoleSettings = {
|
|
937
|
+
name: 'drds',
|
|
938
|
+
run: async (ctx) => {
|
|
939
|
+
const rolePat = ctx.nextArg('normal');
|
|
940
|
+
const dbPat = rolePat ? ctx.nextArg('normal') : null;
|
|
941
|
+
const c = conn(ctx);
|
|
942
|
+
if (!c)
|
|
943
|
+
return noConn(ctx);
|
|
944
|
+
const query = listDbRoleSettings({
|
|
945
|
+
pattern: rolePat ?? undefined,
|
|
946
|
+
pattern2: dbPat ?? undefined,
|
|
947
|
+
serverVersion: c.serverVersion,
|
|
948
|
+
});
|
|
949
|
+
const results = [];
|
|
950
|
+
const curDb = currentDb(c);
|
|
951
|
+
if (rolePat !== null) {
|
|
952
|
+
const r = processSQLNamePattern({
|
|
953
|
+
namevar: 'r.rolname',
|
|
954
|
+
pattern: rolePat,
|
|
955
|
+
});
|
|
956
|
+
// Role names are flat — first dot is "too many".
|
|
957
|
+
const err = validatePattern(rolePat, r, 0, curDb);
|
|
958
|
+
if (err !== null) {
|
|
959
|
+
writeErr(`${err}\n`);
|
|
960
|
+
return { status: 'error', errorWritten: true };
|
|
961
|
+
}
|
|
962
|
+
results.push(r);
|
|
963
|
+
}
|
|
964
|
+
if (dbPat !== null) {
|
|
965
|
+
const r = processSQLNamePattern({
|
|
966
|
+
namevar: 'd.datname',
|
|
967
|
+
pattern: dbPat,
|
|
968
|
+
});
|
|
969
|
+
// Database names are top-level — first dot is "too many".
|
|
970
|
+
const err = validatePattern(dbPat, r, 0, curDb);
|
|
971
|
+
if (err !== null) {
|
|
972
|
+
writeErr(`${err}\n`);
|
|
973
|
+
return { status: 'error', errorWritten: true };
|
|
974
|
+
}
|
|
975
|
+
results.push(r);
|
|
976
|
+
}
|
|
977
|
+
// Upstream deviates from the rest of describe.c here: when the
|
|
978
|
+
// result set is empty and we're not in --quiet, emit a stderr
|
|
979
|
+
// diagnostic instead of printing an empty table — the two-pattern
|
|
980
|
+
// shape makes confusion likely otherwise.
|
|
981
|
+
if (rolePat === null && dbPat === null) {
|
|
982
|
+
return runDualPatternList(ctx, query, results);
|
|
983
|
+
}
|
|
984
|
+
const { sql, params } = applyTwoPatterns(query.sql, [], results);
|
|
985
|
+
const finalQuery = {
|
|
986
|
+
...query,
|
|
987
|
+
sql,
|
|
988
|
+
params,
|
|
989
|
+
};
|
|
990
|
+
try {
|
|
991
|
+
const rs = await c.query(sql, params);
|
|
992
|
+
if (rs.rows.length === 0 && !ctx.settings.quiet) {
|
|
993
|
+
if (rolePat !== null && dbPat !== null) {
|
|
994
|
+
writeErr(`Did not find any settings for role "${rolePat}" and database "${dbPat}".\n`);
|
|
995
|
+
}
|
|
996
|
+
else if (rolePat !== null) {
|
|
997
|
+
writeErr(`Did not find any settings for role "${rolePat}".\n`);
|
|
998
|
+
}
|
|
999
|
+
return { status: 'ok' };
|
|
1000
|
+
}
|
|
1001
|
+
// Re-print via the standard runner so the title and formatting
|
|
1002
|
+
// match peer list queries. We pass the already-substituted SQL
|
|
1003
|
+
// and an empty pattern result so the second-pass replace is a
|
|
1004
|
+
// no-op. (We accept the double-query cost on the small `\drds`
|
|
1005
|
+
// path; alternatives require duplicating the printer plumbing.)
|
|
1006
|
+
const empty = {
|
|
1007
|
+
schemaConditions: [],
|
|
1008
|
+
nameConditions: [],
|
|
1009
|
+
visibilityConditions: [],
|
|
1010
|
+
params: [],
|
|
1011
|
+
dotCount: 0,
|
|
1012
|
+
dbLiteral: null,
|
|
1013
|
+
};
|
|
1014
|
+
await runListQuery(c, finalQuery, empty, process.stdout, ctx.settings.popt);
|
|
1015
|
+
return { status: 'ok' };
|
|
1016
|
+
}
|
|
1017
|
+
catch (err) {
|
|
1018
|
+
writeErr(`\\${ctx.cmdName}: ${errMsg(err)}\n`);
|
|
1019
|
+
return { status: 'error', errorWritten: true };
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1022
|
+
};
|
|
1023
|
+
// ---- \dn / \dn+ / \dnS --------------------------------------------------
|
|
1024
|
+
const cmdListSchemas = (cmdName) => ({
|
|
1025
|
+
name: cmdName,
|
|
1026
|
+
run: async (ctx) => {
|
|
1027
|
+
const pattern = ctx.nextArg('normal');
|
|
1028
|
+
const c = conn(ctx);
|
|
1029
|
+
if (!c)
|
|
1030
|
+
return noConn(ctx);
|
|
1031
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, 'dn');
|
|
1032
|
+
const query = listSchemas({
|
|
1033
|
+
pattern: pattern ?? undefined,
|
|
1034
|
+
verbose,
|
|
1035
|
+
showSystem,
|
|
1036
|
+
serverVersion: c.serverVersion,
|
|
1037
|
+
});
|
|
1038
|
+
// Schemas live in a single namespace; the optional qualifier slot
|
|
1039
|
+
// is interpreted as a database name (cross-database check fires on
|
|
1040
|
+
// mismatch). `maxDots = 1`.
|
|
1041
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'n.nspname' }, 1);
|
|
1042
|
+
},
|
|
1043
|
+
});
|
|
1044
|
+
// ---- \db / \db+ ---------------------------------------------------------
|
|
1045
|
+
// Tablespaces — not typically relevant on Neon. We still register so the
|
|
1046
|
+
// command exists; it returns an empty result against a managed deployment.
|
|
1047
|
+
const cmdDescribeTablespaces = (cmdName) => ({
|
|
1048
|
+
name: cmdName,
|
|
1049
|
+
run: async (ctx) => {
|
|
1050
|
+
const pattern = ctx.nextArg('normal');
|
|
1051
|
+
const c = conn(ctx);
|
|
1052
|
+
if (!c)
|
|
1053
|
+
return noConn(ctx);
|
|
1054
|
+
const { verbose } = decodeSuffix(cmdName, 'db');
|
|
1055
|
+
const query = describeTablespaces({
|
|
1056
|
+
pattern: pattern ?? undefined,
|
|
1057
|
+
verbose,
|
|
1058
|
+
serverVersion: c.serverVersion,
|
|
1059
|
+
});
|
|
1060
|
+
// Tablespaces are flat — first dot is "too many".
|
|
1061
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'spcname' }, 0);
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
// ---- \dD / \dDS / \dD+ -------------------------------------------------
|
|
1065
|
+
const cmdListDomains = (cmdName) => ({
|
|
1066
|
+
name: cmdName,
|
|
1067
|
+
run: async (ctx) => {
|
|
1068
|
+
const pattern = ctx.nextArg('normal');
|
|
1069
|
+
const c = conn(ctx);
|
|
1070
|
+
if (!c)
|
|
1071
|
+
return noConn(ctx);
|
|
1072
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, 'dD');
|
|
1073
|
+
const query = listDomains({
|
|
1074
|
+
pattern: pattern ?? undefined,
|
|
1075
|
+
verbose,
|
|
1076
|
+
showSystem,
|
|
1077
|
+
serverVersion: c.serverVersion,
|
|
1078
|
+
});
|
|
1079
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1080
|
+
namevar: 't.typname',
|
|
1081
|
+
schemavar: 'n.nspname',
|
|
1082
|
+
visibilityrule: 'pg_catalog.pg_type_is_visible(t.oid)',
|
|
1083
|
+
});
|
|
1084
|
+
},
|
|
1085
|
+
});
|
|
1086
|
+
// ---- \dc / \dcS / \dc+ -------------------------------------------------
|
|
1087
|
+
const cmdListConversions = (cmdName) => ({
|
|
1088
|
+
name: cmdName,
|
|
1089
|
+
run: async (ctx) => {
|
|
1090
|
+
const pattern = ctx.nextArg('normal');
|
|
1091
|
+
const c = conn(ctx);
|
|
1092
|
+
if (!c)
|
|
1093
|
+
return noConn(ctx);
|
|
1094
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, 'dc');
|
|
1095
|
+
const query = listConversions({
|
|
1096
|
+
pattern: pattern ?? undefined,
|
|
1097
|
+
verbose,
|
|
1098
|
+
showSystem,
|
|
1099
|
+
serverVersion: c.serverVersion,
|
|
1100
|
+
});
|
|
1101
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1102
|
+
namevar: 'c.conname',
|
|
1103
|
+
schemavar: 'n.nspname',
|
|
1104
|
+
visibilityrule: 'pg_catalog.pg_conversion_is_visible(c.oid)',
|
|
1105
|
+
});
|
|
1106
|
+
},
|
|
1107
|
+
});
|
|
1108
|
+
// ---- \dC / \dC+ --------------------------------------------------------
|
|
1109
|
+
const cmdListCasts = (cmdName) => ({
|
|
1110
|
+
name: cmdName,
|
|
1111
|
+
run: async (ctx) => {
|
|
1112
|
+
const pattern = ctx.nextArg('normal');
|
|
1113
|
+
const c = conn(ctx);
|
|
1114
|
+
if (!c)
|
|
1115
|
+
return noConn(ctx);
|
|
1116
|
+
const { verbose } = decodeSuffix(cmdName, 'dC');
|
|
1117
|
+
const query = listCasts({
|
|
1118
|
+
pattern: pattern ?? undefined,
|
|
1119
|
+
verbose,
|
|
1120
|
+
serverVersion: c.serverVersion,
|
|
1121
|
+
});
|
|
1122
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1123
|
+
namevar: 'ts.typname',
|
|
1124
|
+
schemavar: 'ns.nspname',
|
|
1125
|
+
});
|
|
1126
|
+
},
|
|
1127
|
+
});
|
|
1128
|
+
// ---- \dL / \dLS / \dL+ -------------------------------------------------
|
|
1129
|
+
const cmdListLanguages = (cmdName) => ({
|
|
1130
|
+
name: cmdName,
|
|
1131
|
+
run: async (ctx) => {
|
|
1132
|
+
const pattern = ctx.nextArg('normal');
|
|
1133
|
+
const c = conn(ctx);
|
|
1134
|
+
if (!c)
|
|
1135
|
+
return noConn(ctx);
|
|
1136
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, 'dL');
|
|
1137
|
+
const query = listLanguages({
|
|
1138
|
+
pattern: pattern ?? undefined,
|
|
1139
|
+
verbose,
|
|
1140
|
+
showSystem,
|
|
1141
|
+
serverVersion: c.serverVersion,
|
|
1142
|
+
});
|
|
1143
|
+
// Languages are global; first dot is a database qualifier (cross-db).
|
|
1144
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'l.lanname' }, 1);
|
|
1145
|
+
},
|
|
1146
|
+
});
|
|
1147
|
+
// ---- \dO / \dO+ / \dOS -------------------------------------------------
|
|
1148
|
+
const cmdListCollations = (cmdName) => ({
|
|
1149
|
+
name: cmdName,
|
|
1150
|
+
run: async (ctx) => {
|
|
1151
|
+
const pattern = ctx.nextArg('normal');
|
|
1152
|
+
const c = conn(ctx);
|
|
1153
|
+
if (!c)
|
|
1154
|
+
return noConn(ctx);
|
|
1155
|
+
const { verbose, showSystem } = decodeSuffix(cmdName, 'dO');
|
|
1156
|
+
const query = listCollations({
|
|
1157
|
+
pattern: pattern ?? undefined,
|
|
1158
|
+
verbose,
|
|
1159
|
+
showSystem,
|
|
1160
|
+
serverVersion: c.serverVersion,
|
|
1161
|
+
});
|
|
1162
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1163
|
+
namevar: 'c.collname',
|
|
1164
|
+
schemavar: 'n.nspname',
|
|
1165
|
+
visibilityrule: 'pg_catalog.pg_collation_is_visible(c.oid)',
|
|
1166
|
+
});
|
|
1167
|
+
},
|
|
1168
|
+
});
|
|
1169
|
+
// ---- \dp / \z ----------------------------------------------------------
|
|
1170
|
+
const cmdPermissionsList = (cmdName) => ({
|
|
1171
|
+
name: cmdName,
|
|
1172
|
+
run: async (ctx) => {
|
|
1173
|
+
const pattern = ctx.nextArg('normal');
|
|
1174
|
+
const c = conn(ctx);
|
|
1175
|
+
if (!c)
|
|
1176
|
+
return noConn(ctx);
|
|
1177
|
+
const base = cmdName.startsWith('z') ? 'z' : 'dp';
|
|
1178
|
+
const { showSystem } = decodeSuffix(cmdName, base);
|
|
1179
|
+
// `\z[x]`, `\dp[x]` — same expanded-mode toggle convention as `\df`,
|
|
1180
|
+
// applied whether or not a pattern is present.
|
|
1181
|
+
const forceExpanded = cmdName.slice(base.length).includes('x');
|
|
1182
|
+
const savedPopt = ctx.settings.popt;
|
|
1183
|
+
if (forceExpanded) {
|
|
1184
|
+
ctx.settings.popt = {
|
|
1185
|
+
...savedPopt,
|
|
1186
|
+
topt: { ...savedPopt.topt, expanded: 'on' },
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
try {
|
|
1190
|
+
const query = permissionsList({
|
|
1191
|
+
pattern: pattern ?? undefined,
|
|
1192
|
+
showSystem,
|
|
1193
|
+
serverVersion: c.serverVersion,
|
|
1194
|
+
});
|
|
1195
|
+
return await runWithPattern(ctx, pattern, query, {
|
|
1196
|
+
namevar: 'c.relname',
|
|
1197
|
+
schemavar: 'n.nspname',
|
|
1198
|
+
visibilityrule: 'pg_catalog.pg_table_is_visible(c.oid)',
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
finally {
|
|
1202
|
+
ctx.settings.popt = savedPopt;
|
|
1203
|
+
}
|
|
1204
|
+
},
|
|
1205
|
+
});
|
|
1206
|
+
// ---- \ddp --------------------------------------------------------------
|
|
1207
|
+
const cmdListDefaultACLs = {
|
|
1208
|
+
name: 'ddp',
|
|
1209
|
+
run: async (ctx) => {
|
|
1210
|
+
const pattern = ctx.nextArg('normal');
|
|
1211
|
+
const c = conn(ctx);
|
|
1212
|
+
if (!c)
|
|
1213
|
+
return noConn(ctx);
|
|
1214
|
+
const query = listDefaultACLs({
|
|
1215
|
+
pattern: pattern ?? undefined,
|
|
1216
|
+
serverVersion: c.serverVersion,
|
|
1217
|
+
});
|
|
1218
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1219
|
+
namevar: 'pg_catalog.pg_get_userbyid(d.defaclrole)',
|
|
1220
|
+
schemavar: 'n.nspname',
|
|
1221
|
+
});
|
|
1222
|
+
},
|
|
1223
|
+
};
|
|
1224
|
+
// ---- \dd ---------------------------------------------------------------
|
|
1225
|
+
const cmdObjectDescription = {
|
|
1226
|
+
name: 'dd',
|
|
1227
|
+
run: async (ctx) => {
|
|
1228
|
+
const pattern = ctx.nextArg('normal');
|
|
1229
|
+
const c = conn(ctx);
|
|
1230
|
+
if (!c)
|
|
1231
|
+
return noConn(ctx);
|
|
1232
|
+
const { showSystem } = decodeSuffix(ctx.cmdName, 'dd');
|
|
1233
|
+
const query = objectDescription({
|
|
1234
|
+
pattern: pattern ?? undefined,
|
|
1235
|
+
showSystem,
|
|
1236
|
+
serverVersion: c.serverVersion,
|
|
1237
|
+
});
|
|
1238
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1239
|
+
namevar: 'tt.name',
|
|
1240
|
+
schemavar: 'tt.nspname',
|
|
1241
|
+
});
|
|
1242
|
+
},
|
|
1243
|
+
};
|
|
1244
|
+
// ---- \l / \list -------------------------------------------------------
|
|
1245
|
+
const cmdListAllDbs = {
|
|
1246
|
+
name: 'l',
|
|
1247
|
+
aliases: ['list'],
|
|
1248
|
+
run: async (ctx) => {
|
|
1249
|
+
const pattern = ctx.nextArg('normal');
|
|
1250
|
+
const c = conn(ctx);
|
|
1251
|
+
if (!c)
|
|
1252
|
+
return noConn(ctx);
|
|
1253
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'l');
|
|
1254
|
+
const query = listAllDbs({
|
|
1255
|
+
pattern: pattern ?? undefined,
|
|
1256
|
+
verbose,
|
|
1257
|
+
serverVersion: c.serverVersion,
|
|
1258
|
+
});
|
|
1259
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'd.datname' });
|
|
1260
|
+
},
|
|
1261
|
+
};
|
|
1262
|
+
// ---- \dconfig ----------------------------------------------------------
|
|
1263
|
+
const cmdDescribeConfigParams = {
|
|
1264
|
+
name: 'dconfig',
|
|
1265
|
+
run: async (ctx) => {
|
|
1266
|
+
const pattern = ctx.nextArg('normal');
|
|
1267
|
+
const c = conn(ctx);
|
|
1268
|
+
if (!c)
|
|
1269
|
+
return noConn(ctx);
|
|
1270
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dconfig');
|
|
1271
|
+
const query = describeConfigurationParameters({
|
|
1272
|
+
pattern: pattern ?? undefined,
|
|
1273
|
+
verbose,
|
|
1274
|
+
serverVersion: c.serverVersion,
|
|
1275
|
+
});
|
|
1276
|
+
// GUC names are flat — any dot is "too many".
|
|
1277
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'pg_catalog.lower(s.name)' }, 0);
|
|
1278
|
+
},
|
|
1279
|
+
};
|
|
1280
|
+
// ---- \dy ---------------------------------------------------------------
|
|
1281
|
+
const cmdListEventTriggers = {
|
|
1282
|
+
name: 'dy',
|
|
1283
|
+
run: async (ctx) => {
|
|
1284
|
+
const pattern = ctx.nextArg('normal');
|
|
1285
|
+
const c = conn(ctx);
|
|
1286
|
+
if (!c)
|
|
1287
|
+
return noConn(ctx);
|
|
1288
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dy');
|
|
1289
|
+
const query = listEventTriggers({
|
|
1290
|
+
pattern: pattern ?? undefined,
|
|
1291
|
+
verbose,
|
|
1292
|
+
serverVersion: c.serverVersion,
|
|
1293
|
+
});
|
|
1294
|
+
// Event triggers are global with no schema or database qualifier
|
|
1295
|
+
// (upstream `validateSQLNamePattern(..., NULL, 1)`): any dot in
|
|
1296
|
+
// the pattern is "too many".
|
|
1297
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'evtname' }, 0);
|
|
1298
|
+
},
|
|
1299
|
+
};
|
|
1300
|
+
// ---- \dx / \dx+ -------------------------------------------------------
|
|
1301
|
+
const cmdListExtensions = {
|
|
1302
|
+
name: 'dx',
|
|
1303
|
+
run: async (ctx) => {
|
|
1304
|
+
const pattern = ctx.nextArg('normal');
|
|
1305
|
+
const c = conn(ctx);
|
|
1306
|
+
if (!c)
|
|
1307
|
+
return noConn(ctx);
|
|
1308
|
+
const query = listExtensions({
|
|
1309
|
+
pattern: pattern ?? undefined,
|
|
1310
|
+
serverVersion: c.serverVersion,
|
|
1311
|
+
});
|
|
1312
|
+
// Extensions are global with no schema or database qualifier
|
|
1313
|
+
// (upstream `validateSQLNamePattern(..., NULL, 1)`): any dot is
|
|
1314
|
+
// "too many".
|
|
1315
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'e.extname' }, 0);
|
|
1316
|
+
},
|
|
1317
|
+
};
|
|
1318
|
+
// ---- \dl / \lo_list ---------------------------------------------------
|
|
1319
|
+
const cmdListLargeObjects = {
|
|
1320
|
+
name: 'dl',
|
|
1321
|
+
aliases: ['lo_list'],
|
|
1322
|
+
run: async (ctx) => {
|
|
1323
|
+
const c = conn(ctx);
|
|
1324
|
+
if (!c)
|
|
1325
|
+
return noConn(ctx);
|
|
1326
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dl');
|
|
1327
|
+
const query = listLargeObjects({
|
|
1328
|
+
verbose,
|
|
1329
|
+
serverVersion: c.serverVersion,
|
|
1330
|
+
});
|
|
1331
|
+
return runWithPattern(ctx, null, query, { namevar: 'oid' });
|
|
1332
|
+
},
|
|
1333
|
+
};
|
|
1334
|
+
// ---- \dF / \dFp / \dFd / \dFt ----------------------------------------
|
|
1335
|
+
const cmdListTSConfigs = {
|
|
1336
|
+
name: 'dF',
|
|
1337
|
+
run: async (ctx) => {
|
|
1338
|
+
const pattern = ctx.nextArg('normal');
|
|
1339
|
+
const c = conn(ctx);
|
|
1340
|
+
if (!c)
|
|
1341
|
+
return noConn(ctx);
|
|
1342
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dF');
|
|
1343
|
+
const query = listTSConfigs({
|
|
1344
|
+
pattern: pattern ?? undefined,
|
|
1345
|
+
verbose,
|
|
1346
|
+
serverVersion: c.serverVersion,
|
|
1347
|
+
});
|
|
1348
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1349
|
+
namevar: 'c.cfgname',
|
|
1350
|
+
schemavar: 'n.nspname',
|
|
1351
|
+
});
|
|
1352
|
+
},
|
|
1353
|
+
};
|
|
1354
|
+
const cmdListTSParsers = {
|
|
1355
|
+
name: 'dFp',
|
|
1356
|
+
run: async (ctx) => {
|
|
1357
|
+
const pattern = ctx.nextArg('normal');
|
|
1358
|
+
const c = conn(ctx);
|
|
1359
|
+
if (!c)
|
|
1360
|
+
return noConn(ctx);
|
|
1361
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dFp');
|
|
1362
|
+
const query = listTSParsers({
|
|
1363
|
+
pattern: pattern ?? undefined,
|
|
1364
|
+
verbose,
|
|
1365
|
+
serverVersion: c.serverVersion,
|
|
1366
|
+
});
|
|
1367
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1368
|
+
namevar: 'p.prsname',
|
|
1369
|
+
schemavar: 'n.nspname',
|
|
1370
|
+
});
|
|
1371
|
+
},
|
|
1372
|
+
};
|
|
1373
|
+
const cmdListTSDictionaries = {
|
|
1374
|
+
name: 'dFd',
|
|
1375
|
+
run: async (ctx) => {
|
|
1376
|
+
const pattern = ctx.nextArg('normal');
|
|
1377
|
+
const c = conn(ctx);
|
|
1378
|
+
if (!c)
|
|
1379
|
+
return noConn(ctx);
|
|
1380
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dFd');
|
|
1381
|
+
const query = listTSDictionaries({
|
|
1382
|
+
pattern: pattern ?? undefined,
|
|
1383
|
+
verbose,
|
|
1384
|
+
serverVersion: c.serverVersion,
|
|
1385
|
+
});
|
|
1386
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1387
|
+
namevar: 'd.dictname',
|
|
1388
|
+
schemavar: 'n.nspname',
|
|
1389
|
+
});
|
|
1390
|
+
},
|
|
1391
|
+
};
|
|
1392
|
+
const cmdListTSTemplates = {
|
|
1393
|
+
name: 'dFt',
|
|
1394
|
+
run: async (ctx) => {
|
|
1395
|
+
const pattern = ctx.nextArg('normal');
|
|
1396
|
+
const c = conn(ctx);
|
|
1397
|
+
if (!c)
|
|
1398
|
+
return noConn(ctx);
|
|
1399
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dFt');
|
|
1400
|
+
const query = listTSTemplates({
|
|
1401
|
+
pattern: pattern ?? undefined,
|
|
1402
|
+
verbose,
|
|
1403
|
+
serverVersion: c.serverVersion,
|
|
1404
|
+
});
|
|
1405
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1406
|
+
namevar: 't.tmplname',
|
|
1407
|
+
schemavar: 'n.nspname',
|
|
1408
|
+
});
|
|
1409
|
+
},
|
|
1410
|
+
};
|
|
1411
|
+
// ---- \dew / \des / \deu / \det ---------------------------------------
|
|
1412
|
+
const cmdListForeignDataWrappers = {
|
|
1413
|
+
name: 'dew',
|
|
1414
|
+
run: async (ctx) => {
|
|
1415
|
+
const pattern = ctx.nextArg('normal');
|
|
1416
|
+
const c = conn(ctx);
|
|
1417
|
+
if (!c)
|
|
1418
|
+
return noConn(ctx);
|
|
1419
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dew');
|
|
1420
|
+
const query = listForeignDataWrappers({
|
|
1421
|
+
pattern: pattern ?? undefined,
|
|
1422
|
+
verbose,
|
|
1423
|
+
serverVersion: c.serverVersion,
|
|
1424
|
+
});
|
|
1425
|
+
// FDWs are global with no schema or database qualifier (upstream
|
|
1426
|
+
// `validateSQLNamePattern(..., NULL, 1)`): any dot is "too many".
|
|
1427
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'fdwname' }, 0);
|
|
1428
|
+
},
|
|
1429
|
+
};
|
|
1430
|
+
const cmdListForeignServers = {
|
|
1431
|
+
name: 'des',
|
|
1432
|
+
run: async (ctx) => {
|
|
1433
|
+
const pattern = ctx.nextArg('normal');
|
|
1434
|
+
const c = conn(ctx);
|
|
1435
|
+
if (!c)
|
|
1436
|
+
return noConn(ctx);
|
|
1437
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'des');
|
|
1438
|
+
const query = listForeignServers({
|
|
1439
|
+
pattern: pattern ?? undefined,
|
|
1440
|
+
verbose,
|
|
1441
|
+
serverVersion: c.serverVersion,
|
|
1442
|
+
});
|
|
1443
|
+
// Foreign servers are global with no schema or database qualifier
|
|
1444
|
+
// (upstream `validateSQLNamePattern(..., NULL, 1)`): any dot is
|
|
1445
|
+
// "too many".
|
|
1446
|
+
return runWithPattern(ctx, pattern, query, { namevar: 's.srvname' }, 0);
|
|
1447
|
+
},
|
|
1448
|
+
};
|
|
1449
|
+
const cmdListUserMappings = {
|
|
1450
|
+
name: 'deu',
|
|
1451
|
+
run: async (ctx) => {
|
|
1452
|
+
const pattern = ctx.nextArg('normal');
|
|
1453
|
+
const c = conn(ctx);
|
|
1454
|
+
if (!c)
|
|
1455
|
+
return noConn(ctx);
|
|
1456
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'deu');
|
|
1457
|
+
const query = listUserMappings({
|
|
1458
|
+
pattern: pattern ?? undefined,
|
|
1459
|
+
verbose,
|
|
1460
|
+
serverVersion: c.serverVersion,
|
|
1461
|
+
});
|
|
1462
|
+
// User mappings live alongside foreign servers (global, no schema
|
|
1463
|
+
// or db qualifier per upstream `validateSQLNamePattern(..., 1)`).
|
|
1464
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'um.srvname' }, 0);
|
|
1465
|
+
},
|
|
1466
|
+
};
|
|
1467
|
+
const cmdListForeignTables = {
|
|
1468
|
+
name: 'det',
|
|
1469
|
+
run: async (ctx) => {
|
|
1470
|
+
const pattern = ctx.nextArg('normal');
|
|
1471
|
+
const c = conn(ctx);
|
|
1472
|
+
if (!c)
|
|
1473
|
+
return noConn(ctx);
|
|
1474
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'det');
|
|
1475
|
+
const query = listForeignTables({
|
|
1476
|
+
pattern: pattern ?? undefined,
|
|
1477
|
+
verbose,
|
|
1478
|
+
serverVersion: c.serverVersion,
|
|
1479
|
+
});
|
|
1480
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1481
|
+
namevar: 'c.relname',
|
|
1482
|
+
schemavar: 'n.nspname',
|
|
1483
|
+
});
|
|
1484
|
+
},
|
|
1485
|
+
};
|
|
1486
|
+
// ---- \dP / \dPi / \dPt / \dPn -----------------------------------------
|
|
1487
|
+
const cmdListPartitionedTables = {
|
|
1488
|
+
name: 'dP',
|
|
1489
|
+
run: async (ctx) => {
|
|
1490
|
+
const pattern = ctx.nextArg('normal');
|
|
1491
|
+
const c = conn(ctx);
|
|
1492
|
+
if (!c)
|
|
1493
|
+
return noConn(ctx);
|
|
1494
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dP');
|
|
1495
|
+
let reltypes = '';
|
|
1496
|
+
if (ctx.cmdName.includes('i'))
|
|
1497
|
+
reltypes += 'i';
|
|
1498
|
+
if (ctx.cmdName.includes('t'))
|
|
1499
|
+
reltypes += 't';
|
|
1500
|
+
if (ctx.cmdName.includes('n'))
|
|
1501
|
+
reltypes += 'n';
|
|
1502
|
+
const query = listPartitionedTables({
|
|
1503
|
+
pattern: pattern ?? undefined,
|
|
1504
|
+
verbose,
|
|
1505
|
+
reltypes,
|
|
1506
|
+
serverVersion: c.serverVersion,
|
|
1507
|
+
});
|
|
1508
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1509
|
+
namevar: 'c.relname',
|
|
1510
|
+
schemavar: 'n.nspname',
|
|
1511
|
+
});
|
|
1512
|
+
},
|
|
1513
|
+
};
|
|
1514
|
+
// ---- \dX / \dX+ -------------------------------------------------------
|
|
1515
|
+
// Extended-statistics objects (`pg_statistic_ext`). Patterns accept the
|
|
1516
|
+
// usual schema-qualified form: `\dX` schema.name, with the cross-database
|
|
1517
|
+
// 2-dot check applied.
|
|
1518
|
+
const cmdListExtendedStats = (cmdName) => ({
|
|
1519
|
+
name: cmdName,
|
|
1520
|
+
run: async (ctx) => {
|
|
1521
|
+
const pattern = ctx.nextArg('normal');
|
|
1522
|
+
const c = conn(ctx);
|
|
1523
|
+
if (!c)
|
|
1524
|
+
return noConn(ctx);
|
|
1525
|
+
const { verbose } = decodeSuffix(cmdName, 'dX');
|
|
1526
|
+
const query = listExtendedStats({
|
|
1527
|
+
pattern: pattern ?? undefined,
|
|
1528
|
+
verbose,
|
|
1529
|
+
serverVersion: c.serverVersion,
|
|
1530
|
+
});
|
|
1531
|
+
return runWithPattern(ctx, pattern, query, {
|
|
1532
|
+
namevar: 'es.stxname',
|
|
1533
|
+
schemavar: 'es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text',
|
|
1534
|
+
});
|
|
1535
|
+
},
|
|
1536
|
+
});
|
|
1537
|
+
// ---- \drg / \drg+ -----------------------------------------------------
|
|
1538
|
+
// Role grants — membership rows from `pg_auth_members`. Two-pattern shape
|
|
1539
|
+
// is upstream-specific to `\du`/`\dg`; `\drg` carries the same pattern as
|
|
1540
|
+
// `\du` (role name), with the cross-database check declined because roles
|
|
1541
|
+
// are global.
|
|
1542
|
+
const cmdDescribeRoleGrants = (cmdName) => ({
|
|
1543
|
+
name: cmdName,
|
|
1544
|
+
run: async (ctx) => {
|
|
1545
|
+
const pattern = ctx.nextArg('normal');
|
|
1546
|
+
const c = conn(ctx);
|
|
1547
|
+
if (!c)
|
|
1548
|
+
return noConn(ctx);
|
|
1549
|
+
const { showSystem } = decodeSuffix(cmdName, 'drg');
|
|
1550
|
+
const query = describeRoleGrants({
|
|
1551
|
+
pattern: pattern ?? undefined,
|
|
1552
|
+
showSystem,
|
|
1553
|
+
serverVersion: c.serverVersion,
|
|
1554
|
+
});
|
|
1555
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'm.rolname' }, 0);
|
|
1556
|
+
},
|
|
1557
|
+
});
|
|
1558
|
+
// ---- \dRp / \dRs ------------------------------------------------------
|
|
1559
|
+
const cmdListPublications = {
|
|
1560
|
+
name: 'dRp',
|
|
1561
|
+
run: async (ctx) => {
|
|
1562
|
+
const pattern = ctx.nextArg('normal');
|
|
1563
|
+
const c = conn(ctx);
|
|
1564
|
+
if (!c)
|
|
1565
|
+
return noConn(ctx);
|
|
1566
|
+
const query = listPublications({
|
|
1567
|
+
pattern: pattern ?? undefined,
|
|
1568
|
+
serverVersion: c.serverVersion,
|
|
1569
|
+
});
|
|
1570
|
+
// Publications are global with no schema or database qualifier
|
|
1571
|
+
// (upstream `validateSQLNamePattern(..., NULL, 1)`): any dot is
|
|
1572
|
+
// "too many".
|
|
1573
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'pubname' }, 0);
|
|
1574
|
+
},
|
|
1575
|
+
};
|
|
1576
|
+
const cmdDescribeSubscriptions = {
|
|
1577
|
+
name: 'dRs',
|
|
1578
|
+
run: async (ctx) => {
|
|
1579
|
+
const pattern = ctx.nextArg('normal');
|
|
1580
|
+
const c = conn(ctx);
|
|
1581
|
+
if (!c)
|
|
1582
|
+
return noConn(ctx);
|
|
1583
|
+
const { verbose } = decodeSuffix(ctx.cmdName, 'dRs');
|
|
1584
|
+
const query = describeSubscriptions({
|
|
1585
|
+
pattern: pattern ?? undefined,
|
|
1586
|
+
verbose,
|
|
1587
|
+
serverVersion: c.serverVersion,
|
|
1588
|
+
});
|
|
1589
|
+
// Subscriptions are global with no schema or database qualifier
|
|
1590
|
+
// (upstream `validateSQLNamePattern(..., NULL, 1)`): any dot is
|
|
1591
|
+
// "too many".
|
|
1592
|
+
return runWithPattern(ctx, pattern, query, { namevar: 'subname' }, 0);
|
|
1593
|
+
},
|
|
1594
|
+
};
|
|
1595
|
+
// ===========================================================================
|
|
1596
|
+
// Registration
|
|
1597
|
+
// ===========================================================================
|
|
1598
|
+
/**
|
|
1599
|
+
* Register every `\d*` command this WP implements on the given
|
|
1600
|
+
* registry. Called from `dispatch.ts::defaultRegistry()`.
|
|
1601
|
+
*
|
|
1602
|
+
* We register a separate spec per suffix combination so the dispatcher
|
|
1603
|
+
* can do a single name lookup without re-parsing. The implementation
|
|
1604
|
+
* functions are factory-style (`make…(cmdName)`) so each spec carries
|
|
1605
|
+
* the exact command name for suffix decoding.
|
|
1606
|
+
*/
|
|
1607
|
+
export const registerDescribeCommands = (registry) => {
|
|
1608
|
+
// Relation list+detail family. Each suffix combination is its own
|
|
1609
|
+
// registered name; suffix decoding happens at runtime via the
|
|
1610
|
+
// `cmdName` field on the spec (set per-registration).
|
|
1611
|
+
for (const base of RELATION_BASES) {
|
|
1612
|
+
for (const suffix of SUFFIX_COMBOS) {
|
|
1613
|
+
const name = base + suffix;
|
|
1614
|
+
registry.register({ ...makeDescribeCmd(base), name });
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
// `\d` accepts an additional `x` (expanded-mode toggle) suffix that we
|
|
1618
|
+
// register only for the bare `\d` base — upstream only honours it for
|
|
1619
|
+
// `\d` itself, not for typed variants like `\dt` or `\dv`.
|
|
1620
|
+
for (const suffix of DESCRIBE_X_SUFFIXES) {
|
|
1621
|
+
registry.register({ ...makeDescribeCmd('d'), name: 'd' + suffix });
|
|
1622
|
+
}
|
|
1623
|
+
// Functions. Beyond the standard `\df[+S]` matrix, upstream accepts
|
|
1624
|
+
// function-kind filter letters (a / n / p / t / w) and an expanded-mode
|
|
1625
|
+
// toggle (x) appended in any order, with multiple stacking. The fanout
|
|
1626
|
+
// covers the common one-letter additions used in regress; combined
|
|
1627
|
+
// letter sequences (`\dfax+`, `\dfxw`, …) are handled by the runtime
|
|
1628
|
+
// suffix walk in `cmdDescribeFunctions`.
|
|
1629
|
+
const dfTails = [
|
|
1630
|
+
'',
|
|
1631
|
+
'+',
|
|
1632
|
+
'S',
|
|
1633
|
+
'S+',
|
|
1634
|
+
'+S',
|
|
1635
|
+
'a',
|
|
1636
|
+
'n',
|
|
1637
|
+
'p',
|
|
1638
|
+
't',
|
|
1639
|
+
'w',
|
|
1640
|
+
'x',
|
|
1641
|
+
'ax',
|
|
1642
|
+
'nx',
|
|
1643
|
+
'px',
|
|
1644
|
+
'tx',
|
|
1645
|
+
'wx',
|
|
1646
|
+
'xa',
|
|
1647
|
+
'xn',
|
|
1648
|
+
'xp',
|
|
1649
|
+
'xt',
|
|
1650
|
+
'xw',
|
|
1651
|
+
'a+',
|
|
1652
|
+
'n+',
|
|
1653
|
+
'p+',
|
|
1654
|
+
't+',
|
|
1655
|
+
'w+',
|
|
1656
|
+
'x+',
|
|
1657
|
+
'a+x',
|
|
1658
|
+
'x+a',
|
|
1659
|
+
];
|
|
1660
|
+
for (const tail of dfTails) {
|
|
1661
|
+
registry.register(cmdDescribeFunctions('df' + tail));
|
|
1662
|
+
}
|
|
1663
|
+
// Aggregates.
|
|
1664
|
+
for (const suffix of ['', 'S']) {
|
|
1665
|
+
registry.register(cmdDescribeAggregates('da' + suffix));
|
|
1666
|
+
}
|
|
1667
|
+
// Access methods. `\dA[+]` is upstream; `\dAm[+]` is a Neon-friendly
|
|
1668
|
+
// alias that some docs reach for. `\dAc[+]`, `\dAf[+]`, `\dAo[+]`,
|
|
1669
|
+
// `\dAp[+]` cover the operator-class / family / family-operator /
|
|
1670
|
+
// family-function families upstream's `command.c::exec_command_d`
|
|
1671
|
+
// dispatches.
|
|
1672
|
+
for (const suffix of ['', '+']) {
|
|
1673
|
+
registry.register(cmdDescribeAccessMethods('dA' + suffix));
|
|
1674
|
+
registry.register(cmdDescribeAccessMethods('dAm' + suffix));
|
|
1675
|
+
registry.register(cmdListOperatorClasses('dAc' + suffix));
|
|
1676
|
+
registry.register(cmdListOperatorFamilies('dAf' + suffix));
|
|
1677
|
+
registry.register(cmdListOpFamilyOperators('dAo' + suffix));
|
|
1678
|
+
registry.register(cmdListOpFamilyFunctions('dAp' + suffix));
|
|
1679
|
+
}
|
|
1680
|
+
// `x` (expanded) variants of the two-pattern access-method families.
|
|
1681
|
+
for (const tail of ['x', 'x+', '+x', 'xS', 'Sx']) {
|
|
1682
|
+
registry.register(cmdListOpFamilyFunctions('dAp' + tail));
|
|
1683
|
+
}
|
|
1684
|
+
// Types.
|
|
1685
|
+
for (const suffix of SUFFIX_COMBOS) {
|
|
1686
|
+
registry.register(cmdDescribeTypes('dT' + suffix));
|
|
1687
|
+
}
|
|
1688
|
+
// Operators.
|
|
1689
|
+
for (const suffix of SUFFIX_COMBOS) {
|
|
1690
|
+
registry.register(cmdDescribeOperators('do' + suffix));
|
|
1691
|
+
}
|
|
1692
|
+
// Roles.
|
|
1693
|
+
for (const suffix of ['', '+', 'S', 'S+', '+S']) {
|
|
1694
|
+
registry.register(cmdDescribeRoles('du' + suffix));
|
|
1695
|
+
registry.register(cmdDescribeRoles('dg' + suffix));
|
|
1696
|
+
}
|
|
1697
|
+
// Schemas.
|
|
1698
|
+
for (const suffix of SUFFIX_COMBOS) {
|
|
1699
|
+
registry.register(cmdListSchemas('dn' + suffix));
|
|
1700
|
+
}
|
|
1701
|
+
// Domains.
|
|
1702
|
+
for (const suffix of SUFFIX_COMBOS) {
|
|
1703
|
+
registry.register(cmdListDomains('dD' + suffix));
|
|
1704
|
+
}
|
|
1705
|
+
// Conversions.
|
|
1706
|
+
for (const suffix of SUFFIX_COMBOS) {
|
|
1707
|
+
registry.register(cmdListConversions('dc' + suffix));
|
|
1708
|
+
}
|
|
1709
|
+
// Casts.
|
|
1710
|
+
for (const suffix of ['', '+']) {
|
|
1711
|
+
registry.register(cmdListCasts('dC' + suffix));
|
|
1712
|
+
}
|
|
1713
|
+
// Tablespaces.
|
|
1714
|
+
for (const suffix of ['', '+']) {
|
|
1715
|
+
registry.register(cmdDescribeTablespaces('db' + suffix));
|
|
1716
|
+
}
|
|
1717
|
+
// Languages.
|
|
1718
|
+
for (const suffix of SUFFIX_COMBOS) {
|
|
1719
|
+
registry.register(cmdListLanguages('dL' + suffix));
|
|
1720
|
+
}
|
|
1721
|
+
// Collations.
|
|
1722
|
+
for (const suffix of SUFFIX_COMBOS) {
|
|
1723
|
+
registry.register(cmdListCollations('dO' + suffix));
|
|
1724
|
+
}
|
|
1725
|
+
// Permissions / default ACLs. `\dp` and `\z` are independent base names
|
|
1726
|
+
// (aliasing them via `aliases` couldn't carry separate `x`-suffix
|
|
1727
|
+
// variants), so register each with its own suffix matrix.
|
|
1728
|
+
for (const tail of ['', 'S', 'x', 'Sx', 'xS']) {
|
|
1729
|
+
registry.register(cmdPermissionsList('dp' + tail));
|
|
1730
|
+
registry.register(cmdPermissionsList('z' + tail));
|
|
1731
|
+
}
|
|
1732
|
+
registry.register(cmdListDefaultACLs);
|
|
1733
|
+
// Descriptions.
|
|
1734
|
+
registry.register(cmdObjectDescription);
|
|
1735
|
+
// Databases. `\l`/`\list` plus their `+` (verbose) variants — the scanner
|
|
1736
|
+
// folds the trailing `+` into the command name, so `\l+` needs its own
|
|
1737
|
+
// registration or it dispatches to nothing. `decodeSuffix`
|
|
1738
|
+
// reads the verbose flag back off `ctx.cmdName` at runtime.
|
|
1739
|
+
registry.register(cmdListAllDbs);
|
|
1740
|
+
registry.register({ ...cmdListAllDbs, name: 'l+', aliases: ['list+'] });
|
|
1741
|
+
// Config / event triggers / extensions / large objects.
|
|
1742
|
+
registry.register(cmdDescribeConfigParams);
|
|
1743
|
+
registry.register({ ...cmdDescribeConfigParams, name: 'dconfig+' });
|
|
1744
|
+
registry.register(cmdListEventTriggers);
|
|
1745
|
+
registry.register(cmdListExtensions);
|
|
1746
|
+
registry.register({ ...cmdListExtensions, name: 'dx+' });
|
|
1747
|
+
registry.register(cmdListLargeObjects);
|
|
1748
|
+
// Text search family.
|
|
1749
|
+
registry.register(cmdListTSConfigs);
|
|
1750
|
+
registry.register({ ...cmdListTSConfigs, name: 'dF+' });
|
|
1751
|
+
registry.register(cmdListTSParsers);
|
|
1752
|
+
registry.register({ ...cmdListTSParsers, name: 'dFp+' });
|
|
1753
|
+
registry.register(cmdListTSDictionaries);
|
|
1754
|
+
registry.register({ ...cmdListTSDictionaries, name: 'dFd+' });
|
|
1755
|
+
registry.register(cmdListTSTemplates);
|
|
1756
|
+
registry.register({ ...cmdListTSTemplates, name: 'dFt+' });
|
|
1757
|
+
// Foreign-data family.
|
|
1758
|
+
for (const variant of ['dew', 'dew+']) {
|
|
1759
|
+
registry.register({ ...cmdListForeignDataWrappers, name: variant });
|
|
1760
|
+
}
|
|
1761
|
+
for (const variant of ['des', 'des+']) {
|
|
1762
|
+
registry.register({ ...cmdListForeignServers, name: variant });
|
|
1763
|
+
}
|
|
1764
|
+
for (const variant of ['deu', 'deu+']) {
|
|
1765
|
+
registry.register({ ...cmdListUserMappings, name: variant });
|
|
1766
|
+
}
|
|
1767
|
+
for (const variant of ['det', 'det+']) {
|
|
1768
|
+
registry.register({ ...cmdListForeignTables, name: variant });
|
|
1769
|
+
}
|
|
1770
|
+
// Partitioned tables. Upstream's `\dP` accepts any concatenation of
|
|
1771
|
+
// {i,t,n}: `\dPtn` lists nested partitioned tables (table + nested
|
|
1772
|
+
// toggles), `\dPin` lists nested partitioned indexes, etc. We
|
|
1773
|
+
// register the most common combinations explicitly; the spec's
|
|
1774
|
+
// suffix-decoding loop (`includes('i'|'t'|'n')`) drives the actual
|
|
1775
|
+
// `reltypes` selection at runtime.
|
|
1776
|
+
for (const variant of [
|
|
1777
|
+
'dP',
|
|
1778
|
+
'dP+',
|
|
1779
|
+
'dPi',
|
|
1780
|
+
'dPi+',
|
|
1781
|
+
'dPin',
|
|
1782
|
+
'dPin+',
|
|
1783
|
+
'dPt',
|
|
1784
|
+
'dPt+',
|
|
1785
|
+
'dPtn',
|
|
1786
|
+
'dPtn+',
|
|
1787
|
+
'dPn',
|
|
1788
|
+
'dPn+',
|
|
1789
|
+
]) {
|
|
1790
|
+
registry.register({ ...cmdListPartitionedTables, name: variant });
|
|
1791
|
+
}
|
|
1792
|
+
// Role-database settings.
|
|
1793
|
+
registry.register(cmdListDbRoleSettings);
|
|
1794
|
+
registry.register({ ...cmdListDbRoleSettings, name: 'drds+' });
|
|
1795
|
+
// Extended statistics (\dX / \dX+).
|
|
1796
|
+
for (const suffix of ['', '+']) {
|
|
1797
|
+
registry.register(cmdListExtendedStats('dX' + suffix));
|
|
1798
|
+
}
|
|
1799
|
+
// Role grants (\drg / \drg+ / \drgS).
|
|
1800
|
+
for (const suffix of ['', '+', 'S', 'S+', '+S']) {
|
|
1801
|
+
registry.register(cmdDescribeRoleGrants('drg' + suffix));
|
|
1802
|
+
}
|
|
1803
|
+
// Publication / subscription.
|
|
1804
|
+
for (const variant of ['dRp', 'dRp+']) {
|
|
1805
|
+
registry.register({ ...cmdListPublications, name: variant });
|
|
1806
|
+
}
|
|
1807
|
+
for (const variant of ['dRs', 'dRs+']) {
|
|
1808
|
+
registry.register({ ...cmdDescribeSubscriptions, name: variant });
|
|
1809
|
+
}
|
|
1810
|
+
};
|