Line data Source code
1 : /*
2 : * psql - the PostgreSQL interactive terminal
3 : *
4 : * Support for the various \d ("describe") commands. Note that the current
5 : * expectation is that all functions in this file will succeed when working
6 : * with servers of versions 9.2 and up. It's okay to omit irrelevant
7 : * information for an old server, but not to fail outright. (But failing
8 : * against a pre-9.2 server is allowed.)
9 : *
10 : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
11 : *
12 : * src/bin/psql/describe.c
13 : */
14 : #include "postgres_fe.h"
15 :
16 : #include <ctype.h>
17 :
18 : #include "catalog/pg_am_d.h"
19 : #include "catalog/pg_amop_d.h"
20 : #include "catalog/pg_attribute_d.h"
21 : #include "catalog/pg_cast_d.h"
22 : #include "catalog/pg_class_d.h"
23 : #include "catalog/pg_collation_d.h"
24 : #include "catalog/pg_constraint_d.h"
25 : #include "catalog/pg_default_acl_d.h"
26 : #include "catalog/pg_proc_d.h"
27 : #include "catalog/pg_propgraph_element_d.h"
28 : #include "catalog/pg_publication_d.h"
29 : #include "catalog/pg_statistic_ext_d.h"
30 : #include "catalog/pg_subscription_d.h"
31 : #include "catalog/pg_type_d.h"
32 : #include "common.h"
33 : #include "common/logging.h"
34 : #include "describe.h"
35 : #include "fe_utils/mbprint.h"
36 : #include "fe_utils/print.h"
37 : #include "fe_utils/string_utils.h"
38 : #include "settings.h"
39 :
40 : static const char *map_typename_pattern(const char *pattern);
41 : static bool describeOneTableDetails(const char *schemaname,
42 : const char *relationname,
43 : const char *oid,
44 : bool verbose);
45 : static void add_tablespace_footer(printTableContent *const cont, char relkind,
46 : Oid tablespace, const bool newline);
47 : static void add_role_attribute(PQExpBuffer buf, const char *const str);
48 : static bool listTSParsersVerbose(const char *pattern);
49 : static bool describeOneTSParser(const char *oid, const char *nspname,
50 : const char *prsname);
51 : static bool listTSConfigsVerbose(const char *pattern);
52 : static bool describeOneTSConfig(const char *oid, const char *nspname,
53 : const char *cfgname,
54 : const char *pnspname, const char *prsname);
55 : static void printACLColumn(PQExpBuffer buf, const char *colname);
56 : static bool listOneExtensionContents(const char *extname, const char *oid);
57 : static bool validateSQLNamePattern(PQExpBuffer buf, const char *pattern,
58 : bool have_where, bool force_escape,
59 : const char *schemavar, const char *namevar,
60 : const char *altnamevar,
61 : const char *visibilityrule,
62 : bool *added_clause, int maxparts);
63 :
64 :
65 : /*----------------
66 : * Handlers for various slash commands displaying some sort of list
67 : * of things in the database.
68 : *
69 : * Note: try to format the queries to look nice in -E output.
70 : *----------------
71 : */
72 :
73 :
74 : /*
75 : * \da
76 : * Takes an optional regexp to select particular aggregates
77 : */
78 : bool
79 36 : describeAggregates(const char *pattern, bool verbose, bool showSystem)
80 : {
81 : PQExpBufferData buf;
82 : PGresult *res;
83 36 : printQueryOpt myopt = pset.popt;
84 :
85 36 : initPQExpBuffer(&buf);
86 :
87 36 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching aggregates"));
88 36 : appendPQExpBuffer(&buf,
89 : "SELECT n.nspname as \"%s\",\n"
90 : " p.proname AS \"%s\",\n"
91 : " pg_catalog.format_type(p.prorettype, NULL) AS \"%s\",\n"
92 : " CASE WHEN p.pronargs = 0\n"
93 : " THEN CAST('*' AS pg_catalog.text)\n"
94 : " ELSE pg_catalog.pg_get_function_arguments(p.oid)\n"
95 : " END AS \"%s\",\n",
96 : gettext_noop("Schema"),
97 : gettext_noop("Name"),
98 : gettext_noop("Result data type"),
99 : gettext_noop("Argument data types"));
100 :
101 36 : if (pset.sversion >= 110000)
102 36 : appendPQExpBuffer(&buf,
103 : " pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
104 : "FROM pg_catalog.pg_proc p\n"
105 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
106 : "WHERE p.prokind = " CppAsString2(PROKIND_AGGREGATE) "\n",
107 : gettext_noop("Description"));
108 : else
109 0 : appendPQExpBuffer(&buf,
110 : " pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
111 : "FROM pg_catalog.pg_proc p\n"
112 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
113 : "WHERE p.proisagg\n",
114 : gettext_noop("Description"));
115 :
116 36 : if (!showSystem && !pattern)
117 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
118 : " AND n.nspname <> 'information_schema'\n");
119 :
120 36 : if (!validateSQLNamePattern(&buf, pattern, true, false,
121 : "n.nspname", "p.proname", NULL,
122 : "pg_catalog.pg_function_is_visible(p.oid)",
123 : NULL, 3))
124 : {
125 16 : termPQExpBuffer(&buf);
126 16 : return false;
127 : }
128 :
129 20 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
130 :
131 20 : res = PSQLexec(buf.data);
132 20 : termPQExpBuffer(&buf);
133 20 : if (!res)
134 0 : return false;
135 :
136 20 : myopt.title = _("List of aggregate functions");
137 20 : myopt.translate_header = true;
138 :
139 20 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
140 :
141 20 : PQclear(res);
142 20 : return true;
143 : }
144 :
145 : /*
146 : * \dA
147 : * Takes an optional regexp to select particular access methods
148 : */
149 : bool
150 52 : describeAccessMethods(const char *pattern, bool verbose)
151 : {
152 : PQExpBufferData buf;
153 : PGresult *res;
154 52 : printQueryOpt myopt = pset.popt;
155 : static const bool translate_columns[] = {false, true, false, false};
156 :
157 52 : if (pset.sversion < 90600)
158 : {
159 : char sverbuf[32];
160 :
161 0 : pg_log_error("The server (version %s) does not support access methods.",
162 : formatPGVersionNumber(pset.sversion, false,
163 : sverbuf, sizeof(sverbuf)));
164 0 : return true;
165 : }
166 :
167 52 : initPQExpBuffer(&buf);
168 :
169 52 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching access methods"));
170 52 : appendPQExpBuffer(&buf,
171 : "SELECT amname AS \"%s\",\n"
172 : " CASE amtype"
173 : " WHEN " CppAsString2(AMTYPE_INDEX) " THEN '%s'"
174 : " WHEN " CppAsString2(AMTYPE_TABLE) " THEN '%s'"
175 : " END AS \"%s\"",
176 : gettext_noop("Name"),
177 : gettext_noop("Index"),
178 : gettext_noop("Table"),
179 : gettext_noop("Type"));
180 :
181 52 : if (verbose)
182 : {
183 16 : appendPQExpBuffer(&buf,
184 : ",\n amhandler AS \"%s\",\n"
185 : " pg_catalog.obj_description(oid, 'pg_am') AS \"%s\"",
186 : gettext_noop("Handler"),
187 : gettext_noop("Description"));
188 : }
189 :
190 52 : appendPQExpBufferStr(&buf,
191 : "\nFROM pg_catalog.pg_am\n");
192 :
193 52 : if (!validateSQLNamePattern(&buf, pattern, false, false,
194 : NULL, "amname", NULL,
195 : NULL,
196 : NULL, 1))
197 : {
198 12 : termPQExpBuffer(&buf);
199 12 : return false;
200 : }
201 :
202 40 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
203 :
204 40 : res = PSQLexec(buf.data);
205 40 : termPQExpBuffer(&buf);
206 40 : if (!res)
207 0 : return false;
208 :
209 40 : myopt.title = _("List of access methods");
210 40 : myopt.translate_header = true;
211 40 : myopt.translate_columns = translate_columns;
212 40 : myopt.n_translate_columns = lengthof(translate_columns);
213 :
214 40 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
215 :
216 40 : PQclear(res);
217 40 : return true;
218 : }
219 :
220 : /*
221 : * \db
222 : * Takes an optional regexp to select particular tablespaces
223 : */
224 : bool
225 16 : describeTablespaces(const char *pattern, bool verbose)
226 : {
227 : PQExpBufferData buf;
228 : PGresult *res;
229 16 : printQueryOpt myopt = pset.popt;
230 :
231 16 : initPQExpBuffer(&buf);
232 :
233 16 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching tablespaces"));
234 16 : appendPQExpBuffer(&buf,
235 : "SELECT spcname AS \"%s\",\n"
236 : " pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n"
237 : " pg_catalog.pg_tablespace_location(oid) AS \"%s\"",
238 : gettext_noop("Name"),
239 : gettext_noop("Owner"),
240 : gettext_noop("Location"));
241 :
242 16 : if (verbose)
243 : {
244 0 : appendPQExpBufferStr(&buf, ",\n ");
245 0 : printACLColumn(&buf, "spcacl");
246 0 : appendPQExpBuffer(&buf,
247 : ",\n spcoptions AS \"%s\""
248 : ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"%s\""
249 : ",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
250 : gettext_noop("Options"),
251 : gettext_noop("Size"),
252 : gettext_noop("Description"));
253 : }
254 :
255 16 : appendPQExpBufferStr(&buf,
256 : "\nFROM pg_catalog.pg_tablespace\n");
257 :
258 16 : if (!validateSQLNamePattern(&buf, pattern, false, false,
259 : NULL, "spcname", NULL,
260 : NULL,
261 : NULL, 1))
262 : {
263 12 : termPQExpBuffer(&buf);
264 12 : return false;
265 : }
266 :
267 4 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
268 :
269 4 : res = PSQLexec(buf.data);
270 4 : termPQExpBuffer(&buf);
271 4 : if (!res)
272 0 : return false;
273 :
274 4 : myopt.title = _("List of tablespaces");
275 4 : myopt.translate_header = true;
276 :
277 4 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
278 :
279 4 : PQclear(res);
280 4 : return true;
281 : }
282 :
283 :
284 : /*
285 : * \df
286 : * Takes an optional regexp to select particular functions.
287 : *
288 : * As with \d, you can specify the kinds of functions you want:
289 : *
290 : * a for aggregates
291 : * n for normal
292 : * p for procedure
293 : * t for trigger
294 : * w for window
295 : *
296 : * and you can mix and match these in any order.
297 : */
298 : bool
299 205 : describeFunctions(const char *functypes, const char *func_pattern,
300 : char **arg_patterns, int num_arg_patterns,
301 : bool verbose, bool showSystem)
302 : {
303 205 : const char *df_options = "anptwSx+";
304 205 : bool showAggregate = strchr(functypes, 'a') != NULL;
305 205 : bool showNormal = strchr(functypes, 'n') != NULL;
306 205 : bool showProcedure = strchr(functypes, 'p') != NULL;
307 205 : bool showTrigger = strchr(functypes, 't') != NULL;
308 205 : bool showWindow = strchr(functypes, 'w') != NULL;
309 : bool have_where;
310 : PQExpBufferData buf;
311 : PGresult *res;
312 205 : printQueryOpt myopt = pset.popt;
313 : static const bool translate_columns[] = {false, false, false, false, true, true, true, false, true, true, false, false, false, false};
314 :
315 : /* No "Parallel" column before 9.6 */
316 : static const bool translate_columns_pre_96[] = {false, false, false, false, true, true, false, true, true, false, false, false, false};
317 :
318 205 : if (strlen(functypes) != strspn(functypes, df_options))
319 : {
320 0 : pg_log_error("\\df only takes [%s] as options", df_options);
321 0 : return true;
322 : }
323 :
324 205 : if (showProcedure && pset.sversion < 110000)
325 : {
326 : char sverbuf[32];
327 :
328 0 : pg_log_error("\\df does not take a \"%c\" option with server version %s",
329 : 'p',
330 : formatPGVersionNumber(pset.sversion, false,
331 : sverbuf, sizeof(sverbuf)));
332 0 : return true;
333 : }
334 :
335 205 : if (!showAggregate && !showNormal && !showProcedure && !showTrigger && !showWindow)
336 : {
337 193 : showAggregate = showNormal = showTrigger = showWindow = true;
338 193 : if (pset.sversion >= 110000)
339 193 : showProcedure = true;
340 : }
341 :
342 205 : initPQExpBuffer(&buf);
343 :
344 205 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching functions"));
345 205 : appendPQExpBuffer(&buf,
346 : "SELECT n.nspname as \"%s\",\n"
347 : " p.proname as \"%s\",\n",
348 : gettext_noop("Schema"),
349 : gettext_noop("Name"));
350 :
351 205 : if (pset.sversion >= 110000)
352 205 : appendPQExpBuffer(&buf,
353 : " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
354 : " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
355 : " CASE p.prokind\n"
356 : " WHEN " CppAsString2(PROKIND_AGGREGATE) " THEN '%s'\n"
357 : " WHEN " CppAsString2(PROKIND_WINDOW) " THEN '%s'\n"
358 : " WHEN " CppAsString2(PROKIND_PROCEDURE) " THEN '%s'\n"
359 : " ELSE '%s'\n"
360 : " END as \"%s\"",
361 : gettext_noop("Result data type"),
362 : gettext_noop("Argument data types"),
363 : /* translator: "agg" is short for "aggregate" */
364 : gettext_noop("agg"),
365 : gettext_noop("window"),
366 : gettext_noop("proc"),
367 : gettext_noop("func"),
368 : gettext_noop("Type"));
369 : else
370 0 : appendPQExpBuffer(&buf,
371 : " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
372 : " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
373 : " CASE\n"
374 : " WHEN p.proisagg THEN '%s'\n"
375 : " WHEN p.proiswindow THEN '%s'\n"
376 : " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
377 : " ELSE '%s'\n"
378 : " END as \"%s\"",
379 : gettext_noop("Result data type"),
380 : gettext_noop("Argument data types"),
381 : /* translator: "agg" is short for "aggregate" */
382 : gettext_noop("agg"),
383 : gettext_noop("window"),
384 : gettext_noop("trigger"),
385 : gettext_noop("func"),
386 : gettext_noop("Type"));
387 :
388 205 : if (verbose)
389 : {
390 8 : appendPQExpBuffer(&buf,
391 : ",\n CASE\n"
392 : " WHEN p.provolatile = "
393 : CppAsString2(PROVOLATILE_IMMUTABLE) " THEN '%s'\n"
394 : " WHEN p.provolatile = "
395 : CppAsString2(PROVOLATILE_STABLE) " THEN '%s'\n"
396 : " WHEN p.provolatile = "
397 : CppAsString2(PROVOLATILE_VOLATILE) " THEN '%s'\n"
398 : " END as \"%s\"",
399 : gettext_noop("immutable"),
400 : gettext_noop("stable"),
401 : gettext_noop("volatile"),
402 : gettext_noop("Volatility"));
403 8 : if (pset.sversion >= 90600)
404 8 : appendPQExpBuffer(&buf,
405 : ",\n CASE\n"
406 : " WHEN p.proparallel = "
407 : CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
408 : " WHEN p.proparallel = "
409 : CppAsString2(PROPARALLEL_SAFE) " THEN '%s'\n"
410 : " WHEN p.proparallel = "
411 : CppAsString2(PROPARALLEL_UNSAFE) " THEN '%s'\n"
412 : " END as \"%s\"",
413 : gettext_noop("restricted"),
414 : gettext_noop("safe"),
415 : gettext_noop("unsafe"),
416 : gettext_noop("Parallel"));
417 8 : appendPQExpBuffer(&buf,
418 : ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
419 : ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\""
420 : ",\n CASE WHEN p.proleakproof THEN '%s' ELSE '%s' END as \"%s\"",
421 : gettext_noop("Owner"),
422 : gettext_noop("definer"),
423 : gettext_noop("invoker"),
424 : gettext_noop("Security"),
425 : gettext_noop("yes"),
426 : gettext_noop("no"),
427 : gettext_noop("Leakproof?"));
428 8 : appendPQExpBufferStr(&buf, ",\n ");
429 8 : printACLColumn(&buf, "p.proacl");
430 8 : appendPQExpBuffer(&buf,
431 : ",\n l.lanname as \"%s\"",
432 : gettext_noop("Language"));
433 8 : appendPQExpBuffer(&buf,
434 : ",\n CASE WHEN l.lanname IN ('internal', 'c') THEN p.prosrc END as \"%s\"",
435 : gettext_noop("Internal name"));
436 8 : appendPQExpBuffer(&buf,
437 : ",\n pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"",
438 : gettext_noop("Description"));
439 : }
440 :
441 205 : appendPQExpBufferStr(&buf,
442 : "\nFROM pg_catalog.pg_proc p"
443 : "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
444 :
445 249 : for (int i = 0; i < num_arg_patterns; i++)
446 : {
447 44 : appendPQExpBuffer(&buf,
448 : " LEFT JOIN pg_catalog.pg_type t%d ON t%d.oid = p.proargtypes[%d]\n"
449 : " LEFT JOIN pg_catalog.pg_namespace nt%d ON nt%d.oid = t%d.typnamespace\n",
450 : i, i, i, i, i, i);
451 : }
452 :
453 205 : if (verbose)
454 8 : appendPQExpBufferStr(&buf,
455 : " LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n");
456 :
457 205 : have_where = false;
458 :
459 : /* filter by function type, if requested */
460 205 : if (showNormal && showAggregate && showProcedure && showTrigger && showWindow)
461 : /* Do nothing */ ;
462 12 : else if (showNormal)
463 : {
464 4 : if (!showAggregate)
465 : {
466 4 : if (have_where)
467 0 : appendPQExpBufferStr(&buf, " AND ");
468 : else
469 : {
470 4 : appendPQExpBufferStr(&buf, "WHERE ");
471 4 : have_where = true;
472 : }
473 4 : if (pset.sversion >= 110000)
474 4 : appendPQExpBufferStr(&buf, "p.prokind <> "
475 : CppAsString2(PROKIND_AGGREGATE) "\n");
476 : else
477 0 : appendPQExpBufferStr(&buf, "NOT p.proisagg\n");
478 : }
479 4 : if (!showProcedure && pset.sversion >= 110000)
480 : {
481 4 : if (have_where)
482 4 : appendPQExpBufferStr(&buf, " AND ");
483 : else
484 : {
485 0 : appendPQExpBufferStr(&buf, "WHERE ");
486 0 : have_where = true;
487 : }
488 4 : appendPQExpBufferStr(&buf, "p.prokind <> "
489 : CppAsString2(PROKIND_PROCEDURE) "\n");
490 : }
491 4 : if (!showTrigger)
492 : {
493 4 : if (have_where)
494 4 : appendPQExpBufferStr(&buf, " AND ");
495 : else
496 : {
497 0 : appendPQExpBufferStr(&buf, "WHERE ");
498 0 : have_where = true;
499 : }
500 4 : appendPQExpBufferStr(&buf, "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n");
501 : }
502 4 : if (!showWindow)
503 : {
504 4 : if (have_where)
505 4 : appendPQExpBufferStr(&buf, " AND ");
506 : else
507 : {
508 0 : appendPQExpBufferStr(&buf, "WHERE ");
509 0 : have_where = true;
510 : }
511 4 : if (pset.sversion >= 110000)
512 4 : appendPQExpBufferStr(&buf, "p.prokind <> "
513 : CppAsString2(PROKIND_WINDOW) "\n");
514 : else
515 0 : appendPQExpBufferStr(&buf, "NOT p.proiswindow\n");
516 : }
517 : }
518 : else
519 : {
520 8 : bool needs_or = false;
521 :
522 8 : appendPQExpBufferStr(&buf, "WHERE (\n ");
523 8 : have_where = true;
524 : /* Note: at least one of these must be true ... */
525 8 : if (showAggregate)
526 : {
527 4 : if (pset.sversion >= 110000)
528 4 : appendPQExpBufferStr(&buf, "p.prokind = "
529 : CppAsString2(PROKIND_AGGREGATE) "\n");
530 : else
531 0 : appendPQExpBufferStr(&buf, "p.proisagg\n");
532 4 : needs_or = true;
533 : }
534 8 : if (showTrigger)
535 : {
536 0 : if (needs_or)
537 0 : appendPQExpBufferStr(&buf, " OR ");
538 0 : appendPQExpBufferStr(&buf,
539 : "p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype\n");
540 0 : needs_or = true;
541 : }
542 8 : if (showProcedure)
543 : {
544 4 : if (needs_or)
545 0 : appendPQExpBufferStr(&buf, " OR ");
546 4 : appendPQExpBufferStr(&buf, "p.prokind = "
547 : CppAsString2(PROKIND_PROCEDURE) "\n");
548 4 : needs_or = true;
549 : }
550 8 : if (showWindow)
551 : {
552 0 : if (needs_or)
553 0 : appendPQExpBufferStr(&buf, " OR ");
554 0 : if (pset.sversion >= 110000)
555 0 : appendPQExpBufferStr(&buf, "p.prokind = "
556 : CppAsString2(PROKIND_WINDOW) "\n");
557 : else
558 0 : appendPQExpBufferStr(&buf, "p.proiswindow\n");
559 : }
560 8 : appendPQExpBufferStr(&buf, " )\n");
561 : }
562 :
563 205 : if (!validateSQLNamePattern(&buf, func_pattern, have_where, false,
564 : "n.nspname", "p.proname", NULL,
565 : "pg_catalog.pg_function_is_visible(p.oid)",
566 : NULL, 3))
567 16 : goto error_return;
568 :
569 233 : for (int i = 0; i < num_arg_patterns; i++)
570 : {
571 44 : if (strcmp(arg_patterns[i], "-") != 0)
572 : {
573 : /*
574 : * Match type-name patterns against either internal or external
575 : * name, like \dT. Unlike \dT, there seems no reason to
576 : * discriminate against arrays or composite types.
577 : */
578 : char nspname[64];
579 : char typname[64];
580 : char ft[64];
581 : char tiv[64];
582 :
583 40 : snprintf(nspname, sizeof(nspname), "nt%d.nspname", i);
584 40 : snprintf(typname, sizeof(typname), "t%d.typname", i);
585 40 : snprintf(ft, sizeof(ft),
586 : "pg_catalog.format_type(t%d.oid, NULL)", i);
587 40 : snprintf(tiv, sizeof(tiv),
588 : "pg_catalog.pg_type_is_visible(t%d.oid)", i);
589 40 : if (!validateSQLNamePattern(&buf,
590 40 : map_typename_pattern(arg_patterns[i]),
591 : true, false,
592 : nspname, typname, ft, tiv,
593 : NULL, 3))
594 0 : goto error_return;
595 : }
596 : else
597 : {
598 : /* "-" pattern specifies no such parameter */
599 4 : appendPQExpBuffer(&buf, " AND t%d.typname IS NULL\n", i);
600 : }
601 : }
602 :
603 189 : if (!showSystem && !func_pattern)
604 1 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
605 : " AND n.nspname <> 'information_schema'\n");
606 :
607 189 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
608 :
609 189 : res = PSQLexec(buf.data);
610 189 : termPQExpBuffer(&buf);
611 189 : if (!res)
612 0 : return false;
613 :
614 189 : myopt.title = _("List of functions");
615 189 : myopt.translate_header = true;
616 189 : if (pset.sversion >= 90600)
617 : {
618 189 : myopt.translate_columns = translate_columns;
619 189 : myopt.n_translate_columns = lengthof(translate_columns);
620 : }
621 : else
622 : {
623 0 : myopt.translate_columns = translate_columns_pre_96;
624 0 : myopt.n_translate_columns = lengthof(translate_columns_pre_96);
625 : }
626 :
627 189 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
628 :
629 189 : PQclear(res);
630 189 : return true;
631 :
632 16 : error_return:
633 16 : termPQExpBuffer(&buf);
634 16 : return false;
635 : }
636 :
637 :
638 :
639 : /*
640 : * \dT
641 : * describe types
642 : */
643 : bool
644 47 : describeTypes(const char *pattern, bool verbose, bool showSystem)
645 : {
646 : PQExpBufferData buf;
647 : PGresult *res;
648 47 : printQueryOpt myopt = pset.popt;
649 :
650 47 : initPQExpBuffer(&buf);
651 :
652 47 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching types"));
653 47 : appendPQExpBuffer(&buf,
654 : "SELECT n.nspname as \"%s\",\n"
655 : " pg_catalog.format_type(t.oid, NULL) AS \"%s\",\n",
656 : gettext_noop("Schema"),
657 : gettext_noop("Name"));
658 47 : if (verbose)
659 : {
660 8 : appendPQExpBuffer(&buf,
661 : " t.typname AS \"%s\",\n"
662 : " CASE WHEN t.typrelid != 0\n"
663 : " THEN CAST('tuple' AS pg_catalog.text)\n"
664 : " WHEN t.typlen < 0\n"
665 : " THEN CAST('var' AS pg_catalog.text)\n"
666 : " ELSE CAST(t.typlen AS pg_catalog.text)\n"
667 : " END AS \"%s\",\n"
668 : " pg_catalog.array_to_string(\n"
669 : " ARRAY(\n"
670 : " SELECT e.enumlabel\n"
671 : " FROM pg_catalog.pg_enum e\n"
672 : " WHERE e.enumtypid = t.oid\n"
673 : " ORDER BY e.enumsortorder\n"
674 : " ),\n"
675 : " E'\\n'\n"
676 : " ) AS \"%s\",\n"
677 : " pg_catalog.pg_get_userbyid(t.typowner) AS \"%s\",\n",
678 : gettext_noop("Internal name"),
679 : gettext_noop("Size"),
680 : gettext_noop("Elements"),
681 : gettext_noop("Owner"));
682 8 : printACLColumn(&buf, "t.typacl");
683 8 : appendPQExpBufferStr(&buf, ",\n ");
684 : }
685 :
686 47 : appendPQExpBuffer(&buf,
687 : " pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
688 : gettext_noop("Description"));
689 :
690 47 : appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_type t\n"
691 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
692 :
693 : /*
694 : * do not include complex types (typrelid!=0) unless they are standalone
695 : * composite types
696 : */
697 47 : appendPQExpBufferStr(&buf, "WHERE (t.typrelid = 0 ");
698 47 : appendPQExpBufferStr(&buf, "OR (SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE)
699 : " FROM pg_catalog.pg_class c "
700 : "WHERE c.oid = t.typrelid))\n");
701 :
702 : /*
703 : * do not include array types unless the pattern contains []
704 : */
705 47 : if (pattern == NULL || strstr(pattern, "[]") == NULL)
706 47 : appendPQExpBufferStr(&buf, " AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)\n");
707 :
708 47 : if (!showSystem && !pattern)
709 3 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
710 : " AND n.nspname <> 'information_schema'\n");
711 :
712 : /* Match name pattern against either internal or external name */
713 47 : if (!validateSQLNamePattern(&buf, map_typename_pattern(pattern),
714 : true, false,
715 : "n.nspname", "t.typname",
716 : "pg_catalog.format_type(t.oid, NULL)",
717 : "pg_catalog.pg_type_is_visible(t.oid)",
718 : NULL, 3))
719 : {
720 16 : termPQExpBuffer(&buf);
721 16 : return false;
722 : }
723 :
724 31 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
725 :
726 31 : res = PSQLexec(buf.data);
727 31 : termPQExpBuffer(&buf);
728 31 : if (!res)
729 0 : return false;
730 :
731 31 : myopt.title = _("List of data types");
732 31 : myopt.translate_header = true;
733 :
734 31 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
735 :
736 31 : PQclear(res);
737 31 : return true;
738 : }
739 :
740 : /*
741 : * Map some variant type names accepted by the backend grammar into
742 : * canonical type names.
743 : *
744 : * Helper for \dT and other functions that take typename patterns.
745 : * This doesn't completely mask the fact that these names are special;
746 : * for example, a pattern of "dec*" won't magically match "numeric".
747 : * But it goes a long way to reduce the surprise factor.
748 : */
749 : static const char *
750 99 : map_typename_pattern(const char *pattern)
751 : {
752 : static const char *const typename_map[] = {
753 : /*
754 : * These names are accepted by gram.y, although they are neither the
755 : * "real" name seen in pg_type nor the canonical name printed by
756 : * format_type().
757 : */
758 : "decimal", "numeric",
759 : "float", "double precision",
760 : "int", "integer",
761 :
762 : /*
763 : * We also have to map the array names for cases where the canonical
764 : * name is different from what pg_type says.
765 : */
766 : "bool[]", "boolean[]",
767 : "decimal[]", "numeric[]",
768 : "float[]", "double precision[]",
769 : "float4[]", "real[]",
770 : "float8[]", "double precision[]",
771 : "int[]", "integer[]",
772 : "int2[]", "smallint[]",
773 : "int4[]", "integer[]",
774 : "int8[]", "bigint[]",
775 : "time[]", "time without time zone[]",
776 : "timetz[]", "time with time zone[]",
777 : "timestamp[]", "timestamp without time zone[]",
778 : "timestamptz[]", "timestamp with time zone[]",
779 : "varbit[]", "bit varying[]",
780 : "varchar[]", "character varying[]",
781 : NULL
782 : };
783 :
784 99 : if (pattern == NULL)
785 3 : return NULL;
786 1824 : for (int i = 0; typename_map[i] != NULL; i += 2)
787 : {
788 1728 : if (pg_strcasecmp(pattern, typename_map[i]) == 0)
789 0 : return typename_map[i + 1];
790 : }
791 96 : return pattern;
792 : }
793 :
794 :
795 : /*
796 : * \do
797 : * Describe operators
798 : */
799 : bool
800 44 : describeOperators(const char *oper_pattern,
801 : char **arg_patterns, int num_arg_patterns,
802 : bool verbose, bool showSystem)
803 : {
804 : PQExpBufferData buf;
805 : PGresult *res;
806 44 : printQueryOpt myopt = pset.popt;
807 : static const bool translate_columns[] = {false, false, false, false, false, false, true, false};
808 :
809 44 : initPQExpBuffer(&buf);
810 :
811 : /*
812 : * Note: before Postgres 9.1, we did not assign comments to any built-in
813 : * operators, preferring to let the comment on the underlying function
814 : * suffice. The coalesce() on the obj_description() calls below supports
815 : * this convention by providing a fallback lookup of a comment on the
816 : * operator's function. Since 9.1 there is a policy that every built-in
817 : * operator should have a comment; so the coalesce() is no longer
818 : * necessary so far as built-in operators are concerned. We keep it
819 : * anyway, for now, because third-party modules may still be following the
820 : * old convention.
821 : *
822 : * The support for postfix operators in this query is dead code as of
823 : * Postgres 14, but we need to keep it for as long as we support talking
824 : * to pre-v14 servers.
825 : */
826 :
827 44 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching operators"));
828 44 : appendPQExpBuffer(&buf,
829 : "SELECT n.nspname as \"%s\",\n"
830 : " o.oprname AS \"%s\",\n"
831 : " CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n"
832 : " CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n"
833 : " pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n",
834 : gettext_noop("Schema"),
835 : gettext_noop("Name"),
836 : gettext_noop("Left arg type"),
837 : gettext_noop("Right arg type"),
838 : gettext_noop("Result type"));
839 :
840 44 : if (verbose)
841 0 : appendPQExpBuffer(&buf,
842 : " o.oprcode AS \"%s\",\n"
843 : " CASE WHEN p.proleakproof THEN '%s' ELSE '%s' END AS \"%s\",\n",
844 : gettext_noop("Function"),
845 : gettext_noop("yes"),
846 : gettext_noop("no"),
847 : gettext_noop("Leakproof?"));
848 :
849 44 : appendPQExpBuffer(&buf,
850 : " coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n"
851 : " pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
852 : "FROM pg_catalog.pg_operator o\n"
853 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
854 : gettext_noop("Description"));
855 :
856 44 : if (num_arg_patterns >= 2)
857 : {
858 4 : num_arg_patterns = 2; /* ignore any additional arguments */
859 4 : appendPQExpBufferStr(&buf,
860 : " LEFT JOIN pg_catalog.pg_type t0 ON t0.oid = o.oprleft\n"
861 : " LEFT JOIN pg_catalog.pg_namespace nt0 ON nt0.oid = t0.typnamespace\n"
862 : " LEFT JOIN pg_catalog.pg_type t1 ON t1.oid = o.oprright\n"
863 : " LEFT JOIN pg_catalog.pg_namespace nt1 ON nt1.oid = t1.typnamespace\n");
864 : }
865 40 : else if (num_arg_patterns == 1)
866 : {
867 4 : appendPQExpBufferStr(&buf,
868 : " LEFT JOIN pg_catalog.pg_type t0 ON t0.oid = o.oprright\n"
869 : " LEFT JOIN pg_catalog.pg_namespace nt0 ON nt0.oid = t0.typnamespace\n");
870 : }
871 :
872 44 : if (verbose)
873 0 : appendPQExpBufferStr(&buf,
874 : " LEFT JOIN pg_catalog.pg_proc p ON p.oid = o.oprcode\n");
875 :
876 44 : if (!showSystem && !oper_pattern)
877 1 : appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
878 : " AND n.nspname <> 'information_schema'\n");
879 :
880 44 : if (!validateSQLNamePattern(&buf, oper_pattern,
881 44 : !showSystem && !oper_pattern, true,
882 : "n.nspname", "o.oprname", NULL,
883 : "pg_catalog.pg_operator_is_visible(o.oid)",
884 44 : NULL, 3))
885 16 : goto error_return;
886 :
887 28 : if (num_arg_patterns == 1)
888 4 : appendPQExpBufferStr(&buf, " AND o.oprleft = 0\n");
889 :
890 40 : for (int i = 0; i < num_arg_patterns; i++)
891 : {
892 12 : if (strcmp(arg_patterns[i], "-") != 0)
893 : {
894 : /*
895 : * Match type-name patterns against either internal or external
896 : * name, like \dT. Unlike \dT, there seems no reason to
897 : * discriminate against arrays or composite types.
898 : */
899 : char nspname[64];
900 : char typname[64];
901 : char ft[64];
902 : char tiv[64];
903 :
904 12 : snprintf(nspname, sizeof(nspname), "nt%d.nspname", i);
905 12 : snprintf(typname, sizeof(typname), "t%d.typname", i);
906 12 : snprintf(ft, sizeof(ft),
907 : "pg_catalog.format_type(t%d.oid, NULL)", i);
908 12 : snprintf(tiv, sizeof(tiv),
909 : "pg_catalog.pg_type_is_visible(t%d.oid)", i);
910 12 : if (!validateSQLNamePattern(&buf,
911 12 : map_typename_pattern(arg_patterns[i]),
912 : true, false,
913 : nspname, typname, ft, tiv,
914 : NULL, 3))
915 0 : goto error_return;
916 : }
917 : else
918 : {
919 : /* "-" pattern specifies no such parameter */
920 0 : appendPQExpBuffer(&buf, " AND t%d.typname IS NULL\n", i);
921 : }
922 : }
923 :
924 28 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3, 4;");
925 :
926 28 : res = PSQLexec(buf.data);
927 28 : termPQExpBuffer(&buf);
928 28 : if (!res)
929 0 : return false;
930 :
931 28 : myopt.title = _("List of operators");
932 28 : myopt.translate_header = true;
933 28 : myopt.translate_columns = translate_columns;
934 28 : myopt.n_translate_columns = lengthof(translate_columns);
935 :
936 28 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
937 :
938 28 : PQclear(res);
939 28 : return true;
940 :
941 16 : error_return:
942 16 : termPQExpBuffer(&buf);
943 16 : return false;
944 : }
945 :
946 :
947 : /*
948 : * listAllDbs
949 : *
950 : * for \l, \list, and -l switch
951 : */
952 : bool
953 0 : listAllDbs(const char *pattern, bool verbose)
954 : {
955 : PGresult *res;
956 : PQExpBufferData buf;
957 0 : printQueryOpt myopt = pset.popt;
958 :
959 0 : initPQExpBuffer(&buf);
960 :
961 0 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching databases"));
962 0 : appendPQExpBuffer(&buf,
963 : "SELECT\n"
964 : " d.datname as \"%s\",\n"
965 : " pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n"
966 : " pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n",
967 : gettext_noop("Name"),
968 : gettext_noop("Owner"),
969 : gettext_noop("Encoding"));
970 0 : if (pset.sversion >= 150000)
971 0 : appendPQExpBuffer(&buf,
972 : " CASE d.datlocprovider "
973 : "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
974 : "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
975 : "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
976 : "END AS \"%s\",\n",
977 : gettext_noop("Locale Provider"));
978 : else
979 0 : appendPQExpBuffer(&buf,
980 : " 'libc' AS \"%s\",\n",
981 : gettext_noop("Locale Provider"));
982 0 : appendPQExpBuffer(&buf,
983 : " d.datcollate as \"%s\",\n"
984 : " d.datctype as \"%s\",\n",
985 : gettext_noop("Collate"),
986 : gettext_noop("Ctype"));
987 0 : if (pset.sversion >= 170000)
988 0 : appendPQExpBuffer(&buf,
989 : " d.datlocale as \"%s\",\n",
990 : gettext_noop("Locale"));
991 0 : else if (pset.sversion >= 150000)
992 0 : appendPQExpBuffer(&buf,
993 : " d.daticulocale as \"%s\",\n",
994 : gettext_noop("Locale"));
995 : else
996 0 : appendPQExpBuffer(&buf,
997 : " NULL as \"%s\",\n",
998 : gettext_noop("Locale"));
999 0 : if (pset.sversion >= 160000)
1000 0 : appendPQExpBuffer(&buf,
1001 : " d.daticurules as \"%s\",\n",
1002 : gettext_noop("ICU Rules"));
1003 : else
1004 0 : appendPQExpBuffer(&buf,
1005 : " NULL as \"%s\",\n",
1006 : gettext_noop("ICU Rules"));
1007 0 : appendPQExpBufferStr(&buf, " ");
1008 0 : printACLColumn(&buf, "d.datacl");
1009 0 : if (verbose)
1010 0 : appendPQExpBuffer(&buf,
1011 : ",\n CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n"
1012 : " THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n"
1013 : " ELSE 'No Access'\n"
1014 : " END as \"%s\""
1015 : ",\n t.spcname as \"%s\""
1016 : ",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"",
1017 : gettext_noop("Size"),
1018 : gettext_noop("Tablespace"),
1019 : gettext_noop("Description"));
1020 0 : appendPQExpBufferStr(&buf,
1021 : "\nFROM pg_catalog.pg_database d\n");
1022 0 : if (verbose)
1023 0 : appendPQExpBufferStr(&buf,
1024 : " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n");
1025 :
1026 0 : if (pattern)
1027 : {
1028 0 : if (!validateSQLNamePattern(&buf, pattern, false, false,
1029 : NULL, "d.datname", NULL, NULL,
1030 : NULL, 1))
1031 : {
1032 0 : termPQExpBuffer(&buf);
1033 0 : return false;
1034 : }
1035 : }
1036 :
1037 0 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
1038 0 : res = PSQLexec(buf.data);
1039 0 : termPQExpBuffer(&buf);
1040 0 : if (!res)
1041 0 : return false;
1042 :
1043 0 : myopt.title = _("List of databases");
1044 0 : myopt.translate_header = true;
1045 :
1046 0 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1047 :
1048 0 : PQclear(res);
1049 0 : return true;
1050 : }
1051 :
1052 :
1053 : /*
1054 : * List Tables' Grant/Revoke Permissions
1055 : * \z (now also \dp -- perhaps more mnemonic)
1056 : */
1057 : bool
1058 64 : permissionsList(const char *pattern, bool showSystem)
1059 : {
1060 : PQExpBufferData buf;
1061 : PGresult *res;
1062 64 : printQueryOpt myopt = pset.popt;
1063 : static const bool translate_columns[] = {false, false, true, false, false, false};
1064 :
1065 64 : initPQExpBuffer(&buf);
1066 :
1067 : /*
1068 : * we ignore indexes and toast tables since they have no meaningful rights
1069 : */
1070 64 : printfPQExpBuffer(&buf, "/* %s */\n",
1071 : _("Get access privileges of matching relations"));
1072 64 : appendPQExpBuffer(&buf,
1073 : "SELECT n.nspname as \"%s\",\n"
1074 : " c.relname as \"%s\",\n"
1075 : " CASE c.relkind"
1076 : " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
1077 : " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'"
1078 : " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
1079 : " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
1080 : " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
1081 : " WHEN " CppAsString2(RELKIND_PROPGRAPH) " THEN '%s'"
1082 : " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
1083 : " END as \"%s\",\n"
1084 : " ",
1085 : gettext_noop("Schema"),
1086 : gettext_noop("Name"),
1087 : gettext_noop("table"),
1088 : gettext_noop("view"),
1089 : gettext_noop("materialized view"),
1090 : gettext_noop("sequence"),
1091 : gettext_noop("foreign table"),
1092 : gettext_noop("property graph"),
1093 : gettext_noop("partitioned table"),
1094 : gettext_noop("Type"));
1095 :
1096 64 : printACLColumn(&buf, "c.relacl");
1097 :
1098 : /*
1099 : * The formatting of attacl should match printACLColumn(). However, we
1100 : * need no special case for an empty attacl, because the backend always
1101 : * optimizes that back to NULL.
1102 : */
1103 64 : appendPQExpBuffer(&buf,
1104 : ",\n pg_catalog.array_to_string(ARRAY(\n"
1105 : " SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n"
1106 : " FROM pg_catalog.pg_attribute a\n"
1107 : " WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n"
1108 : " ), E'\\n') AS \"%s\"",
1109 : gettext_noop("Column privileges"));
1110 :
1111 64 : if (pset.sversion >= 90500 && pset.sversion < 100000)
1112 0 : appendPQExpBuffer(&buf,
1113 : ",\n pg_catalog.array_to_string(ARRAY(\n"
1114 : " SELECT polname\n"
1115 : " || CASE WHEN polcmd != '*' THEN\n"
1116 : " E' (' || polcmd::pg_catalog.text || E'):'\n"
1117 : " ELSE E':'\n"
1118 : " END\n"
1119 : " || CASE WHEN polqual IS NOT NULL THEN\n"
1120 : " E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
1121 : " ELSE E''\n"
1122 : " END\n"
1123 : " || CASE WHEN polwithcheck IS NOT NULL THEN\n"
1124 : " E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
1125 : " ELSE E''\n"
1126 : " END"
1127 : " || CASE WHEN polroles <> '{0}' THEN\n"
1128 : " E'\\n to: ' || pg_catalog.array_to_string(\n"
1129 : " ARRAY(\n"
1130 : " SELECT rolname\n"
1131 : " FROM pg_catalog.pg_roles\n"
1132 : " WHERE oid = ANY (polroles)\n"
1133 : " ORDER BY 1\n"
1134 : " ), E', ')\n"
1135 : " ELSE E''\n"
1136 : " END\n"
1137 : " FROM pg_catalog.pg_policy pol\n"
1138 : " WHERE polrelid = c.oid), E'\\n')\n"
1139 : " AS \"%s\"",
1140 : gettext_noop("Policies"));
1141 :
1142 64 : if (pset.sversion >= 100000)
1143 64 : appendPQExpBuffer(&buf,
1144 : ",\n pg_catalog.array_to_string(ARRAY(\n"
1145 : " SELECT polname\n"
1146 : " || CASE WHEN NOT polpermissive THEN\n"
1147 : " E' (RESTRICTIVE)'\n"
1148 : " ELSE '' END\n"
1149 : " || CASE WHEN polcmd != '*' THEN\n"
1150 : " E' (' || polcmd::pg_catalog.text || E'):'\n"
1151 : " ELSE E':'\n"
1152 : " END\n"
1153 : " || CASE WHEN polqual IS NOT NULL THEN\n"
1154 : " E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
1155 : " ELSE E''\n"
1156 : " END\n"
1157 : " || CASE WHEN polwithcheck IS NOT NULL THEN\n"
1158 : " E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
1159 : " ELSE E''\n"
1160 : " END"
1161 : " || CASE WHEN polroles <> '{0}' THEN\n"
1162 : " E'\\n to: ' || pg_catalog.array_to_string(\n"
1163 : " ARRAY(\n"
1164 : " SELECT rolname\n"
1165 : " FROM pg_catalog.pg_roles\n"
1166 : " WHERE oid = ANY (polroles)\n"
1167 : " ORDER BY 1\n"
1168 : " ), E', ')\n"
1169 : " ELSE E''\n"
1170 : " END\n"
1171 : " FROM pg_catalog.pg_policy pol\n"
1172 : " WHERE polrelid = c.oid), E'\\n')\n"
1173 : " AS \"%s\"",
1174 : gettext_noop("Policies"));
1175 :
1176 64 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
1177 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
1178 : "WHERE c.relkind IN ("
1179 : CppAsString2(RELKIND_RELATION) ","
1180 : CppAsString2(RELKIND_VIEW) ","
1181 : CppAsString2(RELKIND_MATVIEW) ","
1182 : CppAsString2(RELKIND_SEQUENCE) ","
1183 : CppAsString2(RELKIND_FOREIGN_TABLE) ","
1184 : CppAsString2(RELKIND_PROPGRAPH) ","
1185 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
1186 :
1187 64 : if (!showSystem && !pattern)
1188 4 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1189 : " AND n.nspname <> 'information_schema'\n");
1190 :
1191 64 : if (!validateSQLNamePattern(&buf, pattern, true, false,
1192 : "n.nspname", "c.relname", NULL,
1193 : "pg_catalog.pg_table_is_visible(c.oid)",
1194 : NULL, 3))
1195 16 : goto error_return;
1196 :
1197 48 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
1198 :
1199 48 : res = PSQLexec(buf.data);
1200 48 : if (!res)
1201 0 : goto error_return;
1202 :
1203 48 : printfPQExpBuffer(&buf, _("Access privileges"));
1204 48 : myopt.title = buf.data;
1205 48 : myopt.translate_header = true;
1206 48 : myopt.translate_columns = translate_columns;
1207 48 : myopt.n_translate_columns = lengthof(translate_columns);
1208 :
1209 48 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1210 :
1211 48 : termPQExpBuffer(&buf);
1212 48 : PQclear(res);
1213 48 : return true;
1214 :
1215 16 : error_return:
1216 16 : termPQExpBuffer(&buf);
1217 16 : return false;
1218 : }
1219 :
1220 :
1221 : /*
1222 : * \ddp
1223 : *
1224 : * List Default ACLs. The pattern can match either schema or role name.
1225 : */
1226 : bool
1227 24 : listDefaultACLs(const char *pattern)
1228 : {
1229 : PQExpBufferData buf;
1230 : PGresult *res;
1231 24 : printQueryOpt myopt = pset.popt;
1232 : static const bool translate_columns[] = {false, false, true, false};
1233 :
1234 24 : initPQExpBuffer(&buf);
1235 :
1236 24 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching default ACLs"));
1237 24 : appendPQExpBuffer(&buf,
1238 : "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
1239 : " n.nspname AS \"%s\",\n"
1240 : " CASE d.defaclobjtype "
1241 : " WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s'"
1242 : " WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n"
1243 : " ",
1244 : gettext_noop("Owner"),
1245 : gettext_noop("Schema"),
1246 : DEFACLOBJ_RELATION,
1247 : gettext_noop("table"),
1248 : DEFACLOBJ_SEQUENCE,
1249 : gettext_noop("sequence"),
1250 : DEFACLOBJ_FUNCTION,
1251 : gettext_noop("function"),
1252 : DEFACLOBJ_TYPE,
1253 : gettext_noop("type"),
1254 : DEFACLOBJ_NAMESPACE,
1255 : gettext_noop("schema"),
1256 : DEFACLOBJ_LARGEOBJECT,
1257 : gettext_noop("large object"),
1258 : gettext_noop("Type"));
1259 :
1260 24 : printACLColumn(&buf, "d.defaclacl");
1261 :
1262 24 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
1263 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
1264 :
1265 24 : if (!validateSQLNamePattern(&buf, pattern, false, false,
1266 : NULL,
1267 : "n.nspname",
1268 : "pg_catalog.pg_get_userbyid(d.defaclrole)",
1269 : NULL,
1270 : NULL, 3))
1271 16 : goto error_return;
1272 :
1273 8 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
1274 :
1275 8 : res = PSQLexec(buf.data);
1276 8 : if (!res)
1277 0 : goto error_return;
1278 :
1279 8 : printfPQExpBuffer(&buf, _("Default access privileges"));
1280 8 : myopt.title = buf.data;
1281 8 : myopt.translate_header = true;
1282 8 : myopt.translate_columns = translate_columns;
1283 8 : myopt.n_translate_columns = lengthof(translate_columns);
1284 :
1285 8 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1286 :
1287 8 : termPQExpBuffer(&buf);
1288 8 : PQclear(res);
1289 8 : return true;
1290 :
1291 16 : error_return:
1292 16 : termPQExpBuffer(&buf);
1293 16 : return false;
1294 : }
1295 :
1296 :
1297 : /*
1298 : * Get object comments
1299 : *
1300 : * \dd [foo]
1301 : *
1302 : * Note: This command only lists comments for object types which do not have
1303 : * their comments displayed by their own backslash commands. The following
1304 : * types of objects will be displayed: constraint, operator class,
1305 : * operator family, rule, and trigger.
1306 : *
1307 : */
1308 : bool
1309 28 : objectDescription(const char *pattern, bool showSystem)
1310 : {
1311 : PQExpBufferData buf;
1312 : PGresult *res;
1313 28 : printQueryOpt myopt = pset.popt;
1314 : static const bool translate_columns[] = {false, false, true, false};
1315 :
1316 28 : initPQExpBuffer(&buf);
1317 :
1318 28 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching object comments"));
1319 28 : appendPQExpBuffer(&buf,
1320 : "SELECT DISTINCT tt.nspname AS \"%s\", tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n"
1321 : "FROM (\n",
1322 : gettext_noop("Schema"),
1323 : gettext_noop("Name"),
1324 : gettext_noop("Object"),
1325 : gettext_noop("Description"));
1326 :
1327 : /* Table constraint descriptions */
1328 28 : appendPQExpBuffer(&buf,
1329 : " SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
1330 : " n.nspname as nspname,\n"
1331 : " CAST(pgc.conname AS pg_catalog.text) as name,"
1332 : " CAST('%s' AS pg_catalog.text) as object\n"
1333 : " FROM pg_catalog.pg_constraint pgc\n"
1334 : " JOIN pg_catalog.pg_class c "
1335 : "ON c.oid = pgc.conrelid\n"
1336 : " LEFT JOIN pg_catalog.pg_namespace n "
1337 : " ON n.oid = c.relnamespace\n",
1338 : gettext_noop("table constraint"));
1339 :
1340 28 : if (!showSystem && !pattern)
1341 0 : appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1342 : " AND n.nspname <> 'information_schema'\n");
1343 :
1344 56 : if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern,
1345 : false, "n.nspname", "pgc.conname", NULL,
1346 : "pg_catalog.pg_table_is_visible(c.oid)",
1347 28 : NULL, 3))
1348 16 : goto error_return;
1349 :
1350 : /* Domain constraint descriptions */
1351 12 : appendPQExpBuffer(&buf,
1352 : "UNION ALL\n"
1353 : " SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
1354 : " n.nspname as nspname,\n"
1355 : " CAST(pgc.conname AS pg_catalog.text) as name,"
1356 : " CAST('%s' AS pg_catalog.text) as object\n"
1357 : " FROM pg_catalog.pg_constraint pgc\n"
1358 : " JOIN pg_catalog.pg_type t "
1359 : "ON t.oid = pgc.contypid\n"
1360 : " LEFT JOIN pg_catalog.pg_namespace n "
1361 : " ON n.oid = t.typnamespace\n",
1362 : gettext_noop("domain constraint"));
1363 :
1364 12 : if (!showSystem && !pattern)
1365 0 : appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1366 : " AND n.nspname <> 'information_schema'\n");
1367 :
1368 24 : if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern,
1369 : false, "n.nspname", "pgc.conname", NULL,
1370 : "pg_catalog.pg_type_is_visible(t.oid)",
1371 12 : NULL, 3))
1372 0 : goto error_return;
1373 :
1374 : /* Operator class descriptions */
1375 12 : appendPQExpBuffer(&buf,
1376 : "UNION ALL\n"
1377 : " SELECT o.oid as oid, o.tableoid as tableoid,\n"
1378 : " n.nspname as nspname,\n"
1379 : " CAST(o.opcname AS pg_catalog.text) as name,\n"
1380 : " CAST('%s' AS pg_catalog.text) as object\n"
1381 : " FROM pg_catalog.pg_opclass o\n"
1382 : " JOIN pg_catalog.pg_am am ON "
1383 : "o.opcmethod = am.oid\n"
1384 : " JOIN pg_catalog.pg_namespace n ON "
1385 : "n.oid = o.opcnamespace\n",
1386 : gettext_noop("operator class"));
1387 :
1388 12 : if (!showSystem && !pattern)
1389 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1390 : " AND n.nspname <> 'information_schema'\n");
1391 :
1392 12 : if (!validateSQLNamePattern(&buf, pattern, true, false,
1393 : "n.nspname", "o.opcname", NULL,
1394 : "pg_catalog.pg_opclass_is_visible(o.oid)",
1395 : NULL, 3))
1396 0 : goto error_return;
1397 :
1398 : /* Operator family descriptions */
1399 12 : appendPQExpBuffer(&buf,
1400 : "UNION ALL\n"
1401 : " SELECT opf.oid as oid, opf.tableoid as tableoid,\n"
1402 : " n.nspname as nspname,\n"
1403 : " CAST(opf.opfname AS pg_catalog.text) AS name,\n"
1404 : " CAST('%s' AS pg_catalog.text) as object\n"
1405 : " FROM pg_catalog.pg_opfamily opf\n"
1406 : " JOIN pg_catalog.pg_am am "
1407 : "ON opf.opfmethod = am.oid\n"
1408 : " JOIN pg_catalog.pg_namespace n "
1409 : "ON opf.opfnamespace = n.oid\n",
1410 : gettext_noop("operator family"));
1411 :
1412 12 : if (!showSystem && !pattern)
1413 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1414 : " AND n.nspname <> 'information_schema'\n");
1415 :
1416 12 : if (!validateSQLNamePattern(&buf, pattern, true, false,
1417 : "n.nspname", "opf.opfname", NULL,
1418 : "pg_catalog.pg_opfamily_is_visible(opf.oid)",
1419 : NULL, 3))
1420 0 : goto error_return;
1421 :
1422 : /* Rule descriptions (ignore rules for views) */
1423 12 : appendPQExpBuffer(&buf,
1424 : "UNION ALL\n"
1425 : " SELECT r.oid as oid, r.tableoid as tableoid,\n"
1426 : " n.nspname as nspname,\n"
1427 : " CAST(r.rulename AS pg_catalog.text) as name,"
1428 : " CAST('%s' AS pg_catalog.text) as object\n"
1429 : " FROM pg_catalog.pg_rewrite r\n"
1430 : " JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n"
1431 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
1432 : " WHERE r.rulename != '_RETURN'\n",
1433 : gettext_noop("rule"));
1434 :
1435 12 : if (!showSystem && !pattern)
1436 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1437 : " AND n.nspname <> 'information_schema'\n");
1438 :
1439 12 : if (!validateSQLNamePattern(&buf, pattern, true, false,
1440 : "n.nspname", "r.rulename", NULL,
1441 : "pg_catalog.pg_table_is_visible(c.oid)",
1442 : NULL, 3))
1443 0 : goto error_return;
1444 :
1445 : /* Trigger descriptions */
1446 12 : appendPQExpBuffer(&buf,
1447 : "UNION ALL\n"
1448 : " SELECT t.oid as oid, t.tableoid as tableoid,\n"
1449 : " n.nspname as nspname,\n"
1450 : " CAST(t.tgname AS pg_catalog.text) as name,"
1451 : " CAST('%s' AS pg_catalog.text) as object\n"
1452 : " FROM pg_catalog.pg_trigger t\n"
1453 : " JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n"
1454 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n",
1455 : gettext_noop("trigger"));
1456 :
1457 12 : if (!showSystem && !pattern)
1458 0 : appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1459 : " AND n.nspname <> 'information_schema'\n");
1460 :
1461 24 : if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern, false,
1462 : "n.nspname", "t.tgname", NULL,
1463 : "pg_catalog.pg_table_is_visible(c.oid)",
1464 12 : NULL, 3))
1465 0 : goto error_return;
1466 :
1467 12 : appendPQExpBufferStr(&buf,
1468 : ") AS tt\n"
1469 : " JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n");
1470 :
1471 12 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
1472 :
1473 12 : res = PSQLexec(buf.data);
1474 12 : termPQExpBuffer(&buf);
1475 12 : if (!res)
1476 0 : return false;
1477 :
1478 12 : myopt.title = _("Object descriptions");
1479 12 : myopt.translate_header = true;
1480 12 : myopt.translate_columns = translate_columns;
1481 12 : myopt.n_translate_columns = lengthof(translate_columns);
1482 :
1483 12 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1484 :
1485 12 : PQclear(res);
1486 12 : return true;
1487 :
1488 16 : error_return:
1489 16 : termPQExpBuffer(&buf);
1490 16 : return false;
1491 : }
1492 :
1493 :
1494 : /*
1495 : * describeTableDetails (for \d)
1496 : *
1497 : * This routine finds the tables to be displayed, and calls
1498 : * describeOneTableDetails for each one.
1499 : *
1500 : * verbose: if true, this is \d+
1501 : */
1502 : bool
1503 2742 : describeTableDetails(const char *pattern, bool verbose, bool showSystem)
1504 : {
1505 : PQExpBufferData buf;
1506 : PGresult *res;
1507 : int i;
1508 :
1509 2742 : initPQExpBuffer(&buf);
1510 :
1511 2742 : printfPQExpBuffer(&buf, "/* %s */\n",
1512 : _("Get matching relations to describe"));
1513 2742 : appendPQExpBuffer(&buf,
1514 : "SELECT c.oid,\n"
1515 : " n.nspname,\n"
1516 : " c.relname\n"
1517 : "FROM pg_catalog.pg_class c\n"
1518 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
1519 :
1520 2742 : if (!showSystem && !pattern)
1521 0 : appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1522 : " AND n.nspname <> 'information_schema'\n");
1523 :
1524 5484 : if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern, false,
1525 : "n.nspname", "c.relname", NULL,
1526 : "pg_catalog.pg_table_is_visible(c.oid)",
1527 2742 : NULL, 3))
1528 : {
1529 0 : termPQExpBuffer(&buf);
1530 0 : return false;
1531 : }
1532 :
1533 2742 : appendPQExpBufferStr(&buf, "ORDER BY 2, 3;");
1534 :
1535 2742 : res = PSQLexec(buf.data);
1536 2742 : termPQExpBuffer(&buf);
1537 2742 : if (!res)
1538 0 : return false;
1539 :
1540 2742 : if (PQntuples(res) == 0)
1541 : {
1542 28 : if (!pset.quiet)
1543 : {
1544 0 : if (pattern)
1545 0 : pg_log_error("Did not find any relation named \"%s\".",
1546 : pattern);
1547 : else
1548 0 : pg_log_error("Did not find any relations.");
1549 : }
1550 28 : PQclear(res);
1551 28 : return false;
1552 : }
1553 :
1554 5510 : for (i = 0; i < PQntuples(res); i++)
1555 : {
1556 : const char *oid;
1557 : const char *nspname;
1558 : const char *relname;
1559 :
1560 2796 : oid = PQgetvalue(res, i, 0);
1561 2796 : nspname = PQgetvalue(res, i, 1);
1562 2796 : relname = PQgetvalue(res, i, 2);
1563 :
1564 2796 : if (!describeOneTableDetails(nspname, relname, oid, verbose))
1565 : {
1566 0 : PQclear(res);
1567 0 : return false;
1568 : }
1569 2796 : if (cancel_pressed)
1570 : {
1571 0 : PQclear(res);
1572 0 : return false;
1573 : }
1574 : }
1575 :
1576 2714 : PQclear(res);
1577 2714 : return true;
1578 : }
1579 :
1580 : /*
1581 : * describeOneTableDetails (for \d)
1582 : *
1583 : * Unfortunately, the information presented here is so complicated that it
1584 : * cannot be done in a single query. So we have to assemble the printed table
1585 : * by hand and pass it to the underlying printTable() function.
1586 : */
1587 : static bool
1588 2796 : describeOneTableDetails(const char *schemaname,
1589 : const char *relationname,
1590 : const char *oid,
1591 : bool verbose)
1592 : {
1593 2796 : bool retval = false;
1594 : PQExpBufferData buf;
1595 2796 : PGresult *res = NULL;
1596 2796 : printTableOpt myopt = pset.popt.topt;
1597 : printTableContent cont;
1598 2796 : bool printTableInitialized = false;
1599 : int i;
1600 2796 : char *view_def = NULL;
1601 : char *headers[12];
1602 : PQExpBufferData title;
1603 : PQExpBufferData tmpbuf;
1604 : int cols;
1605 2796 : int attname_col = -1, /* column indexes in "res" */
1606 2796 : atttype_col = -1,
1607 2796 : attrdef_col = -1,
1608 2796 : attnotnull_col = -1,
1609 2796 : attcoll_col = -1,
1610 2796 : attidentity_col = -1,
1611 2796 : attgenerated_col = -1,
1612 2796 : isindexkey_col = -1,
1613 2796 : indexdef_col = -1,
1614 2796 : fdwopts_col = -1,
1615 2796 : attstorage_col = -1,
1616 2796 : attcompression_col = -1,
1617 2796 : attstattarget_col = -1,
1618 2796 : attdescr_col = -1;
1619 : int numrows;
1620 : struct
1621 : {
1622 : int16 checks;
1623 : char relkind;
1624 : bool hasindex;
1625 : bool hasrules;
1626 : bool hastriggers;
1627 : bool rowsecurity;
1628 : bool forcerowsecurity;
1629 : bool hasoids;
1630 : bool ispartition;
1631 : Oid tablespace;
1632 : char *reloptions;
1633 : char *reloftype;
1634 : char relpersistence;
1635 : char relreplident;
1636 : char *relam;
1637 : } tableinfo;
1638 2796 : bool show_column_details = false;
1639 :
1640 2796 : myopt.default_footer = false;
1641 : /* This output looks confusing in expanded mode. */
1642 2796 : myopt.expanded = false;
1643 :
1644 2796 : initPQExpBuffer(&buf);
1645 2796 : initPQExpBuffer(&title);
1646 2796 : initPQExpBuffer(&tmpbuf);
1647 :
1648 : /* Get general table info */
1649 2796 : printfPQExpBuffer(&buf, "/* %s */\n",
1650 : _("Get general information about one relation"));
1651 2796 : if (pset.sversion >= 120000)
1652 : {
1653 2796 : appendPQExpBuffer(&buf,
1654 : "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1655 : "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1656 : "false AS relhasoids, c.relispartition, %s, c.reltablespace, "
1657 : "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1658 : "c.relpersistence, c.relreplident, am.amname\n"
1659 : "FROM pg_catalog.pg_class c\n "
1660 : "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1661 : "LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid)\n"
1662 : "WHERE c.oid = '%s';",
1663 : (verbose ?
1664 : "pg_catalog.array_to_string(c.reloptions || "
1665 : "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1666 : : "''"),
1667 : oid);
1668 : }
1669 0 : else if (pset.sversion >= 100000)
1670 : {
1671 0 : appendPQExpBuffer(&buf,
1672 : "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1673 : "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1674 : "c.relhasoids, c.relispartition, %s, c.reltablespace, "
1675 : "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1676 : "c.relpersistence, c.relreplident\n"
1677 : "FROM pg_catalog.pg_class c\n "
1678 : "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1679 : "WHERE c.oid = '%s';",
1680 : (verbose ?
1681 : "pg_catalog.array_to_string(c.reloptions || "
1682 : "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1683 : : "''"),
1684 : oid);
1685 : }
1686 0 : else if (pset.sversion >= 90500)
1687 : {
1688 0 : appendPQExpBuffer(&buf,
1689 : "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1690 : "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1691 : "c.relhasoids, false as relispartition, %s, c.reltablespace, "
1692 : "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1693 : "c.relpersistence, c.relreplident\n"
1694 : "FROM pg_catalog.pg_class c\n "
1695 : "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1696 : "WHERE c.oid = '%s';",
1697 : (verbose ?
1698 : "pg_catalog.array_to_string(c.reloptions || "
1699 : "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1700 : : "''"),
1701 : oid);
1702 : }
1703 0 : else if (pset.sversion >= 90400)
1704 : {
1705 0 : appendPQExpBuffer(&buf,
1706 : "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1707 : "c.relhastriggers, false, false, c.relhasoids, "
1708 : "false as relispartition, %s, c.reltablespace, "
1709 : "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1710 : "c.relpersistence, c.relreplident\n"
1711 : "FROM pg_catalog.pg_class c\n "
1712 : "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1713 : "WHERE c.oid = '%s';",
1714 : (verbose ?
1715 : "pg_catalog.array_to_string(c.reloptions || "
1716 : "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1717 : : "''"),
1718 : oid);
1719 : }
1720 : else
1721 : {
1722 0 : appendPQExpBuffer(&buf,
1723 : "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1724 : "c.relhastriggers, false, false, c.relhasoids, "
1725 : "false as relispartition, %s, c.reltablespace, "
1726 : "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1727 : "c.relpersistence\n"
1728 : "FROM pg_catalog.pg_class c\n "
1729 : "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1730 : "WHERE c.oid = '%s';",
1731 : (verbose ?
1732 : "pg_catalog.array_to_string(c.reloptions || "
1733 : "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1734 : : "''"),
1735 : oid);
1736 : }
1737 :
1738 2796 : res = PSQLexec(buf.data);
1739 2796 : if (!res)
1740 0 : goto error_return;
1741 :
1742 : /* Did we get anything? */
1743 2796 : if (PQntuples(res) == 0)
1744 : {
1745 0 : if (!pset.quiet)
1746 0 : pg_log_error("Did not find any relation with OID %s.", oid);
1747 0 : goto error_return;
1748 : }
1749 :
1750 2796 : tableinfo.checks = atoi(PQgetvalue(res, 0, 0));
1751 2796 : tableinfo.relkind = *(PQgetvalue(res, 0, 1));
1752 2796 : tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
1753 2796 : tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
1754 2796 : tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
1755 2796 : tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
1756 2796 : tableinfo.forcerowsecurity = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
1757 2796 : tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 7), "t") == 0;
1758 2796 : tableinfo.ispartition = strcmp(PQgetvalue(res, 0, 8), "t") == 0;
1759 2796 : tableinfo.reloptions = pg_strdup(PQgetvalue(res, 0, 9));
1760 2796 : tableinfo.tablespace = atooid(PQgetvalue(res, 0, 10));
1761 2796 : tableinfo.reloftype = (strcmp(PQgetvalue(res, 0, 11), "") != 0) ?
1762 2796 : pg_strdup(PQgetvalue(res, 0, 11)) : NULL;
1763 2796 : tableinfo.relpersistence = *(PQgetvalue(res, 0, 12));
1764 5592 : tableinfo.relreplident = (pset.sversion >= 90400) ?
1765 2796 : *(PQgetvalue(res, 0, 13)) : 'd';
1766 2796 : if (pset.sversion >= 120000)
1767 5592 : tableinfo.relam = PQgetisnull(res, 0, 14) ?
1768 2796 : NULL : pg_strdup(PQgetvalue(res, 0, 14));
1769 : else
1770 0 : tableinfo.relam = NULL;
1771 2796 : PQclear(res);
1772 2796 : res = NULL;
1773 :
1774 : /*
1775 : * If it's a sequence, deal with it here separately.
1776 : */
1777 2796 : if (tableinfo.relkind == RELKIND_SEQUENCE)
1778 : {
1779 136 : PGresult *result = NULL;
1780 136 : printQueryOpt myopt = pset.popt;
1781 136 : char *footers[3] = {NULL, NULL, NULL};
1782 :
1783 136 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
1784 136 : if (pset.sversion >= 100000)
1785 : {
1786 136 : appendPQExpBuffer(&buf,
1787 : "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
1788 : " seqstart AS \"%s\",\n"
1789 : " seqmin AS \"%s\",\n"
1790 : " seqmax AS \"%s\",\n"
1791 : " seqincrement AS \"%s\",\n"
1792 : " CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
1793 : " seqcache AS \"%s\"\n",
1794 : gettext_noop("Type"),
1795 : gettext_noop("Start"),
1796 : gettext_noop("Minimum"),
1797 : gettext_noop("Maximum"),
1798 : gettext_noop("Increment"),
1799 : gettext_noop("yes"),
1800 : gettext_noop("no"),
1801 : gettext_noop("Cycles?"),
1802 : gettext_noop("Cache"));
1803 136 : appendPQExpBuffer(&buf,
1804 : "FROM pg_catalog.pg_sequence\n"
1805 : "WHERE seqrelid = '%s';",
1806 : oid);
1807 : }
1808 : else
1809 : {
1810 0 : appendPQExpBuffer(&buf,
1811 : "SELECT 'bigint' AS \"%s\",\n"
1812 : " start_value AS \"%s\",\n"
1813 : " min_value AS \"%s\",\n"
1814 : " max_value AS \"%s\",\n"
1815 : " increment_by AS \"%s\",\n"
1816 : " CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n"
1817 : " cache_value AS \"%s\"\n",
1818 : gettext_noop("Type"),
1819 : gettext_noop("Start"),
1820 : gettext_noop("Minimum"),
1821 : gettext_noop("Maximum"),
1822 : gettext_noop("Increment"),
1823 : gettext_noop("yes"),
1824 : gettext_noop("no"),
1825 : gettext_noop("Cycles?"),
1826 : gettext_noop("Cache"));
1827 0 : appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
1828 : /* must be separate because fmtId isn't reentrant */
1829 0 : appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
1830 : }
1831 :
1832 136 : res = PSQLexec(buf.data);
1833 136 : if (!res)
1834 0 : goto error_return;
1835 :
1836 : /* Get the column that owns this sequence */
1837 136 : printfPQExpBuffer(&buf, "/* %s */\n",
1838 : _("Get the column that owns this sequence"));
1839 136 : appendPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
1840 : "\n pg_catalog.quote_ident(relname) || '.' ||"
1841 : "\n pg_catalog.quote_ident(attname),"
1842 : "\n d.deptype"
1843 : "\nFROM pg_catalog.pg_class c"
1844 : "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
1845 : "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
1846 : "\nINNER JOIN pg_catalog.pg_attribute a ON ("
1847 : "\n a.attrelid=c.oid AND"
1848 : "\n a.attnum=d.refobjsubid)"
1849 : "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
1850 : "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
1851 : "\n AND d.objid='%s'"
1852 : "\n AND d.deptype IN ('a', 'i')",
1853 : oid);
1854 :
1855 136 : result = PSQLexec(buf.data);
1856 :
1857 : /*
1858 : * If we get no rows back, don't show anything (obviously). We should
1859 : * never get more than one row back, but if we do, just ignore it and
1860 : * don't print anything.
1861 : */
1862 136 : if (!result)
1863 0 : goto error_return;
1864 136 : else if (PQntuples(result) == 1)
1865 : {
1866 116 : switch (PQgetvalue(result, 0, 1)[0])
1867 : {
1868 80 : case 'a':
1869 80 : footers[0] = psprintf(_("Owned by: %s"),
1870 : PQgetvalue(result, 0, 0));
1871 80 : break;
1872 36 : case 'i':
1873 36 : footers[0] = psprintf(_("Sequence for identity column: %s"),
1874 : PQgetvalue(result, 0, 0));
1875 36 : break;
1876 : }
1877 : }
1878 136 : PQclear(result);
1879 :
1880 : /* Print any publications */
1881 136 : if (pset.sversion >= 190000)
1882 : {
1883 136 : printfPQExpBuffer(&buf, "/* %s */\n",
1884 : _("Get publications containing this sequence"));
1885 136 : appendPQExpBuffer(&buf, "SELECT pubname FROM pg_catalog.pg_publication p"
1886 : "\nWHERE p.puballsequences"
1887 : "\n AND pg_catalog.pg_relation_is_publishable('%s')"
1888 : "\nORDER BY 1",
1889 : oid);
1890 :
1891 136 : result = PSQLexec(buf.data);
1892 136 : if (result)
1893 : {
1894 136 : int nrows = PQntuples(result);
1895 :
1896 136 : if (nrows > 0)
1897 : {
1898 8 : printfPQExpBuffer(&tmpbuf, _("Publications:"));
1899 20 : for (i = 0; i < nrows; i++)
1900 12 : appendPQExpBuffer(&tmpbuf, "\n \"%s\"", PQgetvalue(result, i, 0));
1901 :
1902 : /* Store in the first available footer slot */
1903 8 : if (footers[0] == NULL)
1904 8 : footers[0] = pg_strdup(tmpbuf.data);
1905 : else
1906 0 : footers[1] = pg_strdup(tmpbuf.data);
1907 :
1908 8 : resetPQExpBuffer(&tmpbuf);
1909 : }
1910 :
1911 136 : PQclear(result);
1912 : }
1913 : }
1914 :
1915 136 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
1916 20 : printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
1917 : schemaname, relationname);
1918 : else
1919 116 : printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
1920 : schemaname, relationname);
1921 :
1922 136 : myopt.footers = footers;
1923 136 : myopt.topt.default_footer = false;
1924 136 : myopt.title = title.data;
1925 136 : myopt.translate_header = true;
1926 :
1927 136 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1928 :
1929 136 : free(footers[0]);
1930 136 : free(footers[1]);
1931 :
1932 136 : retval = true;
1933 136 : goto error_return; /* not an error, just return early */
1934 : }
1935 :
1936 : /*
1937 : * If it's a property graph, deal with it here separately.
1938 : */
1939 2660 : if (tableinfo.relkind == RELKIND_PROPGRAPH)
1940 : {
1941 16 : printQueryOpt myopt = pset.popt;
1942 16 : char *footers[3] = {NULL, NULL, NULL};
1943 :
1944 16 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get property graph information"));
1945 16 : appendPQExpBuffer(&buf,
1946 : "SELECT e.pgealias AS \"%s\","
1947 : "\n pg_catalog.quote_ident(n.nspname) || '.' ||"
1948 : "\n pg_catalog.quote_ident(c.relname) AS \"%s\","
1949 : "\n case e.pgekind when " CppAsString2(PGEKIND_VERTEX) " then 'vertex'"
1950 : "\n when " CppAsString2(PGEKIND_EDGE) " then 'edge' end AS \"%s\","
1951 : "\n s.pgealias as \"%s\","
1952 : "\n d.pgealias as \"%s\""
1953 : "\n FROM pg_propgraph_element e"
1954 : "\n INNER JOIN pg_class c ON c.oid = e.pgerelid"
1955 : "\n INNER JOIN pg_namespace n ON c.relnamespace = n.oid"
1956 : "\n LEFT JOIN pg_propgraph_element s ON e.pgesrcvertexid = s.oid"
1957 : "\n LEFT JOIN pg_propgraph_element d ON e.pgedestvertexid = d.oid"
1958 : "\n WHERE e.pgepgid = '%s'"
1959 : "\n ORDER BY e.pgealias",
1960 : gettext_noop("Element Alias"),
1961 : gettext_noop("Element Table"),
1962 : gettext_noop("Element Kind"),
1963 : gettext_noop("Source Vertex Alias"),
1964 : gettext_noop("Destination Vertex Alias"),
1965 : oid);
1966 :
1967 16 : res = PSQLexec(buf.data);
1968 16 : if (!res)
1969 0 : goto error_return;
1970 :
1971 16 : printfPQExpBuffer(&title, _("Property Graph \"%s.%s\""),
1972 : schemaname, relationname);
1973 :
1974 : /* Add property graph definition in verbose mode */
1975 16 : if (verbose)
1976 : {
1977 : PGresult *result;
1978 :
1979 8 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get property graph definition"));
1980 8 : appendPQExpBuffer(&buf,
1981 : "SELECT pg_catalog.pg_get_propgraphdef('%s'::pg_catalog.oid);",
1982 : oid);
1983 8 : result = PSQLexec(buf.data);
1984 :
1985 8 : if (result)
1986 : {
1987 8 : if (PQntuples(result) > 0)
1988 : {
1989 8 : footers[0] = pg_strdup(_("Property graph definition:"));
1990 8 : footers[1] = pg_strdup(PQgetvalue(result, 0, 0));
1991 : }
1992 8 : PQclear(result);
1993 : }
1994 : }
1995 :
1996 16 : myopt.footers = footers;
1997 16 : myopt.topt.default_footer = false;
1998 16 : myopt.title = title.data;
1999 16 : myopt.translate_header = true;
2000 :
2001 16 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
2002 :
2003 16 : free(footers[0]);
2004 16 : free(footers[1]);
2005 :
2006 16 : retval = true;
2007 16 : goto error_return; /* not an error, just return early */
2008 : }
2009 :
2010 : /* Identify whether we should print collation, nullable, default vals */
2011 2644 : if (tableinfo.relkind == RELKIND_RELATION ||
2012 946 : tableinfo.relkind == RELKIND_VIEW ||
2013 696 : tableinfo.relkind == RELKIND_MATVIEW ||
2014 656 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2015 531 : tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
2016 479 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2017 2374 : show_column_details = true;
2018 :
2019 : /*
2020 : * Get per-column info
2021 : *
2022 : * Since the set of query columns we need varies depending on relkind and
2023 : * server version, we compute all the column numbers on-the-fly. Column
2024 : * number variables for columns not fetched are left as -1; this avoids
2025 : * duplicative test logic below.
2026 : */
2027 2644 : cols = 0;
2028 2644 : printfPQExpBuffer(&buf, "/* %s */\n",
2029 : _("Get per-column information for one relation"));
2030 2644 : appendPQExpBuffer(&buf, "SELECT a.attname");
2031 2644 : attname_col = cols++;
2032 2644 : appendPQExpBufferStr(&buf, ",\n pg_catalog.format_type(a.atttypid, a.atttypmod)");
2033 2644 : atttype_col = cols++;
2034 :
2035 2644 : if (show_column_details)
2036 : {
2037 : /* use "pretty" mode for expression to avoid excessive parentheses */
2038 2374 : appendPQExpBufferStr(&buf,
2039 : ",\n (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)"
2040 : "\n FROM pg_catalog.pg_attrdef d"
2041 : "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)"
2042 : ",\n a.attnotnull");
2043 2374 : attrdef_col = cols++;
2044 2374 : attnotnull_col = cols++;
2045 2374 : appendPQExpBufferStr(&buf, ",\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
2046 : " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
2047 2374 : attcoll_col = cols++;
2048 2374 : if (pset.sversion >= 100000)
2049 2374 : appendPQExpBufferStr(&buf, ",\n a.attidentity");
2050 : else
2051 0 : appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attidentity");
2052 2374 : attidentity_col = cols++;
2053 2374 : if (pset.sversion >= 120000)
2054 2374 : appendPQExpBufferStr(&buf, ",\n a.attgenerated");
2055 : else
2056 0 : appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attgenerated");
2057 2374 : attgenerated_col = cols++;
2058 : }
2059 2644 : if (tableinfo.relkind == RELKIND_INDEX ||
2060 2466 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
2061 : {
2062 266 : if (pset.sversion >= 110000)
2063 : {
2064 266 : appendPQExpBuffer(&buf, ",\n CASE WHEN a.attnum <= (SELECT i.indnkeyatts FROM pg_catalog.pg_index i WHERE i.indexrelid = '%s') THEN '%s' ELSE '%s' END AS is_key",
2065 : oid,
2066 : gettext_noop("yes"),
2067 : gettext_noop("no"));
2068 266 : isindexkey_col = cols++;
2069 : }
2070 266 : appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
2071 266 : indexdef_col = cols++;
2072 : }
2073 : /* FDW options for foreign table column */
2074 2644 : if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
2075 : {
2076 125 : appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE "
2077 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM "
2078 : " pg_catalog.pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions");
2079 125 : fdwopts_col = cols++;
2080 : }
2081 2644 : if (verbose)
2082 : {
2083 1203 : appendPQExpBufferStr(&buf, ",\n a.attstorage");
2084 1203 : attstorage_col = cols++;
2085 :
2086 : /* compression info, if relevant to relkind */
2087 1203 : if (pset.sversion >= 140000 &&
2088 1203 : !pset.hide_compression &&
2089 57 : (tableinfo.relkind == RELKIND_RELATION ||
2090 9 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
2091 9 : tableinfo.relkind == RELKIND_MATVIEW))
2092 : {
2093 56 : appendPQExpBufferStr(&buf, ",\n a.attcompression AS attcompression");
2094 56 : attcompression_col = cols++;
2095 : }
2096 :
2097 : /* stats target, if relevant to relkind */
2098 1203 : if (tableinfo.relkind == RELKIND_RELATION ||
2099 506 : tableinfo.relkind == RELKIND_INDEX ||
2100 476 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
2101 472 : tableinfo.relkind == RELKIND_MATVIEW ||
2102 432 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2103 332 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2104 : {
2105 964 : appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
2106 964 : attstattarget_col = cols++;
2107 : }
2108 :
2109 : /*
2110 : * In 9.0+, we have column comments for: relations, views, composite
2111 : * types, and foreign tables (cf. CommentObject() in comment.c).
2112 : */
2113 1203 : if (tableinfo.relkind == RELKIND_RELATION ||
2114 506 : tableinfo.relkind == RELKIND_VIEW ||
2115 267 : tableinfo.relkind == RELKIND_MATVIEW ||
2116 227 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2117 127 : tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
2118 127 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2119 : {
2120 1169 : appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)");
2121 1169 : attdescr_col = cols++;
2122 : }
2123 : }
2124 :
2125 2644 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
2126 2644 : appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
2127 2644 : appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
2128 :
2129 2644 : res = PSQLexec(buf.data);
2130 2644 : if (!res)
2131 0 : goto error_return;
2132 2644 : numrows = PQntuples(res);
2133 :
2134 : /* Make title */
2135 2644 : switch (tableinfo.relkind)
2136 : {
2137 1698 : case RELKIND_RELATION:
2138 1698 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2139 12 : printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
2140 : schemaname, relationname);
2141 : else
2142 1686 : printfPQExpBuffer(&title, _("Table \"%s.%s\""),
2143 : schemaname, relationname);
2144 1698 : break;
2145 250 : case RELKIND_VIEW:
2146 250 : printfPQExpBuffer(&title, _("View \"%s.%s\""),
2147 : schemaname, relationname);
2148 250 : break;
2149 40 : case RELKIND_MATVIEW:
2150 40 : printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""),
2151 : schemaname, relationname);
2152 40 : break;
2153 178 : case RELKIND_INDEX:
2154 178 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2155 0 : printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
2156 : schemaname, relationname);
2157 : else
2158 178 : printfPQExpBuffer(&title, _("Index \"%s.%s\""),
2159 : schemaname, relationname);
2160 178 : break;
2161 88 : case RELKIND_PARTITIONED_INDEX:
2162 88 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2163 0 : printfPQExpBuffer(&title, _("Unlogged partitioned index \"%s.%s\""),
2164 : schemaname, relationname);
2165 : else
2166 88 : printfPQExpBuffer(&title, _("Partitioned index \"%s.%s\""),
2167 : schemaname, relationname);
2168 88 : break;
2169 4 : case RELKIND_TOASTVALUE:
2170 4 : printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
2171 : schemaname, relationname);
2172 4 : break;
2173 52 : case RELKIND_COMPOSITE_TYPE:
2174 52 : printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
2175 : schemaname, relationname);
2176 52 : break;
2177 125 : case RELKIND_FOREIGN_TABLE:
2178 125 : printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
2179 : schemaname, relationname);
2180 125 : break;
2181 209 : case RELKIND_PARTITIONED_TABLE:
2182 209 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2183 0 : printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""),
2184 : schemaname, relationname);
2185 : else
2186 209 : printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""),
2187 : schemaname, relationname);
2188 209 : break;
2189 0 : default:
2190 : /* untranslated unknown relkind */
2191 0 : printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
2192 0 : tableinfo.relkind, schemaname, relationname);
2193 0 : break;
2194 : }
2195 :
2196 : /* Fill headers[] with the names of the columns we will output */
2197 2644 : cols = 0;
2198 2644 : headers[cols++] = gettext_noop("Column");
2199 2644 : headers[cols++] = gettext_noop("Type");
2200 2644 : if (show_column_details)
2201 : {
2202 2374 : headers[cols++] = gettext_noop("Collation");
2203 2374 : headers[cols++] = gettext_noop("Nullable");
2204 2374 : headers[cols++] = gettext_noop("Default");
2205 : }
2206 2644 : if (isindexkey_col >= 0)
2207 266 : headers[cols++] = gettext_noop("Key?");
2208 2644 : if (indexdef_col >= 0)
2209 266 : headers[cols++] = gettext_noop("Definition");
2210 2644 : if (fdwopts_col >= 0)
2211 125 : headers[cols++] = gettext_noop("FDW options");
2212 2644 : if (attstorage_col >= 0)
2213 1203 : headers[cols++] = gettext_noop("Storage");
2214 2644 : if (attcompression_col >= 0)
2215 56 : headers[cols++] = gettext_noop("Compression");
2216 2644 : if (attstattarget_col >= 0)
2217 964 : headers[cols++] = gettext_noop("Stats target");
2218 2644 : if (attdescr_col >= 0)
2219 1169 : headers[cols++] = gettext_noop("Description");
2220 :
2221 : Assert(cols <= lengthof(headers));
2222 :
2223 2644 : printTableInit(&cont, &myopt, title.data, cols, numrows);
2224 2644 : printTableInitialized = true;
2225 :
2226 19103 : for (i = 0; i < cols; i++)
2227 16459 : printTableAddHeader(&cont, headers[i], true, 'l');
2228 :
2229 : /* Generate table cells to be printed */
2230 8976 : for (i = 0; i < numrows; i++)
2231 : {
2232 : /* Column */
2233 6332 : printTableAddCell(&cont, PQgetvalue(res, i, attname_col), false, false);
2234 :
2235 : /* Type */
2236 6332 : printTableAddCell(&cont, PQgetvalue(res, i, atttype_col), false, false);
2237 :
2238 : /* Collation, Nullable, Default */
2239 6332 : if (show_column_details)
2240 : {
2241 : char *identity;
2242 : char *generated;
2243 : char *default_str;
2244 6014 : bool mustfree = false;
2245 :
2246 6014 : printTableAddCell(&cont, PQgetvalue(res, i, attcoll_col), false, false);
2247 :
2248 6014 : printTableAddCell(&cont,
2249 6014 : strcmp(PQgetvalue(res, i, attnotnull_col), "t") == 0 ? "not null" : "",
2250 : false, false);
2251 :
2252 6014 : identity = PQgetvalue(res, i, attidentity_col);
2253 6014 : generated = PQgetvalue(res, i, attgenerated_col);
2254 :
2255 6014 : if (identity[0] == ATTRIBUTE_IDENTITY_ALWAYS)
2256 44 : default_str = "generated always as identity";
2257 5970 : else if (identity[0] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
2258 16 : default_str = "generated by default as identity";
2259 5954 : else if (generated[0] == ATTRIBUTE_GENERATED_STORED)
2260 : {
2261 186 : default_str = psprintf("generated always as (%s) stored",
2262 : PQgetvalue(res, i, attrdef_col));
2263 186 : mustfree = true;
2264 : }
2265 5768 : else if (generated[0] == ATTRIBUTE_GENERATED_VIRTUAL)
2266 : {
2267 132 : default_str = psprintf("generated always as (%s)",
2268 : PQgetvalue(res, i, attrdef_col));
2269 132 : mustfree = true;
2270 : }
2271 : else
2272 5636 : default_str = PQgetvalue(res, i, attrdef_col);
2273 :
2274 6014 : printTableAddCell(&cont, default_str, false, mustfree);
2275 : }
2276 :
2277 : /* Info for index columns */
2278 6332 : if (isindexkey_col >= 0)
2279 306 : printTableAddCell(&cont, PQgetvalue(res, i, isindexkey_col), true, false);
2280 6332 : if (indexdef_col >= 0)
2281 306 : printTableAddCell(&cont, PQgetvalue(res, i, indexdef_col), false, false);
2282 :
2283 : /* FDW options for foreign table columns */
2284 6332 : if (fdwopts_col >= 0)
2285 491 : printTableAddCell(&cont, PQgetvalue(res, i, fdwopts_col), false, false);
2286 :
2287 : /* Storage mode, if relevant */
2288 6332 : if (attstorage_col >= 0)
2289 : {
2290 2983 : char *storage = PQgetvalue(res, i, attstorage_col);
2291 :
2292 : /* these strings are literal in our syntax, so not translated. */
2293 3824 : printTableAddCell(&cont, (storage[0] == TYPSTORAGE_PLAIN ? "plain" :
2294 1578 : (storage[0] == TYPSTORAGE_MAIN ? "main" :
2295 765 : (storage[0] == TYPSTORAGE_EXTENDED ? "extended" :
2296 28 : (storage[0] == TYPSTORAGE_EXTERNAL ? "external" :
2297 : "???")))),
2298 : false, false);
2299 : }
2300 :
2301 : /* Column compression, if relevant */
2302 6332 : if (attcompression_col >= 0)
2303 : {
2304 80 : char *compression = PQgetvalue(res, i, attcompression_col);
2305 :
2306 : /* these strings are literal in our syntax, so not translated. */
2307 144 : printTableAddCell(&cont, (compression[0] == 'p' ? "pglz" :
2308 120 : (compression[0] == 'l' ? "lz4" :
2309 56 : (compression[0] == '\0' ? "" :
2310 : "???"))),
2311 : false, false);
2312 : }
2313 :
2314 : /* Statistics target, if the relkind supports this feature */
2315 6332 : if (attstattarget_col >= 0)
2316 2333 : printTableAddCell(&cont, PQgetvalue(res, i, attstattarget_col),
2317 : false, false);
2318 :
2319 : /* Column comments, if the relkind supports this feature */
2320 6332 : if (attdescr_col >= 0)
2321 2929 : printTableAddCell(&cont, PQgetvalue(res, i, attdescr_col),
2322 : false, false);
2323 : }
2324 :
2325 : /* Make footers */
2326 :
2327 2644 : if (tableinfo.ispartition)
2328 : {
2329 : /* Footer information for a partition child table */
2330 : PGresult *result;
2331 :
2332 405 : printfPQExpBuffer(&buf, "/* %s */\n",
2333 : _("Get partitioning information for this partition"));
2334 405 : appendPQExpBuffer(&buf,
2335 : "SELECT inhparent::pg_catalog.regclass,\n"
2336 : " pg_catalog.pg_get_expr(c.relpartbound, c.oid),\n ");
2337 :
2338 405 : appendPQExpBufferStr(&buf,
2339 405 : pset.sversion >= 140000 ? "inhdetachpending" :
2340 : "false as inhdetachpending");
2341 :
2342 : /* If verbose, also request the partition constraint definition */
2343 405 : if (verbose)
2344 153 : appendPQExpBufferStr(&buf,
2345 : ",\n pg_catalog.pg_get_partition_constraintdef(c.oid)");
2346 405 : appendPQExpBuffer(&buf,
2347 : "\nFROM pg_catalog.pg_class c"
2348 : " JOIN pg_catalog.pg_inherits i"
2349 : " ON c.oid = inhrelid"
2350 : "\nWHERE c.oid = '%s';", oid);
2351 405 : result = PSQLexec(buf.data);
2352 405 : if (!result)
2353 0 : goto error_return;
2354 :
2355 405 : if (PQntuples(result) > 0)
2356 : {
2357 405 : char *parent_name = PQgetvalue(result, 0, 0);
2358 405 : char *partdef = PQgetvalue(result, 0, 1);
2359 405 : char *detached = PQgetvalue(result, 0, 2);
2360 :
2361 405 : printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s%s"), parent_name,
2362 : partdef,
2363 405 : strcmp(detached, "t") == 0 ? " DETACH PENDING" : "");
2364 405 : printTableAddFooter(&cont, tmpbuf.data);
2365 :
2366 405 : if (verbose)
2367 : {
2368 153 : char *partconstraintdef = NULL;
2369 :
2370 153 : if (!PQgetisnull(result, 0, 3))
2371 137 : partconstraintdef = PQgetvalue(result, 0, 3);
2372 : /* If there isn't any constraint, show that explicitly */
2373 153 : if (partconstraintdef == NULL || partconstraintdef[0] == '\0')
2374 16 : printfPQExpBuffer(&tmpbuf, _("No partition constraint"));
2375 : else
2376 137 : printfPQExpBuffer(&tmpbuf, _("Partition constraint: %s"),
2377 : partconstraintdef);
2378 153 : printTableAddFooter(&cont, tmpbuf.data);
2379 : }
2380 : }
2381 405 : PQclear(result);
2382 : }
2383 :
2384 2644 : if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2385 : {
2386 : /* Footer information for a partitioned table (partitioning parent) */
2387 : PGresult *result;
2388 :
2389 209 : printfPQExpBuffer(&buf, "/* %s */\n",
2390 : _("Get partitioning information for this table"));
2391 209 : appendPQExpBuffer(&buf,
2392 : "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);",
2393 : oid);
2394 209 : result = PSQLexec(buf.data);
2395 209 : if (!result)
2396 0 : goto error_return;
2397 :
2398 209 : if (PQntuples(result) == 1)
2399 : {
2400 209 : char *partkeydef = PQgetvalue(result, 0, 0);
2401 :
2402 209 : printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef);
2403 209 : printTableAddFooter(&cont, tmpbuf.data);
2404 : }
2405 209 : PQclear(result);
2406 : }
2407 :
2408 2644 : if (tableinfo.relkind == RELKIND_TOASTVALUE)
2409 : {
2410 : /* For a TOAST table, print name of owning table */
2411 : PGresult *result;
2412 :
2413 4 : printfPQExpBuffer(&buf, "/* %s */\n",
2414 : _("Get the table that owns this TOAST table"));
2415 4 : appendPQExpBuffer(&buf,
2416 : "SELECT n.nspname, c.relname\n"
2417 : "FROM pg_catalog.pg_class c"
2418 : " JOIN pg_catalog.pg_namespace n"
2419 : " ON n.oid = c.relnamespace\n"
2420 : "WHERE reltoastrelid = '%s';", oid);
2421 4 : result = PSQLexec(buf.data);
2422 4 : if (!result)
2423 0 : goto error_return;
2424 :
2425 4 : if (PQntuples(result) == 1)
2426 : {
2427 4 : char *schemaname = PQgetvalue(result, 0, 0);
2428 4 : char *relname = PQgetvalue(result, 0, 1);
2429 :
2430 4 : printfPQExpBuffer(&tmpbuf, _("Owning table: \"%s.%s\""),
2431 : schemaname, relname);
2432 4 : printTableAddFooter(&cont, tmpbuf.data);
2433 : }
2434 4 : PQclear(result);
2435 : }
2436 :
2437 2644 : if (tableinfo.relkind == RELKIND_INDEX ||
2438 2466 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
2439 266 : {
2440 : /* Footer information about an index */
2441 : PGresult *result;
2442 :
2443 266 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get index details"));
2444 266 : appendPQExpBuffer(&buf,
2445 : "SELECT i.indisunique, i.indisprimary, i.indisclustered, "
2446 : "i.indisvalid,\n"
2447 : " (NOT i.indimmediate) AND "
2448 : "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2449 : "WHERE conrelid = i.indrelid AND "
2450 : "conindid = i.indexrelid AND "
2451 : "contype IN (" CppAsString2(CONSTRAINT_PRIMARY) ","
2452 : CppAsString2(CONSTRAINT_UNIQUE) ","
2453 : CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
2454 : "condeferrable) AS condeferrable,\n"
2455 : " (NOT i.indimmediate) AND "
2456 : "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2457 : "WHERE conrelid = i.indrelid AND "
2458 : "conindid = i.indexrelid AND "
2459 : "contype IN (" CppAsString2(CONSTRAINT_PRIMARY) ","
2460 : CppAsString2(CONSTRAINT_UNIQUE) ","
2461 : CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
2462 : "condeferred) AS condeferred,\n");
2463 :
2464 266 : if (pset.sversion >= 90400)
2465 266 : appendPQExpBufferStr(&buf, "i.indisreplident,\n");
2466 : else
2467 0 : appendPQExpBufferStr(&buf, "false AS indisreplident,\n");
2468 :
2469 266 : if (pset.sversion >= 150000)
2470 266 : appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
2471 : else
2472 0 : appendPQExpBufferStr(&buf, "false AS indnullsnotdistinct,\n");
2473 :
2474 266 : appendPQExpBuffer(&buf, " a.amname, c2.relname, "
2475 : "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
2476 : "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
2477 : "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
2478 : "AND i.indrelid = c2.oid;",
2479 : oid);
2480 :
2481 266 : result = PSQLexec(buf.data);
2482 266 : if (!result)
2483 0 : goto error_return;
2484 266 : else if (PQntuples(result) != 1)
2485 : {
2486 0 : PQclear(result);
2487 0 : goto error_return;
2488 : }
2489 : else
2490 : {
2491 266 : char *indisunique = PQgetvalue(result, 0, 0);
2492 266 : char *indisprimary = PQgetvalue(result, 0, 1);
2493 266 : char *indisclustered = PQgetvalue(result, 0, 2);
2494 266 : char *indisvalid = PQgetvalue(result, 0, 3);
2495 266 : char *deferrable = PQgetvalue(result, 0, 4);
2496 266 : char *deferred = PQgetvalue(result, 0, 5);
2497 266 : char *indisreplident = PQgetvalue(result, 0, 6);
2498 266 : char *indnullsnotdistinct = PQgetvalue(result, 0, 7);
2499 266 : char *indamname = PQgetvalue(result, 0, 8);
2500 266 : char *indtable = PQgetvalue(result, 0, 9);
2501 266 : char *indpred = PQgetvalue(result, 0, 10);
2502 :
2503 266 : if (strcmp(indisprimary, "t") == 0)
2504 64 : printfPQExpBuffer(&tmpbuf, _("primary key, "));
2505 202 : else if (strcmp(indisunique, "t") == 0)
2506 : {
2507 68 : printfPQExpBuffer(&tmpbuf, _("unique"));
2508 68 : if (strcmp(indnullsnotdistinct, "t") == 0)
2509 4 : appendPQExpBufferStr(&tmpbuf, _(" nulls not distinct"));
2510 68 : appendPQExpBufferStr(&tmpbuf, _(", "));
2511 : }
2512 : else
2513 134 : resetPQExpBuffer(&tmpbuf);
2514 266 : appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
2515 :
2516 : /* we assume here that index and table are in same schema */
2517 266 : appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
2518 : schemaname, indtable);
2519 :
2520 266 : if (strlen(indpred))
2521 0 : appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
2522 :
2523 266 : if (strcmp(indisclustered, "t") == 0)
2524 0 : appendPQExpBufferStr(&tmpbuf, _(", clustered"));
2525 :
2526 266 : if (strcmp(indisvalid, "t") != 0)
2527 0 : appendPQExpBufferStr(&tmpbuf, _(", invalid"));
2528 :
2529 266 : if (strcmp(deferrable, "t") == 0)
2530 0 : appendPQExpBufferStr(&tmpbuf, _(", deferrable"));
2531 :
2532 266 : if (strcmp(deferred, "t") == 0)
2533 0 : appendPQExpBufferStr(&tmpbuf, _(", initially deferred"));
2534 :
2535 266 : if (strcmp(indisreplident, "t") == 0)
2536 0 : appendPQExpBufferStr(&tmpbuf, _(", replica identity"));
2537 :
2538 266 : printTableAddFooter(&cont, tmpbuf.data);
2539 :
2540 : /*
2541 : * If it's a partitioned index, we'll print the tablespace below
2542 : */
2543 266 : if (tableinfo.relkind == RELKIND_INDEX)
2544 178 : add_tablespace_footer(&cont, tableinfo.relkind,
2545 : tableinfo.tablespace, true);
2546 : }
2547 :
2548 266 : PQclear(result);
2549 : }
2550 : /* If you add relkinds here, see also "Finish printing..." stanza below */
2551 2378 : else if (tableinfo.relkind == RELKIND_RELATION ||
2552 680 : tableinfo.relkind == RELKIND_MATVIEW ||
2553 640 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2554 515 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
2555 306 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
2556 306 : tableinfo.relkind == RELKIND_TOASTVALUE)
2557 : {
2558 : /* Footer information about a table */
2559 2076 : PGresult *result = NULL;
2560 2076 : int tuples = 0;
2561 :
2562 : /* print indexes */
2563 2076 : if (tableinfo.hasindex)
2564 : {
2565 668 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get indexes for this table"));
2566 668 : appendPQExpBuffer(&buf,
2567 : "SELECT c2.relname, i.indisprimary, i.indisunique, "
2568 : "i.indisclustered, i.indisvalid, "
2569 : "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n "
2570 : "pg_catalog.pg_get_constraintdef(con.oid, true), "
2571 : "contype, condeferrable, condeferred");
2572 668 : if (pset.sversion >= 90400)
2573 668 : appendPQExpBufferStr(&buf, ", i.indisreplident");
2574 : else
2575 0 : appendPQExpBufferStr(&buf, ", false AS indisreplident");
2576 668 : appendPQExpBufferStr(&buf, ", c2.reltablespace");
2577 668 : if (pset.sversion >= 180000)
2578 668 : appendPQExpBufferStr(&buf, ", con.conperiod");
2579 : else
2580 0 : appendPQExpBufferStr(&buf, ", false AS conperiod");
2581 668 : appendPQExpBuffer(&buf,
2582 : "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
2583 : " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ("
2584 : CppAsString2(CONSTRAINT_PRIMARY) ","
2585 : CppAsString2(CONSTRAINT_UNIQUE) ","
2586 : CppAsString2(CONSTRAINT_EXCLUSION) "))\n"
2587 : "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
2588 : "ORDER BY i.indisprimary DESC, c2.relname;",
2589 : oid);
2590 668 : result = PSQLexec(buf.data);
2591 668 : if (!result)
2592 0 : goto error_return;
2593 : else
2594 668 : tuples = PQntuples(result);
2595 :
2596 668 : if (tuples > 0)
2597 : {
2598 640 : printTableAddFooter(&cont, _("Indexes:"));
2599 1656 : for (i = 0; i < tuples; i++)
2600 : {
2601 : /* untranslated index name */
2602 1016 : printfPQExpBuffer(&buf, " \"%s\"",
2603 : PQgetvalue(result, i, 0));
2604 :
2605 : /*
2606 : * If exclusion constraint or PK/UNIQUE constraint WITHOUT
2607 : * OVERLAPS, print the constraintdef
2608 : */
2609 1016 : if (strcmp(PQgetvalue(result, i, 7), "x") == 0 ||
2610 980 : strcmp(PQgetvalue(result, i, 12), "t") == 0)
2611 : {
2612 98 : appendPQExpBuffer(&buf, " %s",
2613 : PQgetvalue(result, i, 6));
2614 : }
2615 : else
2616 : {
2617 : const char *indexdef;
2618 : const char *usingpos;
2619 :
2620 : /* Label as primary key or unique (but not both) */
2621 918 : if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
2622 308 : appendPQExpBufferStr(&buf, " PRIMARY KEY,");
2623 610 : else if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
2624 : {
2625 224 : if (strcmp(PQgetvalue(result, i, 7), "u") == 0)
2626 100 : appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,");
2627 : else
2628 124 : appendPQExpBufferStr(&buf, " UNIQUE,");
2629 : }
2630 :
2631 : /* Everything after "USING" is echoed verbatim */
2632 918 : indexdef = PQgetvalue(result, i, 5);
2633 918 : usingpos = strstr(indexdef, " USING ");
2634 918 : if (usingpos)
2635 918 : indexdef = usingpos + 7;
2636 918 : appendPQExpBuffer(&buf, " %s", indexdef);
2637 :
2638 : /* Need these for deferrable PK/UNIQUE indexes */
2639 918 : if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
2640 32 : appendPQExpBufferStr(&buf, " DEFERRABLE");
2641 :
2642 918 : if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
2643 12 : appendPQExpBufferStr(&buf, " INITIALLY DEFERRED");
2644 : }
2645 :
2646 : /* Add these for all cases */
2647 1016 : if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
2648 0 : appendPQExpBufferStr(&buf, " CLUSTER");
2649 :
2650 1016 : if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
2651 28 : appendPQExpBufferStr(&buf, " INVALID");
2652 :
2653 1016 : if (strcmp(PQgetvalue(result, i, 10), "t") == 0)
2654 40 : appendPQExpBufferStr(&buf, " REPLICA IDENTITY");
2655 :
2656 1016 : printTableAddFooter(&cont, buf.data);
2657 :
2658 : /* Print tablespace of the index on the same line */
2659 1016 : add_tablespace_footer(&cont, RELKIND_INDEX,
2660 1016 : atooid(PQgetvalue(result, i, 11)),
2661 : false);
2662 : }
2663 : }
2664 668 : PQclear(result);
2665 : }
2666 :
2667 : /* print table (and column) check constraints */
2668 2076 : if (tableinfo.checks)
2669 : {
2670 288 : printfPQExpBuffer(&buf, "/* %s */\n",
2671 : _("Get check constraints for this table"));
2672 288 : appendPQExpBuffer(&buf,
2673 : "SELECT r.conname, "
2674 : "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
2675 : "FROM pg_catalog.pg_constraint r\n"
2676 : "WHERE r.conrelid = '%s' "
2677 : "AND r.contype = " CppAsString2(CONSTRAINT_CHECK) "\n"
2678 : "ORDER BY 1;",
2679 : oid);
2680 288 : result = PSQLexec(buf.data);
2681 288 : if (!result)
2682 0 : goto error_return;
2683 : else
2684 288 : tuples = PQntuples(result);
2685 :
2686 288 : if (tuples > 0)
2687 : {
2688 288 : printTableAddFooter(&cont, _("Check constraints:"));
2689 788 : for (i = 0; i < tuples; i++)
2690 : {
2691 : /* untranslated constraint name and def */
2692 500 : printfPQExpBuffer(&buf, " \"%s\" %s",
2693 : PQgetvalue(result, i, 0),
2694 : PQgetvalue(result, i, 1));
2695 :
2696 500 : printTableAddFooter(&cont, buf.data);
2697 : }
2698 : }
2699 288 : PQclear(result);
2700 : }
2701 :
2702 : /* Print foreign-key constraints */
2703 2076 : printfPQExpBuffer(&buf, "/* %s */\n",
2704 : _("Get foreign key constraints for this table"));
2705 2076 : if (pset.sversion >= 120000 &&
2706 2076 : (tableinfo.ispartition || tableinfo.relkind == RELKIND_PARTITIONED_TABLE))
2707 : {
2708 : /*
2709 : * Put the constraints defined in this table first, followed by
2710 : * the constraints defined in ancestor partitioned tables.
2711 : */
2712 558 : appendPQExpBuffer(&buf,
2713 : "SELECT conrelid = '%s'::pg_catalog.regclass AS sametable,\n"
2714 : " conname,\n"
2715 : " pg_catalog.pg_get_constraintdef(oid, true) AS condef,\n"
2716 : " conrelid::pg_catalog.regclass AS ontable\n"
2717 : " FROM pg_catalog.pg_constraint,\n"
2718 : " pg_catalog.pg_partition_ancestors('%s')\n"
2719 : " WHERE conrelid = relid AND contype = " CppAsString2(CONSTRAINT_FOREIGN) " AND conparentid = 0\n"
2720 : "ORDER BY sametable DESC, conname;",
2721 : oid, oid);
2722 : }
2723 : else
2724 : {
2725 1518 : appendPQExpBuffer(&buf,
2726 : "SELECT true as sametable, conname,\n"
2727 : " pg_catalog.pg_get_constraintdef(r.oid, true) as condef,\n"
2728 : " conrelid::pg_catalog.regclass AS ontable\n"
2729 : "FROM pg_catalog.pg_constraint r\n"
2730 : "WHERE r.conrelid = '%s' AND r.contype = " CppAsString2(CONSTRAINT_FOREIGN) "\n",
2731 : oid);
2732 :
2733 1518 : if (pset.sversion >= 120000)
2734 1518 : appendPQExpBufferStr(&buf, " AND conparentid = 0\n");
2735 1518 : appendPQExpBufferStr(&buf, "ORDER BY conname");
2736 : }
2737 :
2738 2076 : result = PSQLexec(buf.data);
2739 2076 : if (!result)
2740 0 : goto error_return;
2741 : else
2742 2076 : tuples = PQntuples(result);
2743 :
2744 2076 : if (tuples > 0)
2745 : {
2746 137 : int i_sametable = PQfnumber(result, "sametable"),
2747 137 : i_conname = PQfnumber(result, "conname"),
2748 137 : i_condef = PQfnumber(result, "condef"),
2749 137 : i_ontable = PQfnumber(result, "ontable");
2750 :
2751 137 : printTableAddFooter(&cont, _("Foreign-key constraints:"));
2752 314 : for (i = 0; i < tuples; i++)
2753 : {
2754 : /*
2755 : * Print untranslated constraint name and definition. Use a
2756 : * "TABLE tab" prefix when the constraint is defined in a
2757 : * parent partitioned table.
2758 : */
2759 177 : if (strcmp(PQgetvalue(result, i, i_sametable), "f") == 0)
2760 68 : printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2761 : PQgetvalue(result, i, i_ontable),
2762 : PQgetvalue(result, i, i_conname),
2763 : PQgetvalue(result, i, i_condef));
2764 : else
2765 109 : printfPQExpBuffer(&buf, " \"%s\" %s",
2766 : PQgetvalue(result, i, i_conname),
2767 : PQgetvalue(result, i, i_condef));
2768 :
2769 177 : printTableAddFooter(&cont, buf.data);
2770 : }
2771 : }
2772 2076 : PQclear(result);
2773 :
2774 : /* print incoming foreign-key references */
2775 2076 : printfPQExpBuffer(&buf, "/* %s */\n",
2776 : _("Get foreign keys referencing this table"));
2777 2076 : if (pset.sversion >= 120000)
2778 : {
2779 2076 : appendPQExpBuffer(&buf,
2780 : "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2781 : " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2782 : " FROM pg_catalog.pg_constraint c\n"
2783 : " WHERE confrelid IN (SELECT pg_catalog.pg_partition_ancestors('%s')\n"
2784 : " UNION ALL VALUES ('%s'::pg_catalog.regclass))\n"
2785 : " AND contype = " CppAsString2(CONSTRAINT_FOREIGN) " AND conparentid = 0\n"
2786 : "ORDER BY conname;",
2787 : oid, oid);
2788 : }
2789 : else
2790 : {
2791 0 : appendPQExpBuffer(&buf,
2792 : "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2793 : " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2794 : " FROM pg_catalog.pg_constraint\n"
2795 : " WHERE confrelid = %s AND contype = " CppAsString2(CONSTRAINT_FOREIGN) "\n"
2796 : "ORDER BY conname;",
2797 : oid);
2798 : }
2799 :
2800 2076 : result = PSQLexec(buf.data);
2801 2076 : if (!result)
2802 0 : goto error_return;
2803 : else
2804 2076 : tuples = PQntuples(result);
2805 :
2806 2076 : if (tuples > 0)
2807 : {
2808 48 : int i_conname = PQfnumber(result, "conname"),
2809 48 : i_ontable = PQfnumber(result, "ontable"),
2810 48 : i_condef = PQfnumber(result, "condef");
2811 :
2812 48 : printTableAddFooter(&cont, _("Referenced by:"));
2813 96 : for (i = 0; i < tuples; i++)
2814 : {
2815 48 : printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2816 : PQgetvalue(result, i, i_ontable),
2817 : PQgetvalue(result, i, i_conname),
2818 : PQgetvalue(result, i, i_condef));
2819 :
2820 48 : printTableAddFooter(&cont, buf.data);
2821 : }
2822 : }
2823 2076 : PQclear(result);
2824 :
2825 : /* print any row-level policies */
2826 2076 : if (pset.sversion >= 90500)
2827 : {
2828 2076 : printfPQExpBuffer(&buf, "/* %s */\n",
2829 : _("Get row-level policies for this table"));
2830 2076 : appendPQExpBuffer(&buf, "SELECT pol.polname,");
2831 2076 : if (pset.sversion >= 100000)
2832 2076 : appendPQExpBufferStr(&buf,
2833 : " pol.polpermissive,\n");
2834 : else
2835 0 : appendPQExpBufferStr(&buf,
2836 : " 't' as polpermissive,\n");
2837 2076 : appendPQExpBuffer(&buf,
2838 : " 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,\n"
2839 : " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
2840 : " pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
2841 : " CASE pol.polcmd\n"
2842 : " WHEN 'r' THEN 'SELECT'\n"
2843 : " WHEN 'a' THEN 'INSERT'\n"
2844 : " WHEN 'w' THEN 'UPDATE'\n"
2845 : " WHEN 'd' THEN 'DELETE'\n"
2846 : " END AS cmd\n"
2847 : "FROM pg_catalog.pg_policy pol\n"
2848 : "WHERE pol.polrelid = '%s' ORDER BY 1;",
2849 : oid);
2850 :
2851 2076 : result = PSQLexec(buf.data);
2852 2076 : if (!result)
2853 0 : goto error_return;
2854 : else
2855 2076 : tuples = PQntuples(result);
2856 :
2857 : /*
2858 : * Handle cases where RLS is enabled and there are policies, or
2859 : * there aren't policies, or RLS isn't enabled but there are
2860 : * policies
2861 : */
2862 2076 : if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
2863 8 : printTableAddFooter(&cont, _("Policies:"));
2864 :
2865 2076 : if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
2866 0 : printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
2867 :
2868 2076 : if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
2869 0 : printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
2870 :
2871 2076 : if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
2872 0 : printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
2873 :
2874 2076 : if (!tableinfo.rowsecurity && tuples > 0)
2875 0 : printTableAddFooter(&cont, _("Policies (row security disabled):"));
2876 :
2877 : /* Might be an empty set - that's ok */
2878 2096 : for (i = 0; i < tuples; i++)
2879 : {
2880 20 : printfPQExpBuffer(&buf, " POLICY \"%s\"",
2881 : PQgetvalue(result, i, 0));
2882 :
2883 20 : if (*(PQgetvalue(result, i, 1)) == 'f')
2884 12 : appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
2885 :
2886 20 : if (!PQgetisnull(result, i, 5))
2887 0 : appendPQExpBuffer(&buf, " FOR %s",
2888 : PQgetvalue(result, i, 5));
2889 :
2890 20 : if (!PQgetisnull(result, i, 2))
2891 : {
2892 12 : appendPQExpBuffer(&buf, "\n TO %s",
2893 : PQgetvalue(result, i, 2));
2894 : }
2895 :
2896 20 : if (!PQgetisnull(result, i, 3))
2897 20 : appendPQExpBuffer(&buf, "\n USING (%s)",
2898 : PQgetvalue(result, i, 3));
2899 :
2900 20 : if (!PQgetisnull(result, i, 4))
2901 0 : appendPQExpBuffer(&buf, "\n WITH CHECK (%s)",
2902 : PQgetvalue(result, i, 4));
2903 :
2904 20 : printTableAddFooter(&cont, buf.data);
2905 : }
2906 2076 : PQclear(result);
2907 : }
2908 :
2909 : /* print any extended statistics */
2910 2076 : if (pset.sversion >= 140000)
2911 : {
2912 2076 : printfPQExpBuffer(&buf, "/* %s */\n",
2913 : _("Get extended statistics for this table"));
2914 2076 : appendPQExpBuffer(&buf,
2915 : "SELECT oid, "
2916 : "stxrelid::pg_catalog.regclass, "
2917 : "stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS nsp, "
2918 : "stxname,\n"
2919 : "pg_catalog.pg_get_statisticsobjdef_columns(oid) AS columns,\n"
2920 : " " CppAsString2(STATS_EXT_NDISTINCT) " = any(stxkind) AS ndist_enabled,\n"
2921 : " " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(stxkind) AS deps_enabled,\n"
2922 : " " CppAsString2(STATS_EXT_MCV) " = any(stxkind) AS mcv_enabled,\n"
2923 : "stxstattarget\n"
2924 : "FROM pg_catalog.pg_statistic_ext\n"
2925 : "WHERE stxrelid = '%s'\n"
2926 : "ORDER BY nsp, stxname;",
2927 : oid);
2928 :
2929 2076 : result = PSQLexec(buf.data);
2930 2076 : if (!result)
2931 0 : goto error_return;
2932 : else
2933 2076 : tuples = PQntuples(result);
2934 :
2935 2076 : if (tuples > 0)
2936 : {
2937 44 : printTableAddFooter(&cont, _("Statistics objects:"));
2938 :
2939 100 : for (i = 0; i < tuples; i++)
2940 : {
2941 56 : bool gotone = false;
2942 : bool has_ndistinct;
2943 : bool has_dependencies;
2944 : bool has_mcv;
2945 : bool has_all;
2946 : bool has_some;
2947 :
2948 56 : has_ndistinct = (strcmp(PQgetvalue(result, i, 5), "t") == 0);
2949 56 : has_dependencies = (strcmp(PQgetvalue(result, i, 6), "t") == 0);
2950 56 : has_mcv = (strcmp(PQgetvalue(result, i, 7), "t") == 0);
2951 :
2952 56 : printfPQExpBuffer(&buf, " ");
2953 :
2954 : /* statistics object name (qualified with namespace) */
2955 56 : appendPQExpBuffer(&buf, "\"%s.%s\"",
2956 : PQgetvalue(result, i, 2),
2957 : PQgetvalue(result, i, 3));
2958 :
2959 : /*
2960 : * When printing kinds we ignore expression statistics,
2961 : * which are used only internally and can't be specified
2962 : * by user. We don't print the kinds when none are
2963 : * specified (in which case it has to be statistics on a
2964 : * single expr) or when all are specified (in which case
2965 : * we assume it's expanded by CREATE STATISTICS).
2966 : */
2967 56 : has_all = (has_ndistinct && has_dependencies && has_mcv);
2968 56 : has_some = (has_ndistinct || has_dependencies || has_mcv);
2969 :
2970 56 : if (has_some && !has_all)
2971 : {
2972 8 : appendPQExpBufferStr(&buf, " (");
2973 :
2974 : /* options */
2975 8 : if (has_ndistinct)
2976 : {
2977 0 : appendPQExpBufferStr(&buf, "ndistinct");
2978 0 : gotone = true;
2979 : }
2980 :
2981 8 : if (has_dependencies)
2982 : {
2983 8 : appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2984 8 : gotone = true;
2985 : }
2986 :
2987 8 : if (has_mcv)
2988 : {
2989 0 : appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2990 : }
2991 :
2992 8 : appendPQExpBufferChar(&buf, ')');
2993 : }
2994 :
2995 56 : appendPQExpBuffer(&buf, " ON %s FROM %s",
2996 : PQgetvalue(result, i, 4),
2997 : PQgetvalue(result, i, 1));
2998 :
2999 : /* Show the stats target if it's not default */
3000 56 : if (!PQgetisnull(result, i, 8) &&
3001 4 : strcmp(PQgetvalue(result, i, 8), "-1") != 0)
3002 4 : appendPQExpBuffer(&buf, "; STATISTICS %s",
3003 : PQgetvalue(result, i, 8));
3004 :
3005 56 : printTableAddFooter(&cont, buf.data);
3006 : }
3007 : }
3008 2076 : PQclear(result);
3009 : }
3010 0 : else if (pset.sversion >= 100000)
3011 : {
3012 0 : printfPQExpBuffer(&buf, "/* %s */\n",
3013 : _("Get extended statistics for this table"));
3014 0 : appendPQExpBuffer(&buf,
3015 : "SELECT oid, "
3016 : "stxrelid::pg_catalog.regclass, "
3017 : "stxnamespace::pg_catalog.regnamespace AS nsp, "
3018 : "stxname,\n"
3019 : " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')\n"
3020 : " FROM pg_catalog.unnest(stxkeys) s(attnum)\n"
3021 : " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND\n"
3022 : " a.attnum = s.attnum AND NOT attisdropped)) AS columns,\n"
3023 : " " CppAsString2(STATS_EXT_NDISTINCT) " = any(stxkind) AS ndist_enabled,\n"
3024 : " " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(stxkind) AS deps_enabled,\n"
3025 : " " CppAsString2(STATS_EXT_MCV) " = any(stxkind) AS mcv_enabled,\n");
3026 :
3027 0 : if (pset.sversion >= 130000)
3028 0 : appendPQExpBufferStr(&buf, " stxstattarget\n");
3029 : else
3030 0 : appendPQExpBufferStr(&buf, " -1 AS stxstattarget\n");
3031 0 : appendPQExpBuffer(&buf, "FROM pg_catalog.pg_statistic_ext\n"
3032 : "WHERE stxrelid = '%s'\n"
3033 : "ORDER BY 1;",
3034 : oid);
3035 :
3036 0 : result = PSQLexec(buf.data);
3037 0 : if (!result)
3038 0 : goto error_return;
3039 : else
3040 0 : tuples = PQntuples(result);
3041 :
3042 0 : if (tuples > 0)
3043 : {
3044 0 : printTableAddFooter(&cont, _("Statistics objects:"));
3045 :
3046 0 : for (i = 0; i < tuples; i++)
3047 : {
3048 0 : bool gotone = false;
3049 :
3050 0 : printfPQExpBuffer(&buf, " ");
3051 :
3052 : /* statistics object name (qualified with namespace) */
3053 0 : appendPQExpBuffer(&buf, "\"%s.%s\" (",
3054 : PQgetvalue(result, i, 2),
3055 : PQgetvalue(result, i, 3));
3056 :
3057 : /* options */
3058 0 : if (strcmp(PQgetvalue(result, i, 5), "t") == 0)
3059 : {
3060 0 : appendPQExpBufferStr(&buf, "ndistinct");
3061 0 : gotone = true;
3062 : }
3063 :
3064 0 : if (strcmp(PQgetvalue(result, i, 6), "t") == 0)
3065 : {
3066 0 : appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
3067 0 : gotone = true;
3068 : }
3069 :
3070 0 : if (strcmp(PQgetvalue(result, i, 7), "t") == 0)
3071 : {
3072 0 : appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
3073 : }
3074 :
3075 0 : appendPQExpBuffer(&buf, ") ON %s FROM %s",
3076 : PQgetvalue(result, i, 4),
3077 : PQgetvalue(result, i, 1));
3078 :
3079 : /* Show the stats target if it's not default */
3080 0 : if (strcmp(PQgetvalue(result, i, 8), "-1") != 0)
3081 0 : appendPQExpBuffer(&buf, "; STATISTICS %s",
3082 : PQgetvalue(result, i, 8));
3083 :
3084 0 : printTableAddFooter(&cont, buf.data);
3085 : }
3086 : }
3087 0 : PQclear(result);
3088 : }
3089 :
3090 : /* print rules */
3091 2076 : if (tableinfo.hasrules && tableinfo.relkind != RELKIND_MATVIEW)
3092 : {
3093 24 : printfPQExpBuffer(&buf, "/* %s */\n",
3094 : _("Get rules for this relation"));
3095 24 : appendPQExpBuffer(&buf,
3096 : "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
3097 : "ev_enabled\n"
3098 : "FROM pg_catalog.pg_rewrite r\n"
3099 : "WHERE r.ev_class = '%s' ORDER BY 1;",
3100 : oid);
3101 24 : result = PSQLexec(buf.data);
3102 24 : if (!result)
3103 0 : goto error_return;
3104 : else
3105 24 : tuples = PQntuples(result);
3106 :
3107 24 : if (tuples > 0)
3108 : {
3109 : bool have_heading;
3110 : int category;
3111 :
3112 120 : for (category = 0; category < 4; category++)
3113 : {
3114 96 : have_heading = false;
3115 :
3116 352 : for (i = 0; i < tuples; i++)
3117 : {
3118 : const char *ruledef;
3119 256 : bool list_rule = false;
3120 :
3121 256 : switch (category)
3122 : {
3123 64 : case 0:
3124 64 : if (*PQgetvalue(result, i, 2) == 'O')
3125 64 : list_rule = true;
3126 64 : break;
3127 64 : case 1:
3128 64 : if (*PQgetvalue(result, i, 2) == 'D')
3129 0 : list_rule = true;
3130 64 : break;
3131 64 : case 2:
3132 64 : if (*PQgetvalue(result, i, 2) == 'A')
3133 0 : list_rule = true;
3134 64 : break;
3135 64 : case 3:
3136 64 : if (*PQgetvalue(result, i, 2) == 'R')
3137 0 : list_rule = true;
3138 64 : break;
3139 : }
3140 256 : if (!list_rule)
3141 192 : continue;
3142 :
3143 64 : if (!have_heading)
3144 : {
3145 24 : switch (category)
3146 : {
3147 24 : case 0:
3148 24 : printfPQExpBuffer(&buf, _("Rules:"));
3149 24 : break;
3150 0 : case 1:
3151 0 : printfPQExpBuffer(&buf, _("Disabled rules:"));
3152 0 : break;
3153 0 : case 2:
3154 0 : printfPQExpBuffer(&buf, _("Rules firing always:"));
3155 0 : break;
3156 0 : case 3:
3157 0 : printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
3158 0 : break;
3159 : }
3160 24 : printTableAddFooter(&cont, buf.data);
3161 24 : have_heading = true;
3162 : }
3163 :
3164 : /* Everything after "CREATE RULE" is echoed verbatim */
3165 64 : ruledef = PQgetvalue(result, i, 1);
3166 64 : ruledef += 12;
3167 64 : printfPQExpBuffer(&buf, " %s", ruledef);
3168 64 : printTableAddFooter(&cont, buf.data);
3169 : }
3170 : }
3171 : }
3172 24 : PQclear(result);
3173 : }
3174 :
3175 : /* print any publications */
3176 2076 : if (pset.sversion >= 100000)
3177 : {
3178 2076 : printfPQExpBuffer(&buf, "/* %s */\n",
3179 : _("Get publications that publish this table"));
3180 2076 : if (pset.sversion >= 150000)
3181 : {
3182 2076 : appendPQExpBuffer(&buf,
3183 : "SELECT pubname\n"
3184 : " , NULL\n"
3185 : " , NULL\n"
3186 : "FROM pg_catalog.pg_publication p\n"
3187 : " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
3188 : " JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
3189 : "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
3190 : "UNION\n"
3191 : "SELECT pubname\n"
3192 : " , pg_get_expr(pr.prqual, c.oid)\n"
3193 : " , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
3194 : " (SELECT string_agg(attname, ', ')\n"
3195 : " FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
3196 : " pg_catalog.pg_attribute\n"
3197 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
3198 : " ELSE NULL END) "
3199 : "FROM pg_catalog.pg_publication p\n"
3200 : " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3201 : " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
3202 : "WHERE pr.prrelid = '%s'\n",
3203 : oid, oid, oid);
3204 :
3205 2076 : if (pset.sversion >= 190000)
3206 : {
3207 : /*
3208 : * Skip entries where this relation appears in the
3209 : * publication's EXCEPT list.
3210 : */
3211 2076 : appendPQExpBuffer(&buf,
3212 : " AND NOT pr.prexcept\n"
3213 : "UNION\n"
3214 : "SELECT pubname\n"
3215 : " , NULL\n"
3216 : " , NULL\n"
3217 : "FROM pg_catalog.pg_publication p\n"
3218 : "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3219 : " AND NOT EXISTS (\n"
3220 : " SELECT 1\n"
3221 : " FROM pg_catalog.pg_publication_rel pr\n"
3222 : " WHERE pr.prpubid = p.oid AND\n"
3223 : " (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
3224 : "ORDER BY 1;",
3225 : oid, oid, oid);
3226 : }
3227 : else
3228 : {
3229 0 : appendPQExpBuffer(&buf,
3230 : "UNION\n"
3231 : "SELECT pubname\n"
3232 : " , NULL\n"
3233 : " , NULL\n"
3234 : "FROM pg_catalog.pg_publication p\n"
3235 : "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3236 : "ORDER BY 1;",
3237 : oid);
3238 : }
3239 : }
3240 : else
3241 : {
3242 0 : appendPQExpBuffer(&buf,
3243 : "SELECT pubname\n"
3244 : " , NULL\n"
3245 : " , NULL\n"
3246 : "FROM pg_catalog.pg_publication p\n"
3247 : "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3248 : "WHERE pr.prrelid = '%s'\n"
3249 : "UNION ALL\n"
3250 : "SELECT pubname\n"
3251 : " , NULL\n"
3252 : " , NULL\n"
3253 : "FROM pg_catalog.pg_publication p\n"
3254 : "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3255 : "ORDER BY 1;",
3256 : oid, oid);
3257 : }
3258 :
3259 2076 : result = PSQLexec(buf.data);
3260 2076 : if (!result)
3261 0 : goto error_return;
3262 : else
3263 2076 : tuples = PQntuples(result);
3264 :
3265 2076 : if (tuples > 0)
3266 52 : printTableAddFooter(&cont, _("Publications:"));
3267 :
3268 : /* Might be an empty set - that's ok */
3269 2152 : for (i = 0; i < tuples; i++)
3270 : {
3271 76 : printfPQExpBuffer(&buf, " \"%s\"",
3272 : PQgetvalue(result, i, 0));
3273 :
3274 : /* column list (if any) */
3275 76 : if (!PQgetisnull(result, i, 2))
3276 16 : appendPQExpBuffer(&buf, " (%s)",
3277 : PQgetvalue(result, i, 2));
3278 :
3279 : /* row filter (if any) */
3280 76 : if (!PQgetisnull(result, i, 1))
3281 16 : appendPQExpBuffer(&buf, " WHERE %s",
3282 : PQgetvalue(result, i, 1));
3283 :
3284 76 : printTableAddFooter(&cont, buf.data);
3285 : }
3286 2076 : PQclear(result);
3287 : }
3288 :
3289 : /* Print publications where the table is in the EXCEPT clause */
3290 2076 : if (pset.sversion >= 190000)
3291 : {
3292 2076 : printfPQExpBuffer(&buf, "/* %s */\n",
3293 : _("Get publications that exclude this table"));
3294 2076 : appendPQExpBuffer(&buf,
3295 : "SELECT pubname\n"
3296 : "FROM pg_catalog.pg_publication p\n"
3297 : "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3298 : "WHERE (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s'))\n"
3299 : "AND pr.prexcept\n"
3300 : "ORDER BY 1;", oid, oid);
3301 :
3302 2076 : result = PSQLexec(buf.data);
3303 2076 : if (!result)
3304 0 : goto error_return;
3305 : else
3306 2076 : tuples = PQntuples(result);
3307 :
3308 2076 : if (tuples > 0)
3309 12 : printTableAddFooter(&cont, _("Except Publications:"));
3310 :
3311 : /* Might be an empty set - that's ok */
3312 2092 : for (i = 0; i < tuples; i++)
3313 : {
3314 16 : printfPQExpBuffer(&buf, " \"%s\"", PQgetvalue(result, i, 0));
3315 :
3316 16 : printTableAddFooter(&cont, buf.data);
3317 : }
3318 2076 : PQclear(result);
3319 : }
3320 :
3321 : /*
3322 : * If verbose, print NOT NULL constraints.
3323 : */
3324 2076 : if (verbose)
3325 : {
3326 930 : printfPQExpBuffer(&buf, "/* %s */\n",
3327 : _("Get not-null constraints for this table"));
3328 930 : appendPQExpBuffer(&buf,
3329 : "SELECT c.conname, a.attname, c.connoinherit,\n"
3330 : " c.conislocal, c.coninhcount <> 0,\n"
3331 : " c.convalidated\n"
3332 : "FROM pg_catalog.pg_constraint c JOIN\n"
3333 : " pg_catalog.pg_attribute a ON\n"
3334 : " (a.attrelid = c.conrelid AND a.attnum = c.conkey[1])\n"
3335 : "WHERE c.contype = " CppAsString2(CONSTRAINT_NOTNULL) " AND\n"
3336 : " c.conrelid = '%s'::pg_catalog.regclass\n"
3337 : "ORDER BY a.attnum",
3338 : oid);
3339 :
3340 930 : result = PSQLexec(buf.data);
3341 930 : if (!result)
3342 0 : goto error_return;
3343 : else
3344 930 : tuples = PQntuples(result);
3345 :
3346 930 : if (tuples > 0)
3347 552 : printTableAddFooter(&cont, _("Not-null constraints:"));
3348 :
3349 : /* Might be an empty set - that's ok */
3350 1646 : for (i = 0; i < tuples; i++)
3351 : {
3352 716 : bool islocal = PQgetvalue(result, i, 3)[0] == 't';
3353 716 : bool inherited = PQgetvalue(result, i, 4)[0] == 't';
3354 716 : bool validated = PQgetvalue(result, i, 5)[0] == 't';
3355 :
3356 1432 : printfPQExpBuffer(&buf, " \"%s\" NOT NULL \"%s\"%s%s",
3357 : PQgetvalue(result, i, 0),
3358 : PQgetvalue(result, i, 1),
3359 716 : PQgetvalue(result, i, 2)[0] == 't' ?
3360 : " NO INHERIT" :
3361 1324 : islocal && inherited ? _(" (local, inherited)") :
3362 620 : inherited ? _(" (inherited)") : "",
3363 716 : !validated ? " NOT VALID" : "");
3364 :
3365 716 : printTableAddFooter(&cont, buf.data);
3366 : }
3367 930 : PQclear(result);
3368 : }
3369 : }
3370 :
3371 : /* Get view_def if table is a view or materialized view */
3372 2644 : if ((tableinfo.relkind == RELKIND_VIEW ||
3373 2644 : tableinfo.relkind == RELKIND_MATVIEW) && verbose)
3374 : {
3375 : PGresult *result;
3376 :
3377 279 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get view's definition"));
3378 279 : appendPQExpBuffer(&buf,
3379 : "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
3380 : oid);
3381 279 : result = PSQLexec(buf.data);
3382 279 : if (!result)
3383 0 : goto error_return;
3384 :
3385 279 : if (PQntuples(result) > 0)
3386 279 : view_def = pg_strdup(PQgetvalue(result, 0, 0));
3387 :
3388 279 : PQclear(result);
3389 : }
3390 :
3391 2644 : if (view_def)
3392 : {
3393 279 : PGresult *result = NULL;
3394 :
3395 : /* Footer information about a view */
3396 279 : printTableAddFooter(&cont, _("View definition:"));
3397 279 : printTableAddFooter(&cont, view_def);
3398 :
3399 : /* print rules */
3400 279 : if (tableinfo.hasrules)
3401 : {
3402 279 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get rules for this view"));
3403 279 : appendPQExpBuffer(&buf,
3404 : "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
3405 : "FROM pg_catalog.pg_rewrite r\n"
3406 : "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
3407 : oid);
3408 279 : result = PSQLexec(buf.data);
3409 279 : if (!result)
3410 0 : goto error_return;
3411 :
3412 279 : if (PQntuples(result) > 0)
3413 : {
3414 8 : printTableAddFooter(&cont, _("Rules:"));
3415 16 : for (i = 0; i < PQntuples(result); i++)
3416 : {
3417 : const char *ruledef;
3418 :
3419 : /* Everything after "CREATE RULE" is echoed verbatim */
3420 8 : ruledef = PQgetvalue(result, i, 1);
3421 8 : ruledef += 12;
3422 :
3423 8 : printfPQExpBuffer(&buf, " %s", ruledef);
3424 8 : printTableAddFooter(&cont, buf.data);
3425 : }
3426 : }
3427 279 : PQclear(result);
3428 : }
3429 : }
3430 :
3431 : /*
3432 : * Print triggers next, if any (but only user-defined triggers). This
3433 : * could apply to either a table or a view.
3434 : */
3435 2644 : if (tableinfo.hastriggers)
3436 : {
3437 : PGresult *result;
3438 : int tuples;
3439 :
3440 225 : printfPQExpBuffer(&buf, "/* %s */\n",
3441 : _("Get triggers for this relation"));
3442 225 : appendPQExpBuffer(&buf,
3443 : "SELECT t.tgname, "
3444 : "pg_catalog.pg_get_triggerdef(t.oid, true), "
3445 : "t.tgenabled, t.tgisinternal,\n");
3446 :
3447 : /*
3448 : * Detect whether each trigger is inherited, and if so, get the name
3449 : * of the topmost table it's inherited from. We have no easy way to
3450 : * do that pre-v13, for lack of the tgparentid column. Even with
3451 : * tgparentid, a straightforward search for the topmost parent would
3452 : * require a recursive CTE, which seems unduly expensive. We cheat a
3453 : * bit by assuming parent triggers will match by tgname; then, joining
3454 : * with pg_partition_ancestors() allows the planner to make use of
3455 : * pg_trigger_tgrelid_tgname_index if it wishes. We ensure we find
3456 : * the correct topmost parent by stopping at the first-in-partition-
3457 : * ancestry-order trigger that has tgparentid = 0. (There might be
3458 : * unrelated, non-inherited triggers with the same name further up the
3459 : * stack, so this is important.)
3460 : */
3461 225 : if (pset.sversion >= 130000)
3462 225 : appendPQExpBufferStr(&buf,
3463 : " CASE WHEN t.tgparentid != 0 THEN\n"
3464 : " (SELECT u.tgrelid::pg_catalog.regclass\n"
3465 : " FROM pg_catalog.pg_trigger AS u,\n"
3466 : " pg_catalog.pg_partition_ancestors(t.tgrelid) WITH ORDINALITY AS a(relid, depth)\n"
3467 : " WHERE u.tgname = t.tgname AND u.tgrelid = a.relid\n"
3468 : " AND u.tgparentid = 0\n"
3469 : " ORDER BY a.depth LIMIT 1)\n"
3470 : " END AS parent\n");
3471 : else
3472 0 : appendPQExpBufferStr(&buf, " NULL AS parent\n");
3473 :
3474 225 : appendPQExpBuffer(&buf,
3475 : "FROM pg_catalog.pg_trigger t\n"
3476 : "WHERE t.tgrelid = '%s' AND ",
3477 : oid);
3478 :
3479 : /*
3480 : * tgisinternal is set true for inherited triggers of partitions in
3481 : * servers between v11 and v14, though these must still be shown to
3482 : * the user. So we use another property that is true for such
3483 : * inherited triggers to avoid them being hidden, which is their
3484 : * dependence on another trigger.
3485 : */
3486 225 : if (pset.sversion >= 110000 && pset.sversion < 150000)
3487 0 : appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n"
3488 : " OR EXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n"
3489 : " AND refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass))");
3490 : else
3491 : /* display/warn about disabled internal triggers */
3492 225 : appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))");
3493 225 : appendPQExpBufferStr(&buf, "\nORDER BY 1;");
3494 :
3495 225 : result = PSQLexec(buf.data);
3496 225 : if (!result)
3497 0 : goto error_return;
3498 : else
3499 225 : tuples = PQntuples(result);
3500 :
3501 225 : if (tuples > 0)
3502 : {
3503 : bool have_heading;
3504 : int category;
3505 :
3506 : /*
3507 : * split the output into 4 different categories. Enabled triggers,
3508 : * disabled triggers and the two special ALWAYS and REPLICA
3509 : * configurations.
3510 : */
3511 240 : for (category = 0; category <= 4; category++)
3512 : {
3513 200 : have_heading = false;
3514 740 : for (i = 0; i < tuples; i++)
3515 : {
3516 : bool list_trigger;
3517 : const char *tgdef;
3518 : const char *usingpos;
3519 : const char *tgenabled;
3520 : const char *tgisinternal;
3521 :
3522 : /*
3523 : * Check if this trigger falls into the current category
3524 : */
3525 540 : tgenabled = PQgetvalue(result, i, 2);
3526 540 : tgisinternal = PQgetvalue(result, i, 3);
3527 540 : list_trigger = false;
3528 540 : switch (category)
3529 : {
3530 108 : case 0:
3531 108 : if (*tgenabled == 'O' || *tgenabled == 't')
3532 108 : list_trigger = true;
3533 108 : break;
3534 108 : case 1:
3535 108 : if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3536 0 : *tgisinternal == 'f')
3537 0 : list_trigger = true;
3538 108 : break;
3539 108 : case 2:
3540 108 : if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3541 0 : *tgisinternal == 't')
3542 0 : list_trigger = true;
3543 108 : break;
3544 108 : case 3:
3545 108 : if (*tgenabled == 'A')
3546 0 : list_trigger = true;
3547 108 : break;
3548 108 : case 4:
3549 108 : if (*tgenabled == 'R')
3550 0 : list_trigger = true;
3551 108 : break;
3552 : }
3553 540 : if (list_trigger == false)
3554 432 : continue;
3555 :
3556 : /* Print the category heading once */
3557 108 : if (have_heading == false)
3558 : {
3559 40 : switch (category)
3560 : {
3561 40 : case 0:
3562 40 : printfPQExpBuffer(&buf, _("Triggers:"));
3563 40 : break;
3564 0 : case 1:
3565 0 : printfPQExpBuffer(&buf, _("Disabled user triggers:"));
3566 0 : break;
3567 0 : case 2:
3568 0 : printfPQExpBuffer(&buf, _("Disabled internal triggers:"));
3569 0 : break;
3570 0 : case 3:
3571 0 : printfPQExpBuffer(&buf, _("Triggers firing always:"));
3572 0 : break;
3573 0 : case 4:
3574 0 : printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
3575 0 : break;
3576 : }
3577 40 : printTableAddFooter(&cont, buf.data);
3578 40 : have_heading = true;
3579 : }
3580 :
3581 : /* Everything after "TRIGGER" is echoed verbatim */
3582 108 : tgdef = PQgetvalue(result, i, 1);
3583 108 : usingpos = strstr(tgdef, " TRIGGER ");
3584 108 : if (usingpos)
3585 108 : tgdef = usingpos + 9;
3586 :
3587 108 : printfPQExpBuffer(&buf, " %s", tgdef);
3588 :
3589 : /* Visually distinguish inherited triggers */
3590 108 : if (!PQgetisnull(result, i, 4))
3591 24 : appendPQExpBuffer(&buf, ", ON TABLE %s",
3592 : PQgetvalue(result, i, 4));
3593 :
3594 108 : printTableAddFooter(&cont, buf.data);
3595 : }
3596 : }
3597 : }
3598 225 : PQclear(result);
3599 : }
3600 :
3601 : /*
3602 : * Finish printing the footer information about a table.
3603 : */
3604 2644 : if (tableinfo.relkind == RELKIND_RELATION ||
3605 946 : tableinfo.relkind == RELKIND_MATVIEW ||
3606 906 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
3607 781 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3608 572 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
3609 484 : tableinfo.relkind == RELKIND_TOASTVALUE)
3610 : {
3611 : bool is_partitioned;
3612 : PGresult *result;
3613 : int tuples;
3614 :
3615 : /* simplify some repeated tests below */
3616 4119 : is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3617 1955 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX);
3618 :
3619 : /* print foreign server name */
3620 2164 : if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
3621 : {
3622 : char *ftoptions;
3623 :
3624 : /* Footer information about foreign table */
3625 125 : printfPQExpBuffer(&buf, "/* %s */\n",
3626 : _("Get foreign server for this table"));
3627 125 : appendPQExpBuffer(&buf,
3628 : "SELECT s.srvname,\n"
3629 : " pg_catalog.array_to_string(ARRAY(\n"
3630 : " SELECT pg_catalog.quote_ident(option_name)"
3631 : " || ' ' || pg_catalog.quote_literal(option_value)\n"
3632 : " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ')\n"
3633 : "FROM pg_catalog.pg_foreign_table f,\n"
3634 : " pg_catalog.pg_foreign_server s\n"
3635 : "WHERE f.ftrelid = '%s' AND s.oid = f.ftserver;",
3636 : oid);
3637 125 : result = PSQLexec(buf.data);
3638 125 : if (!result)
3639 0 : goto error_return;
3640 125 : else if (PQntuples(result) != 1)
3641 : {
3642 0 : PQclear(result);
3643 0 : goto error_return;
3644 : }
3645 :
3646 : /* Print server name */
3647 125 : printfPQExpBuffer(&buf, _("Server: %s"),
3648 : PQgetvalue(result, 0, 0));
3649 125 : printTableAddFooter(&cont, buf.data);
3650 :
3651 : /* Print per-table FDW options, if any */
3652 125 : ftoptions = PQgetvalue(result, 0, 1);
3653 125 : if (ftoptions && ftoptions[0] != '\0')
3654 : {
3655 109 : printfPQExpBuffer(&buf, _("FDW options: (%s)"), ftoptions);
3656 109 : printTableAddFooter(&cont, buf.data);
3657 : }
3658 125 : PQclear(result);
3659 : }
3660 :
3661 : /* print tables inherited from (exclude partitioned parents) */
3662 2164 : printfPQExpBuffer(&buf, "/* %s */\n",
3663 : _("Get inheritance parent tables"));
3664 2164 : appendPQExpBuffer(&buf,
3665 : "SELECT c.oid::pg_catalog.regclass\n"
3666 : "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3667 : "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n"
3668 : " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE)
3669 : " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX)
3670 : "\nORDER BY inhseqno;",
3671 : oid);
3672 :
3673 2164 : result = PSQLexec(buf.data);
3674 2164 : if (!result)
3675 0 : goto error_return;
3676 : else
3677 : {
3678 2164 : const char *s = _("Inherits");
3679 :
3680 2164 : tuples = PQntuples(result);
3681 :
3682 2164 : if (tuples > 0)
3683 : {
3684 368 : printfPQExpBuffer(&buf, "%s:", s);
3685 368 : printTableAddFooter(&cont, buf.data);
3686 : }
3687 :
3688 2640 : for (i = 0; i < tuples; i++)
3689 : {
3690 476 : printfPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 0));
3691 476 : printTableAddFooter(&cont, buf.data);
3692 : }
3693 :
3694 2164 : PQclear(result);
3695 : }
3696 :
3697 : /* print child tables (with additional info if partitions) */
3698 2164 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get child tables"));
3699 2164 : if (pset.sversion >= 140000)
3700 2164 : appendPQExpBuffer(&buf,
3701 : "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3702 : " inhdetachpending,"
3703 : " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3704 : "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3705 : "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3706 : "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3707 : " c.oid::pg_catalog.regclass::pg_catalog.text;",
3708 : oid);
3709 0 : else if (pset.sversion >= 100000)
3710 0 : appendPQExpBuffer(&buf,
3711 : "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3712 : " false AS inhdetachpending,"
3713 : " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3714 : "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3715 : "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3716 : "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3717 : " c.oid::pg_catalog.regclass::pg_catalog.text;",
3718 : oid);
3719 : else
3720 0 : appendPQExpBuffer(&buf,
3721 : "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3722 : " false AS inhdetachpending, NULL\n"
3723 : "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3724 : "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3725 : "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;",
3726 : oid);
3727 :
3728 2164 : result = PSQLexec(buf.data);
3729 2164 : if (!result)
3730 0 : goto error_return;
3731 2164 : tuples = PQntuples(result);
3732 :
3733 : /*
3734 : * For a partitioned table with no partitions, always print the number
3735 : * of partitions as zero, even when verbose output is expected.
3736 : * Otherwise, we will not print "Partitions" section for a partitioned
3737 : * table without any partitions.
3738 : */
3739 2164 : if (is_partitioned && tuples == 0)
3740 : {
3741 44 : printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples);
3742 44 : printTableAddFooter(&cont, buf.data);
3743 : }
3744 2120 : else if (!verbose)
3745 : {
3746 : /* print the number of child tables, if any */
3747 1210 : if (tuples > 0)
3748 : {
3749 248 : if (is_partitioned)
3750 180 : printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
3751 : else
3752 68 : printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
3753 248 : printTableAddFooter(&cont, buf.data);
3754 : }
3755 : }
3756 : else
3757 : {
3758 : /* display the list of child tables */
3759 910 : const char *ct = is_partitioned ? _("Partitions") : _("Child tables");
3760 :
3761 910 : if (tuples > 0)
3762 : {
3763 221 : printfPQExpBuffer(&buf, "%s:", ct);
3764 221 : printTableAddFooter(&cont, buf.data);
3765 : }
3766 :
3767 1292 : for (i = 0; i < tuples; i++)
3768 : {
3769 382 : char child_relkind = *PQgetvalue(result, i, 1);
3770 :
3771 382 : printfPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 0));
3772 382 : if (!PQgetisnull(result, i, 3))
3773 178 : appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 3));
3774 382 : if (child_relkind == RELKIND_PARTITIONED_TABLE ||
3775 : child_relkind == RELKIND_PARTITIONED_INDEX)
3776 16 : appendPQExpBufferStr(&buf, ", PARTITIONED");
3777 366 : else if (child_relkind == RELKIND_FOREIGN_TABLE)
3778 72 : appendPQExpBufferStr(&buf, ", FOREIGN");
3779 382 : if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
3780 0 : appendPQExpBufferStr(&buf, " (DETACH PENDING)");
3781 :
3782 382 : printTableAddFooter(&cont, buf.data);
3783 : }
3784 : }
3785 2164 : PQclear(result);
3786 :
3787 : /* Table type */
3788 2164 : if (tableinfo.reloftype)
3789 : {
3790 40 : printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
3791 40 : printTableAddFooter(&cont, buf.data);
3792 : }
3793 :
3794 2164 : if (verbose &&
3795 934 : (tableinfo.relkind == RELKIND_RELATION ||
3796 237 : tableinfo.relkind == RELKIND_MATVIEW) &&
3797 :
3798 : /*
3799 : * No need to display default values; we already display a REPLICA
3800 : * IDENTITY marker on indexes.
3801 : */
3802 737 : tableinfo.relreplident != REPLICA_IDENTITY_INDEX &&
3803 733 : ((strcmp(schemaname, "pg_catalog") != 0 &&
3804 733 : tableinfo.relreplident != REPLICA_IDENTITY_DEFAULT) ||
3805 729 : (strcmp(schemaname, "pg_catalog") == 0 &&
3806 0 : tableinfo.relreplident != REPLICA_IDENTITY_NOTHING)))
3807 : {
3808 4 : const char *s = _("Replica Identity");
3809 :
3810 4 : printfPQExpBuffer(&buf, "%s: %s",
3811 : s,
3812 4 : tableinfo.relreplident == REPLICA_IDENTITY_FULL ? "FULL" :
3813 0 : tableinfo.relreplident == REPLICA_IDENTITY_DEFAULT ? "NOTHING" :
3814 : "???");
3815 :
3816 4 : printTableAddFooter(&cont, buf.data);
3817 : }
3818 :
3819 : /* OIDs, if verbose and not a materialized view */
3820 2164 : if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids)
3821 0 : printTableAddFooter(&cont, _("Has OIDs: yes"));
3822 :
3823 : /* Tablespace info */
3824 2164 : add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
3825 : true);
3826 :
3827 : /* Access method info */
3828 2164 : if (verbose && tableinfo.relam != NULL && !pset.hide_tableam)
3829 : {
3830 8 : printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam);
3831 8 : printTableAddFooter(&cont, buf.data);
3832 : }
3833 : }
3834 :
3835 : /* reloptions, if verbose */
3836 2644 : if (verbose &&
3837 1203 : tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
3838 : {
3839 21 : const char *t = _("Options");
3840 :
3841 21 : printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions);
3842 21 : printTableAddFooter(&cont, buf.data);
3843 : }
3844 :
3845 2644 : printTable(&cont, pset.queryFout, false, pset.logfile);
3846 :
3847 2644 : retval = true;
3848 :
3849 2796 : error_return:
3850 :
3851 : /* clean up */
3852 2796 : if (printTableInitialized)
3853 2644 : printTableCleanup(&cont);
3854 2796 : termPQExpBuffer(&buf);
3855 2796 : termPQExpBuffer(&title);
3856 2796 : termPQExpBuffer(&tmpbuf);
3857 :
3858 2796 : free(view_def);
3859 :
3860 2796 : PQclear(res);
3861 :
3862 2796 : return retval;
3863 : }
3864 :
3865 : /*
3866 : * Add a tablespace description to a footer. If 'newline' is true, it is added
3867 : * in a new line; otherwise it's appended to the current value of the last
3868 : * footer.
3869 : */
3870 : static void
3871 3358 : add_tablespace_footer(printTableContent *const cont, char relkind,
3872 : Oid tablespace, const bool newline)
3873 : {
3874 : /* relkinds for which we support tablespaces */
3875 3358 : if (relkind == RELKIND_RELATION ||
3876 1620 : relkind == RELKIND_MATVIEW ||
3877 426 : relkind == RELKIND_INDEX ||
3878 217 : relkind == RELKIND_PARTITIONED_TABLE ||
3879 129 : relkind == RELKIND_PARTITIONED_INDEX ||
3880 : relkind == RELKIND_TOASTVALUE)
3881 : {
3882 : /*
3883 : * We ignore the database default tablespace so that users not using
3884 : * tablespaces don't need to know about them.
3885 : */
3886 3233 : if (tablespace != 0)
3887 : {
3888 136 : PGresult *result = NULL;
3889 : PQExpBufferData buf;
3890 :
3891 136 : initPQExpBuffer(&buf);
3892 136 : printfPQExpBuffer(&buf, "/* %s */\n",
3893 : _("Get tablespace information for this relation"));
3894 136 : appendPQExpBuffer(&buf,
3895 : "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3896 : "WHERE oid = '%u';", tablespace);
3897 136 : result = PSQLexec(buf.data);
3898 136 : if (!result)
3899 : {
3900 0 : termPQExpBuffer(&buf);
3901 0 : return;
3902 : }
3903 : /* Should always be the case, but.... */
3904 136 : if (PQntuples(result) > 0)
3905 : {
3906 136 : if (newline)
3907 : {
3908 : /* Add the tablespace as a new footer */
3909 116 : printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3910 : PQgetvalue(result, 0, 0));
3911 116 : printTableAddFooter(cont, buf.data);
3912 : }
3913 : else
3914 : {
3915 : /* Append the tablespace to the latest footer */
3916 20 : printfPQExpBuffer(&buf, "%s", cont->footer->data);
3917 :
3918 : /*-------
3919 : translator: before this string there's an index description like
3920 : '"foo_pkey" PRIMARY KEY, btree (a)' */
3921 20 : appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3922 : PQgetvalue(result, 0, 0));
3923 20 : printTableSetFooter(cont, buf.data);
3924 : }
3925 : }
3926 136 : PQclear(result);
3927 136 : termPQExpBuffer(&buf);
3928 : }
3929 : }
3930 : }
3931 :
3932 : /*
3933 : * \du or \dg
3934 : *
3935 : * Describes roles. Any schema portion of the pattern is ignored.
3936 : */
3937 : bool
3938 20 : describeRoles(const char *pattern, bool verbose, bool showSystem)
3939 : {
3940 : PQExpBufferData buf;
3941 : PGresult *res;
3942 : printTableContent cont;
3943 20 : printTableOpt myopt = pset.popt.topt;
3944 20 : int ncols = 2;
3945 20 : int nrows = 0;
3946 : int i;
3947 : int conns;
3948 20 : const char align = 'l';
3949 : char **attr;
3950 :
3951 20 : myopt.default_footer = false;
3952 :
3953 20 : initPQExpBuffer(&buf);
3954 :
3955 20 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching roles"));
3956 20 : appendPQExpBuffer(&buf,
3957 : "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
3958 : " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
3959 : " r.rolconnlimit, r.rolvaliduntil");
3960 :
3961 20 : if (verbose)
3962 : {
3963 0 : appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
3964 0 : ncols++;
3965 : }
3966 20 : appendPQExpBufferStr(&buf, "\n, r.rolreplication");
3967 :
3968 20 : if (pset.sversion >= 90500)
3969 : {
3970 20 : appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
3971 : }
3972 :
3973 20 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
3974 :
3975 20 : if (!showSystem && !pattern)
3976 0 : appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
3977 :
3978 20 : if (!validateSQLNamePattern(&buf, pattern, false, false,
3979 : NULL, "r.rolname", NULL, NULL,
3980 : NULL, 1))
3981 : {
3982 12 : termPQExpBuffer(&buf);
3983 12 : return false;
3984 : }
3985 :
3986 8 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
3987 :
3988 8 : res = PSQLexec(buf.data);
3989 8 : if (!res)
3990 0 : return false;
3991 :
3992 8 : nrows = PQntuples(res);
3993 8 : attr = pg_malloc0_array(char *, nrows + 1);
3994 :
3995 8 : printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
3996 :
3997 8 : printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
3998 8 : printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
3999 :
4000 8 : if (verbose)
4001 0 : printTableAddHeader(&cont, gettext_noop("Description"), true, align);
4002 :
4003 20 : for (i = 0; i < nrows; i++)
4004 : {
4005 12 : printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
4006 :
4007 12 : resetPQExpBuffer(&buf);
4008 12 : if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
4009 0 : add_role_attribute(&buf, _("Superuser"));
4010 :
4011 12 : if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
4012 0 : add_role_attribute(&buf, _("No inheritance"));
4013 :
4014 12 : if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
4015 0 : add_role_attribute(&buf, _("Create role"));
4016 :
4017 12 : if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
4018 0 : add_role_attribute(&buf, _("Create DB"));
4019 :
4020 12 : if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
4021 12 : add_role_attribute(&buf, _("Cannot login"));
4022 :
4023 12 : if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
4024 0 : add_role_attribute(&buf, _("Replication"));
4025 :
4026 12 : if (pset.sversion >= 90500)
4027 12 : if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
4028 0 : add_role_attribute(&buf, _("Bypass RLS"));
4029 :
4030 12 : conns = atoi(PQgetvalue(res, i, 6));
4031 12 : if (conns >= 0)
4032 : {
4033 0 : if (buf.len > 0)
4034 0 : appendPQExpBufferChar(&buf, '\n');
4035 :
4036 0 : if (conns == 0)
4037 0 : appendPQExpBufferStr(&buf, _("No connections"));
4038 : else
4039 0 : appendPQExpBuffer(&buf, ngettext("%d connection",
4040 : "%d connections",
4041 : conns),
4042 : conns);
4043 : }
4044 :
4045 12 : if (strcmp(PQgetvalue(res, i, 7), "") != 0)
4046 : {
4047 0 : if (buf.len > 0)
4048 0 : appendPQExpBufferChar(&buf, '\n');
4049 0 : appendPQExpBufferStr(&buf, _("Password valid until "));
4050 0 : appendPQExpBufferStr(&buf, PQgetvalue(res, i, 7));
4051 : }
4052 :
4053 12 : attr[i] = pg_strdup(buf.data);
4054 :
4055 12 : printTableAddCell(&cont, attr[i], false, false);
4056 :
4057 12 : if (verbose)
4058 0 : printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
4059 : }
4060 8 : termPQExpBuffer(&buf);
4061 :
4062 8 : printTable(&cont, pset.queryFout, false, pset.logfile);
4063 8 : printTableCleanup(&cont);
4064 :
4065 20 : for (i = 0; i < nrows; i++)
4066 12 : free(attr[i]);
4067 8 : free(attr);
4068 :
4069 8 : PQclear(res);
4070 8 : return true;
4071 : }
4072 :
4073 : static void
4074 12 : add_role_attribute(PQExpBuffer buf, const char *const str)
4075 : {
4076 12 : if (buf->len > 0)
4077 0 : appendPQExpBufferStr(buf, ", ");
4078 :
4079 12 : appendPQExpBufferStr(buf, str);
4080 12 : }
4081 :
4082 : /*
4083 : * \drds
4084 : */
4085 : bool
4086 17 : listDbRoleSettings(const char *pattern, const char *pattern2)
4087 : {
4088 : PQExpBufferData buf;
4089 : PGresult *res;
4090 17 : printQueryOpt myopt = pset.popt;
4091 : bool havewhere;
4092 :
4093 17 : initPQExpBuffer(&buf);
4094 :
4095 17 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get per-database and per-role settings"));
4096 17 : appendPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
4097 : "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
4098 : "FROM pg_catalog.pg_db_role_setting s\n"
4099 : "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n"
4100 : "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n",
4101 : gettext_noop("Role"),
4102 : gettext_noop("Database"),
4103 : gettext_noop("Settings"));
4104 17 : if (!validateSQLNamePattern(&buf, pattern, false, false,
4105 : NULL, "r.rolname", NULL, NULL, &havewhere, 1))
4106 12 : goto error_return;
4107 5 : if (!validateSQLNamePattern(&buf, pattern2, havewhere, false,
4108 : NULL, "d.datname", NULL, NULL,
4109 : NULL, 1))
4110 0 : goto error_return;
4111 5 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4112 :
4113 5 : res = PSQLexec(buf.data);
4114 5 : termPQExpBuffer(&buf);
4115 5 : if (!res)
4116 0 : return false;
4117 :
4118 : /*
4119 : * Most functions in this file are content to print an empty table when
4120 : * there are no matching objects. We intentionally deviate from that
4121 : * here, but only in !quiet mode, because of the possibility that the user
4122 : * is confused about what the two pattern arguments mean.
4123 : */
4124 5 : if (PQntuples(res) == 0 && !pset.quiet)
4125 : {
4126 1 : if (pattern && pattern2)
4127 0 : pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".",
4128 : pattern, pattern2);
4129 1 : else if (pattern)
4130 0 : pg_log_error("Did not find any settings for role \"%s\".",
4131 : pattern);
4132 : else
4133 1 : pg_log_error("Did not find any settings.");
4134 : }
4135 : else
4136 : {
4137 4 : myopt.title = _("List of settings");
4138 4 : myopt.translate_header = true;
4139 :
4140 4 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4141 : }
4142 :
4143 5 : PQclear(res);
4144 5 : return true;
4145 :
4146 12 : error_return:
4147 12 : termPQExpBuffer(&buf);
4148 12 : return false;
4149 : }
4150 :
4151 : /*
4152 : * \drg
4153 : * Describes role grants.
4154 : */
4155 : bool
4156 4 : describeRoleGrants(const char *pattern, bool showSystem)
4157 : {
4158 : PQExpBufferData buf;
4159 : PGresult *res;
4160 4 : printQueryOpt myopt = pset.popt;
4161 :
4162 4 : initPQExpBuffer(&buf);
4163 4 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching role grants"));
4164 4 : appendPQExpBuffer(&buf,
4165 : "SELECT m.rolname AS \"%s\", r.rolname AS \"%s\",\n"
4166 : " pg_catalog.concat_ws(', ',\n",
4167 : gettext_noop("Role name"),
4168 : gettext_noop("Member of"));
4169 :
4170 4 : if (pset.sversion >= 160000)
4171 4 : appendPQExpBufferStr(&buf,
4172 : " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
4173 : " CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n"
4174 : " CASE WHEN pam.set_option THEN 'SET' END\n");
4175 : else
4176 0 : appendPQExpBufferStr(&buf,
4177 : " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
4178 : " CASE WHEN m.rolinherit THEN 'INHERIT' END,\n"
4179 : " 'SET'\n");
4180 :
4181 4 : appendPQExpBuffer(&buf,
4182 : " ) AS \"%s\",\n"
4183 : " g.rolname AS \"%s\"\n",
4184 : gettext_noop("Options"),
4185 : gettext_noop("Grantor"));
4186 :
4187 4 : appendPQExpBufferStr(&buf,
4188 : "FROM pg_catalog.pg_roles m\n"
4189 : " JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n"
4190 : " LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n"
4191 : " LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n");
4192 :
4193 4 : if (!showSystem && !pattern)
4194 0 : appendPQExpBufferStr(&buf, "WHERE m.rolname !~ '^pg_'\n");
4195 :
4196 4 : if (!validateSQLNamePattern(&buf, pattern, false, false,
4197 : NULL, "m.rolname", NULL, NULL,
4198 : NULL, 1))
4199 : {
4200 0 : termPQExpBuffer(&buf);
4201 0 : return false;
4202 : }
4203 :
4204 4 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;\n");
4205 :
4206 4 : res = PSQLexec(buf.data);
4207 4 : termPQExpBuffer(&buf);
4208 4 : if (!res)
4209 0 : return false;
4210 :
4211 4 : myopt.title = _("List of role grants");
4212 4 : myopt.translate_header = true;
4213 :
4214 4 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4215 :
4216 4 : PQclear(res);
4217 4 : return true;
4218 : }
4219 :
4220 :
4221 : /*
4222 : * listTables()
4223 : *
4224 : * handler for \dt, \di, etc.
4225 : *
4226 : * tabtypes is an array of characters, specifying what info is desired:
4227 : * t - tables
4228 : * i - indexes
4229 : * v - views
4230 : * m - materialized views
4231 : * s - sequences
4232 : * E - foreign table (Note: different from 'f', the relkind value)
4233 : * G - property graphs
4234 : * (any order of the above is fine)
4235 : */
4236 : bool
4237 256 : listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
4238 : {
4239 256 : bool showTables = strchr(tabtypes, 't') != NULL;
4240 256 : bool showIndexes = strchr(tabtypes, 'i') != NULL;
4241 256 : bool showViews = strchr(tabtypes, 'v') != NULL;
4242 256 : bool showMatViews = strchr(tabtypes, 'm') != NULL;
4243 256 : bool showSeq = strchr(tabtypes, 's') != NULL;
4244 256 : bool showForeign = strchr(tabtypes, 'E') != NULL;
4245 256 : bool showPropGraphs = strchr(tabtypes, 'G') != NULL;
4246 :
4247 : int ntypes;
4248 : PQExpBufferData buf;
4249 : PGresult *res;
4250 256 : printQueryOpt myopt = pset.popt;
4251 : int cols_so_far;
4252 256 : bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
4253 :
4254 : /* Count the number of explicitly-requested relation types */
4255 256 : ntypes = showTables + showIndexes + showViews + showMatViews +
4256 256 : showSeq + showForeign + showPropGraphs;
4257 : /* If none, we default to \dtvmsEG (but see also command.c) */
4258 256 : if (ntypes == 0)
4259 0 : showTables = showViews = showMatViews = showSeq = showForeign = showPropGraphs = true;
4260 :
4261 256 : initPQExpBuffer(&buf);
4262 :
4263 256 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching relations"));
4264 256 : appendPQExpBuffer(&buf,
4265 : "SELECT n.nspname as \"%s\",\n"
4266 : " c.relname as \"%s\",\n"
4267 : " CASE c.relkind"
4268 : " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
4269 : " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'"
4270 : " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
4271 : " WHEN " CppAsString2(RELKIND_INDEX) " THEN '%s'"
4272 : " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
4273 : " WHEN " CppAsString2(RELKIND_TOASTVALUE) " THEN '%s'"
4274 : " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
4275 : " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4276 : " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4277 : " WHEN " CppAsString2(RELKIND_PROPGRAPH) " THEN '%s'"
4278 : " END as \"%s\",\n"
4279 : " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4280 : gettext_noop("Schema"),
4281 : gettext_noop("Name"),
4282 : gettext_noop("table"),
4283 : gettext_noop("view"),
4284 : gettext_noop("materialized view"),
4285 : gettext_noop("index"),
4286 : gettext_noop("sequence"),
4287 : gettext_noop("TOAST table"),
4288 : gettext_noop("foreign table"),
4289 : gettext_noop("partitioned table"),
4290 : gettext_noop("partitioned index"),
4291 : gettext_noop("property graph"),
4292 : gettext_noop("Type"),
4293 : gettext_noop("Owner"));
4294 256 : cols_so_far = 4;
4295 :
4296 256 : if (showIndexes)
4297 : {
4298 28 : appendPQExpBuffer(&buf,
4299 : ",\n c2.relname as \"%s\"",
4300 : gettext_noop("Table"));
4301 28 : cols_so_far++;
4302 : }
4303 :
4304 256 : if (verbose)
4305 : {
4306 : /*
4307 : * Show whether a relation is permanent, temporary, or unlogged.
4308 : */
4309 28 : appendPQExpBuffer(&buf,
4310 : ",\n CASE c.relpersistence "
4311 : "WHEN " CppAsString2(RELPERSISTENCE_PERMANENT) " THEN '%s' "
4312 : "WHEN " CppAsString2(RELPERSISTENCE_TEMP) " THEN '%s' "
4313 : "WHEN " CppAsString2(RELPERSISTENCE_UNLOGGED) " THEN '%s' "
4314 : "END as \"%s\"",
4315 : gettext_noop("permanent"),
4316 : gettext_noop("temporary"),
4317 : gettext_noop("unlogged"),
4318 : gettext_noop("Persistence"));
4319 28 : translate_columns[cols_so_far] = true;
4320 :
4321 : /*
4322 : * We don't bother to count cols_so_far below here, as there's no need
4323 : * to; this might change with future additions to the output columns.
4324 : */
4325 :
4326 : /*
4327 : * Access methods exist for tables, materialized views and indexes.
4328 : * This has been introduced in PostgreSQL 12 for tables.
4329 : */
4330 28 : if (pset.sversion >= 120000 && !pset.hide_tableam &&
4331 8 : (showTables || showMatViews || showIndexes))
4332 12 : appendPQExpBuffer(&buf,
4333 : ",\n am.amname as \"%s\"",
4334 : gettext_noop("Access method"));
4335 :
4336 28 : appendPQExpBuffer(&buf,
4337 : ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\""
4338 : ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4339 : gettext_noop("Size"),
4340 : gettext_noop("Description"));
4341 : }
4342 :
4343 256 : appendPQExpBufferStr(&buf,
4344 : "\nFROM pg_catalog.pg_class c"
4345 : "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4346 :
4347 256 : if (pset.sversion >= 120000 && !pset.hide_tableam &&
4348 8 : (showTables || showMatViews || showIndexes))
4349 12 : appendPQExpBufferStr(&buf,
4350 : "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam");
4351 :
4352 256 : if (showIndexes)
4353 28 : appendPQExpBufferStr(&buf,
4354 : "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4355 : "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4356 :
4357 256 : appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4358 256 : if (showTables)
4359 : {
4360 96 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_RELATION) ","
4361 : CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
4362 : /* with 'S' or a pattern, allow 't' to match TOAST tables too */
4363 96 : if (showSystem || pattern)
4364 80 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_TOASTVALUE) ",");
4365 : }
4366 256 : if (showViews)
4367 44 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_VIEW) ",");
4368 256 : if (showMatViews)
4369 44 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_MATVIEW) ",");
4370 256 : if (showIndexes)
4371 28 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_INDEX) ","
4372 : CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
4373 256 : if (showSeq)
4374 40 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_SEQUENCE) ",");
4375 256 : if (showSystem || pattern)
4376 232 : appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
4377 256 : if (showForeign)
4378 24 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_FOREIGN_TABLE) ",");
4379 256 : if (showPropGraphs)
4380 40 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PROPGRAPH) ",");
4381 :
4382 256 : appendPQExpBufferStr(&buf, "''"); /* dummy */
4383 256 : appendPQExpBufferStr(&buf, ")\n");
4384 :
4385 256 : if (!showSystem && !pattern)
4386 24 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4387 : " AND n.nspname !~ '^pg_toast'\n"
4388 : " AND n.nspname <> 'information_schema'\n");
4389 :
4390 256 : if (!validateSQLNamePattern(&buf, pattern, true, false,
4391 : "n.nspname", "c.relname", NULL,
4392 : "pg_catalog.pg_table_is_visible(c.oid)",
4393 : NULL, 3))
4394 : {
4395 108 : termPQExpBuffer(&buf);
4396 108 : return false;
4397 : }
4398 :
4399 148 : appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
4400 :
4401 148 : res = PSQLexec(buf.data);
4402 148 : termPQExpBuffer(&buf);
4403 148 : if (!res)
4404 0 : return false;
4405 :
4406 : /*
4407 : * Most functions in this file are content to print an empty table when
4408 : * there are no matching objects. We intentionally deviate from that
4409 : * here, but only in !quiet mode, for historical reasons.
4410 : */
4411 148 : if (PQntuples(res) == 0 && !pset.quiet)
4412 : {
4413 4 : if (pattern)
4414 : {
4415 4 : if (ntypes != 1)
4416 0 : pg_log_error("Did not find any relations named \"%s\".",
4417 : pattern);
4418 4 : else if (showTables)
4419 0 : pg_log_error("Did not find any tables named \"%s\".",
4420 : pattern);
4421 4 : else if (showIndexes)
4422 0 : pg_log_error("Did not find any indexes named \"%s\".",
4423 : pattern);
4424 4 : else if (showViews)
4425 0 : pg_log_error("Did not find any views named \"%s\".",
4426 : pattern);
4427 4 : else if (showMatViews)
4428 0 : pg_log_error("Did not find any materialized views named \"%s\".",
4429 : pattern);
4430 4 : else if (showSeq)
4431 0 : pg_log_error("Did not find any sequences named \"%s\".",
4432 : pattern);
4433 4 : else if (showForeign)
4434 0 : pg_log_error("Did not find any foreign tables named \"%s\".",
4435 : pattern);
4436 4 : else if (showPropGraphs)
4437 4 : pg_log_error("Did not find any property graphs named \"%s\".",
4438 : pattern);
4439 : else /* should not get here */
4440 0 : pg_log_error_internal("Did not find any ??? named \"%s\".",
4441 : pattern);
4442 : }
4443 : else
4444 : {
4445 0 : if (ntypes != 1)
4446 0 : pg_log_error("Did not find any relations.");
4447 0 : else if (showTables)
4448 0 : pg_log_error("Did not find any tables.");
4449 0 : else if (showIndexes)
4450 0 : pg_log_error("Did not find any indexes.");
4451 0 : else if (showViews)
4452 0 : pg_log_error("Did not find any views.");
4453 0 : else if (showMatViews)
4454 0 : pg_log_error("Did not find any materialized views.");
4455 0 : else if (showSeq)
4456 0 : pg_log_error("Did not find any sequences.");
4457 0 : else if (showForeign)
4458 0 : pg_log_error("Did not find any foreign tables.");
4459 0 : else if (showPropGraphs)
4460 0 : pg_log_error("Did not find any property graphs.");
4461 : else /* should not get here */
4462 0 : pg_log_error_internal("Did not find any ??? relations.");
4463 : }
4464 : }
4465 : else
4466 : {
4467 144 : myopt.title =
4468 276 : (ntypes != 1) ? _("List of relations") :
4469 212 : (showTables) ? _("List of tables") :
4470 148 : (showIndexes) ? _("List of indexes") :
4471 120 : (showViews) ? _("List of views") :
4472 88 : (showMatViews) ? _("List of materialized views") :
4473 60 : (showSeq) ? _("List of sequences") :
4474 48 : (showForeign) ? _("List of foreign tables") :
4475 24 : (showPropGraphs) ? _("List of property graphs") :
4476 : "List of ???"; /* should not get here */
4477 144 : myopt.translate_header = true;
4478 144 : myopt.translate_columns = translate_columns;
4479 144 : myopt.n_translate_columns = lengthof(translate_columns);
4480 :
4481 144 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4482 : }
4483 :
4484 148 : PQclear(res);
4485 148 : return true;
4486 : }
4487 :
4488 : /*
4489 : * \dP
4490 : * Takes an optional regexp to select particular relations
4491 : *
4492 : * As with \d, you can specify the kinds of relations you want:
4493 : *
4494 : * t for tables
4495 : * i for indexes
4496 : *
4497 : * And there's additional flags:
4498 : *
4499 : * n to list non-leaf partitioned tables
4500 : *
4501 : * and you can mix and match these in any order.
4502 : */
4503 : bool
4504 72 : listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
4505 : {
4506 72 : bool showTables = strchr(reltypes, 't') != NULL;
4507 72 : bool showIndexes = strchr(reltypes, 'i') != NULL;
4508 72 : bool showNested = strchr(reltypes, 'n') != NULL;
4509 : PQExpBufferData buf;
4510 : PQExpBufferData title;
4511 : PGresult *res;
4512 72 : printQueryOpt myopt = pset.popt;
4513 72 : bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
4514 : const char *tabletitle;
4515 72 : bool mixed_output = false;
4516 :
4517 : /*
4518 : * Note: Declarative table partitioning is only supported as of Pg 10.0.
4519 : */
4520 72 : if (pset.sversion < 100000)
4521 : {
4522 : char sverbuf[32];
4523 :
4524 0 : pg_log_error("The server (version %s) does not support declarative table partitioning.",
4525 : formatPGVersionNumber(pset.sversion, false,
4526 : sverbuf, sizeof(sverbuf)));
4527 0 : return true;
4528 : }
4529 :
4530 : /* If no relation kind was selected, show them all */
4531 72 : if (!showTables && !showIndexes)
4532 48 : showTables = showIndexes = true;
4533 :
4534 72 : if (showIndexes && !showTables)
4535 12 : tabletitle = _("List of partitioned indexes"); /* \dPi */
4536 60 : else if (showTables && !showIndexes)
4537 12 : tabletitle = _("List of partitioned tables"); /* \dPt */
4538 : else
4539 : {
4540 : /* show all kinds */
4541 48 : tabletitle = _("List of partitioned relations");
4542 48 : mixed_output = true;
4543 : }
4544 :
4545 72 : initPQExpBuffer(&buf);
4546 :
4547 72 : printfPQExpBuffer(&buf, "/* %s */\n",
4548 : _("Get matching partitioned relations"));
4549 72 : appendPQExpBuffer(&buf,
4550 : "SELECT n.nspname as \"%s\",\n"
4551 : " c.relname as \"%s\",\n"
4552 : " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4553 : gettext_noop("Schema"),
4554 : gettext_noop("Name"),
4555 : gettext_noop("Owner"));
4556 :
4557 72 : if (mixed_output)
4558 : {
4559 48 : appendPQExpBuffer(&buf,
4560 : ",\n CASE c.relkind"
4561 : " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4562 : " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4563 : " END as \"%s\"",
4564 : gettext_noop("partitioned table"),
4565 : gettext_noop("partitioned index"),
4566 : gettext_noop("Type"));
4567 :
4568 48 : translate_columns[3] = true;
4569 : }
4570 :
4571 72 : if (showNested || pattern)
4572 60 : appendPQExpBuffer(&buf,
4573 : ",\n inh.inhparent::pg_catalog.regclass as \"%s\"",
4574 : gettext_noop("Parent name"));
4575 :
4576 72 : if (showIndexes)
4577 60 : appendPQExpBuffer(&buf,
4578 : ",\n c2.oid::pg_catalog.regclass as \"%s\"",
4579 : gettext_noop("Table"));
4580 :
4581 72 : if (verbose)
4582 : {
4583 : /*
4584 : * Table access methods were introduced in v12, and can be set on
4585 : * partitioned tables since v17.
4586 : */
4587 0 : appendPQExpBuffer(&buf, ",\n am.amname as \"%s\"",
4588 : gettext_noop("Access method"));
4589 :
4590 0 : if (showNested)
4591 : {
4592 0 : appendPQExpBuffer(&buf,
4593 : ",\n s.dps as \"%s\"",
4594 : gettext_noop("Leaf partition size"));
4595 0 : appendPQExpBuffer(&buf,
4596 : ",\n s.tps as \"%s\"",
4597 : gettext_noop("Total size"));
4598 : }
4599 : else
4600 : /* Sizes of all partitions are considered in this case. */
4601 0 : appendPQExpBuffer(&buf,
4602 : ",\n s.tps as \"%s\"",
4603 : gettext_noop("Total size"));
4604 :
4605 0 : appendPQExpBuffer(&buf,
4606 : ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4607 : gettext_noop("Description"));
4608 : }
4609 :
4610 72 : appendPQExpBufferStr(&buf,
4611 : "\nFROM pg_catalog.pg_class c"
4612 : "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4613 :
4614 72 : if (showIndexes)
4615 60 : appendPQExpBufferStr(&buf,
4616 : "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4617 : "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4618 :
4619 72 : if (showNested || pattern)
4620 60 : appendPQExpBufferStr(&buf,
4621 : "\n LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid");
4622 :
4623 72 : if (verbose)
4624 : {
4625 0 : appendPQExpBufferStr(&buf,
4626 : "\n LEFT JOIN pg_catalog.pg_am am ON c.relam = am.oid");
4627 :
4628 0 : if (pset.sversion < 120000)
4629 : {
4630 0 : appendPQExpBufferStr(&buf,
4631 : ",\n LATERAL (WITH RECURSIVE d\n"
4632 : " AS (SELECT inhrelid AS oid, 1 AS level\n"
4633 : " FROM pg_catalog.pg_inherits\n"
4634 : " WHERE inhparent = c.oid\n"
4635 : " UNION ALL\n"
4636 : " SELECT inhrelid, level + 1\n"
4637 : " FROM pg_catalog.pg_inherits i\n"
4638 : " JOIN d ON i.inhparent = d.oid)\n"
4639 : " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
4640 : "d.oid))) AS tps,\n"
4641 : " pg_catalog.pg_size_pretty(sum("
4642 : "\n CASE WHEN d.level = 1"
4643 : " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n"
4644 : " FROM d) s");
4645 : }
4646 : else
4647 : {
4648 : /* PostgreSQL 12 has pg_partition_tree function */
4649 0 : appendPQExpBufferStr(&buf,
4650 : ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
4651 : "\n CASE WHEN ppt.isleaf AND ppt.level = 1"
4652 : "\n THEN pg_catalog.pg_table_size(ppt.relid)"
4653 : " ELSE 0 END)) AS dps"
4654 : ",\n pg_catalog.pg_size_pretty(sum("
4655 : "pg_catalog.pg_table_size(ppt.relid))) AS tps"
4656 : "\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s");
4657 : }
4658 : }
4659 :
4660 72 : appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4661 72 : if (showTables)
4662 60 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
4663 72 : if (showIndexes)
4664 60 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
4665 72 : appendPQExpBufferStr(&buf, "''"); /* dummy */
4666 72 : appendPQExpBufferStr(&buf, ")\n");
4667 :
4668 72 : appendPQExpBufferStr(&buf, !showNested && !pattern ?
4669 : " AND NOT c.relispartition\n" : "");
4670 :
4671 72 : if (!pattern)
4672 24 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4673 : " AND n.nspname !~ '^pg_toast'\n"
4674 : " AND n.nspname <> 'information_schema'\n");
4675 :
4676 72 : if (!validateSQLNamePattern(&buf, pattern, true, false,
4677 : "n.nspname", "c.relname", NULL,
4678 : "pg_catalog.pg_table_is_visible(c.oid)",
4679 : NULL, 3))
4680 : {
4681 16 : termPQExpBuffer(&buf);
4682 16 : return false;
4683 : }
4684 :
4685 96 : appendPQExpBuffer(&buf, "ORDER BY \"Schema\", %s%s\"Name\";",
4686 : mixed_output ? "\"Type\" DESC, " : "",
4687 40 : showNested || pattern ? "\"Parent name\" NULLS FIRST, " : "");
4688 :
4689 56 : res = PSQLexec(buf.data);
4690 56 : termPQExpBuffer(&buf);
4691 56 : if (!res)
4692 0 : return false;
4693 :
4694 56 : initPQExpBuffer(&title);
4695 56 : appendPQExpBufferStr(&title, tabletitle);
4696 :
4697 56 : myopt.title = title.data;
4698 56 : myopt.translate_header = true;
4699 56 : myopt.translate_columns = translate_columns;
4700 56 : myopt.n_translate_columns = lengthof(translate_columns);
4701 :
4702 56 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4703 :
4704 56 : termPQExpBuffer(&title);
4705 :
4706 56 : PQclear(res);
4707 56 : return true;
4708 : }
4709 :
4710 : /*
4711 : * \dL
4712 : *
4713 : * Describes languages.
4714 : */
4715 : bool
4716 20 : listLanguages(const char *pattern, bool verbose, bool showSystem)
4717 : {
4718 : PQExpBufferData buf;
4719 : PGresult *res;
4720 20 : printQueryOpt myopt = pset.popt;
4721 :
4722 20 : initPQExpBuffer(&buf);
4723 :
4724 20 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching procedural languages"));
4725 20 : appendPQExpBuffer(&buf,
4726 : "SELECT l.lanname AS \"%s\",\n"
4727 : " pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n"
4728 : " l.lanpltrusted AS \"%s\"",
4729 : gettext_noop("Name"),
4730 : gettext_noop("Owner"),
4731 : gettext_noop("Trusted"));
4732 :
4733 20 : if (verbose)
4734 : {
4735 0 : appendPQExpBuffer(&buf,
4736 : ",\n NOT l.lanispl AS \"%s\",\n"
4737 : " l.lanplcallfoid::pg_catalog.regprocedure AS \"%s\",\n"
4738 : " l.lanvalidator::pg_catalog.regprocedure AS \"%s\",\n "
4739 : "l.laninline::pg_catalog.regprocedure AS \"%s\",\n ",
4740 : gettext_noop("Internal language"),
4741 : gettext_noop("Call handler"),
4742 : gettext_noop("Validator"),
4743 : gettext_noop("Inline handler"));
4744 0 : printACLColumn(&buf, "l.lanacl");
4745 : }
4746 :
4747 20 : appendPQExpBuffer(&buf,
4748 : ",\n d.description AS \"%s\""
4749 : "\nFROM pg_catalog.pg_language l\n"
4750 : "LEFT JOIN pg_catalog.pg_description d\n"
4751 : " ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
4752 : " AND d.objsubid = 0\n",
4753 : gettext_noop("Description"));
4754 :
4755 20 : if (pattern)
4756 : {
4757 20 : if (!validateSQLNamePattern(&buf, pattern, false, false,
4758 : NULL, "l.lanname", NULL, NULL,
4759 : NULL, 2))
4760 : {
4761 16 : termPQExpBuffer(&buf);
4762 16 : return false;
4763 : }
4764 : }
4765 :
4766 4 : if (!showSystem && !pattern)
4767 0 : appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n");
4768 :
4769 :
4770 4 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
4771 :
4772 4 : res = PSQLexec(buf.data);
4773 4 : termPQExpBuffer(&buf);
4774 4 : if (!res)
4775 0 : return false;
4776 :
4777 4 : myopt.title = _("List of languages");
4778 4 : myopt.translate_header = true;
4779 :
4780 4 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4781 :
4782 4 : PQclear(res);
4783 4 : return true;
4784 : }
4785 :
4786 :
4787 : /*
4788 : * \dD
4789 : *
4790 : * Describes domains.
4791 : */
4792 : bool
4793 40 : listDomains(const char *pattern, bool verbose, bool showSystem)
4794 : {
4795 : PQExpBufferData buf;
4796 : PGresult *res;
4797 40 : printQueryOpt myopt = pset.popt;
4798 :
4799 40 : initPQExpBuffer(&buf);
4800 :
4801 40 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching domains"));
4802 40 : appendPQExpBuffer(&buf,
4803 : "SELECT n.nspname as \"%s\",\n"
4804 : " t.typname as \"%s\",\n"
4805 : " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
4806 : " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
4807 : " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4808 : " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4809 : " t.typdefault as \"%s\",\n"
4810 : " pg_catalog.array_to_string(ARRAY(\n"
4811 : " SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid AND r.contype = " CppAsString2(CONSTRAINT_CHECK) " ORDER BY r.conname\n"
4812 : " ), ' ') as \"%s\"",
4813 : gettext_noop("Schema"),
4814 : gettext_noop("Name"),
4815 : gettext_noop("Type"),
4816 : gettext_noop("Collation"),
4817 : gettext_noop("Nullable"),
4818 : gettext_noop("Default"),
4819 : gettext_noop("Check"));
4820 :
4821 40 : if (verbose)
4822 : {
4823 4 : appendPQExpBufferStr(&buf, ",\n ");
4824 4 : printACLColumn(&buf, "t.typacl");
4825 4 : appendPQExpBuffer(&buf,
4826 : ",\n d.description as \"%s\"",
4827 : gettext_noop("Description"));
4828 : }
4829 :
4830 40 : appendPQExpBufferStr(&buf,
4831 : "\nFROM pg_catalog.pg_type t\n"
4832 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
4833 :
4834 40 : if (verbose)
4835 4 : appendPQExpBufferStr(&buf,
4836 : " LEFT JOIN pg_catalog.pg_description d "
4837 : "ON d.classoid = t.tableoid AND d.objoid = t.oid "
4838 : "AND d.objsubid = 0\n");
4839 :
4840 40 : appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
4841 :
4842 40 : if (!showSystem && !pattern)
4843 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4844 : " AND n.nspname <> 'information_schema'\n");
4845 :
4846 40 : if (!validateSQLNamePattern(&buf, pattern, true, false,
4847 : "n.nspname", "t.typname", NULL,
4848 : "pg_catalog.pg_type_is_visible(t.oid)",
4849 : NULL, 3))
4850 : {
4851 16 : termPQExpBuffer(&buf);
4852 16 : return false;
4853 : }
4854 :
4855 24 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4856 :
4857 24 : res = PSQLexec(buf.data);
4858 24 : termPQExpBuffer(&buf);
4859 24 : if (!res)
4860 0 : return false;
4861 :
4862 24 : myopt.title = _("List of domains");
4863 24 : myopt.translate_header = true;
4864 :
4865 24 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4866 :
4867 24 : PQclear(res);
4868 24 : return true;
4869 : }
4870 :
4871 : /*
4872 : * \dc
4873 : *
4874 : * Describes conversions.
4875 : */
4876 : bool
4877 28 : listConversions(const char *pattern, bool verbose, bool showSystem)
4878 : {
4879 : PQExpBufferData buf;
4880 : PGresult *res;
4881 28 : printQueryOpt myopt = pset.popt;
4882 : static const bool translate_columns[] =
4883 : {false, false, false, false, true, false};
4884 :
4885 28 : initPQExpBuffer(&buf);
4886 :
4887 28 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching conversions"));
4888 28 : appendPQExpBuffer(&buf,
4889 : "SELECT n.nspname AS \"%s\",\n"
4890 : " c.conname AS \"%s\",\n"
4891 : " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
4892 : " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
4893 : " CASE WHEN c.condefault THEN '%s'\n"
4894 : " ELSE '%s' END AS \"%s\"",
4895 : gettext_noop("Schema"),
4896 : gettext_noop("Name"),
4897 : gettext_noop("Source"),
4898 : gettext_noop("Destination"),
4899 : gettext_noop("yes"), gettext_noop("no"),
4900 : gettext_noop("Default?"));
4901 :
4902 28 : if (verbose)
4903 0 : appendPQExpBuffer(&buf,
4904 : ",\n d.description AS \"%s\"",
4905 : gettext_noop("Description"));
4906 :
4907 28 : appendPQExpBufferStr(&buf,
4908 : "\nFROM pg_catalog.pg_conversion c\n"
4909 : " JOIN pg_catalog.pg_namespace n "
4910 : "ON n.oid = c.connamespace\n");
4911 :
4912 28 : if (verbose)
4913 0 : appendPQExpBufferStr(&buf,
4914 : "LEFT JOIN pg_catalog.pg_description d "
4915 : "ON d.classoid = c.tableoid\n"
4916 : " AND d.objoid = c.oid "
4917 : "AND d.objsubid = 0\n");
4918 :
4919 28 : appendPQExpBufferStr(&buf, "WHERE true\n");
4920 :
4921 28 : if (!showSystem && !pattern)
4922 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4923 : " AND n.nspname <> 'information_schema'\n");
4924 :
4925 28 : if (!validateSQLNamePattern(&buf, pattern, true, false,
4926 : "n.nspname", "c.conname", NULL,
4927 : "pg_catalog.pg_conversion_is_visible(c.oid)",
4928 : NULL, 3))
4929 : {
4930 16 : termPQExpBuffer(&buf);
4931 16 : return false;
4932 : }
4933 :
4934 12 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4935 :
4936 12 : res = PSQLexec(buf.data);
4937 12 : termPQExpBuffer(&buf);
4938 12 : if (!res)
4939 0 : return false;
4940 :
4941 12 : myopt.title = _("List of conversions");
4942 12 : myopt.translate_header = true;
4943 12 : myopt.translate_columns = translate_columns;
4944 12 : myopt.n_translate_columns = lengthof(translate_columns);
4945 :
4946 12 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4947 :
4948 12 : PQclear(res);
4949 12 : return true;
4950 : }
4951 :
4952 : /*
4953 : * \dconfig
4954 : *
4955 : * Describes configuration parameters.
4956 : */
4957 : bool
4958 8 : describeConfigurationParameters(const char *pattern, bool verbose,
4959 : bool showSystem)
4960 : {
4961 : PQExpBufferData buf;
4962 : PGresult *res;
4963 8 : printQueryOpt myopt = pset.popt;
4964 :
4965 8 : initPQExpBuffer(&buf);
4966 :
4967 8 : printfPQExpBuffer(&buf, "/* %s */\n",
4968 : _("Get matching configuration parameters"));
4969 8 : appendPQExpBuffer(&buf,
4970 : "SELECT s.name AS \"%s\", "
4971 : "pg_catalog.current_setting(s.name) AS \"%s\"",
4972 : gettext_noop("Parameter"),
4973 : gettext_noop("Value"));
4974 :
4975 8 : if (verbose)
4976 : {
4977 4 : appendPQExpBuffer(&buf,
4978 : ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4979 : gettext_noop("Type"),
4980 : gettext_noop("Context"));
4981 4 : if (pset.sversion >= 150000)
4982 4 : printACLColumn(&buf, "p.paracl");
4983 : else
4984 0 : appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4985 : gettext_noop("Access privileges"));
4986 : }
4987 :
4988 8 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4989 :
4990 8 : if (verbose && pset.sversion >= 150000)
4991 4 : appendPQExpBufferStr(&buf,
4992 : " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4993 : " ON pg_catalog.lower(s.name) = p.parname\n");
4994 :
4995 8 : if (pattern)
4996 8 : processSQLNamePattern(pset.db, &buf, pattern,
4997 : false, false,
4998 : NULL, "pg_catalog.lower(s.name)", NULL,
4999 : NULL, NULL, NULL);
5000 : else
5001 0 : appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
5002 : " s.setting IS DISTINCT FROM s.boot_val\n");
5003 :
5004 8 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
5005 :
5006 8 : res = PSQLexec(buf.data);
5007 8 : termPQExpBuffer(&buf);
5008 8 : if (!res)
5009 0 : return false;
5010 :
5011 8 : if (pattern)
5012 8 : myopt.title = _("List of configuration parameters");
5013 : else
5014 0 : myopt.title = _("List of non-default configuration parameters");
5015 8 : myopt.translate_header = true;
5016 :
5017 8 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5018 :
5019 8 : PQclear(res);
5020 8 : return true;
5021 : }
5022 :
5023 : /*
5024 : * \dy
5025 : *
5026 : * Describes Event Triggers.
5027 : */
5028 : bool
5029 16 : listEventTriggers(const char *pattern, bool verbose)
5030 : {
5031 : PQExpBufferData buf;
5032 : PGresult *res;
5033 16 : printQueryOpt myopt = pset.popt;
5034 : static const bool translate_columns[] =
5035 : {false, false, false, true, false, false, false};
5036 :
5037 16 : if (pset.sversion < 90300)
5038 : {
5039 : char sverbuf[32];
5040 :
5041 0 : pg_log_error("The server (version %s) does not support event triggers.",
5042 : formatPGVersionNumber(pset.sversion, false,
5043 : sverbuf, sizeof(sverbuf)));
5044 0 : return true;
5045 : }
5046 :
5047 16 : initPQExpBuffer(&buf);
5048 :
5049 16 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching event triggers"));
5050 16 : appendPQExpBuffer(&buf,
5051 : "SELECT evtname as \"%s\", "
5052 : "evtevent as \"%s\", "
5053 : "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
5054 : " case evtenabled when 'O' then '%s'"
5055 : " when 'R' then '%s'"
5056 : " when 'A' then '%s'"
5057 : " when 'D' then '%s' end as \"%s\",\n"
5058 : " e.evtfoid::pg_catalog.regproc as \"%s\", "
5059 : "pg_catalog.array_to_string(array(select x"
5060 : " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
5061 : gettext_noop("Name"),
5062 : gettext_noop("Event"),
5063 : gettext_noop("Owner"),
5064 : gettext_noop("enabled"),
5065 : gettext_noop("replica"),
5066 : gettext_noop("always"),
5067 : gettext_noop("disabled"),
5068 : gettext_noop("Enabled"),
5069 : gettext_noop("Function"),
5070 : gettext_noop("Tags"));
5071 16 : if (verbose)
5072 0 : appendPQExpBuffer(&buf,
5073 : ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
5074 : gettext_noop("Description"));
5075 16 : appendPQExpBufferStr(&buf,
5076 : "\nFROM pg_catalog.pg_event_trigger e ");
5077 :
5078 16 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5079 : NULL, "evtname", NULL, NULL,
5080 : NULL, 1))
5081 : {
5082 12 : termPQExpBuffer(&buf);
5083 12 : return false;
5084 : }
5085 :
5086 4 : appendPQExpBufferStr(&buf, "ORDER BY 1");
5087 :
5088 4 : res = PSQLexec(buf.data);
5089 4 : termPQExpBuffer(&buf);
5090 4 : if (!res)
5091 0 : return false;
5092 :
5093 4 : myopt.title = _("List of event triggers");
5094 4 : myopt.translate_header = true;
5095 4 : myopt.translate_columns = translate_columns;
5096 4 : myopt.n_translate_columns = lengthof(translate_columns);
5097 :
5098 4 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5099 :
5100 4 : PQclear(res);
5101 4 : return true;
5102 : }
5103 :
5104 : /*
5105 : * \dX
5106 : *
5107 : * Describes extended statistics.
5108 : */
5109 : bool
5110 68 : listExtendedStats(const char *pattern, bool verbose)
5111 : {
5112 : PQExpBufferData buf;
5113 : PGresult *res;
5114 68 : printQueryOpt myopt = pset.popt;
5115 :
5116 68 : if (pset.sversion < 100000)
5117 : {
5118 : char sverbuf[32];
5119 :
5120 0 : pg_log_error("The server (version %s) does not support extended statistics.",
5121 : formatPGVersionNumber(pset.sversion, false,
5122 : sverbuf, sizeof(sverbuf)));
5123 0 : return true;
5124 : }
5125 :
5126 68 : initPQExpBuffer(&buf);
5127 :
5128 68 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching extended statistics"));
5129 68 : appendPQExpBuffer(&buf,
5130 : "SELECT \n"
5131 : "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS \"%s\", \n"
5132 : "es.stxname AS \"%s\", \n",
5133 : gettext_noop("Schema"),
5134 : gettext_noop("Name"));
5135 :
5136 68 : if (pset.sversion >= 140000)
5137 68 : appendPQExpBuffer(&buf,
5138 : "pg_catalog.format('%%s FROM %%s', \n"
5139 : " pg_catalog.pg_get_statisticsobjdef_columns(es.oid), \n"
5140 : " es.stxrelid::pg_catalog.regclass) AS \"%s\"",
5141 : gettext_noop("Definition"));
5142 : else
5143 0 : appendPQExpBuffer(&buf,
5144 : "pg_catalog.format('%%s FROM %%s', \n"
5145 : " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
5146 : " FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
5147 : " JOIN pg_catalog.pg_attribute a \n"
5148 : " ON (es.stxrelid = a.attrelid \n"
5149 : " AND a.attnum = s.attnum \n"
5150 : " AND NOT a.attisdropped)), \n"
5151 : "es.stxrelid::pg_catalog.regclass) AS \"%s\"",
5152 : gettext_noop("Definition"));
5153 :
5154 68 : appendPQExpBuffer(&buf,
5155 : ",\nCASE WHEN " CppAsString2(STATS_EXT_NDISTINCT) " = any(es.stxkind) THEN 'defined' \n"
5156 : "END AS \"%s\", \n"
5157 : "CASE WHEN " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(es.stxkind) THEN 'defined' \n"
5158 : "END AS \"%s\"",
5159 : gettext_noop("Ndistinct"),
5160 : gettext_noop("Dependencies"));
5161 :
5162 : /*
5163 : * Include the MCV statistics kind.
5164 : */
5165 68 : if (pset.sversion >= 120000)
5166 : {
5167 68 : appendPQExpBuffer(&buf,
5168 : ",\nCASE WHEN " CppAsString2(STATS_EXT_MCV) " = any(es.stxkind) THEN 'defined' \n"
5169 : "END AS \"%s\" ",
5170 : gettext_noop("MCV"));
5171 : }
5172 :
5173 68 : if (verbose)
5174 16 : appendPQExpBuffer(&buf,
5175 : ", \npg_catalog.obj_description(oid, 'pg_statistic_ext') AS \"%s\"\n",
5176 : gettext_noop("Description"));
5177 :
5178 68 : appendPQExpBufferStr(&buf,
5179 : " \nFROM pg_catalog.pg_statistic_ext es \n");
5180 :
5181 68 : if (!validateSQLNamePattern(&buf, pattern,
5182 : false, false,
5183 : "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
5184 : NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)",
5185 : NULL, 3))
5186 : {
5187 16 : termPQExpBuffer(&buf);
5188 16 : return false;
5189 : }
5190 :
5191 52 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5192 :
5193 52 : res = PSQLexec(buf.data);
5194 52 : termPQExpBuffer(&buf);
5195 52 : if (!res)
5196 0 : return false;
5197 :
5198 52 : myopt.title = _("List of extended statistics");
5199 52 : myopt.translate_header = true;
5200 :
5201 52 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5202 :
5203 52 : PQclear(res);
5204 52 : return true;
5205 : }
5206 :
5207 : /*
5208 : * \dC
5209 : *
5210 : * Describes casts.
5211 : */
5212 : bool
5213 28 : listCasts(const char *pattern, bool verbose)
5214 : {
5215 : PQExpBufferData buf;
5216 : PGresult *res;
5217 28 : printQueryOpt myopt = pset.popt;
5218 : static const bool translate_columns[] = {false, false, false, true, true, false};
5219 :
5220 28 : initPQExpBuffer(&buf);
5221 :
5222 28 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching casts"));
5223 28 : appendPQExpBuffer(&buf,
5224 : "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
5225 : " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n",
5226 : gettext_noop("Source type"),
5227 : gettext_noop("Target type"));
5228 :
5229 : /*
5230 : * We don't attempt to localize '(binary coercible)' or '(with inout)',
5231 : * because there's too much risk of gettext translating a function name
5232 : * that happens to match some string in the PO database.
5233 : */
5234 28 : appendPQExpBuffer(&buf,
5235 : " CASE WHEN c.castmethod = '%c' THEN '(binary coercible)'\n"
5236 : " WHEN c.castmethod = '%c' THEN '(with inout)'\n"
5237 : " ELSE p.proname\n"
5238 : " END AS \"%s\",\n",
5239 : COERCION_METHOD_BINARY,
5240 : COERCION_METHOD_INOUT,
5241 : gettext_noop("Function"));
5242 :
5243 28 : appendPQExpBuffer(&buf,
5244 : " CASE WHEN c.castcontext = '%c' THEN '%s'\n"
5245 : " WHEN c.castcontext = '%c' THEN '%s'\n"
5246 : " ELSE '%s'\n"
5247 : " END AS \"%s\"",
5248 : COERCION_CODE_EXPLICIT,
5249 : gettext_noop("no"),
5250 : COERCION_CODE_ASSIGNMENT,
5251 : gettext_noop("in assignment"),
5252 : gettext_noop("yes"),
5253 : gettext_noop("Implicit?"));
5254 :
5255 28 : if (verbose)
5256 0 : appendPQExpBuffer(&buf,
5257 : ",\n CASE WHEN p.proleakproof THEN '%s'\n"
5258 : " ELSE '%s'\n"
5259 : " END AS \"%s\",\n"
5260 : " d.description AS \"%s\"",
5261 : gettext_noop("yes"),
5262 : gettext_noop("no"),
5263 : gettext_noop("Leakproof?"),
5264 : gettext_noop("Description"));
5265 :
5266 : /*
5267 : * We need a left join to pg_proc for binary casts; the others are just
5268 : * paranoia.
5269 : */
5270 28 : appendPQExpBufferStr(&buf,
5271 : "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
5272 : " ON c.castfunc = p.oid\n"
5273 : " LEFT JOIN pg_catalog.pg_type ts\n"
5274 : " ON c.castsource = ts.oid\n"
5275 : " LEFT JOIN pg_catalog.pg_namespace ns\n"
5276 : " ON ns.oid = ts.typnamespace\n"
5277 : " LEFT JOIN pg_catalog.pg_type tt\n"
5278 : " ON c.casttarget = tt.oid\n"
5279 : " LEFT JOIN pg_catalog.pg_namespace nt\n"
5280 : " ON nt.oid = tt.typnamespace\n");
5281 :
5282 28 : if (verbose)
5283 0 : appendPQExpBufferStr(&buf,
5284 : " LEFT JOIN pg_catalog.pg_description d\n"
5285 : " ON d.classoid = c.tableoid AND d.objoid = "
5286 : "c.oid AND d.objsubid = 0\n");
5287 :
5288 28 : appendPQExpBufferStr(&buf, "WHERE ( (true");
5289 :
5290 : /*
5291 : * Match name pattern against either internal or external name of either
5292 : * castsource or casttarget
5293 : */
5294 28 : if (!validateSQLNamePattern(&buf, pattern, true, false,
5295 : "ns.nspname", "ts.typname",
5296 : "pg_catalog.format_type(ts.oid, NULL)",
5297 : "pg_catalog.pg_type_is_visible(ts.oid)",
5298 : NULL, 3))
5299 16 : goto error_return;
5300 :
5301 12 : appendPQExpBufferStr(&buf, ") OR (true");
5302 :
5303 12 : if (!validateSQLNamePattern(&buf, pattern, true, false,
5304 : "nt.nspname", "tt.typname",
5305 : "pg_catalog.format_type(tt.oid, NULL)",
5306 : "pg_catalog.pg_type_is_visible(tt.oid)",
5307 : NULL, 3))
5308 0 : goto error_return;
5309 :
5310 12 : appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
5311 :
5312 12 : res = PSQLexec(buf.data);
5313 12 : termPQExpBuffer(&buf);
5314 12 : if (!res)
5315 0 : return false;
5316 :
5317 12 : myopt.title = _("List of casts");
5318 12 : myopt.translate_header = true;
5319 12 : myopt.translate_columns = translate_columns;
5320 12 : myopt.n_translate_columns = lengthof(translate_columns);
5321 :
5322 12 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5323 :
5324 12 : PQclear(res);
5325 12 : return true;
5326 :
5327 16 : error_return:
5328 16 : termPQExpBuffer(&buf);
5329 16 : return false;
5330 : }
5331 :
5332 : /*
5333 : * \dO
5334 : *
5335 : * Describes collations.
5336 : */
5337 : bool
5338 32 : listCollations(const char *pattern, bool verbose, bool showSystem)
5339 : {
5340 : PQExpBufferData buf;
5341 : PGresult *res;
5342 32 : printQueryOpt myopt = pset.popt;
5343 : static const bool translate_columns[] = {false, false, false, false, false, false, false, true, false};
5344 :
5345 32 : initPQExpBuffer(&buf);
5346 :
5347 32 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching collations"));
5348 32 : appendPQExpBuffer(&buf,
5349 : "SELECT\n"
5350 : " n.nspname AS \"%s\",\n"
5351 : " c.collname AS \"%s\",\n",
5352 : gettext_noop("Schema"),
5353 : gettext_noop("Name"));
5354 :
5355 32 : if (pset.sversion >= 100000)
5356 32 : appendPQExpBuffer(&buf,
5357 : " CASE c.collprovider "
5358 : "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
5359 : "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
5360 : "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
5361 : "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
5362 : "END AS \"%s\",\n",
5363 : gettext_noop("Provider"));
5364 : else
5365 0 : appendPQExpBuffer(&buf,
5366 : " 'libc' AS \"%s\",\n",
5367 : gettext_noop("Provider"));
5368 :
5369 32 : appendPQExpBuffer(&buf,
5370 : " c.collcollate AS \"%s\",\n"
5371 : " c.collctype AS \"%s\",\n",
5372 : gettext_noop("Collate"),
5373 : gettext_noop("Ctype"));
5374 :
5375 32 : if (pset.sversion >= 170000)
5376 32 : appendPQExpBuffer(&buf,
5377 : " c.colllocale AS \"%s\",\n",
5378 : gettext_noop("Locale"));
5379 0 : else if (pset.sversion >= 150000)
5380 0 : appendPQExpBuffer(&buf,
5381 : " c.colliculocale AS \"%s\",\n",
5382 : gettext_noop("Locale"));
5383 : else
5384 0 : appendPQExpBuffer(&buf,
5385 : " c.collcollate AS \"%s\",\n",
5386 : gettext_noop("Locale"));
5387 :
5388 32 : if (pset.sversion >= 160000)
5389 32 : appendPQExpBuffer(&buf,
5390 : " c.collicurules AS \"%s\",\n",
5391 : gettext_noop("ICU Rules"));
5392 : else
5393 0 : appendPQExpBuffer(&buf,
5394 : " NULL AS \"%s\",\n",
5395 : gettext_noop("ICU Rules"));
5396 :
5397 32 : if (pset.sversion >= 120000)
5398 32 : appendPQExpBuffer(&buf,
5399 : " CASE WHEN c.collisdeterministic THEN '%s' ELSE '%s' END AS \"%s\"",
5400 : gettext_noop("yes"), gettext_noop("no"),
5401 : gettext_noop("Deterministic?"));
5402 : else
5403 0 : appendPQExpBuffer(&buf,
5404 : " '%s' AS \"%s\"",
5405 : gettext_noop("yes"),
5406 : gettext_noop("Deterministic?"));
5407 :
5408 32 : if (verbose)
5409 0 : appendPQExpBuffer(&buf,
5410 : ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
5411 : gettext_noop("Description"));
5412 :
5413 32 : appendPQExpBufferStr(&buf,
5414 : "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
5415 : "WHERE n.oid = c.collnamespace\n");
5416 :
5417 32 : if (!showSystem && !pattern)
5418 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
5419 : " AND n.nspname <> 'information_schema'\n");
5420 :
5421 : /*
5422 : * Hide collations that aren't usable in the current database's encoding.
5423 : * If you think to change this, note that pg_collation_is_visible rejects
5424 : * unusable collations, so you will need to hack name pattern processing
5425 : * somehow to avoid inconsistent behavior.
5426 : */
5427 32 : appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
5428 :
5429 32 : if (!validateSQLNamePattern(&buf, pattern, true, false,
5430 : "n.nspname", "c.collname", NULL,
5431 : "pg_catalog.pg_collation_is_visible(c.oid)",
5432 : NULL, 3))
5433 : {
5434 16 : termPQExpBuffer(&buf);
5435 16 : return false;
5436 : }
5437 :
5438 16 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5439 :
5440 16 : res = PSQLexec(buf.data);
5441 16 : termPQExpBuffer(&buf);
5442 16 : if (!res)
5443 0 : return false;
5444 :
5445 16 : myopt.title = _("List of collations");
5446 16 : myopt.translate_header = true;
5447 16 : myopt.translate_columns = translate_columns;
5448 16 : myopt.n_translate_columns = lengthof(translate_columns);
5449 :
5450 16 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5451 :
5452 16 : PQclear(res);
5453 16 : return true;
5454 : }
5455 :
5456 : /*
5457 : * \dn
5458 : *
5459 : * Describes schemas (namespaces)
5460 : */
5461 : bool
5462 16 : listSchemas(const char *pattern, bool verbose, bool showSystem)
5463 : {
5464 : PQExpBufferData buf;
5465 : PGresult *res;
5466 16 : printQueryOpt myopt = pset.popt;
5467 16 : int pub_schema_tuples = 0;
5468 16 : char **footers = NULL;
5469 :
5470 16 : initPQExpBuffer(&buf);
5471 :
5472 16 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching schemas"));
5473 16 : appendPQExpBuffer(&buf,
5474 : "SELECT n.nspname AS \"%s\",\n"
5475 : " pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
5476 : gettext_noop("Name"),
5477 : gettext_noop("Owner"));
5478 :
5479 16 : if (verbose)
5480 : {
5481 0 : appendPQExpBufferStr(&buf, ",\n ");
5482 0 : printACLColumn(&buf, "n.nspacl");
5483 0 : appendPQExpBuffer(&buf,
5484 : ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
5485 : gettext_noop("Description"));
5486 : }
5487 :
5488 16 : appendPQExpBufferStr(&buf,
5489 : "\nFROM pg_catalog.pg_namespace n\n");
5490 :
5491 16 : if (!showSystem && !pattern)
5492 0 : appendPQExpBufferStr(&buf,
5493 : "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
5494 :
5495 16 : if (!validateSQLNamePattern(&buf, pattern,
5496 16 : !showSystem && !pattern, false,
5497 : NULL, "n.nspname", NULL,
5498 : NULL,
5499 16 : NULL, 2))
5500 12 : goto error_return;
5501 :
5502 4 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
5503 :
5504 4 : res = PSQLexec(buf.data);
5505 4 : if (!res)
5506 0 : goto error_return;
5507 :
5508 4 : myopt.title = _("List of schemas");
5509 4 : myopt.translate_header = true;
5510 :
5511 4 : if (pattern && pset.sversion >= 150000)
5512 : {
5513 : PGresult *result;
5514 : int i;
5515 :
5516 4 : printfPQExpBuffer(&buf, "/* %s */\n",
5517 : _("Get publications that publish this schema"));
5518 4 : appendPQExpBuffer(&buf,
5519 : "SELECT pubname \n"
5520 : "FROM pg_catalog.pg_publication p\n"
5521 : " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
5522 : " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
5523 : "WHERE n.nspname = '%s'\n"
5524 : "ORDER BY 1",
5525 : pattern);
5526 4 : result = PSQLexec(buf.data);
5527 4 : if (!result)
5528 0 : goto error_return;
5529 : else
5530 4 : pub_schema_tuples = PQntuples(result);
5531 :
5532 4 : if (pub_schema_tuples > 0)
5533 : {
5534 : /*
5535 : * Allocate memory for footers. Size of footers will be 1 (for
5536 : * storing "Publications:" string) + publication schema mapping
5537 : * count + 1 (for storing NULL).
5538 : */
5539 0 : footers = pg_malloc_array(char *, 1 + pub_schema_tuples + 1);
5540 0 : footers[0] = pg_strdup(_("Publications:"));
5541 :
5542 : /* Might be an empty set - that's ok */
5543 0 : for (i = 0; i < pub_schema_tuples; i++)
5544 : {
5545 0 : printfPQExpBuffer(&buf, " \"%s\"",
5546 : PQgetvalue(result, i, 0));
5547 :
5548 0 : footers[i + 1] = pg_strdup(buf.data);
5549 : }
5550 :
5551 0 : footers[i + 1] = NULL;
5552 0 : myopt.footers = footers;
5553 : }
5554 :
5555 4 : PQclear(result);
5556 : }
5557 :
5558 4 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5559 :
5560 4 : termPQExpBuffer(&buf);
5561 4 : PQclear(res);
5562 :
5563 : /* Free the memory allocated for the footer */
5564 4 : if (footers)
5565 : {
5566 0 : char **footer = NULL;
5567 :
5568 0 : for (footer = footers; *footer; footer++)
5569 0 : pg_free(*footer);
5570 :
5571 0 : pg_free(footers);
5572 : }
5573 :
5574 4 : return true;
5575 :
5576 12 : error_return:
5577 12 : termPQExpBuffer(&buf);
5578 12 : return false;
5579 : }
5580 :
5581 :
5582 : /*
5583 : * \dFp
5584 : * list text search parsers
5585 : */
5586 : bool
5587 32 : listTSParsers(const char *pattern, bool verbose)
5588 : {
5589 : PQExpBufferData buf;
5590 : PGresult *res;
5591 32 : printQueryOpt myopt = pset.popt;
5592 :
5593 32 : if (verbose)
5594 0 : return listTSParsersVerbose(pattern);
5595 :
5596 32 : initPQExpBuffer(&buf);
5597 :
5598 32 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search parsers"));
5599 32 : appendPQExpBuffer(&buf,
5600 : "SELECT\n"
5601 : " n.nspname as \"%s\",\n"
5602 : " p.prsname as \"%s\",\n"
5603 : " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
5604 : "FROM pg_catalog.pg_ts_parser p\n"
5605 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
5606 : gettext_noop("Schema"),
5607 : gettext_noop("Name"),
5608 : gettext_noop("Description")
5609 : );
5610 :
5611 32 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5612 : "n.nspname", "p.prsname", NULL,
5613 : "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5614 : NULL, 3))
5615 : {
5616 16 : termPQExpBuffer(&buf);
5617 16 : return false;
5618 : }
5619 :
5620 16 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5621 :
5622 16 : res = PSQLexec(buf.data);
5623 16 : termPQExpBuffer(&buf);
5624 16 : if (!res)
5625 0 : return false;
5626 :
5627 16 : myopt.title = _("List of text search parsers");
5628 16 : myopt.translate_header = true;
5629 :
5630 16 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5631 :
5632 16 : PQclear(res);
5633 16 : return true;
5634 : }
5635 :
5636 : /*
5637 : * full description of parsers
5638 : */
5639 : static bool
5640 0 : listTSParsersVerbose(const char *pattern)
5641 : {
5642 : PQExpBufferData buf;
5643 : PGresult *res;
5644 : int i;
5645 :
5646 0 : initPQExpBuffer(&buf);
5647 :
5648 0 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search parsers"));
5649 0 : appendPQExpBuffer(&buf,
5650 : "SELECT p.oid,\n"
5651 : " n.nspname,\n"
5652 : " p.prsname\n"
5653 : "FROM pg_catalog.pg_ts_parser p\n"
5654 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
5655 : );
5656 :
5657 0 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5658 : "n.nspname", "p.prsname", NULL,
5659 : "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5660 : NULL, 3))
5661 : {
5662 0 : termPQExpBuffer(&buf);
5663 0 : return false;
5664 : }
5665 :
5666 0 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5667 :
5668 0 : res = PSQLexec(buf.data);
5669 0 : termPQExpBuffer(&buf);
5670 0 : if (!res)
5671 0 : return false;
5672 :
5673 0 : if (PQntuples(res) == 0)
5674 : {
5675 0 : if (!pset.quiet)
5676 : {
5677 0 : if (pattern)
5678 0 : pg_log_error("Did not find any text search parser named \"%s\".",
5679 : pattern);
5680 : else
5681 0 : pg_log_error("Did not find any text search parsers.");
5682 : }
5683 0 : PQclear(res);
5684 0 : return false;
5685 : }
5686 :
5687 0 : for (i = 0; i < PQntuples(res); i++)
5688 : {
5689 : const char *oid;
5690 0 : const char *nspname = NULL;
5691 : const char *prsname;
5692 :
5693 0 : oid = PQgetvalue(res, i, 0);
5694 0 : if (!PQgetisnull(res, i, 1))
5695 0 : nspname = PQgetvalue(res, i, 1);
5696 0 : prsname = PQgetvalue(res, i, 2);
5697 :
5698 0 : if (!describeOneTSParser(oid, nspname, prsname))
5699 : {
5700 0 : PQclear(res);
5701 0 : return false;
5702 : }
5703 :
5704 0 : if (cancel_pressed)
5705 : {
5706 0 : PQclear(res);
5707 0 : return false;
5708 : }
5709 : }
5710 :
5711 0 : PQclear(res);
5712 0 : return true;
5713 : }
5714 :
5715 : static bool
5716 0 : describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
5717 : {
5718 : PQExpBufferData buf;
5719 : PGresult *res;
5720 : PQExpBufferData title;
5721 0 : printQueryOpt myopt = pset.popt;
5722 : static const bool translate_columns[] = {true, false, false};
5723 :
5724 0 : initPQExpBuffer(&buf);
5725 :
5726 0 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get text search parser details"));
5727 0 : appendPQExpBuffer(&buf,
5728 : "SELECT '%s' AS \"%s\",\n"
5729 : " p.prsstart::pg_catalog.regproc AS \"%s\",\n"
5730 : " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\"\n"
5731 : " FROM pg_catalog.pg_ts_parser p\n"
5732 : " WHERE p.oid = '%s'\n"
5733 : "UNION ALL\n"
5734 : "SELECT '%s',\n"
5735 : " p.prstoken::pg_catalog.regproc,\n"
5736 : " pg_catalog.obj_description(p.prstoken, 'pg_proc')\n"
5737 : " FROM pg_catalog.pg_ts_parser p\n"
5738 : " WHERE p.oid = '%s'\n"
5739 : "UNION ALL\n"
5740 : "SELECT '%s',\n"
5741 : " p.prsend::pg_catalog.regproc,\n"
5742 : " pg_catalog.obj_description(p.prsend, 'pg_proc')\n"
5743 : " FROM pg_catalog.pg_ts_parser p\n"
5744 : " WHERE p.oid = '%s'\n"
5745 : "UNION ALL\n"
5746 : "SELECT '%s',\n"
5747 : " p.prsheadline::pg_catalog.regproc,\n"
5748 : " pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n"
5749 : " FROM pg_catalog.pg_ts_parser p\n"
5750 : " WHERE p.oid = '%s'\n"
5751 : "UNION ALL\n"
5752 : "SELECT '%s',\n"
5753 : " p.prslextype::pg_catalog.regproc,\n"
5754 : " pg_catalog.obj_description(p.prslextype, 'pg_proc')\n"
5755 : " FROM pg_catalog.pg_ts_parser p\n"
5756 : " WHERE p.oid = '%s';",
5757 : gettext_noop("Start parse"),
5758 : gettext_noop("Method"),
5759 : gettext_noop("Function"),
5760 : gettext_noop("Description"),
5761 : oid,
5762 : gettext_noop("Get next token"),
5763 : oid,
5764 : gettext_noop("End parse"),
5765 : oid,
5766 : gettext_noop("Get headline"),
5767 : oid,
5768 : gettext_noop("Get token types"),
5769 : oid);
5770 :
5771 0 : res = PSQLexec(buf.data);
5772 0 : termPQExpBuffer(&buf);
5773 0 : if (!res)
5774 0 : return false;
5775 :
5776 0 : initPQExpBuffer(&title);
5777 0 : if (nspname)
5778 0 : printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""),
5779 : nspname, prsname);
5780 : else
5781 0 : printfPQExpBuffer(&title, _("Text search parser \"%s\""), prsname);
5782 0 : myopt.title = title.data;
5783 0 : myopt.footers = NULL;
5784 0 : myopt.topt.default_footer = false;
5785 0 : myopt.translate_header = true;
5786 0 : myopt.translate_columns = translate_columns;
5787 0 : myopt.n_translate_columns = lengthof(translate_columns);
5788 :
5789 0 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5790 :
5791 0 : PQclear(res);
5792 :
5793 0 : initPQExpBuffer(&buf);
5794 :
5795 0 : printfPQExpBuffer(&buf, "/* %s */\n",
5796 : _("Get text search parser token types"));
5797 0 : appendPQExpBuffer(&buf,
5798 : "SELECT t.alias as \"%s\",\n"
5799 : " t.description as \"%s\"\n"
5800 : "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t\n"
5801 : "ORDER BY 1;",
5802 : gettext_noop("Token name"),
5803 : gettext_noop("Description"),
5804 : oid);
5805 :
5806 0 : res = PSQLexec(buf.data);
5807 0 : termPQExpBuffer(&buf);
5808 0 : if (!res)
5809 : {
5810 0 : termPQExpBuffer(&title);
5811 0 : return false;
5812 : }
5813 :
5814 0 : if (nspname)
5815 0 : printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""),
5816 : nspname, prsname);
5817 : else
5818 0 : printfPQExpBuffer(&title, _("Token types for parser \"%s\""), prsname);
5819 0 : myopt.title = title.data;
5820 0 : myopt.footers = NULL;
5821 0 : myopt.topt.default_footer = true;
5822 0 : myopt.translate_header = true;
5823 0 : myopt.translate_columns = NULL;
5824 0 : myopt.n_translate_columns = 0;
5825 :
5826 0 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5827 :
5828 0 : termPQExpBuffer(&title);
5829 0 : PQclear(res);
5830 0 : return true;
5831 : }
5832 :
5833 :
5834 : /*
5835 : * \dFd
5836 : * list text search dictionaries
5837 : */
5838 : bool
5839 32 : listTSDictionaries(const char *pattern, bool verbose)
5840 : {
5841 : PQExpBufferData buf;
5842 : PGresult *res;
5843 32 : printQueryOpt myopt = pset.popt;
5844 :
5845 32 : initPQExpBuffer(&buf);
5846 :
5847 32 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search dictionaries"));
5848 32 : appendPQExpBuffer(&buf,
5849 : "SELECT\n"
5850 : " n.nspname as \"%s\",\n"
5851 : " d.dictname as \"%s\",\n",
5852 : gettext_noop("Schema"),
5853 : gettext_noop("Name"));
5854 :
5855 32 : if (verbose)
5856 : {
5857 0 : appendPQExpBuffer(&buf,
5858 : " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM\n"
5859 : " pg_catalog.pg_ts_template t\n"
5860 : " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace\n"
5861 : " WHERE d.dicttemplate = t.oid ) AS \"%s\",\n"
5862 : " d.dictinitoption as \"%s\",\n",
5863 : gettext_noop("Template"),
5864 : gettext_noop("Init options"));
5865 : }
5866 :
5867 32 : appendPQExpBuffer(&buf,
5868 : " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
5869 : gettext_noop("Description"));
5870 :
5871 32 : appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n"
5872 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
5873 :
5874 32 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5875 : "n.nspname", "d.dictname", NULL,
5876 : "pg_catalog.pg_ts_dict_is_visible(d.oid)",
5877 : NULL, 3))
5878 : {
5879 16 : termPQExpBuffer(&buf);
5880 16 : return false;
5881 : }
5882 :
5883 16 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5884 :
5885 16 : res = PSQLexec(buf.data);
5886 16 : termPQExpBuffer(&buf);
5887 16 : if (!res)
5888 0 : return false;
5889 :
5890 16 : myopt.title = _("List of text search dictionaries");
5891 16 : myopt.translate_header = true;
5892 :
5893 16 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5894 :
5895 16 : PQclear(res);
5896 16 : return true;
5897 : }
5898 :
5899 :
5900 : /*
5901 : * \dFt
5902 : * list text search templates
5903 : */
5904 : bool
5905 32 : listTSTemplates(const char *pattern, bool verbose)
5906 : {
5907 : PQExpBufferData buf;
5908 : PGresult *res;
5909 32 : printQueryOpt myopt = pset.popt;
5910 :
5911 32 : initPQExpBuffer(&buf);
5912 :
5913 32 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search templates"));
5914 32 : if (verbose)
5915 0 : appendPQExpBuffer(&buf,
5916 : "SELECT\n"
5917 : " n.nspname AS \"%s\",\n"
5918 : " t.tmplname AS \"%s\",\n"
5919 : " t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
5920 : " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
5921 : " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5922 : gettext_noop("Schema"),
5923 : gettext_noop("Name"),
5924 : gettext_noop("Init"),
5925 : gettext_noop("Lexize"),
5926 : gettext_noop("Description"));
5927 : else
5928 32 : appendPQExpBuffer(&buf,
5929 : "SELECT\n"
5930 : " n.nspname AS \"%s\",\n"
5931 : " t.tmplname AS \"%s\",\n"
5932 : " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5933 : gettext_noop("Schema"),
5934 : gettext_noop("Name"),
5935 : gettext_noop("Description"));
5936 :
5937 32 : appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n"
5938 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
5939 :
5940 32 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5941 : "n.nspname", "t.tmplname", NULL,
5942 : "pg_catalog.pg_ts_template_is_visible(t.oid)",
5943 : NULL, 3))
5944 : {
5945 16 : termPQExpBuffer(&buf);
5946 16 : return false;
5947 : }
5948 :
5949 16 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5950 :
5951 16 : res = PSQLexec(buf.data);
5952 16 : termPQExpBuffer(&buf);
5953 16 : if (!res)
5954 0 : return false;
5955 :
5956 16 : myopt.title = _("List of text search templates");
5957 16 : myopt.translate_header = true;
5958 :
5959 16 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5960 :
5961 16 : PQclear(res);
5962 16 : return true;
5963 : }
5964 :
5965 :
5966 : /*
5967 : * \dF
5968 : * list text search configurations
5969 : */
5970 : bool
5971 32 : listTSConfigs(const char *pattern, bool verbose)
5972 : {
5973 : PQExpBufferData buf;
5974 : PGresult *res;
5975 32 : printQueryOpt myopt = pset.popt;
5976 :
5977 32 : if (verbose)
5978 0 : return listTSConfigsVerbose(pattern);
5979 :
5980 32 : initPQExpBuffer(&buf);
5981 :
5982 32 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search configurations"));
5983 32 : appendPQExpBuffer(&buf,
5984 : "SELECT\n"
5985 : " n.nspname as \"%s\",\n"
5986 : " c.cfgname as \"%s\",\n"
5987 : " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
5988 : "FROM pg_catalog.pg_ts_config c\n"
5989 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace\n",
5990 : gettext_noop("Schema"),
5991 : gettext_noop("Name"),
5992 : gettext_noop("Description")
5993 : );
5994 :
5995 32 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5996 : "n.nspname", "c.cfgname", NULL,
5997 : "pg_catalog.pg_ts_config_is_visible(c.oid)",
5998 : NULL, 3))
5999 : {
6000 16 : termPQExpBuffer(&buf);
6001 16 : return false;
6002 : }
6003 :
6004 16 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6005 :
6006 16 : res = PSQLexec(buf.data);
6007 16 : termPQExpBuffer(&buf);
6008 16 : if (!res)
6009 0 : return false;
6010 :
6011 16 : myopt.title = _("List of text search configurations");
6012 16 : myopt.translate_header = true;
6013 :
6014 16 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6015 :
6016 16 : PQclear(res);
6017 16 : return true;
6018 : }
6019 :
6020 : static bool
6021 0 : listTSConfigsVerbose(const char *pattern)
6022 : {
6023 : PQExpBufferData buf;
6024 : PGresult *res;
6025 : int i;
6026 :
6027 0 : initPQExpBuffer(&buf);
6028 :
6029 0 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search configurations"));
6030 0 : appendPQExpBuffer(&buf,
6031 : "SELECT c.oid, c.cfgname,\n"
6032 : " n.nspname,\n"
6033 : " p.prsname,\n"
6034 : " np.nspname as pnspname\n"
6035 : "FROM pg_catalog.pg_ts_config c\n"
6036 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace,\n"
6037 : " pg_catalog.pg_ts_parser p\n"
6038 : " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace\n"
6039 : "WHERE p.oid = c.cfgparser\n"
6040 : );
6041 :
6042 0 : if (!validateSQLNamePattern(&buf, pattern, true, false,
6043 : "n.nspname", "c.cfgname", NULL,
6044 : "pg_catalog.pg_ts_config_is_visible(c.oid)",
6045 : NULL, 3))
6046 : {
6047 0 : termPQExpBuffer(&buf);
6048 0 : return false;
6049 : }
6050 :
6051 0 : appendPQExpBufferStr(&buf, "ORDER BY 3, 2;");
6052 :
6053 0 : res = PSQLexec(buf.data);
6054 0 : termPQExpBuffer(&buf);
6055 0 : if (!res)
6056 0 : return false;
6057 :
6058 0 : if (PQntuples(res) == 0)
6059 : {
6060 0 : if (!pset.quiet)
6061 : {
6062 0 : if (pattern)
6063 0 : pg_log_error("Did not find any text search configuration named \"%s\".",
6064 : pattern);
6065 : else
6066 0 : pg_log_error("Did not find any text search configurations.");
6067 : }
6068 0 : PQclear(res);
6069 0 : return false;
6070 : }
6071 :
6072 0 : for (i = 0; i < PQntuples(res); i++)
6073 : {
6074 : const char *oid;
6075 : const char *cfgname;
6076 0 : const char *nspname = NULL;
6077 : const char *prsname;
6078 0 : const char *pnspname = NULL;
6079 :
6080 0 : oid = PQgetvalue(res, i, 0);
6081 0 : cfgname = PQgetvalue(res, i, 1);
6082 0 : if (!PQgetisnull(res, i, 2))
6083 0 : nspname = PQgetvalue(res, i, 2);
6084 0 : prsname = PQgetvalue(res, i, 3);
6085 0 : if (!PQgetisnull(res, i, 4))
6086 0 : pnspname = PQgetvalue(res, i, 4);
6087 :
6088 0 : if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
6089 : {
6090 0 : PQclear(res);
6091 0 : return false;
6092 : }
6093 :
6094 0 : if (cancel_pressed)
6095 : {
6096 0 : PQclear(res);
6097 0 : return false;
6098 : }
6099 : }
6100 :
6101 0 : PQclear(res);
6102 0 : return true;
6103 : }
6104 :
6105 : static bool
6106 0 : describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
6107 : const char *pnspname, const char *prsname)
6108 : {
6109 : PQExpBufferData buf,
6110 : title;
6111 : PGresult *res;
6112 0 : printQueryOpt myopt = pset.popt;
6113 :
6114 0 : initPQExpBuffer(&buf);
6115 :
6116 0 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get text search configuration details"));
6117 0 : appendPQExpBuffer(&buf,
6118 : "SELECT\n"
6119 : " ( SELECT t.alias FROM\n"
6120 : " pg_catalog.ts_token_type(c.cfgparser) AS t\n"
6121 : " WHERE t.tokid = m.maptokentype ) AS \"%s\",\n"
6122 : " pg_catalog.btrim(\n"
6123 : " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary\n"
6124 : " FROM pg_catalog.pg_ts_config_map AS mm\n"
6125 : " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype\n"
6126 : " ORDER BY mapcfg, maptokentype, mapseqno\n"
6127 : " ) :: pg_catalog.text,\n"
6128 : " '{}') AS \"%s\"\n"
6129 : "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m\n"
6130 : "WHERE c.oid = '%s' AND m.mapcfg = c.oid\n"
6131 : "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser\n"
6132 : "ORDER BY 1;",
6133 : gettext_noop("Token"),
6134 : gettext_noop("Dictionaries"),
6135 : oid);
6136 :
6137 0 : res = PSQLexec(buf.data);
6138 0 : termPQExpBuffer(&buf);
6139 0 : if (!res)
6140 0 : return false;
6141 :
6142 0 : initPQExpBuffer(&title);
6143 :
6144 0 : if (nspname)
6145 0 : appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
6146 : nspname, cfgname);
6147 : else
6148 0 : appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
6149 : cfgname);
6150 :
6151 0 : if (pnspname)
6152 0 : appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
6153 : pnspname, prsname);
6154 : else
6155 0 : appendPQExpBuffer(&title, _("\nParser: \"%s\""),
6156 : prsname);
6157 :
6158 0 : myopt.title = title.data;
6159 0 : myopt.footers = NULL;
6160 0 : myopt.topt.default_footer = false;
6161 0 : myopt.translate_header = true;
6162 :
6163 0 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6164 :
6165 0 : termPQExpBuffer(&title);
6166 :
6167 0 : PQclear(res);
6168 0 : return true;
6169 : }
6170 :
6171 :
6172 : /*
6173 : * \dew
6174 : *
6175 : * Describes foreign-data wrappers
6176 : */
6177 : bool
6178 76 : listForeignDataWrappers(const char *pattern, bool verbose)
6179 : {
6180 : PQExpBufferData buf;
6181 : PGresult *res;
6182 76 : printQueryOpt myopt = pset.popt;
6183 :
6184 76 : initPQExpBuffer(&buf);
6185 :
6186 76 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign-data wrappers"));
6187 76 : appendPQExpBuffer(&buf,
6188 : "SELECT fdw.fdwname AS \"%s\",\n"
6189 : " pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n"
6190 : " fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n"
6191 : " fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
6192 : gettext_noop("Name"),
6193 : gettext_noop("Owner"),
6194 : gettext_noop("Handler"),
6195 : gettext_noop("Validator"));
6196 :
6197 76 : if (verbose)
6198 : {
6199 56 : appendPQExpBufferStr(&buf, ",\n ");
6200 56 : printACLColumn(&buf, "fdwacl");
6201 56 : appendPQExpBuffer(&buf,
6202 : ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE "
6203 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6204 : " pg_catalog.quote_ident(option_name) || ' ' || "
6205 : " pg_catalog.quote_literal(option_value) FROM "
6206 : " pg_catalog.pg_options_to_table(fdwoptions)), ', ') || ')' "
6207 : " END AS \"%s\""
6208 : ",\n d.description AS \"%s\" ",
6209 : gettext_noop("FDW options"),
6210 : gettext_noop("Description"));
6211 : }
6212 :
6213 76 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
6214 :
6215 76 : if (verbose)
6216 56 : appendPQExpBufferStr(&buf,
6217 : "LEFT JOIN pg_catalog.pg_description d\n"
6218 : " ON d.classoid = fdw.tableoid "
6219 : "AND d.objoid = fdw.oid AND d.objsubid = 0\n");
6220 :
6221 76 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6222 : NULL, "fdwname", NULL, NULL,
6223 : NULL, 1))
6224 : {
6225 12 : termPQExpBuffer(&buf);
6226 12 : return false;
6227 : }
6228 :
6229 64 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6230 :
6231 64 : res = PSQLexec(buf.data);
6232 64 : termPQExpBuffer(&buf);
6233 64 : if (!res)
6234 0 : return false;
6235 :
6236 64 : myopt.title = _("List of foreign-data wrappers");
6237 64 : myopt.translate_header = true;
6238 :
6239 64 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6240 :
6241 64 : PQclear(res);
6242 64 : return true;
6243 : }
6244 :
6245 : /*
6246 : * \des
6247 : *
6248 : * Describes foreign servers.
6249 : */
6250 : bool
6251 80 : listForeignServers(const char *pattern, bool verbose)
6252 : {
6253 : PQExpBufferData buf;
6254 : PGresult *res;
6255 80 : printQueryOpt myopt = pset.popt;
6256 :
6257 80 : initPQExpBuffer(&buf);
6258 :
6259 80 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign servers"));
6260 80 : appendPQExpBuffer(&buf,
6261 : "SELECT s.srvname AS \"%s\",\n"
6262 : " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
6263 : " f.fdwname AS \"%s\"",
6264 : gettext_noop("Name"),
6265 : gettext_noop("Owner"),
6266 : gettext_noop("Foreign-data wrapper"));
6267 :
6268 80 : if (verbose)
6269 : {
6270 32 : appendPQExpBufferStr(&buf, ",\n ");
6271 32 : printACLColumn(&buf, "s.srvacl");
6272 32 : appendPQExpBuffer(&buf,
6273 : ",\n"
6274 : " s.srvtype AS \"%s\",\n"
6275 : " s.srvversion AS \"%s\",\n"
6276 : " CASE WHEN srvoptions IS NULL THEN '' ELSE "
6277 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6278 : " pg_catalog.quote_ident(option_name) || ' ' || "
6279 : " pg_catalog.quote_literal(option_value) FROM "
6280 : " pg_catalog.pg_options_to_table(srvoptions)), ', ') || ')' "
6281 : " END AS \"%s\",\n"
6282 : " d.description AS \"%s\"",
6283 : gettext_noop("Type"),
6284 : gettext_noop("Version"),
6285 : gettext_noop("FDW options"),
6286 : gettext_noop("Description"));
6287 : }
6288 :
6289 80 : appendPQExpBufferStr(&buf,
6290 : "\nFROM pg_catalog.pg_foreign_server s\n"
6291 : " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
6292 :
6293 80 : if (verbose)
6294 32 : appendPQExpBufferStr(&buf,
6295 : "LEFT JOIN pg_catalog.pg_description d\n "
6296 : "ON d.classoid = s.tableoid AND d.objoid = s.oid "
6297 : "AND d.objsubid = 0\n");
6298 :
6299 80 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6300 : NULL, "s.srvname", NULL, NULL,
6301 : NULL, 1))
6302 : {
6303 28 : termPQExpBuffer(&buf);
6304 28 : return false;
6305 : }
6306 :
6307 52 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6308 :
6309 52 : res = PSQLexec(buf.data);
6310 52 : termPQExpBuffer(&buf);
6311 52 : if (!res)
6312 0 : return false;
6313 :
6314 52 : myopt.title = _("List of foreign servers");
6315 52 : myopt.translate_header = true;
6316 :
6317 52 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6318 :
6319 52 : PQclear(res);
6320 52 : return true;
6321 : }
6322 :
6323 : /*
6324 : * \deu
6325 : *
6326 : * Describes user mappings.
6327 : */
6328 : bool
6329 40 : listUserMappings(const char *pattern, bool verbose)
6330 : {
6331 : PQExpBufferData buf;
6332 : PGresult *res;
6333 40 : printQueryOpt myopt = pset.popt;
6334 :
6335 40 : initPQExpBuffer(&buf);
6336 :
6337 40 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching user mappings"));
6338 40 : appendPQExpBuffer(&buf,
6339 : "SELECT um.srvname AS \"%s\",\n"
6340 : " um.usename AS \"%s\"",
6341 : gettext_noop("Server"),
6342 : gettext_noop("User name"));
6343 :
6344 40 : if (verbose)
6345 24 : appendPQExpBuffer(&buf,
6346 : ",\n CASE WHEN umoptions IS NULL THEN '' ELSE "
6347 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6348 : " pg_catalog.quote_ident(option_name) || ' ' || "
6349 : " pg_catalog.quote_literal(option_value) FROM "
6350 : " pg_catalog.pg_options_to_table(umoptions)), ', ') || ')' "
6351 : " END AS \"%s\"",
6352 : gettext_noop("FDW options"));
6353 :
6354 40 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
6355 :
6356 40 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6357 : NULL, "um.srvname", "um.usename", NULL,
6358 : NULL, 1))
6359 : {
6360 0 : termPQExpBuffer(&buf);
6361 0 : return false;
6362 : }
6363 :
6364 40 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6365 :
6366 40 : res = PSQLexec(buf.data);
6367 40 : termPQExpBuffer(&buf);
6368 40 : if (!res)
6369 0 : return false;
6370 :
6371 40 : myopt.title = _("List of user mappings");
6372 40 : myopt.translate_header = true;
6373 :
6374 40 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6375 :
6376 40 : PQclear(res);
6377 40 : return true;
6378 : }
6379 :
6380 : /*
6381 : * \det
6382 : *
6383 : * Describes foreign tables.
6384 : */
6385 : bool
6386 10 : listForeignTables(const char *pattern, bool verbose)
6387 : {
6388 : PQExpBufferData buf;
6389 : PGresult *res;
6390 10 : printQueryOpt myopt = pset.popt;
6391 :
6392 10 : initPQExpBuffer(&buf);
6393 :
6394 10 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign tables"));
6395 10 : appendPQExpBuffer(&buf,
6396 : "SELECT n.nspname AS \"%s\",\n"
6397 : " c.relname AS \"%s\",\n"
6398 : " s.srvname AS \"%s\"",
6399 : gettext_noop("Schema"),
6400 : gettext_noop("Table"),
6401 : gettext_noop("Server"));
6402 :
6403 10 : if (verbose)
6404 10 : appendPQExpBuffer(&buf,
6405 : ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE "
6406 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6407 : " pg_catalog.quote_ident(option_name) || ' ' || "
6408 : " pg_catalog.quote_literal(option_value) FROM "
6409 : " pg_catalog.pg_options_to_table(ftoptions)), ', ') || ')' "
6410 : " END AS \"%s\",\n"
6411 : " d.description AS \"%s\"",
6412 : gettext_noop("FDW options"),
6413 : gettext_noop("Description"));
6414 :
6415 10 : appendPQExpBufferStr(&buf,
6416 : "\nFROM pg_catalog.pg_foreign_table ft\n"
6417 : " INNER JOIN pg_catalog.pg_class c"
6418 : " ON c.oid = ft.ftrelid\n"
6419 : " INNER JOIN pg_catalog.pg_namespace n"
6420 : " ON n.oid = c.relnamespace\n"
6421 : " INNER JOIN pg_catalog.pg_foreign_server s"
6422 : " ON s.oid = ft.ftserver\n");
6423 10 : if (verbose)
6424 10 : appendPQExpBufferStr(&buf,
6425 : " LEFT JOIN pg_catalog.pg_description d\n"
6426 : " ON d.classoid = c.tableoid AND "
6427 : "d.objoid = c.oid AND d.objsubid = 0\n");
6428 :
6429 10 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6430 : "n.nspname", "c.relname", NULL,
6431 : "pg_catalog.pg_table_is_visible(c.oid)",
6432 : NULL, 3))
6433 : {
6434 0 : termPQExpBuffer(&buf);
6435 0 : return false;
6436 : }
6437 :
6438 10 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6439 :
6440 10 : res = PSQLexec(buf.data);
6441 10 : termPQExpBuffer(&buf);
6442 10 : if (!res)
6443 0 : return false;
6444 :
6445 10 : myopt.title = _("List of foreign tables");
6446 10 : myopt.translate_header = true;
6447 :
6448 10 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6449 :
6450 10 : PQclear(res);
6451 10 : return true;
6452 : }
6453 :
6454 : /*
6455 : * \dx
6456 : *
6457 : * Briefly describes installed extensions.
6458 : */
6459 : bool
6460 16 : listExtensions(const char *pattern)
6461 : {
6462 : PQExpBufferData buf;
6463 : PGresult *res;
6464 16 : printQueryOpt myopt = pset.popt;
6465 :
6466 16 : initPQExpBuffer(&buf);
6467 :
6468 16 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching installed extensions"));
6469 16 : appendPQExpBuffer(&buf,
6470 : "SELECT e.extname AS \"%s\", "
6471 : "e.extversion AS \"%s\", ae.default_version AS \"%s\","
6472 : "n.nspname AS \"%s\", d.description AS \"%s\"\n"
6473 : "FROM pg_catalog.pg_extension e "
6474 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
6475 : "LEFT JOIN pg_catalog.pg_description d ON d.objoid = e.oid "
6476 : "AND d.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
6477 : "LEFT JOIN pg_catalog.pg_available_extensions() ae(name, default_version, comment) ON ae.name = e.extname\n",
6478 : gettext_noop("Name"),
6479 : gettext_noop("Version"),
6480 : gettext_noop("Default version"),
6481 : gettext_noop("Schema"),
6482 : gettext_noop("Description"));
6483 :
6484 16 : if (!validateSQLNamePattern(&buf, pattern,
6485 : false, false,
6486 : NULL, "e.extname", NULL,
6487 : NULL,
6488 : NULL, 1))
6489 : {
6490 12 : termPQExpBuffer(&buf);
6491 12 : return false;
6492 : }
6493 :
6494 4 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6495 :
6496 4 : res = PSQLexec(buf.data);
6497 4 : termPQExpBuffer(&buf);
6498 4 : if (!res)
6499 0 : return false;
6500 :
6501 4 : myopt.title = _("List of installed extensions");
6502 4 : myopt.translate_header = true;
6503 :
6504 4 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6505 :
6506 4 : PQclear(res);
6507 4 : return true;
6508 : }
6509 :
6510 : /*
6511 : * \dx+
6512 : *
6513 : * List contents of installed extensions.
6514 : */
6515 : bool
6516 16 : listExtensionContents(const char *pattern)
6517 : {
6518 : PQExpBufferData buf;
6519 : PGresult *res;
6520 : int i;
6521 :
6522 16 : initPQExpBuffer(&buf);
6523 :
6524 16 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching installed extensions"));
6525 16 : appendPQExpBuffer(&buf,
6526 : "SELECT e.extname, e.oid\n"
6527 : "FROM pg_catalog.pg_extension e\n");
6528 :
6529 16 : if (!validateSQLNamePattern(&buf, pattern,
6530 : false, false,
6531 : NULL, "e.extname", NULL,
6532 : NULL,
6533 : NULL, 1))
6534 : {
6535 0 : termPQExpBuffer(&buf);
6536 0 : return false;
6537 : }
6538 :
6539 16 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6540 :
6541 16 : res = PSQLexec(buf.data);
6542 16 : termPQExpBuffer(&buf);
6543 16 : if (!res)
6544 0 : return false;
6545 :
6546 16 : if (PQntuples(res) == 0)
6547 : {
6548 0 : if (!pset.quiet)
6549 : {
6550 0 : if (pattern)
6551 0 : pg_log_error("Did not find any extension named \"%s\".",
6552 : pattern);
6553 : else
6554 0 : pg_log_error("Did not find any extensions.");
6555 : }
6556 0 : PQclear(res);
6557 0 : return false;
6558 : }
6559 :
6560 32 : for (i = 0; i < PQntuples(res); i++)
6561 : {
6562 : const char *extname;
6563 : const char *oid;
6564 :
6565 16 : extname = PQgetvalue(res, i, 0);
6566 16 : oid = PQgetvalue(res, i, 1);
6567 :
6568 16 : if (!listOneExtensionContents(extname, oid))
6569 : {
6570 0 : PQclear(res);
6571 0 : return false;
6572 : }
6573 16 : if (cancel_pressed)
6574 : {
6575 0 : PQclear(res);
6576 0 : return false;
6577 : }
6578 : }
6579 :
6580 16 : PQclear(res);
6581 16 : return true;
6582 : }
6583 :
6584 : static bool
6585 16 : listOneExtensionContents(const char *extname, const char *oid)
6586 : {
6587 : PQExpBufferData buf;
6588 : PGresult *res;
6589 : PQExpBufferData title;
6590 16 : printQueryOpt myopt = pset.popt;
6591 :
6592 16 : initPQExpBuffer(&buf);
6593 :
6594 16 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get installed extension's contents"));
6595 16 : appendPQExpBuffer(&buf,
6596 : "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
6597 : "FROM pg_catalog.pg_depend\n"
6598 : "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
6599 : "ORDER BY 1;",
6600 : gettext_noop("Object description"),
6601 : oid);
6602 :
6603 16 : res = PSQLexec(buf.data);
6604 16 : termPQExpBuffer(&buf);
6605 16 : if (!res)
6606 0 : return false;
6607 :
6608 16 : initPQExpBuffer(&title);
6609 16 : printfPQExpBuffer(&title, _("Objects in extension \"%s\""), extname);
6610 16 : myopt.title = title.data;
6611 16 : myopt.translate_header = true;
6612 :
6613 16 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6614 :
6615 16 : termPQExpBuffer(&title);
6616 16 : PQclear(res);
6617 16 : return true;
6618 : }
6619 :
6620 : /*
6621 : * validateSQLNamePattern
6622 : *
6623 : * Wrapper around string_utils's processSQLNamePattern which also checks the
6624 : * pattern's validity. In addition to that function's parameters, takes a
6625 : * 'maxparts' parameter specifying the maximum number of dotted names the
6626 : * pattern is allowed to have, and a 'added_clause' parameter that returns by
6627 : * reference whether a clause was added to 'buf'. Returns whether the pattern
6628 : * passed validation, after logging any errors.
6629 : */
6630 : static bool
6631 4914 : validateSQLNamePattern(PQExpBuffer buf, const char *pattern, bool have_where,
6632 : bool force_escape, const char *schemavar,
6633 : const char *namevar, const char *altnamevar,
6634 : const char *visibilityrule, bool *added_clause,
6635 : int maxparts)
6636 : {
6637 : PQExpBufferData dbbuf;
6638 : int dotcnt;
6639 : bool added;
6640 :
6641 4914 : initPQExpBuffer(&dbbuf);
6642 4914 : added = processSQLNamePattern(pset.db, buf, pattern, have_where, force_escape,
6643 : schemavar, namevar, altnamevar,
6644 : visibilityrule, &dbbuf, &dotcnt);
6645 4914 : if (added_clause != NULL)
6646 113 : *added_clause = added;
6647 :
6648 4914 : if (dotcnt >= maxparts)
6649 : {
6650 292 : pg_log_error("improper qualified name (too many dotted names): %s",
6651 : pattern);
6652 292 : goto error_return;
6653 : }
6654 :
6655 4622 : if (maxparts > 1 && dotcnt == maxparts - 1)
6656 : {
6657 412 : if (PQdb(pset.db) == NULL)
6658 : {
6659 0 : pg_log_error("You are currently not connected to a database.");
6660 0 : goto error_return;
6661 : }
6662 412 : if (strcmp(PQdb(pset.db), dbbuf.data) != 0)
6663 : {
6664 300 : pg_log_error("cross-database references are not implemented: %s",
6665 : pattern);
6666 300 : goto error_return;
6667 : }
6668 : }
6669 4322 : termPQExpBuffer(&dbbuf);
6670 4322 : return true;
6671 :
6672 592 : error_return:
6673 592 : termPQExpBuffer(&dbbuf);
6674 592 : return false;
6675 : }
6676 :
6677 : /*
6678 : * \dRp
6679 : * Lists publications.
6680 : *
6681 : * Takes an optional regexp to select particular publications
6682 : */
6683 : bool
6684 32 : listPublications(const char *pattern)
6685 : {
6686 : PQExpBufferData buf;
6687 : PGresult *res;
6688 32 : printQueryOpt myopt = pset.popt;
6689 : static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
6690 :
6691 32 : if (pset.sversion < 100000)
6692 : {
6693 : char sverbuf[32];
6694 :
6695 0 : pg_log_error("The server (version %s) does not support publications.",
6696 : formatPGVersionNumber(pset.sversion, false,
6697 : sverbuf, sizeof(sverbuf)));
6698 0 : return true;
6699 : }
6700 :
6701 32 : initPQExpBuffer(&buf);
6702 :
6703 32 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching publications"));
6704 32 : appendPQExpBuffer(&buf,
6705 : "SELECT pubname AS \"%s\",\n"
6706 : " pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
6707 : " puballtables AS \"%s\"",
6708 : gettext_noop("Name"),
6709 : gettext_noop("Owner"),
6710 : gettext_noop("All tables"));
6711 :
6712 32 : if (pset.sversion >= 190000)
6713 32 : appendPQExpBuffer(&buf,
6714 : ",\n puballsequences AS \"%s\"",
6715 : gettext_noop("All sequences"));
6716 :
6717 32 : appendPQExpBuffer(&buf,
6718 : ",\n pubinsert AS \"%s\",\n"
6719 : " pubupdate AS \"%s\",\n"
6720 : " pubdelete AS \"%s\"",
6721 : gettext_noop("Inserts"),
6722 : gettext_noop("Updates"),
6723 : gettext_noop("Deletes"));
6724 32 : if (pset.sversion >= 110000)
6725 32 : appendPQExpBuffer(&buf,
6726 : ",\n pubtruncate AS \"%s\"",
6727 : gettext_noop("Truncates"));
6728 32 : if (pset.sversion >= 180000)
6729 32 : appendPQExpBuffer(&buf,
6730 : ",\n (CASE pubgencols\n"
6731 : " WHEN '%c' THEN 'none'\n"
6732 : " WHEN '%c' THEN 'stored'\n"
6733 : " END) AS \"%s\"",
6734 : PUBLISH_GENCOLS_NONE,
6735 : PUBLISH_GENCOLS_STORED,
6736 : gettext_noop("Generated columns"));
6737 32 : if (pset.sversion >= 130000)
6738 32 : appendPQExpBuffer(&buf,
6739 : ",\n pubviaroot AS \"%s\"",
6740 : gettext_noop("Via root"));
6741 :
6742 32 : appendPQExpBufferStr(&buf,
6743 : "\nFROM pg_catalog.pg_publication\n");
6744 :
6745 32 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6746 : NULL, "pubname", NULL,
6747 : NULL,
6748 : NULL, 1))
6749 : {
6750 12 : termPQExpBuffer(&buf);
6751 12 : return false;
6752 : }
6753 :
6754 20 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6755 :
6756 20 : res = PSQLexec(buf.data);
6757 20 : termPQExpBuffer(&buf);
6758 20 : if (!res)
6759 0 : return false;
6760 :
6761 20 : myopt.title = _("List of publications");
6762 20 : myopt.translate_header = true;
6763 20 : myopt.translate_columns = translate_columns;
6764 20 : myopt.n_translate_columns = lengthof(translate_columns);
6765 :
6766 20 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6767 :
6768 20 : PQclear(res);
6769 :
6770 20 : return true;
6771 : }
6772 :
6773 : /*
6774 : * Add footer to publication description.
6775 : */
6776 : static bool
6777 492 : addFooterToPublicationDesc(PQExpBuffer buf, const char *footermsg,
6778 : bool as_schema, printTableContent *const cont)
6779 : {
6780 : PGresult *res;
6781 492 : int count = 0;
6782 492 : int i = 0;
6783 :
6784 492 : res = PSQLexec(buf->data);
6785 492 : if (!res)
6786 0 : return false;
6787 : else
6788 492 : count = PQntuples(res);
6789 :
6790 492 : if (count > 0)
6791 252 : printTableAddFooter(cont, footermsg);
6792 :
6793 852 : for (i = 0; i < count; i++)
6794 : {
6795 360 : if (as_schema)
6796 212 : printfPQExpBuffer(buf, " \"%s\"", PQgetvalue(res, i, 0));
6797 : else
6798 : {
6799 148 : printfPQExpBuffer(buf, " \"%s.%s\"", PQgetvalue(res, i, 0),
6800 : PQgetvalue(res, i, 1));
6801 :
6802 148 : if (!PQgetisnull(res, i, 3))
6803 28 : appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
6804 :
6805 148 : if (!PQgetisnull(res, i, 2))
6806 36 : appendPQExpBuffer(buf, " WHERE %s", PQgetvalue(res, i, 2));
6807 : }
6808 :
6809 360 : printTableAddFooter(cont, buf->data);
6810 : }
6811 :
6812 492 : PQclear(res);
6813 492 : return true;
6814 : }
6815 :
6816 : /*
6817 : * \dRp+
6818 : * Describes publications including the contents.
6819 : *
6820 : * Takes an optional regexp to select particular publications
6821 : */
6822 : bool
6823 276 : describePublications(const char *pattern)
6824 : {
6825 : PQExpBufferData buf;
6826 : int i;
6827 : PGresult *res;
6828 : bool has_pubtruncate;
6829 : bool has_pubgencols;
6830 : bool has_pubviaroot;
6831 : bool has_pubsequence;
6832 276 : int ncols = 6;
6833 276 : int nrows = 1;
6834 :
6835 : PQExpBufferData title;
6836 : printTableContent cont;
6837 :
6838 276 : if (pset.sversion < 100000)
6839 : {
6840 : char sverbuf[32];
6841 :
6842 0 : pg_log_error("The server (version %s) does not support publications.",
6843 : formatPGVersionNumber(pset.sversion, false,
6844 : sverbuf, sizeof(sverbuf)));
6845 0 : return true;
6846 : }
6847 :
6848 276 : has_pubsequence = (pset.sversion >= 190000);
6849 276 : has_pubtruncate = (pset.sversion >= 110000);
6850 276 : has_pubgencols = (pset.sversion >= 180000);
6851 276 : has_pubviaroot = (pset.sversion >= 130000);
6852 :
6853 276 : initPQExpBuffer(&buf);
6854 :
6855 276 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get details about matching publications"));
6856 276 : appendPQExpBuffer(&buf,
6857 : "SELECT oid, pubname,\n"
6858 : " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
6859 : " puballtables");
6860 :
6861 276 : if (has_pubsequence)
6862 276 : appendPQExpBufferStr(&buf,
6863 : ", puballsequences");
6864 : else
6865 0 : appendPQExpBufferStr(&buf,
6866 : ", false AS puballsequences");
6867 :
6868 276 : appendPQExpBufferStr(&buf,
6869 : ", pubinsert, pubupdate, pubdelete");
6870 :
6871 276 : if (has_pubtruncate)
6872 276 : appendPQExpBufferStr(&buf,
6873 : ", pubtruncate");
6874 : else
6875 0 : appendPQExpBufferStr(&buf,
6876 : ", false AS pubtruncate");
6877 :
6878 276 : if (has_pubgencols)
6879 276 : appendPQExpBuffer(&buf,
6880 : ", (CASE pubgencols\n"
6881 : " WHEN '%c' THEN 'none'\n"
6882 : " WHEN '%c' THEN 'stored'\n"
6883 : " END) AS \"%s\"\n",
6884 : PUBLISH_GENCOLS_NONE,
6885 : PUBLISH_GENCOLS_STORED,
6886 : gettext_noop("Generated columns"));
6887 : else
6888 0 : appendPQExpBufferStr(&buf,
6889 : ", 'none' AS pubgencols");
6890 :
6891 276 : if (has_pubviaroot)
6892 276 : appendPQExpBufferStr(&buf,
6893 : ", pubviaroot");
6894 : else
6895 0 : appendPQExpBufferStr(&buf,
6896 : ", false AS pubviaroot");
6897 :
6898 276 : appendPQExpBufferStr(&buf,
6899 : ", pg_catalog.obj_description(oid, 'pg_publication')");
6900 :
6901 276 : appendPQExpBufferStr(&buf,
6902 : "\nFROM pg_catalog.pg_publication\n");
6903 :
6904 276 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6905 : NULL, "pubname", NULL,
6906 : NULL,
6907 : NULL, 1))
6908 : {
6909 0 : termPQExpBuffer(&buf);
6910 0 : return false;
6911 : }
6912 :
6913 276 : appendPQExpBufferStr(&buf, "ORDER BY 2;");
6914 :
6915 276 : res = PSQLexec(buf.data);
6916 276 : if (!res)
6917 : {
6918 0 : termPQExpBuffer(&buf);
6919 0 : return false;
6920 : }
6921 :
6922 276 : if (PQntuples(res) == 0)
6923 : {
6924 0 : if (!pset.quiet)
6925 : {
6926 0 : if (pattern)
6927 0 : pg_log_error("Did not find any publication named \"%s\".",
6928 : pattern);
6929 : else
6930 0 : pg_log_error("Did not find any publications.");
6931 : }
6932 :
6933 0 : termPQExpBuffer(&buf);
6934 0 : PQclear(res);
6935 0 : return false;
6936 : }
6937 :
6938 276 : if (has_pubsequence)
6939 276 : ncols++;
6940 276 : if (has_pubtruncate)
6941 276 : ncols++;
6942 276 : if (has_pubgencols)
6943 276 : ncols++;
6944 276 : if (has_pubviaroot)
6945 276 : ncols++;
6946 :
6947 552 : for (i = 0; i < PQntuples(res); i++)
6948 : {
6949 276 : const char align = 'l';
6950 276 : char *pubid = PQgetvalue(res, i, 0);
6951 276 : char *pubname = PQgetvalue(res, i, 1);
6952 276 : bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
6953 276 : printTableOpt myopt = pset.popt.topt;
6954 :
6955 276 : initPQExpBuffer(&title);
6956 276 : printfPQExpBuffer(&title, _("Publication %s"), pubname);
6957 276 : printTableInit(&cont, &myopt, title.data, ncols, nrows);
6958 :
6959 276 : printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
6960 276 : printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
6961 276 : if (has_pubsequence)
6962 276 : printTableAddHeader(&cont, gettext_noop("All sequences"), true, align);
6963 276 : printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
6964 276 : printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
6965 276 : printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
6966 276 : if (has_pubtruncate)
6967 276 : printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
6968 276 : if (has_pubgencols)
6969 276 : printTableAddHeader(&cont, gettext_noop("Generated columns"), true, align);
6970 276 : if (has_pubviaroot)
6971 276 : printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
6972 276 : printTableAddHeader(&cont, gettext_noop("Description"), true, align);
6973 :
6974 276 : printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
6975 276 : printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
6976 276 : if (has_pubsequence)
6977 276 : printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
6978 276 : printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
6979 276 : printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
6980 276 : printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
6981 276 : if (has_pubtruncate)
6982 276 : printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
6983 276 : if (has_pubgencols)
6984 276 : printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
6985 276 : if (has_pubviaroot)
6986 276 : printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false);
6987 276 : printTableAddCell(&cont, PQgetvalue(res, i, 11), false, false);
6988 :
6989 276 : if (!puballtables)
6990 : {
6991 : /* Get the tables for the specified publication */
6992 216 : printfPQExpBuffer(&buf, "/* %s */\n",
6993 : _("Get tables published by this publication"));
6994 216 : appendPQExpBuffer(&buf, "SELECT n.nspname, c.relname");
6995 216 : if (pset.sversion >= 150000)
6996 : {
6997 216 : appendPQExpBufferStr(&buf,
6998 : ", pg_get_expr(pr.prqual, c.oid)");
6999 216 : appendPQExpBufferStr(&buf,
7000 : ", (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
7001 : " pg_catalog.array_to_string("
7002 : " ARRAY(SELECT attname\n"
7003 : " FROM\n"
7004 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
7005 : " pg_catalog.pg_attribute\n"
7006 : " WHERE attrelid = c.oid AND attnum = prattrs[s]), ', ')\n"
7007 : " ELSE NULL END)");
7008 : }
7009 : else
7010 0 : appendPQExpBufferStr(&buf,
7011 : ", NULL, NULL");
7012 216 : appendPQExpBuffer(&buf,
7013 : "\nFROM pg_catalog.pg_class c,\n"
7014 : " pg_catalog.pg_namespace n,\n"
7015 : " pg_catalog.pg_publication_rel pr\n"
7016 : "WHERE c.relnamespace = n.oid\n"
7017 : " AND c.oid = pr.prrelid\n"
7018 : " AND pr.prpubid = '%s'\n", pubid);
7019 :
7020 216 : if (pset.sversion >= 190000)
7021 216 : appendPQExpBuffer(&buf, " AND NOT pr.prexcept\n");
7022 :
7023 216 : appendPQExpBuffer(&buf, "ORDER BY 1,2");
7024 216 : if (!addFooterToPublicationDesc(&buf, _("Tables:"), false, &cont))
7025 0 : goto error_return;
7026 :
7027 216 : if (pset.sversion >= 150000)
7028 : {
7029 : /* Get the schemas for the specified publication */
7030 216 : printfPQExpBuffer(&buf, "/* %s */\n",
7031 : _("Get schemas published by this publication"));
7032 216 : appendPQExpBuffer(&buf,
7033 : "SELECT n.nspname\n"
7034 : "FROM pg_catalog.pg_namespace n\n"
7035 : " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
7036 : "WHERE pn.pnpubid = '%s'\n"
7037 : "ORDER BY 1", pubid);
7038 216 : if (!addFooterToPublicationDesc(&buf, _("Tables from schemas:"),
7039 : true, &cont))
7040 0 : goto error_return;
7041 : }
7042 : }
7043 : else
7044 : {
7045 60 : if (pset.sversion >= 190000)
7046 : {
7047 : /* Get tables in the EXCEPT clause for this publication */
7048 60 : printfPQExpBuffer(&buf, "/* %s */\n",
7049 : _("Get tables excluded by this publication"));
7050 60 : appendPQExpBuffer(&buf,
7051 : "SELECT n.nspname || '.' || c.relname\n"
7052 : "FROM pg_catalog.pg_class c\n"
7053 : " JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
7054 : " JOIN pg_catalog.pg_publication_rel pr ON c.oid = pr.prrelid\n"
7055 : "WHERE pr.prpubid = '%s' AND pr.prexcept\n"
7056 : "ORDER BY 1", pubid);
7057 60 : if (!addFooterToPublicationDesc(&buf, _("Except tables:"),
7058 : true, &cont))
7059 0 : goto error_return;
7060 : }
7061 : }
7062 :
7063 276 : printTable(&cont, pset.queryFout, false, pset.logfile);
7064 276 : printTableCleanup(&cont);
7065 :
7066 276 : termPQExpBuffer(&title);
7067 : }
7068 :
7069 276 : termPQExpBuffer(&buf);
7070 276 : PQclear(res);
7071 :
7072 276 : return true;
7073 :
7074 0 : error_return:
7075 0 : printTableCleanup(&cont);
7076 0 : PQclear(res);
7077 0 : termPQExpBuffer(&buf);
7078 0 : termPQExpBuffer(&title);
7079 0 : return false;
7080 : }
7081 :
7082 : /*
7083 : * \dRs
7084 : * Describes subscriptions.
7085 : *
7086 : * Takes an optional regexp to select particular subscriptions
7087 : */
7088 : bool
7089 112 : describeSubscriptions(const char *pattern, bool verbose)
7090 : {
7091 : PQExpBufferData buf;
7092 : PGresult *res;
7093 112 : printQueryOpt myopt = pset.popt;
7094 : static const bool translate_columns[] = {false, false, false, false,
7095 : false, false, false, false, false, false, false, false, false, false,
7096 : false, false, false, false, false, false, false};
7097 :
7098 112 : if (pset.sversion < 100000)
7099 : {
7100 : char sverbuf[32];
7101 :
7102 0 : pg_log_error("The server (version %s) does not support subscriptions.",
7103 : formatPGVersionNumber(pset.sversion, false,
7104 : sverbuf, sizeof(sverbuf)));
7105 0 : return true;
7106 : }
7107 :
7108 112 : initPQExpBuffer(&buf);
7109 :
7110 112 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching subscriptions"));
7111 112 : appendPQExpBuffer(&buf,
7112 : "SELECT subname AS \"%s\"\n"
7113 : ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
7114 : ", subenabled AS \"%s\"\n"
7115 : ", subpublications AS \"%s\"\n",
7116 : gettext_noop("Name"),
7117 : gettext_noop("Owner"),
7118 : gettext_noop("Enabled"),
7119 : gettext_noop("Publication"));
7120 :
7121 112 : if (verbose)
7122 : {
7123 : /* Binary mode and streaming are only supported in v14 and higher */
7124 88 : if (pset.sversion >= 140000)
7125 : {
7126 88 : appendPQExpBuffer(&buf,
7127 : ", subbinary AS \"%s\"\n",
7128 : gettext_noop("Binary"));
7129 :
7130 88 : if (pset.sversion >= 160000)
7131 88 : appendPQExpBuffer(&buf,
7132 : ", (CASE substream\n"
7133 : " WHEN " CppAsString2(LOGICALREP_STREAM_OFF) " THEN 'off'\n"
7134 : " WHEN " CppAsString2(LOGICALREP_STREAM_ON) " THEN 'on'\n"
7135 : " WHEN " CppAsString2(LOGICALREP_STREAM_PARALLEL) " THEN 'parallel'\n"
7136 : " END) AS \"%s\"\n",
7137 : gettext_noop("Streaming"));
7138 : else
7139 0 : appendPQExpBuffer(&buf,
7140 : ", substream AS \"%s\"\n",
7141 : gettext_noop("Streaming"));
7142 : }
7143 :
7144 : /* Two_phase and disable_on_error are only supported in v15 and higher */
7145 88 : if (pset.sversion >= 150000)
7146 88 : appendPQExpBuffer(&buf,
7147 : ", subtwophasestate AS \"%s\"\n"
7148 : ", subdisableonerr AS \"%s\"\n",
7149 : gettext_noop("Two-phase commit"),
7150 : gettext_noop("Disable on error"));
7151 :
7152 88 : if (pset.sversion >= 160000)
7153 88 : appendPQExpBuffer(&buf,
7154 : ", suborigin AS \"%s\"\n"
7155 : ", subpasswordrequired AS \"%s\"\n"
7156 : ", subrunasowner AS \"%s\"\n",
7157 : gettext_noop("Origin"),
7158 : gettext_noop("Password required"),
7159 : gettext_noop("Run as owner?"));
7160 :
7161 88 : if (pset.sversion >= 170000)
7162 88 : appendPQExpBuffer(&buf,
7163 : ", subfailover AS \"%s\"\n",
7164 : gettext_noop("Failover"));
7165 88 : if (pset.sversion >= 190000)
7166 : {
7167 88 : appendPQExpBuffer(&buf,
7168 : ", (select srvname from pg_foreign_server where oid=subserver) AS \"%s\"\n",
7169 : gettext_noop("Server"));
7170 :
7171 88 : appendPQExpBuffer(&buf,
7172 : ", subretaindeadtuples AS \"%s\"\n",
7173 : gettext_noop("Retain dead tuples"));
7174 :
7175 88 : appendPQExpBuffer(&buf,
7176 : ", submaxretention AS \"%s\"\n",
7177 : gettext_noop("Max retention duration"));
7178 :
7179 88 : appendPQExpBuffer(&buf,
7180 : ", subretentionactive AS \"%s\"\n",
7181 : gettext_noop("Retention active"));
7182 : }
7183 :
7184 88 : appendPQExpBuffer(&buf,
7185 : ", subsynccommit AS \"%s\"\n"
7186 : ", subconninfo AS \"%s\"\n",
7187 : gettext_noop("Synchronous commit"),
7188 : gettext_noop("Conninfo"));
7189 :
7190 88 : if (pset.sversion >= 190000)
7191 88 : appendPQExpBuffer(&buf,
7192 : ", subwalrcvtimeout AS \"%s\"\n",
7193 : gettext_noop("Receiver timeout"));
7194 :
7195 : /* Skip LSN is only supported in v15 and higher */
7196 88 : if (pset.sversion >= 150000)
7197 88 : appendPQExpBuffer(&buf,
7198 : ", subskiplsn AS \"%s\"\n",
7199 : gettext_noop("Skip LSN"));
7200 :
7201 88 : appendPQExpBuffer(&buf,
7202 : ", pg_catalog.obj_description(oid, 'pg_subscription') AS \"%s\"\n",
7203 : gettext_noop("Description"));
7204 : }
7205 :
7206 : /* Only display subscriptions in current database. */
7207 112 : appendPQExpBufferStr(&buf,
7208 : "FROM pg_catalog.pg_subscription\n"
7209 : "WHERE subdbid = (SELECT oid\n"
7210 : " FROM pg_catalog.pg_database\n"
7211 : " WHERE datname = pg_catalog.current_database())");
7212 :
7213 112 : if (!validateSQLNamePattern(&buf, pattern, true, false,
7214 : NULL, "subname", NULL,
7215 : NULL,
7216 : NULL, 1))
7217 : {
7218 12 : termPQExpBuffer(&buf);
7219 12 : return false;
7220 : }
7221 :
7222 100 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
7223 :
7224 100 : res = PSQLexec(buf.data);
7225 100 : termPQExpBuffer(&buf);
7226 100 : if (!res)
7227 0 : return false;
7228 :
7229 100 : myopt.title = _("List of subscriptions");
7230 100 : myopt.translate_header = true;
7231 100 : myopt.translate_columns = translate_columns;
7232 100 : myopt.n_translate_columns = lengthof(translate_columns);
7233 :
7234 100 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7235 :
7236 100 : PQclear(res);
7237 100 : return true;
7238 : }
7239 :
7240 : /*
7241 : * printACLColumn
7242 : *
7243 : * Helper function for consistently formatting ACL (privilege) columns.
7244 : * The proper targetlist entry is appended to buf. Note lack of any
7245 : * whitespace or comma decoration.
7246 : *
7247 : * If you change this, see also the handling of attacl in permissionsList(),
7248 : * which can't conveniently use this code.
7249 : */
7250 : static void
7251 204 : printACLColumn(PQExpBuffer buf, const char *colname)
7252 : {
7253 204 : appendPQExpBuffer(buf,
7254 : "CASE"
7255 : " WHEN pg_catalog.array_length(%s, 1) = 0 THEN '%s'"
7256 : " ELSE pg_catalog.array_to_string(%s, E'\\n')"
7257 : " END AS \"%s\"",
7258 : colname, gettext_noop("(none)"),
7259 : colname, gettext_noop("Access privileges"));
7260 204 : }
7261 :
7262 : /*
7263 : * \dAc
7264 : * Lists operator classes
7265 : *
7266 : * Takes optional regexps to filter by index access method and input data type.
7267 : */
7268 : bool
7269 20 : listOperatorClasses(const char *access_method_pattern,
7270 : const char *type_pattern, bool verbose)
7271 : {
7272 : PQExpBufferData buf;
7273 : PGresult *res;
7274 20 : printQueryOpt myopt = pset.popt;
7275 20 : bool have_where = false;
7276 : static const bool translate_columns[] = {false, false, false, false, false, false, false};
7277 :
7278 20 : initPQExpBuffer(&buf);
7279 :
7280 20 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching operator classes"));
7281 20 : appendPQExpBuffer(&buf,
7282 : "SELECT\n"
7283 : " am.amname AS \"%s\",\n"
7284 : " pg_catalog.format_type(c.opcintype, NULL) AS \"%s\",\n"
7285 : " CASE\n"
7286 : " WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n"
7287 : " THEN pg_catalog.format_type(c.opckeytype, NULL)\n"
7288 : " ELSE NULL\n"
7289 : " END AS \"%s\",\n"
7290 : " CASE\n"
7291 : " WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n"
7292 : " THEN pg_catalog.format('%%I', c.opcname)\n"
7293 : " ELSE pg_catalog.format('%%I.%%I', n.nspname, c.opcname)\n"
7294 : " END AS \"%s\",\n"
7295 : " (CASE WHEN c.opcdefault\n"
7296 : " THEN '%s'\n"
7297 : " ELSE '%s'\n"
7298 : " END) AS \"%s\"",
7299 : gettext_noop("AM"),
7300 : gettext_noop("Input type"),
7301 : gettext_noop("Storage type"),
7302 : gettext_noop("Operator class"),
7303 : gettext_noop("yes"),
7304 : gettext_noop("no"),
7305 : gettext_noop("Default?"));
7306 20 : if (verbose)
7307 0 : appendPQExpBuffer(&buf,
7308 : ",\n CASE\n"
7309 : " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7310 : " THEN pg_catalog.format('%%I', of.opfname)\n"
7311 : " ELSE pg_catalog.format('%%I.%%I', ofn.nspname, of.opfname)\n"
7312 : " END AS \"%s\",\n"
7313 : " pg_catalog.pg_get_userbyid(c.opcowner) AS \"%s\"\n",
7314 : gettext_noop("Operator family"),
7315 : gettext_noop("Owner"));
7316 20 : appendPQExpBufferStr(&buf,
7317 : "\nFROM pg_catalog.pg_opclass c\n"
7318 : " LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n"
7319 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n"
7320 : " LEFT JOIN pg_catalog.pg_type t ON t.oid = c.opcintype\n"
7321 : " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n");
7322 20 : if (verbose)
7323 0 : appendPQExpBufferStr(&buf,
7324 : " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n"
7325 : " LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n");
7326 :
7327 20 : if (access_method_pattern)
7328 20 : if (!validateSQLNamePattern(&buf, access_method_pattern,
7329 : false, false, NULL, "am.amname", NULL, NULL,
7330 : &have_where, 1))
7331 12 : goto error_return;
7332 8 : if (type_pattern)
7333 : {
7334 : /* Match type name pattern against either internal or external name */
7335 4 : if (!validateSQLNamePattern(&buf, type_pattern, have_where, false,
7336 : "tn.nspname", "t.typname",
7337 : "pg_catalog.format_type(t.oid, NULL)",
7338 : "pg_catalog.pg_type_is_visible(t.oid)",
7339 : NULL, 3))
7340 0 : goto error_return;
7341 : }
7342 :
7343 8 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
7344 8 : res = PSQLexec(buf.data);
7345 8 : termPQExpBuffer(&buf);
7346 8 : if (!res)
7347 0 : return false;
7348 :
7349 8 : myopt.title = _("List of operator classes");
7350 8 : myopt.translate_header = true;
7351 8 : myopt.translate_columns = translate_columns;
7352 8 : myopt.n_translate_columns = lengthof(translate_columns);
7353 :
7354 8 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7355 :
7356 8 : PQclear(res);
7357 8 : return true;
7358 :
7359 12 : error_return:
7360 12 : termPQExpBuffer(&buf);
7361 12 : return false;
7362 : }
7363 :
7364 : /*
7365 : * \dAf
7366 : * Lists operator families
7367 : *
7368 : * Takes optional regexps to filter by index access method and input data type.
7369 : */
7370 : bool
7371 24 : listOperatorFamilies(const char *access_method_pattern,
7372 : const char *type_pattern, bool verbose)
7373 : {
7374 : PQExpBufferData buf;
7375 : PGresult *res;
7376 24 : printQueryOpt myopt = pset.popt;
7377 24 : bool have_where = false;
7378 : static const bool translate_columns[] = {false, false, false, false};
7379 :
7380 24 : initPQExpBuffer(&buf);
7381 :
7382 24 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching operator families"));
7383 24 : appendPQExpBuffer(&buf,
7384 : "SELECT\n"
7385 : " am.amname AS \"%s\",\n"
7386 : " CASE\n"
7387 : " WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n"
7388 : " THEN pg_catalog.format('%%I', f.opfname)\n"
7389 : " ELSE pg_catalog.format('%%I.%%I', n.nspname, f.opfname)\n"
7390 : " END AS \"%s\",\n"
7391 : " (SELECT\n"
7392 : " pg_catalog.string_agg(pg_catalog.format_type(oc.opcintype, NULL), ', ')\n"
7393 : " FROM pg_catalog.pg_opclass oc\n"
7394 : " WHERE oc.opcfamily = f.oid) \"%s\"",
7395 : gettext_noop("AM"),
7396 : gettext_noop("Operator family"),
7397 : gettext_noop("Applicable types"));
7398 24 : if (verbose)
7399 0 : appendPQExpBuffer(&buf,
7400 : ",\n pg_catalog.pg_get_userbyid(f.opfowner) AS \"%s\"\n",
7401 : gettext_noop("Owner"));
7402 24 : appendPQExpBufferStr(&buf,
7403 : "\nFROM pg_catalog.pg_opfamily f\n"
7404 : " LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n"
7405 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace\n");
7406 :
7407 24 : if (access_method_pattern)
7408 24 : if (!validateSQLNamePattern(&buf, access_method_pattern,
7409 : false, false, NULL, "am.amname", NULL, NULL,
7410 : &have_where, 1))
7411 12 : goto error_return;
7412 12 : if (type_pattern)
7413 : {
7414 4 : appendPQExpBuffer(&buf,
7415 : " %s EXISTS (\n"
7416 : " SELECT 1\n"
7417 : " FROM pg_catalog.pg_type t\n"
7418 : " JOIN pg_catalog.pg_opclass oc ON oc.opcintype = t.oid\n"
7419 : " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n"
7420 : " WHERE oc.opcfamily = f.oid\n",
7421 4 : have_where ? "AND" : "WHERE");
7422 : /* Match type name pattern against either internal or external name */
7423 4 : if (!validateSQLNamePattern(&buf, type_pattern, true, false,
7424 : "tn.nspname", "t.typname",
7425 : "pg_catalog.format_type(t.oid, NULL)",
7426 : "pg_catalog.pg_type_is_visible(t.oid)",
7427 : NULL, 3))
7428 0 : goto error_return;
7429 4 : appendPQExpBufferStr(&buf, " )\n");
7430 : }
7431 :
7432 12 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
7433 12 : res = PSQLexec(buf.data);
7434 12 : termPQExpBuffer(&buf);
7435 12 : if (!res)
7436 0 : return false;
7437 :
7438 12 : myopt.title = _("List of operator families");
7439 12 : myopt.translate_header = true;
7440 12 : myopt.translate_columns = translate_columns;
7441 12 : myopt.n_translate_columns = lengthof(translate_columns);
7442 :
7443 12 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7444 :
7445 12 : PQclear(res);
7446 12 : return true;
7447 :
7448 12 : error_return:
7449 12 : termPQExpBuffer(&buf);
7450 12 : return false;
7451 : }
7452 :
7453 : /*
7454 : * \dAo
7455 : * Lists operators of operator families
7456 : *
7457 : * Takes optional regexps to filter by index access method and operator
7458 : * family.
7459 : */
7460 : bool
7461 24 : listOpFamilyOperators(const char *access_method_pattern,
7462 : const char *family_pattern, bool verbose)
7463 : {
7464 : PQExpBufferData buf;
7465 : PGresult *res;
7466 24 : printQueryOpt myopt = pset.popt;
7467 24 : bool have_where = false;
7468 :
7469 : static const bool translate_columns[] = {false, false, false, false, false, false, true};
7470 :
7471 24 : initPQExpBuffer(&buf);
7472 :
7473 24 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get operators of matching operator families"));
7474 24 : appendPQExpBuffer(&buf,
7475 : "SELECT\n"
7476 : " am.amname AS \"%s\",\n"
7477 : " CASE\n"
7478 : " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7479 : " THEN pg_catalog.format('%%I', of.opfname)\n"
7480 : " ELSE pg_catalog.format('%%I.%%I', nsf.nspname, of.opfname)\n"
7481 : " END AS \"%s\",\n"
7482 : " o.amopopr::pg_catalog.regoperator AS \"%s\"\n,"
7483 : " o.amopstrategy AS \"%s\",\n"
7484 : " CASE o.amoppurpose\n"
7485 : " WHEN " CppAsString2(AMOP_ORDER) " THEN '%s'\n"
7486 : " WHEN " CppAsString2(AMOP_SEARCH) " THEN '%s'\n"
7487 : " END AS \"%s\"\n",
7488 : gettext_noop("AM"),
7489 : gettext_noop("Operator family"),
7490 : gettext_noop("Operator"),
7491 : gettext_noop("Strategy"),
7492 : gettext_noop("ordering"),
7493 : gettext_noop("search"),
7494 : gettext_noop("Purpose"));
7495 :
7496 24 : if (verbose)
7497 4 : appendPQExpBuffer(&buf,
7498 : ", ofs.opfname AS \"%s\",\n"
7499 : " CASE\n"
7500 : " WHEN p.proleakproof THEN '%s'\n"
7501 : " ELSE '%s'\n"
7502 : " END AS \"%s\"\n",
7503 : gettext_noop("Sort opfamily"),
7504 : gettext_noop("yes"),
7505 : gettext_noop("no"),
7506 : gettext_noop("Leakproof?"));
7507 24 : appendPQExpBufferStr(&buf,
7508 : "FROM pg_catalog.pg_amop o\n"
7509 : " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n"
7510 : " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n"
7511 : " LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n");
7512 24 : if (verbose)
7513 4 : appendPQExpBufferStr(&buf,
7514 : " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n"
7515 : " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n"
7516 : " LEFT JOIN pg_catalog.pg_proc p ON p.oid = op.oprcode\n");
7517 :
7518 24 : if (access_method_pattern)
7519 : {
7520 24 : if (!validateSQLNamePattern(&buf, access_method_pattern,
7521 : false, false, NULL, "am.amname",
7522 : NULL, NULL,
7523 : &have_where, 1))
7524 12 : goto error_return;
7525 : }
7526 :
7527 12 : if (family_pattern)
7528 : {
7529 8 : if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
7530 : "nsf.nspname", "of.opfname", NULL, NULL,
7531 : NULL, 3))
7532 0 : goto error_return;
7533 : }
7534 :
7535 12 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7536 : " o.amoplefttype = o.amoprighttype DESC,\n"
7537 : " pg_catalog.format_type(o.amoplefttype, NULL),\n"
7538 : " pg_catalog.format_type(o.amoprighttype, NULL),\n"
7539 : " o.amopstrategy;");
7540 :
7541 12 : res = PSQLexec(buf.data);
7542 12 : termPQExpBuffer(&buf);
7543 12 : if (!res)
7544 0 : return false;
7545 :
7546 12 : myopt.title = _("List of operators of operator families");
7547 12 : myopt.translate_header = true;
7548 12 : myopt.translate_columns = translate_columns;
7549 12 : myopt.n_translate_columns = lengthof(translate_columns);
7550 :
7551 12 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7552 :
7553 12 : PQclear(res);
7554 12 : return true;
7555 :
7556 12 : error_return:
7557 12 : termPQExpBuffer(&buf);
7558 12 : return false;
7559 : }
7560 :
7561 : /*
7562 : * \dAp
7563 : * Lists support functions of operator families
7564 : *
7565 : * Takes optional regexps to filter by index access method and operator
7566 : * family.
7567 : */
7568 : bool
7569 28 : listOpFamilyFunctions(const char *access_method_pattern,
7570 : const char *family_pattern, bool verbose)
7571 : {
7572 : PQExpBufferData buf;
7573 : PGresult *res;
7574 28 : printQueryOpt myopt = pset.popt;
7575 28 : bool have_where = false;
7576 : static const bool translate_columns[] = {false, false, false, false, false, false};
7577 :
7578 28 : initPQExpBuffer(&buf);
7579 :
7580 28 : printfPQExpBuffer(&buf, "/* %s */\n",
7581 : _("Get support functions of matching operator families"));
7582 28 : appendPQExpBuffer(&buf,
7583 : "SELECT\n"
7584 : " am.amname AS \"%s\",\n"
7585 : " CASE\n"
7586 : " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7587 : " THEN pg_catalog.format('%%I', of.opfname)\n"
7588 : " ELSE pg_catalog.format('%%I.%%I', ns.nspname, of.opfname)\n"
7589 : " END AS \"%s\",\n"
7590 : " pg_catalog.format_type(ap.amproclefttype, NULL) AS \"%s\",\n"
7591 : " pg_catalog.format_type(ap.amprocrighttype, NULL) AS \"%s\",\n"
7592 : " ap.amprocnum AS \"%s\"\n",
7593 : gettext_noop("AM"),
7594 : gettext_noop("Operator family"),
7595 : gettext_noop("Registered left type"),
7596 : gettext_noop("Registered right type"),
7597 : gettext_noop("Number"));
7598 :
7599 28 : if (!verbose)
7600 20 : appendPQExpBuffer(&buf,
7601 : ", p.proname AS \"%s\"\n",
7602 : gettext_noop("Function"));
7603 : else
7604 8 : appendPQExpBuffer(&buf,
7605 : ", ap.amproc::pg_catalog.regprocedure AS \"%s\"\n",
7606 : gettext_noop("Function"));
7607 :
7608 28 : appendPQExpBufferStr(&buf,
7609 : "FROM pg_catalog.pg_amproc ap\n"
7610 : " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n"
7611 : " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n"
7612 : " LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n"
7613 : " LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.oid\n");
7614 :
7615 28 : if (access_method_pattern)
7616 : {
7617 28 : if (!validateSQLNamePattern(&buf, access_method_pattern,
7618 : false, false, NULL, "am.amname",
7619 : NULL, NULL,
7620 : &have_where, 1))
7621 12 : goto error_return;
7622 : }
7623 16 : if (family_pattern)
7624 : {
7625 12 : if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
7626 : "ns.nspname", "of.opfname", NULL, NULL,
7627 : NULL, 3))
7628 0 : goto error_return;
7629 : }
7630 :
7631 16 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7632 : " ap.amproclefttype = ap.amprocrighttype DESC,\n"
7633 : " 3, 4, 5;");
7634 :
7635 16 : res = PSQLexec(buf.data);
7636 16 : termPQExpBuffer(&buf);
7637 16 : if (!res)
7638 0 : return false;
7639 :
7640 16 : myopt.title = _("List of support functions of operator families");
7641 16 : myopt.translate_header = true;
7642 16 : myopt.translate_columns = translate_columns;
7643 16 : myopt.n_translate_columns = lengthof(translate_columns);
7644 :
7645 16 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7646 :
7647 16 : PQclear(res);
7648 16 : return true;
7649 :
7650 12 : error_return:
7651 12 : termPQExpBuffer(&buf);
7652 12 : return false;
7653 : }
7654 :
7655 : /*
7656 : * \dl or \lo_list
7657 : * Lists large objects
7658 : */
7659 : bool
7660 12 : listLargeObjects(bool verbose)
7661 : {
7662 : PQExpBufferData buf;
7663 : PGresult *res;
7664 12 : printQueryOpt myopt = pset.popt;
7665 :
7666 12 : initPQExpBuffer(&buf);
7667 :
7668 12 : printfPQExpBuffer(&buf, "/* %s */\n", _("Get large objects"));
7669 12 : appendPQExpBuffer(&buf,
7670 : "SELECT oid as \"%s\",\n"
7671 : " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n ",
7672 : gettext_noop("ID"),
7673 : gettext_noop("Owner"));
7674 :
7675 12 : if (verbose)
7676 : {
7677 4 : printACLColumn(&buf, "lomacl");
7678 4 : appendPQExpBufferStr(&buf, ",\n ");
7679 : }
7680 :
7681 12 : appendPQExpBuffer(&buf,
7682 : "pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
7683 : "FROM pg_catalog.pg_largeobject_metadata\n"
7684 : "ORDER BY oid",
7685 : gettext_noop("Description"));
7686 :
7687 12 : res = PSQLexec(buf.data);
7688 12 : termPQExpBuffer(&buf);
7689 12 : if (!res)
7690 0 : return false;
7691 :
7692 12 : myopt.title = _("Large objects");
7693 12 : myopt.translate_header = true;
7694 :
7695 12 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7696 :
7697 12 : PQclear(res);
7698 12 : return true;
7699 : }
|