PostgreSQL Source Code git master
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-2025, 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;
82 printQueryOpt myopt = pset.popt;
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;
152 printQueryOpt myopt = pset.popt;
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;
226 printQueryOpt myopt = pset.popt;
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;
308 printQueryOpt myopt = pset.popt;
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
314 if (strlen(functypes) != strspn(functypes, df_options))
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
331 if (!showAggregate && !showNormal && !showProcedure && !showTrigger && !showWindow)
332 {
333 showAggregate = showNormal = showTrigger = showWindow = true;
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 = "
402 CppAsString2(PROPARALLEL_RESTRICTED) " THEN '%s'\n"
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 */
455 if (showNormal && showAggregate && showProcedure && showTrigger && showWindow)
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 <> "
470 CppAsString2(PROKIND_AGGREGATE) "\n");
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 <> "
484 CppAsString2(PROKIND_PROCEDURE) "\n");
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 <> "
508 CppAsString2(PROKIND_WINDOW) "\n");
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 = "
524 CppAsString2(PROKIND_AGGREGATE) "\n");
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 = "
542 CppAsString2(PROKIND_PROCEDURE) "\n");
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 = "
551 CppAsString2(PROKIND_WINDOW) "\n");
552 else
553 appendPQExpBufferStr(&buf, "p.proiswindow\n");
554 }
555 appendPQExpBufferStr(&buf, " )\n");
556 }
557
558 if (!validateSQLNamePattern(&buf, func_pattern, have_where, false,
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);
585 map_typename_pattern(arg_patterns[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
627error_return:
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;
643 printQueryOpt myopt = pset.popt;
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 ");
692 appendPQExpBufferStr(&buf, "OR (SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE)
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
794describeOperators(const char *oper_pattern,
795 char **arg_patterns, int num_arg_patterns,
796 bool verbose, bool showSystem)
797{
799 PGresult *res;
800 printQueryOpt myopt = pset.popt;
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
873 if (!validateSQLNamePattern(&buf, oper_pattern,
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);
904 map_typename_pattern(arg_patterns[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
934error_return:
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;
950 printQueryOpt myopt = pset.popt;
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;
1054 printQueryOpt myopt = pset.popt;
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 ("
1167 CppAsString2(RELKIND_RELATION) ","
1168 CppAsString2(RELKIND_VIEW) ","
1169 CppAsString2(RELKIND_MATVIEW) ","
1170 CppAsString2(RELKIND_SEQUENCE) ","
1171 CppAsString2(RELKIND_FOREIGN_TABLE) ","
1172 CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
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
1202error_return:
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;
1218 printQueryOpt myopt = pset.popt;
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"),
1232 DEFACLOBJ_RELATION,
1233 gettext_noop("table"),
1234 DEFACLOBJ_SEQUENCE,
1235 gettext_noop("sequence"),
1236 DEFACLOBJ_FUNCTION,
1237 gettext_noop("function"),
1238 DEFACLOBJ_TYPE,
1239 gettext_noop("type"),
1240 DEFACLOBJ_NAMESPACE,
1241 gettext_noop("schema"),
1242 DEFACLOBJ_LARGEOBJECT,
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
1277error_return:
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;
1299 printQueryOpt myopt = pset.popt;
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
1473error_return:
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;
1579 printTableOpt myopt = pset.popt.topt;
1580 printTableContent cont;
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;
1761 printQueryOpt myopt = pset.popt;
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 */
1809 appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
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 ||
1918 tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
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
1935 if (show_column_details)
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 ||
1960 tableinfo.relkind == RELKIND_PARTITIONED_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 ||
1990 tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
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 ||
2000 tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
2001 tableinfo.relkind == RELKIND_MATVIEW ||
2002 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2003 tableinfo.relkind == RELKIND_PARTITIONED_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 ||
2018 tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
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;
2061 case RELKIND_PARTITIONED_INDEX:
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;
2073 case RELKIND_COMPOSITE_TYPE:
2074 printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
2075 schemaname, relationname);
2076 break;
2077 case RELKIND_FOREIGN_TABLE:
2078 printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
2079 schemaname, relationname);
2080 break;
2081 case RELKIND_PARTITIONED_TABLE:
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");
2100 if (show_column_details)
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 */
2139 if (show_column_details)
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
2148 printTableAddCell(&cont,
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
2172 default_str = PQgetvalue(res, i, attrdef_col);
2173
2174 printTableAddCell(&cont, default_str, false, mustfree);
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)
2216 printTableAddCell(&cont, PQgetvalue(res, i, attstattarget_col),
2217 false, false);
2218
2219 /* Column comments, if the relkind supports this feature */
2220 if (attdescr_col >= 0)
2221 printTableAddCell(&cont, PQgetvalue(res, i, attdescr_col),
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"),
2275 partconstraintdef);
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 ||
2332 tableinfo.relkind == RELKIND_PARTITIONED_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) ","
2345 CppAsString2(CONSTRAINT_UNIQUE) ","
2346 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
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) ","
2353 CppAsString2(CONSTRAINT_UNIQUE) ","
2354 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
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
2407 appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
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)
2437 add_tablespace_footer(&cont, tableinfo.relkind,
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 ||
2447 tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
2448 tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
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 ("
2476 CppAsString2(CONSTRAINT_PRIMARY) ","
2477 CppAsString2(CONSTRAINT_UNIQUE) ","
2478 CppAsString2(CONSTRAINT_EXCLUSION) "))\n"
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
2548 printTableAddFooter(&cont, buf.data);
2549
2550 /* Print tablespace of the index on the same line */
2551 add_tablespace_footer(&cont, RELKIND_INDEX,
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
2586 printTableAddFooter(&cont, buf.data);
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
2657 printTableAddFooter(&cont, buf.data);
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
2706 printTableAddFooter(&cont, buf.data);
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
2788 printTableAddFooter(&cont, buf.data);
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 */
2849 has_all = (has_ndistinct && has_dependencies && has_mcv);
2850 has_some = (has_ndistinct || has_dependencies || has_mcv);
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
2887 printTableAddFooter(&cont, buf.data);
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
2964 printTableAddFooter(&cont, buf.data);
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 }
3038 printTableAddFooter(&cont, buf.data);
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);
3046 printTableAddFooter(&cont, buf.data);
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 "UNION\n"
3080 "SELECT pubname\n"
3081 " , NULL\n"
3082 " , NULL\n"
3083 "FROM pg_catalog.pg_publication p\n"
3084 "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3085 "ORDER BY 1;",
3086 oid, oid, oid, oid);
3087 }
3088 else
3089 {
3091 "SELECT pubname\n"
3092 " , NULL\n"
3093 " , NULL\n"
3094 "FROM pg_catalog.pg_publication p\n"
3095 "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3096 "WHERE pr.prrelid = '%s'\n"
3097 "UNION ALL\n"
3098 "SELECT pubname\n"
3099 " , NULL\n"
3100 " , NULL\n"
3101 "FROM pg_catalog.pg_publication p\n"
3102 "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3103 "ORDER BY 1;",
3104 oid, oid);
3105 }
3106
3107 result = PSQLexec(buf.data);
3108 if (!result)
3109 goto error_return;
3110 else
3111 tuples = PQntuples(result);
3112
3113 if (tuples > 0)
3114 printTableAddFooter(&cont, _("Publications:"));
3115
3116 /* Might be an empty set - that's ok */
3117 for (i = 0; i < tuples; i++)
3118 {
3119 printfPQExpBuffer(&buf, " \"%s\"",
3120 PQgetvalue(result, i, 0));
3121
3122 /* column list (if any) */
3123 if (!PQgetisnull(result, i, 2))
3124 appendPQExpBuffer(&buf, " (%s)",
3125 PQgetvalue(result, i, 2));
3126
3127 /* row filter (if any) */
3128 if (!PQgetisnull(result, i, 1))
3129 appendPQExpBuffer(&buf, " WHERE %s",
3130 PQgetvalue(result, i, 1));
3131
3132 printTableAddFooter(&cont, buf.data);
3133 }
3134 PQclear(result);
3135 }
3136
3137 /*
3138 * If verbose, print NOT NULL constraints.
3139 */
3140 if (verbose)
3141 {
3143 "SELECT c.conname, a.attname, c.connoinherit,\n"
3144 " c.conislocal, c.coninhcount <> 0,\n"
3145 " c.convalidated\n"
3146 "FROM pg_catalog.pg_constraint c JOIN\n"
3147 " pg_catalog.pg_attribute a ON\n"
3148 " (a.attrelid = c.conrelid AND a.attnum = c.conkey[1])\n"
3149 "WHERE c.contype = " CppAsString2(CONSTRAINT_NOTNULL) " AND\n"
3150 " c.conrelid = '%s'::pg_catalog.regclass\n"
3151 "ORDER BY a.attnum",
3152 oid);
3153
3154 result = PSQLexec(buf.data);
3155 if (!result)
3156 goto error_return;
3157 else
3158 tuples = PQntuples(result);
3159
3160 if (tuples > 0)
3161 printTableAddFooter(&cont, _("Not-null constraints:"));
3162
3163 /* Might be an empty set - that's ok */
3164 for (i = 0; i < tuples; i++)
3165 {
3166 bool islocal = PQgetvalue(result, i, 3)[0] == 't';
3167 bool inherited = PQgetvalue(result, i, 4)[0] == 't';
3168 bool validated = PQgetvalue(result, i, 5)[0] == 't';
3169
3170 printfPQExpBuffer(&buf, " \"%s\" NOT NULL \"%s\"%s%s",
3171 PQgetvalue(result, i, 0),
3172 PQgetvalue(result, i, 1),
3173 PQgetvalue(result, i, 2)[0] == 't' ?
3174 " NO INHERIT" :
3175 islocal && inherited ? _(" (local, inherited)") :
3176 inherited ? _(" (inherited)") : "",
3177 !validated ? " NOT VALID" : "");
3178
3179 printTableAddFooter(&cont, buf.data);
3180 }
3181 PQclear(result);
3182 }
3183 }
3184
3185 /* Get view_def if table is a view or materialized view */
3186 if ((tableinfo.relkind == RELKIND_VIEW ||
3187 tableinfo.relkind == RELKIND_MATVIEW) && verbose)
3188 {
3189 PGresult *result;
3190
3192 "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
3193 oid);
3194 result = PSQLexec(buf.data);
3195 if (!result)
3196 goto error_return;
3197
3198 if (PQntuples(result) > 0)
3199 view_def = pg_strdup(PQgetvalue(result, 0, 0));
3200
3201 PQclear(result);
3202 }
3203
3204 if (view_def)
3205 {
3206 PGresult *result = NULL;
3207
3208 /* Footer information about a view */
3209 printTableAddFooter(&cont, _("View definition:"));
3210 printTableAddFooter(&cont, view_def);
3211
3212 /* print rules */
3213 if (tableinfo.hasrules)
3214 {
3216 "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
3217 "FROM pg_catalog.pg_rewrite r\n"
3218 "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
3219 oid);
3220 result = PSQLexec(buf.data);
3221 if (!result)
3222 goto error_return;
3223
3224 if (PQntuples(result) > 0)
3225 {
3226 printTableAddFooter(&cont, _("Rules:"));
3227 for (i = 0; i < PQntuples(result); i++)
3228 {
3229 const char *ruledef;
3230
3231 /* Everything after "CREATE RULE" is echoed verbatim */
3232 ruledef = PQgetvalue(result, i, 1);
3233 ruledef += 12;
3234
3235 printfPQExpBuffer(&buf, " %s", ruledef);
3236 printTableAddFooter(&cont, buf.data);
3237 }
3238 }
3239 PQclear(result);
3240 }
3241 }
3242
3243 /*
3244 * Print triggers next, if any (but only user-defined triggers). This
3245 * could apply to either a table or a view.
3246 */
3247 if (tableinfo.hastriggers)
3248 {
3249 PGresult *result;
3250 int tuples;
3251
3253 "SELECT t.tgname, "
3254 "pg_catalog.pg_get_triggerdef(t.oid, true), "
3255 "t.tgenabled, t.tgisinternal,\n");
3256
3257 /*
3258 * Detect whether each trigger is inherited, and if so, get the name
3259 * of the topmost table it's inherited from. We have no easy way to
3260 * do that pre-v13, for lack of the tgparentid column. Even with
3261 * tgparentid, a straightforward search for the topmost parent would
3262 * require a recursive CTE, which seems unduly expensive. We cheat a
3263 * bit by assuming parent triggers will match by tgname; then, joining
3264 * with pg_partition_ancestors() allows the planner to make use of
3265 * pg_trigger_tgrelid_tgname_index if it wishes. We ensure we find
3266 * the correct topmost parent by stopping at the first-in-partition-
3267 * ancestry-order trigger that has tgparentid = 0. (There might be
3268 * unrelated, non-inherited triggers with the same name further up the
3269 * stack, so this is important.)
3270 */
3271 if (pset.sversion >= 130000)
3273 " CASE WHEN t.tgparentid != 0 THEN\n"
3274 " (SELECT u.tgrelid::pg_catalog.regclass\n"
3275 " FROM pg_catalog.pg_trigger AS u,\n"
3276 " pg_catalog.pg_partition_ancestors(t.tgrelid) WITH ORDINALITY AS a(relid, depth)\n"
3277 " WHERE u.tgname = t.tgname AND u.tgrelid = a.relid\n"
3278 " AND u.tgparentid = 0\n"
3279 " ORDER BY a.depth LIMIT 1)\n"
3280 " END AS parent\n");
3281 else
3282 appendPQExpBufferStr(&buf, " NULL AS parent\n");
3283
3285 "FROM pg_catalog.pg_trigger t\n"
3286 "WHERE t.tgrelid = '%s' AND ",
3287 oid);
3288
3289 /*
3290 * tgisinternal is set true for inherited triggers of partitions in
3291 * servers between v11 and v14, though these must still be shown to
3292 * the user. So we use another property that is true for such
3293 * inherited triggers to avoid them being hidden, which is their
3294 * dependence on another trigger.
3295 */
3296 if (pset.sversion >= 110000 && pset.sversion < 150000)
3297 appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n"
3298 " OR EXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n"
3299 " AND refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass))");
3300 else
3301 /* display/warn about disabled internal triggers */
3302 appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))");
3303 appendPQExpBufferStr(&buf, "\nORDER BY 1;");
3304
3305 result = PSQLexec(buf.data);
3306 if (!result)
3307 goto error_return;
3308 else
3309 tuples = PQntuples(result);
3310
3311 if (tuples > 0)
3312 {
3313 bool have_heading;
3314 int category;
3315
3316 /*
3317 * split the output into 4 different categories. Enabled triggers,
3318 * disabled triggers and the two special ALWAYS and REPLICA
3319 * configurations.
3320 */
3321 for (category = 0; category <= 4; category++)
3322 {
3323 have_heading = false;
3324 for (i = 0; i < tuples; i++)
3325 {
3326 bool list_trigger;
3327 const char *tgdef;
3328 const char *usingpos;
3329 const char *tgenabled;
3330 const char *tgisinternal;
3331
3332 /*
3333 * Check if this trigger falls into the current category
3334 */
3335 tgenabled = PQgetvalue(result, i, 2);
3336 tgisinternal = PQgetvalue(result, i, 3);
3337 list_trigger = false;
3338 switch (category)
3339 {
3340 case 0:
3341 if (*tgenabled == 'O' || *tgenabled == 't')
3342 list_trigger = true;
3343 break;
3344 case 1:
3345 if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3346 *tgisinternal == 'f')
3347 list_trigger = true;
3348 break;
3349 case 2:
3350 if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3351 *tgisinternal == 't')
3352 list_trigger = true;
3353 break;
3354 case 3:
3355 if (*tgenabled == 'A')
3356 list_trigger = true;
3357 break;
3358 case 4:
3359 if (*tgenabled == 'R')
3360 list_trigger = true;
3361 break;
3362 }
3363 if (list_trigger == false)
3364 continue;
3365
3366 /* Print the category heading once */
3367 if (have_heading == false)
3368 {
3369 switch (category)
3370 {
3371 case 0:
3372 printfPQExpBuffer(&buf, _("Triggers:"));
3373 break;
3374 case 1:
3375 printfPQExpBuffer(&buf, _("Disabled user triggers:"));
3376 break;
3377 case 2:
3378 printfPQExpBuffer(&buf, _("Disabled internal triggers:"));
3379 break;
3380 case 3:
3381 printfPQExpBuffer(&buf, _("Triggers firing always:"));
3382 break;
3383 case 4:
3384 printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
3385 break;
3386 }
3387 printTableAddFooter(&cont, buf.data);
3388 have_heading = true;
3389 }
3390
3391 /* Everything after "TRIGGER" is echoed verbatim */
3392 tgdef = PQgetvalue(result, i, 1);
3393 usingpos = strstr(tgdef, " TRIGGER ");
3394 if (usingpos)
3395 tgdef = usingpos + 9;
3396
3397 printfPQExpBuffer(&buf, " %s", tgdef);
3398
3399 /* Visually distinguish inherited triggers */
3400 if (!PQgetisnull(result, i, 4))
3401 appendPQExpBuffer(&buf, ", ON TABLE %s",
3402 PQgetvalue(result, i, 4));
3403
3404 printTableAddFooter(&cont, buf.data);
3405 }
3406 }
3407 }
3408 PQclear(result);
3409 }
3410
3411 /*
3412 * Finish printing the footer information about a table.
3413 */
3414 if (tableinfo.relkind == RELKIND_RELATION ||
3415 tableinfo.relkind == RELKIND_MATVIEW ||
3416 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
3417 tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3418 tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
3419 tableinfo.relkind == RELKIND_TOASTVALUE)
3420 {
3421 bool is_partitioned;
3422 PGresult *result;
3423 int tuples;
3424
3425 /* simplify some repeated tests below */
3426 is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3427 tableinfo.relkind == RELKIND_PARTITIONED_INDEX);
3428
3429 /* print foreign server name */
3430 if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
3431 {
3432 char *ftoptions;
3433
3434 /* Footer information about foreign table */
3436 "SELECT s.srvname,\n"
3437 " pg_catalog.array_to_string(ARRAY(\n"
3438 " SELECT pg_catalog.quote_ident(option_name)"
3439 " || ' ' || pg_catalog.quote_literal(option_value)\n"
3440 " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ')\n"
3441 "FROM pg_catalog.pg_foreign_table f,\n"
3442 " pg_catalog.pg_foreign_server s\n"
3443 "WHERE f.ftrelid = '%s' AND s.oid = f.ftserver;",
3444 oid);
3445 result = PSQLexec(buf.data);
3446 if (!result)
3447 goto error_return;
3448 else if (PQntuples(result) != 1)
3449 {
3450 PQclear(result);
3451 goto error_return;
3452 }
3453
3454 /* Print server name */
3455 printfPQExpBuffer(&buf, _("Server: %s"),
3456 PQgetvalue(result, 0, 0));
3457 printTableAddFooter(&cont, buf.data);
3458
3459 /* Print per-table FDW options, if any */
3460 ftoptions = PQgetvalue(result, 0, 1);
3461 if (ftoptions && ftoptions[0] != '\0')
3462 {
3463 printfPQExpBuffer(&buf, _("FDW options: (%s)"), ftoptions);
3464 printTableAddFooter(&cont, buf.data);
3465 }
3466 PQclear(result);
3467 }
3468
3469 /* print tables inherited from (exclude partitioned parents) */
3471 "SELECT c.oid::pg_catalog.regclass\n"
3472 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3473 "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n"
3474 " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE)
3475 " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX)
3476 "\nORDER BY inhseqno;",
3477 oid);
3478
3479 result = PSQLexec(buf.data);
3480 if (!result)
3481 goto error_return;
3482 else
3483 {
3484 const char *s = _("Inherits");
3485 int sw = pg_wcswidth(s, strlen(s), pset.encoding);
3486
3487 tuples = PQntuples(result);
3488
3489 for (i = 0; i < tuples; i++)
3490 {
3491 if (i == 0)
3492 printfPQExpBuffer(&buf, "%s: %s",
3493 s, PQgetvalue(result, i, 0));
3494 else
3495 printfPQExpBuffer(&buf, "%*s %s",
3496 sw, "", PQgetvalue(result, i, 0));
3497 if (i < tuples - 1)
3499
3500 printTableAddFooter(&cont, buf.data);
3501 }
3502
3503 PQclear(result);
3504 }
3505
3506 /* print child tables (with additional info if partitions) */
3507 if (pset.sversion >= 140000)
3509 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3510 " inhdetachpending,"
3511 " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3512 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3513 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3514 "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3515 " c.oid::pg_catalog.regclass::pg_catalog.text;",
3516 oid);
3517 else if (pset.sversion >= 100000)
3519 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3520 " false AS inhdetachpending,"
3521 " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3522 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3523 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3524 "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3525 " c.oid::pg_catalog.regclass::pg_catalog.text;",
3526 oid);
3527 else
3529 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3530 " false AS inhdetachpending, NULL\n"
3531 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3532 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3533 "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;",
3534 oid);
3535
3536 result = PSQLexec(buf.data);
3537 if (!result)
3538 goto error_return;
3539 tuples = PQntuples(result);
3540
3541 /*
3542 * For a partitioned table with no partitions, always print the number
3543 * of partitions as zero, even when verbose output is expected.
3544 * Otherwise, we will not print "Partitions" section for a partitioned
3545 * table without any partitions.
3546 */
3547 if (is_partitioned && tuples == 0)
3548 {
3549 printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples);
3550 printTableAddFooter(&cont, buf.data);
3551 }
3552 else if (!verbose)
3553 {
3554 /* print the number of child tables, if any */
3555 if (tuples > 0)
3556 {
3557 if (is_partitioned)
3558 printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
3559 else
3560 printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
3561 printTableAddFooter(&cont, buf.data);
3562 }
3563 }
3564 else
3565 {
3566 /* display the list of child tables */
3567 const char *ct = is_partitioned ? _("Partitions") : _("Child tables");
3568 int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding);
3569
3570 for (i = 0; i < tuples; i++)
3571 {
3572 char child_relkind = *PQgetvalue(result, i, 1);
3573
3574 if (i == 0)
3575 printfPQExpBuffer(&buf, "%s: %s",
3576 ct, PQgetvalue(result, i, 0));
3577 else
3578 printfPQExpBuffer(&buf, "%*s %s",
3579 ctw, "", PQgetvalue(result, i, 0));
3580 if (!PQgetisnull(result, i, 3))
3581 appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 3));
3582 if (child_relkind == RELKIND_PARTITIONED_TABLE ||
3583 child_relkind == RELKIND_PARTITIONED_INDEX)
3584 appendPQExpBufferStr(&buf, ", PARTITIONED");
3585 else if (child_relkind == RELKIND_FOREIGN_TABLE)
3586 appendPQExpBufferStr(&buf, ", FOREIGN");
3587 if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
3588 appendPQExpBufferStr(&buf, " (DETACH PENDING)");
3589 if (i < tuples - 1)
3591
3592 printTableAddFooter(&cont, buf.data);
3593 }
3594 }
3595 PQclear(result);
3596
3597 /* Table type */
3598 if (tableinfo.reloftype)
3599 {
3600 printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
3601 printTableAddFooter(&cont, buf.data);
3602 }
3603
3604 if (verbose &&
3605 (tableinfo.relkind == RELKIND_RELATION ||
3606 tableinfo.relkind == RELKIND_MATVIEW) &&
3607
3608 /*
3609 * No need to display default values; we already display a REPLICA
3610 * IDENTITY marker on indexes.
3611 */
3612 tableinfo.relreplident != REPLICA_IDENTITY_INDEX &&
3613 ((strcmp(schemaname, "pg_catalog") != 0 &&
3614 tableinfo.relreplident != REPLICA_IDENTITY_DEFAULT) ||
3615 (strcmp(schemaname, "pg_catalog") == 0 &&
3616 tableinfo.relreplident != REPLICA_IDENTITY_NOTHING)))
3617 {
3618 const char *s = _("Replica Identity");
3619
3620 printfPQExpBuffer(&buf, "%s: %s",
3621 s,
3622 tableinfo.relreplident == REPLICA_IDENTITY_FULL ? "FULL" :
3623 tableinfo.relreplident == REPLICA_IDENTITY_DEFAULT ? "NOTHING" :
3624 "???");
3625
3626 printTableAddFooter(&cont, buf.data);
3627 }
3628
3629 /* OIDs, if verbose and not a materialized view */
3630 if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids)
3631 printTableAddFooter(&cont, _("Has OIDs: yes"));
3632
3633 /* Tablespace info */
3634 add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
3635 true);
3636
3637 /* Access method info */
3638 if (verbose && tableinfo.relam != NULL && !pset.hide_tableam)
3639 {
3640 printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam);
3641 printTableAddFooter(&cont, buf.data);
3642 }
3643 }
3644
3645 /* reloptions, if verbose */
3646 if (verbose &&
3647 tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
3648 {
3649 const char *t = _("Options");
3650
3651 printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions);
3652 printTableAddFooter(&cont, buf.data);
3653 }
3654
3655 printTable(&cont, pset.queryFout, false, pset.logfile);
3656
3657 retval = true;
3658
3659error_return:
3660
3661 /* clean up */
3662 if (printTableInitialized)
3663 printTableCleanup(&cont);
3665 termPQExpBuffer(&title);
3667
3668 free(view_def);
3669
3670 PQclear(res);
3671
3672 return retval;
3673}
3674
3675/*
3676 * Add a tablespace description to a footer. If 'newline' is true, it is added
3677 * in a new line; otherwise it's appended to the current value of the last
3678 * footer.
3679 */
3680static void
3682 Oid tablespace, const bool newline)
3683{
3684 /* relkinds for which we support tablespaces */
3685 if (relkind == RELKIND_RELATION ||
3686 relkind == RELKIND_MATVIEW ||
3687 relkind == RELKIND_INDEX ||
3688 relkind == RELKIND_PARTITIONED_TABLE ||
3689 relkind == RELKIND_PARTITIONED_INDEX ||
3690 relkind == RELKIND_TOASTVALUE)
3691 {
3692 /*
3693 * We ignore the database default tablespace so that users not using
3694 * tablespaces don't need to know about them.
3695 */
3696 if (tablespace != 0)
3697 {
3698 PGresult *result = NULL;
3700
3703 "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3704 "WHERE oid = '%u';", tablespace);
3705 result = PSQLexec(buf.data);
3706 if (!result)
3707 {
3709 return;
3710 }
3711 /* Should always be the case, but.... */
3712 if (PQntuples(result) > 0)
3713 {
3714 if (newline)
3715 {
3716 /* Add the tablespace as a new footer */
3717 printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3718 PQgetvalue(result, 0, 0));
3719 printTableAddFooter(cont, buf.data);
3720 }
3721 else
3722 {
3723 /* Append the tablespace to the latest footer */
3724 printfPQExpBuffer(&buf, "%s", cont->footer->data);
3725
3726 /*-------
3727 translator: before this string there's an index description like
3728 '"foo_pkey" PRIMARY KEY, btree (a)' */
3729 appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3730 PQgetvalue(result, 0, 0));
3731 printTableSetFooter(cont, buf.data);
3732 }
3733 }
3734 PQclear(result);
3736 }
3737 }
3738}
3739
3740/*
3741 * \du or \dg
3742 *
3743 * Describes roles. Any schema portion of the pattern is ignored.
3744 */
3745bool
3746describeRoles(const char *pattern, bool verbose, bool showSystem)
3747{
3749 PGresult *res;
3750 printTableContent cont;
3751 printTableOpt myopt = pset.popt.topt;
3752 int ncols = 2;
3753 int nrows = 0;
3754 int i;
3755 int conns;
3756 const char align = 'l';
3757 char **attr;
3758
3759 myopt.default_footer = false;
3760
3762
3764 "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
3765 " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
3766 " r.rolconnlimit, r.rolvaliduntil");
3767
3768 if (verbose)
3769 {
3770 appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
3771 ncols++;
3772 }
3773 appendPQExpBufferStr(&buf, "\n, r.rolreplication");
3774
3775 if (pset.sversion >= 90500)
3776 {
3777 appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
3778 }
3779
3780 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
3781
3782 if (!showSystem && !pattern)
3783 appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
3784
3785 if (!validateSQLNamePattern(&buf, pattern, false, false,
3786 NULL, "r.rolname", NULL, NULL,
3787 NULL, 1))
3788 {
3790 return false;
3791 }
3792
3793 appendPQExpBufferStr(&buf, "ORDER BY 1;");
3794
3795 res = PSQLexec(buf.data);
3796 if (!res)
3797 return false;
3798
3799 nrows = PQntuples(res);
3800 attr = pg_malloc0((nrows + 1) * sizeof(*attr));
3801
3802 printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
3803
3804 printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
3805 printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
3806
3807 if (verbose)
3808 printTableAddHeader(&cont, gettext_noop("Description"), true, align);
3809
3810 for (i = 0; i < nrows; i++)
3811 {
3812 printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
3813
3815 if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
3816 add_role_attribute(&buf, _("Superuser"));
3817
3818 if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
3819 add_role_attribute(&buf, _("No inheritance"));
3820
3821 if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
3822 add_role_attribute(&buf, _("Create role"));
3823
3824 if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
3825 add_role_attribute(&buf, _("Create DB"));
3826
3827 if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
3828 add_role_attribute(&buf, _("Cannot login"));
3829
3830 if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
3831 add_role_attribute(&buf, _("Replication"));
3832
3833 if (pset.sversion >= 90500)
3834 if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
3835 add_role_attribute(&buf, _("Bypass RLS"));
3836
3837 conns = atoi(PQgetvalue(res, i, 6));
3838 if (conns >= 0)
3839 {
3840 if (buf.len > 0)
3841 appendPQExpBufferChar(&buf, '\n');
3842
3843 if (conns == 0)
3844 appendPQExpBufferStr(&buf, _("No connections"));
3845 else
3846 appendPQExpBuffer(&buf, ngettext("%d connection",
3847 "%d connections",
3848 conns),
3849 conns);
3850 }
3851
3852 if (strcmp(PQgetvalue(res, i, 7), "") != 0)
3853 {
3854 if (buf.len > 0)
3855 appendPQExpBufferChar(&buf, '\n');
3856 appendPQExpBufferStr(&buf, _("Password valid until "));
3858 }
3859
3860 attr[i] = pg_strdup(buf.data);
3861
3862 printTableAddCell(&cont, attr[i], false, false);
3863
3864 if (verbose)
3865 printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
3866 }
3868
3869 printTable(&cont, pset.queryFout, false, pset.logfile);
3870 printTableCleanup(&cont);
3871
3872 for (i = 0; i < nrows; i++)
3873 free(attr[i]);
3874 free(attr);
3875
3876 PQclear(res);
3877 return true;
3878}
3879
3880static void
3882{
3883 if (buf->len > 0)
3885
3887}
3888
3889/*
3890 * \drds
3891 */
3892bool
3893listDbRoleSettings(const char *pattern, const char *pattern2)
3894{
3896 PGresult *res;
3897 printQueryOpt myopt = pset.popt;
3898 bool havewhere;
3899
3901
3902 printfPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
3903 "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
3904 "FROM pg_catalog.pg_db_role_setting s\n"
3905 "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n"
3906 "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n",
3907 gettext_noop("Role"),
3908 gettext_noop("Database"),
3909 gettext_noop("Settings"));
3910 if (!validateSQLNamePattern(&buf, pattern, false, false,
3911 NULL, "r.rolname", NULL, NULL, &havewhere, 1))
3912 goto error_return;
3913 if (!validateSQLNamePattern(&buf, pattern2, havewhere, false,
3914 NULL, "d.datname", NULL, NULL,
3915 NULL, 1))
3916 goto error_return;
3917 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3918
3919 res = PSQLexec(buf.data);
3921 if (!res)
3922 return false;
3923
3924 /*
3925 * Most functions in this file are content to print an empty table when
3926 * there are no matching objects. We intentionally deviate from that
3927 * here, but only in !quiet mode, because of the possibility that the user
3928 * is confused about what the two pattern arguments mean.
3929 */
3930 if (PQntuples(res) == 0 && !pset.quiet)
3931 {
3932 if (pattern && pattern2)
3933 pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".",
3934 pattern, pattern2);
3935 else if (pattern)
3936 pg_log_error("Did not find any settings for role \"%s\".",
3937 pattern);
3938 else
3939 pg_log_error("Did not find any settings.");
3940 }
3941 else
3942 {
3943 myopt.title = _("List of settings");
3944 myopt.translate_header = true;
3945
3946 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3947 }
3948
3949 PQclear(res);
3950 return true;
3951
3952error_return:
3954 return false;
3955}
3956
3957/*
3958 * \drg
3959 * Describes role grants.
3960 */
3961bool
3962describeRoleGrants(const char *pattern, bool showSystem)
3963{
3965 PGresult *res;
3966 printQueryOpt myopt = pset.popt;
3967
3970 "SELECT m.rolname AS \"%s\", r.rolname AS \"%s\",\n"
3971 " pg_catalog.concat_ws(', ',\n",
3972 gettext_noop("Role name"),
3973 gettext_noop("Member of"));
3974
3975 if (pset.sversion >= 160000)
3977 " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
3978 " CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n"
3979 " CASE WHEN pam.set_option THEN 'SET' END\n");
3980 else
3982 " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
3983 " CASE WHEN m.rolinherit THEN 'INHERIT' END,\n"
3984 " 'SET'\n");
3985
3987 " ) AS \"%s\",\n"
3988 " g.rolname AS \"%s\"\n",
3989 gettext_noop("Options"),
3990 gettext_noop("Grantor"));
3991
3993 "FROM pg_catalog.pg_roles m\n"
3994 " JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n"
3995 " LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n"
3996 " LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n");
3997
3998 if (!showSystem && !pattern)
3999 appendPQExpBufferStr(&buf, "WHERE m.rolname !~ '^pg_'\n");
4000
4001 if (!validateSQLNamePattern(&buf, pattern, false, false,
4002 NULL, "m.rolname", NULL, NULL,
4003 NULL, 1))
4004 {
4006 return false;
4007 }
4008
4009 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;\n");
4010
4011 res = PSQLexec(buf.data);
4013 if (!res)
4014 return false;
4015
4016 myopt.title = _("List of role grants");
4017 myopt.translate_header = true;
4018
4019 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4020
4021 PQclear(res);
4022 return true;
4023}
4024
4025
4026/*
4027 * listTables()
4028 *
4029 * handler for \dt, \di, etc.
4030 *
4031 * tabtypes is an array of characters, specifying what info is desired:
4032 * t - tables
4033 * i - indexes
4034 * v - views
4035 * m - materialized views
4036 * s - sequences
4037 * E - foreign table (Note: different from 'f', the relkind value)
4038 * (any order of the above is fine)
4039 */
4040bool
4041listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
4042{
4043 bool showTables = strchr(tabtypes, 't') != NULL;
4044 bool showIndexes = strchr(tabtypes, 'i') != NULL;
4045 bool showViews = strchr(tabtypes, 'v') != NULL;
4046 bool showMatViews = strchr(tabtypes, 'm') != NULL;
4047 bool showSeq = strchr(tabtypes, 's') != NULL;
4048 bool showForeign = strchr(tabtypes, 'E') != NULL;
4049
4050 int ntypes;
4052 PGresult *res;
4053 printQueryOpt myopt = pset.popt;
4054 int cols_so_far;
4055 bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
4056
4057 /* Count the number of explicitly-requested relation types */
4058 ntypes = showTables + showIndexes + showViews + showMatViews +
4059 showSeq + showForeign;
4060 /* If none, we default to \dtvmsE (but see also command.c) */
4061 if (ntypes == 0)
4062 showTables = showViews = showMatViews = showSeq = showForeign = true;
4063
4065
4067 "SELECT n.nspname as \"%s\",\n"
4068 " c.relname as \"%s\",\n"
4069 " CASE c.relkind"
4070 " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
4071 " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'"
4072 " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
4073 " WHEN " CppAsString2(RELKIND_INDEX) " THEN '%s'"
4074 " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
4075 " WHEN " CppAsString2(RELKIND_TOASTVALUE) " THEN '%s'"
4076 " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
4077 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4078 " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4079 " END as \"%s\",\n"
4080 " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4081 gettext_noop("Schema"),
4082 gettext_noop("Name"),
4083 gettext_noop("table"),
4084 gettext_noop("view"),
4085 gettext_noop("materialized view"),
4086 gettext_noop("index"),
4087 gettext_noop("sequence"),
4088 gettext_noop("TOAST table"),
4089 gettext_noop("foreign table"),
4090 gettext_noop("partitioned table"),
4091 gettext_noop("partitioned index"),
4092 gettext_noop("Type"),
4093 gettext_noop("Owner"));
4094 cols_so_far = 4;
4095
4096 if (showIndexes)
4097 {
4099 ",\n c2.relname as \"%s\"",
4100 gettext_noop("Table"));
4101 cols_so_far++;
4102 }
4103
4104 if (verbose)
4105 {
4106 /*
4107 * Show whether a relation is permanent, temporary, or unlogged.
4108 */
4110 ",\n CASE c.relpersistence "
4111 "WHEN " CppAsString2(RELPERSISTENCE_PERMANENT) " THEN '%s' "
4112 "WHEN " CppAsString2(RELPERSISTENCE_TEMP) " THEN '%s' "
4113 "WHEN " CppAsString2(RELPERSISTENCE_UNLOGGED) " THEN '%s' "
4114 "END as \"%s\"",
4115 gettext_noop("permanent"),
4116 gettext_noop("temporary"),
4117 gettext_noop("unlogged"),
4118 gettext_noop("Persistence"));
4119 translate_columns[cols_so_far] = true;
4120
4121 /*
4122 * We don't bother to count cols_so_far below here, as there's no need
4123 * to; this might change with future additions to the output columns.
4124 */
4125
4126 /*
4127 * Access methods exist for tables, materialized views and indexes.
4128 * This has been introduced in PostgreSQL 12 for tables.
4129 */
4130 if (pset.sversion >= 120000 && !pset.hide_tableam &&
4131 (showTables || showMatViews || showIndexes))
4133 ",\n am.amname as \"%s\"",
4134 gettext_noop("Access method"));
4135
4137 ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\""
4138 ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4139 gettext_noop("Size"),
4140 gettext_noop("Description"));
4141 }
4142
4144 "\nFROM pg_catalog.pg_class c"
4145 "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4146
4147 if (pset.sversion >= 120000 && !pset.hide_tableam &&
4148 (showTables || showMatViews || showIndexes))
4150 "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam");
4151
4152 if (showIndexes)
4154 "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4155 "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4156
4157 appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4158 if (showTables)
4159 {
4160 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_RELATION) ","
4161 CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
4162 /* with 'S' or a pattern, allow 't' to match TOAST tables too */
4163 if (showSystem || pattern)
4164 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_TOASTVALUE) ",");
4165 }
4166 if (showViews)
4167 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_VIEW) ",");
4168 if (showMatViews)
4169 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_MATVIEW) ",");
4170 if (showIndexes)
4171 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_INDEX) ","
4172 CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
4173 if (showSeq)
4174 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_SEQUENCE) ",");
4175 if (showSystem || pattern)
4176 appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
4177 if (showForeign)
4178 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_FOREIGN_TABLE) ",");
4179
4180 appendPQExpBufferStr(&buf, "''"); /* dummy */
4181 appendPQExpBufferStr(&buf, ")\n");
4182
4183 if (!showSystem && !pattern)
4184 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4185 " AND n.nspname !~ '^pg_toast'\n"
4186 " AND n.nspname <> 'information_schema'\n");
4187
4188 if (!validateSQLNamePattern(&buf, pattern, true, false,
4189 "n.nspname", "c.relname", NULL,
4190 "pg_catalog.pg_table_is_visible(c.oid)",
4191 NULL, 3))
4192 {
4194 return false;
4195 }
4196
4197 appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
4198
4199 res = PSQLexec(buf.data);
4201 if (!res)
4202 return false;
4203
4204 /*
4205 * Most functions in this file are content to print an empty table when
4206 * there are no matching objects. We intentionally deviate from that
4207 * here, but only in !quiet mode, for historical reasons.
4208 */
4209 if (PQntuples(res) == 0 && !pset.quiet)
4210 {
4211 if (pattern)
4212 {
4213 if (ntypes != 1)
4214 pg_log_error("Did not find any relations named \"%s\".",
4215 pattern);
4216 else if (showTables)
4217 pg_log_error("Did not find any tables named \"%s\".",
4218 pattern);
4219 else if (showIndexes)
4220 pg_log_error("Did not find any indexes named \"%s\".",
4221 pattern);
4222 else if (showViews)
4223 pg_log_error("Did not find any views named \"%s\".",
4224 pattern);
4225 else if (showMatViews)
4226 pg_log_error("Did not find any materialized views named \"%s\".",
4227 pattern);
4228 else if (showSeq)
4229 pg_log_error("Did not find any sequences named \"%s\".",
4230 pattern);
4231 else if (showForeign)
4232 pg_log_error("Did not find any foreign tables named \"%s\".",
4233 pattern);
4234 else /* should not get here */
4235 pg_log_error_internal("Did not find any ??? named \"%s\".",
4236 pattern);
4237 }
4238 else
4239 {
4240 if (ntypes != 1)
4241 pg_log_error("Did not find any relations.");
4242 else if (showTables)
4243 pg_log_error("Did not find any tables.");
4244 else if (showIndexes)
4245 pg_log_error("Did not find any indexes.");
4246 else if (showViews)
4247 pg_log_error("Did not find any views.");
4248 else if (showMatViews)
4249 pg_log_error("Did not find any materialized views.");
4250 else if (showSeq)
4251 pg_log_error("Did not find any sequences.");
4252 else if (showForeign)
4253 pg_log_error("Did not find any foreign tables.");
4254 else /* should not get here */
4255 pg_log_error_internal("Did not find any ??? relations.");
4256 }
4257 }
4258 else
4259 {
4260 myopt.title =
4261 (ntypes != 1) ? _("List of relations") :
4262 (showTables) ? _("List of tables") :
4263 (showIndexes) ? _("List of indexes") :
4264 (showViews) ? _("List of views") :
4265 (showMatViews) ? _("List of materialized views") :
4266 (showSeq) ? _("List of sequences") :
4267 (showForeign) ? _("List of foreign tables") :
4268 "List of ???"; /* should not get here */
4269 myopt.translate_header = true;
4270 myopt.translate_columns = translate_columns;
4271 myopt.n_translate_columns = lengthof(translate_columns);
4272
4273 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4274 }
4275
4276 PQclear(res);
4277 return true;
4278}
4279
4280/*
4281 * \dP
4282 * Takes an optional regexp to select particular relations
4283 *
4284 * As with \d, you can specify the kinds of relations you want:
4285 *
4286 * t for tables
4287 * i for indexes
4288 *
4289 * And there's additional flags:
4290 *
4291 * n to list non-leaf partitioned tables
4292 *
4293 * and you can mix and match these in any order.
4294 */
4295bool
4296listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
4297{
4298 bool showTables = strchr(reltypes, 't') != NULL;
4299 bool showIndexes = strchr(reltypes, 'i') != NULL;
4300 bool showNested = strchr(reltypes, 'n') != NULL;
4302 PQExpBufferData title;
4303 PGresult *res;
4304 printQueryOpt myopt = pset.popt;
4305 bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
4306 const char *tabletitle;
4307 bool mixed_output = false;
4308
4309 /*
4310 * Note: Declarative table partitioning is only supported as of Pg 10.0.
4311 */
4312 if (pset.sversion < 100000)
4313 {
4314 char sverbuf[32];
4315
4316 pg_log_error("The server (version %s) does not support declarative table partitioning.",
4318 sverbuf, sizeof(sverbuf)));
4319 return true;
4320 }
4321
4322 /* If no relation kind was selected, show them all */
4323 if (!showTables && !showIndexes)
4324 showTables = showIndexes = true;
4325
4326 if (showIndexes && !showTables)
4327 tabletitle = _("List of partitioned indexes"); /* \dPi */
4328 else if (showTables && !showIndexes)
4329 tabletitle = _("List of partitioned tables"); /* \dPt */
4330 else
4331 {
4332 /* show all kinds */
4333 tabletitle = _("List of partitioned relations");
4334 mixed_output = true;
4335 }
4336
4338
4340 "SELECT n.nspname as \"%s\",\n"
4341 " c.relname as \"%s\",\n"
4342 " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4343 gettext_noop("Schema"),
4344 gettext_noop("Name"),
4345 gettext_noop("Owner"));
4346
4347 if (mixed_output)
4348 {
4350 ",\n CASE c.relkind"
4351 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4352 " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4353 " END as \"%s\"",
4354 gettext_noop("partitioned table"),
4355 gettext_noop("partitioned index"),
4356 gettext_noop("Type"));
4357
4358 translate_columns[3] = true;
4359 }
4360
4361 if (showNested || pattern)
4363 ",\n inh.inhparent::pg_catalog.regclass as \"%s\"",
4364 gettext_noop("Parent name"));
4365
4366 if (showIndexes)
4368 ",\n c2.oid::pg_catalog.regclass as \"%s\"",
4369 gettext_noop("Table"));
4370
4371 if (verbose)
4372 {
4373 /*
4374 * Table access methods were introduced in v12, and can be set on
4375 * partitioned tables since v17.
4376 */
4377 appendPQExpBuffer(&buf, ",\n am.amname as \"%s\"",
4378 gettext_noop("Access method"));
4379
4380 if (showNested)
4381 {
4383 ",\n s.dps as \"%s\"",
4384 gettext_noop("Leaf partition size"));
4386 ",\n s.tps as \"%s\"",
4387 gettext_noop("Total size"));
4388 }
4389 else
4390 /* Sizes of all partitions are considered in this case. */
4392 ",\n s.tps as \"%s\"",
4393 gettext_noop("Total size"));
4394
4396 ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4397 gettext_noop("Description"));
4398 }
4399
4401 "\nFROM pg_catalog.pg_class c"
4402 "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4403
4404 if (showIndexes)
4406 "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4407 "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4408
4409 if (showNested || pattern)
4411 "\n LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid");
4412
4413 if (verbose)
4414 {
4416 "\n LEFT JOIN pg_catalog.pg_am am ON c.relam = am.oid");
4417
4418 if (pset.sversion < 120000)
4419 {
4421 ",\n LATERAL (WITH RECURSIVE d\n"
4422 " AS (SELECT inhrelid AS oid, 1 AS level\n"
4423 " FROM pg_catalog.pg_inherits\n"
4424 " WHERE inhparent = c.oid\n"
4425 " UNION ALL\n"
4426 " SELECT inhrelid, level + 1\n"
4427 " FROM pg_catalog.pg_inherits i\n"
4428 " JOIN d ON i.inhparent = d.oid)\n"
4429 " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
4430 "d.oid))) AS tps,\n"
4431 " pg_catalog.pg_size_pretty(sum("
4432 "\n CASE WHEN d.level = 1"
4433 " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n"
4434 " FROM d) s");
4435 }
4436 else
4437 {
4438 /* PostgreSQL 12 has pg_partition_tree function */
4440 ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
4441 "\n CASE WHEN ppt.isleaf AND ppt.level = 1"
4442 "\n THEN pg_catalog.pg_table_size(ppt.relid)"
4443 " ELSE 0 END)) AS dps"
4444 ",\n pg_catalog.pg_size_pretty(sum("
4445 "pg_catalog.pg_table_size(ppt.relid))) AS tps"
4446 "\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s");
4447 }
4448 }
4449
4450 appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4451 if (showTables)
4452 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
4453 if (showIndexes)
4454 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
4455 appendPQExpBufferStr(&buf, "''"); /* dummy */
4456 appendPQExpBufferStr(&buf, ")\n");
4457
4458 appendPQExpBufferStr(&buf, !showNested && !pattern ?
4459 " AND NOT c.relispartition\n" : "");
4460
4461 if (!pattern)
4462 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4463 " AND n.nspname !~ '^pg_toast'\n"
4464 " AND n.nspname <> 'information_schema'\n");
4465
4466 if (!validateSQLNamePattern(&buf, pattern, true, false,
4467 "n.nspname", "c.relname", NULL,
4468 "pg_catalog.pg_table_is_visible(c.oid)",
4469 NULL, 3))
4470 {
4472 return false;
4473 }
4474
4475 appendPQExpBuffer(&buf, "ORDER BY \"Schema\", %s%s\"Name\";",
4476 mixed_output ? "\"Type\" DESC, " : "",
4477 showNested || pattern ? "\"Parent name\" NULLS FIRST, " : "");
4478
4479 res = PSQLexec(buf.data);
4481 if (!res)
4482 return false;
4483
4484 initPQExpBuffer(&title);
4485 appendPQExpBufferStr(&title, tabletitle);
4486
4487 myopt.title = title.data;
4488 myopt.translate_header = true;
4489 myopt.translate_columns = translate_columns;
4490 myopt.n_translate_columns = lengthof(translate_columns);
4491
4492 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4493
4494 termPQExpBuffer(&title);
4495
4496 PQclear(res);
4497 return true;
4498}
4499
4500/*
4501 * \dL
4502 *
4503 * Describes languages.
4504 */
4505bool
4506listLanguages(const char *pattern, bool verbose, bool showSystem)
4507{
4509 PGresult *res;
4510 printQueryOpt myopt = pset.popt;
4511
4513
4515 "SELECT l.lanname AS \"%s\",\n"
4516 " pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n"
4517 " l.lanpltrusted AS \"%s\"",
4518 gettext_noop("Name"),
4519 gettext_noop("Owner"),
4520 gettext_noop("Trusted"));
4521
4522 if (verbose)
4523 {
4525 ",\n NOT l.lanispl AS \"%s\",\n"
4526 " l.lanplcallfoid::pg_catalog.regprocedure AS \"%s\",\n"
4527 " l.lanvalidator::pg_catalog.regprocedure AS \"%s\",\n "
4528 "l.laninline::pg_catalog.regprocedure AS \"%s\",\n ",
4529 gettext_noop("Internal language"),
4530 gettext_noop("Call handler"),
4531 gettext_noop("Validator"),
4532 gettext_noop("Inline handler"));
4533 printACLColumn(&buf, "l.lanacl");
4534 }
4535
4537 ",\n d.description AS \"%s\""
4538 "\nFROM pg_catalog.pg_language l\n"
4539 "LEFT JOIN pg_catalog.pg_description d\n"
4540 " ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
4541 " AND d.objsubid = 0\n",
4542 gettext_noop("Description"));
4543
4544 if (pattern)
4545 {
4546 if (!validateSQLNamePattern(&buf, pattern, false, false,
4547 NULL, "l.lanname", NULL, NULL,
4548 NULL, 2))
4549 {
4551 return false;
4552 }
4553 }
4554
4555 if (!showSystem && !pattern)
4556 appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n");
4557
4558
4559 appendPQExpBufferStr(&buf, "ORDER BY 1;");
4560
4561 res = PSQLexec(buf.data);
4563 if (!res)
4564 return false;
4565
4566 myopt.title = _("List of languages");
4567 myopt.translate_header = true;
4568
4569 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4570
4571 PQclear(res);
4572 return true;
4573}
4574
4575
4576/*
4577 * \dD
4578 *
4579 * Describes domains.
4580 */
4581bool
4582listDomains(const char *pattern, bool verbose, bool showSystem)
4583{
4585 PGresult *res;
4586 printQueryOpt myopt = pset.popt;
4587
4589
4591 "SELECT n.nspname as \"%s\",\n"
4592 " t.typname as \"%s\",\n"
4593 " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
4594 " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
4595 " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4596 " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4597 " t.typdefault as \"%s\",\n"
4598 " pg_catalog.array_to_string(ARRAY(\n"
4599 " 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"
4600 " ), ' ') as \"%s\"",
4601 gettext_noop("Schema"),
4602 gettext_noop("Name"),
4603 gettext_noop("Type"),
4604 gettext_noop("Collation"),
4605 gettext_noop("Nullable"),
4606 gettext_noop("Default"),
4607 gettext_noop("Check"));
4608
4609 if (verbose)
4610 {
4611 appendPQExpBufferStr(&buf, ",\n ");
4612 printACLColumn(&buf, "t.typacl");
4614 ",\n d.description as \"%s\"",
4615 gettext_noop("Description"));
4616 }
4617
4619 "\nFROM pg_catalog.pg_type t\n"
4620 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
4621
4622 if (verbose)
4624 " LEFT JOIN pg_catalog.pg_description d "
4625 "ON d.classoid = t.tableoid AND d.objoid = t.oid "
4626 "AND d.objsubid = 0\n");
4627
4628 appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
4629
4630 if (!showSystem && !pattern)
4631 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4632 " AND n.nspname <> 'information_schema'\n");
4633
4634 if (!validateSQLNamePattern(&buf, pattern, true, false,
4635 "n.nspname", "t.typname", NULL,
4636 "pg_catalog.pg_type_is_visible(t.oid)",
4637 NULL, 3))
4638 {
4640 return false;
4641 }
4642
4643 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4644
4645 res = PSQLexec(buf.data);
4647 if (!res)
4648 return false;
4649
4650 myopt.title = _("List of domains");
4651 myopt.translate_header = true;
4652
4653 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4654
4655 PQclear(res);
4656 return true;
4657}
4658
4659/*
4660 * \dc
4661 *
4662 * Describes conversions.
4663 */
4664bool
4665listConversions(const char *pattern, bool verbose, bool showSystem)
4666{
4668 PGresult *res;
4669 printQueryOpt myopt = pset.popt;
4670 static const bool translate_columns[] =
4671 {false, false, false, false, true, false};
4672
4674
4676 "SELECT n.nspname AS \"%s\",\n"
4677 " c.conname AS \"%s\",\n"
4678 " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
4679 " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
4680 " CASE WHEN c.condefault THEN '%s'\n"
4681 " ELSE '%s' END AS \"%s\"",
4682 gettext_noop("Schema"),
4683 gettext_noop("Name"),
4684 gettext_noop("Source"),
4685 gettext_noop("Destination"),
4686 gettext_noop("yes"), gettext_noop("no"),
4687 gettext_noop("Default?"));
4688
4689 if (verbose)
4691 ",\n d.description AS \"%s\"",
4692 gettext_noop("Description"));
4693
4695 "\nFROM pg_catalog.pg_conversion c\n"
4696 " JOIN pg_catalog.pg_namespace n "
4697 "ON n.oid = c.connamespace\n");
4698
4699 if (verbose)
4701 "LEFT JOIN pg_catalog.pg_description d "
4702 "ON d.classoid = c.tableoid\n"
4703 " AND d.objoid = c.oid "
4704 "AND d.objsubid = 0\n");
4705
4706 appendPQExpBufferStr(&buf, "WHERE true\n");
4707
4708 if (!showSystem && !pattern)
4709 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4710 " AND n.nspname <> 'information_schema'\n");
4711
4712 if (!validateSQLNamePattern(&buf, pattern, true, false,
4713 "n.nspname", "c.conname", NULL,
4714 "pg_catalog.pg_conversion_is_visible(c.oid)",
4715 NULL, 3))
4716 {
4718 return false;
4719 }
4720
4721 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4722
4723 res = PSQLexec(buf.data);
4725 if (!res)
4726 return false;
4727
4728 myopt.title = _("List of conversions");
4729 myopt.translate_header = true;
4730 myopt.translate_columns = translate_columns;
4731 myopt.n_translate_columns = lengthof(translate_columns);
4732
4733 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4734
4735 PQclear(res);
4736 return true;
4737}
4738
4739/*
4740 * \dconfig
4741 *
4742 * Describes configuration parameters.
4743 */
4744bool
4746 bool showSystem)
4747{
4749 PGresult *res;
4750 printQueryOpt myopt = pset.popt;
4751
4754 "SELECT s.name AS \"%s\", "
4755 "pg_catalog.current_setting(s.name) AS \"%s\"",
4756 gettext_noop("Parameter"),
4757 gettext_noop("Value"));
4758
4759 if (verbose)
4760 {
4762 ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4763 gettext_noop("Type"),
4764 gettext_noop("Context"));
4765 if (pset.sversion >= 150000)
4766 printACLColumn(&buf, "p.paracl");
4767 else
4768 appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4769 gettext_noop("Access privileges"));
4770 }
4771
4772 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4773
4774 if (verbose && pset.sversion >= 150000)
4776 " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4777 " ON pg_catalog.lower(s.name) = p.parname\n");
4778
4779 if (pattern)
4780 processSQLNamePattern(pset.db, &buf, pattern,
4781 false, false,
4782 NULL, "pg_catalog.lower(s.name)", NULL,
4783 NULL, NULL, NULL);
4784 else
4785 appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
4786 " s.setting IS DISTINCT FROM s.boot_val\n");
4787
4788 appendPQExpBufferStr(&buf, "ORDER BY 1;");
4789
4790 res = PSQLexec(buf.data);
4792 if (!res)
4793 return false;
4794
4795 if (pattern)
4796 myopt.title = _("List of configuration parameters");
4797 else
4798 myopt.title = _("List of non-default configuration parameters");
4799 myopt.translate_header = true;
4800
4801 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4802
4803 PQclear(res);
4804 return true;
4805}
4806
4807/*
4808 * \dy
4809 *
4810 * Describes Event Triggers.
4811 */
4812bool
4813listEventTriggers(const char *pattern, bool verbose)
4814{
4816 PGresult *res;
4817 printQueryOpt myopt = pset.popt;
4818 static const bool translate_columns[] =
4819 {false, false, false, true, false, false, false};
4820
4821 if (pset.sversion < 90300)
4822 {
4823 char sverbuf[32];
4824
4825 pg_log_error("The server (version %s) does not support event triggers.",
4827 sverbuf, sizeof(sverbuf)));
4828 return true;
4829 }
4830
4832
4834 "SELECT evtname as \"%s\", "
4835 "evtevent as \"%s\", "
4836 "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
4837 " case evtenabled when 'O' then '%s'"
4838 " when 'R' then '%s'"
4839 " when 'A' then '%s'"
4840 " when 'D' then '%s' end as \"%s\",\n"
4841 " e.evtfoid::pg_catalog.regproc as \"%s\", "
4842 "pg_catalog.array_to_string(array(select x"
4843 " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
4844 gettext_noop("Name"),
4845 gettext_noop("Event"),
4846 gettext_noop("Owner"),
4847 gettext_noop("enabled"),
4848 gettext_noop("replica"),
4849 gettext_noop("always"),
4850 gettext_noop("disabled"),
4851 gettext_noop("Enabled"),
4852 gettext_noop("Function"),
4853 gettext_noop("Tags"));
4854 if (verbose)
4856 ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
4857 gettext_noop("Description"));
4859 "\nFROM pg_catalog.pg_event_trigger e ");
4860
4861 if (!validateSQLNamePattern(&buf, pattern, false, false,
4862 NULL, "evtname", NULL, NULL,
4863 NULL, 1))
4864 {
4866 return false;
4867 }
4868
4869 appendPQExpBufferStr(&buf, "ORDER BY 1");
4870
4871 res = PSQLexec(buf.data);
4873 if (!res)
4874 return false;
4875
4876 myopt.title = _("List of event triggers");
4877 myopt.translate_header = true;
4878 myopt.translate_columns = translate_columns;
4879 myopt.n_translate_columns = lengthof(translate_columns);
4880
4881 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4882
4883 PQclear(res);
4884 return true;
4885}
4886
4887/*
4888 * \dX
4889 *
4890 * Describes extended statistics.
4891 */
4892bool
4893listExtendedStats(const char *pattern)
4894{
4896 PGresult *res;
4897 printQueryOpt myopt = pset.popt;
4898
4899 if (pset.sversion < 100000)
4900 {
4901 char sverbuf[32];
4902
4903 pg_log_error("The server (version %s) does not support extended statistics.",
4905 sverbuf, sizeof(sverbuf)));
4906 return true;
4907 }
4908
4911 "SELECT \n"
4912 "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS \"%s\", \n"
4913 "es.stxname AS \"%s\", \n",
4914 gettext_noop("Schema"),
4915 gettext_noop("Name"));
4916
4917 if (pset.sversion >= 140000)
4919 "pg_catalog.format('%%s FROM %%s', \n"
4920 " pg_catalog.pg_get_statisticsobjdef_columns(es.oid), \n"
4921 " es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4922 gettext_noop("Definition"));
4923 else
4925 "pg_catalog.format('%%s FROM %%s', \n"
4926 " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
4927 " FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
4928 " JOIN pg_catalog.pg_attribute a \n"
4929 " ON (es.stxrelid = a.attrelid \n"
4930 " AND a.attnum = s.attnum \n"
4931 " AND NOT a.attisdropped)), \n"
4932 "es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4933 gettext_noop("Definition"));
4934
4936 ",\nCASE WHEN " CppAsString2(STATS_EXT_NDISTINCT) " = any(es.stxkind) THEN 'defined' \n"
4937 "END AS \"%s\", \n"
4938 "CASE WHEN " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(es.stxkind) THEN 'defined' \n"
4939 "END AS \"%s\"",
4940 gettext_noop("Ndistinct"),
4941 gettext_noop("Dependencies"));
4942
4943 /*
4944 * Include the MCV statistics kind.
4945 */
4946 if (pset.sversion >= 120000)
4947 {
4949 ",\nCASE WHEN " CppAsString2(STATS_EXT_MCV) " = any(es.stxkind) THEN 'defined' \n"
4950 "END AS \"%s\" ",
4951 gettext_noop("MCV"));
4952 }
4953
4955 " \nFROM pg_catalog.pg_statistic_ext es \n");
4956
4957 if (!validateSQLNamePattern(&buf, pattern,
4958 false, false,
4959 "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
4960 NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)",
4961 NULL, 3))
4962 {
4964 return false;
4965 }
4966
4967 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4968
4969 res = PSQLexec(buf.data);
4971 if (!res)
4972 return false;
4973
4974 myopt.title = _("List of extended statistics");
4975 myopt.translate_header = true;
4976
4977 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4978
4979 PQclear(res);
4980 return true;
4981}
4982
4983/*
4984 * \dC
4985 *
4986 * Describes casts.
4987 */
4988bool
4989listCasts(const char *pattern, bool verbose)
4990{
4992 PGresult *res;
4993 printQueryOpt myopt = pset.popt;
4994 static const bool translate_columns[] = {false, false, false, true, true, false};
4995
4997
4999 "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
5000 " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n",
5001 gettext_noop("Source type"),
5002 gettext_noop("Target type"));
5003
5004 /*
5005 * We don't attempt to localize '(binary coercible)' or '(with inout)',
5006 * because there's too much risk of gettext translating a function name
5007 * that happens to match some string in the PO database.
5008 */
5010 " CASE WHEN c.castmethod = '%c' THEN '(binary coercible)'\n"
5011 " WHEN c.castmethod = '%c' THEN '(with inout)'\n"
5012 " ELSE p.proname\n"
5013 " END AS \"%s\",\n",
5014 COERCION_METHOD_BINARY,
5015 COERCION_METHOD_INOUT,
5016 gettext_noop("Function"));
5017
5019 " CASE WHEN c.castcontext = '%c' THEN '%s'\n"
5020 " WHEN c.castcontext = '%c' THEN '%s'\n"
5021 " ELSE '%s'\n"
5022 " END AS \"%s\"",
5023 COERCION_CODE_EXPLICIT,
5024 gettext_noop("no"),
5025 COERCION_CODE_ASSIGNMENT,
5026 gettext_noop("in assignment"),
5027 gettext_noop("yes"),
5028 gettext_noop("Implicit?"));
5029
5030 if (verbose)
5032 ",\n CASE WHEN p.proleakproof THEN '%s'\n"
5033 " ELSE '%s'\n"
5034 " END AS \"%s\",\n"
5035 " d.description AS \"%s\"",
5036 gettext_noop("yes"),
5037 gettext_noop("no"),
5038 gettext_noop("Leakproof?"),
5039 gettext_noop("Description"));
5040
5041 /*
5042 * We need a left join to pg_proc for binary casts; the others are just
5043 * paranoia.
5044 */
5046 "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
5047 " ON c.castfunc = p.oid\n"
5048 " LEFT JOIN pg_catalog.pg_type ts\n"
5049 " ON c.castsource = ts.oid\n"
5050 " LEFT JOIN pg_catalog.pg_namespace ns\n"
5051 " ON ns.oid = ts.typnamespace\n"
5052 " LEFT JOIN pg_catalog.pg_type tt\n"
5053 " ON c.casttarget = tt.oid\n"
5054 " LEFT JOIN pg_catalog.pg_namespace nt\n"
5055 " ON nt.oid = tt.typnamespace\n");
5056
5057 if (verbose)
5059 " LEFT JOIN pg_catalog.pg_description d\n"
5060 " ON d.classoid = c.tableoid AND d.objoid = "
5061 "c.oid AND d.objsubid = 0\n");
5062
5063 appendPQExpBufferStr(&buf, "WHERE ( (true");
5064
5065 /*
5066 * Match name pattern against either internal or external name of either
5067 * castsource or casttarget
5068 */
5069 if (!validateSQLNamePattern(&buf, pattern, true, false,
5070 "ns.nspname", "ts.typname",
5071 "pg_catalog.format_type(ts.oid, NULL)",
5072 "pg_catalog.pg_type_is_visible(ts.oid)",
5073 NULL, 3))
5074 goto error_return;
5075
5076 appendPQExpBufferStr(&buf, ") OR (true");
5077
5078 if (!validateSQLNamePattern(&buf, pattern, true, false,
5079 "nt.nspname", "tt.typname",
5080 "pg_catalog.format_type(tt.oid, NULL)",
5081 "pg_catalog.pg_type_is_visible(tt.oid)",
5082 NULL, 3))
5083 goto error_return;
5084
5085 appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
5086
5087 res = PSQLexec(buf.data);
5089 if (!res)
5090 return false;
5091
5092 myopt.title = _("List of casts");
5093 myopt.translate_header = true;
5094 myopt.translate_columns = translate_columns;
5095 myopt.n_translate_columns = lengthof(translate_columns);
5096
5097 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5098
5099 PQclear(res);
5100 return true;
5101
5102error_return:
5104 return false;
5105}
5106
5107/*
5108 * \dO
5109 *
5110 * Describes collations.
5111 */
5112bool
5113listCollations(const char *pattern, bool verbose, bool showSystem)
5114{
5116 PGresult *res;
5117 printQueryOpt myopt = pset.popt;
5118 static const bool translate_columns[] = {false, false, false, false, false, false, false, true, false};
5119
5121
5123 "SELECT\n"
5124 " n.nspname AS \"%s\",\n"
5125 " c.collname AS \"%s\",\n",
5126 gettext_noop("Schema"),
5127 gettext_noop("Name"));
5128
5129 if (pset.sversion >= 100000)
5131 " CASE c.collprovider "
5132 "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
5133 "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
5134 "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
5135 "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
5136 "END AS \"%s\",\n",
5137 gettext_noop("Provider"));
5138 else
5140 " 'libc' AS \"%s\",\n",
5141 gettext_noop("Provider"));
5142
5144 " c.collcollate AS \"%s\",\n"
5145 " c.collctype AS \"%s\",\n",
5146 gettext_noop("Collate"),
5147 gettext_noop("Ctype"));
5148
5149 if (pset.sversion >= 170000)
5151 " c.colllocale AS \"%s\",\n",
5152 gettext_noop("Locale"));
5153 else if (pset.sversion >= 150000)
5155 " c.colliculocale AS \"%s\",\n",
5156 gettext_noop("Locale"));
5157 else
5159 " c.collcollate AS \"%s\",\n",
5160 gettext_noop("Locale"));
5161
5162 if (pset.sversion >= 160000)
5164 " c.collicurules AS \"%s\",\n",
5165 gettext_noop("ICU Rules"));
5166 else
5168 " NULL AS \"%s\",\n",
5169 gettext_noop("ICU Rules"));
5170
5171 if (pset.sversion >= 120000)
5173 " CASE WHEN c.collisdeterministic THEN '%s' ELSE '%s' END AS \"%s\"",
5174 gettext_noop("yes"), gettext_noop("no"),
5175 gettext_noop("Deterministic?"));
5176 else
5178 " '%s' AS \"%s\"",
5179 gettext_noop("yes"),
5180 gettext_noop("Deterministic?"));
5181
5182 if (verbose)
5184 ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
5185 gettext_noop("Description"));
5186
5188 "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
5189 "WHERE n.oid = c.collnamespace\n");
5190
5191 if (!showSystem && !pattern)
5192 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
5193 " AND n.nspname <> 'information_schema'\n");
5194
5195 /*
5196 * Hide collations that aren't usable in the current database's encoding.
5197 * If you think to change this, note that pg_collation_is_visible rejects
5198 * unusable collations, so you will need to hack name pattern processing
5199 * somehow to avoid inconsistent behavior.
5200 */
5201 appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
5202
5203 if (!validateSQLNamePattern(&buf, pattern, true, false,
5204 "n.nspname", "c.collname", NULL,
5205 "pg_catalog.pg_collation_is_visible(c.oid)",
5206 NULL, 3))
5207 {
5209 return false;
5210 }
5211
5212 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5213
5214 res = PSQLexec(buf.data);
5216 if (!res)
5217 return false;
5218
5219 myopt.title = _("List of collations");
5220 myopt.translate_header = true;
5221 myopt.translate_columns = translate_columns;
5222 myopt.n_translate_columns = lengthof(translate_columns);
5223
5224 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5225
5226 PQclear(res);
5227 return true;
5228}
5229
5230/*
5231 * \dn
5232 *
5233 * Describes schemas (namespaces)
5234 */
5235bool
5236listSchemas(const char *pattern, bool verbose, bool showSystem)
5237{
5239 PGresult *res;
5240 printQueryOpt myopt = pset.popt;
5241 int pub_schema_tuples = 0;
5242 char **footers = NULL;
5243
5246 "SELECT n.nspname AS \"%s\",\n"
5247 " pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
5248 gettext_noop("Name"),
5249 gettext_noop("Owner"));
5250
5251 if (verbose)
5252 {
5253 appendPQExpBufferStr(&buf, ",\n ");
5254 printACLColumn(&buf, "n.nspacl");
5256 ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
5257 gettext_noop("Description"));
5258 }
5259
5261 "\nFROM pg_catalog.pg_namespace n\n");
5262
5263 if (!showSystem && !pattern)
5265 "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
5266
5267 if (!validateSQLNamePattern(&buf, pattern,
5268 !showSystem && !pattern, false,
5269 NULL, "n.nspname", NULL,
5270 NULL,
5271 NULL, 2))
5272 goto error_return;
5273
5274 appendPQExpBufferStr(&buf, "ORDER BY 1;");
5275
5276 res = PSQLexec(buf.data);
5277 if (!res)
5278 goto error_return;
5279
5280 myopt.title = _("List of schemas");
5281 myopt.translate_header = true;
5282
5283 if (pattern && pset.sversion >= 150000)
5284 {
5285 PGresult *result;
5286 int i;
5287
5289 "SELECT pubname \n"
5290 "FROM pg_catalog.pg_publication p\n"
5291 " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
5292 " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
5293 "WHERE n.nspname = '%s'\n"
5294 "ORDER BY 1",
5295 pattern);
5296 result = PSQLexec(buf.data);
5297 if (!result)
5298 goto error_return;
5299 else
5300 pub_schema_tuples = PQntuples(result);
5301
5302 if (pub_schema_tuples > 0)
5303 {
5304 /*
5305 * Allocate memory for footers. Size of footers will be 1 (for
5306 * storing "Publications:" string) + publication schema mapping
5307 * count + 1 (for storing NULL).
5308 */
5309 footers = (char **) pg_malloc((1 + pub_schema_tuples + 1) * sizeof(char *));
5310 footers[0] = pg_strdup(_("Publications:"));
5311
5312 /* Might be an empty set - that's ok */
5313 for (i = 0; i < pub_schema_tuples; i++)
5314 {
5315 printfPQExpBuffer(&buf, " \"%s\"",
5316 PQgetvalue(result, i, 0));
5317
5318 footers[i + 1] = pg_strdup(buf.data);
5319 }
5320
5321 footers[i + 1] = NULL;
5322 myopt.footers = footers;
5323 }
5324
5325 PQclear(result);
5326 }
5327
5328 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5329
5331 PQclear(res);
5332
5333 /* Free the memory allocated for the footer */
5334 if (footers)
5335 {
5336 char **footer = NULL;
5337
5338 for (footer = footers; *footer; footer++)
5339 pg_free(*footer);
5340
5341 pg_free(footers);
5342 }
5343
5344 return true;
5345
5346error_return:
5348 return false;
5349}
5350
5351
5352/*
5353 * \dFp
5354 * list text search parsers
5355 */
5356bool
5357listTSParsers(const char *pattern, bool verbose)
5358{
5360 PGresult *res;
5361 printQueryOpt myopt = pset.popt;
5362
5363 if (verbose)
5364 return listTSParsersVerbose(pattern);
5365
5367
5369 "SELECT\n"
5370 " n.nspname as \"%s\",\n"
5371 " p.prsname as \"%s\",\n"
5372 " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
5373 "FROM pg_catalog.pg_ts_parser p\n"
5374 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
5375 gettext_noop("Schema"),
5376 gettext_noop("Name"),
5377 gettext_noop("Description")
5378 );
5379
5380 if (!validateSQLNamePattern(&buf, pattern, false, false,
5381 "n.nspname", "p.prsname", NULL,
5382 "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5383 NULL, 3))
5384 {
5386 return false;
5387 }
5388
5389 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5390
5391 res = PSQLexec(buf.data);
5393 if (!res)
5394 return false;
5395
5396 myopt.title = _("List of text search parsers");
5397 myopt.translate_header = true;
5398
5399 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5400
5401 PQclear(res);
5402 return true;
5403}
5404
5405/*
5406 * full description of parsers
5407 */
5408static bool
5409listTSParsersVerbose(const char *pattern)
5410{
5412 PGresult *res;
5413 int i;
5414
5416
5418 "SELECT p.oid,\n"
5419 " n.nspname,\n"
5420 " p.prsname\n"
5421 "FROM pg_catalog.pg_ts_parser p\n"
5422 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
5423 );
5424
5425 if (!validateSQLNamePattern(&buf, pattern, false, false,
5426 "n.nspname", "p.prsname", NULL,
5427 "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5428 NULL, 3))
5429 {
5431 return false;
5432 }
5433
5434 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5435
5436 res = PSQLexec(buf.data);
5438 if (!res)
5439 return false;
5440
5441 if (PQntuples(res) == 0)
5442 {
5443 if (!pset.quiet)
5444 {
5445 if (pattern)
5446 pg_log_error("Did not find any text search parser named \"%s\".",
5447 pattern);
5448 else
5449 pg_log_error("Did not find any text search parsers.");
5450 }
5451 PQclear(res);
5452 return false;
5453 }
5454
5455 for (i = 0; i < PQntuples(res); i++)
5456 {
5457 const char *oid;
5458 const char *nspname = NULL;
5459 const char *prsname;
5460
5461 oid = PQgetvalue(res, i, 0);
5462 if (!PQgetisnull(res, i, 1))
5463 nspname = PQgetvalue(res, i, 1);
5464 prsname = PQgetvalue(res, i, 2);
5465
5466 if (!describeOneTSParser(oid, nspname, prsname))
5467 {
5468 PQclear(res);
5469 return false;
5470 }
5471
5472 if (cancel_pressed)
5473 {
5474 PQclear(res);
5475 return false;
5476 }
5477 }
5478
5479 PQclear(res);
5480 return true;
5481}
5482
5483static bool
5484describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
5485{
5487 PGresult *res;
5488 PQExpBufferData title;
5489 printQueryOpt myopt = pset.popt;
5490 static const bool translate_columns[] = {true, false, false};
5491
5493
5495 "SELECT '%s' AS \"%s\",\n"
5496 " p.prsstart::pg_catalog.regproc AS \"%s\",\n"
5497 " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\"\n"
5498 " FROM pg_catalog.pg_ts_parser p\n"
5499 " WHERE p.oid = '%s'\n"
5500 "UNION ALL\n"
5501 "SELECT '%s',\n"
5502 " p.prstoken::pg_catalog.regproc,\n"
5503 " pg_catalog.obj_description(p.prstoken, 'pg_proc')\n"
5504 " FROM pg_catalog.pg_ts_parser p\n"
5505 " WHERE p.oid = '%s'\n"
5506 "UNION ALL\n"
5507 "SELECT '%s',\n"
5508 " p.prsend::pg_catalog.regproc,\n"
5509 " pg_catalog.obj_description(p.prsend, 'pg_proc')\n"
5510 " FROM pg_catalog.pg_ts_parser p\n"
5511 " WHERE p.oid = '%s'\n"
5512 "UNION ALL\n"
5513 "SELECT '%s',\n"
5514 " p.prsheadline::pg_catalog.regproc,\n"
5515 " pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n"
5516 " FROM pg_catalog.pg_ts_parser p\n"
5517 " WHERE p.oid = '%s'\n"
5518 "UNION ALL\n"
5519 "SELECT '%s',\n"
5520 " p.prslextype::pg_catalog.regproc,\n"
5521 " pg_catalog.obj_description(p.prslextype, 'pg_proc')\n"
5522 " FROM pg_catalog.pg_ts_parser p\n"
5523 " WHERE p.oid = '%s';",
5524 gettext_noop("Start parse"),
5525 gettext_noop("Method"),
5526 gettext_noop("Function"),
5527 gettext_noop("Description"),
5528 oid,
5529 gettext_noop("Get next token"),
5530 oid,
5531 gettext_noop("End parse"),
5532 oid,
5533 gettext_noop("Get headline"),
5534 oid,
5535 gettext_noop("Get token types"),
5536 oid);
5537
5538 res = PSQLexec(buf.data);
5540 if (!res)
5541 return false;
5542
5543 initPQExpBuffer(&title);
5544 if (nspname)
5545 printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""),
5546 nspname, prsname);
5547 else
5548 printfPQExpBuffer(&title, _("Text search parser \"%s\""), prsname);
5549 myopt.title = title.data;
5550 myopt.footers = NULL;
5551 myopt.topt.default_footer = false;
5552 myopt.translate_header = true;
5553 myopt.translate_columns = translate_columns;
5554 myopt.n_translate_columns = lengthof(translate_columns);
5555
5556 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5557
5558 PQclear(res);
5559
5561
5563 "SELECT t.alias as \"%s\",\n"
5564 " t.description as \"%s\"\n"
5565 "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t\n"
5566 "ORDER BY 1;",
5567 gettext_noop("Token name"),
5568 gettext_noop("Description"),
5569 oid);
5570
5571 res = PSQLexec(buf.data);
5573 if (!res)
5574 {
5575 termPQExpBuffer(&title);
5576 return false;
5577 }
5578
5579 if (nspname)
5580 printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""),
5581 nspname, prsname);
5582 else
5583 printfPQExpBuffer(&title, _("Token types for parser \"%s\""), prsname);
5584 myopt.title = title.data;
5585 myopt.footers = NULL;
5586 myopt.topt.default_footer = true;
5587 myopt.translate_header = true;
5588 myopt.translate_columns = NULL;
5589 myopt.n_translate_columns = 0;
5590
5591 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5592
5593 termPQExpBuffer(&title);
5594 PQclear(res);
5595 return true;
5596}
5597
5598
5599/*
5600 * \dFd
5601 * list text search dictionaries
5602 */
5603bool
5604listTSDictionaries(const char *pattern, bool verbose)
5605{
5607 PGresult *res;
5608 printQueryOpt myopt = pset.popt;
5609
5611
5613 "SELECT\n"
5614 " n.nspname as \"%s\",\n"
5615 " d.dictname as \"%s\",\n",
5616 gettext_noop("Schema"),
5617 gettext_noop("Name"));
5618
5619 if (verbose)
5620 {
5622 " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM\n"
5623 " pg_catalog.pg_ts_template t\n"
5624 " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace\n"
5625 " WHERE d.dicttemplate = t.oid ) AS \"%s\",\n"
5626 " d.dictinitoption as \"%s\",\n",
5627 gettext_noop("Template"),
5628 gettext_noop("Init options"));
5629 }
5630
5632 " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
5633 gettext_noop("Description"));
5634
5635 appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n"
5636 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
5637
5638 if (!validateSQLNamePattern(&buf, pattern, false, false,
5639 "n.nspname", "d.dictname", NULL,
5640 "pg_catalog.pg_ts_dict_is_visible(d.oid)",
5641 NULL, 3))
5642 {
5644 return false;
5645 }
5646
5647 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5648
5649 res = PSQLexec(buf.data);
5651 if (!res)
5652 return false;
5653
5654 myopt.title = _("List of text search dictionaries");
5655 myopt.translate_header = true;
5656
5657 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5658
5659 PQclear(res);
5660 return true;
5661}
5662
5663
5664/*
5665 * \dFt
5666 * list text search templates
5667 */
5668bool
5669listTSTemplates(const char *pattern, bool verbose)
5670{
5672 PGresult *res;
5673 printQueryOpt myopt = pset.popt;
5674
5676
5677 if (verbose)
5679 "SELECT\n"
5680 " n.nspname AS \"%s\",\n"
5681 " t.tmplname AS \"%s\",\n"
5682 " t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
5683 " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
5684 " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5685 gettext_noop("Schema"),
5686 gettext_noop("Name"),
5687 gettext_noop("Init"),
5688 gettext_noop("Lexize"),
5689 gettext_noop("Description"));
5690 else
5692 "SELECT\n"
5693 " n.nspname AS \"%s\",\n"
5694 " t.tmplname AS \"%s\",\n"
5695 " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5696 gettext_noop("Schema"),
5697 gettext_noop("Name"),
5698 gettext_noop("Description"));
5699
5700 appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n"
5701 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
5702
5703 if (!validateSQLNamePattern(&buf, pattern, false, false,
5704 "n.nspname", "t.tmplname", NULL,
5705 "pg_catalog.pg_ts_template_is_visible(t.oid)",
5706 NULL, 3))
5707 {
5709 return false;
5710 }
5711
5712 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5713
5714 res = PSQLexec(buf.data);
5716 if (!res)
5717 return false;
5718
5719 myopt.title = _("List of text search templates");
5720 myopt.translate_header = true;
5721
5722 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5723
5724 PQclear(res);
5725 return true;
5726}
5727
5728
5729/*
5730 * \dF
5731 * list text search configurations
5732 */
5733bool
5734listTSConfigs(const char *pattern, bool verbose)
5735{
5737 PGresult *res;
5738 printQueryOpt myopt = pset.popt;
5739
5740 if (verbose)
5741 return listTSConfigsVerbose(pattern);
5742
5744
5746 "SELECT\n"
5747 " n.nspname as \"%s\",\n"
5748 " c.cfgname as \"%s\",\n"
5749 " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
5750 "FROM pg_catalog.pg_ts_config c\n"
5751 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace\n",
5752 gettext_noop("Schema"),
5753 gettext_noop("Name"),
5754 gettext_noop("Description")
5755 );
5756
5757 if (!validateSQLNamePattern(&buf, pattern, false, false,
5758 "n.nspname", "c.cfgname", NULL,
5759 "pg_catalog.pg_ts_config_is_visible(c.oid)",
5760 NULL, 3))
5761 {
5763 return false;
5764 }
5765
5766 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5767
5768 res = PSQLexec(buf.data);
5770 if (!res)
5771 return false;
5772
5773 myopt.title = _("List of text search configurations");
5774 myopt.translate_header = true;
5775
5776 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5777
5778 PQclear(res);
5779 return true;
5780}
5781
5782static bool
5783listTSConfigsVerbose(const char *pattern)
5784{
5786 PGresult *res;
5787 int i;
5788
5790
5792 "SELECT c.oid, c.cfgname,\n"
5793 " n.nspname,\n"
5794 " p.prsname,\n"
5795 " np.nspname as pnspname\n"
5796 "FROM pg_catalog.pg_ts_config c\n"
5797 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace,\n"
5798 " pg_catalog.pg_ts_parser p\n"
5799 " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace\n"
5800 "WHERE p.oid = c.cfgparser\n"
5801 );
5802
5803 if (!validateSQLNamePattern(&buf, pattern, true, false,
5804 "n.nspname", "c.cfgname", NULL,
5805 "pg_catalog.pg_ts_config_is_visible(c.oid)",
5806 NULL, 3))
5807 {
5809 return false;
5810 }
5811
5812 appendPQExpBufferStr(&buf, "ORDER BY 3, 2;");
5813
5814 res = PSQLexec(buf.data);
5816 if (!res)
5817 return false;
5818
5819 if (PQntuples(res) == 0)
5820 {
5821 if (!pset.quiet)
5822 {
5823 if (pattern)
5824 pg_log_error("Did not find any text search configuration named \"%s\".",
5825 pattern);
5826 else
5827 pg_log_error("Did not find any text search configurations.");
5828 }
5829 PQclear(res);
5830 return false;
5831 }
5832
5833 for (i = 0; i < PQntuples(res); i++)
5834 {
5835 const char *oid;
5836 const char *cfgname;
5837 const char *nspname = NULL;
5838 const char *prsname;
5839 const char *pnspname = NULL;
5840
5841 oid = PQgetvalue(res, i, 0);
5842 cfgname = PQgetvalue(res, i, 1);
5843 if (!PQgetisnull(res, i, 2))
5844 nspname = PQgetvalue(res, i, 2);
5845 prsname = PQgetvalue(res, i, 3);
5846 if (!PQgetisnull(res, i, 4))
5847 pnspname = PQgetvalue(res, i, 4);
5848
5849 if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
5850 {
5851 PQclear(res);
5852 return false;
5853 }
5854
5855 if (cancel_pressed)
5856 {
5857 PQclear(res);
5858 return false;
5859 }
5860 }
5861
5862 PQclear(res);
5863 return true;
5864}
5865
5866static bool
5867describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
5868 const char *pnspname, const char *prsname)
5869{
5871 title;
5872 PGresult *res;
5873 printQueryOpt myopt = pset.popt;
5874
5876
5878 "SELECT\n"
5879 " ( SELECT t.alias FROM\n"
5880 " pg_catalog.ts_token_type(c.cfgparser) AS t\n"
5881 " WHERE t.tokid = m.maptokentype ) AS \"%s\",\n"
5882 " pg_catalog.btrim(\n"
5883 " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary\n"
5884 " FROM pg_catalog.pg_ts_config_map AS mm\n"
5885 " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype\n"
5886 " ORDER BY mapcfg, maptokentype, mapseqno\n"
5887 " ) :: pg_catalog.text,\n"
5888 " '{}') AS \"%s\"\n"
5889 "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m\n"
5890 "WHERE c.oid = '%s' AND m.mapcfg = c.oid\n"
5891 "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser\n"
5892 "ORDER BY 1;",
5893 gettext_noop("Token"),
5894 gettext_noop("Dictionaries"),
5895 oid);
5896
5897 res = PSQLexec(buf.data);
5899 if (!res)
5900 return false;
5901
5902 initPQExpBuffer(&title);
5903
5904 if (nspname)
5905 appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
5906 nspname, cfgname);
5907 else
5908 appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
5909 cfgname);
5910
5911 if (pnspname)
5912 appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
5913 pnspname, prsname);
5914 else
5915 appendPQExpBuffer(&title, _("\nParser: \"%s\""),
5916 prsname);
5917
5918 myopt.title = title.data;
5919 myopt.footers = NULL;
5920 myopt.topt.default_footer = false;
5921 myopt.translate_header = true;
5922
5923 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5924
5925 termPQExpBuffer(&title);
5926
5927 PQclear(res);
5928 return true;
5929}
5930
5931
5932/*
5933 * \dew
5934 *
5935 * Describes foreign-data wrappers
5936 */
5937bool
5938listForeignDataWrappers(const char *pattern, bool verbose)
5939{
5941 PGresult *res;
5942 printQueryOpt myopt = pset.popt;
5943
5946 "SELECT fdw.fdwname AS \"%s\",\n"
5947 " pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n"
5948 " fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n"
5949 " fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
5950 gettext_noop("Name"),
5951 gettext_noop("Owner"),
5952 gettext_noop("Handler"),
5953 gettext_noop("Validator"));
5954
5955 if (verbose)
5956 {
5957 appendPQExpBufferStr(&buf, ",\n ");
5958 printACLColumn(&buf, "fdwacl");
5960 ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE "
5961 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
5962 " pg_catalog.quote_ident(option_name) || ' ' || "
5963 " pg_catalog.quote_literal(option_value) FROM "
5964 " pg_catalog.pg_options_to_table(fdwoptions)), ', ') || ')' "
5965 " END AS \"%s\""
5966 ",\n d.description AS \"%s\" ",
5967 gettext_noop("FDW options"),
5968 gettext_noop("Description"));
5969 }
5970
5971 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
5972
5973 if (verbose)
5975 "LEFT JOIN pg_catalog.pg_description d\n"
5976 " ON d.classoid = fdw.tableoid "
5977 "AND d.objoid = fdw.oid AND d.objsubid = 0\n");
5978
5979 if (!validateSQLNamePattern(&buf, pattern, false, false,
5980 NULL, "fdwname", NULL, NULL,
5981 NULL, 1))
5982 {
5984 return false;
5985 }
5986
5987 appendPQExpBufferStr(&buf, "ORDER BY 1;");
5988
5989 res = PSQLexec(buf.data);
5991 if (!res)
5992 return false;
5993
5994 myopt.title = _("List of foreign-data wrappers");
5995 myopt.translate_header = true;
5996
5997 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5998
5999 PQclear(res);
6000 return true;
6001}
6002
6003/*
6004 * \des
6005 *
6006 * Describes foreign servers.
6007 */
6008bool
6009listForeignServers(const char *pattern, bool verbose)
6010{
6012 PGresult *res;
6013 printQueryOpt myopt = pset.popt;
6014
6017 "SELECT s.srvname AS \"%s\",\n"
6018 " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
6019 " f.fdwname AS \"%s\"",
6020 gettext_noop("Name"),
6021 gettext_noop("Owner"),
6022 gettext_noop("Foreign-data wrapper"));
6023
6024 if (verbose)
6025 {
6026 appendPQExpBufferStr(&buf, ",\n ");
6027 printACLColumn(&buf, "s.srvacl");
6029 ",\n"
6030 " s.srvtype AS \"%s\",\n"
6031 " s.srvversion AS \"%s\",\n"
6032 " CASE WHEN srvoptions IS NULL THEN '' ELSE "
6033 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6034 " pg_catalog.quote_ident(option_name) || ' ' || "
6035 " pg_catalog.quote_literal(option_value) FROM "
6036 " pg_catalog.pg_options_to_table(srvoptions)), ', ') || ')' "
6037 " END AS \"%s\",\n"
6038 " d.description AS \"%s\"",
6039 gettext_noop("Type"),
6040 gettext_noop("Version"),
6041 gettext_noop("FDW options"),
6042 gettext_noop("Description"));
6043 }
6044
6046 "\nFROM pg_catalog.pg_foreign_server s\n"
6047 " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
6048
6049 if (verbose)
6051 "LEFT JOIN pg_catalog.pg_description d\n "
6052 "ON d.classoid = s.tableoid AND d.objoid = s.oid "
6053 "AND d.objsubid = 0\n");
6054
6055 if (!validateSQLNamePattern(&buf, pattern, false, false,
6056 NULL, "s.srvname", NULL, NULL,
6057 NULL, 1))
6058 {
6060 return false;
6061 }
6062
6063 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6064
6065 res = PSQLexec(buf.data);
6067 if (!res)
6068 return false;
6069
6070 myopt.title = _("List of foreign servers");
6071 myopt.translate_header = true;
6072
6073 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6074
6075 PQclear(res);
6076 return true;
6077}
6078
6079/*
6080 * \deu
6081 *
6082 * Describes user mappings.
6083 */
6084bool
6085listUserMappings(const char *pattern, bool verbose)
6086{
6088 PGresult *res;
6089 printQueryOpt myopt = pset.popt;
6090
6093 "SELECT um.srvname AS \"%s\",\n"
6094 " um.usename AS \"%s\"",
6095 gettext_noop("Server"),
6096 gettext_noop("User name"));
6097
6098 if (verbose)
6100 ",\n CASE WHEN umoptions IS NULL THEN '' ELSE "
6101 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6102 " pg_catalog.quote_ident(option_name) || ' ' || "
6103 " pg_catalog.quote_literal(option_value) FROM "
6104 " pg_catalog.pg_options_to_table(umoptions)), ', ') || ')' "
6105 " END AS \"%s\"",
6106 gettext_noop("FDW options"));
6107
6108 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
6109
6110 if (!validateSQLNamePattern(&buf, pattern, false, false,
6111 NULL, "um.srvname", "um.usename", NULL,
6112 NULL, 1))
6113 {
6115 return false;
6116 }
6117
6118 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6119
6120 res = PSQLexec(buf.data);
6122 if (!res)
6123 return false;
6124
6125 myopt.title = _("List of user mappings");
6126 myopt.translate_header = true;
6127
6128 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6129
6130 PQclear(res);
6131 return true;
6132}
6133
6134/*
6135 * \det
6136 *
6137 * Describes foreign tables.
6138 */
6139bool
6140listForeignTables(const char *pattern, bool verbose)
6141{
6143 PGresult *res;
6144 printQueryOpt myopt = pset.popt;
6145
6148 "SELECT n.nspname AS \"%s\",\n"
6149 " c.relname AS \"%s\",\n"
6150 " s.srvname AS \"%s\"",
6151 gettext_noop("Schema"),
6152 gettext_noop("Table"),
6153 gettext_noop("Server"));
6154
6155 if (verbose)
6157 ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE "
6158 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6159 " pg_catalog.quote_ident(option_name) || ' ' || "
6160 " pg_catalog.quote_literal(option_value) FROM "
6161 " pg_catalog.pg_options_to_table(ftoptions)), ', ') || ')' "
6162 " END AS \"%s\",\n"
6163 " d.description AS \"%s\"",
6164 gettext_noop("FDW options"),
6165 gettext_noop("Description"));
6166
6168 "\nFROM pg_catalog.pg_foreign_table ft\n"
6169 " INNER JOIN pg_catalog.pg_class c"
6170 " ON c.oid = ft.ftrelid\n"
6171 " INNER JOIN pg_catalog.pg_namespace n"
6172 " ON n.oid = c.relnamespace\n"
6173 " INNER JOIN pg_catalog.pg_foreign_server s"
6174 " ON s.oid = ft.ftserver\n");
6175 if (verbose)
6177 " LEFT JOIN pg_catalog.pg_description d\n"
6178 " ON d.classoid = c.tableoid AND "
6179 "d.objoid = c.oid AND d.objsubid = 0\n");
6180
6181 if (!validateSQLNamePattern(&buf, pattern, false, false,
6182 "n.nspname", "c.relname", NULL,
6183 "pg_catalog.pg_table_is_visible(c.oid)",
6184 NULL, 3))
6185 {
6187 return false;
6188 }
6189
6190 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6191
6192 res = PSQLexec(buf.data);
6194 if (!res)
6195 return false;
6196
6197 myopt.title = _("List of foreign tables");
6198 myopt.translate_header = true;
6199
6200 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6201
6202 PQclear(res);
6203 return true;
6204}
6205
6206/*
6207 * \dx
6208 *
6209 * Briefly describes installed extensions.
6210 */
6211bool
6212listExtensions(const char *pattern)
6213{
6215 PGresult *res;
6216 printQueryOpt myopt = pset.popt;
6217
6220 "SELECT e.extname AS \"%s\", "
6221 "e.extversion AS \"%s\", ae.default_version AS \"%s\","
6222 "n.nspname AS \"%s\", d.description AS \"%s\"\n"
6223 "FROM pg_catalog.pg_extension e "
6224 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
6225 "LEFT JOIN pg_catalog.pg_description d ON d.objoid = e.oid "
6226 "AND d.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
6227 "LEFT JOIN pg_catalog.pg_available_extensions() ae(name, default_version, comment) ON ae.name = e.extname\n",
6228 gettext_noop("Name"),
6229 gettext_noop("Version"),
6230 gettext_noop("Default version"),
6231 gettext_noop("Schema"),
6232 gettext_noop("Description"));
6233
6234 if (!validateSQLNamePattern(&buf, pattern,
6235 false, false,
6236 NULL, "e.extname", NULL,
6237 NULL,
6238 NULL, 1))
6239 {
6241 return false;
6242 }
6243
6244 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6245
6246 res = PSQLexec(buf.data);
6248 if (!res)
6249 return false;
6250
6251 myopt.title = _("List of installed extensions");
6252 myopt.translate_header = true;
6253
6254 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6255
6256 PQclear(res);
6257 return true;
6258}
6259
6260/*
6261 * \dx+
6262 *
6263 * List contents of installed extensions.
6264 */
6265bool
6266listExtensionContents(const char *pattern)
6267{
6269 PGresult *res;
6270 int i;
6271
6274 "SELECT e.extname, e.oid\n"
6275 "FROM pg_catalog.pg_extension e\n");
6276
6277 if (!validateSQLNamePattern(&buf, pattern,
6278 false, false,
6279 NULL, "e.extname", NULL,
6280 NULL,
6281 NULL, 1))
6282 {
6284 return false;
6285 }
6286
6287 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6288
6289 res = PSQLexec(buf.data);
6291 if (!res)
6292 return false;
6293
6294 if (PQntuples(res) == 0)
6295 {
6296 if (!pset.quiet)
6297 {
6298 if (pattern)
6299 pg_log_error("Did not find any extension named \"%s\".",
6300 pattern);
6301 else
6302 pg_log_error("Did not find any extensions.");
6303 }
6304 PQclear(res);
6305 return false;
6306 }
6307
6308 for (i = 0; i < PQntuples(res); i++)
6309 {
6310 const char *extname;
6311 const char *oid;
6312
6313 extname = PQgetvalue(res, i, 0);
6314 oid = PQgetvalue(res, i, 1);
6315
6316 if (!listOneExtensionContents(extname, oid))
6317 {
6318 PQclear(res);
6319 return false;
6320 }
6321 if (cancel_pressed)
6322 {
6323 PQclear(res);
6324 return false;
6325 }
6326 }
6327
6328 PQclear(res);
6329 return true;
6330}
6331
6332static bool
6333listOneExtensionContents(const char *extname, const char *oid)
6334{
6336 PGresult *res;
6337 PQExpBufferData title;
6338 printQueryOpt myopt = pset.popt;
6339
6342 "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
6343 "FROM pg_catalog.pg_depend\n"
6344 "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
6345 "ORDER BY 1;",
6346 gettext_noop("Object description"),
6347 oid);
6348
6349 res = PSQLexec(buf.data);
6351 if (!res)
6352 return false;
6353
6354 initPQExpBuffer(&title);
6355 printfPQExpBuffer(&title, _("Objects in extension \"%s\""), extname);
6356 myopt.title = title.data;
6357 myopt.translate_header = true;
6358
6359 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6360
6361 termPQExpBuffer(&title);
6362 PQclear(res);
6363 return true;
6364}
6365
6366/*
6367 * validateSQLNamePattern
6368 *
6369 * Wrapper around string_utils's processSQLNamePattern which also checks the
6370 * pattern's validity. In addition to that function's parameters, takes a
6371 * 'maxparts' parameter specifying the maximum number of dotted names the
6372 * pattern is allowed to have, and a 'added_clause' parameter that returns by
6373 * reference whether a clause was added to 'buf'. Returns whether the pattern
6374 * passed validation, after logging any errors.
6375 */
6376static bool
6377validateSQLNamePattern(PQExpBuffer buf, const char *pattern, bool have_where,
6378 bool force_escape, const char *schemavar,
6379 const char *namevar, const char *altnamevar,
6380 const char *visibilityrule, bool *added_clause,
6381 int maxparts)
6382{
6383 PQExpBufferData dbbuf;
6384 int dotcnt;
6385 bool added;
6386
6387 initPQExpBuffer(&dbbuf);
6388 added = processSQLNamePattern(pset.db, buf, pattern, have_where, force_escape,
6389 schemavar, namevar, altnamevar,
6390 visibilityrule, &dbbuf, &dotcnt);
6391 if (added_clause != NULL)
6392 *added_clause = added;
6393
6394 if (dotcnt >= maxparts)
6395 {
6396 pg_log_error("improper qualified name (too many dotted names): %s",
6397 pattern);
6398 goto error_return;
6399 }
6400
6401 if (maxparts > 1 && dotcnt == maxparts - 1)
6402 {
6403 if (PQdb(pset.db) == NULL)
6404 {
6405 pg_log_error("You are currently not connected to a database.");
6406 goto error_return;
6407 }
6408 if (strcmp(PQdb(pset.db), dbbuf.data) != 0)
6409 {
6410 pg_log_error("cross-database references are not implemented: %s",
6411 pattern);
6412 goto error_return;
6413 }
6414 }
6415 termPQExpBuffer(&dbbuf);
6416 return true;
6417
6418error_return:
6419 termPQExpBuffer(&dbbuf);
6420 return false;
6421}
6422
6423/*
6424 * \dRp
6425 * Lists publications.
6426 *
6427 * Takes an optional regexp to select particular publications
6428 */
6429bool
6430listPublications(const char *pattern)
6431{
6433 PGresult *res;
6434 printQueryOpt myopt = pset.popt;
6435 static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
6436
6437 if (pset.sversion < 100000)
6438 {
6439 char sverbuf[32];
6440
6441 pg_log_error("The server (version %s) does not support publications.",
6443 sverbuf, sizeof(sverbuf)));
6444 return true;
6445 }
6446
6448
6450 "SELECT pubname AS \"%s\",\n"
6451 " pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
6452 " puballtables AS \"%s\"",
6453 gettext_noop("Name"),
6454 gettext_noop("Owner"),
6455 gettext_noop("All tables"));
6456
6457 if (pset.sversion >= 190000)
6459 ",\n puballsequences AS \"%s\"",
6460 gettext_noop("All sequences"));
6461
6463 ",\n pubinsert AS \"%s\",\n"
6464 " pubupdate AS \"%s\",\n"
6465 " pubdelete AS \"%s\"",
6466 gettext_noop("Inserts"),
6467 gettext_noop("Updates"),
6468 gettext_noop("Deletes"));
6469 if (pset.sversion >= 110000)
6471 ",\n pubtruncate AS \"%s\"",
6472 gettext_noop("Truncates"));
6473 if (pset.sversion >= 180000)
6475 ",\n (CASE pubgencols\n"
6476 " WHEN '%c' THEN 'none'\n"
6477 " WHEN '%c' THEN 'stored'\n"
6478 " END) AS \"%s\"",
6479 PUBLISH_GENCOLS_NONE,
6480 PUBLISH_GENCOLS_STORED,
6481 gettext_noop("Generated columns"));
6482 if (pset.sversion >= 130000)
6484 ",\n pubviaroot AS \"%s\"",
6485 gettext_noop("Via root"));
6486
6488 "\nFROM pg_catalog.pg_publication\n");
6489
6490 if (!validateSQLNamePattern(&buf, pattern, false, false,
6491 NULL, "pubname", NULL,
6492 NULL,
6493 NULL, 1))
6494 {
6496 return false;
6497 }
6498
6499 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6500
6501 res = PSQLexec(buf.data);
6503 if (!res)
6504 return false;
6505
6506 myopt.title = _("List of publications");
6507 myopt.translate_header = true;
6508 myopt.translate_columns = translate_columns;
6509 myopt.n_translate_columns = lengthof(translate_columns);
6510
6511 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6512
6513 PQclear(res);
6514
6515 return true;
6516}
6517
6518/*
6519 * Add footer to publication description.
6520 */
6521static bool
6523 bool as_schema, printTableContent *const cont)
6524{
6525 PGresult *res;
6526 int count = 0;
6527 int i = 0;
6528
6529 res = PSQLexec(buf->data);
6530 if (!res)
6531 return false;
6532 else
6533 count = PQntuples(res);
6534
6535 if (count > 0)
6536 printTableAddFooter(cont, footermsg);
6537
6538 for (i = 0; i < count; i++)
6539 {
6540 if (as_schema)
6541 printfPQExpBuffer(buf, " \"%s\"", PQgetvalue(res, i, 0));
6542 else
6543 {
6544 printfPQExpBuffer(buf, " \"%s.%s\"", PQgetvalue(res, i, 0),
6545 PQgetvalue(res, i, 1));
6546
6547 if (!PQgetisnull(res, i, 3))
6548 appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
6549
6550 if (!PQgetisnull(res, i, 2))
6551 appendPQExpBuffer(buf, " WHERE %s", PQgetvalue(res, i, 2));
6552 }
6553
6554 printTableAddFooter(cont, buf->data);
6555 }
6556
6557 PQclear(res);
6558 return true;
6559}
6560
6561/*
6562 * \dRp+
6563 * Describes publications including the contents.
6564 *
6565 * Takes an optional regexp to select particular publications
6566 */
6567bool
6568describePublications(const char *pattern)
6569{
6571 int i;
6572 PGresult *res;
6573 bool has_pubtruncate;
6574 bool has_pubgencols;
6575 bool has_pubviaroot;
6576 bool has_pubsequence;
6577
6578 PQExpBufferData title;
6579 printTableContent cont;
6580
6581 if (pset.sversion < 100000)
6582 {
6583 char sverbuf[32];
6584
6585 pg_log_error("The server (version %s) does not support publications.",
6587 sverbuf, sizeof(sverbuf)));
6588 return true;
6589 }
6590
6591 has_pubsequence = (pset.sversion >= 190000);
6592 has_pubtruncate = (pset.sversion >= 110000);
6593 has_pubgencols = (pset.sversion >= 180000);
6594 has_pubviaroot = (pset.sversion >= 130000);
6595
6597
6599 "SELECT oid, pubname,\n"
6600 " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
6601 " puballtables");
6602
6603 if (has_pubsequence)
6605 ", puballsequences");
6606 else
6608 ", false AS puballsequences");
6609
6611 ", pubinsert, pubupdate, pubdelete");
6612
6613 if (has_pubtruncate)
6615 ", pubtruncate");
6616 else
6618 ", false AS pubtruncate");
6619
6620 if (has_pubgencols)
6622 ", (CASE pubgencols\n"
6623 " WHEN '%c' THEN 'none'\n"
6624 " WHEN '%c' THEN 'stored'\n"
6625 " END) AS \"%s\"\n",
6626 PUBLISH_GENCOLS_NONE,
6627 PUBLISH_GENCOLS_STORED,
6628 gettext_noop("Generated columns"));
6629 else
6631 ", 'none' AS pubgencols");
6632
6633 if (has_pubviaroot)
6635 ", pubviaroot");
6636 else
6638 ", false AS pubviaroot");
6639
6641 "\nFROM pg_catalog.pg_publication\n");
6642
6643 if (!validateSQLNamePattern(&buf, pattern, false, false,
6644 NULL, "pubname", NULL,
6645 NULL,
6646 NULL, 1))
6647 {
6649 return false;
6650 }
6651
6652 appendPQExpBufferStr(&buf, "ORDER BY 2;");
6653
6654 res = PSQLexec(buf.data);
6655 if (!res)
6656 {
6658 return false;
6659 }
6660
6661 if (PQntuples(res) == 0)
6662 {
6663 if (!pset.quiet)
6664 {
6665 if (pattern)
6666 pg_log_error("Did not find any publication named \"%s\".",
6667 pattern);
6668 else
6669 pg_log_error("Did not find any publications.");
6670 }
6671
6673 PQclear(res);
6674 return false;
6675 }
6676
6677 for (i = 0; i < PQntuples(res); i++)
6678 {
6679 const char align = 'l';
6680 int ncols = 5;
6681 int nrows = 1;
6682 char *pubid = PQgetvalue(res, i, 0);
6683 char *pubname = PQgetvalue(res, i, 1);
6684 bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
6685 printTableOpt myopt = pset.popt.topt;
6686
6687 if (has_pubsequence)
6688 ncols++;
6689 if (has_pubtruncate)
6690 ncols++;
6691 if (has_pubgencols)
6692 ncols++;
6693 if (has_pubviaroot)
6694 ncols++;
6695
6696 initPQExpBuffer(&title);
6697 printfPQExpBuffer(&title, _("Publication %s"), pubname);
6698 printTableInit(&cont, &myopt, title.data, ncols, nrows);
6699
6700 printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
6701 printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
6702 if (has_pubsequence)
6703 printTableAddHeader(&cont, gettext_noop("All sequences"), true, align);
6704 printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
6705 printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
6706 printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
6707 if (has_pubtruncate)
6708 printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
6709 if (has_pubgencols)
6710 printTableAddHeader(&cont, gettext_noop("Generated columns"), true, align);
6711 if (has_pubviaroot)
6712 printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
6713
6714 printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
6715 printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
6716 if (has_pubsequence)
6717 printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
6718 printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
6719 printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
6720 printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
6721 if (has_pubtruncate)
6722 printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
6723 if (has_pubgencols)
6724 printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
6725 if (has_pubviaroot)
6726 printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false);
6727
6728 if (!puballtables)
6729 {
6730 /* Get the tables for the specified publication */
6732 "SELECT n.nspname, c.relname");
6733 if (pset.sversion >= 150000)
6734 {
6736 ", pg_get_expr(pr.prqual, c.oid)");
6738 ", (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
6739 " pg_catalog.array_to_string("
6740 " ARRAY(SELECT attname\n"
6741 " FROM\n"
6742 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
6743 " pg_catalog.pg_attribute\n"
6744 " WHERE attrelid = c.oid AND attnum = prattrs[s]), ', ')\n"
6745 " ELSE NULL END)");
6746 }
6747 else
6749 ", NULL, NULL");
6751 "\nFROM pg_catalog.pg_class c,\n"
6752 " pg_catalog.pg_namespace n,\n"
6753 " pg_catalog.pg_publication_rel pr\n"
6754 "WHERE c.relnamespace = n.oid\n"
6755 " AND c.oid = pr.prrelid\n"
6756 " AND pr.prpubid = '%s'\n"
6757 "ORDER BY 1,2", pubid);
6758 if (!addFooterToPublicationDesc(&buf, _("Tables:"), false, &cont))
6759 goto error_return;
6760
6761 if (pset.sversion >= 150000)
6762 {
6763 /* Get the schemas for the specified publication */
6765 "SELECT n.nspname\n"
6766 "FROM pg_catalog.pg_namespace n\n"
6767 " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
6768 "WHERE pn.pnpubid = '%s'\n"
6769 "ORDER BY 1", pubid);
6770 if (!addFooterToPublicationDesc(&buf, _("Tables from schemas:"),
6771 true, &cont))
6772 goto error_return;
6773 }
6774 }
6775
6776 printTable(&cont, pset.queryFout, false, pset.logfile);
6777 printTableCleanup(&cont);
6778
6779 termPQExpBuffer(&title);
6780 }
6781
6783 PQclear(res);
6784
6785 return true;
6786
6787error_return:
6788 printTableCleanup(&cont);
6789 PQclear(res);
6791 termPQExpBuffer(&title);
6792 return false;
6793}
6794
6795/*
6796 * \dRs
6797 * Describes subscriptions.
6798 *
6799 * Takes an optional regexp to select particular subscriptions
6800 */
6801bool
6802describeSubscriptions(const char *pattern, bool verbose)
6803{
6805 PGresult *res;
6806 printQueryOpt myopt = pset.popt;
6807 static const bool translate_columns[] = {false, false, false, false,
6808 false, false, false, false, false, false, false, false, false, false,
6809 false, false, false, false};
6810
6811 if (pset.sversion < 100000)
6812 {
6813 char sverbuf[32];
6814
6815 pg_log_error("The server (version %s) does not support subscriptions.",
6817 sverbuf, sizeof(sverbuf)));
6818 return true;
6819 }
6820
6822
6824 "SELECT subname AS \"%s\"\n"
6825 ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
6826 ", subenabled AS \"%s\"\n"
6827 ", subpublications AS \"%s\"\n",
6828 gettext_noop("Name"),
6829 gettext_noop("Owner"),
6830 gettext_noop("Enabled"),
6831 gettext_noop("Publication"));
6832
6833 if (verbose)
6834 {
6835 /* Binary mode and streaming are only supported in v14 and higher */
6836 if (pset.sversion >= 140000)
6837 {
6839 ", subbinary AS \"%s\"\n",
6840 gettext_noop("Binary"));
6841
6842 if (pset.sversion >= 160000)
6844 ", (CASE substream\n"
6845 " WHEN " CppAsString2(LOGICALREP_STREAM_OFF) " THEN 'off'\n"
6846 " WHEN " CppAsString2(LOGICALREP_STREAM_ON) " THEN 'on'\n"
6847 " WHEN " CppAsString2(LOGICALREP_STREAM_PARALLEL) " THEN 'parallel'\n"
6848 " END) AS \"%s\"\n",
6849 gettext_noop("Streaming"));
6850 else
6852 ", substream AS \"%s\"\n",
6853 gettext_noop("Streaming"));
6854 }
6855
6856 /* Two_phase and disable_on_error are only supported in v15 and higher */
6857 if (pset.sversion >= 150000)
6859 ", subtwophasestate AS \"%s\"\n"
6860 ", subdisableonerr AS \"%s\"\n",
6861 gettext_noop("Two-phase commit"),
6862 gettext_noop("Disable on error"));
6863
6864 if (pset.sversion >= 160000)
6866 ", suborigin AS \"%s\"\n"
6867 ", subpasswordrequired AS \"%s\"\n"
6868 ", subrunasowner AS \"%s\"\n",
6869 gettext_noop("Origin"),
6870 gettext_noop("Password required"),
6871 gettext_noop("Run as owner?"));
6872
6873 if (pset.sversion >= 170000)
6875 ", subfailover AS \"%s\"\n",
6876 gettext_noop("Failover"));
6877 if (pset.sversion >= 190000)
6878 {
6880 ", subretaindeadtuples AS \"%s\"\n",
6881 gettext_noop("Retain dead tuples"));
6882
6884 ", submaxretention AS \"%s\"\n",
6885 gettext_noop("Max retention duration"));
6886
6888 ", subretentionactive AS \"%s\"\n",
6889 gettext_noop("Retention active"));
6890 }
6891
6893 ", subsynccommit AS \"%s\"\n"
6894 ", subconninfo AS \"%s\"\n",
6895 gettext_noop("Synchronous commit"),
6896 gettext_noop("Conninfo"));
6897
6898 /* Skip LSN is only supported in v15 and higher */
6899 if (pset.sversion >= 150000)
6901 ", subskiplsn AS \"%s\"\n",
6902 gettext_noop("Skip LSN"));
6903 }
6904
6905 /* Only display subscriptions in current database. */
6907 "FROM pg_catalog.pg_subscription\n"
6908 "WHERE subdbid = (SELECT oid\n"
6909 " FROM pg_catalog.pg_database\n"
6910 " WHERE datname = pg_catalog.current_database())");
6911
6912 if (!validateSQLNamePattern(&buf, pattern, true, false,
6913 NULL, "subname", NULL,
6914 NULL,
6915 NULL, 1))
6916 {
6918 return false;
6919 }
6920
6921 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6922
6923 res = PSQLexec(buf.data);
6925 if (!res)
6926 return false;
6927
6928 myopt.title = _("List of subscriptions");
6929 myopt.translate_header = true;
6930 myopt.translate_columns = translate_columns;
6931 myopt.n_translate_columns = lengthof(translate_columns);
6932
6933 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6934
6935 PQclear(res);
6936 return true;
6937}
6938
6939/*
6940 * printACLColumn
6941 *
6942 * Helper function for consistently formatting ACL (privilege) columns.
6943 * The proper targetlist entry is appended to buf. Note lack of any
6944 * whitespace or comma decoration.
6945 *
6946 * If you change this, see also the handling of attacl in permissionsList(),
6947 * which can't conveniently use this code.
6948 */
6949static void
6950printACLColumn(PQExpBuffer buf, const char *colname)
6951{
6953 "CASE"
6954 " WHEN pg_catalog.array_length(%s, 1) = 0 THEN '%s'"
6955 " ELSE pg_catalog.array_to_string(%s, E'\\n')"
6956 " END AS \"%s\"",
6957 colname, gettext_noop("(none)"),
6958 colname, gettext_noop("Access privileges"));
6959}
6960
6961/*
6962 * \dAc
6963 * Lists operator classes
6964 *
6965 * Takes optional regexps to filter by index access method and input data type.
6966 */
6967bool
6968listOperatorClasses(const char *access_method_pattern,
6969 const char *type_pattern, bool verbose)
6970{
6972 PGresult *res;
6973 printQueryOpt myopt = pset.popt;
6974 bool have_where = false;
6975 static const bool translate_columns[] = {false, false, false, false, false, false, false};
6976
6978
6980 "SELECT\n"
6981 " am.amname AS \"%s\",\n"
6982 " pg_catalog.format_type(c.opcintype, NULL) AS \"%s\",\n"
6983 " CASE\n"
6984 " WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n"
6985 " THEN pg_catalog.format_type(c.opckeytype, NULL)\n"
6986 " ELSE NULL\n"
6987 " END AS \"%s\",\n"
6988 " CASE\n"
6989 " WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n"
6990 " THEN pg_catalog.format('%%I', c.opcname)\n"
6991 " ELSE pg_catalog.format('%%I.%%I', n.nspname, c.opcname)\n"
6992 " END AS \"%s\",\n"
6993 " (CASE WHEN c.opcdefault\n"
6994 " THEN '%s'\n"
6995 " ELSE '%s'\n"
6996 " END) AS \"%s\"",
6997 gettext_noop("AM"),
6998 gettext_noop("Input type"),
6999 gettext_noop("Storage type"),
7000 gettext_noop("Operator class"),
7001 gettext_noop("yes"),
7002 gettext_noop("no"),
7003 gettext_noop("Default?"));
7004 if (verbose)
7006 ",\n CASE\n"
7007 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7008 " THEN pg_catalog.format('%%I', of.opfname)\n"
7009 " ELSE pg_catalog.format('%%I.%%I', ofn.nspname, of.opfname)\n"
7010 " END AS \"%s\",\n"
7011 " pg_catalog.pg_get_userbyid(c.opcowner) AS \"%s\"\n",
7012 gettext_noop("Operator family"),
7013 gettext_noop("Owner"));
7015 "\nFROM pg_catalog.pg_opclass c\n"
7016 " LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n"
7017 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n"
7018 " LEFT JOIN pg_catalog.pg_type t ON t.oid = c.opcintype\n"
7019 " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n");
7020 if (verbose)
7022 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n"
7023 " LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n");
7024
7025 if (access_method_pattern)
7026 if (!validateSQLNamePattern(&buf, access_method_pattern,
7027 false, false, NULL, "am.amname", NULL, NULL,
7028 &have_where, 1))
7029 goto error_return;
7030 if (type_pattern)
7031 {
7032 /* Match type name pattern against either internal or external name */
7033 if (!validateSQLNamePattern(&buf, type_pattern, have_where, false,
7034 "tn.nspname", "t.typname",
7035 "pg_catalog.format_type(t.oid, NULL)",
7036 "pg_catalog.pg_type_is_visible(t.oid)",
7037 NULL, 3))
7038 goto error_return;
7039 }
7040
7041 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
7042 res = PSQLexec(buf.data);
7044 if (!res)
7045 return false;
7046
7047 myopt.title = _("List of operator classes");
7048 myopt.translate_header = true;
7049 myopt.translate_columns = translate_columns;
7050 myopt.n_translate_columns = lengthof(translate_columns);
7051
7052 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7053
7054 PQclear(res);
7055 return true;
7056
7057error_return:
7059 return false;
7060}
7061
7062/*
7063 * \dAf
7064 * Lists operator families
7065 *
7066 * Takes optional regexps to filter by index access method and input data type.
7067 */
7068bool
7069listOperatorFamilies(const char *access_method_pattern,
7070 const char *type_pattern, bool verbose)
7071{
7073 PGresult *res;
7074 printQueryOpt myopt = pset.popt;
7075 bool have_where = false;
7076 static const bool translate_columns[] = {false, false, false, false};
7077
7079
7081 "SELECT\n"
7082 " am.amname AS \"%s\",\n"
7083 " CASE\n"
7084 " WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n"
7085 " THEN pg_catalog.format('%%I', f.opfname)\n"
7086 " ELSE pg_catalog.format('%%I.%%I', n.nspname, f.opfname)\n"
7087 " END AS \"%s\",\n"
7088 " (SELECT\n"
7089 " pg_catalog.string_agg(pg_catalog.format_type(oc.opcintype, NULL), ', ')\n"
7090 " FROM pg_catalog.pg_opclass oc\n"
7091 " WHERE oc.opcfamily = f.oid) \"%s\"",
7092 gettext_noop("AM"),
7093 gettext_noop("Operator family"),
7094 gettext_noop("Applicable types"));
7095 if (verbose)
7097 ",\n pg_catalog.pg_get_userbyid(f.opfowner) AS \"%s\"\n",
7098 gettext_noop("Owner"));
7100 "\nFROM pg_catalog.pg_opfamily f\n"
7101 " LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n"
7102 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace\n");
7103
7104 if (access_method_pattern)
7105 if (!validateSQLNamePattern(&buf, access_method_pattern,
7106 false, false, NULL, "am.amname", NULL, NULL,
7107 &have_where, 1))
7108 goto error_return;
7109 if (type_pattern)
7110 {
7112 " %s EXISTS (\n"
7113 " SELECT 1\n"
7114 " FROM pg_catalog.pg_type t\n"
7115 " JOIN pg_catalog.pg_opclass oc ON oc.opcintype = t.oid\n"
7116 " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n"
7117 " WHERE oc.opcfamily = f.oid\n",
7118 have_where ? "AND" : "WHERE");
7119 /* Match type name pattern against either internal or external name */
7120 if (!validateSQLNamePattern(&buf, type_pattern, true, false,
7121 "tn.nspname", "t.typname",
7122 "pg_catalog.format_type(t.oid, NULL)",
7123 "pg_catalog.pg_type_is_visible(t.oid)",
7124 NULL, 3))
7125 goto error_return;
7126 appendPQExpBufferStr(&buf, " )\n");
7127 }
7128
7129 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
7130 res = PSQLexec(buf.data);
7132 if (!res)
7133 return false;
7134
7135 myopt.title = _("List of operator families");
7136 myopt.translate_header = true;
7137 myopt.translate_columns = translate_columns;
7138 myopt.n_translate_columns = lengthof(translate_columns);
7139
7140 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7141
7142 PQclear(res);
7143 return true;
7144
7145error_return:
7147 return false;
7148}
7149
7150/*
7151 * \dAo
7152 * Lists operators of operator families
7153 *
7154 * Takes optional regexps to filter by index access method and operator
7155 * family.
7156 */
7157bool
7158listOpFamilyOperators(const char *access_method_pattern,
7159 const char *family_pattern, bool verbose)
7160{
7162 PGresult *res;
7163 printQueryOpt myopt = pset.popt;
7164 bool have_where = false;
7165
7166 static const bool translate_columns[] = {false, false, false, false, false, false, true};
7167
7169
7171 "SELECT\n"
7172 " am.amname AS \"%s\",\n"
7173 " CASE\n"
7174 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7175 " THEN pg_catalog.format('%%I', of.opfname)\n"
7176 " ELSE pg_catalog.format('%%I.%%I', nsf.nspname, of.opfname)\n"
7177 " END AS \"%s\",\n"
7178 " o.amopopr::pg_catalog.regoperator AS \"%s\"\n,"
7179 " o.amopstrategy AS \"%s\",\n"
7180 " CASE o.amoppurpose\n"
7181 " WHEN " CppAsString2(AMOP_ORDER) " THEN '%s'\n"
7182 " WHEN " CppAsString2(AMOP_SEARCH) " THEN '%s'\n"
7183 " END AS \"%s\"\n",
7184 gettext_noop("AM"),
7185 gettext_noop("Operator family"),
7186 gettext_noop("Operator"),
7187 gettext_noop("Strategy"),
7188 gettext_noop("ordering"),
7189 gettext_noop("search"),
7190 gettext_noop("Purpose"));
7191
7192 if (verbose)
7194 ", ofs.opfname AS \"%s\",\n"
7195 " CASE\n"
7196 " WHEN p.proleakproof THEN '%s'\n"
7197 " ELSE '%s'\n"
7198 " END AS \"%s\"\n",
7199 gettext_noop("Sort opfamily"),
7200 gettext_noop("yes"),
7201 gettext_noop("no"),
7202 gettext_noop("Leakproof?"));
7204 "FROM pg_catalog.pg_amop o\n"
7205 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n"
7206 " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n"
7207 " LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n");
7208 if (verbose)
7210 " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n"
7211 " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n"
7212 " LEFT JOIN pg_catalog.pg_proc p ON p.oid = op.oprcode\n");
7213
7214 if (access_method_pattern)
7215 {
7216 if (!validateSQLNamePattern(&buf, access_method_pattern,
7217 false, false, NULL, "am.amname",
7218 NULL, NULL,
7219 &have_where, 1))
7220 goto error_return;
7221 }
7222
7223 if (family_pattern)
7224 {
7225 if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
7226 "nsf.nspname", "of.opfname", NULL, NULL,
7227 NULL, 3))
7228 goto error_return;
7229 }
7230
7231 appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7232 " o.amoplefttype = o.amoprighttype DESC,\n"
7233 " pg_catalog.format_type(o.amoplefttype, NULL),\n"
7234 " pg_catalog.format_type(o.amoprighttype, NULL),\n"
7235 " o.amopstrategy;");
7236
7237 res = PSQLexec(buf.data);
7239 if (!res)
7240 return false;
7241
7242 myopt.title = _("List of operators of operator families");
7243 myopt.translate_header = true;
7244 myopt.translate_columns = translate_columns;
7245 myopt.n_translate_columns = lengthof(translate_columns);
7246
7247 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7248
7249 PQclear(res);
7250 return true;
7251
7252error_return:
7254 return false;
7255}
7256
7257/*
7258 * \dAp
7259 * Lists support functions of operator families
7260 *
7261 * Takes optional regexps to filter by index access method and operator
7262 * family.
7263 */
7264bool
7265listOpFamilyFunctions(const char *access_method_pattern,
7266 const char *family_pattern, bool verbose)
7267{
7269 PGresult *res;
7270 printQueryOpt myopt = pset.popt;
7271 bool have_where = false;
7272 static const bool translate_columns[] = {false, false, false, false, false, false};
7273
7275
7277 "SELECT\n"
7278 " am.amname AS \"%s\",\n"
7279 " CASE\n"
7280 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7281 " THEN pg_catalog.format('%%I', of.opfname)\n"
7282 " ELSE pg_catalog.format('%%I.%%I', ns.nspname, of.opfname)\n"
7283 " END AS \"%s\",\n"
7284 " pg_catalog.format_type(ap.amproclefttype, NULL) AS \"%s\",\n"
7285 " pg_catalog.format_type(ap.amprocrighttype, NULL) AS \"%s\",\n"
7286 " ap.amprocnum AS \"%s\"\n",
7287 gettext_noop("AM"),
7288 gettext_noop("Operator family"),
7289 gettext_noop("Registered left type"),
7290 gettext_noop("Registered right type"),
7291 gettext_noop("Number"));
7292
7293 if (!verbose)
7295 ", p.proname AS \"%s\"\n",
7296 gettext_noop("Function"));
7297 else
7299 ", ap.amproc::pg_catalog.regprocedure AS \"%s\"\n",
7300 gettext_noop("Function"));
7301
7303 "FROM pg_catalog.pg_amproc ap\n"
7304 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n"
7305 " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n"
7306 " LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n"
7307 " LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.oid\n");
7308
7309 if (access_method_pattern)
7310 {
7311 if (!validateSQLNamePattern(&buf, access_method_pattern,
7312 false, false, NULL, "am.amname",
7313 NULL, NULL,
7314 &have_where, 1))
7315 goto error_return;
7316 }
7317 if (family_pattern)
7318 {
7319 if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
7320 "ns.nspname", "of.opfname", NULL, NULL,
7321 NULL, 3))
7322 goto error_return;
7323 }
7324
7325 appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7326 " ap.amproclefttype = ap.amprocrighttype DESC,\n"
7327 " 3, 4, 5;");
7328
7329 res = PSQLexec(buf.data);
7331 if (!res)
7332 return false;
7333
7334 myopt.title = _("List of support functions of operator families");
7335 myopt.translate_header = true;
7336 myopt.translate_columns = translate_columns;
7337 myopt.n_translate_columns = lengthof(translate_columns);
7338
7339 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7340
7341 PQclear(res);
7342 return true;
7343
7344error_return:
7346 return false;
7347}
7348
7349/*
7350 * \dl or \lo_list
7351 * Lists large objects
7352 */
7353bool
7355{
7357 PGresult *res;
7358 printQueryOpt myopt = pset.popt;
7359
7361
7363 "SELECT oid as \"%s\",\n"
7364 " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n ",
7365 gettext_noop("ID"),
7366 gettext_noop("Owner"));
7367
7368 if (verbose)
7369 {
7370 printACLColumn(&buf, "lomacl");
7371 appendPQExpBufferStr(&buf, ",\n ");
7372 }
7373
7375 "pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
7376 "FROM pg_catalog.pg_largeobject_metadata\n"
7377 "ORDER BY oid",
7378 gettext_noop("Description"));
7379
7380 res = PSQLexec(buf.data);
7382 if (!res)
7383 return false;
7384
7385 myopt.title = _("Large objects");
7386 myopt.translate_header = true;
7387
7388 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7389
7390 PQclear(res);
7391 return true;
7392}
PGresult * PSQLexec(const char *query)
Definition: common.c:655
#define ngettext(s, p, n)
Definition: c.h:1181
#define gettext_noop(x)
Definition: c.h:1196
int16_t int16
Definition: c.h:538
#define CppAsString2(x)
Definition: c.h:423
#define lengthof(array)
Definition: c.h:792
bool listUserMappings(const char *pattern, bool verbose)
Definition: describe.c:6085
bool listTSConfigs(const char *pattern, bool verbose)
Definition: describe.c:5734
bool describeRoles(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:3746
bool listOpFamilyFunctions(const char *access_method_pattern, const char *family_pattern, bool verbose)
Definition: describe.c:7265
bool listPublications(const char *pattern)
Definition: describe.c:6430
bool listTSParsers(const char *pattern, bool verbose)
Definition: describe.c:5357
bool listExtensionContents(const char *pattern)
Definition: describe.c:6266
bool describeAggregates(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:78
bool listForeignDataWrappers(const char *pattern, bool verbose)
Definition: describe.c:5938
bool listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
Definition: describe.c:4296
bool describeSubscriptions(const char *pattern, bool verbose)
Definition: describe.c:6802
bool describeRoleGrants(const char *pattern, bool showSystem)
Definition: describe.c:3962
bool listExtendedStats(const char *pattern)
Definition: describe.c:4893
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:7069
bool listForeignServers(const char *pattern, bool verbose)
Definition: describe.c:6009
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:4582
bool listTSDictionaries(const char *pattern, bool verbose)
Definition: describe.c:5604
bool listDbRoleSettings(const char *pattern, const char *pattern2)
Definition: describe.c:3893
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:3881
bool listCollations(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:5113
bool listSchemas(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:5236
bool listExtensions(const char *pattern)
Definition: describe.c:6212
static bool describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, const char *pnspname, const char *prsname)
Definition: describe.c:5867
static bool listOneExtensionContents(const char *extname, const char *oid)
Definition: describe.c:6333
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:5409
bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:4041
bool listTSTemplates(const char *pattern, bool verbose)
Definition: describe.c:5669
bool describeTablespaces(const char *pattern, bool verbose)
Definition: describe.c:222
bool listCasts(const char *pattern, bool verbose)
Definition: describe.c:4989
bool listOpFamilyOperators(const char *access_method_pattern, const char *family_pattern, bool verbose)
Definition: describe.c:7158
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:6968
static const char * map_typename_pattern(const char *pattern)
Definition: describe.c:744
bool listForeignTables(const char *pattern, bool verbose)
Definition: describe.c:6140
bool describeConfigurationParameters(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:4745
bool listEventTriggers(const char *pattern, bool verbose)
Definition: describe.c:4813
bool listDefaultACLs(const char *pattern)
Definition: describe.c:1214
static void printACLColumn(PQExpBuffer buf, const char *colname)
Definition: describe.c:6950
static bool addFooterToPublicationDesc(PQExpBuffer buf, const char *footermsg, bool as_schema, printTableContent *const cont)
Definition: describe.c:6522
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:6377
bool listAllDbs(const char *pattern, bool verbose)
Definition: describe.c:946
bool listConversions(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:4665
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:5783
bool listLargeObjects(bool verbose)
Definition: describe.c:7354
static bool describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
Definition: describe.c:5484
static void add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline)
Definition: describe.c:3681
bool listLanguages(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:4506
bool describePublications(const char *pattern)
Definition: describe.c:6568
bool objectDescription(const char *pattern, bool showSystem)
Definition: describe.c:1295
#define _(x)
Definition: elog.c:91
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:7538
int PQfnumber(const PGresult *res, const char *field_name)
Definition: fe-exec.c:3605
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
void pg_free(void *ptr)
Definition: fe_memutils.c:105
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
Assert(PointerIsAligned(start, uint64))
const char * str
#define free(a)
Definition: header.h:65
#define storage
Definition: indent_codes.h:68
#define newline
Definition: indent_codes.h:35
int verbose
int i
Definition: isn.c:77
static IsoConnInfo * conns
#define PQgetvalue
Definition: libpq-be-fe.h:253
#define PQclear
Definition: libpq-be-fe.h:245
#define PQgetisnull
Definition: libpq-be-fe.h:255
#define PQntuples
Definition: libpq-be-fe.h:251
#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
NameData relname
Definition: pg_class.h:38
static char * buf
Definition: pg_test_fsync.c:72
NameData typname
Definition: pg_type.h:41
static char * tablespace
Definition: pgbench.c:217
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
#define snprintf
Definition: port.h:239
unsigned int Oid
Definition: postgres_ext.h:32
#define atooid(x)
Definition: postgres_ext.h:43
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:235
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:146
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
PsqlSettings pset
Definition: startup.c:32
const char * fmtId(const char *rawid)
Definition: string_utils.c:248
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)
Definition: string_utils.c:313
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
const bool * translate_columns
Definition: print.h:192
printTableOpt topt
Definition: print.h:185
char * title
Definition: print.h:189
char ** footers
Definition: print.h:190
bool translate_header
Definition: print.h:191
int n_translate_columns
Definition: print.h:194
printTableFooter * footer
Definition: print.h:177
char * data
Definition: print.h:155
unsigned short int expanded
Definition: print.h:114
bool default_footer
Definition: print.h:129
static StringInfoData tmpbuf
Definition: walsender.c:178