neon 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +54 -0
  2. package/dist/_shared/auth_selection.js +76 -79
  3. package/dist/_shared/credentials.js +166 -187
  4. package/dist/_shared/env-core/env.js +354 -517
  5. package/dist/_shared/env-core/reuse-secrets.js +159 -203
  6. package/dist/_shared/paths.js +129 -126
  7. package/dist/_shared/profiles.js +192 -242
  8. package/dist/_shared/secure_file.js +36 -38
  9. package/dist/_virtual/_rolldown/runtime.js +13 -0
  10. package/dist/analytics.js +163 -207
  11. package/dist/api.js +815 -758
  12. package/dist/auth.js +121 -141
  13. package/dist/auth_context.js +39 -53
  14. package/dist/cli.js +4 -7
  15. package/dist/commands/api.js +220 -250
  16. package/dist/commands/api_keys.js +251 -314
  17. package/dist/commands/auth.js +283 -328
  18. package/dist/commands/bootstrap.js +372 -437
  19. package/dist/commands/branches.js +304 -455
  20. package/dist/commands/bucket.js +374 -514
  21. package/dist/commands/checkout.js +213 -298
  22. package/dist/commands/config.js +573 -690
  23. package/dist/commands/connection_string.js +137 -165
  24. package/dist/commands/data_api.js +238 -260
  25. package/dist/commands/databases.js +67 -76
  26. package/dist/commands/deploy.js +31 -25
  27. package/dist/commands/dev.js +639 -719
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -355
  31. package/dist/commands/index.js +70 -65
  32. package/dist/commands/init.js +84 -119
  33. package/dist/commands/inspect.js +55 -55
  34. package/dist/commands/ip_allow.js +88 -120
  35. package/dist/commands/link.js +874 -1019
  36. package/dist/commands/logs.js +291 -0
  37. package/dist/commands/neon_auth.js +725 -933
  38. package/dist/commands/operations.js +34 -25
  39. package/dist/commands/orgs.js +28 -18
  40. package/dist/commands/profile.js +614 -845
  41. package/dist/commands/projects.js +313 -373
  42. package/dist/commands/psql.js +60 -58
  43. package/dist/commands/roles.js +55 -58
  44. package/dist/commands/schema_diff.js +87 -131
  45. package/dist/commands/set_context.js +34 -26
  46. package/dist/commands/snapshots.js +288 -413
  47. package/dist/commands/status.js +41 -37
  48. package/dist/commands/user.js +21 -10
  49. package/dist/commands/vpc_endpoints.js +85 -113
  50. package/dist/config.js +7 -6
  51. package/dist/config_format.js +50 -66
  52. package/dist/config_template.js +128 -157
  53. package/dist/context.js +183 -235
  54. package/dist/current_branch_fast_path.js +40 -49
  55. package/dist/dev/env.js +313 -394
  56. package/dist/dev/functions.js +54 -68
  57. package/dist/dev/inputs.js +46 -58
  58. package/dist/dev/runtime.js +135 -164
  59. package/dist/dev/websocket.js +766 -959
  60. package/dist/env.js +27 -33
  61. package/dist/env_file.js +118 -132
  62. package/dist/env_services.js +36 -38
  63. package/dist/errors.js +57 -68
  64. package/dist/functions_api.js +45 -43
  65. package/dist/help.js +189 -140
  66. package/dist/index.js +182 -257
  67. package/dist/init/agents.js +137 -118
  68. package/dist/init/auth.js +58 -68
  69. package/dist/init/bootstrap.js +325 -396
  70. package/dist/init/build_config.js +4 -2
  71. package/dist/init/detect_agent.js +56 -101
  72. package/dist/init/editors.js +35 -52
  73. package/dist/init/enrich_output.js +51 -66
  74. package/dist/init/extension.js +134 -171
  75. package/dist/init/inspect.js +179 -266
  76. package/dist/init/interactive.js +510 -622
  77. package/dist/init/neonctl.js +117 -168
  78. package/dist/init/orchestrate.js +157 -173
  79. package/dist/init/phases/auth.js +188 -202
  80. package/dist/init/phases/cleanup.js +23 -23
  81. package/dist/init/phases/db.js +251 -277
  82. package/dist/init/phases/getting_started.js +213 -223
  83. package/dist/init/phases/mcp.js +174 -224
  84. package/dist/init/phases/migrations.js +247 -248
  85. package/dist/init/phases/neon_auth.js +114 -133
  86. package/dist/init/phases/setup.js +546 -703
  87. package/dist/init/phases/skills.js +75 -86
  88. package/dist/init/phases/status.js +72 -67
  89. package/dist/init/resolve_context.js +102 -99
  90. package/dist/init/route_command.js +91 -98
  91. package/dist/init/skills.js +174 -218
  92. package/dist/init/vsix.js +77 -99
  93. package/dist/log.js +17 -16
  94. package/dist/neon_services.js +104 -129
  95. package/dist/parameters.gen.js +481 -471
  96. package/dist/pkg.js +17 -19
  97. package/dist/profile_keys.js +44 -47
  98. package/dist/psql/cli.js +44 -47
  99. package/dist/psql/command/cmd_cond.js +231 -406
  100. package/dist/psql/command/cmd_connect.js +557 -764
  101. package/dist/psql/command/cmd_copy.js +727 -983
  102. package/dist/psql/command/cmd_describe.js +1499 -1688
  103. package/dist/psql/command/cmd_format.js +733 -905
  104. package/dist/psql/command/cmd_io.js +1293 -2082
  105. package/dist/psql/command/cmd_lo.js +297 -359
  106. package/dist/psql/command/cmd_meta.js +727 -878
  107. package/dist/psql/command/cmd_misc.js +138 -172
  108. package/dist/psql/command/cmd_pipeline.js +547 -1099
  109. package/dist/psql/command/cmd_restrict.js +119 -155
  110. package/dist/psql/command/cmd_show.js +529 -688
  111. package/dist/psql/command/dispatch.js +261 -325
  112. package/dist/psql/command/inputQueue.js +35 -33
  113. package/dist/psql/command/shared.js +49 -63
  114. package/dist/psql/complete/filenames.js +90 -133
  115. package/dist/psql/complete/index.js +59 -97
  116. package/dist/psql/complete/matcher.js +236 -300
  117. package/dist/psql/complete/psqlVars.js +218 -223
  118. package/dist/psql/complete/queries.js +159 -177
  119. package/dist/psql/complete/rules.js +1493 -2299
  120. package/dist/psql/core/common.js +762 -1180
  121. package/dist/psql/core/help.js +456 -546
  122. package/dist/psql/core/mainloop.js +692 -1302
  123. package/dist/psql/core/prompt.js +391 -408
  124. package/dist/psql/core/settings.js +429 -644
  125. package/dist/psql/core/sqlHelp.js +480 -554
  126. package/dist/psql/core/startup.js +626 -815
  127. package/dist/psql/core/syncVars.js +67 -110
  128. package/dist/psql/core/variables.js +156 -278
  129. package/dist/psql/describe/formatters.js +884 -1285
  130. package/dist/psql/describe/processNamePattern.js +173 -260
  131. package/dist/psql/describe/queries.js +1368 -2403
  132. package/dist/psql/describe/versionGate.js +32 -41
  133. package/dist/psql/index.js +1414 -1927
  134. package/dist/psql/io/history.js +232 -271
  135. package/dist/psql/io/input.js +103 -108
  136. package/dist/psql/io/lineEditor/buffer.js +238 -319
  137. package/dist/psql/io/lineEditor/complete.js +135 -213
  138. package/dist/psql/io/lineEditor/filename.js +139 -148
  139. package/dist/psql/io/lineEditor/index.js +653 -870
  140. package/dist/psql/io/lineEditor/keymap.js +544 -702
  141. package/dist/psql/io/lineEditor/vt100.js +294 -341
  142. package/dist/psql/io/pgpass.js +158 -187
  143. package/dist/psql/io/pgservice.js +146 -183
  144. package/dist/psql/io/psqlrc.js +328 -403
  145. package/dist/psql/print/aligned.js +1020 -1683
  146. package/dist/psql/print/asciidoc.js +180 -214
  147. package/dist/psql/print/crosstab.js +281 -442
  148. package/dist/psql/print/csv.js +48 -70
  149. package/dist/psql/print/html.js +195 -226
  150. package/dist/psql/print/json.js +75 -88
  151. package/dist/psql/print/latex.js +291 -364
  152. package/dist/psql/print/pager.js +171 -242
  153. package/dist/psql/print/troff.js +194 -226
  154. package/dist/psql/print/unaligned.js +69 -95
  155. package/dist/psql/print/units.js +167 -169
  156. package/dist/psql/scanner/slash.js +428 -483
  157. package/dist/psql/scanner/sql.js +445 -889
  158. package/dist/psql/scanner/stringutils.js +309 -379
  159. package/dist/psql/types/index.js +2 -7
  160. package/dist/psql/types/scanner.js +25 -22
  161. package/dist/psql/wire/connection.js +2042 -2803
  162. package/dist/psql/wire/copy.js +84 -100
  163. package/dist/psql/wire/notify.js +39 -59
  164. package/dist/psql/wire/pipeline.js +305 -518
  165. package/dist/psql/wire/protocol.js +349 -417
  166. package/dist/psql/wire/sasl.js +180 -265
  167. package/dist/psql/wire/tls.js +400 -561
  168. package/dist/storage_api.js +115 -129
  169. package/dist/test_utils/fixtures.js +94 -113
  170. package/dist/test_utils/oauth_server.js +10 -7
  171. package/dist/test_utils/project_dir.js +33 -0
  172. package/dist/utils/ai_gateway_notice.js +131 -162
  173. package/dist/utils/api_enums.js +21 -28
  174. package/dist/utils/auth.js +10 -4
  175. package/dist/utils/branch_notice.js +20 -19
  176. package/dist/utils/branch_picker.js +83 -89
  177. package/dist/utils/cli_name.js +15 -12
  178. package/dist/utils/compute_units.js +20 -27
  179. package/dist/utils/config_diff.js +127 -158
  180. package/dist/utils/enrichers.js +95 -148
  181. package/dist/utils/esbuild.js +130 -189
  182. package/dist/utils/flags.js +35 -47
  183. package/dist/utils/formats.js +8 -15
  184. package/dist/utils/git_diff.js +69 -80
  185. package/dist/utils/inspect_db.js +101 -143
  186. package/dist/utils/inspect_queries.js +179 -142
  187. package/dist/utils/middlewares.js +37 -44
  188. package/dist/utils/openapi.js +87 -99
  189. package/dist/utils/package_manager.js +312 -110
  190. package/dist/utils/point_in_time.js +49 -53
  191. package/dist/utils/psql.js +89 -106
  192. package/dist/utils/service_picker.js +55 -58
  193. package/dist/utils/string.js +5 -5
  194. package/dist/utils/ui.js +38 -55
  195. package/dist/utils/write_sync.js +26 -35
  196. package/dist/utils/zip.js +4 -3
  197. package/dist/writer.js +67 -87
  198. package/package.json +7 -5
@@ -1,2452 +1,1417 @@
1
+ import { PG_10, PG_11, PG_12, PG_13, PG_14, PG_15, PG_18, PG_19, PG_9_5, serverAtLeast, serverLess } from "./versionGate.js";
2
+ //#region src/psql/describe/queries.ts
1
3
  /**
2
- * SQL builders for psql's `\d*` describe commands.
3
- *
4
- * Each exported function returns a {@link DescribeQuery} carrying the SQL the
5
- * upstream C `describe.c` would have produced for the same `\d*` invocation,
6
- * adapted for the active `serverVersion`. The header text (column aliases)
7
- * matches upstream literally so that WP-20's formatter can rely on stable
8
- * output column names.
9
- *
10
- * Pattern matching (the `processSQLNamePattern` flex routine in upstream) is
11
- * NOT yet implemented; we accept a `pattern` option and emit a placeholder
12
- * `(<col> ~ $1 OR $1 IS NULL)` clause where needed. WP-20 wires the real
13
- * pattern parser. See TODO comments throughout.
14
- *
15
- * Translation policy: SQL whitespace is normalized but column aliases and
16
- * catalog table/column names are byte-identical to upstream. Conditional
17
- * branches in the C source collapse into TS conditional concatenation gated
18
- * on `serverVersion`.
19
- */
20
- import { PG_9_5, PG_9_6, PG_10, PG_11, PG_12, PG_13, PG_14, PG_15, PG_16, PG_17, PG_18, PG_19, serverAtLeast, serverLess, } from "./versionGate.js";
21
- /* ------------------------------------------------------------------ */
22
- /* Shared helpers */
23
- /* ------------------------------------------------------------------ */
4
+ * Reproduces upstream `printACLColumn(buf, colname)`: emits the standard
5
+ * ACL pretty-printing expression with column alias "Access privileges".
6
+ */
7
+ const aclColumn = (colname) => `CASE WHEN pg_catalog.array_length(${colname}, 1) = 0 THEN '(none)' ELSE pg_catalog.array_to_string(${colname}, E'\\n') END AS "Access privileges"`;
24
8
  /**
25
- * Reproduces upstream `printACLColumn(buf, colname)`: emits the standard
26
- * ACL pretty-printing expression with column alias "Access privileges".
27
- */
28
- const aclColumn = (colname) => `CASE WHEN pg_catalog.array_length(${colname}, 1) = 0 THEN '(none)'` +
29
- ` ELSE pg_catalog.array_to_string(${colname}, E'\\n') END AS "Access privileges"`;
30
- /**
31
- * Placeholder pattern clause. WP-20 will replace these stubs with the real
32
- * `processSQLNamePattern` port. For now the clause is a tautology so the
33
- * query runs unfiltered; the pattern parameter is still threaded through
34
- * `params` so callers do not need to change call sites later.
35
- *
36
- * @param hasWhere whether a WHERE clause already exists in the SQL
37
- * @param schemaCol column expression for the schema name, or undefined
38
- * @param nameCol column expression for the object name
39
- */
9
+ * Placeholder pattern clause. WP-20 will replace these stubs with the real
10
+ * `processSQLNamePattern` port. For now the clause is a tautology so the
11
+ * query runs unfiltered; the pattern parameter is still threaded through
12
+ * `params` so callers do not need to change call sites later.
13
+ *
14
+ * @param hasWhere whether a WHERE clause already exists in the SQL
15
+ * @param schemaCol column expression for the schema name, or undefined
16
+ * @param nameCol column expression for the object name
17
+ */
40
18
  const patternStub = (hasWhere, schemaCol, nameCol) => {
41
- // TODO(WP-20): real pattern matching via processSQLNamePattern port.
42
- void schemaCol;
43
- void nameCol;
44
- const join = hasWhere ? " AND " : "WHERE ";
45
- return `${join}true /* TODO(WP-20): pattern matching */\n`;
19
+ return `${hasWhere ? " AND " : "WHERE "}true /* TODO(WP-20): pattern matching */\n`;
46
20
  };
47
21
  /**
48
- * Per-argument pattern slot for `\df name argtype1 argtype2 …` and
49
- * `\do name argtype1 argtype2`. Each slot emits a uniquely tagged
50
- * placeholder so the dispatcher can substitute the i-th processed
51
- * type-name pattern (matched against `t<i>.typname`, `nt<i>.nspname`,
52
- * and `format_type(t<i>.oid, NULL)` like upstream).
53
- */
22
+ * Per-argument pattern slot for `\df name argtype1 argtype2 …` and
23
+ * `\do name argtype1 argtype2`. Each slot emits a uniquely tagged
24
+ * placeholder so the dispatcher can substitute the i-th processed
25
+ * type-name pattern (matched against `t<i>.typname`, `nt<i>.nspname`,
26
+ * and `format_type(t<i>.oid, NULL)` like upstream).
27
+ */
54
28
  const argPatternStub = (slot) => `true /* ARG_PATTERN_${slot} */`;
55
29
  const orderBy = (cols) => `ORDER BY ${cols};`;
56
- // Returns the base params for a describe query. Always empty: the pattern
57
- // itself is threaded through `processSQLNamePattern` + `applyPattern` in
58
- // cmd_describe.ts, which renders the conditions with their own `$N` slots
59
- // and pushes the right values into params at runtime. Returning [pattern]
60
- // here would leave $1 unreferenced in the final SQL (applyPattern shifts
61
- // the conditions' $N by baseParams.length), causing the server to reject
62
- // the Parse with "could not determine data type of parameter $1".
63
30
  const params = () => [];
64
- /* ------------------------------------------------------------------ */
65
- /* \da — describeAggregates */
66
- /* ------------------------------------------------------------------ */
67
- export const describeAggregates = (opts) => {
68
- const { showSystem, pattern, serverVersion } = opts;
69
- let sql = "";
70
- sql +=
71
- 'SELECT n.nspname as "Schema",\n' +
72
- ' p.proname AS "Name",\n' +
73
- ' pg_catalog.format_type(p.prorettype, NULL) AS "Result data type",\n' +
74
- " CASE WHEN p.pronargs = 0\n" +
75
- " THEN CAST('*' AS pg_catalog.text)\n" +
76
- " ELSE pg_catalog.pg_get_function_arguments(p.oid)\n" +
77
- ' END AS "Argument data types",\n' +
78
- " pg_catalog.obj_description(p.oid, 'pg_proc') as \"Description\"\n" +
79
- "FROM pg_catalog.pg_proc p\n" +
80
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n";
81
- sql += serverAtLeast(serverVersion, PG_11)
82
- ? "WHERE p.prokind = 'a'\n"
83
- : "WHERE p.proisagg\n";
84
- if (!showSystem && pattern === undefined) {
85
- sql +=
86
- " AND n.nspname <> 'pg_catalog'\n" +
87
- " AND n.nspname <> 'information_schema'\n";
88
- }
89
- sql += patternStub(true, "n.nspname", "p.proname");
90
- sql += orderBy("1, 2, 4");
91
- return {
92
- sql,
93
- params: params(),
94
- description: "List of aggregate functions",
95
- };
96
- };
97
- /* ------------------------------------------------------------------ */
98
- /* \dA — describeAccessMethods */
99
- /* ------------------------------------------------------------------ */
100
- export const describeAccessMethods = (opts) => {
101
- const { verbose, serverVersion } = opts;
102
- if (serverLess(serverVersion, 9, 6)) {
103
- return {
104
- sql: "/* server < 9.6 does not support access methods */ SELECT 1 WHERE false;",
105
- params: [],
106
- description: "List of access methods",
107
- };
108
- }
109
- let sql = 'SELECT amname AS "Name",\n' +
110
- " CASE amtype WHEN 'i' THEN 'Index' WHEN 't' THEN 'Table' END AS \"Type\"";
111
- if (verbose) {
112
- sql +=
113
- ',\n amhandler AS "Handler",\n' +
114
- " pg_catalog.obj_description(oid, 'pg_am') AS \"Description\"";
115
- }
116
- sql += "\nFROM pg_catalog.pg_am\n";
117
- sql += patternStub(false, undefined, "amname");
118
- sql += orderBy("1");
119
- return { sql, params: params(), description: "List of access methods" };
120
- };
121
- /* ------------------------------------------------------------------ */
122
- /* \db — describeTablespaces */
123
- /* ------------------------------------------------------------------ */
124
- export const describeTablespaces = (opts) => {
125
- const { verbose } = opts;
126
- let sql = 'SELECT spcname AS "Name",\n' +
127
- ' pg_catalog.pg_get_userbyid(spcowner) AS "Owner",\n' +
128
- ' pg_catalog.pg_tablespace_location(oid) AS "Location"';
129
- if (verbose) {
130
- sql += ",\n " + aclColumn("spcacl");
131
- sql +=
132
- ',\n spcoptions AS "Options",\n' +
133
- ' pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS "Size",\n' +
134
- " pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"Description\"";
135
- }
136
- sql += "\nFROM pg_catalog.pg_tablespace\n";
137
- sql += patternStub(false, undefined, "spcname");
138
- sql += orderBy("1");
139
- return { sql, params: params(), description: "List of tablespaces" };
140
- };
141
- export const describeFunctions = (opts) => {
142
- const { verbose, showSystem, pattern, serverVersion } = opts;
143
- const functypes = opts.functypes ?? "";
144
- const argPatterns = opts.argPatterns ?? [];
145
- let showAgg = functypes.includes("a");
146
- let showNorm = functypes.includes("n");
147
- let showProc = functypes.includes("p");
148
- let showTrig = functypes.includes("t");
149
- let showWin = functypes.includes("w");
150
- if (!showAgg && !showNorm && !showProc && !showTrig && !showWin) {
151
- showAgg = showNorm = showTrig = showWin = true;
152
- if (serverAtLeast(serverVersion, PG_11))
153
- showProc = true;
154
- }
155
- let sql = 'SELECT n.nspname as "Schema",\n p.proname as "Name",\n';
156
- if (serverAtLeast(serverVersion, PG_11)) {
157
- sql +=
158
- ' pg_catalog.pg_get_function_result(p.oid) as "Result data type",\n' +
159
- ' pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types",\n' +
160
- " CASE p.prokind\n" +
161
- " WHEN 'a' THEN 'agg'\n" +
162
- " WHEN 'w' THEN 'window'\n" +
163
- " WHEN 'p' THEN 'proc'\n" +
164
- " ELSE 'func'\n" +
165
- ' END as "Type"';
166
- }
167
- else {
168
- sql +=
169
- ' pg_catalog.pg_get_function_result(p.oid) as "Result data type",\n' +
170
- ' pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types",\n' +
171
- " CASE\n" +
172
- " WHEN p.proisagg THEN 'agg'\n" +
173
- " WHEN p.proiswindow THEN 'window'\n" +
174
- " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger'\n" +
175
- " ELSE 'func'\n" +
176
- ' END as "Type"';
177
- }
178
- if (verbose) {
179
- sql +=
180
- ",\n CASE\n" +
181
- " WHEN p.provolatile = 'i' THEN 'immutable'\n" +
182
- " WHEN p.provolatile = 's' THEN 'stable'\n" +
183
- " WHEN p.provolatile = 'v' THEN 'volatile'\n" +
184
- ' END as "Volatility"';
185
- if (serverAtLeast(serverVersion, PG_9_6)) {
186
- sql +=
187
- ",\n CASE\n" +
188
- " WHEN p.proparallel = 'r' THEN 'restricted'\n" +
189
- " WHEN p.proparallel = 's' THEN 'safe'\n" +
190
- " WHEN p.proparallel = 'u' THEN 'unsafe'\n" +
191
- ' END as "Parallel"';
192
- }
193
- sql +=
194
- ',\n pg_catalog.pg_get_userbyid(p.proowner) as "Owner"' +
195
- ",\n CASE WHEN prosecdef THEN 'definer' ELSE 'invoker' END AS \"Security\"";
196
- // PG 18 added a "Leakproof?" column to verbose `\df+`. The pg_proc
197
- // catalog has carried `proleakproof` since 9.2, but upstream's psql
198
- // only began surfacing it in 18.
199
- if (serverAtLeast(serverVersion, PG_18)) {
200
- sql +=
201
- ",\n CASE WHEN p.proleakproof THEN 'yes' ELSE 'no' END as \"Leakproof?\"";
202
- }
203
- sql += ",\n " + aclColumn("p.proacl");
204
- sql +=
205
- ',\n l.lanname as "Language"' +
206
- ",\n CASE WHEN l.lanname IN ('internal', 'c') THEN p.prosrc END as \"Internal name\"" +
207
- ",\n pg_catalog.obj_description(p.oid, 'pg_proc') as \"Description\"";
208
- }
209
- sql +=
210
- "\nFROM pg_catalog.pg_proc p" +
211
- "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n";
212
- // Upstream emits the per-argument joins *before* the verbose-language
213
- // join so that all `t<i>`/`nt<i>` tables exist by the time the WHERE
214
- // pulls in per-arg conditions. See describe.c::describeFunctions.
215
- for (let i = 0; i < argPatterns.length; i++) {
216
- sql +=
217
- ` LEFT JOIN pg_catalog.pg_type t${i} ON t${i}.oid = p.proargtypes[${i}]\n` +
218
- ` LEFT JOIN pg_catalog.pg_namespace nt${i} ON nt${i}.oid = t${i}.typnamespace\n`;
219
- }
220
- if (verbose) {
221
- sql += " LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n";
222
- }
223
- let hasWhere = false;
224
- const allTypes = showAgg && showNorm && showProc && showTrig && showWin;
225
- if (!allTypes && showNorm) {
226
- if (!showAgg) {
227
- sql += hasWhere ? " AND " : "WHERE ";
228
- hasWhere = true;
229
- sql += serverAtLeast(serverVersion, PG_11)
230
- ? "p.prokind <> 'a'\n"
231
- : "NOT p.proisagg\n";
232
- }
233
- if (!showProc && serverAtLeast(serverVersion, PG_11)) {
234
- sql += hasWhere ? " AND " : "WHERE ";
235
- hasWhere = true;
236
- sql += "p.prokind <> 'p'\n";
237
- }
238
- if (!showTrig) {
239
- sql += hasWhere ? " AND " : "WHERE ";
240
- hasWhere = true;
241
- sql += "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n";
242
- }
243
- if (!showWin) {
244
- sql += hasWhere ? " AND " : "WHERE ";
245
- hasWhere = true;
246
- sql += serverAtLeast(serverVersion, PG_11)
247
- ? "p.prokind <> 'w'\n"
248
- : "NOT p.proiswindow\n";
249
- }
250
- }
251
- else if (!allTypes) {
252
- sql += "WHERE (\n ";
253
- hasWhere = true;
254
- const parts = [];
255
- if (showAgg) {
256
- parts.push(serverAtLeast(serverVersion, PG_11)
257
- ? "p.prokind = 'a'"
258
- : "p.proisagg");
259
- }
260
- if (showTrig) {
261
- parts.push("p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype");
262
- }
263
- if (showProc)
264
- parts.push("p.prokind = 'p'");
265
- if (showWin) {
266
- parts.push(serverAtLeast(serverVersion, PG_11)
267
- ? "p.prokind = 'w'"
268
- : "p.proiswindow");
269
- }
270
- sql += parts.join("\n OR ") + "\n )\n";
271
- }
272
- sql += patternStub(hasWhere, "n.nspname", "p.proname");
273
- // Upstream's per-argument filter loop. `-` means "no type in this
274
- // slot" (function has fewer args); anything else is a type-name
275
- // pattern matched against typname/format_type — same shape as `\dT`.
276
- // Each slot emits a uniquely tagged placeholder so cmd_describe.ts
277
- // can splice in the appropriate per-arg conditions.
278
- for (let i = 0; i < argPatterns.length; i++) {
279
- if (argPatterns[i] === "-") {
280
- sql += ` AND t${i}.typname IS NULL\n`;
281
- }
282
- else {
283
- sql += ` AND ${argPatternStub(i)}\n`;
284
- }
285
- }
286
- if (!showSystem && pattern === undefined) {
287
- sql +=
288
- " AND n.nspname <> 'pg_catalog'\n" +
289
- " AND n.nspname <> 'information_schema'\n";
290
- }
291
- sql += orderBy("1, 2, 4");
292
- return { sql, params: params(), description: "List of functions" };
293
- };
294
- /* ------------------------------------------------------------------ */
295
- /* \dT — describeTypes */
296
- /* ------------------------------------------------------------------ */
297
- export const describeTypes = (opts) => {
298
- const { verbose, showSystem, pattern } = opts;
299
- let sql = 'SELECT n.nspname as "Schema",\n pg_catalog.format_type(t.oid, NULL) AS "Name",\n';
300
- if (verbose) {
301
- sql +=
302
- ' t.typname AS "Internal name",\n' +
303
- " CASE WHEN t.typrelid != 0\n" +
304
- " THEN CAST('tuple' AS pg_catalog.text)\n" +
305
- " WHEN t.typlen < 0\n" +
306
- " THEN CAST('var' AS pg_catalog.text)\n" +
307
- " ELSE CAST(t.typlen AS pg_catalog.text)\n" +
308
- ' END AS "Size",\n' +
309
- " pg_catalog.array_to_string(\n" +
310
- " ARRAY(\n" +
311
- " SELECT e.enumlabel\n" +
312
- " FROM pg_catalog.pg_enum e\n" +
313
- " WHERE e.enumtypid = t.oid\n" +
314
- " ORDER BY e.enumsortorder\n" +
315
- " ),\n" +
316
- " E'\\n'\n" +
317
- ' ) AS "Elements",\n' +
318
- ' pg_catalog.pg_get_userbyid(t.typowner) AS "Owner",\n ' +
319
- aclColumn("t.typacl") +
320
- ",\n ";
321
- }
322
- sql +=
323
- " pg_catalog.obj_description(t.oid, 'pg_type') as \"Description\"\n";
324
- sql +=
325
- "FROM pg_catalog.pg_type t\n" +
326
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n";
327
- sql += "WHERE (t.typrelid = 0 ";
328
- sql +=
329
- "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))\n";
330
- if (!pattern?.includes("[]")) {
331
- sql +=
332
- " AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)\n";
333
- }
334
- if (!showSystem && pattern === undefined) {
335
- sql +=
336
- " AND n.nspname <> 'pg_catalog'\n" +
337
- " AND n.nspname <> 'information_schema'\n";
338
- }
339
- sql += patternStub(true, "n.nspname", "t.typname");
340
- sql += orderBy("1, 2");
341
- return { sql, params: params(), description: "List of data types" };
342
- };
343
- export const describeOperators = (opts) => {
344
- const { verbose, showSystem, pattern, serverVersion } = opts;
345
- let argPatterns = opts.argPatterns ?? [];
346
- // Upstream caps at 2 — extra args past two are silently dropped.
347
- if (argPatterns.length > 2)
348
- argPatterns = argPatterns.slice(0, 2);
349
- let sql = 'SELECT n.nspname as "Schema",\n' +
350
- ' o.oprname AS "Name",\n' +
351
- " CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"Left arg type\",\n" +
352
- " CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"Right arg type\",\n" +
353
- ' pg_catalog.format_type(o.oprresult, NULL) AS "Result type",\n';
354
- if (verbose) {
355
- sql += ' o.oprcode AS "Function",\n';
356
- // PG 18 added "Leakproof?" to verbose `\do+`. Earlier servers only
357
- // emitted Function and Description.
358
- if (serverAtLeast(serverVersion, PG_18)) {
359
- sql +=
360
- " CASE WHEN p.proleakproof THEN 'yes' ELSE 'no' END AS \"Leakproof?\",\n";
361
- }
362
- }
363
- sql +=
364
- " coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n" +
365
- " pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"Description\"\n" +
366
- "FROM pg_catalog.pg_operator o\n" +
367
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n";
368
- // Upstream emits arg-type joins before the verbose `pg_proc` join.
369
- // With one arg pattern: only join the right-hand side (oprright);
370
- // with two: both oprleft/oprright. Tag indices match the join name
371
- // so `t0/nt0` correspond to the first arg pattern slot.
372
- if (argPatterns.length === 1) {
373
- sql +=
374
- " LEFT JOIN pg_catalog.pg_type t0 ON t0.oid = o.oprright\n" +
375
- " LEFT JOIN pg_catalog.pg_namespace nt0 ON nt0.oid = t0.typnamespace\n";
376
- }
377
- else if (argPatterns.length >= 2) {
378
- sql +=
379
- " LEFT JOIN pg_catalog.pg_type t0 ON t0.oid = o.oprleft\n" +
380
- " LEFT JOIN pg_catalog.pg_namespace nt0 ON nt0.oid = t0.typnamespace\n" +
381
- " LEFT JOIN pg_catalog.pg_type t1 ON t1.oid = o.oprright\n" +
382
- " LEFT JOIN pg_catalog.pg_namespace nt1 ON nt1.oid = t1.typnamespace\n";
383
- }
384
- if (verbose && serverAtLeast(serverVersion, PG_18)) {
385
- // Only joined when we need proleakproof from pg_proc (PG 18+).
386
- sql += " LEFT JOIN pg_catalog.pg_proc p ON p.oid = o.oprcode\n";
387
- }
388
- let hasWhere = false;
389
- if (!showSystem && pattern === undefined) {
390
- sql +=
391
- "WHERE n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
392
- hasWhere = true;
393
- }
394
- sql += patternStub(hasWhere, "n.nspname", "o.oprname");
395
- // With a single arg pattern, upstream additionally constrains the
396
- // operator to be unary-right (oprleft = 0) so unrelated binary
397
- // operators don't leak through the `t0` join.
398
- if (argPatterns.length === 1) {
399
- sql += " AND o.oprleft = 0\n";
400
- }
401
- for (let i = 0; i < argPatterns.length; i++) {
402
- if (argPatterns[i] === "-") {
403
- sql += ` AND t${i}.typname IS NULL\n`;
404
- }
405
- else {
406
- sql += ` AND ${argPatternStub(i)}\n`;
407
- }
408
- }
409
- sql += orderBy("1, 2, 3, 4");
410
- return { sql, params: params(), description: "List of operators" };
411
- };
412
- /* ------------------------------------------------------------------ */
413
- /* \l / \list — listAllDbs */
414
- /* ------------------------------------------------------------------ */
415
- export const listAllDbs = (opts) => {
416
- const { verbose, serverVersion } = opts;
417
- let sql = "SELECT\n" +
418
- ' d.datname as "Name",\n' +
419
- ' pg_catalog.pg_get_userbyid(d.datdba) as "Owner",\n' +
420
- ' pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",\n';
421
- if (serverAtLeast(serverVersion, PG_15)) {
422
- sql +=
423
- " CASE d.datlocprovider" +
424
- " WHEN 'b' THEN 'builtin'" +
425
- " WHEN 'c' THEN 'libc'" +
426
- " WHEN 'i' THEN 'icu'" +
427
- ' END AS "Locale Provider",\n';
428
- }
429
- else {
430
- sql += " 'libc' AS \"Locale Provider\",\n";
431
- }
432
- sql += ' d.datcollate as "Collate",\n d.datctype as "Ctype",\n';
433
- if (serverAtLeast(serverVersion, PG_17)) {
434
- sql += ' d.datlocale as "Locale",\n';
435
- }
436
- else if (serverAtLeast(serverVersion, PG_15)) {
437
- sql += ' d.daticulocale as "Locale",\n';
438
- }
439
- else {
440
- sql += ' NULL as "Locale",\n';
441
- }
442
- if (serverAtLeast(serverVersion, PG_16)) {
443
- sql += ' d.daticurules as "ICU Rules",\n';
444
- }
445
- else {
446
- sql += ' NULL as "ICU Rules",\n';
447
- }
448
- sql += " " + aclColumn("d.datacl");
449
- if (verbose) {
450
- sql +=
451
- ",\n CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n" +
452
- " THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n" +
453
- " ELSE 'No Access'\n" +
454
- ' END as "Size"' +
455
- ',\n t.spcname as "Tablespace"' +
456
- ",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"Description\"";
457
- }
458
- sql += "\nFROM pg_catalog.pg_database d\n";
459
- if (verbose) {
460
- sql += " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n";
461
- }
462
- if (opts.pattern !== undefined) {
463
- sql += patternStub(false, undefined, "d.datname");
464
- }
465
- sql += orderBy("1");
466
- return { sql, params: params(), description: "List of databases" };
467
- };
468
- /* ------------------------------------------------------------------ */
469
- /* \dp / \z — permissionsList */
470
- /* ------------------------------------------------------------------ */
471
- export const permissionsList = (opts) => {
472
- const { showSystem, pattern, serverVersion } = opts;
473
- let sql = 'SELECT n.nspname as "Schema",\n' +
474
- ' c.relname as "Name",\n' +
475
- " CASE c.relkind" +
476
- " WHEN 'r' THEN 'table'" +
477
- " WHEN 'v' THEN 'view'" +
478
- " WHEN 'm' THEN 'materialized view'" +
479
- " WHEN 'S' THEN 'sequence'" +
480
- " WHEN 'f' THEN 'foreign table'" +
481
- " WHEN 'p' THEN 'partitioned table'" +
482
- ' END as "Type",\n ';
483
- sql += aclColumn("c.relacl");
484
- sql +=
485
- ",\n pg_catalog.array_to_string(ARRAY(\n" +
486
- " SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n" +
487
- " FROM pg_catalog.pg_attribute a\n" +
488
- " WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n" +
489
- " ), E'\\n') AS \"Column privileges\"";
490
- if (serverAtLeast(serverVersion, PG_10)) {
491
- sql +=
492
- ",\n pg_catalog.array_to_string(ARRAY(\n" +
493
- " SELECT polname\n" +
494
- " || CASE WHEN NOT polpermissive THEN E' (RESTRICTIVE)' ELSE '' END\n" +
495
- " || CASE WHEN polcmd != '*' THEN E' (' || polcmd::pg_catalog.text || E'):' ELSE E':' END\n" +
496
- " || CASE WHEN polqual IS NOT NULL THEN E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid) ELSE E'' END\n" +
497
- " || CASE WHEN polwithcheck IS NOT NULL THEN E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid) ELSE E'' END\n" +
498
- " || CASE WHEN polroles <> '{0}' THEN E'\\n to: ' || pg_catalog.array_to_string(\n" +
499
- " ARRAY(SELECT rolname FROM pg_catalog.pg_roles WHERE oid = ANY (polroles) ORDER BY 1)\n" +
500
- " , E', ') ELSE E'' END\n" +
501
- " FROM pg_catalog.pg_policy pol\n" +
502
- " WHERE polrelid = c.oid), E'\\n') AS \"Policies\"";
503
- }
504
- else if (serverAtLeast(serverVersion, PG_9_5)) {
505
- sql +=
506
- ",\n pg_catalog.array_to_string(ARRAY(\n" +
507
- " SELECT polname\n" +
508
- " || CASE WHEN polcmd != '*' THEN E' (' || polcmd::pg_catalog.text || E'):' ELSE E':' END\n" +
509
- " || CASE WHEN polqual IS NOT NULL THEN E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid) ELSE E'' END\n" +
510
- " || CASE WHEN polwithcheck IS NOT NULL THEN E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid) ELSE E'' END\n" +
511
- " || CASE WHEN polroles <> '{0}' THEN E'\\n to: ' || pg_catalog.array_to_string(\n" +
512
- " ARRAY(SELECT rolname FROM pg_catalog.pg_roles WHERE oid = ANY (polroles) ORDER BY 1)\n" +
513
- " , E', ') ELSE E'' END\n" +
514
- " FROM pg_catalog.pg_policy pol\n" +
515
- " WHERE polrelid = c.oid), E'\\n') AS \"Policies\"";
516
- }
517
- sql +=
518
- "\nFROM pg_catalog.pg_class c\n" +
519
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" +
520
- "WHERE c.relkind IN ('r','v','m','S','f','p')\n";
521
- if (!showSystem && pattern === undefined) {
522
- sql +=
523
- " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
524
- }
525
- sql += patternStub(true, "n.nspname", "c.relname");
526
- sql += orderBy("1, 2");
527
- return { sql, params: params(), description: "Access privileges" };
528
- };
529
- /* ------------------------------------------------------------------ */
530
- /* \ddp — listDefaultACLs */
531
- /* ------------------------------------------------------------------ */
532
- export const listDefaultACLs = (opts) => {
533
- const { serverVersion } = opts;
534
- // PG 18 added DEFACLOBJ_LARGEOBJECT ('L'); pre-18 servers can never
535
- // produce that defaclobjtype value, so emitting the WHEN is harmless
536
- // there — but to mirror upstream `\ddp` output, we gate it.
537
- let sql = 'SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS "Owner",\n' +
538
- ' n.nspname AS "Schema",\n' +
539
- " CASE d.defaclobjtype" +
540
- " WHEN 'r' THEN 'table'" +
541
- " WHEN 'S' THEN 'sequence'" +
542
- " WHEN 'f' THEN 'function'" +
543
- " WHEN 'T' THEN 'type'" +
544
- " WHEN 'n' THEN 'schema'";
545
- if (serverAtLeast(serverVersion, PG_18)) {
546
- sql += " WHEN 'L' THEN 'large object'";
547
- }
548
- sql += ' END AS "Type",\n ';
549
- sql += aclColumn("d.defaclacl");
550
- sql +=
551
- "\nFROM pg_catalog.pg_default_acl d\n" +
552
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n";
553
- sql += patternStub(false, "n.nspname", "pg_catalog.pg_get_userbyid(d.defaclrole)");
554
- sql += orderBy("1, 2, 3");
555
- return {
556
- sql,
557
- params: params(),
558
- description: "Default access privileges",
559
- };
560
- };
561
- /* ------------------------------------------------------------------ */
562
- /* \dd — objectDescription */
563
- /* ------------------------------------------------------------------ */
564
- export const objectDescription = (opts) => {
565
- const sysPart = (objLabel) => !opts.showSystem && opts.pattern === undefined
566
- ? `WHERE n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema'` +
567
- ` /* ${objLabel} */ `
568
- : `WHERE true /* ${objLabel} */ `;
569
- let sql = 'SELECT DISTINCT tt.nspname AS "Schema", tt.name AS "Name", tt.object AS "Object", d.description AS "Description"\n' +
570
- "FROM (\n";
571
- sql +=
572
- " SELECT pgc.oid as oid, pgc.tableoid AS tableoid, n.nspname as nspname,\n" +
573
- " CAST(pgc.conname AS pg_catalog.text) as name," +
574
- " CAST('table constraint' AS pg_catalog.text) as object\n" +
575
- " FROM pg_catalog.pg_constraint pgc\n" +
576
- " JOIN pg_catalog.pg_class c ON c.oid = pgc.conrelid\n" +
577
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" +
578
- sysPart("table constraint") +
579
- "\n";
580
- sql +=
581
- "UNION ALL\n" +
582
- " SELECT pgc.oid, pgc.tableoid, n.nspname,\n" +
583
- " CAST(pgc.conname AS pg_catalog.text)," +
584
- " CAST('domain constraint' AS pg_catalog.text)\n" +
585
- " FROM pg_catalog.pg_constraint pgc\n" +
586
- " JOIN pg_catalog.pg_type t ON t.oid = pgc.contypid\n" +
587
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n" +
588
- sysPart("domain constraint") +
589
- "\n";
590
- sql +=
591
- "UNION ALL\n" +
592
- " SELECT o.oid, o.tableoid, n.nspname,\n" +
593
- " CAST(o.opcname AS pg_catalog.text)," +
594
- " CAST('operator class' AS pg_catalog.text)\n" +
595
- " FROM pg_catalog.pg_opclass o\n" +
596
- " JOIN pg_catalog.pg_am am ON o.opcmethod = am.oid\n" +
597
- " JOIN pg_catalog.pg_namespace n ON n.oid = o.opcnamespace\n" +
598
- sysPart("operator class") +
599
- "\n";
600
- sql +=
601
- "UNION ALL\n" +
602
- " SELECT opf.oid, opf.tableoid, n.nspname,\n" +
603
- " CAST(opf.opfname AS pg_catalog.text)," +
604
- " CAST('operator family' AS pg_catalog.text)\n" +
605
- " FROM pg_catalog.pg_opfamily opf\n" +
606
- " JOIN pg_catalog.pg_am am ON opf.opfmethod = am.oid\n" +
607
- " JOIN pg_catalog.pg_namespace n ON opf.opfnamespace = n.oid\n" +
608
- sysPart("operator family") +
609
- "\n";
610
- sql +=
611
- "UNION ALL\n" +
612
- " SELECT r.oid, r.tableoid, n.nspname,\n" +
613
- " CAST(r.rulename AS pg_catalog.text)," +
614
- " CAST('rule' AS pg_catalog.text)\n" +
615
- " FROM pg_catalog.pg_rewrite r\n" +
616
- " JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n" +
617
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" +
618
- " WHERE r.rulename != '_RETURN'\n";
619
- sql +=
620
- "UNION ALL\n" +
621
- " SELECT t.oid, t.tableoid, n.nspname,\n" +
622
- " CAST(t.tgname AS pg_catalog.text)," +
623
- " CAST('trigger' AS pg_catalog.text)\n" +
624
- " FROM pg_catalog.pg_trigger t\n" +
625
- " JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n" +
626
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" +
627
- sysPart("trigger") +
628
- "\n";
629
- sql +=
630
- ") AS tt\n" +
631
- " JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n";
632
- sql += orderBy("1, 2, 3");
633
- return { sql, params: params(), description: "Object descriptions" };
634
- };
635
- /* ------------------------------------------------------------------ */
636
- /* \d (no args / \d <name>) — describeTableDetails */
637
- /* This WP delivers ONLY the lookup query; the per-relation detail */
638
- /* render is WP-20. */
639
- /* ------------------------------------------------------------------ */
640
- export const describeTableDetails = (opts) => {
641
- const { verbose, showSystem, pattern, serverVersion } = opts;
642
- const hideTableam = opts.hideTableam ?? false;
643
- // For bare `\d [pattern]` upstream renders the same relations table as
644
- // `\dtvmsiE` does, with the per-relkind type column populated. Include
645
- // the verbose-mode columns when caller asks for them so `\d+ [pat]`
646
- // matches `\dt+ [pat]` shape.
647
- let sql = 'SELECT n.nspname as "Schema",\n' +
648
- ' c.relname as "Name",\n' +
649
- " CASE c.relkind" +
650
- " WHEN 'r' THEN 'table'" +
651
- " WHEN 'v' THEN 'view'" +
652
- " WHEN 'm' THEN 'materialized view'" +
653
- " WHEN 'i' THEN 'index'" +
654
- " WHEN 'S' THEN 'sequence'" +
655
- " WHEN 't' THEN 'TOAST table'" +
656
- " WHEN 'f' THEN 'foreign table'" +
657
- " WHEN 'p' THEN 'partitioned table'" +
658
- " WHEN 'I' THEN 'partitioned index'" +
659
- ' END as "Type",\n' +
660
- ' pg_catalog.pg_get_userbyid(c.relowner) as "Owner"';
661
- if (verbose) {
662
- sql +=
663
- ",\n CASE c.relpersistence" +
664
- " WHEN 'p' THEN 'permanent'" +
665
- " WHEN 't' THEN 'temporary'" +
666
- " WHEN 'u' THEN 'unlogged'" +
667
- ' END as "Persistence"';
668
- if (!hideTableam && serverAtLeast(serverVersion, PG_12)) {
669
- sql += ',\n am.amname as "Access method"';
670
- }
671
- sql +=
672
- ',\n pg_catalog.pg_size_pretty(pg_catalog.pg_total_relation_size(c.oid)) as "Size"' +
673
- ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"Description\"";
674
- }
675
- sql +=
676
- "\nFROM pg_catalog.pg_class c" +
677
- "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace";
678
- if (verbose && !hideTableam && serverAtLeast(serverVersion, PG_12)) {
679
- sql += "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam";
680
- }
681
- sql += "\nWHERE c.relkind IN ('r','p','v','m','S','f','')\n";
682
- if (!showSystem && pattern === undefined) {
683
- sql +=
684
- " AND n.nspname <> 'pg_catalog'\n" +
685
- " AND n.nspname !~ '^pg_toast'\n" +
686
- " AND n.nspname <> 'information_schema'\n";
687
- }
688
- sql += patternStub(true, "n.nspname", "c.relname");
689
- sql += orderBy("1,2");
690
- return {
691
- sql,
692
- params: params(),
693
- description: "List of relations",
694
- };
695
- };
696
- /* ------------------------------------------------------------------ */
697
- /* \du / \dg — describeRoles */
698
- /* ------------------------------------------------------------------ */
699
- /**
700
- * Upstream `describeRoles` (psql/describe.c) fetches the raw boolean
701
- * columns then builds the cooked "Attributes" cell client-side from
702
- * fixed per-flag strings. We do that aggregation in SQL via a
703
- * NULL-skipping array_to_string so the printer sees only the final
704
- * two visible columns ("Role name", "Attributes") plus an optional
705
- * "Description" column when `+` is used. This matches the column
706
- * layout, header banner ("List of roles"), and content ordering of
707
- * vanilla psql 18, including the `Cannot login`-only rows produced by
708
- * `\du regress_du_role*` in `src/test/regress/expected/psql.out`.
709
- */
710
- export const describeRoles = (opts) => {
711
- const { verbose, showSystem, pattern, serverVersion } = opts;
712
- // Per-flag attribute strings, in upstream order. Each VALUES row
713
- // contributes one string (or NULL when the flag is off) so
714
- // array_to_string can join them with ", ".
715
- const attrParts = [
716
- "CASE WHEN r.rolsuper THEN 'Superuser' END",
717
- "CASE WHEN NOT r.rolinherit THEN 'No inheritance' END",
718
- "CASE WHEN r.rolcreaterole THEN 'Create role' END",
719
- "CASE WHEN r.rolcreatedb THEN 'Create DB' END",
720
- "CASE WHEN NOT r.rolcanlogin THEN 'Cannot login' END",
721
- "CASE WHEN r.rolreplication THEN 'Replication' END",
722
- ];
723
- if (serverAtLeast(serverVersion, PG_9_5)) {
724
- attrParts.push("CASE WHEN r.rolbypassrls THEN 'Bypass RLS' END");
725
- }
726
- // `rolconnlimit = -1` means unlimited; only render when >= 0.
727
- attrParts.push("CASE WHEN r.rolconnlimit >= 0 THEN" +
728
- " pg_catalog.format('%s connection%s', r.rolconnlimit," +
729
- " CASE WHEN r.rolconnlimit = 1 THEN '' ELSE 's' END) END");
730
- // `rolvaliduntil` rendered with upstream's `Password valid until <ts>`.
731
- attrParts.push("CASE WHEN r.rolvaliduntil IS NOT NULL THEN" +
732
- " pg_catalog.format('Password valid until %s', r.rolvaliduntil) END");
733
- const attrValues = attrParts.map((p) => `(${p})`).join(",\n ");
734
- let sql = 'SELECT r.rolname AS "Role name",\n' +
735
- " pg_catalog.array_to_string(ARRAY(\n" +
736
- " SELECT a FROM (VALUES\n" +
737
- ` ${attrValues}\n` +
738
- " ) AS x(a)\n" +
739
- " WHERE a IS NOT NULL\n" +
740
- " ), ', ') AS \"Attributes\"";
741
- if (verbose) {
742
- sql +=
743
- ",\n pg_catalog.shobj_description(r.oid, 'pg_authid') AS \"Description\"";
744
- }
745
- sql += "\nFROM pg_catalog.pg_roles r\n";
746
- let hasWhere = false;
747
- if (!showSystem && pattern === undefined) {
748
- sql += "WHERE r.rolname !~ '^pg_'\n";
749
- hasWhere = true;
750
- }
751
- sql += patternStub(hasWhere, undefined, "r.rolname");
752
- sql += orderBy("1");
753
- return { sql, params: params(), description: "List of roles" };
754
- };
755
- export const listDbRoleSettings = (opts) => {
756
- let sql = 'SELECT rolname AS "Role", datname AS "Database",\n' +
757
- "pg_catalog.array_to_string(setconfig, E'\\n') AS \"Settings\"\n" +
758
- "FROM pg_catalog.pg_db_role_setting s\n" +
759
- "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n" +
760
- "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n";
761
- let hasWhere = false;
762
- if (opts.pattern !== undefined) {
763
- sql += patternStub(hasWhere, undefined, "r.rolname");
764
- hasWhere = true;
765
- }
766
- if (opts.pattern2 !== undefined) {
767
- sql += patternStub(hasWhere, undefined, "d.datname");
768
- }
769
- sql += orderBy("1, 2");
770
- const ps = [];
771
- if (opts.pattern !== undefined)
772
- ps.push(opts.pattern);
773
- if (opts.pattern2 !== undefined)
774
- ps.push(opts.pattern2);
775
- return { sql, params: ps, description: "List of settings" };
776
- };
777
- /* ------------------------------------------------------------------ */
778
- /* \drg — describeRoleGrants */
779
- /* ------------------------------------------------------------------ */
780
- export const describeRoleGrants = (opts) => {
781
- const { showSystem, pattern, serverVersion } = opts;
782
- let sql = 'SELECT m.rolname AS "Role name", r.rolname AS "Member of",\n' +
783
- " pg_catalog.concat_ws(', ',\n";
784
- if (serverAtLeast(serverVersion, PG_16)) {
785
- sql +=
786
- " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n" +
787
- " CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n" +
788
- " CASE WHEN pam.set_option THEN 'SET' END\n";
789
- }
790
- else {
791
- sql +=
792
- " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n" +
793
- " CASE WHEN m.rolinherit THEN 'INHERIT' END,\n" +
794
- " 'SET'\n";
795
- }
796
- sql += ' ) AS "Options",\n g.rolname AS "Grantor"\n';
797
- sql +=
798
- "FROM pg_catalog.pg_roles m\n" +
799
- " JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n" +
800
- " LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n" +
801
- " LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n";
802
- let hasWhere = false;
803
- if (!showSystem && pattern === undefined) {
804
- sql += "WHERE m.rolname !~ '^pg_'\n";
805
- hasWhere = true;
806
- }
807
- sql += patternStub(hasWhere, undefined, "m.rolname");
808
- sql += orderBy("1, 2, 4");
809
- return { sql, params: params(), description: "List of role grants" };
810
- };
811
- export const listTables = (opts) => {
812
- const { verbose, showSystem, pattern, serverVersion } = opts;
813
- const hideTableam = opts.hideTableam ?? false;
814
- const tt = opts.tabtypes ?? "";
815
- let showTables = tt.includes("t");
816
- const showIndexes = tt.includes("i");
817
- let showViews = tt.includes("v");
818
- let showMatViews = tt.includes("m");
819
- let showSeq = tt.includes("s");
820
- let showForeign = tt.includes("E");
821
- const ntypes = +showTables +
822
- +showIndexes +
823
- +showViews +
824
- +showMatViews +
825
- +showSeq +
826
- +showForeign;
827
- if (ntypes === 0) {
828
- showTables = showViews = showMatViews = showSeq = showForeign = true;
829
- }
830
- let sql = 'SELECT n.nspname as "Schema",\n' +
831
- ' c.relname as "Name",\n' +
832
- " CASE c.relkind" +
833
- " WHEN 'r' THEN 'table'" +
834
- " WHEN 'v' THEN 'view'" +
835
- " WHEN 'm' THEN 'materialized view'" +
836
- " WHEN 'i' THEN 'index'" +
837
- " WHEN 'S' THEN 'sequence'" +
838
- " WHEN 't' THEN 'TOAST table'" +
839
- " WHEN 'f' THEN 'foreign table'" +
840
- " WHEN 'p' THEN 'partitioned table'" +
841
- " WHEN 'I' THEN 'partitioned index'" +
842
- ' END as "Type",\n' +
843
- ' pg_catalog.pg_get_userbyid(c.relowner) as "Owner"';
844
- if (showIndexes)
845
- sql += ',\n c2.relname as "Table"';
846
- if (verbose) {
847
- sql +=
848
- ",\n CASE c.relpersistence" +
849
- " WHEN 'p' THEN 'permanent'" +
850
- " WHEN 't' THEN 'temporary'" +
851
- " WHEN 'u' THEN 'unlogged'" +
852
- ' END as "Persistence"';
853
- const wantsAm = !hideTableam &&
854
- serverAtLeast(serverVersion, PG_12) &&
855
- (showTables || showMatViews || showIndexes);
856
- if (wantsAm)
857
- sql += ',\n am.amname as "Access method"';
858
- // Upstream uses `pg_total_relation_size` (table + indexes + toast)
859
- // in verbose mode so the formatting matches `pg_size_pretty` exactly
860
- // — including the "8192 bytes" vs "8 kB" threshold the upstream
861
- // tests pin to.
862
- sql +=
863
- ',\n pg_catalog.pg_size_pretty(pg_catalog.pg_total_relation_size(c.oid)) as "Size"' +
864
- ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"Description\"";
865
- }
866
- sql +=
867
- "\nFROM pg_catalog.pg_class c" +
868
- "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace";
869
- if (verbose &&
870
- !hideTableam &&
871
- serverAtLeast(serverVersion, PG_12) &&
872
- (showTables || showMatViews || showIndexes)) {
873
- sql += "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam";
874
- }
875
- if (showIndexes) {
876
- sql +=
877
- "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid" +
878
- "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid";
879
- }
880
- sql += "\nWHERE c.relkind IN (";
881
- const kinds = [];
882
- if (showTables) {
883
- kinds.push("'r'", "'p'");
884
- if (showSystem || pattern !== undefined)
885
- kinds.push("'t'");
886
- }
887
- if (showViews)
888
- kinds.push("'v'");
889
- if (showMatViews)
890
- kinds.push("'m'");
891
- if (showIndexes)
892
- kinds.push("'i'", "'I'");
893
- if (showSeq)
894
- kinds.push("'S'");
895
- if (showSystem || pattern !== undefined)
896
- kinds.push("'s'");
897
- if (showForeign)
898
- kinds.push("'f'");
899
- kinds.push("''");
900
- sql += kinds.join(",") + ")\n";
901
- if (!showSystem && pattern === undefined) {
902
- sql +=
903
- " AND n.nspname <> 'pg_catalog'\n" +
904
- " AND n.nspname !~ '^pg_toast'\n" +
905
- " AND n.nspname <> 'information_schema'\n";
906
- }
907
- sql += patternStub(true, "n.nspname", "c.relname");
908
- sql += orderBy("1,2");
909
- // Upstream `describe.c::listTables` uses a type-specific title when the
910
- // caller filters to a single relkind family, falling back to "relations"
911
- // for mixed (or default) selections. Mirror that so `\dt` / `\dv` /
912
- // `\dm` / `\di` / `\ds` / `\dE` titles match.
913
- let description = "List of relations";
914
- if (ntypes === 1) {
915
- if (showTables)
916
- description = "List of tables";
917
- else if (showIndexes)
918
- description = "List of indexes";
919
- else if (showViews)
920
- description = "List of views";
921
- else if (showMatViews)
922
- description = "List of materialized views";
923
- else if (showSeq)
924
- description = "List of sequences";
925
- else if (showForeign)
926
- description = "List of foreign tables";
927
- }
928
- return { sql, params: params(), description };
929
- };
930
- export const listPartitionedTables = (opts) => {
931
- const { verbose, pattern, serverVersion } = opts;
932
- if (serverLess(serverVersion, PG_10)) {
933
- return {
934
- sql: "/* server < 10 does not support declarative partitioning */ SELECT 1 WHERE false;",
935
- params: [],
936
- description: "List of partitioned relations",
937
- };
938
- }
939
- const rt = opts.reltypes ?? "";
940
- let showTables = rt.includes("t");
941
- let showIndexes = rt.includes("i");
942
- const showNested = rt.includes("n");
943
- if (!showTables && !showIndexes)
944
- showTables = showIndexes = true;
945
- const mixed = showTables && showIndexes;
946
- let sql = 'SELECT n.nspname as "Schema",\n' +
947
- ' c.relname as "Name",\n' +
948
- ' pg_catalog.pg_get_userbyid(c.relowner) as "Owner"';
949
- if (mixed) {
950
- sql +=
951
- ",\n CASE c.relkind" +
952
- " WHEN 'p' THEN 'partitioned table'" +
953
- " WHEN 'I' THEN 'partitioned index'" +
954
- ' END as "Type"';
955
- }
956
- if (showNested || pattern !== undefined) {
957
- sql += ',\n inh.inhparent::pg_catalog.regclass as "Parent name"';
958
- }
959
- if (showIndexes) {
960
- sql += ',\n c2.oid::pg_catalog.regclass as "Table"';
961
- }
962
- if (verbose) {
963
- sql += ',\n am.amname as "Access method"';
964
- if (showNested) {
965
- sql += ',\n s.dps as "Leaf partition size"';
966
- sql += ',\n s.tps as "Total size"';
967
- }
968
- else {
969
- sql += ',\n s.tps as "Total size"';
970
- }
971
- sql +=
972
- ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"Description\"";
973
- }
974
- sql +=
975
- "\nFROM pg_catalog.pg_class c" +
976
- "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace";
977
- if (showIndexes) {
978
- sql +=
979
- "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid" +
980
- "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid";
981
- }
982
- if (showNested || pattern !== undefined) {
983
- sql +=
984
- "\n LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid";
985
- }
986
- if (verbose) {
987
- sql += "\n LEFT JOIN pg_catalog.pg_am am ON c.relam = am.oid";
988
- if (serverLess(serverVersion, PG_12)) {
989
- sql +=
990
- ",\n LATERAL (WITH RECURSIVE d AS (\n" +
991
- " SELECT inhrelid AS oid, 1 AS level FROM pg_catalog.pg_inherits WHERE inhparent = c.oid\n" +
992
- " UNION ALL\n" +
993
- " SELECT inhrelid, level + 1 FROM pg_catalog.pg_inherits i JOIN d ON i.inhparent = d.oid)\n" +
994
- " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size(d.oid))) AS tps,\n" +
995
- " pg_catalog.pg_size_pretty(sum(CASE WHEN d.level = 1 THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n" +
996
- " FROM d) s";
997
- }
998
- else {
999
- sql +=
1000
- ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum(\n" +
1001
- " CASE WHEN ppt.isleaf AND ppt.level = 1\n" +
1002
- " THEN pg_catalog.pg_table_size(ppt.relid) ELSE 0 END)) AS dps,\n" +
1003
- " pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size(ppt.relid))) AS tps\n" +
1004
- " FROM pg_catalog.pg_partition_tree(c.oid) ppt) s";
1005
- }
1006
- }
1007
- sql += "\nWHERE c.relkind IN (";
1008
- const kinds = [];
1009
- if (showTables)
1010
- kinds.push("'p'");
1011
- if (showIndexes)
1012
- kinds.push("'I'");
1013
- kinds.push("''");
1014
- sql += kinds.join(",") + ")\n";
1015
- if (!showNested && pattern === undefined) {
1016
- sql += " AND NOT c.relispartition\n";
1017
- }
1018
- if (pattern === undefined) {
1019
- sql +=
1020
- " AND n.nspname <> 'pg_catalog'\n" +
1021
- " AND n.nspname !~ '^pg_toast'\n" +
1022
- " AND n.nspname <> 'information_schema'\n";
1023
- }
1024
- sql += patternStub(true, "n.nspname", "c.relname");
1025
- sql += `ORDER BY "Schema", ${mixed ? '"Type" DESC, ' : ""}${showNested || pattern !== undefined ? '"Parent name" NULLS FIRST, ' : ""}"Name";`;
1026
- // Mirror upstream's `tabletitle` switch: `\dPi*` lists indexes,
1027
- // `\dPt*` lists tables, and any mixed/empty form lists relations.
1028
- const description = showIndexes && !showTables
1029
- ? "List of partitioned indexes"
1030
- : showTables && !showIndexes
1031
- ? "List of partitioned tables"
1032
- : "List of partitioned relations";
1033
- return {
1034
- sql,
1035
- params: params(),
1036
- description,
1037
- };
1038
- };
1039
- /* ------------------------------------------------------------------ */
1040
- /* \dL — listLanguages */
1041
- /* ------------------------------------------------------------------ */
1042
- export const listLanguages = (opts) => {
1043
- const { verbose, showSystem, pattern } = opts;
1044
- let sql = 'SELECT l.lanname AS "Name",\n' +
1045
- ' pg_catalog.pg_get_userbyid(l.lanowner) as "Owner",\n' +
1046
- ' l.lanpltrusted AS "Trusted"';
1047
- if (verbose) {
1048
- sql +=
1049
- ',\n NOT l.lanispl AS "Internal language",\n' +
1050
- ' l.lanplcallfoid::pg_catalog.regprocedure AS "Call handler",\n' +
1051
- ' l.lanvalidator::pg_catalog.regprocedure AS "Validator",\n ' +
1052
- 'l.laninline::pg_catalog.regprocedure AS "Inline handler",\n ' +
1053
- aclColumn("l.lanacl");
1054
- }
1055
- sql +=
1056
- ',\n d.description AS "Description"\n' +
1057
- "FROM pg_catalog.pg_language l\n" +
1058
- "LEFT JOIN pg_catalog.pg_description d\n" +
1059
- " ON d.classoid = l.tableoid AND d.objoid = l.oid AND d.objsubid = 0\n";
1060
- let hasWhere = false;
1061
- if (pattern !== undefined) {
1062
- sql += patternStub(hasWhere, undefined, "l.lanname");
1063
- hasWhere = true;
1064
- }
1065
- if (!showSystem && pattern === undefined) {
1066
- sql += "WHERE l.lanplcallfoid != 0\n";
1067
- hasWhere = true;
1068
- }
1069
- sql += orderBy("1");
1070
- return { sql, params: params(), description: "List of languages" };
1071
- };
1072
- /* ------------------------------------------------------------------ */
1073
- /* \dD — listDomains */
1074
- /* ------------------------------------------------------------------ */
1075
- export const listDomains = (opts) => {
1076
- const { verbose, showSystem, pattern } = opts;
1077
- let sql = 'SELECT n.nspname as "Schema",\n' +
1078
- ' t.typname as "Name",\n' +
1079
- ' pg_catalog.format_type(t.typbasetype, t.typtypmod) as "Type",\n' +
1080
- " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n" +
1081
- ' WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as "Collation",\n' +
1082
- " CASE WHEN t.typnotnull THEN 'not null' END as \"Nullable\",\n" +
1083
- ' t.typdefault as "Default",\n' +
1084
- " pg_catalog.array_to_string(ARRAY(\n SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid AND r.contype = 'c' ORDER BY r.conname\n ), ' ') as \"Check\"";
1085
- if (verbose) {
1086
- sql += ",\n " + aclColumn("t.typacl");
1087
- sql += ',\n d.description as "Description"';
1088
- }
1089
- sql +=
1090
- "\nFROM pg_catalog.pg_type t\n" +
1091
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n";
1092
- if (verbose) {
1093
- sql +=
1094
- " LEFT JOIN pg_catalog.pg_description d ON d.classoid = t.tableoid AND d.objoid = t.oid AND d.objsubid = 0\n";
1095
- }
1096
- sql += "WHERE t.typtype = 'd'\n";
1097
- if (!showSystem && pattern === undefined) {
1098
- sql +=
1099
- " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
1100
- }
1101
- sql += patternStub(true, "n.nspname", "t.typname");
1102
- sql += orderBy("1, 2");
1103
- return { sql, params: params(), description: "List of domains" };
1104
- };
1105
- /* ------------------------------------------------------------------ */
1106
- /* \dc — listConversions */
1107
- /* ------------------------------------------------------------------ */
1108
- export const listConversions = (opts) => {
1109
- const { verbose, showSystem, pattern } = opts;
1110
- let sql = 'SELECT n.nspname AS "Schema",\n' +
1111
- ' c.conname AS "Name",\n' +
1112
- ' pg_catalog.pg_encoding_to_char(c.conforencoding) AS "Source",\n' +
1113
- ' pg_catalog.pg_encoding_to_char(c.contoencoding) AS "Destination",\n' +
1114
- " CASE WHEN c.condefault THEN 'yes' ELSE 'no' END AS \"Default?\"";
1115
- if (verbose)
1116
- sql += ',\n d.description AS "Description"';
1117
- sql +=
1118
- "\nFROM pg_catalog.pg_conversion c\n" +
1119
- " JOIN pg_catalog.pg_namespace n ON n.oid = c.connamespace\n";
1120
- if (verbose) {
1121
- sql +=
1122
- "LEFT JOIN pg_catalog.pg_description d ON d.classoid = c.tableoid AND d.objoid = c.oid AND d.objsubid = 0\n";
1123
- }
1124
- sql += "WHERE true\n";
1125
- if (!showSystem && pattern === undefined) {
1126
- sql +=
1127
- " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
1128
- }
1129
- sql += patternStub(true, "n.nspname", "c.conname");
1130
- sql += orderBy("1, 2");
1131
- return { sql, params: params(), description: "List of conversions" };
1132
- };
1133
- /* ------------------------------------------------------------------ */
1134
- /* \dconfig — describeConfigurationParameters */
1135
- /* ------------------------------------------------------------------ */
1136
- export const describeConfigurationParameters = (opts) => {
1137
- const { verbose, pattern, serverVersion } = opts;
1138
- let sql = 'SELECT s.name AS "Parameter", pg_catalog.current_setting(s.name) AS "Value"';
1139
- if (verbose) {
1140
- sql += ', s.vartype AS "Type", s.context AS "Context", ';
1141
- if (serverAtLeast(serverVersion, PG_15)) {
1142
- sql += aclColumn("p.paracl");
1143
- }
1144
- else {
1145
- sql += 'NULL AS "Access privileges"';
1146
- }
1147
- }
1148
- sql += "\nFROM pg_catalog.pg_settings s\n";
1149
- if (verbose && serverAtLeast(serverVersion, PG_15)) {
1150
- sql +=
1151
- " LEFT JOIN pg_catalog.pg_parameter_acl p ON pg_catalog.lower(s.name) = p.parname\n";
1152
- }
1153
- if (pattern !== undefined) {
1154
- sql += patternStub(false, undefined, "pg_catalog.lower(s.name)");
1155
- }
1156
- else {
1157
- sql +=
1158
- "WHERE s.source <> 'default' AND s.setting IS DISTINCT FROM s.boot_val\n";
1159
- }
1160
- sql += orderBy("1");
1161
- return {
1162
- sql,
1163
- params: params(),
1164
- description: pattern !== undefined
1165
- ? "List of configuration parameters"
1166
- : "List of non-default configuration parameters",
1167
- };
1168
- };
1169
- /* ------------------------------------------------------------------ */
1170
- /* \dy — listEventTriggers */
1171
- /* ------------------------------------------------------------------ */
1172
- export const listEventTriggers = (opts) => {
1173
- const { verbose } = opts;
1174
- let sql = 'SELECT evtname as "Name", evtevent as "Event", pg_catalog.pg_get_userbyid(e.evtowner) as "Owner",\n' +
1175
- " case evtenabled when 'O' then 'enabled' when 'R' then 'replica' when 'A' then 'always' when 'D' then 'disabled' end as \"Enabled\",\n" +
1176
- ' e.evtfoid::pg_catalog.regproc as "Function", ' +
1177
- "pg_catalog.array_to_string(array(select x from pg_catalog.unnest(evttags) as t(x)), ', ') as \"Tags\"";
1178
- if (verbose) {
1179
- sql +=
1180
- ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"Description\"";
1181
- }
1182
- sql += "\nFROM pg_catalog.pg_event_trigger e\n";
1183
- sql += patternStub(false, undefined, "evtname");
1184
- sql += orderBy("1");
1185
- return { sql, params: params(), description: "List of event triggers" };
1186
- };
1187
- /* ------------------------------------------------------------------ */
1188
- /* \dX — listExtendedStats */
1189
- /* ------------------------------------------------------------------ */
1190
- export const listExtendedStats = (opts) => {
1191
- const { verbose, serverVersion } = opts;
1192
- if (serverLess(serverVersion, PG_10)) {
1193
- return {
1194
- sql: "/* server < 10 does not support extended statistics */ SELECT 1 WHERE false;",
1195
- params: [],
1196
- description: "List of extended statistics",
1197
- };
1198
- }
1199
- let sql = "SELECT\n" +
1200
- 'es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS "Schema",\n' +
1201
- 'es.stxname AS "Name",\n';
1202
- if (serverAtLeast(serverVersion, PG_14)) {
1203
- sql +=
1204
- "pg_catalog.format('%s FROM %s', pg_catalog.pg_get_statisticsobjdef_columns(es.oid), es.stxrelid::pg_catalog.regclass) AS \"Definition\"";
1205
- }
1206
- else {
1207
- sql +=
1208
- "pg_catalog.format('%s FROM %s',\n" +
1209
- " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ')\n" +
1210
- " FROM pg_catalog.unnest(es.stxkeys) s(attnum)\n" +
1211
- " JOIN pg_catalog.pg_attribute a\n" +
1212
- " ON (es.stxrelid = a.attrelid AND a.attnum = s.attnum AND NOT a.attisdropped)),\n" +
1213
- 'es.stxrelid::pg_catalog.regclass) AS "Definition"';
1214
- }
1215
- sql +=
1216
- ",\nCASE WHEN 'd' = any(es.stxkind) THEN 'defined' END AS \"Ndistinct\",\n" +
1217
- "CASE WHEN 'f' = any(es.stxkind) THEN 'defined' END AS \"Dependencies\"";
1218
- if (serverAtLeast(serverVersion, PG_12)) {
1219
- sql +=
1220
- ",\nCASE WHEN 'm' = any(es.stxkind) THEN 'defined' END AS \"MCV\"";
1221
- }
1222
- if (verbose) {
1223
- sql +=
1224
- ", \npg_catalog.obj_description(oid, 'pg_statistic_ext') AS \"Description\"";
1225
- }
1226
- sql += "\nFROM pg_catalog.pg_statistic_ext es\n";
1227
- sql += patternStub(false, "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname");
1228
- sql += orderBy("1, 2");
1229
- return {
1230
- sql,
1231
- params: params(),
1232
- description: "List of extended statistics",
1233
- };
1234
- };
1235
- /* ------------------------------------------------------------------ */
1236
- /* \dC — listCasts */
1237
- /* ------------------------------------------------------------------ */
1238
- export const listCasts = (opts) => {
1239
- const { verbose, serverVersion } = opts;
1240
- let sql = 'SELECT pg_catalog.format_type(castsource, NULL) AS "Source type",\n' +
1241
- ' pg_catalog.format_type(casttarget, NULL) AS "Target type",\n' +
1242
- " CASE WHEN c.castmethod = 'b' THEN '(binary coercible)'\n" +
1243
- " WHEN c.castmethod = 'i' THEN '(with inout)'\n" +
1244
- " ELSE p.proname\n" +
1245
- ' END AS "Function",\n' +
1246
- " CASE WHEN c.castcontext = 'e' THEN 'no'\n" +
1247
- " WHEN c.castcontext = 'a' THEN 'in assignment'\n" +
1248
- " ELSE 'yes'\n" +
1249
- ' END AS "Implicit?"';
1250
- if (verbose) {
1251
- // PG 18 added "Leakproof?" to verbose `\dC+`. The pg_proc join
1252
- // already exists for the Function column, so emitting the cast's
1253
- // function's `proleakproof` is incremental.
1254
- if (serverAtLeast(serverVersion, PG_18)) {
1255
- sql +=
1256
- ",\n CASE WHEN p.proleakproof THEN 'yes' ELSE 'no' END AS \"Leakproof?\"";
1257
- }
1258
- sql += ',\n d.description AS "Description"';
1259
- }
1260
- sql +=
1261
- "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n" +
1262
- " ON c.castfunc = p.oid\n" +
1263
- " LEFT JOIN pg_catalog.pg_type ts ON c.castsource = ts.oid\n" +
1264
- " LEFT JOIN pg_catalog.pg_namespace ns ON ns.oid = ts.typnamespace\n" +
1265
- " LEFT JOIN pg_catalog.pg_type tt ON c.casttarget = tt.oid\n" +
1266
- " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = tt.typnamespace\n";
1267
- if (verbose) {
1268
- sql +=
1269
- " LEFT JOIN pg_catalog.pg_description d ON d.classoid = c.tableoid AND d.objoid = c.oid AND d.objsubid = 0\n";
1270
- }
1271
- sql += "WHERE ( (true";
1272
- sql += patternStub(true, "ns.nspname", "ts.typname");
1273
- sql += ") OR (true";
1274
- sql += patternStub(true, "nt.nspname", "tt.typname");
1275
- sql += ") )\n";
1276
- sql += orderBy("1, 2");
1277
- return { sql, params: params(), description: "List of casts" };
1278
- };
1279
- /* ------------------------------------------------------------------ */
1280
- /* \dO — listCollations */
1281
- /* ------------------------------------------------------------------ */
1282
- export const listCollations = (opts) => {
1283
- const { verbose, showSystem, pattern, serverVersion } = opts;
1284
- let sql = 'SELECT\n n.nspname AS "Schema",\n c.collname AS "Name",\n';
1285
- if (serverAtLeast(serverVersion, PG_10)) {
1286
- sql +=
1287
- " CASE c.collprovider" +
1288
- " WHEN 'd' THEN 'default'" +
1289
- " WHEN 'b' THEN 'builtin'" +
1290
- " WHEN 'c' THEN 'libc'" +
1291
- " WHEN 'i' THEN 'icu'" +
1292
- ' END AS "Provider",\n';
1293
- }
1294
- else {
1295
- sql += " 'libc' AS \"Provider\",\n";
1296
- }
1297
- sql += ' c.collcollate AS "Collate",\n c.collctype AS "Ctype",\n';
1298
- if (serverAtLeast(serverVersion, PG_17)) {
1299
- sql += ' c.colllocale AS "Locale",\n';
1300
- }
1301
- else if (serverAtLeast(serverVersion, PG_15)) {
1302
- sql += ' c.colliculocale AS "Locale",\n';
1303
- }
1304
- else {
1305
- sql += ' c.collcollate AS "Locale",\n';
1306
- }
1307
- if (serverAtLeast(serverVersion, PG_16)) {
1308
- sql += ' c.collicurules AS "ICU Rules",\n';
1309
- }
1310
- else {
1311
- sql += ' NULL AS "ICU Rules",\n';
1312
- }
1313
- if (serverAtLeast(serverVersion, PG_12)) {
1314
- sql +=
1315
- " CASE WHEN c.collisdeterministic THEN 'yes' ELSE 'no' END AS \"Deterministic?\"";
1316
- }
1317
- else {
1318
- sql += " 'yes' AS \"Deterministic?\"";
1319
- }
1320
- if (verbose) {
1321
- sql +=
1322
- ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"Description\"";
1323
- }
1324
- sql +=
1325
- "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n" +
1326
- "WHERE n.oid = c.collnamespace\n";
1327
- if (!showSystem && pattern === undefined) {
1328
- sql +=
1329
- " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
1330
- }
1331
- sql +=
1332
- " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n";
1333
- sql += patternStub(true, "n.nspname", "c.collname");
1334
- sql += orderBy("1, 2");
1335
- return { sql, params: params(), description: "List of collations" };
1336
- };
1337
- /* ------------------------------------------------------------------ */
1338
- /* \dn — listSchemas */
1339
- /* ------------------------------------------------------------------ */
1340
- export const listSchemas = (opts) => {
1341
- const { verbose, showSystem, pattern } = opts;
1342
- let sql = 'SELECT n.nspname AS "Name",\n' +
1343
- ' pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"';
1344
- if (verbose) {
1345
- sql += ",\n " + aclColumn("n.nspacl");
1346
- sql +=
1347
- ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"Description\"";
1348
- }
1349
- sql += "\nFROM pg_catalog.pg_namespace n\n";
1350
- if (!showSystem && pattern === undefined) {
1351
- sql +=
1352
- "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n";
1353
- }
1354
- sql += patternStub(!showSystem && pattern === undefined, undefined, "n.nspname");
1355
- sql += orderBy("1");
1356
- return { sql, params: params(), description: "List of schemas" };
1357
- };
1358
- /* ------------------------------------------------------------------ */
1359
- /* \dFp — listTSParsers */
1360
- /* ------------------------------------------------------------------ */
1361
- export const listTSParsers = (opts) => {
1362
- const { verbose } = opts;
1363
- if (verbose) {
1364
- // Verbose form: fetch (oid, schema, name) so the renderer can issue
1365
- // per-parser detail queries (see describeOneTSParser below). WP-20 will
1366
- // wire the iteration.
1367
- let sql = "SELECT p.oid,\n n.nspname,\n p.prsname\n" +
1368
- "FROM pg_catalog.pg_ts_parser p\n" +
1369
- "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n";
1370
- sql += patternStub(false, "n.nspname", "p.prsname");
1371
- sql += orderBy("1, 2");
1372
- return {
1373
- sql,
1374
- params: params(),
1375
- description: "List of text search parsers (verbose)",
1376
- };
1377
- }
1378
- let sql = 'SELECT\n n.nspname as "Schema",\n p.prsname as "Name",\n' +
1379
- " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"Description\"\n" +
1380
- "FROM pg_catalog.pg_ts_parser p\n" +
1381
- "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n";
1382
- sql += patternStub(false, "n.nspname", "p.prsname");
1383
- sql += orderBy("1, 2");
1384
- return {
1385
- sql,
1386
- params: params(),
1387
- description: "List of text search parsers",
1388
- };
31
+ const describeAggregates = (opts) => {
32
+ const { showSystem, pattern, serverVersion } = opts;
33
+ let sql = "";
34
+ sql += "SELECT n.nspname as \"Schema\",\n p.proname AS \"Name\",\n pg_catalog.format_type(p.prorettype, NULL) AS \"Result data type\",\n CASE WHEN p.pronargs = 0\n THEN CAST('*' AS pg_catalog.text)\n ELSE pg_catalog.pg_get_function_arguments(p.oid)\n END AS \"Argument data types\",\n pg_catalog.obj_description(p.oid, 'pg_proc') as \"Description\"\nFROM pg_catalog.pg_proc p\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n";
35
+ sql += serverAtLeast(serverVersion, 11e4) ? "WHERE p.prokind = 'a'\n" : "WHERE p.proisagg\n";
36
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
37
+ sql += patternStub(true, "n.nspname", "p.proname");
38
+ sql += orderBy("1, 2, 4");
39
+ return {
40
+ sql,
41
+ params: params(),
42
+ description: "List of aggregate functions"
43
+ };
44
+ };
45
+ const describeAccessMethods = (opts) => {
46
+ const { verbose, serverVersion } = opts;
47
+ if (serverLess(serverVersion, 9, 6)) return {
48
+ sql: "/* server < 9.6 does not support access methods */ SELECT 1 WHERE false;",
49
+ params: [],
50
+ description: "List of access methods"
51
+ };
52
+ let sql = "SELECT amname AS \"Name\",\n CASE amtype WHEN 'i' THEN 'Index' WHEN 't' THEN 'Table' END AS \"Type\"";
53
+ if (verbose) sql += ",\n amhandler AS \"Handler\",\n pg_catalog.obj_description(oid, 'pg_am') AS \"Description\"";
54
+ sql += "\nFROM pg_catalog.pg_am\n";
55
+ sql += patternStub(false, void 0, "amname");
56
+ sql += orderBy("1");
57
+ return {
58
+ sql,
59
+ params: params(),
60
+ description: "List of access methods"
61
+ };
62
+ };
63
+ const describeTablespaces = (opts) => {
64
+ const { verbose } = opts;
65
+ let sql = "SELECT spcname AS \"Name\",\n pg_catalog.pg_get_userbyid(spcowner) AS \"Owner\",\n pg_catalog.pg_tablespace_location(oid) AS \"Location\"";
66
+ if (verbose) {
67
+ sql += ",\n " + aclColumn("spcacl");
68
+ sql += ",\n spcoptions AS \"Options\",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"Size\",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"Description\"";
69
+ }
70
+ sql += "\nFROM pg_catalog.pg_tablespace\n";
71
+ sql += patternStub(false, void 0, "spcname");
72
+ sql += orderBy("1");
73
+ return {
74
+ sql,
75
+ params: params(),
76
+ description: "List of tablespaces"
77
+ };
78
+ };
79
+ const describeFunctions = (opts) => {
80
+ const { verbose, showSystem, pattern, serverVersion } = opts;
81
+ const functypes = opts.functypes ?? "";
82
+ const argPatterns = opts.argPatterns ?? [];
83
+ let showAgg = functypes.includes("a");
84
+ let showNorm = functypes.includes("n");
85
+ let showProc = functypes.includes("p");
86
+ let showTrig = functypes.includes("t");
87
+ let showWin = functypes.includes("w");
88
+ if (!showAgg && !showNorm && !showProc && !showTrig && !showWin) {
89
+ showAgg = showNorm = showTrig = showWin = true;
90
+ if (serverAtLeast(serverVersion, 11e4)) showProc = true;
91
+ }
92
+ let sql = "SELECT n.nspname as \"Schema\",\n p.proname as \"Name\",\n";
93
+ if (serverAtLeast(serverVersion, 11e4)) sql += " pg_catalog.pg_get_function_result(p.oid) as \"Result data type\",\n pg_catalog.pg_get_function_arguments(p.oid) as \"Argument data types\",\n CASE p.prokind\n WHEN 'a' THEN 'agg'\n WHEN 'w' THEN 'window'\n WHEN 'p' THEN 'proc'\n ELSE 'func'\n END as \"Type\"";
94
+ else sql += " pg_catalog.pg_get_function_result(p.oid) as \"Result data type\",\n pg_catalog.pg_get_function_arguments(p.oid) as \"Argument data types\",\n CASE\n WHEN p.proisagg THEN 'agg'\n WHEN p.proiswindow THEN 'window'\n WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger'\n ELSE 'func'\n END as \"Type\"";
95
+ if (verbose) {
96
+ sql += ",\n CASE\n WHEN p.provolatile = 'i' THEN 'immutable'\n WHEN p.provolatile = 's' THEN 'stable'\n WHEN p.provolatile = 'v' THEN 'volatile'\n END as \"Volatility\"";
97
+ if (serverAtLeast(serverVersion, 90600)) sql += ",\n CASE\n WHEN p.proparallel = 'r' THEN 'restricted'\n WHEN p.proparallel = 's' THEN 'safe'\n WHEN p.proparallel = 'u' THEN 'unsafe'\n END as \"Parallel\"";
98
+ sql += ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"Owner\",\n CASE WHEN prosecdef THEN 'definer' ELSE 'invoker' END AS \"Security\"";
99
+ if (serverAtLeast(serverVersion, 18e4)) sql += ",\n CASE WHEN p.proleakproof THEN 'yes' ELSE 'no' END as \"Leakproof?\"";
100
+ sql += ",\n " + aclColumn("p.proacl");
101
+ sql += ",\n l.lanname as \"Language\",\n CASE WHEN l.lanname IN ('internal', 'c') THEN p.prosrc END as \"Internal name\",\n pg_catalog.obj_description(p.oid, 'pg_proc') as \"Description\"";
102
+ }
103
+ sql += "\nFROM pg_catalog.pg_proc p\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n";
104
+ for (let i = 0; i < argPatterns.length; i++) sql += ` LEFT JOIN pg_catalog.pg_type t${i} ON t${i}.oid = p.proargtypes[${i}]\n LEFT JOIN pg_catalog.pg_namespace nt${i} ON nt${i}.oid = t${i}.typnamespace\n`;
105
+ if (verbose) sql += " LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n";
106
+ let hasWhere = false;
107
+ const allTypes = showAgg && showNorm && showProc && showTrig && showWin;
108
+ if (!allTypes && showNorm) {
109
+ if (!showAgg) {
110
+ sql += hasWhere ? " AND " : "WHERE ";
111
+ hasWhere = true;
112
+ sql += serverAtLeast(serverVersion, 11e4) ? "p.prokind <> 'a'\n" : "NOT p.proisagg\n";
113
+ }
114
+ if (!showProc && serverAtLeast(serverVersion, 11e4)) {
115
+ sql += hasWhere ? " AND " : "WHERE ";
116
+ hasWhere = true;
117
+ sql += "p.prokind <> 'p'\n";
118
+ }
119
+ if (!showTrig) {
120
+ sql += hasWhere ? " AND " : "WHERE ";
121
+ hasWhere = true;
122
+ sql += "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n";
123
+ }
124
+ if (!showWin) {
125
+ sql += hasWhere ? " AND " : "WHERE ";
126
+ hasWhere = true;
127
+ sql += serverAtLeast(serverVersion, 11e4) ? "p.prokind <> 'w'\n" : "NOT p.proiswindow\n";
128
+ }
129
+ } else if (!allTypes) {
130
+ sql += "WHERE (\n ";
131
+ hasWhere = true;
132
+ const parts = [];
133
+ if (showAgg) parts.push(serverAtLeast(serverVersion, 11e4) ? "p.prokind = 'a'" : "p.proisagg");
134
+ if (showTrig) parts.push("p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype");
135
+ if (showProc) parts.push("p.prokind = 'p'");
136
+ if (showWin) parts.push(serverAtLeast(serverVersion, 11e4) ? "p.prokind = 'w'" : "p.proiswindow");
137
+ sql += parts.join("\n OR ") + "\n )\n";
138
+ }
139
+ sql += patternStub(hasWhere, "n.nspname", "p.proname");
140
+ for (let i = 0; i < argPatterns.length; i++) if (argPatterns[i] === "-") sql += ` AND t${i}.typname IS NULL\n`;
141
+ else sql += ` AND ${argPatternStub(i)}\n`;
142
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
143
+ sql += orderBy("1, 2, 4");
144
+ return {
145
+ sql,
146
+ params: params(),
147
+ description: "List of functions"
148
+ };
149
+ };
150
+ const describeTypes = (opts) => {
151
+ const { verbose, showSystem, pattern } = opts;
152
+ let sql = "SELECT n.nspname as \"Schema\",\n pg_catalog.format_type(t.oid, NULL) AS \"Name\",\n";
153
+ if (verbose) sql += " t.typname AS \"Internal name\",\n CASE WHEN t.typrelid != 0\n THEN CAST('tuple' AS pg_catalog.text)\n WHEN t.typlen < 0\n THEN CAST('var' AS pg_catalog.text)\n ELSE CAST(t.typlen AS pg_catalog.text)\n END AS \"Size\",\n pg_catalog.array_to_string(\n ARRAY(\n SELECT e.enumlabel\n FROM pg_catalog.pg_enum e\n WHERE e.enumtypid = t.oid\n ORDER BY e.enumsortorder\n ),\n E'\\n'\n ) AS \"Elements\",\n pg_catalog.pg_get_userbyid(t.typowner) AS \"Owner\",\n " + aclColumn("t.typacl") + ",\n ";
154
+ sql += " pg_catalog.obj_description(t.oid, 'pg_type') as \"Description\"\n";
155
+ sql += "FROM pg_catalog.pg_type t\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n";
156
+ sql += "WHERE (t.typrelid = 0 ";
157
+ sql += "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))\n";
158
+ if (!pattern?.includes("[]")) sql += " AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)\n";
159
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
160
+ sql += patternStub(true, "n.nspname", "t.typname");
161
+ sql += orderBy("1, 2");
162
+ return {
163
+ sql,
164
+ params: params(),
165
+ description: "List of data types"
166
+ };
167
+ };
168
+ const describeOperators = (opts) => {
169
+ const { verbose, showSystem, pattern, serverVersion } = opts;
170
+ let argPatterns = opts.argPatterns ?? [];
171
+ if (argPatterns.length > 2) argPatterns = argPatterns.slice(0, 2);
172
+ let sql = "SELECT n.nspname as \"Schema\",\n o.oprname AS \"Name\",\n CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"Left arg type\",\n CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"Right arg type\",\n pg_catalog.format_type(o.oprresult, NULL) AS \"Result type\",\n";
173
+ if (verbose) {
174
+ sql += " o.oprcode AS \"Function\",\n";
175
+ if (serverAtLeast(serverVersion, 18e4)) sql += " CASE WHEN p.proleakproof THEN 'yes' ELSE 'no' END AS \"Leakproof?\",\n";
176
+ }
177
+ sql += " coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"Description\"\nFROM pg_catalog.pg_operator o\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n";
178
+ if (argPatterns.length === 1) sql += " LEFT JOIN pg_catalog.pg_type t0 ON t0.oid = o.oprright\n LEFT JOIN pg_catalog.pg_namespace nt0 ON nt0.oid = t0.typnamespace\n";
179
+ else if (argPatterns.length >= 2) sql += " LEFT JOIN pg_catalog.pg_type t0 ON t0.oid = o.oprleft\n LEFT JOIN pg_catalog.pg_namespace nt0 ON nt0.oid = t0.typnamespace\n LEFT JOIN pg_catalog.pg_type t1 ON t1.oid = o.oprright\n LEFT JOIN pg_catalog.pg_namespace nt1 ON nt1.oid = t1.typnamespace\n";
180
+ if (verbose && serverAtLeast(serverVersion, 18e4)) sql += " LEFT JOIN pg_catalog.pg_proc p ON p.oid = o.oprcode\n";
181
+ let hasWhere = false;
182
+ if (!showSystem && pattern === void 0) {
183
+ sql += "WHERE n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
184
+ hasWhere = true;
185
+ }
186
+ sql += patternStub(hasWhere, "n.nspname", "o.oprname");
187
+ if (argPatterns.length === 1) sql += " AND o.oprleft = 0\n";
188
+ for (let i = 0; i < argPatterns.length; i++) if (argPatterns[i] === "-") sql += ` AND t${i}.typname IS NULL\n`;
189
+ else sql += ` AND ${argPatternStub(i)}\n`;
190
+ sql += orderBy("1, 2, 3, 4");
191
+ return {
192
+ sql,
193
+ params: params(),
194
+ description: "List of operators"
195
+ };
196
+ };
197
+ const listAllDbs = (opts) => {
198
+ const { verbose, serverVersion } = opts;
199
+ let sql = "SELECT\n d.datname as \"Name\",\n pg_catalog.pg_get_userbyid(d.datdba) as \"Owner\",\n pg_catalog.pg_encoding_to_char(d.encoding) as \"Encoding\",\n";
200
+ if (serverAtLeast(serverVersion, 15e4)) sql += " CASE d.datlocprovider WHEN 'b' THEN 'builtin' WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS \"Locale Provider\",\n";
201
+ else sql += " 'libc' AS \"Locale Provider\",\n";
202
+ sql += " d.datcollate as \"Collate\",\n d.datctype as \"Ctype\",\n";
203
+ if (serverAtLeast(serverVersion, 17e4)) sql += " d.datlocale as \"Locale\",\n";
204
+ else if (serverAtLeast(serverVersion, 15e4)) sql += " d.daticulocale as \"Locale\",\n";
205
+ else sql += " NULL as \"Locale\",\n";
206
+ if (serverAtLeast(serverVersion, 16e4)) sql += " d.daticurules as \"ICU Rules\",\n";
207
+ else sql += " NULL as \"ICU Rules\",\n";
208
+ sql += " " + aclColumn("d.datacl");
209
+ if (verbose) sql += ",\n CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n ELSE 'No Access'\n END as \"Size\",\n t.spcname as \"Tablespace\",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"Description\"";
210
+ sql += "\nFROM pg_catalog.pg_database d\n";
211
+ if (verbose) sql += " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n";
212
+ if (opts.pattern !== void 0) sql += patternStub(false, void 0, "d.datname");
213
+ sql += orderBy("1");
214
+ return {
215
+ sql,
216
+ params: params(),
217
+ description: "List of databases"
218
+ };
219
+ };
220
+ const permissionsList = (opts) => {
221
+ const { showSystem, pattern, serverVersion } = opts;
222
+ let sql = "SELECT n.nspname as \"Schema\",\n c.relname as \"Name\",\n CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'S' THEN 'sequence' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' END as \"Type\",\n ";
223
+ sql += aclColumn("c.relacl");
224
+ sql += ",\n pg_catalog.array_to_string(ARRAY(\n SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n FROM pg_catalog.pg_attribute a\n WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n ), E'\\n') AS \"Column privileges\"";
225
+ if (serverAtLeast(serverVersion, 1e5)) sql += ",\n pg_catalog.array_to_string(ARRAY(\n SELECT polname\n || CASE WHEN NOT polpermissive THEN E' (RESTRICTIVE)' ELSE '' END\n || CASE WHEN polcmd != '*' THEN E' (' || polcmd::pg_catalog.text || E'):' ELSE E':' END\n || CASE WHEN polqual IS NOT NULL THEN E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid) ELSE E'' END\n || CASE WHEN polwithcheck IS NOT NULL THEN E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid) ELSE E'' END\n || CASE WHEN polroles <> '{0}' THEN E'\\n to: ' || pg_catalog.array_to_string(\n ARRAY(SELECT rolname FROM pg_catalog.pg_roles WHERE oid = ANY (polroles) ORDER BY 1)\n , E', ') ELSE E'' END\n FROM pg_catalog.pg_policy pol\n WHERE polrelid = c.oid), E'\\n') AS \"Policies\"";
226
+ else if (serverAtLeast(serverVersion, 90500)) sql += ",\n pg_catalog.array_to_string(ARRAY(\n SELECT polname\n || CASE WHEN polcmd != '*' THEN E' (' || polcmd::pg_catalog.text || E'):' ELSE E':' END\n || CASE WHEN polqual IS NOT NULL THEN E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid) ELSE E'' END\n || CASE WHEN polwithcheck IS NOT NULL THEN E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid) ELSE E'' END\n || CASE WHEN polroles <> '{0}' THEN E'\\n to: ' || pg_catalog.array_to_string(\n ARRAY(SELECT rolname FROM pg_catalog.pg_roles WHERE oid = ANY (polroles) ORDER BY 1)\n , E', ') ELSE E'' END\n FROM pg_catalog.pg_policy pol\n WHERE polrelid = c.oid), E'\\n') AS \"Policies\"";
227
+ sql += "\nFROM pg_catalog.pg_class c\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\nWHERE c.relkind IN ('r','v','m','S','f','p')\n";
228
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
229
+ sql += patternStub(true, "n.nspname", "c.relname");
230
+ sql += orderBy("1, 2");
231
+ return {
232
+ sql,
233
+ params: params(),
234
+ description: "Access privileges"
235
+ };
236
+ };
237
+ const listDefaultACLs = (opts) => {
238
+ const { serverVersion } = opts;
239
+ let sql = "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"Owner\",\n n.nspname AS \"Schema\",\n CASE d.defaclobjtype WHEN 'r' THEN 'table' WHEN 'S' THEN 'sequence' WHEN 'f' THEN 'function' WHEN 'T' THEN 'type' WHEN 'n' THEN 'schema'";
240
+ if (serverAtLeast(serverVersion, 18e4)) sql += " WHEN 'L' THEN 'large object'";
241
+ sql += " END AS \"Type\",\n ";
242
+ sql += aclColumn("d.defaclacl");
243
+ sql += "\nFROM pg_catalog.pg_default_acl d\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n";
244
+ sql += patternStub(false, "n.nspname", "pg_catalog.pg_get_userbyid(d.defaclrole)");
245
+ sql += orderBy("1, 2, 3");
246
+ return {
247
+ sql,
248
+ params: params(),
249
+ description: "Default access privileges"
250
+ };
251
+ };
252
+ const objectDescription = (opts) => {
253
+ const sysPart = (objLabel) => !opts.showSystem && opts.pattern === void 0 ? `WHERE n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema' /* ${objLabel} */ ` : `WHERE true /* ${objLabel} */ `;
254
+ let sql = "SELECT DISTINCT tt.nspname AS \"Schema\", tt.name AS \"Name\", tt.object AS \"Object\", d.description AS \"Description\"\nFROM (\n";
255
+ sql += " SELECT pgc.oid as oid, pgc.tableoid AS tableoid, n.nspname as nspname,\n CAST(pgc.conname AS pg_catalog.text) as name, CAST('table constraint' AS pg_catalog.text) as object\n FROM pg_catalog.pg_constraint pgc\n JOIN pg_catalog.pg_class c ON c.oid = pgc.conrelid\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" + sysPart("table constraint") + "\n";
256
+ sql += "UNION ALL\n SELECT pgc.oid, pgc.tableoid, n.nspname,\n CAST(pgc.conname AS pg_catalog.text), CAST('domain constraint' AS pg_catalog.text)\n FROM pg_catalog.pg_constraint pgc\n JOIN pg_catalog.pg_type t ON t.oid = pgc.contypid\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n" + sysPart("domain constraint") + "\n";
257
+ sql += "UNION ALL\n SELECT o.oid, o.tableoid, n.nspname,\n CAST(o.opcname AS pg_catalog.text), CAST('operator class' AS pg_catalog.text)\n FROM pg_catalog.pg_opclass o\n JOIN pg_catalog.pg_am am ON o.opcmethod = am.oid\n JOIN pg_catalog.pg_namespace n ON n.oid = o.opcnamespace\n" + sysPart("operator class") + "\n";
258
+ sql += "UNION ALL\n SELECT opf.oid, opf.tableoid, n.nspname,\n CAST(opf.opfname AS pg_catalog.text), CAST('operator family' AS pg_catalog.text)\n FROM pg_catalog.pg_opfamily opf\n JOIN pg_catalog.pg_am am ON opf.opfmethod = am.oid\n JOIN pg_catalog.pg_namespace n ON opf.opfnamespace = n.oid\n" + sysPart("operator family") + "\n";
259
+ sql += "UNION ALL\n SELECT r.oid, r.tableoid, n.nspname,\n CAST(r.rulename AS pg_catalog.text), CAST('rule' AS pg_catalog.text)\n FROM pg_catalog.pg_rewrite r\n JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n WHERE r.rulename != '_RETURN'\n";
260
+ sql += "UNION ALL\n SELECT t.oid, t.tableoid, n.nspname,\n CAST(t.tgname AS pg_catalog.text), CAST('trigger' AS pg_catalog.text)\n FROM pg_catalog.pg_trigger t\n JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" + sysPart("trigger") + "\n";
261
+ sql += ") AS tt\n JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n";
262
+ sql += orderBy("1, 2, 3");
263
+ return {
264
+ sql,
265
+ params: params(),
266
+ description: "Object descriptions"
267
+ };
268
+ };
269
+ const describeTableDetails = (opts) => {
270
+ const { verbose, showSystem, pattern, serverVersion } = opts;
271
+ const hideTableam = opts.hideTableam ?? false;
272
+ let sql = "SELECT n.nspname as \"Schema\",\n c.relname as \"Name\",\n CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' WHEN 'I' THEN 'partitioned index' END as \"Type\",\n pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"";
273
+ if (verbose) {
274
+ sql += ",\n CASE c.relpersistence WHEN 'p' THEN 'permanent' WHEN 't' THEN 'temporary' WHEN 'u' THEN 'unlogged' END as \"Persistence\"";
275
+ if (!hideTableam && serverAtLeast(serverVersion, 12e4)) sql += ",\n am.amname as \"Access method\"";
276
+ sql += ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_total_relation_size(c.oid)) as \"Size\",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"Description\"";
277
+ }
278
+ sql += "\nFROM pg_catalog.pg_class c\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace";
279
+ if (verbose && !hideTableam && serverAtLeast(serverVersion, 12e4)) sql += "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam";
280
+ sql += "\nWHERE c.relkind IN ('r','p','v','m','S','f','')\n";
281
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname !~ '^pg_toast'\n AND n.nspname <> 'information_schema'\n";
282
+ sql += patternStub(true, "n.nspname", "c.relname");
283
+ sql += orderBy("1,2");
284
+ return {
285
+ sql,
286
+ params: params(),
287
+ description: "List of relations"
288
+ };
1389
289
  };
1390
290
  /**
1391
- * Per-parser detail (one row per phase). Caller binds the parser oid.
1392
- * Two queries upstream; we emit both as a UNION via a single string.
1393
- */
1394
- export const describeOneTSParser = (opts) => {
1395
- const { oid } = opts;
1396
- const sql = `SELECT 'Start parse' AS "Method",\n` +
1397
- ` p.prsstart::pg_catalog.regproc AS "Function",\n` +
1398
- ` pg_catalog.obj_description(p.prsstart, 'pg_proc') as "Description"\n` +
1399
- ` FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}'\n` +
1400
- `UNION ALL\n` +
1401
- `SELECT 'Get next token', p.prstoken::pg_catalog.regproc, pg_catalog.obj_description(p.prstoken, 'pg_proc')\n` +
1402
- ` FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}'\n` +
1403
- `UNION ALL\n` +
1404
- `SELECT 'End parse', p.prsend::pg_catalog.regproc, pg_catalog.obj_description(p.prsend, 'pg_proc')\n` +
1405
- ` FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}'\n` +
1406
- `UNION ALL\n` +
1407
- `SELECT 'Get headline', p.prsheadline::pg_catalog.regproc, pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n` +
1408
- ` FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}'\n` +
1409
- `UNION ALL\n` +
1410
- `SELECT 'Get token types', p.prslextype::pg_catalog.regproc, pg_catalog.obj_description(p.prslextype, 'pg_proc')\n` +
1411
- ` FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}';`;
1412
- return { sql, params: [], description: "Text search parser details" };
1413
- };
1414
- /* ------------------------------------------------------------------ */
1415
- /* \dFd — listTSDictionaries */
1416
- /* ------------------------------------------------------------------ */
1417
- export const listTSDictionaries = (opts) => {
1418
- const { verbose } = opts;
1419
- let sql = 'SELECT\n n.nspname as "Schema",\n d.dictname as "Name",\n';
1420
- if (verbose) {
1421
- sql +=
1422
- " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM\n" +
1423
- " pg_catalog.pg_ts_template t\n" +
1424
- " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace\n" +
1425
- ' WHERE d.dicttemplate = t.oid ) AS "Template",\n' +
1426
- ' d.dictinitoption as "Init options",\n';
1427
- }
1428
- sql +=
1429
- " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"Description\"\n" +
1430
- "FROM pg_catalog.pg_ts_dict d\n" +
1431
- "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n";
1432
- sql += patternStub(false, "n.nspname", "d.dictname");
1433
- sql += orderBy("1, 2");
1434
- return {
1435
- sql,
1436
- params: params(),
1437
- description: "List of text search dictionaries",
1438
- };
1439
- };
1440
- /* ------------------------------------------------------------------ */
1441
- /* \dFt — listTSTemplates */
1442
- /* ------------------------------------------------------------------ */
1443
- export const listTSTemplates = (opts) => {
1444
- const { verbose } = opts;
1445
- let sql;
1446
- if (verbose) {
1447
- sql =
1448
- 'SELECT\n n.nspname AS "Schema",\n t.tmplname AS "Name",\n' +
1449
- ' t.tmplinit::pg_catalog.regproc AS "Init",\n' +
1450
- ' t.tmpllexize::pg_catalog.regproc AS "Lexize",\n' +
1451
- " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"Description\"\n";
1452
- }
1453
- else {
1454
- sql =
1455
- 'SELECT\n n.nspname AS "Schema",\n t.tmplname AS "Name",\n' +
1456
- " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"Description\"\n";
1457
- }
1458
- sql +=
1459
- "FROM pg_catalog.pg_ts_template t\n" +
1460
- "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n";
1461
- sql += patternStub(false, "n.nspname", "t.tmplname");
1462
- sql += orderBy("1, 2");
1463
- return {
1464
- sql,
1465
- params: params(),
1466
- description: "List of text search templates",
1467
- };
1468
- };
1469
- /* ------------------------------------------------------------------ */
1470
- /* \dF — listTSConfigs */
1471
- /* ------------------------------------------------------------------ */
1472
- export const listTSConfigs = (opts) => {
1473
- const { verbose } = opts;
1474
- if (verbose) {
1475
- let sql = "SELECT c.oid, c.cfgname, n.nspname, p.prsname, np.nspname as pnspname\n" +
1476
- "FROM pg_catalog.pg_ts_config c\n" +
1477
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace,\n" +
1478
- " pg_catalog.pg_ts_parser p\n" +
1479
- " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace\n" +
1480
- "WHERE p.oid = c.cfgparser\n";
1481
- sql += patternStub(true, "n.nspname", "c.cfgname");
1482
- sql += orderBy("3, 2");
1483
- return {
1484
- sql,
1485
- params: params(),
1486
- description: "List of text search configurations (verbose)",
1487
- };
1488
- }
1489
- let sql = 'SELECT\n n.nspname as "Schema",\n c.cfgname as "Name",\n' +
1490
- " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"Description\"\n" +
1491
- "FROM pg_catalog.pg_ts_config c\n" +
1492
- "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace\n";
1493
- sql += patternStub(false, "n.nspname", "c.cfgname");
1494
- sql += orderBy("1, 2");
1495
- return {
1496
- sql,
1497
- params: params(),
1498
- description: "List of text search configurations",
1499
- };
1500
- };
1501
- /* ------------------------------------------------------------------ */
1502
- /* \dew — listForeignDataWrappers */
1503
- /* ------------------------------------------------------------------ */
1504
- export const listForeignDataWrappers = (opts) => {
1505
- const { verbose } = opts;
1506
- let sql = 'SELECT fdw.fdwname AS "Name",\n' +
1507
- ' pg_catalog.pg_get_userbyid(fdw.fdwowner) AS "Owner",\n' +
1508
- ' fdw.fdwhandler::pg_catalog.regproc AS "Handler",\n' +
1509
- ' fdw.fdwvalidator::pg_catalog.regproc AS "Validator"';
1510
- if (verbose) {
1511
- sql += ",\n " + aclColumn("fdwacl");
1512
- sql +=
1513
- ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE\n" +
1514
- " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM pg_catalog.pg_options_to_table(fdwoptions)), ', ') || ')'\n" +
1515
- ' END AS "FDW options",\n' +
1516
- ' d.description AS "Description" ';
1517
- }
1518
- sql += "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n";
1519
- if (verbose) {
1520
- sql +=
1521
- "LEFT JOIN pg_catalog.pg_description d ON d.classoid = fdw.tableoid AND d.objoid = fdw.oid AND d.objsubid = 0\n";
1522
- }
1523
- sql += patternStub(false, undefined, "fdwname");
1524
- sql += orderBy("1");
1525
- return {
1526
- sql,
1527
- params: params(),
1528
- description: "List of foreign-data wrappers",
1529
- };
1530
- };
1531
- /* ------------------------------------------------------------------ */
1532
- /* \des — listForeignServers */
1533
- /* ------------------------------------------------------------------ */
1534
- export const listForeignServers = (opts) => {
1535
- const { verbose } = opts;
1536
- let sql = 'SELECT s.srvname AS "Name",\n' +
1537
- ' pg_catalog.pg_get_userbyid(s.srvowner) AS "Owner",\n' +
1538
- ' f.fdwname AS "Foreign-data wrapper"';
1539
- if (verbose) {
1540
- sql += ",\n " + aclColumn("s.srvacl");
1541
- sql +=
1542
- ',\n s.srvtype AS "Type",\n' +
1543
- ' s.srvversion AS "Version",\n' +
1544
- " CASE WHEN srvoptions IS NULL THEN '' ELSE\n" +
1545
- " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM pg_catalog.pg_options_to_table(srvoptions)), ', ') || ')'\n" +
1546
- ' END AS "FDW options",\n' +
1547
- ' d.description AS "Description"';
1548
- }
1549
- sql +=
1550
- "\nFROM pg_catalog.pg_foreign_server s\n" +
1551
- " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n";
1552
- if (verbose) {
1553
- sql +=
1554
- "LEFT JOIN pg_catalog.pg_description d ON d.classoid = s.tableoid AND d.objoid = s.oid AND d.objsubid = 0\n";
1555
- }
1556
- sql += patternStub(false, undefined, "s.srvname");
1557
- sql += orderBy("1");
1558
- return { sql, params: params(), description: "List of foreign servers" };
1559
- };
1560
- /* ------------------------------------------------------------------ */
1561
- /* \deu — listUserMappings */
1562
- /* ------------------------------------------------------------------ */
1563
- export const listUserMappings = (opts) => {
1564
- const { verbose } = opts;
1565
- let sql = 'SELECT um.srvname AS "Server",\n um.usename AS "User name"';
1566
- if (verbose) {
1567
- sql +=
1568
- ",\n CASE WHEN umoptions IS NULL THEN '' ELSE\n" +
1569
- " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM pg_catalog.pg_options_to_table(umoptions)), ', ') || ')'\n" +
1570
- ' END AS "FDW options"';
1571
- }
1572
- sql += "\nFROM pg_catalog.pg_user_mappings um\n";
1573
- sql += patternStub(false, undefined, "um.srvname");
1574
- sql += orderBy("1, 2");
1575
- return { sql, params: params(), description: "List of user mappings" };
1576
- };
1577
- /* ------------------------------------------------------------------ */
1578
- /* \det — listForeignTables */
1579
- /* ------------------------------------------------------------------ */
1580
- export const listForeignTables = (opts) => {
1581
- const { verbose } = opts;
1582
- let sql = 'SELECT n.nspname AS "Schema",\n c.relname AS "Table",\n s.srvname AS "Server"';
1583
- if (verbose) {
1584
- sql +=
1585
- ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE\n" +
1586
- " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM pg_catalog.pg_options_to_table(ftoptions)), ', ') || ')'\n" +
1587
- ' END AS "FDW options",\n' +
1588
- ' d.description AS "Description"';
1589
- }
1590
- sql +=
1591
- "\nFROM pg_catalog.pg_foreign_table ft\n" +
1592
- " INNER JOIN pg_catalog.pg_class c ON c.oid = ft.ftrelid\n" +
1593
- " INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" +
1594
- " INNER JOIN pg_catalog.pg_foreign_server s ON s.oid = ft.ftserver\n";
1595
- if (verbose) {
1596
- sql +=
1597
- " LEFT JOIN pg_catalog.pg_description d ON d.classoid = c.tableoid AND d.objoid = c.oid AND d.objsubid = 0\n";
1598
- }
1599
- sql += patternStub(false, "n.nspname", "c.relname");
1600
- sql += orderBy("1, 2");
1601
- return { sql, params: params(), description: "List of foreign tables" };
1602
- };
1603
- /* ------------------------------------------------------------------ */
1604
- /* \dx — listExtensions / \dx+ — listExtensionContents */
1605
- /* ------------------------------------------------------------------ */
1606
- export const listExtensions = (opts) => {
1607
- const { serverVersion } = opts;
1608
- // PG 18 added the "Default version" column to `\dx` to make it
1609
- // easy to spot extensions that are behind the available default.
1610
- const hasDefaultVersion = serverAtLeast(serverVersion, PG_18);
1611
- let sql = 'SELECT e.extname AS "Name", e.extversion AS "Version", ';
1612
- if (hasDefaultVersion) {
1613
- sql += 'ae.default_version AS "Default version",';
1614
- }
1615
- sql +=
1616
- 'n.nspname AS "Schema", d.description AS "Description"\n' +
1617
- "FROM pg_catalog.pg_extension e " +
1618
- "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace " +
1619
- "LEFT JOIN pg_catalog.pg_description d ON d.objoid = e.oid AND d.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass";
1620
- if (hasDefaultVersion) {
1621
- sql +=
1622
- " LEFT JOIN pg_catalog.pg_available_extensions() ae(name, default_version, comment) ON ae.name = e.extname";
1623
- }
1624
- sql += "\n";
1625
- sql += patternStub(false, undefined, "e.extname");
1626
- sql += orderBy("1");
1627
- return {
1628
- sql,
1629
- params: params(),
1630
- description: "List of installed extensions",
1631
- };
1632
- };
1633
- export const listExtensionContents = () => {
1634
- let sql = "SELECT e.extname, e.oid\nFROM pg_catalog.pg_extension e\n";
1635
- sql += patternStub(false, undefined, "e.extname");
1636
- sql += orderBy("1");
1637
- return {
1638
- sql,
1639
- params: params(),
1640
- description: "Get matching extensions to list contents for",
1641
- };
291
+ * Upstream `describeRoles` (psql/describe.c) fetches the raw boolean
292
+ * columns then builds the cooked "Attributes" cell client-side from
293
+ * fixed per-flag strings. We do that aggregation in SQL via a
294
+ * NULL-skipping array_to_string so the printer sees only the final
295
+ * two visible columns ("Role name", "Attributes") plus an optional
296
+ * "Description" column when `+` is used. This matches the column
297
+ * layout, header banner ("List of roles"), and content ordering of
298
+ * vanilla psql 18, including the `Cannot login`-only rows produced by
299
+ * `\du regress_du_role*` in `src/test/regress/expected/psql.out`.
300
+ */
301
+ const describeRoles = (opts) => {
302
+ const { verbose, showSystem, pattern, serverVersion } = opts;
303
+ const attrParts = [
304
+ "CASE WHEN r.rolsuper THEN 'Superuser' END",
305
+ "CASE WHEN NOT r.rolinherit THEN 'No inheritance' END",
306
+ "CASE WHEN r.rolcreaterole THEN 'Create role' END",
307
+ "CASE WHEN r.rolcreatedb THEN 'Create DB' END",
308
+ "CASE WHEN NOT r.rolcanlogin THEN 'Cannot login' END",
309
+ "CASE WHEN r.rolreplication THEN 'Replication' END"
310
+ ];
311
+ if (serverAtLeast(serverVersion, 90500)) attrParts.push("CASE WHEN r.rolbypassrls THEN 'Bypass RLS' END");
312
+ attrParts.push("CASE WHEN r.rolconnlimit >= 0 THEN pg_catalog.format('%s connection%s', r.rolconnlimit, CASE WHEN r.rolconnlimit = 1 THEN '' ELSE 's' END) END");
313
+ attrParts.push("CASE WHEN r.rolvaliduntil IS NOT NULL THEN pg_catalog.format('Password valid until %s', r.rolvaliduntil) END");
314
+ let sql = `SELECT r.rolname AS "Role name",
315
+ pg_catalog.array_to_string(ARRAY(
316
+ SELECT a FROM (VALUES
317
+ ${attrParts.map((p) => `(${p})`).join(",\n ")}\n ) AS x(a)
318
+ WHERE a IS NOT NULL
319
+ ), ', ') AS "Attributes"`;
320
+ if (verbose) sql += ",\n pg_catalog.shobj_description(r.oid, 'pg_authid') AS \"Description\"";
321
+ sql += "\nFROM pg_catalog.pg_roles r\n";
322
+ let hasWhere = false;
323
+ if (!showSystem && pattern === void 0) {
324
+ sql += "WHERE r.rolname !~ '^pg_'\n";
325
+ hasWhere = true;
326
+ }
327
+ sql += patternStub(hasWhere, void 0, "r.rolname");
328
+ sql += orderBy("1");
329
+ return {
330
+ sql,
331
+ params: params(),
332
+ description: "List of roles"
333
+ };
334
+ };
335
+ const listDbRoleSettings = (opts) => {
336
+ let sql = "SELECT rolname AS \"Role\", datname AS \"Database\",\npg_catalog.array_to_string(setconfig, E'\\n') AS \"Settings\"\nFROM pg_catalog.pg_db_role_setting s\nLEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\nLEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n";
337
+ let hasWhere = false;
338
+ if (opts.pattern !== void 0) {
339
+ sql += patternStub(hasWhere, void 0, "r.rolname");
340
+ hasWhere = true;
341
+ }
342
+ if (opts.pattern2 !== void 0) sql += patternStub(hasWhere, void 0, "d.datname");
343
+ sql += orderBy("1, 2");
344
+ const ps = [];
345
+ if (opts.pattern !== void 0) ps.push(opts.pattern);
346
+ if (opts.pattern2 !== void 0) ps.push(opts.pattern2);
347
+ return {
348
+ sql,
349
+ params: ps,
350
+ description: "List of settings"
351
+ };
352
+ };
353
+ const describeRoleGrants = (opts) => {
354
+ const { showSystem, pattern, serverVersion } = opts;
355
+ let sql = "SELECT m.rolname AS \"Role name\", r.rolname AS \"Member of\",\n pg_catalog.concat_ws(', ',\n";
356
+ if (serverAtLeast(serverVersion, 16e4)) sql += " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n CASE WHEN pam.set_option THEN 'SET' END\n";
357
+ else sql += " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n CASE WHEN m.rolinherit THEN 'INHERIT' END,\n 'SET'\n";
358
+ sql += " ) AS \"Options\",\n g.rolname AS \"Grantor\"\n";
359
+ sql += "FROM pg_catalog.pg_roles m\n JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n";
360
+ let hasWhere = false;
361
+ if (!showSystem && pattern === void 0) {
362
+ sql += "WHERE m.rolname !~ '^pg_'\n";
363
+ hasWhere = true;
364
+ }
365
+ sql += patternStub(hasWhere, void 0, "m.rolname");
366
+ sql += orderBy("1, 2, 4");
367
+ return {
368
+ sql,
369
+ params: params(),
370
+ description: "List of role grants"
371
+ };
372
+ };
373
+ const listTables = (opts) => {
374
+ const { verbose, showSystem, pattern, serverVersion } = opts;
375
+ const hideTableam = opts.hideTableam ?? false;
376
+ const tt = opts.tabtypes ?? "";
377
+ let showTables = tt.includes("t");
378
+ const showIndexes = tt.includes("i");
379
+ let showViews = tt.includes("v");
380
+ let showMatViews = tt.includes("m");
381
+ let showSeq = tt.includes("s");
382
+ let showForeign = tt.includes("E");
383
+ const ntypes = +showTables + +showIndexes + +showViews + +showMatViews + +showSeq + +showForeign;
384
+ if (ntypes === 0) showTables = showViews = showMatViews = showSeq = showForeign = true;
385
+ let sql = "SELECT n.nspname as \"Schema\",\n c.relname as \"Name\",\n CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' WHEN 'I' THEN 'partitioned index' END as \"Type\",\n pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"";
386
+ if (showIndexes) sql += ",\n c2.relname as \"Table\"";
387
+ if (verbose) {
388
+ sql += ",\n CASE c.relpersistence WHEN 'p' THEN 'permanent' WHEN 't' THEN 'temporary' WHEN 'u' THEN 'unlogged' END as \"Persistence\"";
389
+ if (!hideTableam && serverAtLeast(serverVersion, 12e4) && (showTables || showMatViews || showIndexes)) sql += ",\n am.amname as \"Access method\"";
390
+ sql += ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_total_relation_size(c.oid)) as \"Size\",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"Description\"";
391
+ }
392
+ sql += "\nFROM pg_catalog.pg_class c\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace";
393
+ if (verbose && !hideTableam && serverAtLeast(serverVersion, 12e4) && (showTables || showMatViews || showIndexes)) sql += "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam";
394
+ if (showIndexes) sql += "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid";
395
+ sql += "\nWHERE c.relkind IN (";
396
+ const kinds = [];
397
+ if (showTables) {
398
+ kinds.push("'r'", "'p'");
399
+ if (showSystem || pattern !== void 0) kinds.push("'t'");
400
+ }
401
+ if (showViews) kinds.push("'v'");
402
+ if (showMatViews) kinds.push("'m'");
403
+ if (showIndexes) kinds.push("'i'", "'I'");
404
+ if (showSeq) kinds.push("'S'");
405
+ if (showSystem || pattern !== void 0) kinds.push("'s'");
406
+ if (showForeign) kinds.push("'f'");
407
+ kinds.push("''");
408
+ sql += kinds.join(",") + ")\n";
409
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname !~ '^pg_toast'\n AND n.nspname <> 'information_schema'\n";
410
+ sql += patternStub(true, "n.nspname", "c.relname");
411
+ sql += orderBy("1,2");
412
+ let description = "List of relations";
413
+ if (ntypes === 1) {
414
+ if (showTables) description = "List of tables";
415
+ else if (showIndexes) description = "List of indexes";
416
+ else if (showViews) description = "List of views";
417
+ else if (showMatViews) description = "List of materialized views";
418
+ else if (showSeq) description = "List of sequences";
419
+ else if (showForeign) description = "List of foreign tables";
420
+ }
421
+ return {
422
+ sql,
423
+ params: params(),
424
+ description
425
+ };
426
+ };
427
+ const listPartitionedTables = (opts) => {
428
+ const { verbose, pattern, serverVersion } = opts;
429
+ if (serverLess(serverVersion, 1e5)) return {
430
+ sql: "/* server < 10 does not support declarative partitioning */ SELECT 1 WHERE false;",
431
+ params: [],
432
+ description: "List of partitioned relations"
433
+ };
434
+ const rt = opts.reltypes ?? "";
435
+ let showTables = rt.includes("t");
436
+ let showIndexes = rt.includes("i");
437
+ const showNested = rt.includes("n");
438
+ if (!showTables && !showIndexes) showTables = showIndexes = true;
439
+ const mixed = showTables && showIndexes;
440
+ let sql = "SELECT n.nspname as \"Schema\",\n c.relname as \"Name\",\n pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"";
441
+ if (mixed) sql += ",\n CASE c.relkind WHEN 'p' THEN 'partitioned table' WHEN 'I' THEN 'partitioned index' END as \"Type\"";
442
+ if (showNested || pattern !== void 0) sql += ",\n inh.inhparent::pg_catalog.regclass as \"Parent name\"";
443
+ if (showIndexes) sql += ",\n c2.oid::pg_catalog.regclass as \"Table\"";
444
+ if (verbose) {
445
+ sql += ",\n am.amname as \"Access method\"";
446
+ if (showNested) {
447
+ sql += ",\n s.dps as \"Leaf partition size\"";
448
+ sql += ",\n s.tps as \"Total size\"";
449
+ } else sql += ",\n s.tps as \"Total size\"";
450
+ sql += ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"Description\"";
451
+ }
452
+ sql += "\nFROM pg_catalog.pg_class c\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace";
453
+ if (showIndexes) sql += "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid";
454
+ if (showNested || pattern !== void 0) sql += "\n LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid";
455
+ if (verbose) {
456
+ sql += "\n LEFT JOIN pg_catalog.pg_am am ON c.relam = am.oid";
457
+ if (serverLess(serverVersion, 12e4)) sql += ",\n LATERAL (WITH RECURSIVE d AS (\n SELECT inhrelid AS oid, 1 AS level FROM pg_catalog.pg_inherits WHERE inhparent = c.oid\n UNION ALL\n SELECT inhrelid, level + 1 FROM pg_catalog.pg_inherits i JOIN d ON i.inhparent = d.oid)\n SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size(d.oid))) AS tps,\n pg_catalog.pg_size_pretty(sum(CASE WHEN d.level = 1 THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n FROM d) s";
458
+ else sql += ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum(\n CASE WHEN ppt.isleaf AND ppt.level = 1\n THEN pg_catalog.pg_table_size(ppt.relid) ELSE 0 END)) AS dps,\n pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size(ppt.relid))) AS tps\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s";
459
+ }
460
+ sql += "\nWHERE c.relkind IN (";
461
+ const kinds = [];
462
+ if (showTables) kinds.push("'p'");
463
+ if (showIndexes) kinds.push("'I'");
464
+ kinds.push("''");
465
+ sql += kinds.join(",") + ")\n";
466
+ if (!showNested && pattern === void 0) sql += " AND NOT c.relispartition\n";
467
+ if (pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname !~ '^pg_toast'\n AND n.nspname <> 'information_schema'\n";
468
+ sql += patternStub(true, "n.nspname", "c.relname");
469
+ sql += `ORDER BY "Schema", ${mixed ? "\"Type\" DESC, " : ""}${showNested || pattern !== void 0 ? "\"Parent name\" NULLS FIRST, " : ""}"Name";`;
470
+ const description = showIndexes && !showTables ? "List of partitioned indexes" : showTables && !showIndexes ? "List of partitioned tables" : "List of partitioned relations";
471
+ return {
472
+ sql,
473
+ params: params(),
474
+ description
475
+ };
476
+ };
477
+ const listLanguages = (opts) => {
478
+ const { verbose, showSystem, pattern } = opts;
479
+ let sql = "SELECT l.lanname AS \"Name\",\n pg_catalog.pg_get_userbyid(l.lanowner) as \"Owner\",\n l.lanpltrusted AS \"Trusted\"";
480
+ if (verbose) sql += ",\n NOT l.lanispl AS \"Internal language\",\n l.lanplcallfoid::pg_catalog.regprocedure AS \"Call handler\",\n l.lanvalidator::pg_catalog.regprocedure AS \"Validator\",\n l.laninline::pg_catalog.regprocedure AS \"Inline handler\",\n " + aclColumn("l.lanacl");
481
+ sql += ",\n d.description AS \"Description\"\nFROM pg_catalog.pg_language l\nLEFT JOIN pg_catalog.pg_description d\n ON d.classoid = l.tableoid AND d.objoid = l.oid AND d.objsubid = 0\n";
482
+ let hasWhere = false;
483
+ if (pattern !== void 0) {
484
+ sql += patternStub(hasWhere, void 0, "l.lanname");
485
+ hasWhere = true;
486
+ }
487
+ if (!showSystem && pattern === void 0) {
488
+ sql += "WHERE l.lanplcallfoid != 0\n";
489
+ hasWhere = true;
490
+ }
491
+ sql += orderBy("1");
492
+ return {
493
+ sql,
494
+ params: params(),
495
+ description: "List of languages"
496
+ };
497
+ };
498
+ const listDomains = (opts) => {
499
+ const { verbose, showSystem, pattern } = opts;
500
+ let sql = "SELECT n.nspname as \"Schema\",\n t.typname as \"Name\",\n pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"Type\",\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"Collation\",\n CASE WHEN t.typnotnull THEN 'not null' END as \"Nullable\",\n t.typdefault as \"Default\",\n pg_catalog.array_to_string(ARRAY(\n SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid AND r.contype = 'c' ORDER BY r.conname\n ), ' ') as \"Check\"";
501
+ if (verbose) {
502
+ sql += ",\n " + aclColumn("t.typacl");
503
+ sql += ",\n d.description as \"Description\"";
504
+ }
505
+ sql += "\nFROM pg_catalog.pg_type t\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n";
506
+ if (verbose) sql += " LEFT JOIN pg_catalog.pg_description d ON d.classoid = t.tableoid AND d.objoid = t.oid AND d.objsubid = 0\n";
507
+ sql += "WHERE t.typtype = 'd'\n";
508
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
509
+ sql += patternStub(true, "n.nspname", "t.typname");
510
+ sql += orderBy("1, 2");
511
+ return {
512
+ sql,
513
+ params: params(),
514
+ description: "List of domains"
515
+ };
516
+ };
517
+ const listConversions = (opts) => {
518
+ const { verbose, showSystem, pattern } = opts;
519
+ let sql = "SELECT n.nspname AS \"Schema\",\n c.conname AS \"Name\",\n pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"Source\",\n pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"Destination\",\n CASE WHEN c.condefault THEN 'yes' ELSE 'no' END AS \"Default?\"";
520
+ if (verbose) sql += ",\n d.description AS \"Description\"";
521
+ sql += "\nFROM pg_catalog.pg_conversion c\n JOIN pg_catalog.pg_namespace n ON n.oid = c.connamespace\n";
522
+ if (verbose) sql += "LEFT JOIN pg_catalog.pg_description d ON d.classoid = c.tableoid AND d.objoid = c.oid AND d.objsubid = 0\n";
523
+ sql += "WHERE true\n";
524
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
525
+ sql += patternStub(true, "n.nspname", "c.conname");
526
+ sql += orderBy("1, 2");
527
+ return {
528
+ sql,
529
+ params: params(),
530
+ description: "List of conversions"
531
+ };
532
+ };
533
+ const describeConfigurationParameters = (opts) => {
534
+ const { verbose, pattern, serverVersion } = opts;
535
+ let sql = "SELECT s.name AS \"Parameter\", pg_catalog.current_setting(s.name) AS \"Value\"";
536
+ if (verbose) {
537
+ sql += ", s.vartype AS \"Type\", s.context AS \"Context\", ";
538
+ if (serverAtLeast(serverVersion, 15e4)) sql += aclColumn("p.paracl");
539
+ else sql += "NULL AS \"Access privileges\"";
540
+ }
541
+ sql += "\nFROM pg_catalog.pg_settings s\n";
542
+ if (verbose && serverAtLeast(serverVersion, 15e4)) sql += " LEFT JOIN pg_catalog.pg_parameter_acl p ON pg_catalog.lower(s.name) = p.parname\n";
543
+ if (pattern !== void 0) sql += patternStub(false, void 0, "pg_catalog.lower(s.name)");
544
+ else sql += "WHERE s.source <> 'default' AND s.setting IS DISTINCT FROM s.boot_val\n";
545
+ sql += orderBy("1");
546
+ return {
547
+ sql,
548
+ params: params(),
549
+ description: pattern !== void 0 ? "List of configuration parameters" : "List of non-default configuration parameters"
550
+ };
551
+ };
552
+ const listEventTriggers = (opts) => {
553
+ const { verbose } = opts;
554
+ let sql = "SELECT evtname as \"Name\", evtevent as \"Event\", pg_catalog.pg_get_userbyid(e.evtowner) as \"Owner\",\n case evtenabled when 'O' then 'enabled' when 'R' then 'replica' when 'A' then 'always' when 'D' then 'disabled' end as \"Enabled\",\n e.evtfoid::pg_catalog.regproc as \"Function\", pg_catalog.array_to_string(array(select x from pg_catalog.unnest(evttags) as t(x)), ', ') as \"Tags\"";
555
+ if (verbose) sql += ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"Description\"";
556
+ sql += "\nFROM pg_catalog.pg_event_trigger e\n";
557
+ sql += patternStub(false, void 0, "evtname");
558
+ sql += orderBy("1");
559
+ return {
560
+ sql,
561
+ params: params(),
562
+ description: "List of event triggers"
563
+ };
564
+ };
565
+ const listExtendedStats = (opts) => {
566
+ const { verbose, serverVersion } = opts;
567
+ if (serverLess(serverVersion, 1e5)) return {
568
+ sql: "/* server < 10 does not support extended statistics */ SELECT 1 WHERE false;",
569
+ params: [],
570
+ description: "List of extended statistics"
571
+ };
572
+ let sql = "SELECT\nes.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS \"Schema\",\nes.stxname AS \"Name\",\n";
573
+ if (serverAtLeast(serverVersion, 14e4)) sql += "pg_catalog.format('%s FROM %s', pg_catalog.pg_get_statisticsobjdef_columns(es.oid), es.stxrelid::pg_catalog.regclass) AS \"Definition\"";
574
+ else sql += "pg_catalog.format('%s FROM %s',\n (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ')\n FROM pg_catalog.unnest(es.stxkeys) s(attnum)\n JOIN pg_catalog.pg_attribute a\n ON (es.stxrelid = a.attrelid AND a.attnum = s.attnum AND NOT a.attisdropped)),\nes.stxrelid::pg_catalog.regclass) AS \"Definition\"";
575
+ sql += ",\nCASE WHEN 'd' = any(es.stxkind) THEN 'defined' END AS \"Ndistinct\",\nCASE WHEN 'f' = any(es.stxkind) THEN 'defined' END AS \"Dependencies\"";
576
+ if (serverAtLeast(serverVersion, 12e4)) sql += ",\nCASE WHEN 'm' = any(es.stxkind) THEN 'defined' END AS \"MCV\"";
577
+ if (verbose) sql += ", \npg_catalog.obj_description(oid, 'pg_statistic_ext') AS \"Description\"";
578
+ sql += "\nFROM pg_catalog.pg_statistic_ext es\n";
579
+ sql += patternStub(false, "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname");
580
+ sql += orderBy("1, 2");
581
+ return {
582
+ sql,
583
+ params: params(),
584
+ description: "List of extended statistics"
585
+ };
586
+ };
587
+ const listCasts = (opts) => {
588
+ const { verbose, serverVersion } = opts;
589
+ let sql = "SELECT pg_catalog.format_type(castsource, NULL) AS \"Source type\",\n pg_catalog.format_type(casttarget, NULL) AS \"Target type\",\n CASE WHEN c.castmethod = 'b' THEN '(binary coercible)'\n WHEN c.castmethod = 'i' THEN '(with inout)'\n ELSE p.proname\n END AS \"Function\",\n CASE WHEN c.castcontext = 'e' THEN 'no'\n WHEN c.castcontext = 'a' THEN 'in assignment'\n ELSE 'yes'\n END AS \"Implicit?\"";
590
+ if (verbose) {
591
+ if (serverAtLeast(serverVersion, 18e4)) sql += ",\n CASE WHEN p.proleakproof THEN 'yes' ELSE 'no' END AS \"Leakproof?\"";
592
+ sql += ",\n d.description AS \"Description\"";
593
+ }
594
+ sql += "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n ON c.castfunc = p.oid\n LEFT JOIN pg_catalog.pg_type ts ON c.castsource = ts.oid\n LEFT JOIN pg_catalog.pg_namespace ns ON ns.oid = ts.typnamespace\n LEFT JOIN pg_catalog.pg_type tt ON c.casttarget = tt.oid\n LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = tt.typnamespace\n";
595
+ if (verbose) sql += " LEFT JOIN pg_catalog.pg_description d ON d.classoid = c.tableoid AND d.objoid = c.oid AND d.objsubid = 0\n";
596
+ sql += "WHERE ( (true";
597
+ sql += patternStub(true, "ns.nspname", "ts.typname");
598
+ sql += ") OR (true";
599
+ sql += patternStub(true, "nt.nspname", "tt.typname");
600
+ sql += ") )\n";
601
+ sql += orderBy("1, 2");
602
+ return {
603
+ sql,
604
+ params: params(),
605
+ description: "List of casts"
606
+ };
607
+ };
608
+ const listCollations = (opts) => {
609
+ const { verbose, showSystem, pattern, serverVersion } = opts;
610
+ let sql = "SELECT\n n.nspname AS \"Schema\",\n c.collname AS \"Name\",\n";
611
+ if (serverAtLeast(serverVersion, 1e5)) sql += " CASE c.collprovider WHEN 'd' THEN 'default' WHEN 'b' THEN 'builtin' WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS \"Provider\",\n";
612
+ else sql += " 'libc' AS \"Provider\",\n";
613
+ sql += " c.collcollate AS \"Collate\",\n c.collctype AS \"Ctype\",\n";
614
+ if (serverAtLeast(serverVersion, 17e4)) sql += " c.colllocale AS \"Locale\",\n";
615
+ else if (serverAtLeast(serverVersion, 15e4)) sql += " c.colliculocale AS \"Locale\",\n";
616
+ else sql += " c.collcollate AS \"Locale\",\n";
617
+ if (serverAtLeast(serverVersion, 16e4)) sql += " c.collicurules AS \"ICU Rules\",\n";
618
+ else sql += " NULL AS \"ICU Rules\",\n";
619
+ if (serverAtLeast(serverVersion, 12e4)) sql += " CASE WHEN c.collisdeterministic THEN 'yes' ELSE 'no' END AS \"Deterministic?\"";
620
+ else sql += " 'yes' AS \"Deterministic?\"";
621
+ if (verbose) sql += ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"Description\"";
622
+ sql += "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\nWHERE n.oid = c.collnamespace\n";
623
+ if (!showSystem && pattern === void 0) sql += " AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n";
624
+ sql += " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n";
625
+ sql += patternStub(true, "n.nspname", "c.collname");
626
+ sql += orderBy("1, 2");
627
+ return {
628
+ sql,
629
+ params: params(),
630
+ description: "List of collations"
631
+ };
632
+ };
633
+ const listSchemas = (opts) => {
634
+ const { verbose, showSystem, pattern } = opts;
635
+ let sql = "SELECT n.nspname AS \"Name\",\n pg_catalog.pg_get_userbyid(n.nspowner) AS \"Owner\"";
636
+ if (verbose) {
637
+ sql += ",\n " + aclColumn("n.nspacl");
638
+ sql += ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"Description\"";
639
+ }
640
+ sql += "\nFROM pg_catalog.pg_namespace n\n";
641
+ if (!showSystem && pattern === void 0) sql += "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n";
642
+ sql += patternStub(!showSystem && pattern === void 0, void 0, "n.nspname");
643
+ sql += orderBy("1");
644
+ return {
645
+ sql,
646
+ params: params(),
647
+ description: "List of schemas"
648
+ };
649
+ };
650
+ const listTSParsers = (opts) => {
651
+ const { verbose } = opts;
652
+ if (verbose) {
653
+ let sql = "SELECT p.oid,\n n.nspname,\n p.prsname\nFROM pg_catalog.pg_ts_parser p\nLEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n";
654
+ sql += patternStub(false, "n.nspname", "p.prsname");
655
+ sql += orderBy("1, 2");
656
+ return {
657
+ sql,
658
+ params: params(),
659
+ description: "List of text search parsers (verbose)"
660
+ };
661
+ }
662
+ let sql = "SELECT\n n.nspname as \"Schema\",\n p.prsname as \"Name\",\n pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"Description\"\nFROM pg_catalog.pg_ts_parser p\nLEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n";
663
+ sql += patternStub(false, "n.nspname", "p.prsname");
664
+ sql += orderBy("1, 2");
665
+ return {
666
+ sql,
667
+ params: params(),
668
+ description: "List of text search parsers"
669
+ };
1642
670
  };
1643
671
  /**
1644
- * Per-extension contents (`\dx+ foo`). Caller binds the extension oid.
1645
- */
1646
- export const listOneExtensionContents = (opts) => {
1647
- const sql = 'SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS "Object description"\n' +
1648
- "FROM pg_catalog.pg_depend\n" +
1649
- `WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '${opts.oid}' AND deptype = 'e'\n` +
1650
- "ORDER BY 1;";
1651
- return { sql, params: [], description: "Objects in extension" };
1652
- };
1653
- /* ------------------------------------------------------------------ */
1654
- /* \dRp — listPublications / describePublications (\dRp+) */
1655
- /* ------------------------------------------------------------------ */
1656
- export const listPublications = (opts) => {
1657
- const { serverVersion } = opts;
1658
- if (serverLess(serverVersion, PG_10)) {
1659
- return {
1660
- sql: "/* server < 10 does not support publications */ SELECT 1 WHERE false;",
1661
- params: [],
1662
- description: "List of publications",
1663
- };
1664
- }
1665
- let sql = 'SELECT pubname AS "Name",\n' +
1666
- ' pg_catalog.pg_get_userbyid(pubowner) AS "Owner",\n' +
1667
- ' puballtables AS "All tables"';
1668
- if (serverAtLeast(serverVersion, PG_19)) {
1669
- sql += ',\n puballsequences AS "All sequences"';
1670
- }
1671
- sql +=
1672
- ',\n pubinsert AS "Inserts",\n pubupdate AS "Updates",\n pubdelete AS "Deletes"';
1673
- if (serverAtLeast(serverVersion, PG_11)) {
1674
- sql += ',\n pubtruncate AS "Truncates"';
1675
- }
1676
- // PG 18 added `pg_publication.pubgencols`, surfaced as "Generated
1677
- // columns" in `\dRp`. Values: 'n' = none, 's' = stored.
1678
- if (serverAtLeast(serverVersion, PG_18)) {
1679
- sql +=
1680
- ",\n (CASE pubgencols " +
1681
- "WHEN 'n' THEN 'none' WHEN 's' THEN 'stored' END) AS \"Generated columns\"";
1682
- }
1683
- if (serverAtLeast(serverVersion, PG_13)) {
1684
- sql += ',\n pubviaroot AS "Via root"';
1685
- }
1686
- sql += "\nFROM pg_catalog.pg_publication\n";
1687
- sql += patternStub(false, undefined, "pubname");
1688
- sql += orderBy("1");
1689
- return { sql, params: params(), description: "List of publications" };
672
+ * Per-parser detail (one row per phase). Caller binds the parser oid.
673
+ * Two queries upstream; we emit both as a UNION via a single string.
674
+ */
675
+ const describeOneTSParser = (opts) => {
676
+ const { oid } = opts;
677
+ return {
678
+ sql: `SELECT 'Start parse' AS "Method",
679
+ p.prsstart::pg_catalog.regproc AS "Function",
680
+ pg_catalog.obj_description(p.prsstart, 'pg_proc') as "Description"
681
+ FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}'\nUNION ALL\nSELECT 'Get next token', p.prstoken::pg_catalog.regproc, pg_catalog.obj_description(p.prstoken, 'pg_proc')\n FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}'\nUNION ALL\nSELECT 'End parse', p.prsend::pg_catalog.regproc, pg_catalog.obj_description(p.prsend, 'pg_proc')\n FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}'\nUNION ALL\nSELECT 'Get headline', p.prsheadline::pg_catalog.regproc, pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}'\nUNION ALL\nSELECT 'Get token types', p.prslextype::pg_catalog.regproc, pg_catalog.obj_description(p.prslextype, 'pg_proc')\n FROM pg_catalog.pg_ts_parser p WHERE p.oid = '${oid}';`,
682
+ params: [],
683
+ description: "Text search parser details"
684
+ };
685
+ };
686
+ const listTSDictionaries = (opts) => {
687
+ const { verbose } = opts;
688
+ let sql = "SELECT\n n.nspname as \"Schema\",\n d.dictname as \"Name\",\n";
689
+ if (verbose) sql += " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM\n pg_catalog.pg_ts_template t\n LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace\n WHERE d.dicttemplate = t.oid ) AS \"Template\",\n d.dictinitoption as \"Init options\",\n";
690
+ sql += " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"Description\"\nFROM pg_catalog.pg_ts_dict d\nLEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n";
691
+ sql += patternStub(false, "n.nspname", "d.dictname");
692
+ sql += orderBy("1, 2");
693
+ return {
694
+ sql,
695
+ params: params(),
696
+ description: "List of text search dictionaries"
697
+ };
698
+ };
699
+ const listTSTemplates = (opts) => {
700
+ const { verbose } = opts;
701
+ let sql;
702
+ if (verbose) sql = "SELECT\n n.nspname AS \"Schema\",\n t.tmplname AS \"Name\",\n t.tmplinit::pg_catalog.regproc AS \"Init\",\n t.tmpllexize::pg_catalog.regproc AS \"Lexize\",\n pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"Description\"\n";
703
+ else sql = "SELECT\n n.nspname AS \"Schema\",\n t.tmplname AS \"Name\",\n pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"Description\"\n";
704
+ sql += "FROM pg_catalog.pg_ts_template t\nLEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n";
705
+ sql += patternStub(false, "n.nspname", "t.tmplname");
706
+ sql += orderBy("1, 2");
707
+ return {
708
+ sql,
709
+ params: params(),
710
+ description: "List of text search templates"
711
+ };
712
+ };
713
+ const listTSConfigs = (opts) => {
714
+ const { verbose } = opts;
715
+ if (verbose) {
716
+ let sql = "SELECT c.oid, c.cfgname, n.nspname, p.prsname, np.nspname as pnspname\nFROM pg_catalog.pg_ts_config c\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace,\n pg_catalog.pg_ts_parser p\n LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace\nWHERE p.oid = c.cfgparser\n";
717
+ sql += patternStub(true, "n.nspname", "c.cfgname");
718
+ sql += orderBy("3, 2");
719
+ return {
720
+ sql,
721
+ params: params(),
722
+ description: "List of text search configurations (verbose)"
723
+ };
724
+ }
725
+ let sql = "SELECT\n n.nspname as \"Schema\",\n c.cfgname as \"Name\",\n pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"Description\"\nFROM pg_catalog.pg_ts_config c\nLEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace\n";
726
+ sql += patternStub(false, "n.nspname", "c.cfgname");
727
+ sql += orderBy("1, 2");
728
+ return {
729
+ sql,
730
+ params: params(),
731
+ description: "List of text search configurations"
732
+ };
733
+ };
734
+ const listForeignDataWrappers = (opts) => {
735
+ const { verbose } = opts;
736
+ let sql = "SELECT fdw.fdwname AS \"Name\",\n pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"Owner\",\n fdw.fdwhandler::pg_catalog.regproc AS \"Handler\",\n fdw.fdwvalidator::pg_catalog.regproc AS \"Validator\"";
737
+ if (verbose) {
738
+ sql += ",\n " + aclColumn("fdwacl");
739
+ sql += ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE\n '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM pg_catalog.pg_options_to_table(fdwoptions)), ', ') || ')'\n END AS \"FDW options\",\n d.description AS \"Description\" ";
740
+ }
741
+ sql += "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n";
742
+ if (verbose) sql += "LEFT JOIN pg_catalog.pg_description d ON d.classoid = fdw.tableoid AND d.objoid = fdw.oid AND d.objsubid = 0\n";
743
+ sql += patternStub(false, void 0, "fdwname");
744
+ sql += orderBy("1");
745
+ return {
746
+ sql,
747
+ params: params(),
748
+ description: "List of foreign-data wrappers"
749
+ };
750
+ };
751
+ const listForeignServers = (opts) => {
752
+ const { verbose } = opts;
753
+ let sql = "SELECT s.srvname AS \"Name\",\n pg_catalog.pg_get_userbyid(s.srvowner) AS \"Owner\",\n f.fdwname AS \"Foreign-data wrapper\"";
754
+ if (verbose) {
755
+ sql += ",\n " + aclColumn("s.srvacl");
756
+ sql += ",\n s.srvtype AS \"Type\",\n s.srvversion AS \"Version\",\n CASE WHEN srvoptions IS NULL THEN '' ELSE\n '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM pg_catalog.pg_options_to_table(srvoptions)), ', ') || ')'\n END AS \"FDW options\",\n d.description AS \"Description\"";
757
+ }
758
+ sql += "\nFROM pg_catalog.pg_foreign_server s\n JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n";
759
+ if (verbose) sql += "LEFT JOIN pg_catalog.pg_description d ON d.classoid = s.tableoid AND d.objoid = s.oid AND d.objsubid = 0\n";
760
+ sql += patternStub(false, void 0, "s.srvname");
761
+ sql += orderBy("1");
762
+ return {
763
+ sql,
764
+ params: params(),
765
+ description: "List of foreign servers"
766
+ };
767
+ };
768
+ const listUserMappings = (opts) => {
769
+ const { verbose } = opts;
770
+ let sql = "SELECT um.srvname AS \"Server\",\n um.usename AS \"User name\"";
771
+ if (verbose) sql += ",\n CASE WHEN umoptions IS NULL THEN '' ELSE\n '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM pg_catalog.pg_options_to_table(umoptions)), ', ') || ')'\n END AS \"FDW options\"";
772
+ sql += "\nFROM pg_catalog.pg_user_mappings um\n";
773
+ sql += patternStub(false, void 0, "um.srvname");
774
+ sql += orderBy("1, 2");
775
+ return {
776
+ sql,
777
+ params: params(),
778
+ description: "List of user mappings"
779
+ };
780
+ };
781
+ const listForeignTables = (opts) => {
782
+ const { verbose } = opts;
783
+ let sql = "SELECT n.nspname AS \"Schema\",\n c.relname AS \"Table\",\n s.srvname AS \"Server\"";
784
+ if (verbose) sql += ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE\n '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM pg_catalog.pg_options_to_table(ftoptions)), ', ') || ')'\n END AS \"FDW options\",\n d.description AS \"Description\"";
785
+ sql += "\nFROM pg_catalog.pg_foreign_table ft\n INNER JOIN pg_catalog.pg_class c ON c.oid = ft.ftrelid\n INNER JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n INNER JOIN pg_catalog.pg_foreign_server s ON s.oid = ft.ftserver\n";
786
+ if (verbose) sql += " LEFT JOIN pg_catalog.pg_description d ON d.classoid = c.tableoid AND d.objoid = c.oid AND d.objsubid = 0\n";
787
+ sql += patternStub(false, "n.nspname", "c.relname");
788
+ sql += orderBy("1, 2");
789
+ return {
790
+ sql,
791
+ params: params(),
792
+ description: "List of foreign tables"
793
+ };
794
+ };
795
+ const listExtensions = (opts) => {
796
+ const { serverVersion } = opts;
797
+ const hasDefaultVersion = serverAtLeast(serverVersion, PG_18);
798
+ let sql = "SELECT e.extname AS \"Name\", e.extversion AS \"Version\", ";
799
+ if (hasDefaultVersion) sql += "ae.default_version AS \"Default version\",";
800
+ sql += "n.nspname AS \"Schema\", d.description AS \"Description\"\nFROM pg_catalog.pg_extension e LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace LEFT JOIN pg_catalog.pg_description d ON d.objoid = e.oid AND d.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass";
801
+ if (hasDefaultVersion) sql += " LEFT JOIN pg_catalog.pg_available_extensions() ae(name, default_version, comment) ON ae.name = e.extname";
802
+ sql += "\n";
803
+ sql += patternStub(false, void 0, "e.extname");
804
+ sql += orderBy("1");
805
+ return {
806
+ sql,
807
+ params: params(),
808
+ description: "List of installed extensions"
809
+ };
810
+ };
811
+ const listExtensionContents = () => {
812
+ let sql = "SELECT e.extname, e.oid\nFROM pg_catalog.pg_extension e\n";
813
+ sql += patternStub(false, void 0, "e.extname");
814
+ sql += orderBy("1");
815
+ return {
816
+ sql,
817
+ params: params(),
818
+ description: "Get matching extensions to list contents for"
819
+ };
1690
820
  };
1691
821
  /**
1692
- * Detail-lookup query for `\dRp+`. Output columns match the unmodified
1693
- * `describePublications` upstream lookup (positional columns are then
1694
- * fanned-out into per-publication detail blocks by WP-20).
1695
- */
1696
- export const describePublications = (opts) => {
1697
- const { serverVersion } = opts;
1698
- if (serverLess(serverVersion, PG_10)) {
1699
- return {
1700
- sql: "/* server < 10 does not support publications */ SELECT 1 WHERE false;",
1701
- params: [],
1702
- description: "Details of publications",
1703
- };
1704
- }
1705
- const hasSeq = serverAtLeast(serverVersion, PG_19);
1706
- const hasTrunc = serverAtLeast(serverVersion, PG_11);
1707
- const hasGen = serverAtLeast(serverVersion, PG_18);
1708
- const hasViaRoot = serverAtLeast(serverVersion, PG_13);
1709
- let sql = "SELECT oid, pubname,\n pg_catalog.pg_get_userbyid(pubowner) AS owner,\n puballtables";
1710
- sql += hasSeq ? ", puballsequences" : ", false AS puballsequences";
1711
- sql += ", pubinsert, pubupdate, pubdelete";
1712
- sql += hasTrunc ? ", pubtruncate" : ", false AS pubtruncate";
1713
- if (hasGen) {
1714
- sql +=
1715
- ", (CASE pubgencols WHEN 'n' THEN 'none' WHEN 's' THEN 'stored' END) AS pubgencols";
1716
- }
1717
- else {
1718
- sql += ", 'none' AS pubgencols";
1719
- }
1720
- sql += hasViaRoot ? ", pubviaroot" : ", false AS pubviaroot";
1721
- sql += ", pg_catalog.obj_description(oid, 'pg_publication')";
1722
- sql += "\nFROM pg_catalog.pg_publication\n";
1723
- sql += patternStub(false, undefined, "pubname");
1724
- sql += orderBy("2");
1725
- return { sql, params: params(), description: "Details of publications" };
1726
- };
1727
- /* ------------------------------------------------------------------ */
1728
- /* \dRs — describeSubscriptions */
1729
- /* ------------------------------------------------------------------ */
1730
- export const describeSubscriptions = (opts) => {
1731
- const { verbose, serverVersion } = opts;
1732
- if (serverLess(serverVersion, PG_10)) {
1733
- return {
1734
- sql: "/* server < 10 does not support subscriptions */ SELECT 1 WHERE false;",
1735
- params: [],
1736
- description: "List of subscriptions",
1737
- };
1738
- }
1739
- let sql = 'SELECT subname AS "Name"\n' +
1740
- ', pg_catalog.pg_get_userbyid(subowner) AS "Owner"\n' +
1741
- ', subenabled AS "Enabled"\n' +
1742
- ', subpublications AS "Publication"\n';
1743
- if (verbose) {
1744
- if (serverAtLeast(serverVersion, PG_14)) {
1745
- sql += ', subbinary AS "Binary"\n';
1746
- if (serverAtLeast(serverVersion, PG_16)) {
1747
- sql +=
1748
- ", (CASE substream\n" +
1749
- " WHEN 'f' THEN 'off'\n" +
1750
- " WHEN 't' THEN 'on'\n" +
1751
- " WHEN 'p' THEN 'parallel'\n" +
1752
- ' END) AS "Streaming"\n';
1753
- }
1754
- else {
1755
- sql += ', substream AS "Streaming"\n';
1756
- }
1757
- }
1758
- if (serverAtLeast(serverVersion, PG_15)) {
1759
- sql +=
1760
- ', subtwophasestate AS "Two-phase commit"\n' +
1761
- ', subdisableonerr AS "Disable on error"\n';
1762
- }
1763
- if (serverAtLeast(serverVersion, PG_16)) {
1764
- sql +=
1765
- ', suborigin AS "Origin"\n' +
1766
- ', subpasswordrequired AS "Password required"\n' +
1767
- ', subrunasowner AS "Run as owner?"\n';
1768
- }
1769
- if (serverAtLeast(serverVersion, PG_17)) {
1770
- sql += ', subfailover AS "Failover"\n';
1771
- }
1772
- // PG 19 added foreign-server-backed subscriptions and dead-tuple
1773
- // retention. Upstream surfaces these five columns in `\dRs+`:
1774
- // the source server name (resolved from subserver), the retention
1775
- // toggles/limits, and the WAL-receiver timeout (placed after
1776
- // Conninfo, matching describe.c).
1777
- if (serverAtLeast(serverVersion, PG_19)) {
1778
- sql +=
1779
- ', (select srvname from pg_foreign_server where oid=subserver) AS "Server"\n' +
1780
- ', subretaindeadtuples AS "Retain dead tuples"\n' +
1781
- ', submaxretention AS "Max retention duration"\n' +
1782
- ', subretentionactive AS "Retention active"\n';
1783
- }
1784
- sql +=
1785
- ', subsynccommit AS "Synchronous commit"\n' +
1786
- ', subconninfo AS "Conninfo"\n';
1787
- if (serverAtLeast(serverVersion, PG_19)) {
1788
- sql += ', subwalrcvtimeout AS "Receiver timeout"\n';
1789
- }
1790
- if (serverAtLeast(serverVersion, PG_15)) {
1791
- sql += ', subskiplsn AS "Skip LSN"\n';
1792
- }
1793
- sql +=
1794
- ", pg_catalog.obj_description(oid, 'pg_subscription') AS \"Description\"\n";
1795
- }
1796
- sql +=
1797
- "FROM pg_catalog.pg_subscription\n" +
1798
- "WHERE subdbid = (SELECT oid FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database())";
1799
- sql += patternStub(true, undefined, "subname");
1800
- sql += orderBy("1");
1801
- return { sql, params: params(), description: "List of subscriptions" };
1802
- };
1803
- export const listOperatorClasses = (opts) => {
1804
- const { verbose } = opts;
1805
- let sql = 'SELECT\n am.amname AS "AM",\n' +
1806
- ' pg_catalog.format_type(c.opcintype, NULL) AS "Input type",\n' +
1807
- " CASE\n" +
1808
- " WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n" +
1809
- " THEN pg_catalog.format_type(c.opckeytype, NULL)\n" +
1810
- " ELSE NULL\n" +
1811
- ' END AS "Storage type",\n' +
1812
- " CASE\n" +
1813
- " WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n" +
1814
- " THEN pg_catalog.format('%I', c.opcname)\n" +
1815
- " ELSE pg_catalog.format('%I.%I', n.nspname, c.opcname)\n" +
1816
- ' END AS "Operator class",\n' +
1817
- " (CASE WHEN c.opcdefault THEN 'yes' ELSE 'no' END) AS \"Default?\"";
1818
- if (verbose) {
1819
- sql +=
1820
- ",\n CASE\n" +
1821
- " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n" +
1822
- " THEN pg_catalog.format('%I', of.opfname)\n" +
1823
- " ELSE pg_catalog.format('%I.%I', ofn.nspname, of.opfname)\n" +
1824
- ' END AS "Operator family",\n' +
1825
- ' pg_catalog.pg_get_userbyid(c.opcowner) AS "Owner"\n';
1826
- }
1827
- sql +=
1828
- "\nFROM pg_catalog.pg_opclass c\n" +
1829
- " LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n" +
1830
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n" +
1831
- " LEFT JOIN pg_catalog.pg_type t ON t.oid = c.opcintype\n" +
1832
- " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n";
1833
- if (verbose) {
1834
- sql +=
1835
- " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n" +
1836
- " LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n";
1837
- }
1838
- let hasWhere = false;
1839
- if (opts.amPattern !== undefined) {
1840
- sql += patternStub(hasWhere, undefined, "am.amname");
1841
- hasWhere = true;
1842
- }
1843
- if (opts.typePattern !== undefined) {
1844
- sql += patternStub(hasWhere, "tn.nspname", "t.typname");
1845
- }
1846
- sql += orderBy("1, 2, 4");
1847
- const ps = [];
1848
- if (opts.amPattern !== undefined)
1849
- ps.push(opts.amPattern);
1850
- if (opts.typePattern !== undefined)
1851
- ps.push(opts.typePattern);
1852
- return { sql, params: ps, description: "List of operator classes" };
1853
- };
1854
- /* ------------------------------------------------------------------ */
1855
- /* \dAf — listOperatorFamilies */
1856
- /* ------------------------------------------------------------------ */
1857
- export const listOperatorFamilies = (opts) => {
1858
- const { verbose } = opts;
1859
- let sql = 'SELECT\n am.amname AS "AM",\n' +
1860
- " CASE\n" +
1861
- " WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n" +
1862
- " THEN pg_catalog.format('%I', f.opfname)\n" +
1863
- " ELSE pg_catalog.format('%I.%I', n.nspname, f.opfname)\n" +
1864
- ' END AS "Operator family",\n' +
1865
- " (SELECT\n" +
1866
- " pg_catalog.string_agg(pg_catalog.format_type(oc.opcintype, NULL), ', ')\n" +
1867
- " FROM pg_catalog.pg_opclass oc\n" +
1868
- ' WHERE oc.opcfamily = f.oid) "Applicable types"';
1869
- if (verbose) {
1870
- sql += ',\n pg_catalog.pg_get_userbyid(f.opfowner) AS "Owner"\n';
1871
- }
1872
- sql +=
1873
- "\nFROM pg_catalog.pg_opfamily f\n" +
1874
- " LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n" +
1875
- " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace\n";
1876
- let hasWhere = false;
1877
- if (opts.amPattern !== undefined) {
1878
- sql += patternStub(hasWhere, undefined, "am.amname");
1879
- hasWhere = true;
1880
- }
1881
- if (opts.typePattern !== undefined) {
1882
- sql +=
1883
- ` ${hasWhere ? "AND" : "WHERE"} EXISTS (\n` +
1884
- " SELECT 1 FROM pg_catalog.pg_type t\n" +
1885
- " JOIN pg_catalog.pg_opclass oc ON oc.opcintype = t.oid\n" +
1886
- " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n" +
1887
- " WHERE oc.opcfamily = f.oid\n" +
1888
- patternStub(true, "tn.nspname", "t.typname") +
1889
- " )\n";
1890
- }
1891
- sql += orderBy("1, 2");
1892
- const ps = [];
1893
- if (opts.amPattern !== undefined)
1894
- ps.push(opts.amPattern);
1895
- if (opts.typePattern !== undefined)
1896
- ps.push(opts.typePattern);
1897
- return { sql, params: ps, description: "List of operator families" };
1898
- };
1899
- export const listOpFamilyOperators = (opts) => {
1900
- const { verbose, serverVersion } = opts;
1901
- let sql = 'SELECT\n am.amname AS "AM",\n' +
1902
- " CASE\n" +
1903
- " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n" +
1904
- " THEN pg_catalog.format('%I', of.opfname)\n" +
1905
- " ELSE pg_catalog.format('%I.%I', nsf.nspname, of.opfname)\n" +
1906
- ' END AS "Operator family",\n' +
1907
- ' o.amopopr::pg_catalog.regoperator AS "Operator"\n,' +
1908
- ' o.amopstrategy AS "Strategy",\n' +
1909
- " CASE o.amoppurpose\n" +
1910
- " WHEN 'o' THEN 'ordering'\n" +
1911
- " WHEN 's' THEN 'search'\n" +
1912
- ' END AS "Purpose"\n';
1913
- if (verbose) {
1914
- sql += ', ofs.opfname AS "Sort opfamily"';
1915
- // PG 18 added "Leakproof?" to verbose `\dAo+`. Same pattern as
1916
- // `\df+` — the operator's underlying function's proleakproof.
1917
- if (serverAtLeast(serverVersion, PG_18)) {
1918
- sql +=
1919
- ",\n CASE WHEN p.proleakproof THEN 'yes' ELSE 'no' END AS \"Leakproof?\"";
1920
- }
1921
- sql += "\n";
1922
- }
1923
- sql +=
1924
- "FROM pg_catalog.pg_amop o\n" +
1925
- " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n" +
1926
- " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n" +
1927
- " LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n";
1928
- if (verbose) {
1929
- sql +=
1930
- " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n";
1931
- if (serverAtLeast(serverVersion, PG_18)) {
1932
- sql +=
1933
- " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n" +
1934
- " LEFT JOIN pg_catalog.pg_proc p ON p.oid = op.oprcode\n";
1935
- }
1936
- }
1937
- let hasWhere = false;
1938
- if (opts.amPattern !== undefined) {
1939
- sql += patternStub(hasWhere, undefined, "am.amname");
1940
- hasWhere = true;
1941
- }
1942
- if (opts.familyPattern !== undefined) {
1943
- sql += patternStub(hasWhere, "nsf.nspname", "of.opfname");
1944
- }
1945
- sql +=
1946
- "ORDER BY 1, 2,\n" +
1947
- " o.amoplefttype = o.amoprighttype DESC,\n" +
1948
- " pg_catalog.format_type(o.amoplefttype, NULL),\n" +
1949
- " pg_catalog.format_type(o.amoprighttype, NULL),\n" +
1950
- " o.amopstrategy;";
1951
- const ps = [];
1952
- if (opts.amPattern !== undefined)
1953
- ps.push(opts.amPattern);
1954
- if (opts.familyPattern !== undefined)
1955
- ps.push(opts.familyPattern);
1956
- return {
1957
- sql,
1958
- params: ps,
1959
- description: "List of operators of operator families",
1960
- };
1961
- };
1962
- /* ------------------------------------------------------------------ */
1963
- /* \dAp — listOpFamilyFunctions */
1964
- /* ------------------------------------------------------------------ */
1965
- export const listOpFamilyFunctions = (opts) => {
1966
- const { verbose } = opts;
1967
- let sql = 'SELECT\n am.amname AS "AM",\n' +
1968
- " CASE\n" +
1969
- " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n" +
1970
- " THEN pg_catalog.format('%I', of.opfname)\n" +
1971
- " ELSE pg_catalog.format('%I.%I', ns.nspname, of.opfname)\n" +
1972
- ' END AS "Operator family",\n' +
1973
- ' pg_catalog.format_type(ap.amproclefttype, NULL) AS "Registered left type",\n' +
1974
- ' pg_catalog.format_type(ap.amprocrighttype, NULL) AS "Registered right type",\n' +
1975
- ' ap.amprocnum AS "Number"\n';
1976
- sql += verbose
1977
- ? ', ap.amproc::pg_catalog.regprocedure AS "Function"\n'
1978
- : ', p.proname AS "Function"\n';
1979
- sql +=
1980
- "FROM pg_catalog.pg_amproc ap\n" +
1981
- " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n" +
1982
- " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n" +
1983
- " LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n" +
1984
- " LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.oid\n";
1985
- let hasWhere = false;
1986
- if (opts.amPattern !== undefined) {
1987
- sql += patternStub(hasWhere, undefined, "am.amname");
1988
- hasWhere = true;
1989
- }
1990
- if (opts.familyPattern !== undefined) {
1991
- sql += patternStub(hasWhere, "ns.nspname", "of.opfname");
1992
- }
1993
- sql +=
1994
- "ORDER BY 1, 2,\n ap.amproclefttype = ap.amprocrighttype DESC,\n 3, 4, 5;";
1995
- const ps = [];
1996
- if (opts.amPattern !== undefined)
1997
- ps.push(opts.amPattern);
1998
- if (opts.familyPattern !== undefined)
1999
- ps.push(opts.familyPattern);
2000
- return {
2001
- sql,
2002
- params: ps,
2003
- description: "List of support functions of operator families",
2004
- };
2005
- };
2006
- /* ------------------------------------------------------------------ */
2007
- /* \dl / \lo_list — listLargeObjects */
2008
- /* ------------------------------------------------------------------ */
2009
- export const listLargeObjects = (opts) => {
2010
- const { verbose } = opts;
2011
- let sql = 'SELECT oid as "ID",\n pg_catalog.pg_get_userbyid(lomowner) as "Owner",\n ';
2012
- if (verbose) {
2013
- sql += aclColumn("lomacl") + ",\n ";
2014
- }
2015
- sql +=
2016
- "pg_catalog.obj_description(oid, 'pg_largeobject') as \"Description\"\n" +
2017
- "FROM pg_catalog.pg_largeobject_metadata\n" +
2018
- "ORDER BY oid";
2019
- return { sql, params: [], description: "Large objects" };
2020
- };
2021
- /* ------------------------------------------------------------------ */
2022
- /* \sf — show function definition */
2023
- /* \sv — show view definition */
2024
- /* These are command-level (in command.c) but the SQL is trivial and */
2025
- /* belongs with the rest of the describe SQL. */
2026
- /* ------------------------------------------------------------------ */
822
+ * Per-extension contents (`\dx+ foo`). Caller binds the extension oid.
823
+ */
824
+ const listOneExtensionContents = (opts) => {
825
+ return {
826
+ sql: `SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS "Object description"
827
+ FROM pg_catalog.pg_depend
828
+ WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '${opts.oid}' AND deptype = 'e'\nORDER BY 1;`,
829
+ params: [],
830
+ description: "Objects in extension"
831
+ };
832
+ };
833
+ const listPublications = (opts) => {
834
+ const { serverVersion } = opts;
835
+ if (serverLess(serverVersion, 1e5)) return {
836
+ sql: "/* server < 10 does not support publications */ SELECT 1 WHERE false;",
837
+ params: [],
838
+ description: "List of publications"
839
+ };
840
+ let sql = "SELECT pubname AS \"Name\",\n pg_catalog.pg_get_userbyid(pubowner) AS \"Owner\",\n puballtables AS \"All tables\"";
841
+ if (serverAtLeast(serverVersion, 19e4)) sql += ",\n puballsequences AS \"All sequences\"";
842
+ sql += ",\n pubinsert AS \"Inserts\",\n pubupdate AS \"Updates\",\n pubdelete AS \"Deletes\"";
843
+ if (serverAtLeast(serverVersion, 11e4)) sql += ",\n pubtruncate AS \"Truncates\"";
844
+ if (serverAtLeast(serverVersion, 18e4)) sql += ",\n (CASE pubgencols WHEN 'n' THEN 'none' WHEN 's' THEN 'stored' END) AS \"Generated columns\"";
845
+ if (serverAtLeast(serverVersion, 13e4)) sql += ",\n pubviaroot AS \"Via root\"";
846
+ sql += "\nFROM pg_catalog.pg_publication\n";
847
+ sql += patternStub(false, void 0, "pubname");
848
+ sql += orderBy("1");
849
+ return {
850
+ sql,
851
+ params: params(),
852
+ description: "List of publications"
853
+ };
854
+ };
2027
855
  /**
2028
- * Look up function OID by name for `\sf` / `\sf+`. Caller renders
2029
- * `pg_catalog.pg_get_functiondef(oid)` afterwards. The lookup itself
2030
- * uses regprocedure casting via the placeholder; WP-20 will replace.
2031
- */
2032
- export const showFunction = (opts) => {
2033
- // We emit a select returning the function definition by name; upstream
2034
- // psql resolves to oid then calls pg_get_functiondef(oid). Combine.
2035
- const sql = `SELECT pg_catalog.pg_get_functiondef('${opts.name.replace(/'/g, "''")}'::pg_catalog.regprocedure) AS def;`;
2036
- return { sql, params: [], description: "Function definition" };
856
+ * Detail-lookup query for `\dRp+`. Output columns match the unmodified
857
+ * `describePublications` upstream lookup (positional columns are then
858
+ * fanned-out into per-publication detail blocks by WP-20).
859
+ */
860
+ const describePublications = (opts) => {
861
+ const { serverVersion } = opts;
862
+ if (serverLess(serverVersion, 1e5)) return {
863
+ sql: "/* server < 10 does not support publications */ SELECT 1 WHERE false;",
864
+ params: [],
865
+ description: "Details of publications"
866
+ };
867
+ const hasSeq = serverAtLeast(serverVersion, PG_19);
868
+ const hasTrunc = serverAtLeast(serverVersion, PG_11);
869
+ const hasGen = serverAtLeast(serverVersion, PG_18);
870
+ const hasViaRoot = serverAtLeast(serverVersion, PG_13);
871
+ let sql = "SELECT oid, pubname,\n pg_catalog.pg_get_userbyid(pubowner) AS owner,\n puballtables";
872
+ sql += hasSeq ? ", puballsequences" : ", false AS puballsequences";
873
+ sql += ", pubinsert, pubupdate, pubdelete";
874
+ sql += hasTrunc ? ", pubtruncate" : ", false AS pubtruncate";
875
+ if (hasGen) sql += ", (CASE pubgencols WHEN 'n' THEN 'none' WHEN 's' THEN 'stored' END) AS pubgencols";
876
+ else sql += ", 'none' AS pubgencols";
877
+ sql += hasViaRoot ? ", pubviaroot" : ", false AS pubviaroot";
878
+ sql += ", pg_catalog.obj_description(oid, 'pg_publication')";
879
+ sql += "\nFROM pg_catalog.pg_publication\n";
880
+ sql += patternStub(false, void 0, "pubname");
881
+ sql += orderBy("2");
882
+ return {
883
+ sql,
884
+ params: params(),
885
+ description: "Details of publications"
886
+ };
887
+ };
888
+ const describeSubscriptions = (opts) => {
889
+ const { verbose, serverVersion } = opts;
890
+ if (serverLess(serverVersion, 1e5)) return {
891
+ sql: "/* server < 10 does not support subscriptions */ SELECT 1 WHERE false;",
892
+ params: [],
893
+ description: "List of subscriptions"
894
+ };
895
+ let sql = "SELECT subname AS \"Name\"\n, pg_catalog.pg_get_userbyid(subowner) AS \"Owner\"\n, subenabled AS \"Enabled\"\n, subpublications AS \"Publication\"\n";
896
+ if (verbose) {
897
+ if (serverAtLeast(serverVersion, 14e4)) {
898
+ sql += ", subbinary AS \"Binary\"\n";
899
+ if (serverAtLeast(serverVersion, 16e4)) sql += ", (CASE substream\n WHEN 'f' THEN 'off'\n WHEN 't' THEN 'on'\n WHEN 'p' THEN 'parallel'\n END) AS \"Streaming\"\n";
900
+ else sql += ", substream AS \"Streaming\"\n";
901
+ }
902
+ if (serverAtLeast(serverVersion, 15e4)) sql += ", subtwophasestate AS \"Two-phase commit\"\n, subdisableonerr AS \"Disable on error\"\n";
903
+ if (serverAtLeast(serverVersion, 16e4)) sql += ", suborigin AS \"Origin\"\n, subpasswordrequired AS \"Password required\"\n, subrunasowner AS \"Run as owner?\"\n";
904
+ if (serverAtLeast(serverVersion, 17e4)) sql += ", subfailover AS \"Failover\"\n";
905
+ if (serverAtLeast(serverVersion, 19e4)) sql += ", (select srvname from pg_foreign_server where oid=subserver) AS \"Server\"\n, subretaindeadtuples AS \"Retain dead tuples\"\n, submaxretention AS \"Max retention duration\"\n, subretentionactive AS \"Retention active\"\n";
906
+ sql += ", subsynccommit AS \"Synchronous commit\"\n, subconninfo AS \"Conninfo\"\n";
907
+ if (serverAtLeast(serverVersion, 19e4)) sql += ", subwalrcvtimeout AS \"Receiver timeout\"\n";
908
+ if (serverAtLeast(serverVersion, 15e4)) sql += ", subskiplsn AS \"Skip LSN\"\n";
909
+ sql += ", pg_catalog.obj_description(oid, 'pg_subscription') AS \"Description\"\n";
910
+ }
911
+ sql += "FROM pg_catalog.pg_subscription\nWHERE subdbid = (SELECT oid FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database())";
912
+ sql += patternStub(true, void 0, "subname");
913
+ sql += orderBy("1");
914
+ return {
915
+ sql,
916
+ params: params(),
917
+ description: "List of subscriptions"
918
+ };
919
+ };
920
+ const listOperatorClasses = (opts) => {
921
+ const { verbose } = opts;
922
+ let sql = "SELECT\n am.amname AS \"AM\",\n pg_catalog.format_type(c.opcintype, NULL) AS \"Input type\",\n CASE\n WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n THEN pg_catalog.format_type(c.opckeytype, NULL)\n ELSE NULL\n END AS \"Storage type\",\n CASE\n WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n THEN pg_catalog.format('%I', c.opcname)\n ELSE pg_catalog.format('%I.%I', n.nspname, c.opcname)\n END AS \"Operator class\",\n (CASE WHEN c.opcdefault THEN 'yes' ELSE 'no' END) AS \"Default?\"";
923
+ if (verbose) sql += ",\n CASE\n WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n THEN pg_catalog.format('%I', of.opfname)\n ELSE pg_catalog.format('%I.%I', ofn.nspname, of.opfname)\n END AS \"Operator family\",\n pg_catalog.pg_get_userbyid(c.opcowner) AS \"Owner\"\n";
924
+ sql += "\nFROM pg_catalog.pg_opclass c\n LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n LEFT JOIN pg_catalog.pg_type t ON t.oid = c.opcintype\n LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n";
925
+ if (verbose) sql += " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n";
926
+ let hasWhere = false;
927
+ if (opts.amPattern !== void 0) {
928
+ sql += patternStub(hasWhere, void 0, "am.amname");
929
+ hasWhere = true;
930
+ }
931
+ if (opts.typePattern !== void 0) sql += patternStub(hasWhere, "tn.nspname", "t.typname");
932
+ sql += orderBy("1, 2, 4");
933
+ const ps = [];
934
+ if (opts.amPattern !== void 0) ps.push(opts.amPattern);
935
+ if (opts.typePattern !== void 0) ps.push(opts.typePattern);
936
+ return {
937
+ sql,
938
+ params: ps,
939
+ description: "List of operator classes"
940
+ };
941
+ };
942
+ const listOperatorFamilies = (opts) => {
943
+ const { verbose } = opts;
944
+ let sql = "SELECT\n am.amname AS \"AM\",\n CASE\n WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n THEN pg_catalog.format('%I', f.opfname)\n ELSE pg_catalog.format('%I.%I', n.nspname, f.opfname)\n END AS \"Operator family\",\n (SELECT\n pg_catalog.string_agg(pg_catalog.format_type(oc.opcintype, NULL), ', ')\n FROM pg_catalog.pg_opclass oc\n WHERE oc.opcfamily = f.oid) \"Applicable types\"";
945
+ if (verbose) sql += ",\n pg_catalog.pg_get_userbyid(f.opfowner) AS \"Owner\"\n";
946
+ sql += "\nFROM pg_catalog.pg_opfamily f\n LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace\n";
947
+ let hasWhere = false;
948
+ if (opts.amPattern !== void 0) {
949
+ sql += patternStub(hasWhere, void 0, "am.amname");
950
+ hasWhere = true;
951
+ }
952
+ if (opts.typePattern !== void 0) sql += ` ${hasWhere ? "AND" : "WHERE"} EXISTS (\n SELECT 1 FROM pg_catalog.pg_type t
953
+ JOIN pg_catalog.pg_opclass oc ON oc.opcintype = t.oid
954
+ LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace
955
+ WHERE oc.opcfamily = f.oid
956
+ ` + patternStub(true, "tn.nspname", "t.typname") + " )\n";
957
+ sql += orderBy("1, 2");
958
+ const ps = [];
959
+ if (opts.amPattern !== void 0) ps.push(opts.amPattern);
960
+ if (opts.typePattern !== void 0) ps.push(opts.typePattern);
961
+ return {
962
+ sql,
963
+ params: ps,
964
+ description: "List of operator families"
965
+ };
966
+ };
967
+ const listOpFamilyOperators = (opts) => {
968
+ const { verbose, serverVersion } = opts;
969
+ let sql = "SELECT\n am.amname AS \"AM\",\n CASE\n WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n THEN pg_catalog.format('%I', of.opfname)\n ELSE pg_catalog.format('%I.%I', nsf.nspname, of.opfname)\n END AS \"Operator family\",\n o.amopopr::pg_catalog.regoperator AS \"Operator\"\n, o.amopstrategy AS \"Strategy\",\n CASE o.amoppurpose\n WHEN 'o' THEN 'ordering'\n WHEN 's' THEN 'search'\n END AS \"Purpose\"\n";
970
+ if (verbose) {
971
+ sql += ", ofs.opfname AS \"Sort opfamily\"";
972
+ if (serverAtLeast(serverVersion, 18e4)) sql += ",\n CASE WHEN p.proleakproof THEN 'yes' ELSE 'no' END AS \"Leakproof?\"";
973
+ sql += "\n";
974
+ }
975
+ sql += "FROM pg_catalog.pg_amop o\n LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n";
976
+ if (verbose) {
977
+ sql += " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n";
978
+ if (serverAtLeast(serverVersion, 18e4)) sql += " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n LEFT JOIN pg_catalog.pg_proc p ON p.oid = op.oprcode\n";
979
+ }
980
+ let hasWhere = false;
981
+ if (opts.amPattern !== void 0) {
982
+ sql += patternStub(hasWhere, void 0, "am.amname");
983
+ hasWhere = true;
984
+ }
985
+ if (opts.familyPattern !== void 0) sql += patternStub(hasWhere, "nsf.nspname", "of.opfname");
986
+ sql += "ORDER BY 1, 2,\n o.amoplefttype = o.amoprighttype DESC,\n pg_catalog.format_type(o.amoplefttype, NULL),\n pg_catalog.format_type(o.amoprighttype, NULL),\n o.amopstrategy;";
987
+ const ps = [];
988
+ if (opts.amPattern !== void 0) ps.push(opts.amPattern);
989
+ if (opts.familyPattern !== void 0) ps.push(opts.familyPattern);
990
+ return {
991
+ sql,
992
+ params: ps,
993
+ description: "List of operators of operator families"
994
+ };
995
+ };
996
+ const listOpFamilyFunctions = (opts) => {
997
+ const { verbose } = opts;
998
+ let sql = "SELECT\n am.amname AS \"AM\",\n CASE\n WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n THEN pg_catalog.format('%I', of.opfname)\n ELSE pg_catalog.format('%I.%I', ns.nspname, of.opfname)\n END AS \"Operator family\",\n pg_catalog.format_type(ap.amproclefttype, NULL) AS \"Registered left type\",\n pg_catalog.format_type(ap.amprocrighttype, NULL) AS \"Registered right type\",\n ap.amprocnum AS \"Number\"\n";
999
+ sql += verbose ? ", ap.amproc::pg_catalog.regprocedure AS \"Function\"\n" : ", p.proname AS \"Function\"\n";
1000
+ sql += "FROM pg_catalog.pg_amproc ap\n LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.oid\n";
1001
+ let hasWhere = false;
1002
+ if (opts.amPattern !== void 0) {
1003
+ sql += patternStub(hasWhere, void 0, "am.amname");
1004
+ hasWhere = true;
1005
+ }
1006
+ if (opts.familyPattern !== void 0) sql += patternStub(hasWhere, "ns.nspname", "of.opfname");
1007
+ sql += "ORDER BY 1, 2,\n ap.amproclefttype = ap.amprocrighttype DESC,\n 3, 4, 5;";
1008
+ const ps = [];
1009
+ if (opts.amPattern !== void 0) ps.push(opts.amPattern);
1010
+ if (opts.familyPattern !== void 0) ps.push(opts.familyPattern);
1011
+ return {
1012
+ sql,
1013
+ params: ps,
1014
+ description: "List of support functions of operator families"
1015
+ };
1016
+ };
1017
+ const listLargeObjects = (opts) => {
1018
+ const { verbose } = opts;
1019
+ let sql = "SELECT oid as \"ID\",\n pg_catalog.pg_get_userbyid(lomowner) as \"Owner\",\n ";
1020
+ if (verbose) sql += aclColumn("lomacl") + ",\n ";
1021
+ sql += "pg_catalog.obj_description(oid, 'pg_largeobject') as \"Description\"\nFROM pg_catalog.pg_largeobject_metadata\nORDER BY oid";
1022
+ return {
1023
+ sql,
1024
+ params: [],
1025
+ description: "Large objects"
1026
+ };
2037
1027
  };
2038
1028
  /**
2039
- * Look up view definition for `\sv` / `\sv+`.
2040
- */
2041
- export const showView = (opts) => {
2042
- const sql = `SELECT pg_catalog.pg_get_viewdef('${opts.name.replace(/'/g, "''")}'::pg_catalog.regclass, true) AS def;`;
2043
- return { sql, params: [], description: "View definition" };
2044
- };
2045
- /* ------------------------------------------------------------------ */
2046
- /* Per-relation follow-up queries used by describeOneTableDetails. */
2047
- /* Each builder takes a relation oid and returns a DescribeQuery the */
2048
- /* formatter executes after the columns table. These mirror the */
2049
- /* PSQLexec calls in upstream `describe.c` for the same sections. */
2050
- /* ------------------------------------------------------------------ */
1029
+ * Look up function OID by name for `\sf` / `\sf+`. Caller renders
1030
+ * `pg_catalog.pg_get_functiondef(oid)` afterwards. The lookup itself
1031
+ * uses regprocedure casting via the placeholder; WP-20 will replace.
1032
+ */
1033
+ const showFunction = (opts) => {
1034
+ return {
1035
+ sql: `SELECT pg_catalog.pg_get_functiondef('${opts.name.replace(/'/g, "''")}'::pg_catalog.regprocedure) AS def;`,
1036
+ params: [],
1037
+ description: "Function definition"
1038
+ };
1039
+ };
2051
1040
  /**
2052
- * Per-relation table-info query. Returns one row carrying the flags
2053
- * the formatter needs for the optional footer sections: RLS toggles
2054
- * (relrowsecurity, relforcerowsecurity), replica-identity char,
2055
- * partition flag, tablespace oid, access-method oid and pre-joined
2056
- * pg_tablespace.spcname / pg_am.amname so the formatter doesn't have
2057
- * to fan out for those.
2058
- *
2059
- * Columns: relrowsecurity, relforcerowsecurity, relreplident,
2060
- * relispartition, reltablespace, relam, spcname, amname.
2061
- *
2062
- * Pre-PG 9.5 servers don't expose `relrowsecurity` /
2063
- * `relforcerowsecurity` / `relispartition`; we synthesize false there.
2064
- * Pre-PG 12 doesn't carry table AMs in pg_class.relam, so we leave the
2065
- * amname slot null on those servers.
2066
- */
2067
- export const fetchTableInfo = (opts) => {
2068
- const { oid, serverVersion } = opts;
2069
- const hasRowSec = serverAtLeast(serverVersion, PG_9_5);
2070
- const hasIsPart = serverAtLeast(serverVersion, PG_10);
2071
- const hasRelAm = serverAtLeast(serverVersion, PG_12);
2072
- const rowSec = hasRowSec
2073
- ? "c.relrowsecurity, c.relforcerowsecurity"
2074
- : "false AS relrowsecurity, false AS relforcerowsecurity";
2075
- const isPart = hasIsPart ? "c.relispartition" : "false AS relispartition";
2076
- const amCol = hasRelAm ? "c.relam" : "0::oid AS relam";
2077
- const amName = hasRelAm ? "am.amname" : "NULL::name AS amname";
2078
- const amJoin = hasRelAm
2079
- ? " LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam\n"
2080
- : "";
2081
- const sql = "SELECT " +
2082
- rowSec +
2083
- ",\n c.relreplident,\n " +
2084
- isPart +
2085
- ",\n c.reltablespace,\n " +
2086
- amCol +
2087
- ",\n ts.spcname,\n " +
2088
- amName +
2089
- "\nFROM pg_catalog.pg_class c\n" +
2090
- " LEFT JOIN pg_catalog.pg_tablespace ts ON ts.oid = c.reltablespace\n" +
2091
- amJoin +
2092
- `WHERE c.oid = '${oid}';`;
2093
- return { sql, params: [], description: "Relation footer info" };
1041
+ * Look up view definition for `\sv` / `\sv+`.
1042
+ */
1043
+ const showView = (opts) => {
1044
+ return {
1045
+ sql: `SELECT pg_catalog.pg_get_viewdef('${opts.name.replace(/'/g, "''")}'::pg_catalog.regclass, true) AS def;`,
1046
+ params: [],
1047
+ description: "View definition"
1048
+ };
2094
1049
  };
2095
1050
  /**
2096
- * Named NOT NULL constraints (`pg_constraint.contype = 'n'`), introduced
2097
- * in PG 18. Output columns mirror upstream `describeOneTableDetails`
2098
- * (describe.c ~3104):
2099
- * conname, attname, connoinherit, conislocal.
2100
- *
2101
- * The constraint references a single column via `conkey[1]`; we join
2102
- * `pg_attribute` on that attnum and order by it so the footer lists the
2103
- * constraints in column order. On pre-PG-18 servers the catalog never
2104
- * carries `contype = 'n'` rows, so the query returns empty and the
2105
- * caller suppresses the footer — no version branch is needed in the SQL.
2106
- */
2107
- export const fetchNotNullConstraints = (opts) => {
2108
- const { oid, serverVersion } = opts;
2109
- if (serverLess(serverVersion, PG_18)) {
2110
- return {
2111
- sql: "SELECT NULL::pg_catalog.text, NULL::pg_catalog.text, false, false WHERE false;",
2112
- params: [],
2113
- description: "Not-null constraints",
2114
- };
2115
- }
2116
- const sql = "SELECT co.conname, at.attname, co.connoinherit, co.conislocal\n" +
2117
- "FROM pg_catalog.pg_constraint co\n" +
2118
- " JOIN pg_catalog.pg_attribute at\n" +
2119
- " ON (at.attrelid = co.conrelid AND at.attnum = co.conkey[1])\n" +
2120
- `WHERE co.contype = 'n' AND co.conrelid = '${oid}'\n` +
2121
- "ORDER BY at.attnum;";
2122
- return { sql, params: [], description: "Not-null constraints" };
1051
+ * Per-relation table-info query. Returns one row carrying the flags
1052
+ * the formatter needs for the optional footer sections: RLS toggles
1053
+ * (relrowsecurity, relforcerowsecurity), replica-identity char,
1054
+ * partition flag, tablespace oid, access-method oid and pre-joined
1055
+ * pg_tablespace.spcname / pg_am.amname so the formatter doesn't have
1056
+ * to fan out for those.
1057
+ *
1058
+ * Columns: relrowsecurity, relforcerowsecurity, relreplident,
1059
+ * relispartition, reltablespace, relam, spcname, amname.
1060
+ *
1061
+ * Pre-PG 9.5 servers don't expose `relrowsecurity` /
1062
+ * `relforcerowsecurity` / `relispartition`; we synthesize false there.
1063
+ * Pre-PG 12 doesn't carry table AMs in pg_class.relam, so we leave the
1064
+ * amname slot null on those servers.
1065
+ */
1066
+ const fetchTableInfo = (opts) => {
1067
+ const { oid, serverVersion } = opts;
1068
+ const hasRowSec = serverAtLeast(serverVersion, PG_9_5);
1069
+ const hasIsPart = serverAtLeast(serverVersion, PG_10);
1070
+ const hasRelAm = serverAtLeast(serverVersion, PG_12);
1071
+ const rowSec = hasRowSec ? "c.relrowsecurity, c.relforcerowsecurity" : "false AS relrowsecurity, false AS relforcerowsecurity";
1072
+ const isPart = hasIsPart ? "c.relispartition" : "false AS relispartition";
1073
+ const amCol = hasRelAm ? "c.relam" : "0::oid AS relam";
1074
+ const amName = hasRelAm ? "am.amname" : "NULL::name AS amname";
1075
+ const amJoin = hasRelAm ? " LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam\n" : "";
1076
+ return {
1077
+ sql: "SELECT " + rowSec + ",\n c.relreplident,\n " + isPart + ",\n c.reltablespace,\n " + amCol + ",\n ts.spcname,\n " + amName + "\nFROM pg_catalog.pg_class c\n LEFT JOIN pg_catalog.pg_tablespace ts ON ts.oid = c.reltablespace\n" + amJoin + `WHERE c.oid = '${oid}';`,
1078
+ params: [],
1079
+ description: "Relation footer info"
1080
+ };
2123
1081
  };
2124
1082
  /**
2125
- * RLS policies for a relation. Output columns mirror upstream:
2126
- * polname, polpermissive, roles (CSV or NULL), polqual, polwithcheck,
2127
- * polcmd (mapped to text).
2128
- *
2129
- * The polcmd→keyword mapping matches upstream: 'r'→SELECT, 'a'→INSERT,
2130
- * 'w'→UPDATE, 'd'→DELETE, '*'→NULL (i.e. ALL — emitted as no FOR clause).
2131
- */
2132
- export const fetchPolicies = (opts) => {
2133
- const { oid, serverVersion } = opts;
2134
- const permissive = serverAtLeast(serverVersion, PG_10)
2135
- ? "pol.polpermissive"
2136
- : "'t' AS polpermissive";
2137
- const sql = "SELECT pol.polname,\n " +
2138
- permissive +
2139
- ",\n CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(SELECT rolname FROM pg_catalog.pg_roles WHERE oid = ANY (pol.polroles) ORDER BY 1), ', ') END AS roles,\n" +
2140
- " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS qual,\n" +
2141
- " pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS withcheck,\n" +
2142
- " CASE pol.polcmd\n" +
2143
- " WHEN 'r' THEN 'SELECT'\n" +
2144
- " WHEN 'a' THEN 'INSERT'\n" +
2145
- " WHEN 'w' THEN 'UPDATE'\n" +
2146
- " WHEN 'd' THEN 'DELETE'\n" +
2147
- " END AS cmd\n" +
2148
- "FROM pg_catalog.pg_policy pol\n" +
2149
- `WHERE pol.polrelid = '${oid}'\n` +
2150
- "ORDER BY pol.polname;";
2151
- return { sql, params: [], description: "Relation policies" };
1083
+ * Named NOT NULL constraints (`pg_constraint.contype = 'n'`), introduced
1084
+ * in PG 18. Output columns mirror upstream `describeOneTableDetails`
1085
+ * (describe.c ~3104):
1086
+ * conname, attname, connoinherit, conislocal.
1087
+ *
1088
+ * The constraint references a single column via `conkey[1]`; we join
1089
+ * `pg_attribute` on that attnum and order by it so the footer lists the
1090
+ * constraints in column order. On pre-PG-18 servers the catalog never
1091
+ * carries `contype = 'n'` rows, so the query returns empty and the
1092
+ * caller suppresses the footer — no version branch is needed in the SQL.
1093
+ */
1094
+ const fetchNotNullConstraints = (opts) => {
1095
+ const { oid, serverVersion } = opts;
1096
+ if (serverLess(serverVersion, 18e4)) return {
1097
+ sql: "SELECT NULL::pg_catalog.text, NULL::pg_catalog.text, false, false WHERE false;",
1098
+ params: [],
1099
+ description: "Not-null constraints"
1100
+ };
1101
+ return {
1102
+ sql: `SELECT co.conname, at.attname, co.connoinherit, co.conislocal
1103
+ FROM pg_catalog.pg_constraint co
1104
+ JOIN pg_catalog.pg_attribute at
1105
+ ON (at.attrelid = co.conrelid AND at.attnum = co.conkey[1])
1106
+ WHERE co.contype = 'n' AND co.conrelid = '${oid}'\nORDER BY at.attnum;`,
1107
+ params: [],
1108
+ description: "Not-null constraints"
1109
+ };
2152
1110
  };
2153
1111
  /**
2154
- * Parents from `pg_inherits`. We exclude partitioned parents so
2155
- * "Inherits:" only lists genuine inheritance (partition parents go in
2156
- * "Partition of:" instead). Output: one column with a regclass-formatted
2157
- * parent name per row.
2158
- */
2159
- export const fetchInherits = (opts) => {
2160
- const { oid } = opts;
2161
- const sql = "SELECT c.oid::pg_catalog.regclass\n" +
2162
- "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" +
2163
- `WHERE c.oid = i.inhparent AND i.inhrelid = '${oid}'\n` +
2164
- " AND c.relkind NOT IN ('p', 'I')\n" +
2165
- "ORDER BY inhseqno;";
2166
- return { sql, params: [], description: "Inherited-from relations" };
1112
+ * RLS policies for a relation. Output columns mirror upstream:
1113
+ * polname, polpermissive, roles (CSV or NULL), polqual, polwithcheck,
1114
+ * polcmd (mapped to text).
1115
+ *
1116
+ * The polcmd→keyword mapping matches upstream: 'r'→SELECT, 'a'→INSERT,
1117
+ * 'w'→UPDATE, 'd'→DELETE, '*'→NULL (i.e. ALL — emitted as no FOR clause).
1118
+ */
1119
+ const fetchPolicies = (opts) => {
1120
+ const { oid, serverVersion } = opts;
1121
+ return {
1122
+ sql: "SELECT pol.polname,\n " + (serverAtLeast(serverVersion, 1e5) ? "pol.polpermissive" : "'t' AS polpermissive") + `,
1123
+ CASE WHEN pol.polroles = '{0}' THEN NULL ELSE pg_catalog.array_to_string(array(SELECT rolname FROM pg_catalog.pg_roles WHERE oid = ANY (pol.polroles) ORDER BY 1), ', ') END AS roles,
1124
+ pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS qual,
1125
+ pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS withcheck,
1126
+ CASE pol.polcmd
1127
+ WHEN 'r' THEN 'SELECT'
1128
+ WHEN 'a' THEN 'INSERT'
1129
+ WHEN 'w' THEN 'UPDATE'
1130
+ WHEN 'd' THEN 'DELETE'
1131
+ END AS cmd
1132
+ FROM pg_catalog.pg_policy pol
1133
+ WHERE pol.polrelid = '${oid}'\nORDER BY pol.polname;`,
1134
+ params: [],
1135
+ description: "Relation policies"
1136
+ };
2167
1137
  };
2168
1138
  /**
2169
- * Child tables / partitions from `pg_inherits`. Output columns:
2170
- * relname (regclass), relkind, inhdetachpending, partition-bound (or NULL).
2171
- *
2172
- * Pre-PG 10 servers don't expose `relpartbound`; pre-PG 14 don't have
2173
- * `inhdetachpending`. We synthesize NULL / false there.
2174
- */
2175
- export const fetchInheritedBy = (opts) => {
2176
- const { oid, serverVersion } = opts;
2177
- const hasDetach = serverAtLeast(serverVersion, PG_14);
2178
- const hasPartBound = serverAtLeast(serverVersion, PG_10);
2179
- const detachCol = hasDetach
2180
- ? "i.inhdetachpending"
2181
- : "false AS inhdetachpending";
2182
- const boundCol = hasPartBound
2183
- ? "pg_catalog.pg_get_expr(c.relpartbound, c.oid)"
2184
- : "NULL::text";
2185
- const orderBy = hasPartBound
2186
- ? "pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT'," +
2187
- " c.oid::pg_catalog.regclass::pg_catalog.text"
2188
- : "c.oid::pg_catalog.regclass::pg_catalog.text";
2189
- const sql = "SELECT c.oid::pg_catalog.regclass, c.relkind,\n " +
2190
- detachCol +
2191
- ",\n " +
2192
- boundCol +
2193
- "\nFROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n" +
2194
- `WHERE c.oid = i.inhrelid AND i.inhparent = '${oid}'\n` +
2195
- `ORDER BY ${orderBy};`;
2196
- return { sql, params: [], description: "Inheriting / child relations" };
1139
+ * Parents from `pg_inherits`. We exclude partitioned parents so
1140
+ * "Inherits:" only lists genuine inheritance (partition parents go in
1141
+ * "Partition of:" instead). Output: one column with a regclass-formatted
1142
+ * parent name per row.
1143
+ */
1144
+ const fetchInherits = (opts) => {
1145
+ const { oid } = opts;
1146
+ return {
1147
+ sql: `SELECT c.oid::pg_catalog.regclass
1148
+ FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
1149
+ WHERE c.oid = i.inhparent AND i.inhrelid = '${oid}'\n AND c.relkind NOT IN ('p', 'I')
1150
+ ORDER BY inhseqno;`,
1151
+ params: [],
1152
+ description: "Inherited-from relations"
1153
+ };
2197
1154
  };
2198
1155
  /**
2199
- * Partition-of details for a child partition (`relispartition = true`).
2200
- * Returns parent regclass + partition bound (+ partition constraint when
2201
- * verbose is requested by the caller). Per upstream, this is invoked
2202
- * unconditionally for child partitions regardless of verbose, with the
2203
- * partition-constraint column conditionally selected.
2204
- */
2205
- export const fetchPartitionOf = (opts) => {
2206
- const { oid, serverVersion, withConstraint } = opts;
2207
- const detachCol = serverAtLeast(serverVersion, PG_14)
2208
- ? "i.inhdetachpending"
2209
- : "false AS inhdetachpending";
2210
- let sql = "SELECT inhparent::pg_catalog.regclass,\n" +
2211
- " pg_catalog.pg_get_expr(c.relpartbound, c.oid),\n " +
2212
- detachCol;
2213
- if (withConstraint) {
2214
- sql += ",\n pg_catalog.pg_get_partition_constraintdef(c.oid)";
2215
- }
2216
- sql +=
2217
- "\nFROM pg_catalog.pg_class c\n" +
2218
- " JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid\n" +
2219
- `WHERE c.oid = '${oid}';`;
2220
- return { sql, params: [], description: "Partition-of details" };
1156
+ * Child tables / partitions from `pg_inherits`. Output columns:
1157
+ * relname (regclass), relkind, inhdetachpending, partition-bound (or NULL).
1158
+ *
1159
+ * Pre-PG 10 servers don't expose `relpartbound`; pre-PG 14 don't have
1160
+ * `inhdetachpending`. We synthesize NULL / false there.
1161
+ */
1162
+ const fetchInheritedBy = (opts) => {
1163
+ const { oid, serverVersion } = opts;
1164
+ const hasDetach = serverAtLeast(serverVersion, PG_14);
1165
+ const hasPartBound = serverAtLeast(serverVersion, PG_10);
1166
+ const detachCol = hasDetach ? "i.inhdetachpending" : "false AS inhdetachpending";
1167
+ const boundCol = hasPartBound ? "pg_catalog.pg_get_expr(c.relpartbound, c.oid)" : "NULL::text";
1168
+ const orderBy = hasPartBound ? "pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT', c.oid::pg_catalog.regclass::pg_catalog.text" : "c.oid::pg_catalog.regclass::pg_catalog.text";
1169
+ return {
1170
+ sql: "SELECT c.oid::pg_catalog.regclass, c.relkind,\n " + detachCol + ",\n " + boundCol + `
1171
+ FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i
1172
+ WHERE c.oid = i.inhrelid AND i.inhparent = '${oid}'\nORDER BY ${orderBy};`,
1173
+ params: [],
1174
+ description: "Inheriting / child relations"
1175
+ };
2221
1176
  };
2222
1177
  /**
2223
- * Partition-key definition for a partitioned-table parent
2224
- * (`relkind = 'p'`). One row, one text column.
2225
- */
2226
- export const fetchPartitionKey = (opts) => {
2227
- const sql = `SELECT pg_catalog.pg_get_partkeydef('${opts.oid}'::pg_catalog.oid);`;
2228
- return { sql, params: [], description: "Partition key" };
1178
+ * Partition-of details for a child partition (`relispartition = true`).
1179
+ * Returns parent regclass + partition bound (+ partition constraint when
1180
+ * verbose is requested by the caller). Per upstream, this is invoked
1181
+ * unconditionally for child partitions regardless of verbose, with the
1182
+ * partition-constraint column conditionally selected.
1183
+ */
1184
+ const fetchPartitionOf = (opts) => {
1185
+ const { oid, serverVersion, withConstraint } = opts;
1186
+ let sql = "SELECT inhparent::pg_catalog.regclass,\n pg_catalog.pg_get_expr(c.relpartbound, c.oid),\n " + (serverAtLeast(serverVersion, 14e4) ? "i.inhdetachpending" : "false AS inhdetachpending");
1187
+ if (withConstraint) sql += ",\n pg_catalog.pg_get_partition_constraintdef(c.oid)";
1188
+ sql += `
1189
+ FROM pg_catalog.pg_class c
1190
+ JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid
1191
+ WHERE c.oid = '${oid}';`;
1192
+ return {
1193
+ sql,
1194
+ params: [],
1195
+ description: "Partition-of details"
1196
+ };
2229
1197
  };
2230
1198
  /**
2231
- * Foreign-table footer info: server name + ftoptions as a pre-formatted
2232
- * "key 'val', key 'val'" string. Two-column single-row result; an empty
2233
- * options string means no FDW options.
2234
- */
2235
- export const fetchForeignTableInfo = (opts) => {
2236
- const { oid } = opts;
2237
- const sql = "SELECT s.srvname,\n" +
2238
- " pg_catalog.array_to_string(ARRAY(\n" +
2239
- " SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value)\n" +
2240
- " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ') AS ftoptions\n" +
2241
- "FROM pg_catalog.pg_foreign_table f,\n" +
2242
- " pg_catalog.pg_foreign_server s\n" +
2243
- `WHERE f.ftrelid = '${oid}' AND s.oid = f.ftserver;`;
2244
- return { sql, params: [], description: "Foreign-table footer info" };
1199
+ * Partition-key definition for a partitioned-table parent
1200
+ * (`relkind = 'p'`). One row, one text column.
1201
+ */
1202
+ const fetchPartitionKey = (opts) => {
1203
+ return {
1204
+ sql: `SELECT pg_catalog.pg_get_partkeydef('${opts.oid}'::pg_catalog.oid);`,
1205
+ params: [],
1206
+ description: "Partition key"
1207
+ };
2245
1208
  };
2246
1209
  /**
2247
- * Pull `indexrelid::regclass` for the index marked as REPLICA IDENTITY
2248
- * on this relation. Returns at most one row when relreplident = 'i'.
2249
- */
2250
- export const fetchReplicaIdentityIndex = (opts) => {
2251
- const { oid } = opts;
2252
- const sql = "SELECT c2.relname\n" +
2253
- "FROM pg_catalog.pg_index i\n" +
2254
- " JOIN pg_catalog.pg_class c2 ON c2.oid = i.indexrelid\n" +
2255
- `WHERE i.indrelid = '${oid}' AND i.indisreplident\n` +
2256
- "LIMIT 1;";
2257
- return { sql, params: [], description: "Replica-identity index" };
1210
+ * Foreign-table footer info: server name + ftoptions as a pre-formatted
1211
+ * "key 'val', key 'val'" string. Two-column single-row result; an empty
1212
+ * options string means no FDW options.
1213
+ */
1214
+ const fetchForeignTableInfo = (opts) => {
1215
+ const { oid } = opts;
1216
+ return {
1217
+ sql: `SELECT s.srvname,
1218
+ pg_catalog.array_to_string(ARRAY(
1219
+ SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value)
1220
+ FROM pg_catalog.pg_options_to_table(ftoptions)), ', ') AS ftoptions
1221
+ FROM pg_catalog.pg_foreign_table f,
1222
+ pg_catalog.pg_foreign_server s
1223
+ WHERE f.ftrelid = '${oid}' AND s.oid = f.ftserver;`,
1224
+ params: [],
1225
+ description: "Foreign-table footer info"
1226
+ };
2258
1227
  };
2259
1228
  /**
2260
- * Extended-statistics objects defined on a relation. Mirrors the upstream
2261
- * verbose-only `\d+` section. Output columns:
2262
- * stxnsp, stxname, ndist_enabled, deps_enabled, mcv_enabled, columns,
2263
- * stxrelname, stxstattarget.
2264
- *
2265
- * Only meaningful on PG 10+ (the catalog table `pg_statistic_ext` doesn't
2266
- * exist before then). Caller gates by `relkind in ('r','m','p','f')` and
2267
- * verbose mode.
2268
- */
2269
- export const fetchStatisticsObjects = (opts) => {
2270
- const { oid, serverVersion } = opts;
2271
- if (serverLess(serverVersion, PG_10)) {
2272
- return {
2273
- sql: "/* server < 10 does not support extended statistics */ SELECT 1 WHERE false;",
2274
- params: [],
2275
- description: "Statistics objects",
2276
- };
2277
- }
2278
- // `pg_get_statisticsobjdef_columns` was added in PG 14. Older servers
2279
- // reconstruct the column list from `pg_statistic_ext.stxkeys`.
2280
- const columnsExpr = serverAtLeast(serverVersion, PG_14)
2281
- ? "pg_catalog.pg_get_statisticsobjdef_columns(es.oid)"
2282
- : "(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname), ', ')\n" +
2283
- " FROM pg_catalog.unnest(es.stxkeys) k(attnum)\n" +
2284
- " JOIN pg_catalog.pg_attribute a ON a.attrelid = es.stxrelid AND a.attnum = k.attnum AND NOT a.attisdropped)";
2285
- // `pg_statistic_ext.stxstattarget` exists on every supported PG; PG 17
2286
- // changed its type from int4 to int2 but we just CAST to text.
2287
- const sql = "SELECT es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS stxnsp,\n" +
2288
- " es.stxname,\n" +
2289
- " 'd' = any(es.stxkind) AS ndist_enabled,\n" +
2290
- " 'f' = any(es.stxkind) AS deps_enabled,\n" +
2291
- " 'm' = any(es.stxkind) AS mcv_enabled,\n" +
2292
- " " +
2293
- columnsExpr +
2294
- " AS columns,\n" +
2295
- " es.stxrelid::pg_catalog.regclass::pg_catalog.text AS stxrelname,\n" +
2296
- " es.stxstattarget\n" +
2297
- "FROM pg_catalog.pg_statistic_ext es\n" +
2298
- `WHERE es.stxrelid = '${oid}'\n` +
2299
- "ORDER BY stxnsp, es.stxname;";
2300
- return { sql, params: [], description: "Statistics objects" };
1229
+ * Pull `indexrelid::regclass` for the index marked as REPLICA IDENTITY
1230
+ * on this relation. Returns at most one row when relreplident = 'i'.
1231
+ */
1232
+ const fetchReplicaIdentityIndex = (opts) => {
1233
+ const { oid } = opts;
1234
+ return {
1235
+ sql: `SELECT c2.relname
1236
+ FROM pg_catalog.pg_index i
1237
+ JOIN pg_catalog.pg_class c2 ON c2.oid = i.indexrelid
1238
+ WHERE i.indrelid = '${oid}' AND i.indisreplident\nLIMIT 1;`,
1239
+ params: [],
1240
+ description: "Replica-identity index"
1241
+ };
2301
1242
  };
2302
1243
  /**
2303
- * Publications a relation belongs to. Returns one column `pubname` per
2304
- * publication. Covers the three sources upstream considers:
2305
- * - explicit `pg_publication_rel` membership
2306
- * - `puballtables` (FOR ALL TABLES)
2307
- * - `pg_publication_namespace` (FOR ALL TABLES IN SCHEMA, PG 15+)
2308
- *
2309
- * Pre-PG 10 servers return an empty set (publications don't exist).
2310
- */
2311
- export const fetchTablePublications = (opts) => {
2312
- const { oid, serverVersion } = opts;
2313
- if (serverLess(serverVersion, PG_10)) {
2314
- return {
2315
- sql: "/* server < 10 does not support publications */ SELECT 1 WHERE false;",
2316
- params: [],
2317
- description: "Table publications",
2318
- };
2319
- }
2320
- const hasPubNs = serverAtLeast(serverVersion, PG_15);
2321
- // PG 19 added publication EXCEPT lists (`pg_publication_rel.prexcept`).
2322
- // An excepted relation still has a `pg_publication_rel` row, so the
2323
- // explicit-membership check must skip those rows or the table would be
2324
- // wrongly reported as "Included in publications". The excepted set is
2325
- // surfaced separately via `fetchExcludedFromPublications`.
2326
- const hasExcept = serverAtLeast(serverVersion, PG_19);
2327
- const exceptGuard = hasExcept ? " AND NOT pr.prexcept" : "";
2328
- let sql = "SELECT pub.pubname\n" +
2329
- "FROM pg_catalog.pg_publication pub\n" +
2330
- "WHERE pub.puballtables\n" +
2331
- " OR EXISTS (\n" +
2332
- " SELECT 1 FROM pg_catalog.pg_publication_rel pr\n" +
2333
- ` WHERE pr.prpubid = pub.oid AND pr.prrelid = '${oid}'${exceptGuard}\n` +
2334
- " )";
2335
- if (hasPubNs) {
2336
- sql +=
2337
- "\n OR EXISTS (\n" +
2338
- " SELECT 1 FROM pg_catalog.pg_publication_namespace pn\n" +
2339
- ` JOIN pg_catalog.pg_class c ON c.oid = '${oid}'\n` +
2340
- " WHERE pn.pnpubid = pub.oid AND pn.pnnspid = c.relnamespace\n" +
2341
- " )";
2342
- }
2343
- sql += "\nORDER BY 1;";
2344
- return { sql, params: [], description: "Table publications" };
1244
+ * Extended-statistics objects defined on a relation. Mirrors the upstream
1245
+ * verbose-only `\d+` section. Output columns:
1246
+ * stxnsp, stxname, ndist_enabled, deps_enabled, mcv_enabled, columns,
1247
+ * stxrelname, stxstattarget.
1248
+ *
1249
+ * Only meaningful on PG 10+ (the catalog table `pg_statistic_ext` doesn't
1250
+ * exist before then). Caller gates by `relkind in ('r','m','p','f')` and
1251
+ * verbose mode.
1252
+ */
1253
+ const fetchStatisticsObjects = (opts) => {
1254
+ const { oid, serverVersion } = opts;
1255
+ if (serverLess(serverVersion, 1e5)) return {
1256
+ sql: "/* server < 10 does not support extended statistics */ SELECT 1 WHERE false;",
1257
+ params: [],
1258
+ description: "Statistics objects"
1259
+ };
1260
+ return {
1261
+ sql: "SELECT es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS stxnsp,\n es.stxname,\n 'd' = any(es.stxkind) AS ndist_enabled,\n 'f' = any(es.stxkind) AS deps_enabled,\n 'm' = any(es.stxkind) AS mcv_enabled,\n " + (serverAtLeast(serverVersion, 14e4) ? "pg_catalog.pg_get_statisticsobjdef_columns(es.oid)" : "(SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname), ', ')\n FROM pg_catalog.unnest(es.stxkeys) k(attnum)\n JOIN pg_catalog.pg_attribute a ON a.attrelid = es.stxrelid AND a.attnum = k.attnum AND NOT a.attisdropped)") + ` AS columns,
1262
+ es.stxrelid::pg_catalog.regclass::pg_catalog.text AS stxrelname,
1263
+ es.stxstattarget
1264
+ FROM pg_catalog.pg_statistic_ext es
1265
+ WHERE es.stxrelid = '${oid}'\nORDER BY stxnsp, es.stxname;`,
1266
+ params: [],
1267
+ description: "Statistics objects"
1268
+ };
2345
1269
  };
2346
1270
  /**
2347
- * Subscriptions that include this relation. Mirrors the upstream
2348
- * `\d+` section. Requires read access to `pg_subscription`, which is
2349
- * only granted to superusers — the formatter must swallow
2350
- * permission-denied errors silently.
2351
- *
2352
- * Pre-PG 10 servers return an empty set.
2353
- */
2354
- export const fetchTableSubscriptions = (opts) => {
2355
- const { oid, serverVersion } = opts;
2356
- if (serverLess(serverVersion, PG_10)) {
2357
- return {
2358
- sql: "/* server < 10 does not support subscriptions */ SELECT 1 WHERE false;",
2359
- params: [],
2360
- description: "Table subscriptions",
2361
- };
2362
- }
2363
- const sql = "SELECT sub.subname\n" +
2364
- "FROM pg_catalog.pg_subscription sub\n" +
2365
- "JOIN pg_catalog.pg_subscription_rel sr ON sr.srsubid = sub.oid\n" +
2366
- `WHERE sr.srrelid = '${oid}'\n` +
2367
- "ORDER BY 1;";
2368
- return { sql, params: [], description: "Table subscriptions" };
1271
+ * Publications a relation belongs to. Returns one column `pubname` per
1272
+ * publication. Covers the three sources upstream considers:
1273
+ * - explicit `pg_publication_rel` membership
1274
+ * - `puballtables` (FOR ALL TABLES)
1275
+ * - `pg_publication_namespace` (FOR ALL TABLES IN SCHEMA, PG 15+)
1276
+ *
1277
+ * Pre-PG 10 servers return an empty set (publications don't exist).
1278
+ */
1279
+ const fetchTablePublications = (opts) => {
1280
+ const { oid, serverVersion } = opts;
1281
+ if (serverLess(serverVersion, 1e5)) return {
1282
+ sql: "/* server < 10 does not support publications */ SELECT 1 WHERE false;",
1283
+ params: [],
1284
+ description: "Table publications"
1285
+ };
1286
+ const hasPubNs = serverAtLeast(serverVersion, PG_15);
1287
+ let sql = `SELECT pub.pubname
1288
+ FROM pg_catalog.pg_publication pub
1289
+ WHERE pub.puballtables
1290
+ OR EXISTS (
1291
+ SELECT 1 FROM pg_catalog.pg_publication_rel pr
1292
+ WHERE pr.prpubid = pub.oid AND pr.prrelid = '${oid}'${serverAtLeast(serverVersion, 19e4) ? " AND NOT pr.prexcept" : ""}\n )`;
1293
+ if (hasPubNs) sql += `
1294
+ OR EXISTS (
1295
+ SELECT 1 FROM pg_catalog.pg_publication_namespace pn
1296
+ JOIN pg_catalog.pg_class c ON c.oid = '${oid}'\n WHERE pn.pnpubid = pub.oid AND pn.pnnspid = c.relnamespace
1297
+ )`;
1298
+ sql += "\nORDER BY 1;";
1299
+ return {
1300
+ sql,
1301
+ params: [],
1302
+ description: "Table publications"
1303
+ };
2369
1304
  };
2370
1305
  /**
2371
- * Publications that exclude this relation via a FOR ALL TABLES ... EXCEPT
2372
- * clause (PG 19+, `pg_publication_rel.prexcept`). Mirrors the upstream
2373
- * "Excluded from publications:" footer in `describeOneTableDetails`.
2374
- * Considers the partition root as well, since an EXCEPT can be declared on
2375
- * the root of a partitioned table.
2376
- *
2377
- * Returns an empty set on servers older than PG 19 (the column and the
2378
- * feature don't exist), so the caller suppresses the footer without a
2379
- * separate version branch at the call site.
2380
- */
2381
- export const fetchExcludedFromPublications = (opts) => {
2382
- const { oid, serverVersion } = opts;
2383
- if (serverLess(serverVersion, PG_19)) {
2384
- return {
2385
- sql: "/* server < 19 does not support publication EXCEPT lists */ SELECT 1 WHERE false;",
2386
- params: [],
2387
- description: "Excluded from publications",
2388
- };
2389
- }
2390
- const sql = "SELECT pubname\n" +
2391
- "FROM pg_catalog.pg_publication p\n" +
2392
- "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n" +
2393
- `WHERE (pr.prrelid = '${oid}' OR pr.prrelid = pg_catalog.pg_partition_root('${oid}'))\n` +
2394
- "AND pr.prexcept\n" +
2395
- "ORDER BY 1;";
2396
- return { sql, params: [], description: "Excluded from publications" };
1306
+ * Subscriptions that include this relation. Mirrors the upstream
1307
+ * `\d+` section. Requires read access to `pg_subscription`, which is
1308
+ * only granted to superusers — the formatter must swallow
1309
+ * permission-denied errors silently.
1310
+ *
1311
+ * Pre-PG 10 servers return an empty set.
1312
+ */
1313
+ const fetchTableSubscriptions = (opts) => {
1314
+ const { oid, serverVersion } = opts;
1315
+ if (serverLess(serverVersion, 1e5)) return {
1316
+ sql: "/* server < 10 does not support subscriptions */ SELECT 1 WHERE false;",
1317
+ params: [],
1318
+ description: "Table subscriptions"
1319
+ };
1320
+ return {
1321
+ sql: `SELECT sub.subname
1322
+ FROM pg_catalog.pg_subscription sub
1323
+ JOIN pg_catalog.pg_subscription_rel sr ON sr.srsubid = sub.oid
1324
+ WHERE sr.srrelid = '${oid}'\nORDER BY 1;`,
1325
+ params: [],
1326
+ description: "Table subscriptions"
1327
+ };
2397
1328
  };
2398
1329
  /**
2399
- * Publications a sequence belongs to. PG 19 added sequence replication
2400
- * (`pg_publication.puballsequences`); a sequence is included when a
2401
- * publication is declared FOR ALL SEQUENCES and the sequence itself is
2402
- * publishable. Mirrors the "Included in publications:" footer emitted for
2403
- * sequences in upstream `describeOneTableDetails`.
2404
- *
2405
- * Returns an empty set on servers older than PG 19.
2406
- */
2407
- export const fetchSequencePublications = (opts) => {
2408
- const { oid, serverVersion } = opts;
2409
- if (serverLess(serverVersion, PG_19)) {
2410
- return {
2411
- sql: "/* server < 19 does not support sequence publications */ SELECT 1 WHERE false;",
2412
- params: [],
2413
- description: "Sequence publications",
2414
- };
2415
- }
2416
- const sql = "SELECT pubname FROM pg_catalog.pg_publication p\n" +
2417
- "WHERE p.puballsequences\n" +
2418
- ` AND pg_catalog.pg_relation_is_publishable('${oid}')\n` +
2419
- "ORDER BY 1;";
2420
- return { sql, params: [], description: "Sequence publications" };
1330
+ * Publications that exclude this relation via a FOR ALL TABLES ... EXCEPT
1331
+ * clause (PG 19+, `pg_publication_rel.prexcept`). Mirrors the upstream
1332
+ * "Excluded from publications:" footer in `describeOneTableDetails`.
1333
+ * Considers the partition root as well, since an EXCEPT can be declared on
1334
+ * the root of a partitioned table.
1335
+ *
1336
+ * Returns an empty set on servers older than PG 19 (the column and the
1337
+ * feature don't exist), so the caller suppresses the footer without a
1338
+ * separate version branch at the call site.
1339
+ */
1340
+ const fetchExcludedFromPublications = (opts) => {
1341
+ const { oid, serverVersion } = opts;
1342
+ if (serverLess(serverVersion, 19e4)) return {
1343
+ sql: "/* server < 19 does not support publication EXCEPT lists */ SELECT 1 WHERE false;",
1344
+ params: [],
1345
+ description: "Excluded from publications"
1346
+ };
1347
+ return {
1348
+ sql: `SELECT pubname
1349
+ FROM pg_catalog.pg_publication p
1350
+ JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid
1351
+ WHERE (pr.prrelid = '${oid}' OR pr.prrelid = pg_catalog.pg_partition_root('${oid}'))\nAND pr.prexcept
1352
+ ORDER BY 1;`,
1353
+ params: [],
1354
+ description: "Excluded from publications"
1355
+ };
2421
1356
  };
2422
1357
  /**
2423
- * Per-column FDW options for a foreign table. Returns one row per column
2424
- * that has at least one `attfdwoptions` entry. Output columns:
2425
- * attname, opts (pre-formatted "k 'v', k 'v'").
2426
- *
2427
- * Only meaningful for `relkind = 'f'`.
2428
- */
2429
- export const fetchPerColumnFdwOptions = (opts) => {
2430
- const { oid } = opts;
2431
- const sql = "SELECT a.attname,\n" +
2432
- " pg_catalog.array_to_string(ARRAY(\n" +
2433
- " SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value)\n" +
2434
- " FROM pg_catalog.pg_options_to_table(a.attfdwoptions)), ', ') AS opts\n" +
2435
- "FROM pg_catalog.pg_attribute a\n" +
2436
- `WHERE a.attrelid = '${oid}' AND a.attnum > 0 AND NOT a.attisdropped\n` +
2437
- " AND a.attfdwoptions IS NOT NULL\n" +
2438
- "ORDER BY a.attnum;";
2439
- return { sql, params: [], description: "Per-column FDW options" };
1358
+ * Publications a sequence belongs to. PG 19 added sequence replication
1359
+ * (`pg_publication.puballsequences`); a sequence is included when a
1360
+ * publication is declared FOR ALL SEQUENCES and the sequence itself is
1361
+ * publishable. Mirrors the "Included in publications:" footer emitted for
1362
+ * sequences in upstream `describeOneTableDetails`.
1363
+ *
1364
+ * Returns an empty set on servers older than PG 19.
1365
+ */
1366
+ const fetchSequencePublications = (opts) => {
1367
+ const { oid, serverVersion } = opts;
1368
+ if (serverLess(serverVersion, 19e4)) return {
1369
+ sql: "/* server < 19 does not support sequence publications */ SELECT 1 WHERE false;",
1370
+ params: [],
1371
+ description: "Sequence publications"
1372
+ };
1373
+ return {
1374
+ sql: `SELECT pubname FROM pg_catalog.pg_publication p
1375
+ WHERE p.puballsequences
1376
+ AND pg_catalog.pg_relation_is_publishable('${oid}')\nORDER BY 1;`,
1377
+ params: [],
1378
+ description: "Sequence publications"
1379
+ };
2440
1380
  };
2441
1381
  /**
2442
- * For a TOAST table (`relkind = 't'`), look up the owning user table.
2443
- * Returns at most one row with the parent's regclass-formatted name.
2444
- */
2445
- export const fetchToastOwningTable = (opts) => {
2446
- const { oid } = opts;
2447
- const sql = "SELECT oid::pg_catalog.regclass::pg_catalog.text AS relname\n" +
2448
- "FROM pg_catalog.pg_class\n" +
2449
- `WHERE reltoastrelid = '${oid}'\n` +
2450
- "LIMIT 1;";
2451
- return { sql, params: [], description: "TOAST owning table" };
1382
+ * Per-column FDW options for a foreign table. Returns one row per column
1383
+ * that has at least one `attfdwoptions` entry. Output columns:
1384
+ * attname, opts (pre-formatted "k 'v', k 'v'").
1385
+ *
1386
+ * Only meaningful for `relkind = 'f'`.
1387
+ */
1388
+ const fetchPerColumnFdwOptions = (opts) => {
1389
+ const { oid } = opts;
1390
+ return {
1391
+ sql: `SELECT a.attname,
1392
+ pg_catalog.array_to_string(ARRAY(
1393
+ SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value)
1394
+ FROM pg_catalog.pg_options_to_table(a.attfdwoptions)), ', ') AS opts
1395
+ FROM pg_catalog.pg_attribute a
1396
+ WHERE a.attrelid = '${oid}' AND a.attnum > 0 AND NOT a.attisdropped\n AND a.attfdwoptions IS NOT NULL
1397
+ ORDER BY a.attnum;`,
1398
+ params: [],
1399
+ description: "Per-column FDW options"
1400
+ };
2452
1401
  };
1402
+ /**
1403
+ * For a TOAST table (`relkind = 't'`), look up the owning user table.
1404
+ * Returns at most one row with the parent's regclass-formatted name.
1405
+ */
1406
+ const fetchToastOwningTable = (opts) => {
1407
+ const { oid } = opts;
1408
+ return {
1409
+ sql: `SELECT oid::pg_catalog.regclass::pg_catalog.text AS relname
1410
+ FROM pg_catalog.pg_class
1411
+ WHERE reltoastrelid = '${oid}'\nLIMIT 1;`,
1412
+ params: [],
1413
+ description: "TOAST owning table"
1414
+ };
1415
+ };
1416
+ //#endregion
1417
+ export { describeAccessMethods, describeAggregates, describeConfigurationParameters, describeFunctions, describeOneTSParser, describeOperators, describePublications, describeRoleGrants, describeRoles, describeSubscriptions, describeTableDetails, describeTablespaces, describeTypes, fetchExcludedFromPublications, fetchForeignTableInfo, fetchInheritedBy, fetchInherits, fetchNotNullConstraints, fetchPartitionKey, fetchPartitionOf, fetchPerColumnFdwOptions, fetchPolicies, fetchReplicaIdentityIndex, fetchSequencePublications, fetchStatisticsObjects, fetchTableInfo, fetchTablePublications, fetchTableSubscriptions, fetchToastOwningTable, listAllDbs, listCasts, listCollations, listConversions, listDbRoleSettings, listDefaultACLs, listDomains, listEventTriggers, listExtendedStats, listExtensionContents, listExtensions, listForeignDataWrappers, listForeignServers, listForeignTables, listLanguages, listLargeObjects, listOneExtensionContents, listOpFamilyFunctions, listOpFamilyOperators, listOperatorClasses, listOperatorFamilies, listPartitionedTables, listPublications, listSchemas, listTSConfigs, listTSDictionaries, listTSParsers, listTSTemplates, listTables, listUserMappings, objectDescription, permissionsList, showFunction, showView };