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