Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

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[2] = {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 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
1859 printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
1860 schemaname, relationname);
1861 else
1862 printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
1863 schemaname, relationname);
1864
1865 myopt.footers = footers;
1866 myopt.topt.default_footer = false;
1867 myopt.title = title.data;
1868 myopt.translate_header = true;
1869
1870 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1871
1872 free(footers[0]);
1873
1874 retval = true;
1875 goto error_return; /* not an error, just return early */
1876 }
1877
1878 /* Identify whether we should print collation, nullable, default vals */
1879 if (tableinfo.relkind == RELKIND_RELATION ||
1880 tableinfo.relkind == RELKIND_VIEW ||
1881 tableinfo.relkind == RELKIND_MATVIEW ||
1882 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1883 tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
1884 tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
1885 show_column_details = true;
1886
1887 /*
1888 * Get per-column info
1889 *
1890 * Since the set of query columns we need varies depending on relkind and
1891 * server version, we compute all the column numbers on-the-fly. Column
1892 * number variables for columns not fetched are left as -1; this avoids
1893 * duplicative test logic below.
1894 */
1895 cols = 0;
1896 printfPQExpBuffer(&buf, "SELECT a.attname");
1897 attname_col = cols++;
1898 appendPQExpBufferStr(&buf, ",\n pg_catalog.format_type(a.atttypid, a.atttypmod)");
1899 atttype_col = cols++;
1900
1901 if (show_column_details)
1902 {
1903 /* use "pretty" mode for expression to avoid excessive parentheses */
1905 ",\n (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)"
1906 "\n FROM pg_catalog.pg_attrdef d"
1907 "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)"
1908 ",\n a.attnotnull");
1909 attrdef_col = cols++;
1910 attnotnull_col = cols++;
1911 appendPQExpBufferStr(&buf, ",\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
1912 " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
1913 attcoll_col = cols++;
1914 if (pset.sversion >= 100000)
1915 appendPQExpBufferStr(&buf, ",\n a.attidentity");
1916 else
1917 appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attidentity");
1918 attidentity_col = cols++;
1919 if (pset.sversion >= 120000)
1920 appendPQExpBufferStr(&buf, ",\n a.attgenerated");
1921 else
1922 appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attgenerated");
1923 attgenerated_col = cols++;
1924 }
1925 if (tableinfo.relkind == RELKIND_INDEX ||
1926 tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
1927 {
1928 if (pset.sversion >= 110000)
1929 {
1930 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",
1931 oid,
1932 gettext_noop("yes"),
1933 gettext_noop("no"));
1934 isindexkey_col = cols++;
1935 }
1936 appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
1937 indexdef_col = cols++;
1938 }
1939 /* FDW options for foreign table column */
1940 if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
1941 {
1942 appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE "
1943 " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM "
1944 " pg_catalog.pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions");
1945 fdwopts_col = cols++;
1946 }
1947 if (verbose)
1948 {
1949 appendPQExpBufferStr(&buf, ",\n a.attstorage");
1950 attstorage_col = cols++;
1951
1952 /* compression info, if relevant to relkind */
1953 if (pset.sversion >= 140000 &&
1955 (tableinfo.relkind == RELKIND_RELATION ||
1956 tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
1957 tableinfo.relkind == RELKIND_MATVIEW))
1958 {
1959 appendPQExpBufferStr(&buf, ",\n a.attcompression AS attcompression");
1960 attcompression_col = cols++;
1961 }
1962
1963 /* stats target, if relevant to relkind */
1964 if (tableinfo.relkind == RELKIND_RELATION ||
1965 tableinfo.relkind == RELKIND_INDEX ||
1966 tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
1967 tableinfo.relkind == RELKIND_MATVIEW ||
1968 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1969 tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
1970 {
1971 appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
1972 attstattarget_col = cols++;
1973 }
1974
1975 /*
1976 * In 9.0+, we have column comments for: relations, views, composite
1977 * types, and foreign tables (cf. CommentObject() in comment.c).
1978 */
1979 if (tableinfo.relkind == RELKIND_RELATION ||
1980 tableinfo.relkind == RELKIND_VIEW ||
1981 tableinfo.relkind == RELKIND_MATVIEW ||
1982 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1983 tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
1984 tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
1985 {
1986 appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)");
1987 attdescr_col = cols++;
1988 }
1989 }
1990
1991 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
1992 appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
1993 appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
1994
1995 res = PSQLexec(buf.data);
1996 if (!res)
1997 goto error_return;
1998 numrows = PQntuples(res);
1999
2000 /* Make title */
2001 switch (tableinfo.relkind)
2002 {
2003 case RELKIND_RELATION:
2004 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2005 printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
2006 schemaname, relationname);
2007 else
2008 printfPQExpBuffer(&title, _("Table \"%s.%s\""),
2009 schemaname, relationname);
2010 break;
2011 case RELKIND_VIEW:
2012 printfPQExpBuffer(&title, _("View \"%s.%s\""),
2013 schemaname, relationname);
2014 break;
2015 case RELKIND_MATVIEW:
2016 printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""),
2017 schemaname, relationname);
2018 break;
2019 case RELKIND_INDEX:
2020 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2021 printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
2022 schemaname, relationname);
2023 else
2024 printfPQExpBuffer(&title, _("Index \"%s.%s\""),
2025 schemaname, relationname);
2026 break;
2027 case RELKIND_PARTITIONED_INDEX:
2028 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2029 printfPQExpBuffer(&title, _("Unlogged partitioned index \"%s.%s\""),
2030 schemaname, relationname);
2031 else
2032 printfPQExpBuffer(&title, _("Partitioned index \"%s.%s\""),
2033 schemaname, relationname);
2034 break;
2035 case RELKIND_TOASTVALUE:
2036 printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
2037 schemaname, relationname);
2038 break;
2039 case RELKIND_COMPOSITE_TYPE:
2040 printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
2041 schemaname, relationname);
2042 break;
2043 case RELKIND_FOREIGN_TABLE:
2044 printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
2045 schemaname, relationname);
2046 break;
2047 case RELKIND_PARTITIONED_TABLE:
2048 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2049 printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""),
2050 schemaname, relationname);
2051 else
2052 printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""),
2053 schemaname, relationname);
2054 break;
2055 default:
2056 /* untranslated unknown relkind */
2057 printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
2058 tableinfo.relkind, schemaname, relationname);
2059 break;
2060 }
2061
2062 /* Fill headers[] with the names of the columns we will output */
2063 cols = 0;
2064 headers[cols++] = gettext_noop("Column");
2065 headers[cols++] = gettext_noop("Type");
2066 if (show_column_details)
2067 {
2068 headers[cols++] = gettext_noop("Collation");
2069 headers[cols++] = gettext_noop("Nullable");
2070 headers[cols++] = gettext_noop("Default");
2071 }
2072 if (isindexkey_col >= 0)
2073 headers[cols++] = gettext_noop("Key?");
2074 if (indexdef_col >= 0)
2075 headers[cols++] = gettext_noop("Definition");
2076 if (fdwopts_col >= 0)
2077 headers[cols++] = gettext_noop("FDW options");
2078 if (attstorage_col >= 0)
2079 headers[cols++] = gettext_noop("Storage");
2080 if (attcompression_col >= 0)
2081 headers[cols++] = gettext_noop("Compression");
2082 if (attstattarget_col >= 0)
2083 headers[cols++] = gettext_noop("Stats target");
2084 if (attdescr_col >= 0)
2085 headers[cols++] = gettext_noop("Description");
2086
2087 Assert(cols <= lengthof(headers));
2088
2089 printTableInit(&cont, &myopt, title.data, cols, numrows);
2090 printTableInitialized = true;
2091
2092 for (i = 0; i < cols; i++)
2093 printTableAddHeader(&cont, headers[i], true, 'l');
2094
2095 /* Generate table cells to be printed */
2096 for (i = 0; i < numrows; i++)
2097 {
2098 /* Column */
2099 printTableAddCell(&cont, PQgetvalue(res, i, attname_col), false, false);
2100
2101 /* Type */
2102 printTableAddCell(&cont, PQgetvalue(res, i, atttype_col), false, false);
2103
2104 /* Collation, Nullable, Default */
2105 if (show_column_details)
2106 {
2107 char *identity;
2108 char *generated;
2109 char *default_str;
2110 bool mustfree = false;
2111
2112 printTableAddCell(&cont, PQgetvalue(res, i, attcoll_col), false, false);
2113
2114 printTableAddCell(&cont,
2115 strcmp(PQgetvalue(res, i, attnotnull_col), "t") == 0 ? "not null" : "",
2116 false, false);
2117
2118 identity = PQgetvalue(res, i, attidentity_col);
2119 generated = PQgetvalue(res, i, attgenerated_col);
2120
2121 if (identity[0] == ATTRIBUTE_IDENTITY_ALWAYS)
2122 default_str = "generated always as identity";
2123 else if (identity[0] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
2124 default_str = "generated by default as identity";
2125 else if (generated[0] == ATTRIBUTE_GENERATED_STORED)
2126 {
2127 default_str = psprintf("generated always as (%s) stored",
2128 PQgetvalue(res, i, attrdef_col));
2129 mustfree = true;
2130 }
2131 else if (generated[0] == ATTRIBUTE_GENERATED_VIRTUAL)
2132 {
2133 default_str = psprintf("generated always as (%s)",
2134 PQgetvalue(res, i, attrdef_col));
2135 mustfree = true;
2136 }
2137 else
2138 default_str = PQgetvalue(res, i, attrdef_col);
2139
2140 printTableAddCell(&cont, default_str, false, mustfree);
2141 }
2142
2143 /* Info for index columns */
2144 if (isindexkey_col >= 0)
2145 printTableAddCell(&cont, PQgetvalue(res, i, isindexkey_col), true, false);
2146 if (indexdef_col >= 0)
2147 printTableAddCell(&cont, PQgetvalue(res, i, indexdef_col), false, false);
2148
2149 /* FDW options for foreign table columns */
2150 if (fdwopts_col >= 0)
2151 printTableAddCell(&cont, PQgetvalue(res, i, fdwopts_col), false, false);
2152
2153 /* Storage mode, if relevant */
2154 if (attstorage_col >= 0)
2155 {
2156 char *storage = PQgetvalue(res, i, attstorage_col);
2157
2158 /* these strings are literal in our syntax, so not translated. */
2159 printTableAddCell(&cont, (storage[0] == TYPSTORAGE_PLAIN ? "plain" :
2160 (storage[0] == TYPSTORAGE_MAIN ? "main" :
2161 (storage[0] == TYPSTORAGE_EXTENDED ? "extended" :
2162 (storage[0] == TYPSTORAGE_EXTERNAL ? "external" :
2163 "???")))),
2164 false, false);
2165 }
2166
2167 /* Column compression, if relevant */
2168 if (attcompression_col >= 0)
2169 {
2170 char *compression = PQgetvalue(res, i, attcompression_col);
2171
2172 /* these strings are literal in our syntax, so not translated. */
2173 printTableAddCell(&cont, (compression[0] == 'p' ? "pglz" :
2174 (compression[0] == 'l' ? "lz4" :
2175 (compression[0] == '\0' ? "" :
2176 "???"))),
2177 false, false);
2178 }
2179
2180 /* Statistics target, if the relkind supports this feature */
2181 if (attstattarget_col >= 0)
2182 printTableAddCell(&cont, PQgetvalue(res, i, attstattarget_col),
2183 false, false);
2184
2185 /* Column comments, if the relkind supports this feature */
2186 if (attdescr_col >= 0)
2187 printTableAddCell(&cont, PQgetvalue(res, i, attdescr_col),
2188 false, false);
2189 }
2190
2191 /* Make footers */
2192
2193 if (tableinfo.ispartition)
2194 {
2195 /* Footer information for a partition child table */
2196 PGresult *result;
2197
2199 "SELECT inhparent::pg_catalog.regclass,\n"
2200 " pg_catalog.pg_get_expr(c.relpartbound, c.oid),\n ");
2201
2203 pset.sversion >= 140000 ? "inhdetachpending" :
2204 "false as inhdetachpending");
2205
2206 /* If verbose, also request the partition constraint definition */
2207 if (verbose)
2209 ",\n pg_catalog.pg_get_partition_constraintdef(c.oid)");
2211 "\nFROM pg_catalog.pg_class c"
2212 " JOIN pg_catalog.pg_inherits i"
2213 " ON c.oid = inhrelid"
2214 "\nWHERE c.oid = '%s';", oid);
2215 result = PSQLexec(buf.data);
2216 if (!result)
2217 goto error_return;
2218
2219 if (PQntuples(result) > 0)
2220 {
2221 char *parent_name = PQgetvalue(result, 0, 0);
2222 char *partdef = PQgetvalue(result, 0, 1);
2223 char *detached = PQgetvalue(result, 0, 2);
2224
2225 printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s%s"), parent_name,
2226 partdef,
2227 strcmp(detached, "t") == 0 ? " DETACH PENDING" : "");
2229
2230 if (verbose)
2231 {
2232 char *partconstraintdef = NULL;
2233
2234 if (!PQgetisnull(result, 0, 3))
2235 partconstraintdef = PQgetvalue(result, 0, 3);
2236 /* If there isn't any constraint, show that explicitly */
2237 if (partconstraintdef == NULL || partconstraintdef[0] == '\0')
2238 printfPQExpBuffer(&tmpbuf, _("No partition constraint"));
2239 else
2240 printfPQExpBuffer(&tmpbuf, _("Partition constraint: %s"),
2241 partconstraintdef);
2243 }
2244 }
2245 PQclear(result);
2246 }
2247
2248 if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2249 {
2250 /* Footer information for a partitioned table (partitioning parent) */
2251 PGresult *result;
2252
2254 "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);",
2255 oid);
2256 result = PSQLexec(buf.data);
2257 if (!result)
2258 goto error_return;
2259
2260 if (PQntuples(result) == 1)
2261 {
2262 char *partkeydef = PQgetvalue(result, 0, 0);
2263
2264 printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef);
2266 }
2267 PQclear(result);
2268 }
2269
2270 if (tableinfo.relkind == RELKIND_TOASTVALUE)
2271 {
2272 /* For a TOAST table, print name of owning table */
2273 PGresult *result;
2274
2276 "SELECT n.nspname, c.relname\n"
2277 "FROM pg_catalog.pg_class c"
2278 " JOIN pg_catalog.pg_namespace n"
2279 " ON n.oid = c.relnamespace\n"
2280 "WHERE reltoastrelid = '%s';", oid);
2281 result = PSQLexec(buf.data);
2282 if (!result)
2283 goto error_return;
2284
2285 if (PQntuples(result) == 1)
2286 {
2287 char *schemaname = PQgetvalue(result, 0, 0);
2288 char *relname = PQgetvalue(result, 0, 1);
2289
2290 printfPQExpBuffer(&tmpbuf, _("Owning table: \"%s.%s\""),
2291 schemaname, relname);
2293 }
2294 PQclear(result);
2295 }
2296
2297 if (tableinfo.relkind == RELKIND_INDEX ||
2298 tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
2299 {
2300 /* Footer information about an index */
2301 PGresult *result;
2302
2304 "SELECT i.indisunique, i.indisprimary, i.indisclustered, "
2305 "i.indisvalid,\n"
2306 " (NOT i.indimmediate) AND "
2307 "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2308 "WHERE conrelid = i.indrelid AND "
2309 "conindid = i.indexrelid AND "
2310 "contype IN (" CppAsString2(CONSTRAINT_PRIMARY) ","
2311 CppAsString2(CONSTRAINT_UNIQUE) ","
2312 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
2313 "condeferrable) AS condeferrable,\n"
2314 " (NOT i.indimmediate) AND "
2315 "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2316 "WHERE conrelid = i.indrelid AND "
2317 "conindid = i.indexrelid AND "
2318 "contype IN (" CppAsString2(CONSTRAINT_PRIMARY) ","
2319 CppAsString2(CONSTRAINT_UNIQUE) ","
2320 CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
2321 "condeferred) AS condeferred,\n");
2322
2323 if (pset.sversion >= 90400)
2324 appendPQExpBufferStr(&buf, "i.indisreplident,\n");
2325 else
2326 appendPQExpBufferStr(&buf, "false AS indisreplident,\n");
2327
2328 if (pset.sversion >= 150000)
2329 appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
2330 else
2331 appendPQExpBufferStr(&buf, "false AS indnullsnotdistinct,\n");
2332
2333 appendPQExpBuffer(&buf, " a.amname, c2.relname, "
2334 "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
2335 "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
2336 "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
2337 "AND i.indrelid = c2.oid;",
2338 oid);
2339
2340 result = PSQLexec(buf.data);
2341 if (!result)
2342 goto error_return;
2343 else if (PQntuples(result) != 1)
2344 {
2345 PQclear(result);
2346 goto error_return;
2347 }
2348 else
2349 {
2350 char *indisunique = PQgetvalue(result, 0, 0);
2351 char *indisprimary = PQgetvalue(result, 0, 1);
2352 char *indisclustered = PQgetvalue(result, 0, 2);
2353 char *indisvalid = PQgetvalue(result, 0, 3);
2354 char *deferrable = PQgetvalue(result, 0, 4);
2355 char *deferred = PQgetvalue(result, 0, 5);
2356 char *indisreplident = PQgetvalue(result, 0, 6);
2357 char *indnullsnotdistinct = PQgetvalue(result, 0, 7);
2358 char *indamname = PQgetvalue(result, 0, 8);
2359 char *indtable = PQgetvalue(result, 0, 9);
2360 char *indpred = PQgetvalue(result, 0, 10);
2361
2362 if (strcmp(indisprimary, "t") == 0)
2363 printfPQExpBuffer(&tmpbuf, _("primary key, "));
2364 else if (strcmp(indisunique, "t") == 0)
2365 {
2366 printfPQExpBuffer(&tmpbuf, _("unique"));
2367 if (strcmp(indnullsnotdistinct, "t") == 0)
2368 appendPQExpBufferStr(&tmpbuf, _(" nulls not distinct"));
2369 appendPQExpBufferStr(&tmpbuf, _(", "));
2370 }
2371 else
2373 appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
2374
2375 /* we assume here that index and table are in same schema */
2376 appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
2377 schemaname, indtable);
2378
2379 if (strlen(indpred))
2380 appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
2381
2382 if (strcmp(indisclustered, "t") == 0)
2383 appendPQExpBufferStr(&tmpbuf, _(", clustered"));
2384
2385 if (strcmp(indisvalid, "t") != 0)
2386 appendPQExpBufferStr(&tmpbuf, _(", invalid"));
2387
2388 if (strcmp(deferrable, "t") == 0)
2389 appendPQExpBufferStr(&tmpbuf, _(", deferrable"));
2390
2391 if (strcmp(deferred, "t") == 0)
2392 appendPQExpBufferStr(&tmpbuf, _(", initially deferred"));
2393
2394 if (strcmp(indisreplident, "t") == 0)
2395 appendPQExpBufferStr(&tmpbuf, _(", replica identity"));
2396
2398
2399 /*
2400 * If it's a partitioned index, we'll print the tablespace below
2401 */
2402 if (tableinfo.relkind == RELKIND_INDEX)
2403 add_tablespace_footer(&cont, tableinfo.relkind,
2404 tableinfo.tablespace, true);
2405 }
2406
2407 PQclear(result);
2408 }
2409 /* If you add relkinds here, see also "Finish printing..." stanza below */
2410 else if (tableinfo.relkind == RELKIND_RELATION ||
2411 tableinfo.relkind == RELKIND_MATVIEW ||
2412 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2413 tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
2414 tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
2415 tableinfo.relkind == RELKIND_TOASTVALUE)
2416 {
2417 /* Footer information about a table */
2418 PGresult *result = NULL;
2419 int tuples = 0;
2420
2421 /* print indexes */
2422 if (tableinfo.hasindex)
2423 {
2425 "SELECT c2.relname, i.indisprimary, i.indisunique, "
2426 "i.indisclustered, i.indisvalid, "
2427 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n "
2428 "pg_catalog.pg_get_constraintdef(con.oid, true), "
2429 "contype, condeferrable, condeferred");
2430 if (pset.sversion >= 90400)
2431 appendPQExpBufferStr(&buf, ", i.indisreplident");
2432 else
2433 appendPQExpBufferStr(&buf, ", false AS indisreplident");
2434 appendPQExpBufferStr(&buf, ", c2.reltablespace");
2435 if (pset.sversion >= 180000)
2436 appendPQExpBufferStr(&buf, ", con.conperiod");
2437 else
2438 appendPQExpBufferStr(&buf, ", false AS conperiod");
2440 "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
2441 " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ("
2442 CppAsString2(CONSTRAINT_PRIMARY) ","
2443 CppAsString2(CONSTRAINT_UNIQUE) ","
2444 CppAsString2(CONSTRAINT_EXCLUSION) "))\n"
2445 "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
2446 "ORDER BY i.indisprimary DESC, c2.relname;",
2447 oid);
2448 result = PSQLexec(buf.data);
2449 if (!result)
2450 goto error_return;
2451 else
2452 tuples = PQntuples(result);
2453
2454 if (tuples > 0)
2455 {
2456 printTableAddFooter(&cont, _("Indexes:"));
2457 for (i = 0; i < tuples; i++)
2458 {
2459 /* untranslated index name */
2460 printfPQExpBuffer(&buf, " \"%s\"",
2461 PQgetvalue(result, i, 0));
2462
2463 /*
2464 * If exclusion constraint or PK/UNIQUE constraint WITHOUT
2465 * OVERLAPS, print the constraintdef
2466 */
2467 if (strcmp(PQgetvalue(result, i, 7), "x") == 0 ||
2468 strcmp(PQgetvalue(result, i, 12), "t") == 0)
2469 {
2470 appendPQExpBuffer(&buf, " %s",
2471 PQgetvalue(result, i, 6));
2472 }
2473 else
2474 {
2475 const char *indexdef;
2476 const char *usingpos;
2477
2478 /* Label as primary key or unique (but not both) */
2479 if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
2480 appendPQExpBufferStr(&buf, " PRIMARY KEY,");
2481 else if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
2482 {
2483 if (strcmp(PQgetvalue(result, i, 7), "u") == 0)
2484 appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,");
2485 else
2486 appendPQExpBufferStr(&buf, " UNIQUE,");
2487 }
2488
2489 /* Everything after "USING" is echoed verbatim */
2490 indexdef = PQgetvalue(result, i, 5);
2491 usingpos = strstr(indexdef, " USING ");
2492 if (usingpos)
2493 indexdef = usingpos + 7;
2494 appendPQExpBuffer(&buf, " %s", indexdef);
2495
2496 /* Need these for deferrable PK/UNIQUE indexes */
2497 if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
2498 appendPQExpBufferStr(&buf, " DEFERRABLE");
2499
2500 if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
2501 appendPQExpBufferStr(&buf, " INITIALLY DEFERRED");
2502 }
2503
2504 /* Add these for all cases */
2505 if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
2506 appendPQExpBufferStr(&buf, " CLUSTER");
2507
2508 if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
2509 appendPQExpBufferStr(&buf, " INVALID");
2510
2511 if (strcmp(PQgetvalue(result, i, 10), "t") == 0)
2512 appendPQExpBufferStr(&buf, " REPLICA IDENTITY");
2513
2514 printTableAddFooter(&cont, buf.data);
2515
2516 /* Print tablespace of the index on the same line */
2517 add_tablespace_footer(&cont, RELKIND_INDEX,
2518 atooid(PQgetvalue(result, i, 11)),
2519 false);
2520 }
2521 }
2522 PQclear(result);
2523 }
2524
2525 /* print table (and column) check constraints */
2526 if (tableinfo.checks)
2527 {
2529 "SELECT r.conname, "
2530 "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
2531 "FROM pg_catalog.pg_constraint r\n"
2532 "WHERE r.conrelid = '%s' "
2533 "AND r.contype = " CppAsString2(CONSTRAINT_CHECK) "\n"
2534 "ORDER BY 1;",
2535 oid);
2536 result = PSQLexec(buf.data);
2537 if (!result)
2538 goto error_return;
2539 else
2540 tuples = PQntuples(result);
2541
2542 if (tuples > 0)
2543 {
2544 printTableAddFooter(&cont, _("Check constraints:"));
2545 for (i = 0; i < tuples; i++)
2546 {
2547 /* untranslated constraint name and def */
2548 printfPQExpBuffer(&buf, " \"%s\" %s",
2549 PQgetvalue(result, i, 0),
2550 PQgetvalue(result, i, 1));
2551
2552 printTableAddFooter(&cont, buf.data);
2553 }
2554 }
2555 PQclear(result);
2556 }
2557
2558 /* Print foreign-key constraints */
2559 if (pset.sversion >= 120000 &&
2560 (tableinfo.ispartition || tableinfo.relkind == RELKIND_PARTITIONED_TABLE))
2561 {
2562 /*
2563 * Put the constraints defined in this table first, followed by
2564 * the constraints defined in ancestor partitioned tables.
2565 */
2567 "SELECT conrelid = '%s'::pg_catalog.regclass AS sametable,\n"
2568 " conname,\n"
2569 " pg_catalog.pg_get_constraintdef(oid, true) AS condef,\n"
2570 " conrelid::pg_catalog.regclass AS ontable\n"
2571 " FROM pg_catalog.pg_constraint,\n"
2572 " pg_catalog.pg_partition_ancestors('%s')\n"
2573 " WHERE conrelid = relid AND contype = " CppAsString2(CONSTRAINT_FOREIGN) " AND conparentid = 0\n"
2574 "ORDER BY sametable DESC, conname;",
2575 oid, oid);
2576 }
2577 else
2578 {
2580 "SELECT true as sametable, conname,\n"
2581 " pg_catalog.pg_get_constraintdef(r.oid, true) as condef,\n"
2582 " conrelid::pg_catalog.regclass AS ontable\n"
2583 "FROM pg_catalog.pg_constraint r\n"
2584 "WHERE r.conrelid = '%s' AND r.contype = " CppAsString2(CONSTRAINT_FOREIGN) "\n",
2585 oid);
2586
2587 if (pset.sversion >= 120000)
2588 appendPQExpBufferStr(&buf, " AND conparentid = 0\n");
2589 appendPQExpBufferStr(&buf, "ORDER BY conname");
2590 }
2591
2592 result = PSQLexec(buf.data);
2593 if (!result)
2594 goto error_return;
2595 else
2596 tuples = PQntuples(result);
2597
2598 if (tuples > 0)
2599 {
2600 int i_sametable = PQfnumber(result, "sametable"),
2601 i_conname = PQfnumber(result, "conname"),
2602 i_condef = PQfnumber(result, "condef"),
2603 i_ontable = PQfnumber(result, "ontable");
2604
2605 printTableAddFooter(&cont, _("Foreign-key constraints:"));
2606 for (i = 0; i < tuples; i++)
2607 {
2608 /*
2609 * Print untranslated constraint name and definition. Use a
2610 * "TABLE tab" prefix when the constraint is defined in a
2611 * parent partitioned table.
2612 */
2613 if (strcmp(PQgetvalue(result, i, i_sametable), "f") == 0)
2614 printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2615 PQgetvalue(result, i, i_ontable),
2616 PQgetvalue(result, i, i_conname),
2617 PQgetvalue(result, i, i_condef));
2618 else
2619 printfPQExpBuffer(&buf, " \"%s\" %s",
2620 PQgetvalue(result, i, i_conname),
2621 PQgetvalue(result, i, i_condef));
2622
2623 printTableAddFooter(&cont, buf.data);
2624 }
2625 }
2626 PQclear(result);
2627
2628 /* print incoming foreign-key references */
2629 if (pset.sversion >= 120000)
2630 {
2632 "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2633 " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2634 " FROM pg_catalog.pg_constraint c\n"
2635 " WHERE confrelid IN (SELECT pg_catalog.pg_partition_ancestors('%s')\n"
2636 " UNION ALL VALUES ('%s'::pg_catalog.regclass))\n"
2637 " AND contype = " CppAsString2(CONSTRAINT_FOREIGN) " AND conparentid = 0\n"
2638 "ORDER BY conname;",
2639 oid, oid);
2640 }
2641 else
2642 {
2644 "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2645 " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2646 " FROM pg_catalog.pg_constraint\n"
2647 " WHERE confrelid = %s AND contype = " CppAsString2(CONSTRAINT_FOREIGN) "\n"
2648 "ORDER BY conname;",
2649 oid);
2650 }
2651
2652 result = PSQLexec(buf.data);
2653 if (!result)
2654 goto error_return;
2655 else
2656 tuples = PQntuples(result);
2657
2658 if (tuples > 0)
2659 {
2660 int i_conname = PQfnumber(result, "conname"),
2661 i_ontable = PQfnumber(result, "ontable"),
2662 i_condef = PQfnumber(result, "condef");
2663
2664 printTableAddFooter(&cont, _("Referenced by:"));
2665 for (i = 0; i < tuples; i++)
2666 {
2667 printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2668 PQgetvalue(result, i, i_ontable),
2669 PQgetvalue(result, i, i_conname),
2670 PQgetvalue(result, i, i_condef));
2671
2672 printTableAddFooter(&cont, buf.data);
2673 }
2674 }
2675 PQclear(result);
2676
2677 /* print any row-level policies */
2678 if (pset.sversion >= 90500)
2679 {
2680 printfPQExpBuffer(&buf, "SELECT pol.polname,");
2681 if (pset.sversion >= 100000)
2683 " pol.polpermissive,\n");
2684 else
2686 " 't' as polpermissive,\n");
2688 " 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"
2689 " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
2690 " pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
2691 " CASE pol.polcmd\n"
2692 " WHEN 'r' THEN 'SELECT'\n"
2693 " WHEN 'a' THEN 'INSERT'\n"
2694 " WHEN 'w' THEN 'UPDATE'\n"
2695 " WHEN 'd' THEN 'DELETE'\n"
2696 " END AS cmd\n"
2697 "FROM pg_catalog.pg_policy pol\n"
2698 "WHERE pol.polrelid = '%s' ORDER BY 1;",
2699 oid);
2700
2701 result = PSQLexec(buf.data);
2702 if (!result)
2703 goto error_return;
2704 else
2705 tuples = PQntuples(result);
2706
2707 /*
2708 * Handle cases where RLS is enabled and there are policies, or
2709 * there aren't policies, or RLS isn't enabled but there are
2710 * policies
2711 */
2712 if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
2713 printTableAddFooter(&cont, _("Policies:"));
2714
2715 if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
2716 printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
2717
2718 if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
2719 printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
2720
2721 if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
2722 printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
2723
2724 if (!tableinfo.rowsecurity && tuples > 0)
2725 printTableAddFooter(&cont, _("Policies (row security disabled):"));
2726
2727 /* Might be an empty set - that's ok */
2728 for (i = 0; i < tuples; i++)
2729 {
2730 printfPQExpBuffer(&buf, " POLICY \"%s\"",
2731 PQgetvalue(result, i, 0));
2732
2733 if (*(PQgetvalue(result, i, 1)) == 'f')
2734 appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
2735
2736 if (!PQgetisnull(result, i, 5))
2737 appendPQExpBuffer(&buf, " FOR %s",
2738 PQgetvalue(result, i, 5));
2739
2740 if (!PQgetisnull(result, i, 2))
2741 {
2742 appendPQExpBuffer(&buf, "\n TO %s",
2743 PQgetvalue(result, i, 2));
2744 }
2745
2746 if (!PQgetisnull(result, i, 3))
2747 appendPQExpBuffer(&buf, "\n USING (%s)",
2748 PQgetvalue(result, i, 3));
2749
2750 if (!PQgetisnull(result, i, 4))
2751 appendPQExpBuffer(&buf, "\n WITH CHECK (%s)",
2752 PQgetvalue(result, i, 4));
2753
2754 printTableAddFooter(&cont, buf.data);
2755 }
2756 PQclear(result);
2757 }
2758
2759 /* print any extended statistics */
2760 if (pset.sversion >= 140000)
2761 {
2763 "SELECT oid, "
2764 "stxrelid::pg_catalog.regclass, "
2765 "stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS nsp, "
2766 "stxname,\n"
2767 "pg_catalog.pg_get_statisticsobjdef_columns(oid) AS columns,\n"
2768 " " CppAsString2(STATS_EXT_NDISTINCT) " = any(stxkind) AS ndist_enabled,\n"
2769 " " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(stxkind) AS deps_enabled,\n"
2770 " " CppAsString2(STATS_EXT_MCV) " = any(stxkind) AS mcv_enabled,\n"
2771 "stxstattarget\n"
2772 "FROM pg_catalog.pg_statistic_ext\n"
2773 "WHERE stxrelid = '%s'\n"
2774 "ORDER BY nsp, stxname;",
2775 oid);
2776
2777 result = PSQLexec(buf.data);
2778 if (!result)
2779 goto error_return;
2780 else
2781 tuples = PQntuples(result);
2782
2783 if (tuples > 0)
2784 {
2785 printTableAddFooter(&cont, _("Statistics objects:"));
2786
2787 for (i = 0; i < tuples; i++)
2788 {
2789 bool gotone = false;
2790 bool has_ndistinct;
2791 bool has_dependencies;
2792 bool has_mcv;
2793 bool has_all;
2794 bool has_some;
2795
2796 has_ndistinct = (strcmp(PQgetvalue(result, i, 5), "t") == 0);
2797 has_dependencies = (strcmp(PQgetvalue(result, i, 6), "t") == 0);
2798 has_mcv = (strcmp(PQgetvalue(result, i, 7), "t") == 0);
2799
2800 printfPQExpBuffer(&buf, " ");
2801
2802 /* statistics object name (qualified with namespace) */
2803 appendPQExpBuffer(&buf, "\"%s.%s\"",
2804 PQgetvalue(result, i, 2),
2805 PQgetvalue(result, i, 3));
2806
2807 /*
2808 * When printing kinds we ignore expression statistics,
2809 * which are used only internally and can't be specified
2810 * by user. We don't print the kinds when none are
2811 * specified (in which case it has to be statistics on a
2812 * single expr) or when all are specified (in which case
2813 * we assume it's expanded by CREATE STATISTICS).
2814 */
2815 has_all = (has_ndistinct && has_dependencies && has_mcv);
2816 has_some = (has_ndistinct || has_dependencies || has_mcv);
2817
2818 if (has_some && !has_all)
2819 {
2820 appendPQExpBufferStr(&buf, " (");
2821
2822 /* options */
2823 if (has_ndistinct)
2824 {
2825 appendPQExpBufferStr(&buf, "ndistinct");
2826 gotone = true;
2827 }
2828
2829 if (has_dependencies)
2830 {
2831 appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2832 gotone = true;
2833 }
2834
2835 if (has_mcv)
2836 {
2837 appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2838 }
2839
2841 }
2842
2843 appendPQExpBuffer(&buf, " ON %s FROM %s",
2844 PQgetvalue(result, i, 4),
2845 PQgetvalue(result, i, 1));
2846
2847 /* Show the stats target if it's not default */
2848 if (!PQgetisnull(result, i, 8) &&
2849 strcmp(PQgetvalue(result, i, 8), "-1") != 0)
2850 appendPQExpBuffer(&buf, "; STATISTICS %s",
2851 PQgetvalue(result, i, 8));
2852
2853 printTableAddFooter(&cont, buf.data);
2854 }
2855 }
2856 PQclear(result);
2857 }
2858 else if (pset.sversion >= 100000)
2859 {
2861 "SELECT oid, "
2862 "stxrelid::pg_catalog.regclass, "
2863 "stxnamespace::pg_catalog.regnamespace AS nsp, "
2864 "stxname,\n"
2865 " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')\n"
2866 " FROM pg_catalog.unnest(stxkeys) s(attnum)\n"
2867 " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND\n"
2868 " a.attnum = s.attnum AND NOT attisdropped)) AS columns,\n"
2869 " " CppAsString2(STATS_EXT_NDISTINCT) " = any(stxkind) AS ndist_enabled,\n"
2870 " " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(stxkind) AS deps_enabled,\n"
2871 " " CppAsString2(STATS_EXT_MCV) " = any(stxkind) AS mcv_enabled,\n");
2872
2873 if (pset.sversion >= 130000)
2874 appendPQExpBufferStr(&buf, " stxstattarget\n");
2875 else
2876 appendPQExpBufferStr(&buf, " -1 AS stxstattarget\n");
2877 appendPQExpBuffer(&buf, "FROM pg_catalog.pg_statistic_ext\n"
2878 "WHERE stxrelid = '%s'\n"
2879 "ORDER BY 1;",
2880 oid);
2881
2882 result = PSQLexec(buf.data);
2883 if (!result)
2884 goto error_return;
2885 else
2886 tuples = PQntuples(result);
2887
2888 if (tuples > 0)
2889 {
2890 printTableAddFooter(&cont, _("Statistics objects:"));
2891
2892 for (i = 0; i < tuples; i++)
2893 {
2894 bool gotone = false;
2895
2896 printfPQExpBuffer(&buf, " ");
2897
2898 /* statistics object name (qualified with namespace) */
2899 appendPQExpBuffer(&buf, "\"%s.%s\" (",
2900 PQgetvalue(result, i, 2),
2901 PQgetvalue(result, i, 3));
2902
2903 /* options */
2904 if (strcmp(PQgetvalue(result, i, 5), "t") == 0)
2905 {
2906 appendPQExpBufferStr(&buf, "ndistinct");
2907 gotone = true;
2908 }
2909
2910 if (strcmp(PQgetvalue(result, i, 6), "t") == 0)
2911 {
2912 appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2913 gotone = true;
2914 }
2915
2916 if (strcmp(PQgetvalue(result, i, 7), "t") == 0)
2917 {
2918 appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2919 }
2920
2921 appendPQExpBuffer(&buf, ") ON %s FROM %s",
2922 PQgetvalue(result, i, 4),
2923 PQgetvalue(result, i, 1));
2924
2925 /* Show the stats target if it's not default */
2926 if (strcmp(PQgetvalue(result, i, 8), "-1") != 0)
2927 appendPQExpBuffer(&buf, "; STATISTICS %s",
2928 PQgetvalue(result, i, 8));
2929
2930 printTableAddFooter(&cont, buf.data);
2931 }
2932 }
2933 PQclear(result);
2934 }
2935
2936 /* print rules */
2937 if (tableinfo.hasrules && tableinfo.relkind != RELKIND_MATVIEW)
2938 {
2940 "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
2941 "ev_enabled\n"
2942 "FROM pg_catalog.pg_rewrite r\n"
2943 "WHERE r.ev_class = '%s' ORDER BY 1;",
2944 oid);
2945 result = PSQLexec(buf.data);
2946 if (!result)
2947 goto error_return;
2948 else
2949 tuples = PQntuples(result);
2950
2951 if (tuples > 0)
2952 {
2953 bool have_heading;
2954 int category;
2955
2956 for (category = 0; category < 4; category++)
2957 {
2958 have_heading = false;
2959
2960 for (i = 0; i < tuples; i++)
2961 {
2962 const char *ruledef;
2963 bool list_rule = false;
2964
2965 switch (category)
2966 {
2967 case 0:
2968 if (*PQgetvalue(result, i, 2) == 'O')
2969 list_rule = true;
2970 break;
2971 case 1:
2972 if (*PQgetvalue(result, i, 2) == 'D')
2973 list_rule = true;
2974 break;
2975 case 2:
2976 if (*PQgetvalue(result, i, 2) == 'A')
2977 list_rule = true;
2978 break;
2979 case 3:
2980 if (*PQgetvalue(result, i, 2) == 'R')
2981 list_rule = true;
2982 break;
2983 }
2984 if (!list_rule)
2985 continue;
2986
2987 if (!have_heading)
2988 {
2989 switch (category)
2990 {
2991 case 0:
2992 printfPQExpBuffer(&buf, _("Rules:"));
2993 break;
2994 case 1:
2995 printfPQExpBuffer(&buf, _("Disabled rules:"));
2996 break;
2997 case 2:
2998 printfPQExpBuffer(&buf, _("Rules firing always:"));
2999 break;
3000 case 3:
3001 printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
3002 break;
3003 }
3004 printTableAddFooter(&cont, buf.data);
3005 have_heading = true;
3006 }
3007
3008 /* Everything after "CREATE RULE" is echoed verbatim */
3009 ruledef = PQgetvalue(result, i, 1);
3010 ruledef += 12;
3011 printfPQExpBuffer(&buf, " %s", ruledef);
3012 printTableAddFooter(&cont, buf.data);
3013 }
3014 }
3015 }
3016 PQclear(result);
3017 }
3018
3019 /* print any publications */
3020 if (pset.sversion >= 100000)
3021 {
3022 if (pset.sversion >= 150000)
3023 {
3025 "SELECT pubname\n"
3026 " , NULL\n"
3027 " , NULL\n"
3028 "FROM pg_catalog.pg_publication p\n"
3029 " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
3030 " JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
3031 "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
3032 "UNION\n"
3033 "SELECT pubname\n"
3034 " , pg_get_expr(pr.prqual, c.oid)\n"
3035 " , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
3036 " (SELECT string_agg(attname, ', ')\n"
3037 " FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
3038 " pg_catalog.pg_attribute\n"
3039 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
3040 " ELSE NULL END) "
3041 "FROM pg_catalog.pg_publication p\n"
3042 " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3043 " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
3044 "WHERE pr.prrelid = '%s'\n"
3045 "UNION\n"
3046 "SELECT pubname\n"
3047 " , NULL\n"
3048 " , NULL\n"
3049 "FROM pg_catalog.pg_publication p\n"
3050 "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3051 "ORDER BY 1;",
3052 oid, oid, oid, oid);
3053 }
3054 else
3055 {
3057 "SELECT pubname\n"
3058 " , NULL\n"
3059 " , NULL\n"
3060 "FROM pg_catalog.pg_publication p\n"
3061 "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3062 "WHERE pr.prrelid = '%s'\n"
3063 "UNION ALL\n"
3064 "SELECT pubname\n"
3065 " , NULL\n"
3066 " , NULL\n"
3067 "FROM pg_catalog.pg_publication p\n"
3068 "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3069 "ORDER BY 1;",
3070 oid, oid);
3071 }
3072
3073 result = PSQLexec(buf.data);
3074 if (!result)
3075 goto error_return;
3076 else
3077 tuples = PQntuples(result);
3078
3079 if (tuples > 0)
3080 printTableAddFooter(&cont, _("Publications:"));
3081
3082 /* Might be an empty set - that's ok */
3083 for (i = 0; i < tuples; i++)
3084 {
3085 printfPQExpBuffer(&buf, " \"%s\"",
3086 PQgetvalue(result, i, 0));
3087
3088 /* column list (if any) */
3089 if (!PQgetisnull(result, i, 2))
3090 appendPQExpBuffer(&buf, " (%s)",
3091 PQgetvalue(result, i, 2));
3092
3093 /* row filter (if any) */
3094 if (!PQgetisnull(result, i, 1))
3095 appendPQExpBuffer(&buf, " WHERE %s",
3096 PQgetvalue(result, i, 1));
3097
3098 printTableAddFooter(&cont, buf.data);
3099 }
3100 PQclear(result);
3101 }
3102
3103 /*
3104 * If verbose, print NOT NULL constraints.
3105 */
3106 if (verbose)
3107 {
3109 "SELECT c.conname, a.attname, c.connoinherit,\n"
3110 " c.conislocal, c.coninhcount <> 0,\n"
3111 " c.convalidated\n"
3112 "FROM pg_catalog.pg_constraint c JOIN\n"
3113 " pg_catalog.pg_attribute a ON\n"
3114 " (a.attrelid = c.conrelid AND a.attnum = c.conkey[1])\n"
3115 "WHERE c.contype = " CppAsString2(CONSTRAINT_NOTNULL) " AND\n"
3116 " c.conrelid = '%s'::pg_catalog.regclass\n"
3117 "ORDER BY a.attnum",
3118 oid);
3119
3120 result = PSQLexec(buf.data);
3121 if (!result)
3122 goto error_return;
3123 else
3124 tuples = PQntuples(result);
3125
3126 if (tuples > 0)
3127 printTableAddFooter(&cont, _("Not-null constraints:"));
3128
3129 /* Might be an empty set - that's ok */
3130 for (i = 0; i < tuples; i++)
3131 {
3132 bool islocal = PQgetvalue(result, i, 3)[0] == 't';
3133 bool inherited = PQgetvalue(result, i, 4)[0] == 't';
3134 bool validated = PQgetvalue(result, i, 5)[0] == 't';
3135
3136 printfPQExpBuffer(&buf, " \"%s\" NOT NULL \"%s\"%s%s",
3137 PQgetvalue(result, i, 0),
3138 PQgetvalue(result, i, 1),
3139 PQgetvalue(result, i, 2)[0] == 't' ?
3140 " NO INHERIT" :
3141 islocal && inherited ? _(" (local, inherited)") :
3142 inherited ? _(" (inherited)") : "",
3143 !validated ? " NOT VALID" : "");
3144
3145 printTableAddFooter(&cont, buf.data);
3146 }
3147 PQclear(result);
3148 }
3149 }
3150
3151 /* Get view_def if table is a view or materialized view */
3152 if ((tableinfo.relkind == RELKIND_VIEW ||
3153 tableinfo.relkind == RELKIND_MATVIEW) && verbose)
3154 {
3155 PGresult *result;
3156
3158 "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
3159 oid);
3160 result = PSQLexec(buf.data);
3161 if (!result)
3162 goto error_return;
3163
3164 if (PQntuples(result) > 0)
3165 view_def = pg_strdup(PQgetvalue(result, 0, 0));
3166
3167 PQclear(result);
3168 }
3169
3170 if (view_def)
3171 {
3172 PGresult *result = NULL;
3173
3174 /* Footer information about a view */
3175 printTableAddFooter(&cont, _("View definition:"));
3176 printTableAddFooter(&cont, view_def);
3177
3178 /* print rules */
3179 if (tableinfo.hasrules)
3180 {
3182 "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
3183 "FROM pg_catalog.pg_rewrite r\n"
3184 "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
3185 oid);
3186 result = PSQLexec(buf.data);
3187 if (!result)
3188 goto error_return;
3189
3190 if (PQntuples(result) > 0)
3191 {
3192 printTableAddFooter(&cont, _("Rules:"));
3193 for (i = 0; i < PQntuples(result); i++)
3194 {
3195 const char *ruledef;
3196
3197 /* Everything after "CREATE RULE" is echoed verbatim */
3198 ruledef = PQgetvalue(result, i, 1);
3199 ruledef += 12;
3200
3201 printfPQExpBuffer(&buf, " %s", ruledef);
3202 printTableAddFooter(&cont, buf.data);
3203 }
3204 }
3205 PQclear(result);
3206 }
3207 }
3208
3209 /*
3210 * Print triggers next, if any (but only user-defined triggers). This
3211 * could apply to either a table or a view.
3212 */
3213 if (tableinfo.hastriggers)
3214 {
3215 PGresult *result;
3216 int tuples;
3217
3219 "SELECT t.tgname, "
3220 "pg_catalog.pg_get_triggerdef(t.oid, true), "
3221 "t.tgenabled, t.tgisinternal,\n");
3222
3223 /*
3224 * Detect whether each trigger is inherited, and if so, get the name
3225 * of the topmost table it's inherited from. We have no easy way to
3226 * do that pre-v13, for lack of the tgparentid column. Even with
3227 * tgparentid, a straightforward search for the topmost parent would
3228 * require a recursive CTE, which seems unduly expensive. We cheat a
3229 * bit by assuming parent triggers will match by tgname; then, joining
3230 * with pg_partition_ancestors() allows the planner to make use of
3231 * pg_trigger_tgrelid_tgname_index if it wishes. We ensure we find
3232 * the correct topmost parent by stopping at the first-in-partition-
3233 * ancestry-order trigger that has tgparentid = 0. (There might be
3234 * unrelated, non-inherited triggers with the same name further up the
3235 * stack, so this is important.)
3236 */
3237 if (pset.sversion >= 130000)
3239 " CASE WHEN t.tgparentid != 0 THEN\n"
3240 " (SELECT u.tgrelid::pg_catalog.regclass\n"
3241 " FROM pg_catalog.pg_trigger AS u,\n"
3242 " pg_catalog.pg_partition_ancestors(t.tgrelid) WITH ORDINALITY AS a(relid, depth)\n"
3243 " WHERE u.tgname = t.tgname AND u.tgrelid = a.relid\n"
3244 " AND u.tgparentid = 0\n"
3245 " ORDER BY a.depth LIMIT 1)\n"
3246 " END AS parent\n");
3247 else
3248 appendPQExpBufferStr(&buf, " NULL AS parent\n");
3249
3251 "FROM pg_catalog.pg_trigger t\n"
3252 "WHERE t.tgrelid = '%s' AND ",
3253 oid);
3254
3255 /*
3256 * tgisinternal is set true for inherited triggers of partitions in
3257 * servers between v11 and v14, though these must still be shown to
3258 * the user. So we use another property that is true for such
3259 * inherited triggers to avoid them being hidden, which is their
3260 * dependence on another trigger.
3261 */
3262 if (pset.sversion >= 110000 && pset.sversion < 150000)
3263 appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n"
3264 " OR EXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n"
3265 " AND refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass))");
3266 else
3267 /* display/warn about disabled internal triggers */
3268 appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))");
3269 appendPQExpBufferStr(&buf, "\nORDER BY 1;");
3270
3271 result = PSQLexec(buf.data);
3272 if (!result)
3273 goto error_return;
3274 else
3275 tuples = PQntuples(result);
3276
3277 if (tuples > 0)
3278 {
3279 bool have_heading;
3280 int category;
3281
3282 /*
3283 * split the output into 4 different categories. Enabled triggers,
3284 * disabled triggers and the two special ALWAYS and REPLICA
3285 * configurations.
3286 */
3287 for (category = 0; category <= 4; category++)
3288 {
3289 have_heading = false;
3290 for (i = 0; i < tuples; i++)
3291 {
3292 bool list_trigger;
3293 const char *tgdef;
3294 const char *usingpos;
3295 const char *tgenabled;
3296 const char *tgisinternal;
3297
3298 /*
3299 * Check if this trigger falls into the current category
3300 */
3301 tgenabled = PQgetvalue(result, i, 2);
3302 tgisinternal = PQgetvalue(result, i, 3);
3303 list_trigger = false;
3304 switch (category)
3305 {
3306 case 0:
3307 if (*tgenabled == 'O' || *tgenabled == 't')
3308 list_trigger = true;
3309 break;
3310 case 1:
3311 if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3312 *tgisinternal == 'f')
3313 list_trigger = true;
3314 break;
3315 case 2:
3316 if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3317 *tgisinternal == 't')
3318 list_trigger = true;
3319 break;
3320 case 3:
3321 if (*tgenabled == 'A')
3322 list_trigger = true;
3323 break;
3324 case 4:
3325 if (*tgenabled == 'R')
3326 list_trigger = true;
3327 break;
3328 }
3329 if (list_trigger == false)
3330 continue;
3331
3332 /* Print the category heading once */
3333 if (have_heading == false)
3334 {
3335 switch (category)
3336 {
3337 case 0:
3338 printfPQExpBuffer(&buf, _("Triggers:"));
3339 break;
3340 case 1:
3341 printfPQExpBuffer(&buf, _("Disabled user triggers:"));
3342 break;
3343 case 2:
3344 printfPQExpBuffer(&buf, _("Disabled internal triggers:"));
3345 break;
3346 case 3:
3347 printfPQExpBuffer(&buf, _("Triggers firing always:"));
3348 break;
3349 case 4:
3350 printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
3351 break;
3352 }
3353 printTableAddFooter(&cont, buf.data);
3354 have_heading = true;
3355 }
3356
3357 /* Everything after "TRIGGER" is echoed verbatim */
3358 tgdef = PQgetvalue(result, i, 1);
3359 usingpos = strstr(tgdef, " TRIGGER ");
3360 if (usingpos)
3361 tgdef = usingpos + 9;
3362
3363 printfPQExpBuffer(&buf, " %s", tgdef);
3364
3365 /* Visually distinguish inherited triggers */
3366 if (!PQgetisnull(result, i, 4))
3367 appendPQExpBuffer(&buf, ", ON TABLE %s",
3368 PQgetvalue(result, i, 4));
3369
3370 printTableAddFooter(&cont, buf.data);
3371 }
3372 }
3373 }
3374 PQclear(result);
3375 }
3376
3377 /*
3378 * Finish printing the footer information about a table.
3379 */
3380 if (tableinfo.relkind == RELKIND_RELATION ||
3381 tableinfo.relkind == RELKIND_MATVIEW ||
3382 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
3383 tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3384 tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
3385 tableinfo.relkind == RELKIND_TOASTVALUE)
3386 {
3387 bool is_partitioned;
3388 PGresult *result;
3389 int tuples;
3390
3391 /* simplify some repeated tests below */
3392 is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3393 tableinfo.relkind == RELKIND_PARTITIONED_INDEX);
3394
3395 /* print foreign server name */
3396 if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
3397 {
3398 char *ftoptions;
3399
3400 /* Footer information about foreign table */
3402 "SELECT s.srvname,\n"
3403 " pg_catalog.array_to_string(ARRAY(\n"
3404 " SELECT pg_catalog.quote_ident(option_name)"
3405 " || ' ' || pg_catalog.quote_literal(option_value)\n"
3406 " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ')\n"
3407 "FROM pg_catalog.pg_foreign_table f,\n"
3408 " pg_catalog.pg_foreign_server s\n"
3409 "WHERE f.ftrelid = '%s' AND s.oid = f.ftserver;",
3410 oid);
3411 result = PSQLexec(buf.data);
3412 if (!result)
3413 goto error_return;
3414 else if (PQntuples(result) != 1)
3415 {
3416 PQclear(result);
3417 goto error_return;
3418 }
3419
3420 /* Print server name */
3421 printfPQExpBuffer(&buf, _("Server: %s"),
3422 PQgetvalue(result, 0, 0));
3423 printTableAddFooter(&cont, buf.data);
3424
3425 /* Print per-table FDW options, if any */
3426 ftoptions = PQgetvalue(result, 0, 1);
3427 if (ftoptions && ftoptions[0] != '\0')
3428 {
3429 printfPQExpBuffer(&buf, _("FDW options: (%s)"), ftoptions);
3430 printTableAddFooter(&cont, buf.data);
3431 }
3432 PQclear(result);
3433 }
3434
3435 /* print tables inherited from (exclude partitioned parents) */
3437 "SELECT c.oid::pg_catalog.regclass\n"
3438 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3439 "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n"
3440 " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE)
3441 " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX)
3442 "\nORDER BY inhseqno;",
3443 oid);
3444
3445 result = PSQLexec(buf.data);
3446 if (!result)
3447 goto error_return;
3448 else
3449 {
3450 const char *s = _("Inherits");
3451 int sw = pg_wcswidth(s, strlen(s), pset.encoding);
3452
3453 tuples = PQntuples(result);
3454
3455 for (i = 0; i < tuples; i++)
3456 {
3457 if (i == 0)
3458 printfPQExpBuffer(&buf, "%s: %s",
3459 s, PQgetvalue(result, i, 0));
3460 else
3461 printfPQExpBuffer(&buf, "%*s %s",
3462 sw, "", PQgetvalue(result, i, 0));
3463 if (i < tuples - 1)
3465
3466 printTableAddFooter(&cont, buf.data);
3467 }
3468
3469 PQclear(result);
3470 }
3471
3472 /* print child tables (with additional info if partitions) */
3473 if (pset.sversion >= 140000)
3475 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3476 " inhdetachpending,"
3477 " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3478 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3479 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3480 "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3481 " c.oid::pg_catalog.regclass::pg_catalog.text;",
3482 oid);
3483 else if (pset.sversion >= 100000)
3485 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3486 " false AS inhdetachpending,"
3487 " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3488 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3489 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3490 "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3491 " c.oid::pg_catalog.regclass::pg_catalog.text;",
3492 oid);
3493 else
3495 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3496 " false AS inhdetachpending, NULL\n"
3497 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3498 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3499 "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;",
3500 oid);
3501
3502 result = PSQLexec(buf.data);
3503 if (!result)
3504 goto error_return;
3505 tuples = PQntuples(result);
3506
3507 /*
3508 * For a partitioned table with no partitions, always print the number
3509 * of partitions as zero, even when verbose output is expected.
3510 * Otherwise, we will not print "Partitions" section for a partitioned
3511 * table without any partitions.
3512 */
3513 if (is_partitioned && tuples == 0)
3514 {
3515 printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples);
3516 printTableAddFooter(&cont, buf.data);
3517 }
3518 else if (!verbose)
3519 {
3520 /* print the number of child tables, if any */
3521 if (tuples > 0)
3522 {
3523 if (is_partitioned)
3524 printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
3525 else
3526 printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
3527 printTableAddFooter(&cont, buf.data);
3528 }
3529 }
3530 else
3531 {
3532 /* display the list of child tables */
3533 const char *ct = is_partitioned ? _("Partitions") : _("Child tables");
3534 int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding);
3535
3536 for (i = 0; i < tuples; i++)
3537 {
3538 char child_relkind = *PQgetvalue(result, i, 1);
3539
3540 if (i == 0)
3541 printfPQExpBuffer(&buf, "%s: %s",
3542 ct, PQgetvalue(result, i, 0));
3543 else
3544 printfPQExpBuffer(&buf, "%*s %s",
3545 ctw, "", PQgetvalue(result, i, 0));
3546 if (!PQgetisnull(result, i, 3))
3547 appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 3));
3548 if (child_relkind == RELKIND_PARTITIONED_TABLE ||
3549 child_relkind == RELKIND_PARTITIONED_INDEX)
3550 appendPQExpBufferStr(&buf, ", PARTITIONED");
3551 else if (child_relkind == RELKIND_FOREIGN_TABLE)
3552 appendPQExpBufferStr(&buf, ", FOREIGN");
3553 if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
3554 appendPQExpBufferStr(&buf, " (DETACH PENDING)");
3555 if (i < tuples - 1)
3557
3558 printTableAddFooter(&cont, buf.data);
3559 }
3560 }
3561 PQclear(result);
3562
3563 /* Table type */
3564 if (tableinfo.reloftype)
3565 {
3566 printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
3567 printTableAddFooter(&cont, buf.data);
3568 }
3569
3570 if (verbose &&
3571 (tableinfo.relkind == RELKIND_RELATION ||
3572 tableinfo.relkind == RELKIND_MATVIEW) &&
3573
3574 /*
3575 * No need to display default values; we already display a REPLICA
3576 * IDENTITY marker on indexes.
3577 */
3578 tableinfo.relreplident != REPLICA_IDENTITY_INDEX &&
3579 ((strcmp(schemaname, "pg_catalog") != 0 &&
3580 tableinfo.relreplident != REPLICA_IDENTITY_DEFAULT) ||
3581 (strcmp(schemaname, "pg_catalog") == 0 &&
3582 tableinfo.relreplident != REPLICA_IDENTITY_NOTHING)))
3583 {
3584 const char *s = _("Replica Identity");
3585
3586 printfPQExpBuffer(&buf, "%s: %s",
3587 s,
3588 tableinfo.relreplident == REPLICA_IDENTITY_FULL ? "FULL" :
3589 tableinfo.relreplident == REPLICA_IDENTITY_DEFAULT ? "NOTHING" :
3590 "???");
3591
3592 printTableAddFooter(&cont, buf.data);
3593 }
3594
3595 /* OIDs, if verbose and not a materialized view */
3596 if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids)
3597 printTableAddFooter(&cont, _("Has OIDs: yes"));
3598
3599 /* Tablespace info */
3600 add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
3601 true);
3602
3603 /* Access method info */
3604 if (verbose && tableinfo.relam != NULL && !pset.hide_tableam)
3605 {
3606 printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam);
3607 printTableAddFooter(&cont, buf.data);
3608 }
3609 }
3610
3611 /* reloptions, if verbose */
3612 if (verbose &&
3613 tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
3614 {
3615 const char *t = _("Options");
3616
3617 printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions);
3618 printTableAddFooter(&cont, buf.data);
3619 }
3620
3621 printTable(&cont, pset.queryFout, false, pset.logfile);
3622
3623 retval = true;
3624
3625error_return:
3626
3627 /* clean up */
3628 if (printTableInitialized)
3629 printTableCleanup(&cont);
3631 termPQExpBuffer(&title);
3633
3634 free(view_def);
3635
3636 PQclear(res);
3637
3638 return retval;
3639}
3640
3641/*
3642 * Add a tablespace description to a footer. If 'newline' is true, it is added
3643 * in a new line; otherwise it's appended to the current value of the last
3644 * footer.
3645 */
3646static void
3648 Oid tablespace, const bool newline)
3649{
3650 /* relkinds for which we support tablespaces */
3651 if (relkind == RELKIND_RELATION ||
3652 relkind == RELKIND_MATVIEW ||
3653 relkind == RELKIND_INDEX ||
3654 relkind == RELKIND_PARTITIONED_TABLE ||
3655 relkind == RELKIND_PARTITIONED_INDEX ||
3656 relkind == RELKIND_TOASTVALUE)
3657 {
3658 /*
3659 * We ignore the database default tablespace so that users not using
3660 * tablespaces don't need to know about them.
3661 */
3662 if (tablespace != 0)
3663 {
3664 PGresult *result = NULL;
3666
3669 "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3670 "WHERE oid = '%u';", tablespace);
3671 result = PSQLexec(buf.data);
3672 if (!result)
3673 {
3675 return;
3676 }
3677 /* Should always be the case, but.... */
3678 if (PQntuples(result) > 0)
3679 {
3680 if (newline)
3681 {
3682 /* Add the tablespace as a new footer */
3683 printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3684 PQgetvalue(result, 0, 0));
3685 printTableAddFooter(cont, buf.data);
3686 }
3687 else
3688 {
3689 /* Append the tablespace to the latest footer */
3690 printfPQExpBuffer(&buf, "%s", cont->footer->data);
3691
3692 /*-------
3693 translator: before this string there's an index description like
3694 '"foo_pkey" PRIMARY KEY, btree (a)' */
3695 appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3696 PQgetvalue(result, 0, 0));
3697 printTableSetFooter(cont, buf.data);
3698 }
3699 }
3700 PQclear(result);
3702 }
3703 }
3704}
3705
3706/*
3707 * \du or \dg
3708 *
3709 * Describes roles. Any schema portion of the pattern is ignored.
3710 */
3711bool
3712describeRoles(const char *pattern, bool verbose, bool showSystem)
3713{
3715 PGresult *res;
3716 printTableContent cont;
3717 printTableOpt myopt = pset.popt.topt;
3718 int ncols = 2;
3719 int nrows = 0;
3720 int i;
3721 int conns;
3722 const char align = 'l';
3723 char **attr;
3724
3725 myopt.default_footer = false;
3726
3728
3730 "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
3731 " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
3732 " r.rolconnlimit, r.rolvaliduntil");
3733
3734 if (verbose)
3735 {
3736 appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
3737 ncols++;
3738 }
3739 appendPQExpBufferStr(&buf, "\n, r.rolreplication");
3740
3741 if (pset.sversion >= 90500)
3742 {
3743 appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
3744 }
3745
3746 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
3747
3748 if (!showSystem && !pattern)
3749 appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
3750
3751 if (!validateSQLNamePattern(&buf, pattern, false, false,
3752 NULL, "r.rolname", NULL, NULL,
3753 NULL, 1))
3754 {
3756 return false;
3757 }
3758
3759 appendPQExpBufferStr(&buf, "ORDER BY 1;");
3760
3761 res = PSQLexec(buf.data);
3762 if (!res)
3763 return false;
3764
3765 nrows = PQntuples(res);
3766 attr = pg_malloc0((nrows + 1) * sizeof(*attr));
3767
3768 printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
3769
3770 printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
3771 printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
3772
3773 if (verbose)
3774 printTableAddHeader(&cont, gettext_noop("Description"), true, align);
3775
3776 for (i = 0; i < nrows; i++)
3777 {
3778 printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
3779
3781 if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
3782 add_role_attribute(&buf, _("Superuser"));
3783
3784 if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
3785 add_role_attribute(&buf, _("No inheritance"));
3786
3787 if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
3788 add_role_attribute(&buf, _("Create role"));
3789
3790 if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
3791 add_role_attribute(&buf, _("Create DB"));
3792
3793 if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
3794 add_role_attribute(&buf, _("Cannot login"));
3795
3796 if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
3797 add_role_attribute(&buf, _("Replication"));
3798
3799 if (pset.sversion >= 90500)
3800 if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
3801 add_role_attribute(&buf, _("Bypass RLS"));
3802
3803 conns = atoi(PQgetvalue(res, i, 6));
3804 if (conns >= 0)
3805 {
3806 if (buf.len > 0)
3807 appendPQExpBufferChar(&buf, '\n');
3808
3809 if (conns == 0)
3810 appendPQExpBufferStr(&buf, _("No connections"));
3811 else
3812 appendPQExpBuffer(&buf, ngettext("%d connection",
3813 "%d connections",
3814 conns),
3815 conns);
3816 }
3817
3818 if (strcmp(PQgetvalue(res, i, 7), "") != 0)
3819 {
3820 if (buf.len > 0)
3821 appendPQExpBufferChar(&buf, '\n');
3822 appendPQExpBufferStr(&buf, _("Password valid until "));
3824 }
3825
3826 attr[i] = pg_strdup(buf.data);
3827
3828 printTableAddCell(&cont, attr[i], false, false);
3829
3830 if (verbose)
3831 printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
3832 }
3834
3835 printTable(&cont, pset.queryFout, false, pset.logfile);
3836 printTableCleanup(&cont);
3837
3838 for (i = 0; i < nrows; i++)
3839 free(attr[i]);
3840 free(attr);
3841
3842 PQclear(res);
3843 return true;
3844}
3845
3846static void
3848{
3849 if (buf->len > 0)
3851
3853}
3854
3855/*
3856 * \drds
3857 */
3858bool
3859listDbRoleSettings(const char *pattern, const char *pattern2)
3860{
3862 PGresult *res;
3863 printQueryOpt myopt = pset.popt;
3864 bool havewhere;
3865
3867
3868 printfPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
3869 "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
3870 "FROM pg_catalog.pg_db_role_setting s\n"
3871 "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n"
3872 "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n",
3873 gettext_noop("Role"),
3874 gettext_noop("Database"),
3875 gettext_noop("Settings"));
3876 if (!validateSQLNamePattern(&buf, pattern, false, false,
3877 NULL, "r.rolname", NULL, NULL, &havewhere, 1))
3878 goto error_return;
3879 if (!validateSQLNamePattern(&buf, pattern2, havewhere, false,
3880 NULL, "d.datname", NULL, NULL,
3881 NULL, 1))
3882 goto error_return;
3883 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3884
3885 res = PSQLexec(buf.data);
3887 if (!res)
3888 return false;
3889
3890 /*
3891 * Most functions in this file are content to print an empty table when
3892 * there are no matching objects. We intentionally deviate from that
3893 * here, but only in !quiet mode, because of the possibility that the user
3894 * is confused about what the two pattern arguments mean.
3895 */
3896 if (PQntuples(res) == 0 && !pset.quiet)
3897 {
3898 if (pattern && pattern2)
3899 pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".",
3900 pattern, pattern2);
3901 else if (pattern)
3902 pg_log_error("Did not find any settings for role \"%s\".",
3903 pattern);
3904 else
3905 pg_log_error("Did not find any settings.");
3906 }
3907 else
3908 {
3909 myopt.title = _("List of settings");
3910 myopt.translate_header = true;
3911
3912 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3913 }
3914
3915 PQclear(res);
3916 return true;
3917
3918error_return:
3920 return false;
3921}
3922
3923/*
3924 * \drg
3925 * Describes role grants.
3926 */
3927bool
3928describeRoleGrants(const char *pattern, bool showSystem)
3929{
3931 PGresult *res;
3932 printQueryOpt myopt = pset.popt;
3933
3936 "SELECT m.rolname AS \"%s\", r.rolname AS \"%s\",\n"
3937 " pg_catalog.concat_ws(', ',\n",
3938 gettext_noop("Role name"),
3939 gettext_noop("Member of"));
3940
3941 if (pset.sversion >= 160000)
3943 " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
3944 " CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n"
3945 " CASE WHEN pam.set_option THEN 'SET' END\n");
3946 else
3948 " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
3949 " CASE WHEN m.rolinherit THEN 'INHERIT' END,\n"
3950 " 'SET'\n");
3951
3953 " ) AS \"%s\",\n"
3954 " g.rolname AS \"%s\"\n",
3955 gettext_noop("Options"),
3956 gettext_noop("Grantor"));
3957
3959 "FROM pg_catalog.pg_roles m\n"
3960 " JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n"
3961 " LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n"
3962 " LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n");
3963
3964 if (!showSystem && !pattern)
3965 appendPQExpBufferStr(&buf, "WHERE m.rolname !~ '^pg_'\n");
3966
3967 if (!validateSQLNamePattern(&buf, pattern, false, false,
3968 NULL, "m.rolname", NULL, NULL,
3969 NULL, 1))
3970 {
3972 return false;
3973 }
3974
3975 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;\n");
3976
3977 res = PSQLexec(buf.data);
3979 if (!res)
3980 return false;
3981
3982 myopt.title = _("List of role grants");
3983 myopt.translate_header = true;
3984
3985 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3986
3987 PQclear(res);
3988 return true;
3989}
3990
3991
3992/*
3993 * listTables()
3994 *
3995 * handler for \dt, \di, etc.
3996 *
3997 * tabtypes is an array of characters, specifying what info is desired:
3998 * t - tables
3999 * i - indexes
4000 * v - views
4001 * m - materialized views
4002 * s - sequences
4003 * E - foreign table (Note: different from 'f', the relkind value)
4004 * (any order of the above is fine)
4005 */
4006bool
4007listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
4008{
4009 bool showTables = strchr(tabtypes, 't') != NULL;
4010 bool showIndexes = strchr(tabtypes, 'i') != NULL;
4011 bool showViews = strchr(tabtypes, 'v') != NULL;
4012 bool showMatViews = strchr(tabtypes, 'm') != NULL;
4013 bool showSeq = strchr(tabtypes, 's') != NULL;
4014 bool showForeign = strchr(tabtypes, 'E') != NULL;
4015
4016 int ntypes;
4018 PGresult *res;
4019 printQueryOpt myopt = pset.popt;
4020 int cols_so_far;
4021 bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
4022
4023 /* Count the number of explicitly-requested relation types */
4024 ntypes = showTables + showIndexes + showViews + showMatViews +
4025 showSeq + showForeign;
4026 /* If none, we default to \dtvmsE (but see also command.c) */
4027 if (ntypes == 0)
4028 showTables = showViews = showMatViews = showSeq = showForeign = true;
4029
4031
4033 "SELECT n.nspname as \"%s\",\n"
4034 " c.relname as \"%s\",\n"
4035 " CASE c.relkind"
4036 " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
4037 " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'"
4038 " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
4039 " WHEN " CppAsString2(RELKIND_INDEX) " THEN '%s'"
4040 " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
4041 " WHEN " CppAsString2(RELKIND_TOASTVALUE) " THEN '%s'"
4042 " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
4043 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4044 " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4045 " END as \"%s\",\n"
4046 " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4047 gettext_noop("Schema"),
4048 gettext_noop("Name"),
4049 gettext_noop("table"),
4050 gettext_noop("view"),
4051 gettext_noop("materialized view"),
4052 gettext_noop("index"),
4053 gettext_noop("sequence"),
4054 gettext_noop("TOAST table"),
4055 gettext_noop("foreign table"),
4056 gettext_noop("partitioned table"),
4057 gettext_noop("partitioned index"),
4058 gettext_noop("Type"),
4059 gettext_noop("Owner"));
4060 cols_so_far = 4;
4061
4062 if (showIndexes)
4063 {
4065 ",\n c2.relname as \"%s\"",
4066 gettext_noop("Table"));
4067 cols_so_far++;
4068 }
4069
4070 if (verbose)
4071 {
4072 /*
4073 * Show whether a relation is permanent, temporary, or unlogged.
4074 */
4076 ",\n CASE c.relpersistence "
4077 "WHEN " CppAsString2(RELPERSISTENCE_PERMANENT) " THEN '%s' "
4078 "WHEN " CppAsString2(RELPERSISTENCE_TEMP) " THEN '%s' "
4079 "WHEN " CppAsString2(RELPERSISTENCE_UNLOGGED) " THEN '%s' "
4080 "END as \"%s\"",
4081 gettext_noop("permanent"),
4082 gettext_noop("temporary"),
4083 gettext_noop("unlogged"),
4084 gettext_noop("Persistence"));
4085 translate_columns[cols_so_far] = true;
4086
4087 /*
4088 * We don't bother to count cols_so_far below here, as there's no need
4089 * to; this might change with future additions to the output columns.
4090 */
4091
4092 /*
4093 * Access methods exist for tables, materialized views and indexes.
4094 * This has been introduced in PostgreSQL 12 for tables.
4095 */
4096 if (pset.sversion >= 120000 && !pset.hide_tableam &&
4097 (showTables || showMatViews || showIndexes))
4099 ",\n am.amname as \"%s\"",
4100 gettext_noop("Access method"));
4101
4103 ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\""
4104 ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4105 gettext_noop("Size"),
4106 gettext_noop("Description"));
4107 }
4108
4110 "\nFROM pg_catalog.pg_class c"
4111 "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4112
4113 if (pset.sversion >= 120000 && !pset.hide_tableam &&
4114 (showTables || showMatViews || showIndexes))
4116 "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam");
4117
4118 if (showIndexes)
4120 "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4121 "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4122
4123 appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4124 if (showTables)
4125 {
4126 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_RELATION) ","
4127 CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
4128 /* with 'S' or a pattern, allow 't' to match TOAST tables too */
4129 if (showSystem || pattern)
4130 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_TOASTVALUE) ",");
4131 }
4132 if (showViews)
4133 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_VIEW) ",");
4134 if (showMatViews)
4135 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_MATVIEW) ",");
4136 if (showIndexes)
4137 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_INDEX) ","
4138 CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
4139 if (showSeq)
4140 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_SEQUENCE) ",");
4141 if (showSystem || pattern)
4142 appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
4143 if (showForeign)
4144 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_FOREIGN_TABLE) ",");
4145
4146 appendPQExpBufferStr(&buf, "''"); /* dummy */
4147 appendPQExpBufferStr(&buf, ")\n");
4148
4149 if (!showSystem && !pattern)
4150 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4151 " AND n.nspname !~ '^pg_toast'\n"
4152 " AND n.nspname <> 'information_schema'\n");
4153
4154 if (!validateSQLNamePattern(&buf, pattern, true, false,
4155 "n.nspname", "c.relname", NULL,
4156 "pg_catalog.pg_table_is_visible(c.oid)",
4157 NULL, 3))
4158 {
4160 return false;
4161 }
4162
4163 appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
4164
4165 res = PSQLexec(buf.data);
4167 if (!res)
4168 return false;
4169
4170 /*
4171 * Most functions in this file are content to print an empty table when
4172 * there are no matching objects. We intentionally deviate from that
4173 * here, but only in !quiet mode, for historical reasons.
4174 */
4175 if (PQntuples(res) == 0 && !pset.quiet)
4176 {
4177 if (pattern)
4178 {
4179 if (ntypes != 1)
4180 pg_log_error("Did not find any relations named \"%s\".",
4181 pattern);
4182 else if (showTables)
4183 pg_log_error("Did not find any tables named \"%s\".",
4184 pattern);
4185 else if (showIndexes)
4186 pg_log_error("Did not find any indexes named \"%s\".",
4187 pattern);
4188 else if (showViews)
4189 pg_log_error("Did not find any views named \"%s\".",
4190 pattern);
4191 else if (showMatViews)
4192 pg_log_error("Did not find any materialized views named \"%s\".",
4193 pattern);
4194 else if (showSeq)
4195 pg_log_error("Did not find any sequences named \"%s\".",
4196 pattern);
4197 else if (showForeign)
4198 pg_log_error("Did not find any foreign tables named \"%s\".",
4199 pattern);
4200 else /* should not get here */
4201 pg_log_error_internal("Did not find any ??? named \"%s\".",
4202 pattern);
4203 }
4204 else
4205 {
4206 if (ntypes != 1)
4207 pg_log_error("Did not find any relations.");
4208 else if (showTables)
4209 pg_log_error("Did not find any tables.");
4210 else if (showIndexes)
4211 pg_log_error("Did not find any indexes.");
4212 else if (showViews)
4213 pg_log_error("Did not find any views.");
4214 else if (showMatViews)
4215 pg_log_error("Did not find any materialized views.");
4216 else if (showSeq)
4217 pg_log_error("Did not find any sequences.");
4218 else if (showForeign)
4219 pg_log_error("Did not find any foreign tables.");
4220 else /* should not get here */
4221 pg_log_error_internal("Did not find any ??? relations.");
4222 }
4223 }
4224 else
4225 {
4226 myopt.title =
4227 (ntypes != 1) ? _("List of relations") :
4228 (showTables) ? _("List of tables") :
4229 (showIndexes) ? _("List of indexes") :
4230 (showViews) ? _("List of views") :
4231 (showMatViews) ? _("List of materialized views") :
4232 (showSeq) ? _("List of sequences") :
4233 (showForeign) ? _("List of foreign tables") :
4234 "List of ???"; /* should not get here */
4235 myopt.translate_header = true;
4236 myopt.translate_columns = translate_columns;
4237 myopt.n_translate_columns = lengthof(translate_columns);
4238
4239 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4240 }
4241
4242 PQclear(res);
4243 return true;
4244}
4245
4246/*
4247 * \dP
4248 * Takes an optional regexp to select particular relations
4249 *
4250 * As with \d, you can specify the kinds of relations you want:
4251 *
4252 * t for tables
4253 * i for indexes
4254 *
4255 * And there's additional flags:
4256 *
4257 * n to list non-leaf partitioned tables
4258 *
4259 * and you can mix and match these in any order.
4260 */
4261bool
4262listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
4263{
4264 bool showTables = strchr(reltypes, 't') != NULL;
4265 bool showIndexes = strchr(reltypes, 'i') != NULL;
4266 bool showNested = strchr(reltypes, 'n') != NULL;
4268 PQExpBufferData title;
4269 PGresult *res;
4270 printQueryOpt myopt = pset.popt;
4271 bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
4272 const char *tabletitle;
4273 bool mixed_output = false;
4274
4275 /*
4276 * Note: Declarative table partitioning is only supported as of Pg 10.0.
4277 */
4278 if (pset.sversion < 100000)
4279 {
4280 char sverbuf[32];
4281
4282 pg_log_error("The server (version %s) does not support declarative table partitioning.",
4284 sverbuf, sizeof(sverbuf)));
4285 return true;
4286 }
4287
4288 /* If no relation kind was selected, show them all */
4289 if (!showTables && !showIndexes)
4290 showTables = showIndexes = true;
4291
4292 if (showIndexes && !showTables)
4293 tabletitle = _("List of partitioned indexes"); /* \dPi */
4294 else if (showTables && !showIndexes)
4295 tabletitle = _("List of partitioned tables"); /* \dPt */
4296 else
4297 {
4298 /* show all kinds */
4299 tabletitle = _("List of partitioned relations");
4300 mixed_output = true;
4301 }
4302
4304
4306 "SELECT n.nspname as \"%s\",\n"
4307 " c.relname as \"%s\",\n"
4308 " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4309 gettext_noop("Schema"),
4310 gettext_noop("Name"),
4311 gettext_noop("Owner"));
4312
4313 if (mixed_output)
4314 {
4316 ",\n CASE c.relkind"
4317 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4318 " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4319 " END as \"%s\"",
4320 gettext_noop("partitioned table"),
4321 gettext_noop("partitioned index"),
4322 gettext_noop("Type"));
4323
4324 translate_columns[3] = true;
4325 }
4326
4327 if (showNested || pattern)
4329 ",\n inh.inhparent::pg_catalog.regclass as \"%s\"",
4330 gettext_noop("Parent name"));
4331
4332 if (showIndexes)
4334 ",\n c2.oid::pg_catalog.regclass as \"%s\"",
4335 gettext_noop("Table"));
4336
4337 if (verbose)
4338 {
4339 /*
4340 * Table access methods were introduced in v12, and can be set on
4341 * partitioned tables since v17.
4342 */
4343 appendPQExpBuffer(&buf, ",\n am.amname as \"%s\"",
4344 gettext_noop("Access method"));
4345
4346 if (showNested)
4347 {
4349 ",\n s.dps as \"%s\"",
4350 gettext_noop("Leaf partition size"));
4352 ",\n s.tps as \"%s\"",
4353 gettext_noop("Total size"));
4354 }
4355 else
4356 /* Sizes of all partitions are considered in this case. */
4358 ",\n s.tps as \"%s\"",
4359 gettext_noop("Total size"));
4360
4362 ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4363 gettext_noop("Description"));
4364 }
4365
4367 "\nFROM pg_catalog.pg_class c"
4368 "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4369
4370 if (showIndexes)
4372 "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4373 "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4374
4375 if (showNested || pattern)
4377 "\n LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid");
4378
4379 if (verbose)
4380 {
4382 "\n LEFT JOIN pg_catalog.pg_am am ON c.relam = am.oid");
4383
4384 if (pset.sversion < 120000)
4385 {
4387 ",\n LATERAL (WITH RECURSIVE d\n"
4388 " AS (SELECT inhrelid AS oid, 1 AS level\n"
4389 " FROM pg_catalog.pg_inherits\n"
4390 " WHERE inhparent = c.oid\n"
4391 " UNION ALL\n"
4392 " SELECT inhrelid, level + 1\n"
4393 " FROM pg_catalog.pg_inherits i\n"
4394 " JOIN d ON i.inhparent = d.oid)\n"
4395 " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
4396 "d.oid))) AS tps,\n"
4397 " pg_catalog.pg_size_pretty(sum("
4398 "\n CASE WHEN d.level = 1"
4399 " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n"
4400 " FROM d) s");
4401 }
4402 else
4403 {
4404 /* PostgreSQL 12 has pg_partition_tree function */
4406 ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
4407 "\n CASE WHEN ppt.isleaf AND ppt.level = 1"
4408 "\n THEN pg_catalog.pg_table_size(ppt.relid)"
4409 " ELSE 0 END)) AS dps"
4410 ",\n pg_catalog.pg_size_pretty(sum("
4411 "pg_catalog.pg_table_size(ppt.relid))) AS tps"
4412 "\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s");
4413 }
4414 }
4415
4416 appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4417 if (showTables)
4418 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
4419 if (showIndexes)
4420 appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
4421 appendPQExpBufferStr(&buf, "''"); /* dummy */
4422 appendPQExpBufferStr(&buf, ")\n");
4423
4424 appendPQExpBufferStr(&buf, !showNested && !pattern ?
4425 " AND NOT c.relispartition\n" : "");
4426
4427 if (!pattern)
4428 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4429 " AND n.nspname !~ '^pg_toast'\n"
4430 " AND n.nspname <> 'information_schema'\n");
4431
4432 if (!validateSQLNamePattern(&buf, pattern, true, false,
4433 "n.nspname", "c.relname", NULL,
4434 "pg_catalog.pg_table_is_visible(c.oid)",
4435 NULL, 3))
4436 {
4438 return false;
4439 }
4440
4441 appendPQExpBuffer(&buf, "ORDER BY \"Schema\", %s%s\"Name\";",
4442 mixed_output ? "\"Type\" DESC, " : "",
4443 showNested || pattern ? "\"Parent name\" NULLS FIRST, " : "");
4444
4445 res = PSQLexec(buf.data);
4447 if (!res)
4448 return false;
4449
4450 initPQExpBuffer(&title);
4451 appendPQExpBufferStr(&title, tabletitle);
4452
4453 myopt.title = title.data;
4454 myopt.translate_header = true;
4455 myopt.translate_columns = translate_columns;
4456 myopt.n_translate_columns = lengthof(translate_columns);
4457
4458 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4459
4460 termPQExpBuffer(&title);
4461
4462 PQclear(res);
4463 return true;
4464}
4465
4466/*
4467 * \dL
4468 *
4469 * Describes languages.
4470 */
4471bool
4472listLanguages(const char *pattern, bool verbose, bool showSystem)
4473{
4475 PGresult *res;
4476 printQueryOpt myopt = pset.popt;
4477
4479
4481 "SELECT l.lanname AS \"%s\",\n"
4482 " pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n"
4483 " l.lanpltrusted AS \"%s\"",
4484 gettext_noop("Name"),
4485 gettext_noop("Owner"),
4486 gettext_noop("Trusted"));
4487
4488 if (verbose)
4489 {
4491 ",\n NOT l.lanispl AS \"%s\",\n"
4492 " l.lanplcallfoid::pg_catalog.regprocedure AS \"%s\",\n"
4493 " l.lanvalidator::pg_catalog.regprocedure AS \"%s\",\n "
4494 "l.laninline::pg_catalog.regprocedure AS \"%s\",\n ",
4495 gettext_noop("Internal language"),
4496 gettext_noop("Call handler"),
4497 gettext_noop("Validator"),
4498 gettext_noop("Inline handler"));
4499 printACLColumn(&buf, "l.lanacl");
4500 }
4501
4503 ",\n d.description AS \"%s\""
4504 "\nFROM pg_catalog.pg_language l\n"
4505 "LEFT JOIN pg_catalog.pg_description d\n"
4506 " ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
4507 " AND d.objsubid = 0\n",
4508 gettext_noop("Description"));
4509
4510 if (pattern)
4511 {
4512 if (!validateSQLNamePattern(&buf, pattern, false, false,
4513 NULL, "l.lanname", NULL, NULL,
4514 NULL, 2))
4515 {
4517 return false;
4518 }
4519 }
4520
4521 if (!showSystem && !pattern)
4522 appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n");
4523
4524
4525 appendPQExpBufferStr(&buf, "ORDER BY 1;");
4526
4527 res = PSQLexec(buf.data);
4529 if (!res)
4530 return false;
4531
4532 myopt.title = _("List of languages");
4533 myopt.translate_header = true;
4534
4535 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4536
4537 PQclear(res);
4538 return true;
4539}
4540
4541
4542/*
4543 * \dD
4544 *
4545 * Describes domains.
4546 */
4547bool
4548listDomains(const char *pattern, bool verbose, bool showSystem)
4549{
4551 PGresult *res;
4552 printQueryOpt myopt = pset.popt;
4553
4555
4557 "SELECT n.nspname as \"%s\",\n"
4558 " t.typname as \"%s\",\n"
4559 " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
4560 " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
4561 " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4562 " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4563 " t.typdefault as \"%s\",\n"
4564 " pg_catalog.array_to_string(ARRAY(\n"
4565 " 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"
4566 " ), ' ') as \"%s\"",
4567 gettext_noop("Schema"),
4568 gettext_noop("Name"),
4569 gettext_noop("Type"),
4570 gettext_noop("Collation"),
4571 gettext_noop("Nullable"),
4572 gettext_noop("Default"),
4573 gettext_noop("Check"));
4574
4575 if (verbose)
4576 {
4577 appendPQExpBufferStr(&buf, ",\n ");
4578 printACLColumn(&buf, "t.typacl");
4580 ",\n d.description as \"%s\"",
4581 gettext_noop("Description"));
4582 }
4583
4585 "\nFROM pg_catalog.pg_type t\n"
4586 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
4587
4588 if (verbose)
4590 " LEFT JOIN pg_catalog.pg_description d "
4591 "ON d.classoid = t.tableoid AND d.objoid = t.oid "
4592 "AND d.objsubid = 0\n");
4593
4594 appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
4595
4596 if (!showSystem && !pattern)
4597 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4598 " AND n.nspname <> 'information_schema'\n");
4599
4600 if (!validateSQLNamePattern(&buf, pattern, true, false,
4601 "n.nspname", "t.typname", NULL,
4602 "pg_catalog.pg_type_is_visible(t.oid)",
4603 NULL, 3))
4604 {
4606 return false;
4607 }
4608
4609 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4610
4611 res = PSQLexec(buf.data);
4613 if (!res)
4614 return false;
4615
4616 myopt.title = _("List of domains");
4617 myopt.translate_header = true;
4618
4619 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4620
4621 PQclear(res);
4622 return true;
4623}
4624
4625/*
4626 * \dc
4627 *
4628 * Describes conversions.
4629 */
4630bool
4631listConversions(const char *pattern, bool verbose, bool showSystem)
4632{
4634 PGresult *res;
4635 printQueryOpt myopt = pset.popt;
4636 static const bool translate_columns[] =
4637 {false, false, false, false, true, false};
4638
4640
4642 "SELECT n.nspname AS \"%s\",\n"
4643 " c.conname AS \"%s\",\n"
4644 " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
4645 " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
4646 " CASE WHEN c.condefault THEN '%s'\n"
4647 " ELSE '%s' END AS \"%s\"",
4648 gettext_noop("Schema"),
4649 gettext_noop("Name"),
4650 gettext_noop("Source"),
4651 gettext_noop("Destination"),
4652 gettext_noop("yes"), gettext_noop("no"),
4653 gettext_noop("Default?"));
4654
4655 if (verbose)
4657 ",\n d.description AS \"%s\"",
4658 gettext_noop("Description"));
4659
4661 "\nFROM pg_catalog.pg_conversion c\n"
4662 " JOIN pg_catalog.pg_namespace n "
4663 "ON n.oid = c.connamespace\n");
4664
4665 if (verbose)
4667 "LEFT JOIN pg_catalog.pg_description d "
4668 "ON d.classoid = c.tableoid\n"
4669 " AND d.objoid = c.oid "
4670 "AND d.objsubid = 0\n");
4671
4672 appendPQExpBufferStr(&buf, "WHERE true\n");
4673
4674 if (!showSystem && !pattern)
4675 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4676 " AND n.nspname <> 'information_schema'\n");
4677
4678 if (!validateSQLNamePattern(&buf, pattern, true, false,
4679 "n.nspname", "c.conname", NULL,
4680 "pg_catalog.pg_conversion_is_visible(c.oid)",
4681 NULL, 3))
4682 {
4684 return false;
4685 }
4686
4687 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4688
4689 res = PSQLexec(buf.data);
4691 if (!res)
4692 return false;
4693
4694 myopt.title = _("List of conversions");
4695 myopt.translate_header = true;
4696 myopt.translate_columns = translate_columns;
4697 myopt.n_translate_columns = lengthof(translate_columns);
4698
4699 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4700
4701 PQclear(res);
4702 return true;
4703}
4704
4705/*
4706 * \dconfig
4707 *
4708 * Describes configuration parameters.
4709 */
4710bool
4712 bool showSystem)
4713{
4715 PGresult *res;
4716 printQueryOpt myopt = pset.popt;
4717
4720 "SELECT s.name AS \"%s\", "
4721 "pg_catalog.current_setting(s.name) AS \"%s\"",
4722 gettext_noop("Parameter"),
4723 gettext_noop("Value"));
4724
4725 if (verbose)
4726 {
4728 ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4729 gettext_noop("Type"),
4730 gettext_noop("Context"));
4731 if (pset.sversion >= 150000)
4732 printACLColumn(&buf, "p.paracl");
4733 else
4734 appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4735 gettext_noop("Access privileges"));
4736 }
4737
4738 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4739
4740 if (verbose && pset.sversion >= 150000)
4742 " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4743 " ON pg_catalog.lower(s.name) = p.parname\n");
4744
4745 if (pattern)
4746 processSQLNamePattern(pset.db, &buf, pattern,
4747 false, false,
4748 NULL, "pg_catalog.lower(s.name)", NULL,
4749 NULL, NULL, NULL);
4750 else
4751 appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
4752 " s.setting IS DISTINCT FROM s.boot_val\n");
4753
4754 appendPQExpBufferStr(&buf, "ORDER BY 1;");
4755
4756 res = PSQLexec(buf.data);
4758 if (!res)
4759 return false;
4760
4761 if (pattern)
4762 myopt.title = _("List of configuration parameters");
4763 else
4764 myopt.title = _("List of non-default configuration parameters");
4765 myopt.translate_header = true;
4766
4767 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4768
4769 PQclear(res);
4770 return true;
4771}
4772
4773/*
4774 * \dy
4775 *
4776 * Describes Event Triggers.
4777 */
4778bool
4779listEventTriggers(const char *pattern, bool verbose)
4780{
4782 PGresult *res;
4783 printQueryOpt myopt = pset.popt;
4784 static const bool translate_columns[] =
4785 {false, false, false, true, false, false, false};
4786
4787 if (pset.sversion < 90300)
4788 {
4789 char sverbuf[32];
4790
4791 pg_log_error("The server (version %s) does not support event triggers.",
4793 sverbuf, sizeof(sverbuf)));
4794 return true;
4795 }
4796
4798
4800 "SELECT evtname as \"%s\", "
4801 "evtevent as \"%s\", "
4802 "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
4803 " case evtenabled when 'O' then '%s'"
4804 " when 'R' then '%s'"
4805 " when 'A' then '%s'"
4806 " when 'D' then '%s' end as \"%s\",\n"
4807 " e.evtfoid::pg_catalog.regproc as \"%s\", "
4808 "pg_catalog.array_to_string(array(select x"
4809 " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
4810 gettext_noop("Name"),
4811 gettext_noop("Event"),
4812 gettext_noop("Owner"),
4813 gettext_noop("enabled"),
4814 gettext_noop("replica"),
4815 gettext_noop("always"),
4816 gettext_noop("disabled"),
4817 gettext_noop("Enabled"),
4818 gettext_noop("Function"),
4819 gettext_noop("Tags"));
4820 if (verbose)
4822 ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
4823 gettext_noop("Description"));
4825 "\nFROM pg_catalog.pg_event_trigger e ");
4826
4827 if (!validateSQLNamePattern(&buf, pattern, false, false,
4828 NULL, "evtname", NULL, NULL,
4829 NULL, 1))
4830 {
4832 return false;
4833 }
4834
4835 appendPQExpBufferStr(&buf, "ORDER BY 1");
4836
4837 res = PSQLexec(buf.data);
4839 if (!res)
4840 return false;
4841
4842 myopt.title = _("List of event triggers");
4843 myopt.translate_header = true;
4844 myopt.translate_columns = translate_columns;
4845 myopt.n_translate_columns = lengthof(translate_columns);
4846
4847 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4848
4849 PQclear(res);
4850 return true;
4851}
4852
4853/*
4854 * \dX
4855 *
4856 * Describes extended statistics.
4857 */
4858bool
4859listExtendedStats(const char *pattern)
4860{
4862 PGresult *res;
4863 printQueryOpt myopt = pset.popt;
4864
4865 if (pset.sversion < 100000)
4866 {
4867 char sverbuf[32];
4868
4869 pg_log_error("The server (version %s) does not support extended statistics.",
4871 sverbuf, sizeof(sverbuf)));
4872 return true;
4873 }
4874
4877 "SELECT \n"
4878 "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS \"%s\", \n"
4879 "es.stxname AS \"%s\", \n",
4880 gettext_noop("Schema"),
4881 gettext_noop("Name"));
4882
4883 if (pset.sversion >= 140000)
4885 "pg_catalog.format('%%s FROM %%s', \n"
4886 " pg_catalog.pg_get_statisticsobjdef_columns(es.oid), \n"
4887 " es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4888 gettext_noop("Definition"));
4889 else
4891 "pg_catalog.format('%%s FROM %%s', \n"
4892 " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
4893 " FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
4894 " JOIN pg_catalog.pg_attribute a \n"
4895 " ON (es.stxrelid = a.attrelid \n"
4896 " AND a.attnum = s.attnum \n"
4897 " AND NOT a.attisdropped)), \n"
4898 "es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4899 gettext_noop("Definition"));
4900
4902 ",\nCASE WHEN " CppAsString2(STATS_EXT_NDISTINCT) " = any(es.stxkind) THEN 'defined' \n"
4903 "END AS \"%s\", \n"
4904 "CASE WHEN " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(es.stxkind) THEN 'defined' \n"
4905 "END AS \"%s\"",
4906 gettext_noop("Ndistinct"),
4907 gettext_noop("Dependencies"));
4908
4909 /*
4910 * Include the MCV statistics kind.
4911 */
4912 if (pset.sversion >= 120000)
4913 {
4915 ",\nCASE WHEN " CppAsString2(STATS_EXT_MCV) " = any(es.stxkind) THEN 'defined' \n"
4916 "END AS \"%s\" ",
4917 gettext_noop("MCV"));
4918 }
4919
4921 " \nFROM pg_catalog.pg_statistic_ext es \n");
4922
4923 if (!validateSQLNamePattern(&buf, pattern,
4924 false, false,
4925 "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
4926 NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)",
4927 NULL, 3))
4928 {
4930 return false;
4931 }
4932
4933 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4934
4935 res = PSQLexec(buf.data);
4937 if (!res)
4938 return false;
4939
4940 myopt.title = _("List of extended statistics");
4941 myopt.translate_header = true;
4942
4943 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4944
4945 PQclear(res);
4946 return true;
4947}
4948
4949/*
4950 * \dC
4951 *
4952 * Describes casts.
4953 */
4954bool
4955listCasts(const char *pattern, bool verbose)
4956{
4958 PGresult *res;
4959 printQueryOpt myopt = pset.popt;
4960 static const bool translate_columns[] = {false, false, false, true, true, false};
4961
4963
4965 "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
4966 " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n",
4967 gettext_noop("Source type"),
4968 gettext_noop("Target type"));
4969
4970 /*
4971 * We don't attempt to localize '(binary coercible)' or '(with inout)',
4972 * because there's too much risk of gettext translating a function name
4973 * that happens to match some string in the PO database.
4974 */
4976 " CASE WHEN c.castmethod = '%c' THEN '(binary coercible)'\n"
4977 " WHEN c.castmethod = '%c' THEN '(with inout)'\n"
4978 " ELSE p.proname\n"
4979 " END AS \"%s\",\n",
4980 COERCION_METHOD_BINARY,
4981 COERCION_METHOD_INOUT,
4982 gettext_noop("Function"));
4983
4985 " CASE WHEN c.castcontext = '%c' THEN '%s'\n"
4986 " WHEN c.castcontext = '%c' THEN '%s'\n"
4987 " ELSE '%s'\n"
4988 " END AS \"%s\"",
4989 COERCION_CODE_EXPLICIT,
4990 gettext_noop("no"),
4991 COERCION_CODE_ASSIGNMENT,
4992 gettext_noop("in assignment"),
4993 gettext_noop("yes"),
4994 gettext_noop("Implicit?"));
4995
4996 if (verbose)
4998 ",\n CASE WHEN p.proleakproof THEN '%s'\n"
4999 " ELSE '%s'\n"
5000 " END AS \"%s\",\n"
5001 " d.description AS \"%s\"",
5002 gettext_noop("yes"),
5003 gettext_noop("no"),
5004 gettext_noop("Leakproof?"),
5005 gettext_noop("Description"));
5006
5007 /*
5008 * We need a left join to pg_proc for binary casts; the others are just
5009 * paranoia.
5010 */
5012 "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
5013 " ON c.castfunc = p.oid\n"
5014 " LEFT JOIN pg_catalog.pg_type ts\n"
5015 " ON c.castsource = ts.oid\n"
5016 " LEFT JOIN pg_catalog.pg_namespace ns\n"
5017 " ON ns.oid = ts.typnamespace\n"
5018 " LEFT JOIN pg_catalog.pg_type tt\n"
5019 " ON c.casttarget = tt.oid\n"
5020 " LEFT JOIN pg_catalog.pg_namespace nt\n"
5021 " ON nt.oid = tt.typnamespace\n");
5022
5023 if (verbose)
5025 " LEFT JOIN pg_catalog.pg_description d\n"
5026 " ON d.classoid = c.tableoid AND d.objoid = "
5027 "c.oid AND d.objsubid = 0\n");
5028
5029 appendPQExpBufferStr(&buf, "WHERE ( (true");
5030
5031 /*
5032 * Match name pattern against either internal or external name of either
5033 * castsource or casttarget
5034 */
5035 if (!validateSQLNamePattern(&buf, pattern, true, false,
5036 "ns.nspname", "ts.typname",
5037 "pg_catalog.format_type(ts.oid, NULL)",
5038 "pg_catalog.pg_type_is_visible(ts.oid)",
5039 NULL, 3))
5040 goto error_return;
5041
5042 appendPQExpBufferStr(&buf, ") OR (true");
5043
5044 if (!validateSQLNamePattern(&buf, pattern, true, false,
5045 "nt.nspname", "tt.typname",
5046 "pg_catalog.format_type(tt.oid, NULL)",
5047 "pg_catalog.pg_type_is_visible(tt.oid)",
5048 NULL, 3))
5049 goto error_return;
5050
5051 appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
5052
5053 res = PSQLexec(buf.data);
5055 if (!res)
5056 return false;
5057
5058 myopt.title = _("List of casts");
5059 myopt.translate_header = true;
5060 myopt.translate_columns = translate_columns;
5061 myopt.n_translate_columns = lengthof(translate_columns);
5062
5063 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5064
5065 PQclear(res);
5066 return true;
5067
5068error_return:
5070 return false;
5071}
5072
5073/*
5074 * \dO
5075 *
5076 * Describes collations.
5077 */
5078bool
5079listCollations(const char *pattern, bool verbose, bool showSystem)
5080{
5082 PGresult *res;
5083 printQueryOpt myopt = pset.popt;
5084 static const bool translate_columns[] = {false, false, false, false, false, false, false, true, false};
5085
5087
5089 "SELECT\n"
5090 " n.nspname AS \"%s\",\n"
5091 " c.collname AS \"%s\",\n",
5092 gettext_noop("Schema"),
5093 gettext_noop("Name"));
5094
5095 if (pset.sversion >= 100000)
5097 " CASE c.collprovider "
5098 "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
5099 "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
5100 "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
5101 "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
5102 "END AS \"%s\",\n",
5103 gettext_noop("Provider"));
5104 else
5106 " 'libc' AS \"%s\",\n",
5107 gettext_noop("Provider"));
5108
5110 " c.collcollate AS \"%s\",\n"
5111 " c.collctype AS \"%s\",\n",
5112 gettext_noop("Collate"),
5113 gettext_noop("Ctype"));
5114
5115 if (pset.sversion >= 170000)
5117 " c.colllocale AS \"%s\",\n",
5118 gettext_noop("Locale"));
5119 else if (pset.sversion >= 150000)
5121 " c.colliculocale AS \"%s\",\n",
5122 gettext_noop("Locale"));
5123 else
5125 " c.collcollate AS \"%s\",\n",
5126 gettext_noop("Locale"));
5127
5128 if (pset.sversion >= 160000)
5130 " c.collicurules AS \"%s\",\n",
5131 gettext_noop("ICU Rules"));
5132 else
5134 " NULL AS \"%s\",\n",
5135 gettext_noop("ICU Rules"));
5136
5137 if (pset.sversion >= 120000)
5139 " CASE WHEN c.collisdeterministic THEN '%s' ELSE '%s' END AS \"%s\"",
5140 gettext_noop("yes"), gettext_noop("no"),
5141 gettext_noop("Deterministic?"));
5142 else
5144 " '%s' AS \"%s\"",
5145 gettext_noop("yes"),
5146 gettext_noop("Deterministic?"));
5147
5148 if (verbose)
5150 ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
5151 gettext_noop("Description"));
5152
5154 "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
5155 "WHERE n.oid = c.collnamespace\n");
5156
5157 if (!showSystem && !pattern)
5158 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
5159 " AND n.nspname <> 'information_schema'\n");
5160
5161 /*
5162 * Hide collations that aren't usable in the current database's encoding.
5163 * If you think to change this, note that pg_collation_is_visible rejects
5164 * unusable collations, so you will need to hack name pattern processing
5165 * somehow to avoid inconsistent behavior.
5166 */
5167 appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
5168
5169 if (!validateSQLNamePattern(&buf, pattern, true, false,
5170 "n.nspname", "c.collname", NULL,
5171 "pg_catalog.pg_collation_is_visible(c.oid)",
5172 NULL, 3))
5173 {
5175 return false;
5176 }
5177
5178 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5179
5180 res = PSQLexec(buf.data);
5182 if (!res)
5183 return false;
5184
5185 myopt.title = _("List of collations");
5186 myopt.translate_header = true;
5187 myopt.translate_columns = translate_columns;
5188 myopt.n_translate_columns = lengthof(translate_columns);
5189
5190 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5191
5192 PQclear(res);
5193 return true;
5194}
5195
5196/*
5197 * \dn
5198 *
5199 * Describes schemas (namespaces)
5200 */
5201bool
5202listSchemas(const char *pattern, bool verbose, bool showSystem)
5203{
5205 PGresult *res;
5206 printQueryOpt myopt = pset.popt;
5207 int pub_schema_tuples = 0;
5208 char **footers = NULL;
5209
5212 "SELECT n.nspname AS \"%s\",\n"
5213 " pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
5214 gettext_noop("Name"),
5215 gettext_noop("Owner"));
5216
5217 if (verbose)
5218 {
5219 appendPQExpBufferStr(&buf, ",\n ");
5220 printACLColumn(&buf, "n.nspacl");
5222 ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
5223 gettext_noop("Description"));
5224 }
5225
5227 "\nFROM pg_catalog.pg_namespace n\n");
5228
5229 if (!showSystem && !pattern)
5231 "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
5232
5233 if (!validateSQLNamePattern(&buf, pattern,
5234 !showSystem && !pattern, false,
5235 NULL, "n.nspname", NULL,
5236 NULL,
5237 NULL, 2))
5238 goto error_return;
5239
5240 appendPQExpBufferStr(&buf, "ORDER BY 1;");
5241
5242 res = PSQLexec(buf.data);
5243 if (!res)
5244 goto error_return;
5245
5246 myopt.title = _("List of schemas");
5247 myopt.translate_header = true;
5248
5249 if (pattern && pset.sversion >= 150000)
5250 {
5251 PGresult *result;
5252 int i;
5253
5255 "SELECT pubname \n"
5256 "FROM pg_catalog.pg_publication p\n"
5257 " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
5258 " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
5259 "WHERE n.nspname = '%s'\n"
5260 "ORDER BY 1",
5261 pattern);
5262 result = PSQLexec(buf.data);
5263 if (!result)
5264 goto error_return;
5265 else
5266 pub_schema_tuples = PQntuples(result);
5267
5268 if (pub_schema_tuples > 0)
5269 {
5270 /*
5271 * Allocate memory for footers. Size of footers will be 1 (for
5272 * storing "Publications:" string) + publication schema mapping
5273 * count + 1 (for storing NULL).
5274 */
5275 footers = (char **) pg_malloc((1 + pub_schema_tuples + 1) * sizeof(char *));
5276 footers[0] = pg_strdup(_("Publications:"));
5277
5278 /* Might be an empty set - that's ok */
5279 for (i = 0; i < pub_schema_tuples; i++)
5280 {
5281 printfPQExpBuffer(&buf, " \"%s\"",
5282 PQgetvalue(result, i, 0));
5283
5284 footers[i + 1] = pg_strdup(buf.data);
5285 }
5286
5287 footers[i + 1] = NULL;
5288 myopt.footers = footers;
5289 }
5290
5291 PQclear(result);
5292 }
5293
5294 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5295
5297 PQclear(res);
5298
5299 /* Free the memory allocated for the footer */
5300 if (footers)
5301 {
5302 char **footer = NULL;
5303
5304 for (footer = footers; *footer; footer++)
5305 pg_free(*footer);
5306
5307 pg_free(footers);
5308 }
5309
5310 return true;
5311
5312error_return:
5314 return false;
5315}
5316
5317
5318/*
5319 * \dFp
5320 * list text search parsers
5321 */
5322bool
5323listTSParsers(const char *pattern, bool verbose)
5324{
5326 PGresult *res;
5327 printQueryOpt myopt = pset.popt;
5328
5329 if (verbose)
5330 return listTSParsersVerbose(pattern);
5331
5333
5335 "SELECT\n"
5336 " n.nspname as \"%s\",\n"
5337 " p.prsname as \"%s\",\n"
5338 " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
5339 "FROM pg_catalog.pg_ts_parser p\n"
5340 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
5341 gettext_noop("Schema"),
5342 gettext_noop("Name"),
5343 gettext_noop("Description")
5344 );
5345
5346 if (!validateSQLNamePattern(&buf, pattern, false, false,
5347 "n.nspname", "p.prsname", NULL,
5348 "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5349 NULL, 3))
5350 {
5352 return false;
5353 }
5354
5355 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5356
5357 res = PSQLexec(buf.data);
5359 if (!res)
5360 return false;
5361
5362 myopt.title = _("List of text search parsers");
5363 myopt.translate_header = true;
5364
5365 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5366
5367 PQclear(res);
5368 return true;
5369}
5370
5371/*
5372 * full description of parsers
5373 */
5374static bool
5375listTSParsersVerbose(const char *pattern)
5376{
5378 PGresult *res;
5379 int i;
5380
5382
5384 "SELECT p.oid,\n"
5385 " n.nspname,\n"
5386 " p.prsname\n"
5387 "FROM pg_catalog.pg_ts_parser p\n"
5388 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
5389 );
5390
5391 if (!validateSQLNamePattern(&buf, pattern, false, false,
5392 "n.nspname", "p.prsname", NULL,
5393 "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5394 NULL, 3))
5395 {
5397 return false;
5398 }
5399
5400 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5401
5402 res = PSQLexec(buf.data);
5404 if (!res)
5405 return false;
5406
5407 if (PQntuples(res) == 0)
5408 {
5409 if (!pset.quiet)
5410 {
5411 if (pattern)
5412 pg_log_error("Did not find any text search parser named \"%s\".",
5413 pattern);
5414 else
5415 pg_log_error("Did not find any text search parsers.");
5416 }
5417 PQclear(res);
5418 return false;
5419 }
5420
5421 for (i = 0; i < PQntuples(res); i++)
5422 {
5423 const char *oid;
5424 const char *nspname = NULL;
5425 const char *prsname;
5426
5427 oid = PQgetvalue(res, i, 0);
5428 if (!PQgetisnull(res, i, 1))
5429 nspname = PQgetvalue(res, i, 1);
5430 prsname = PQgetvalue(res, i, 2);
5431
5432 if (!describeOneTSParser(oid, nspname, prsname))
5433 {
5434 PQclear(res);
5435 return false;
5436 }
5437
5438 if (cancel_pressed)
5439 {
5440 PQclear(res);
5441 return false;
5442 }
5443 }
5444
5445 PQclear(res);
5446 return true;
5447}
5448
5449static bool
5450describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
5451{
5453 PGresult *res;
5454 PQExpBufferData title;
5455 printQueryOpt myopt = pset.popt;
5456 static const bool translate_columns[] = {true, false, false};
5457
5459
5461 "SELECT '%s' AS \"%s\",\n"
5462 " p.prsstart::pg_catalog.regproc AS \"%s\",\n"
5463 " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\"\n"
5464 " FROM pg_catalog.pg_ts_parser p\n"
5465 " WHERE p.oid = '%s'\n"
5466 "UNION ALL\n"
5467 "SELECT '%s',\n"
5468 " p.prstoken::pg_catalog.regproc,\n"
5469 " pg_catalog.obj_description(p.prstoken, 'pg_proc')\n"
5470 " FROM pg_catalog.pg_ts_parser p\n"
5471 " WHERE p.oid = '%s'\n"
5472 "UNION ALL\n"
5473 "SELECT '%s',\n"
5474 " p.prsend::pg_catalog.regproc,\n"
5475 " pg_catalog.obj_description(p.prsend, 'pg_proc')\n"
5476 " FROM pg_catalog.pg_ts_parser p\n"
5477 " WHERE p.oid = '%s'\n"
5478 "UNION ALL\n"
5479 "SELECT '%s',\n"
5480 " p.prsheadline::pg_catalog.regproc,\n"
5481 " pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n"
5482 " FROM pg_catalog.pg_ts_parser p\n"
5483 " WHERE p.oid = '%s'\n"
5484 "UNION ALL\n"
5485 "SELECT '%s',\n"
5486 " p.prslextype::pg_catalog.regproc,\n"
5487 " pg_catalog.obj_description(p.prslextype, 'pg_proc')\n"
5488 " FROM pg_catalog.pg_ts_parser p\n"
5489 " WHERE p.oid = '%s';",
5490 gettext_noop("Start parse"),
5491 gettext_noop("Method"),
5492 gettext_noop("Function"),
5493 gettext_noop("Description"),
5494 oid,
5495 gettext_noop("Get next token"),
5496 oid,
5497 gettext_noop("End parse"),
5498 oid,
5499 gettext_noop("Get headline"),
5500 oid,
5501 gettext_noop("Get token types"),
5502 oid);
5503
5504 res = PSQLexec(buf.data);
5506 if (!res)
5507 return false;
5508
5509 initPQExpBuffer(&title);
5510 if (nspname)
5511 printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""),
5512 nspname, prsname);
5513 else
5514 printfPQExpBuffer(&title, _("Text search parser \"%s\""), prsname);
5515 myopt.title = title.data;
5516 myopt.footers = NULL;
5517 myopt.topt.default_footer = false;
5518 myopt.translate_header = true;
5519 myopt.translate_columns = translate_columns;
5520 myopt.n_translate_columns = lengthof(translate_columns);
5521
5522 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5523
5524 PQclear(res);
5525
5527
5529 "SELECT t.alias as \"%s\",\n"
5530 " t.description as \"%s\"\n"
5531 "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t\n"
5532 "ORDER BY 1;",
5533 gettext_noop("Token name"),
5534 gettext_noop("Description"),
5535 oid);
5536
5537 res = PSQLexec(buf.data);
5539 if (!res)
5540 {
5541 termPQExpBuffer(&title);
5542 return false;
5543 }
5544
5545 if (nspname)
5546 printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""),
5547 nspname, prsname);
5548 else
5549 printfPQExpBuffer(&title, _("Token types for parser \"%s\""), prsname);
5550 myopt.title = title.data;
5551 myopt.footers = NULL;
5552 myopt.topt.default_footer = true;
5553 myopt.translate_header = true;
5554 myopt.translate_columns = NULL;
5555 myopt.n_translate_columns = 0;
5556
5557 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5558
5559 termPQExpBuffer(&title);
5560 PQclear(res);
5561 return true;
5562}
5563
5564
5565/*
5566 * \dFd
5567 * list text search dictionaries
5568 */
5569bool
5570listTSDictionaries(const char *pattern, bool verbose)
5571{
5573 PGresult *res;
5574 printQueryOpt myopt = pset.popt;
5575
5577
5579 "SELECT\n"
5580 " n.nspname as \"%s\",\n"
5581 " d.dictname as \"%s\",\n",
5582 gettext_noop("Schema"),
5583 gettext_noop("Name"));
5584
5585 if (verbose)
5586 {
5588 " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM\n"
5589 " pg_catalog.pg_ts_template t\n"
5590 " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace\n"
5591 " WHERE d.dicttemplate = t.oid ) AS \"%s\",\n"
5592 " d.dictinitoption as \"%s\",\n",
5593 gettext_noop("Template"),
5594 gettext_noop("Init options"));
5595 }
5596
5598 " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
5599 gettext_noop("Description"));
5600
5601 appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n"
5602 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
5603
5604 if (!validateSQLNamePattern(&buf, pattern, false, false,
5605 "n.nspname", "d.dictname", NULL,
5606 "pg_catalog.pg_ts_dict_is_visible(d.oid)",
5607 NULL, 3))
5608 {
5610 return false;
5611 }
5612
5613 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5614
5615 res = PSQLexec(buf.data);
5617 if (!res)
5618 return false;
5619
5620 myopt.title = _("List of text search dictionaries");
5621 myopt.translate_header = true;
5622
5623 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5624
5625 PQclear(res);
5626 return true;
5627}
5628
5629
5630/*
5631 * \dFt
5632 * list text search templates
5633 */
5634bool
5635listTSTemplates(const char *pattern, bool verbose)
5636{
5638 PGresult *res;
5639 printQueryOpt myopt = pset.popt;
5640
5642
5643 if (verbose)
5645 "SELECT\n"
5646 " n.nspname AS \"%s\",\n"
5647 " t.tmplname AS \"%s\",\n"
5648 " t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
5649 " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
5650 " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5651 gettext_noop("Schema"),
5652 gettext_noop("Name"),
5653 gettext_noop("Init"),
5654 gettext_noop("Lexize"),
5655 gettext_noop("Description"));
5656 else
5658 "SELECT\n"
5659 " n.nspname AS \"%s\",\n"
5660 " t.tmplname AS \"%s\",\n"
5661 " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5662 gettext_noop("Schema"),
5663 gettext_noop("Name"),
5664 gettext_noop("Description"));
5665
5666 appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n"
5667 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
5668
5669 if (!validateSQLNamePattern(&buf, pattern, false, false,
5670 "n.nspname", "t.tmplname", NULL,
5671 "pg_catalog.pg_ts_template_is_visible(t.oid)",
5672 NULL, 3))
5673 {
5675 return false;
5676 }
5677
5678 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5679
5680 res = PSQLexec(buf.data);
5682 if (!res)
5683 return false;
5684
5685 myopt.title = _("List of text search templates");
5686 myopt.translate_header = true;
5687
5688 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5689
5690 PQclear(res);
5691 return true;
5692}
5693
5694
5695/*
5696 * \dF
5697 * list text search configurations
5698 */
5699bool
5700listTSConfigs(const char *pattern, bool verbose)
5701{
5703 PGresult *res;
5704 printQueryOpt myopt = pset.popt;
5705
5706 if (verbose)
5707 return listTSConfigsVerbose(pattern);
5708
5710
5712 "SELECT\n"
5713 " n.nspname as \"%s\",\n"
5714 " c.cfgname as \"%s\",\n"
5715 " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
5716 "FROM pg_catalog.pg_ts_config c\n"
5717 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace\n",
5718 gettext_noop("Schema"),
5719 gettext_noop("Name"),
5720 gettext_noop("Description")
5721 );
5722
5723 if (!validateSQLNamePattern(&buf, pattern, false, false,
5724 "n.nspname", "c.cfgname", NULL,
5725 "pg_catalog.pg_ts_config_is_visible(c.oid)",
5726 NULL, 3))
5727 {
5729 return false;
5730 }
5731
5732 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5733
5734 res = PSQLexec(buf.data);
5736 if (!res)
5737 return false;
5738
5739 myopt.title = _("List of text search configurations");
5740 myopt.translate_header = true;
5741
5742 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5743
5744 PQclear(res);
5745 return true;
5746}
5747
5748static bool
5749listTSConfigsVerbose(const char *pattern)
5750{
5752 PGresult *res;
5753 int i;
5754
5756
5758 "SELECT c.oid, c.cfgname,\n"
5759 " n.nspname,\n"
5760 " p.prsname,\n"
5761 " np.nspname as pnspname\n"
5762 "FROM pg_catalog.pg_ts_config c\n"
5763 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace,\n"
5764 " pg_catalog.pg_ts_parser p\n"
5765 " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace\n"
5766 "WHERE p.oid = c.cfgparser\n"
5767 );
5768
5769 if (!validateSQLNamePattern(&buf, pattern, true, false,
5770 "n.nspname", "c.cfgname", NULL,
5771 "pg_catalog.pg_ts_config_is_visible(c.oid)",
5772 NULL, 3))
5773 {
5775 return false;
5776 }
5777
5778 appendPQExpBufferStr(&buf, "ORDER BY 3, 2;");
5779
5780 res = PSQLexec(buf.data);
5782 if (!res)
5783 return false;
5784
5785 if (PQntuples(res) == 0)
5786 {
5787 if (!pset.quiet)
5788 {
5789 if (pattern)
5790 pg_log_error("Did not find any text search configuration named \"%s\".",
5791 pattern);
5792 else
5793 pg_log_error("Did not find any text search configurations.");
5794 }
5795 PQclear(res);
5796 return false;
5797 }
5798
5799 for (i = 0; i < PQntuples(res); i++)
5800 {
5801 const char *oid;
5802 const char *cfgname;
5803 const char *nspname = NULL;
5804 const char *prsname;
5805 const char *pnspname = NULL;
5806
5807 oid = PQgetvalue(res, i, 0);
5808 cfgname = PQgetvalue(res, i, 1);
5809 if (!PQgetisnull(res, i, 2))
5810 nspname = PQgetvalue(res, i, 2);
5811 prsname = PQgetvalue(res, i, 3);
5812 if (!PQgetisnull(res, i, 4))
5813 pnspname = PQgetvalue(res, i, 4);
5814
5815 if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
5816 {
5817 PQclear(res);
5818 return false;
5819 }
5820
5821 if (cancel_pressed)
5822 {
5823 PQclear(res);
5824 return false;
5825 }
5826 }
5827
5828 PQclear(res);
5829 return true;
5830}
5831
5832static bool
5833describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
5834 const char *pnspname, const char *prsname)
5835{
5837 title;
5838 PGresult *res;
5839 printQueryOpt myopt = pset.popt;
5840
5842
5844 "SELECT\n"
5845 " ( SELECT t.alias FROM\n"
5846 " pg_catalog.ts_token_type(c.cfgparser) AS t\n"
5847 " WHERE t.tokid = m.maptokentype ) AS \"%s\",\n"
5848 " pg_catalog.btrim(\n"
5849 " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary\n"
5850 " FROM pg_catalog.pg_ts_config_map AS mm\n"
5851 " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype\n"
5852 " ORDER BY mapcfg, maptokentype, mapseqno\n"
5853 " ) :: pg_catalog.text,\n"
5854 " '{}') AS \"%s\"\n"
5855 "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m\n"
5856 "WHERE c.oid = '%s' AND m.mapcfg = c.oid\n"
5857 "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser\n"
5858 "ORDER BY 1;",
5859 gettext_noop("Token"),
5860 gettext_noop("Dictionaries"),
5861 oid);
5862
5863 res = PSQLexec(buf.data);
5865 if (!res)
5866 return false;
5867
5868 initPQExpBuffer(&title);
5869
5870 if (nspname)
5871 appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
5872 nspname, cfgname);
5873 else
5874 appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
5875 cfgname);
5876
5877 if (pnspname)
5878 appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
5879 pnspname, prsname);
5880 else
5881 appendPQExpBuffer(&title, _("\nParser: \"%s\""),
5882 prsname);
5883
5884 myopt.title = title.data;
5885 myopt.footers = NULL;
5886 myopt.topt.default_footer = false;
5887 myopt.translate_header = true;
5888
5889 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5890
5891 termPQExpBuffer(&title);
5892
5893 PQclear(res);
5894 return true;
5895}
5896
5897
5898/*
5899 * \dew
5900 *
5901 * Describes foreign-data wrappers
5902 */
5903bool
5904listForeignDataWrappers(const char *pattern, bool verbose)
5905{
5907 PGresult *res;
5908 printQueryOpt myopt = pset.popt;
5909
5912 "SELECT fdw.fdwname AS \"%s\",\n"
5913 " pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n"
5914 " fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n"
5915 " fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
5916 gettext_noop("Name"),
5917 gettext_noop("Owner"),
5918 gettext_noop("Handler"),
5919 gettext_noop("Validator"));
5920
5921 if (verbose)
5922 {
5923 appendPQExpBufferStr(&buf, ",\n ");
5924 printACLColumn(&buf, "fdwacl");
5926 ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE "
5927 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
5928 " pg_catalog.quote_ident(option_name) || ' ' || "
5929 " pg_catalog.quote_literal(option_value) FROM "
5930 " pg_catalog.pg_options_to_table(fdwoptions)), ', ') || ')' "
5931 " END AS \"%s\""
5932 ",\n d.description AS \"%s\" ",
5933 gettext_noop("FDW options"),
5934 gettext_noop("Description"));
5935 }
5936
5937 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
5938
5939 if (verbose)
5941 "LEFT JOIN pg_catalog.pg_description d\n"
5942 " ON d.classoid = fdw.tableoid "
5943 "AND d.objoid = fdw.oid AND d.objsubid = 0\n");
5944
5945 if (!validateSQLNamePattern(&buf, pattern, false, false,
5946 NULL, "fdwname", NULL, NULL,
5947 NULL, 1))
5948 {
5950 return false;
5951 }
5952
5953 appendPQExpBufferStr(&buf, "ORDER BY 1;");
5954
5955 res = PSQLexec(buf.data);
5957 if (!res)
5958 return false;
5959
5960 myopt.title = _("List of foreign-data wrappers");
5961 myopt.translate_header = true;
5962
5963 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5964
5965 PQclear(res);
5966 return true;
5967}
5968
5969/*
5970 * \des
5971 *
5972 * Describes foreign servers.
5973 */
5974bool
5975listForeignServers(const char *pattern, bool verbose)
5976{
5978 PGresult *res;
5979 printQueryOpt myopt = pset.popt;
5980
5983 "SELECT s.srvname AS \"%s\",\n"
5984 " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
5985 " f.fdwname AS \"%s\"",
5986 gettext_noop("Name"),
5987 gettext_noop("Owner"),
5988 gettext_noop("Foreign-data wrapper"));
5989
5990 if (verbose)
5991 {
5992 appendPQExpBufferStr(&buf, ",\n ");
5993 printACLColumn(&buf, "s.srvacl");
5995 ",\n"
5996 " s.srvtype AS \"%s\",\n"
5997 " s.srvversion AS \"%s\",\n"
5998 " CASE WHEN srvoptions IS NULL THEN '' ELSE "
5999 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6000 " pg_catalog.quote_ident(option_name) || ' ' || "
6001 " pg_catalog.quote_literal(option_value) FROM "
6002 " pg_catalog.pg_options_to_table(srvoptions)), ', ') || ')' "
6003 " END AS \"%s\",\n"
6004 " d.description AS \"%s\"",
6005 gettext_noop("Type"),
6006 gettext_noop("Version"),
6007 gettext_noop("FDW options"),
6008 gettext_noop("Description"));
6009 }
6010
6012 "\nFROM pg_catalog.pg_foreign_server s\n"
6013 " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
6014
6015 if (verbose)
6017 "LEFT JOIN pg_catalog.pg_description d\n "
6018 "ON d.classoid = s.tableoid AND d.objoid = s.oid "
6019 "AND d.objsubid = 0\n");
6020
6021 if (!validateSQLNamePattern(&buf, pattern, false, false,
6022 NULL, "s.srvname", NULL, NULL,
6023 NULL, 1))
6024 {
6026 return false;
6027 }
6028
6029 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6030
6031 res = PSQLexec(buf.data);
6033 if (!res)
6034 return false;
6035
6036 myopt.title = _("List of foreign servers");
6037 myopt.translate_header = true;
6038
6039 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6040
6041 PQclear(res);
6042 return true;
6043}
6044
6045/*
6046 * \deu
6047 *
6048 * Describes user mappings.
6049 */
6050bool
6051listUserMappings(const char *pattern, bool verbose)
6052{
6054 PGresult *res;
6055 printQueryOpt myopt = pset.popt;
6056
6059 "SELECT um.srvname AS \"%s\",\n"
6060 " um.usename AS \"%s\"",
6061 gettext_noop("Server"),
6062 gettext_noop("User name"));
6063
6064 if (verbose)
6066 ",\n CASE WHEN umoptions IS NULL THEN '' ELSE "
6067 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6068 " pg_catalog.quote_ident(option_name) || ' ' || "
6069 " pg_catalog.quote_literal(option_value) FROM "
6070 " pg_catalog.pg_options_to_table(umoptions)), ', ') || ')' "
6071 " END AS \"%s\"",
6072 gettext_noop("FDW options"));
6073
6074 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
6075
6076 if (!validateSQLNamePattern(&buf, pattern, false, false,
6077 NULL, "um.srvname", "um.usename", NULL,
6078 NULL, 1))
6079 {
6081 return false;
6082 }
6083
6084 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6085
6086 res = PSQLexec(buf.data);
6088 if (!res)
6089 return false;
6090
6091 myopt.title = _("List of user mappings");
6092 myopt.translate_header = true;
6093
6094 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6095
6096 PQclear(res);
6097 return true;
6098}
6099
6100/*
6101 * \det
6102 *
6103 * Describes foreign tables.
6104 */
6105bool
6106listForeignTables(const char *pattern, bool verbose)
6107{
6109 PGresult *res;
6110 printQueryOpt myopt = pset.popt;
6111
6114 "SELECT n.nspname AS \"%s\",\n"
6115 " c.relname AS \"%s\",\n"
6116 " s.srvname AS \"%s\"",
6117 gettext_noop("Schema"),
6118 gettext_noop("Table"),
6119 gettext_noop("Server"));
6120
6121 if (verbose)
6123 ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE "
6124 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6125 " pg_catalog.quote_ident(option_name) || ' ' || "
6126 " pg_catalog.quote_literal(option_value) FROM "
6127 " pg_catalog.pg_options_to_table(ftoptions)), ', ') || ')' "
6128 " END AS \"%s\",\n"
6129 " d.description AS \"%s\"",
6130 gettext_noop("FDW options"),
6131 gettext_noop("Description"));
6132
6134 "\nFROM pg_catalog.pg_foreign_table ft\n"
6135 " INNER JOIN pg_catalog.pg_class c"
6136 " ON c.oid = ft.ftrelid\n"
6137 " INNER JOIN pg_catalog.pg_namespace n"
6138 " ON n.oid = c.relnamespace\n"
6139 " INNER JOIN pg_catalog.pg_foreign_server s"
6140 " ON s.oid = ft.ftserver\n");
6141 if (verbose)
6143 " LEFT JOIN pg_catalog.pg_description d\n"
6144 " ON d.classoid = c.tableoid AND "
6145 "d.objoid = c.oid AND d.objsubid = 0\n");
6146
6147 if (!validateSQLNamePattern(&buf, pattern, false, false,
6148 "n.nspname", "c.relname", NULL,
6149 "pg_catalog.pg_table_is_visible(c.oid)",
6150 NULL, 3))
6151 {
6153 return false;
6154 }
6155
6156 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6157
6158 res = PSQLexec(buf.data);
6160 if (!res)
6161 return false;
6162
6163 myopt.title = _("List of foreign tables");
6164 myopt.translate_header = true;
6165
6166 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6167
6168 PQclear(res);
6169 return true;
6170}
6171
6172/*
6173 * \dx
6174 *
6175 * Briefly describes installed extensions.
6176 */
6177bool
6178listExtensions(const char *pattern)
6179{
6181 PGresult *res;
6182 printQueryOpt myopt = pset.popt;
6183
6186 "SELECT e.extname AS \"%s\", "
6187 "e.extversion AS \"%s\", ae.default_version AS \"%s\","
6188 "n.nspname AS \"%s\", d.description AS \"%s\"\n"
6189 "FROM pg_catalog.pg_extension e "
6190 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
6191 "LEFT JOIN pg_catalog.pg_description d ON d.objoid = e.oid "
6192 "AND d.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
6193 "LEFT JOIN pg_catalog.pg_available_extensions() ae(name, default_version, comment) ON ae.name = e.extname\n",
6194 gettext_noop("Name"),
6195 gettext_noop("Version"),
6196 gettext_noop("Default version"),
6197 gettext_noop("Schema"),
6198 gettext_noop("Description"));
6199
6200 if (!validateSQLNamePattern(&buf, pattern,
6201 false, false,
6202 NULL, "e.extname", NULL,
6203 NULL,
6204 NULL, 1))
6205 {
6207 return false;
6208 }
6209
6210 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6211
6212 res = PSQLexec(buf.data);
6214 if (!res)
6215 return false;
6216
6217 myopt.title = _("List of installed extensions");
6218 myopt.translate_header = true;
6219
6220 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6221
6222 PQclear(res);
6223 return true;
6224}
6225
6226/*
6227 * \dx+
6228 *
6229 * List contents of installed extensions.
6230 */
6231bool
6232listExtensionContents(const char *pattern)
6233{
6235 PGresult *res;
6236 int i;
6237
6240 "SELECT e.extname, e.oid\n"
6241 "FROM pg_catalog.pg_extension e\n");
6242
6243 if (!validateSQLNamePattern(&buf, pattern,
6244 false, false,
6245 NULL, "e.extname", NULL,
6246 NULL,
6247 NULL, 1))
6248 {
6250 return false;
6251 }
6252
6253 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6254
6255 res = PSQLexec(buf.data);
6257 if (!res)
6258 return false;
6259
6260 if (PQntuples(res) == 0)
6261 {
6262 if (!pset.quiet)
6263 {
6264 if (pattern)
6265 pg_log_error("Did not find any extension named \"%s\".",
6266 pattern);
6267 else
6268 pg_log_error("Did not find any extensions.");
6269 }
6270 PQclear(res);
6271 return false;
6272 }
6273
6274 for (i = 0; i < PQntuples(res); i++)
6275 {
6276 const char *extname;
6277 const char *oid;
6278
6279 extname = PQgetvalue(res, i, 0);
6280 oid = PQgetvalue(res, i, 1);
6281
6282 if (!listOneExtensionContents(extname, oid))
6283 {
6284 PQclear(res);
6285 return false;
6286 }
6287 if (cancel_pressed)
6288 {
6289 PQclear(res);
6290 return false;
6291 }
6292 }
6293
6294 PQclear(res);
6295 return true;
6296}
6297
6298static bool
6299listOneExtensionContents(const char *extname, const char *oid)
6300{
6302 PGresult *res;
6303 PQExpBufferData title;
6304 printQueryOpt myopt = pset.popt;
6305
6308 "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
6309 "FROM pg_catalog.pg_depend\n"
6310 "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
6311 "ORDER BY 1;",
6312 gettext_noop("Object description"),
6313 oid);
6314
6315 res = PSQLexec(buf.data);
6317 if (!res)
6318 return false;
6319
6320 initPQExpBuffer(&title);
6321 printfPQExpBuffer(&title, _("Objects in extension \"%s\""), extname);
6322 myopt.title = title.data;
6323 myopt.translate_header = true;
6324
6325 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6326
6327 termPQExpBuffer(&title);
6328 PQclear(res);
6329 return true;
6330}
6331
6332/*
6333 * validateSQLNamePattern
6334 *
6335 * Wrapper around string_utils's processSQLNamePattern which also checks the
6336 * pattern's validity. In addition to that function's parameters, takes a
6337 * 'maxparts' parameter specifying the maximum number of dotted names the
6338 * pattern is allowed to have, and a 'added_clause' parameter that returns by
6339 * reference whether a clause was added to 'buf'. Returns whether the pattern
6340 * passed validation, after logging any errors.
6341 */
6342static bool
6343validateSQLNamePattern(PQExpBuffer buf, const char *pattern, bool have_where,
6344 bool force_escape, const char *schemavar,
6345 const char *namevar, const char *altnamevar,
6346 const char *visibilityrule, bool *added_clause,
6347 int maxparts)
6348{
6349 PQExpBufferData dbbuf;
6350 int dotcnt;
6351 bool added;
6352
6353 initPQExpBuffer(&dbbuf);
6354 added = processSQLNamePattern(pset.db, buf, pattern, have_where, force_escape,
6355 schemavar, namevar, altnamevar,
6356 visibilityrule, &dbbuf, &dotcnt);
6357 if (added_clause != NULL)
6358 *added_clause = added;
6359
6360 if (dotcnt >= maxparts)
6361 {
6362 pg_log_error("improper qualified name (too many dotted names): %s",
6363 pattern);
6364 goto error_return;
6365 }
6366
6367 if (maxparts > 1 && dotcnt == maxparts - 1)
6368 {
6369 if (PQdb(pset.db) == NULL)
6370 {
6371 pg_log_error("You are currently not connected to a database.");
6372 goto error_return;
6373 }
6374 if (strcmp(PQdb(pset.db), dbbuf.data) != 0)
6375 {
6376 pg_log_error("cross-database references are not implemented: %s",
6377 pattern);
6378 goto error_return;
6379 }
6380 }
6381 termPQExpBuffer(&dbbuf);
6382 return true;
6383
6384error_return:
6385 termPQExpBuffer(&dbbuf);
6386 return false;
6387}
6388
6389/*
6390 * \dRp
6391 * Lists publications.
6392 *
6393 * Takes an optional regexp to select particular publications
6394 */
6395bool
6396listPublications(const char *pattern)
6397{
6399 PGresult *res;
6400 printQueryOpt myopt = pset.popt;
6401 static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false};
6402
6403 if (pset.sversion < 100000)
6404 {
6405 char sverbuf[32];
6406
6407 pg_log_error("The server (version %s) does not support publications.",
6409 sverbuf, sizeof(sverbuf)));
6410 return true;
6411 }
6412
6414
6416 "SELECT pubname AS \"%s\",\n"
6417 " pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
6418 " puballtables AS \"%s\",\n"
6419 " pubinsert AS \"%s\",\n"
6420 " pubupdate AS \"%s\",\n"
6421 " pubdelete AS \"%s\"",
6422 gettext_noop("Name"),
6423 gettext_noop("Owner"),
6424 gettext_noop("All tables"),
6425 gettext_noop("Inserts"),
6426 gettext_noop("Updates"),
6427 gettext_noop("Deletes"));
6428 if (pset.sversion >= 110000)
6430 ",\n pubtruncate AS \"%s\"",
6431 gettext_noop("Truncates"));
6432 if (pset.sversion >= 180000)
6434 ",\n (CASE pubgencols\n"
6435 " WHEN '%c' THEN 'none'\n"
6436 " WHEN '%c' THEN 'stored'\n"
6437 " END) AS \"%s\"",
6438 PUBLISH_GENCOLS_NONE,
6439 PUBLISH_GENCOLS_STORED,
6440 gettext_noop("Generated columns"));
6441 if (pset.sversion >= 130000)
6443 ",\n pubviaroot AS \"%s\"",
6444 gettext_noop("Via root"));
6445
6447 "\nFROM pg_catalog.pg_publication\n");
6448
6449 if (!validateSQLNamePattern(&buf, pattern, false, false,
6450 NULL, "pubname", NULL,
6451 NULL,
6452 NULL, 1))
6453 {
6455 return false;
6456 }
6457
6458 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6459
6460 res = PSQLexec(buf.data);
6462 if (!res)
6463 return false;
6464
6465 myopt.title = _("List of publications");
6466 myopt.translate_header = true;
6467 myopt.translate_columns = translate_columns;
6468 myopt.n_translate_columns = lengthof(translate_columns);
6469
6470 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6471
6472 PQclear(res);
6473
6474 return true;
6475}
6476
6477/*
6478 * Add footer to publication description.
6479 */
6480static bool
6482 bool as_schema, printTableContent *const cont)
6483{
6484 PGresult *res;
6485 int count = 0;
6486 int i = 0;
6487
6488 res = PSQLexec(buf->data);
6489 if (!res)
6490 return false;
6491 else
6492 count = PQntuples(res);
6493
6494 if (count > 0)
6495 printTableAddFooter(cont, footermsg);
6496
6497 for (i = 0; i < count; i++)
6498 {
6499 if (as_schema)
6500 printfPQExpBuffer(buf, " \"%s\"", PQgetvalue(res, i, 0));
6501 else
6502 {
6503 printfPQExpBuffer(buf, " \"%s.%s\"", PQgetvalue(res, i, 0),
6504 PQgetvalue(res, i, 1));
6505
6506 if (!PQgetisnull(res, i, 3))
6507 appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
6508
6509 if (!PQgetisnull(res, i, 2))
6510 appendPQExpBuffer(buf, " WHERE %s", PQgetvalue(res, i, 2));
6511 }
6512
6513 printTableAddFooter(cont, buf->data);
6514 }
6515
6516 PQclear(res);
6517 return true;
6518}
6519
6520/*
6521 * \dRp+
6522 * Describes publications including the contents.
6523 *
6524 * Takes an optional regexp to select particular publications
6525 */
6526bool
6527describePublications(const char *pattern)
6528{
6530 int i;
6531 PGresult *res;
6532 bool has_pubtruncate;
6533 bool has_pubgencols;
6534 bool has_pubviaroot;
6535
6536 PQExpBufferData title;
6537 printTableContent cont;
6538
6539 if (pset.sversion < 100000)
6540 {
6541 char sverbuf[32];
6542
6543 pg_log_error("The server (version %s) does not support publications.",
6545 sverbuf, sizeof(sverbuf)));
6546 return true;
6547 }
6548
6549 has_pubtruncate = (pset.sversion >= 110000);
6550 has_pubgencols = (pset.sversion >= 180000);
6551 has_pubviaroot = (pset.sversion >= 130000);
6552
6554
6556 "SELECT oid, pubname,\n"
6557 " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
6558 " puballtables, pubinsert, pubupdate, pubdelete");
6559 if (has_pubtruncate)
6561 ", pubtruncate");
6562 else
6564 ", false AS pubtruncate");
6565
6566 if (has_pubgencols)
6568 ", (CASE pubgencols\n"
6569 " WHEN '%c' THEN 'none'\n"
6570 " WHEN '%c' THEN 'stored'\n"
6571 " END) AS \"%s\"\n",
6572 PUBLISH_GENCOLS_NONE,
6573 PUBLISH_GENCOLS_STORED,
6574 gettext_noop("Generated columns"));
6575 else
6577 ", 'none' AS pubgencols");
6578
6579 if (has_pubviaroot)
6581 ", pubviaroot");
6582 else
6584 ", false AS pubviaroot");
6585
6587 "\nFROM pg_catalog.pg_publication\n");
6588
6589 if (!validateSQLNamePattern(&buf, pattern, false, false,
6590 NULL, "pubname", NULL,
6591 NULL,
6592 NULL, 1))
6593 {
6595 return false;
6596 }
6597
6598 appendPQExpBufferStr(&buf, "ORDER BY 2;");
6599
6600 res = PSQLexec(buf.data);
6601 if (!res)
6602 {
6604 return false;
6605 }
6606
6607 if (PQntuples(res) == 0)
6608 {
6609 if (!pset.quiet)
6610 {
6611 if (pattern)
6612 pg_log_error("Did not find any publication named \"%s\".",
6613 pattern);
6614 else
6615 pg_log_error("Did not find any publications.");
6616 }
6617
6619 PQclear(res);
6620 return false;
6621 }
6622
6623 for (i = 0; i < PQntuples(res); i++)
6624 {
6625 const char align = 'l';
6626 int ncols = 5;
6627 int nrows = 1;
6628 char *pubid = PQgetvalue(res, i, 0);
6629 char *pubname = PQgetvalue(res, i, 1);
6630 bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
6631 printTableOpt myopt = pset.popt.topt;
6632
6633 if (has_pubtruncate)
6634 ncols++;
6635 if (has_pubgencols)
6636 ncols++;
6637 if (has_pubviaroot)
6638 ncols++;
6639
6640 initPQExpBuffer(&title);
6641 printfPQExpBuffer(&title, _("Publication %s"), pubname);
6642 printTableInit(&cont, &myopt, title.data, ncols, nrows);
6643
6644 printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
6645 printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
6646 printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
6647 printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
6648 printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
6649 if (has_pubtruncate)
6650 printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
6651 if (has_pubgencols)
6652 printTableAddHeader(&cont, gettext_noop("Generated columns"), true, align);
6653 if (has_pubviaroot)
6654 printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
6655
6656 printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
6657 printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
6658 printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
6659 printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
6660 printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
6661 if (has_pubtruncate)
6662 printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
6663 if (has_pubgencols)
6664 printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
6665 if (has_pubviaroot)
6666 printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
6667
6668 if (!puballtables)
6669 {
6670 /* Get the tables for the specified publication */
6672 "SELECT n.nspname, c.relname");
6673 if (pset.sversion >= 150000)
6674 {
6676 ", pg_get_expr(pr.prqual, c.oid)");
6678 ", (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
6679 " pg_catalog.array_to_string("
6680 " ARRAY(SELECT attname\n"
6681 " FROM\n"
6682 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
6683 " pg_catalog.pg_attribute\n"
6684 " WHERE attrelid = c.oid AND attnum = prattrs[s]), ', ')\n"
6685 " ELSE NULL END)");
6686 }
6687 else
6689 ", NULL, NULL");
6691 "\nFROM pg_catalog.pg_class c,\n"
6692 " pg_catalog.pg_namespace n,\n"
6693 " pg_catalog.pg_publication_rel pr\n"
6694 "WHERE c.relnamespace = n.oid\n"
6695 " AND c.oid = pr.prrelid\n"
6696 " AND pr.prpubid = '%s'\n"
6697 "ORDER BY 1,2", pubid);
6698 if (!addFooterToPublicationDesc(&buf, _("Tables:"), false, &cont))
6699 goto error_return;
6700
6701 if (pset.sversion >= 150000)
6702 {
6703 /* Get the schemas for the specified publication */
6705 "SELECT n.nspname\n"
6706 "FROM pg_catalog.pg_namespace n\n"
6707 " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
6708 "WHERE pn.pnpubid = '%s'\n"
6709 "ORDER BY 1", pubid);
6710 if (!addFooterToPublicationDesc(&buf, _("Tables from schemas:"),
6711 true, &cont))
6712 goto error_return;
6713 }
6714 }
6715
6716 printTable(&cont, pset.queryFout, false, pset.logfile);
6717 printTableCleanup(&cont);
6718
6719 termPQExpBuffer(&title);
6720 }
6721
6723 PQclear(res);
6724
6725 return true;
6726
6727error_return:
6728 printTableCleanup(&cont);
6729 PQclear(res);
6731 termPQExpBuffer(&title);
6732 return false;
6733}
6734
6735/*
6736 * \dRs
6737 * Describes subscriptions.
6738 *
6739 * Takes an optional regexp to select particular subscriptions
6740 */
6741bool
6742describeSubscriptions(const char *pattern, bool verbose)
6743{
6745 PGresult *res;
6746 printQueryOpt myopt = pset.popt;
6747 static const bool translate_columns[] = {false, false, false, false,
6748 false, false, false, false, false, false, false, false, false, false,
6749 false, false, false, false};
6750
6751 if (pset.sversion < 100000)
6752 {
6753 char sverbuf[32];
6754
6755 pg_log_error("The server (version %s) does not support subscriptions.",
6757 sverbuf, sizeof(sverbuf)));
6758 return true;
6759 }
6760
6762
6764 "SELECT subname AS \"%s\"\n"
6765 ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
6766 ", subenabled AS \"%s\"\n"
6767 ", subpublications AS \"%s\"\n",
6768 gettext_noop("Name"),
6769 gettext_noop("Owner"),
6770 gettext_noop("Enabled"),
6771 gettext_noop("Publication"));
6772
6773 if (verbose)
6774 {
6775 /* Binary mode and streaming are only supported in v14 and higher */
6776 if (pset.sversion >= 140000)
6777 {
6779 ", subbinary AS \"%s\"\n",
6780 gettext_noop("Binary"));
6781
6782 if (pset.sversion >= 160000)
6784 ", (CASE substream\n"
6785 " WHEN " CppAsString2(LOGICALREP_STREAM_OFF) " THEN 'off'\n"
6786 " WHEN " CppAsString2(LOGICALREP_STREAM_ON) " THEN 'on'\n"
6787 " WHEN " CppAsString2(LOGICALREP_STREAM_PARALLEL) " THEN 'parallel'\n"
6788 " END) AS \"%s\"\n",
6789 gettext_noop("Streaming"));
6790 else
6792 ", substream AS \"%s\"\n",
6793 gettext_noop("Streaming"));
6794 }
6795
6796 /* Two_phase and disable_on_error are only supported in v15 and higher */
6797 if (pset.sversion >= 150000)
6799 ", subtwophasestate AS \"%s\"\n"
6800 ", subdisableonerr AS \"%s\"\n",
6801 gettext_noop("Two-phase commit"),
6802 gettext_noop("Disable on error"));
6803
6804 if (pset.sversion >= 160000)
6806 ", suborigin AS \"%s\"\n"
6807 ", subpasswordrequired AS \"%s\"\n"
6808 ", subrunasowner AS \"%s\"\n",
6809 gettext_noop("Origin"),
6810 gettext_noop("Password required"),
6811 gettext_noop("Run as owner?"));
6812
6813 if (pset.sversion >= 170000)
6815 ", subfailover AS \"%s\"\n",
6816 gettext_noop("Failover"));
6817 if (pset.sversion >= 190000)
6818 {
6820 ", subretaindeadtuples AS \"%s\"\n",
6821 gettext_noop("Retain dead tuples"));
6822
6824 ", submaxretention AS \"%s\"\n",
6825 gettext_noop("Max retention duration"));
6826
6828 ", subretentionactive AS \"%s\"\n",
6829 gettext_noop("Retention active"));
6830 }
6831
6833 ", subsynccommit AS \"%s\"\n"
6834 ", subconninfo AS \"%s\"\n",
6835 gettext_noop("Synchronous commit"),
6836 gettext_noop("Conninfo"));
6837
6838 /* Skip LSN is only supported in v15 and higher */
6839 if (pset.sversion >= 150000)
6841 ", subskiplsn AS \"%s\"\n",
6842 gettext_noop("Skip LSN"));
6843 }
6844
6845 /* Only display subscriptions in current database. */
6847 "FROM pg_catalog.pg_subscription\n"
6848 "WHERE subdbid = (SELECT oid\n"
6849 " FROM pg_catalog.pg_database\n"
6850 " WHERE datname = pg_catalog.current_database())");
6851
6852 if (!validateSQLNamePattern(&buf, pattern, true, false,
6853 NULL, "subname", NULL,
6854 NULL,
6855 NULL, 1))
6856 {
6858 return false;
6859 }
6860
6861 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6862
6863 res = PSQLexec(buf.data);
6865 if (!res)
6866 return false;
6867
6868 myopt.title = _("List of subscriptions");
6869 myopt.translate_header = true;
6870 myopt.translate_columns = translate_columns;
6871 myopt.n_translate_columns = lengthof(translate_columns);
6872
6873 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6874
6875 PQclear(res);
6876 return true;
6877}
6878
6879/*
6880 * printACLColumn
6881 *
6882 * Helper function for consistently formatting ACL (privilege) columns.
6883 * The proper targetlist entry is appended to buf. Note lack of any
6884 * whitespace or comma decoration.
6885 *
6886 * If you change this, see also the handling of attacl in permissionsList(),
6887 * which can't conveniently use this code.
6888 */
6889static void
6890printACLColumn(PQExpBuffer buf, const char *colname)
6891{
6893 "CASE"
6894 " WHEN pg_catalog.array_length(%s, 1) = 0 THEN '%s'"
6895 " ELSE pg_catalog.array_to_string(%s, E'\\n')"
6896 " END AS \"%s\"",
6897 colname, gettext_noop("(none)"),
6898 colname, gettext_noop("Access privileges"));
6899}
6900
6901/*
6902 * \dAc
6903 * Lists operator classes
6904 *
6905 * Takes optional regexps to filter by index access method and input data type.
6906 */
6907bool
6908listOperatorClasses(const char *access_method_pattern,
6909 const char *type_pattern, bool verbose)
6910{
6912 PGresult *res;
6913 printQueryOpt myopt = pset.popt;
6914 bool have_where = false;
6915 static const bool translate_columns[] = {false, false, false, false, false, false, false};
6916
6918
6920 "SELECT\n"
6921 " am.amname AS \"%s\",\n"
6922 " pg_catalog.format_type(c.opcintype, NULL) AS \"%s\",\n"
6923 " CASE\n"
6924 " WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n"
6925 " THEN pg_catalog.format_type(c.opckeytype, NULL)\n"
6926 " ELSE NULL\n"
6927 " END AS \"%s\",\n"
6928 " CASE\n"
6929 " WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n"
6930 " THEN pg_catalog.format('%%I', c.opcname)\n"
6931 " ELSE pg_catalog.format('%%I.%%I', n.nspname, c.opcname)\n"
6932 " END AS \"%s\",\n"
6933 " (CASE WHEN c.opcdefault\n"
6934 " THEN '%s'\n"
6935 " ELSE '%s'\n"
6936 " END) AS \"%s\"",
6937 gettext_noop("AM"),
6938 gettext_noop("Input type"),
6939 gettext_noop("Storage type"),
6940 gettext_noop("Operator class"),
6941 gettext_noop("yes"),
6942 gettext_noop("no"),
6943 gettext_noop("Default?"));
6944 if (verbose)
6946 ",\n CASE\n"
6947 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
6948 " THEN pg_catalog.format('%%I', of.opfname)\n"
6949 " ELSE pg_catalog.format('%%I.%%I', ofn.nspname, of.opfname)\n"
6950 " END AS \"%s\",\n"
6951 " pg_catalog.pg_get_userbyid(c.opcowner) AS \"%s\"\n",
6952 gettext_noop("Operator family"),
6953 gettext_noop("Owner"));
6955 "\nFROM pg_catalog.pg_opclass c\n"
6956 " LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n"
6957 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n"
6958 " LEFT JOIN pg_catalog.pg_type t ON t.oid = c.opcintype\n"
6959 " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n");
6960 if (verbose)
6962 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n"
6963 " LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n");
6964
6965 if (access_method_pattern)
6966 if (!validateSQLNamePattern(&buf, access_method_pattern,
6967 false, false, NULL, "am.amname", NULL, NULL,
6968 &have_where, 1))
6969 goto error_return;
6970 if (type_pattern)
6971 {
6972 /* Match type name pattern against either internal or external name */
6973 if (!validateSQLNamePattern(&buf, type_pattern, have_where, false,
6974 "tn.nspname", "t.typname",
6975 "pg_catalog.format_type(t.oid, NULL)",
6976 "pg_catalog.pg_type_is_visible(t.oid)",
6977 NULL, 3))
6978 goto error_return;
6979 }
6980
6981 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
6982 res = PSQLexec(buf.data);
6984 if (!res)
6985 return false;
6986
6987 myopt.title = _("List of operator classes");
6988 myopt.translate_header = true;
6989 myopt.translate_columns = translate_columns;
6990 myopt.n_translate_columns = lengthof(translate_columns);
6991
6992 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6993
6994 PQclear(res);
6995 return true;
6996
6997error_return:
6999 return false;
7000}
7001
7002/*
7003 * \dAf
7004 * Lists operator families
7005 *
7006 * Takes optional regexps to filter by index access method and input data type.
7007 */
7008bool
7009listOperatorFamilies(const char *access_method_pattern,
7010 const char *type_pattern, bool verbose)
7011{
7013 PGresult *res;
7014 printQueryOpt myopt = pset.popt;
7015 bool have_where = false;
7016 static const bool translate_columns[] = {false, false, false, false};
7017
7019
7021 "SELECT\n"
7022 " am.amname AS \"%s\",\n"
7023 " CASE\n"
7024 " WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n"
7025 " THEN pg_catalog.format('%%I', f.opfname)\n"
7026 " ELSE pg_catalog.format('%%I.%%I', n.nspname, f.opfname)\n"
7027 " END AS \"%s\",\n"
7028 " (SELECT\n"
7029 " pg_catalog.string_agg(pg_catalog.format_type(oc.opcintype, NULL), ', ')\n"
7030 " FROM pg_catalog.pg_opclass oc\n"
7031 " WHERE oc.opcfamily = f.oid) \"%s\"",
7032 gettext_noop("AM"),
7033 gettext_noop("Operator family"),
7034 gettext_noop("Applicable types"));
7035 if (verbose)
7037 ",\n pg_catalog.pg_get_userbyid(f.opfowner) AS \"%s\"\n",
7038 gettext_noop("Owner"));
7040 "\nFROM pg_catalog.pg_opfamily f\n"
7041 " LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n"
7042 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace\n");
7043
7044 if (access_method_pattern)
7045 if (!validateSQLNamePattern(&buf, access_method_pattern,
7046 false, false, NULL, "am.amname", NULL, NULL,
7047 &have_where, 1))
7048 goto error_return;
7049 if (type_pattern)
7050 {
7052 " %s EXISTS (\n"
7053 " SELECT 1\n"
7054 " FROM pg_catalog.pg_type t\n"
7055 " JOIN pg_catalog.pg_opclass oc ON oc.opcintype = t.oid\n"
7056 " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n"
7057 " WHERE oc.opcfamily = f.oid\n",
7058 have_where ? "AND" : "WHERE");
7059 /* Match type name pattern against either internal or external name */
7060 if (!validateSQLNamePattern(&buf, type_pattern, true, false,
7061 "tn.nspname", "t.typname",
7062 "pg_catalog.format_type(t.oid, NULL)",
7063 "pg_catalog.pg_type_is_visible(t.oid)",
7064 NULL, 3))
7065 goto error_return;
7066 appendPQExpBufferStr(&buf, " )\n");
7067 }
7068
7069 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
7070 res = PSQLexec(buf.data);
7072 if (!res)
7073 return false;
7074
7075 myopt.title = _("List of operator families");
7076 myopt.translate_header = true;
7077 myopt.translate_columns = translate_columns;
7078 myopt.n_translate_columns = lengthof(translate_columns);
7079
7080 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7081
7082 PQclear(res);
7083 return true;
7084
7085error_return:
7087 return false;
7088}
7089
7090/*
7091 * \dAo
7092 * Lists operators of operator families
7093 *
7094 * Takes optional regexps to filter by index access method and operator
7095 * family.
7096 */
7097bool
7098listOpFamilyOperators(const char *access_method_pattern,
7099 const char *family_pattern, bool verbose)
7100{
7102 PGresult *res;
7103 printQueryOpt myopt = pset.popt;
7104 bool have_where = false;
7105
7106 static const bool translate_columns[] = {false, false, false, false, false, false, true};
7107
7109
7111 "SELECT\n"
7112 " am.amname AS \"%s\",\n"
7113 " CASE\n"
7114 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7115 " THEN pg_catalog.format('%%I', of.opfname)\n"
7116 " ELSE pg_catalog.format('%%I.%%I', nsf.nspname, of.opfname)\n"
7117 " END AS \"%s\",\n"
7118 " o.amopopr::pg_catalog.regoperator AS \"%s\"\n,"
7119 " o.amopstrategy AS \"%s\",\n"
7120 " CASE o.amoppurpose\n"
7121 " WHEN " CppAsString2(AMOP_ORDER) " THEN '%s'\n"
7122 " WHEN " CppAsString2(AMOP_SEARCH) " THEN '%s'\n"
7123 " END AS \"%s\"\n",
7124 gettext_noop("AM"),
7125 gettext_noop("Operator family"),
7126 gettext_noop("Operator"),
7127 gettext_noop("Strategy"),
7128 gettext_noop("ordering"),
7129 gettext_noop("search"),
7130 gettext_noop("Purpose"));
7131
7132 if (verbose)
7134 ", ofs.opfname AS \"%s\",\n"
7135 " CASE\n"
7136 " WHEN p.proleakproof THEN '%s'\n"
7137 " ELSE '%s'\n"
7138 " END AS \"%s\"\n",
7139 gettext_noop("Sort opfamily"),
7140 gettext_noop("yes"),
7141 gettext_noop("no"),
7142 gettext_noop("Leakproof?"));
7144 "FROM pg_catalog.pg_amop o\n"
7145 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n"
7146 " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n"
7147 " LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n");
7148 if (verbose)
7150 " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n"
7151 " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n"
7152 " LEFT JOIN pg_catalog.pg_proc p ON p.oid = op.oprcode\n");
7153
7154 if (access_method_pattern)
7155 {
7156 if (!validateSQLNamePattern(&buf, access_method_pattern,
7157 false, false, NULL, "am.amname",
7158 NULL, NULL,
7159 &have_where, 1))
7160 goto error_return;
7161 }
7162
7163 if (family_pattern)
7164 {
7165 if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
7166 "nsf.nspname", "of.opfname", NULL, NULL,
7167 NULL, 3))
7168 goto error_return;
7169 }
7170
7171 appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7172 " o.amoplefttype = o.amoprighttype DESC,\n"
7173 " pg_catalog.format_type(o.amoplefttype, NULL),\n"
7174 " pg_catalog.format_type(o.amoprighttype, NULL),\n"
7175 " o.amopstrategy;");
7176
7177 res = PSQLexec(buf.data);
7179 if (!res)
7180 return false;
7181
7182 myopt.title = _("List of operators of operator families");
7183 myopt.translate_header = true;
7184 myopt.translate_columns = translate_columns;
7185 myopt.n_translate_columns = lengthof(translate_columns);
7186
7187 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7188
7189 PQclear(res);
7190 return true;
7191
7192error_return:
7194 return false;
7195}
7196
7197/*
7198 * \dAp
7199 * Lists support functions of operator families
7200 *
7201 * Takes optional regexps to filter by index access method and operator
7202 * family.
7203 */
7204bool
7205listOpFamilyFunctions(const char *access_method_pattern,
7206 const char *family_pattern, bool verbose)
7207{
7209 PGresult *res;
7210 printQueryOpt myopt = pset.popt;
7211 bool have_where = false;
7212 static const bool translate_columns[] = {false, false, false, false, false, false};
7213
7215
7217 "SELECT\n"
7218 " am.amname AS \"%s\",\n"
7219 " CASE\n"
7220 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7221 " THEN pg_catalog.format('%%I', of.opfname)\n"
7222 " ELSE pg_catalog.format('%%I.%%I', ns.nspname, of.opfname)\n"
7223 " END AS \"%s\",\n"
7224 " pg_catalog.format_type(ap.amproclefttype, NULL) AS \"%s\",\n"
7225 " pg_catalog.format_type(ap.amprocrighttype, NULL) AS \"%s\",\n"
7226 " ap.amprocnum AS \"%s\"\n",
7227 gettext_noop("AM"),
7228 gettext_noop("Operator family"),
7229 gettext_noop("Registered left type"),
7230 gettext_noop("Registered right type"),
7231 gettext_noop("Number"));
7232
7233 if (!verbose)
7235 ", p.proname AS \"%s\"\n",
7236 gettext_noop("Function"));
7237 else
7239 ", ap.amproc::pg_catalog.regprocedure AS \"%s\"\n",
7240 gettext_noop("Function"));
7241
7243 "FROM pg_catalog.pg_amproc ap\n"
7244 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n"
7245 " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n"
7246 " LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n"
7247 " LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.oid\n");
7248
7249 if (access_method_pattern)
7250 {
7251 if (!validateSQLNamePattern(&buf, access_method_pattern,
7252 false, false, NULL, "am.amname",
7253 NULL, NULL,
7254 &have_where, 1))
7255 goto error_return;
7256 }
7257 if (family_pattern)
7258 {
7259 if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
7260 "ns.nspname", "of.opfname", NULL, NULL,
7261 NULL, 3))
7262 goto error_return;
7263 }
7264
7265 appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7266 " ap.amproclefttype = ap.amprocrighttype DESC,\n"
7267 " 3, 4, 5;");
7268
7269 res = PSQLexec(buf.data);
7271 if (!res)
7272 return false;
7273
7274 myopt.title = _("List of support functions of operator families");
7275 myopt.translate_header = true;
7276 myopt.translate_columns = translate_columns;
7277 myopt.n_translate_columns = lengthof(translate_columns);
7278
7279 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7280
7281 PQclear(res);
7282 return true;
7283
7284error_return:
7286 return false;
7287}
7288
7289/*
7290 * \dl or \lo_list
7291 * Lists large objects
7292 */
7293bool
7295{
7297 PGresult *res;
7298 printQueryOpt myopt = pset.popt;
7299
7301
7303 "SELECT oid as \"%s\",\n"
7304 " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n ",
7305 gettext_noop("ID"),
7306 gettext_noop("Owner"));
7307
7308 if (verbose)
7309 {
7310 printACLColumn(&buf, "lomacl");
7311 appendPQExpBufferStr(&buf, ",\n ");
7312 }
7313
7315 "pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
7316 "FROM pg_catalog.pg_largeobject_metadata\n"
7317 "ORDER BY oid",
7318 gettext_noop("Description"));
7319
7320 res = PSQLexec(buf.data);
7322 if (!res)
7323 return false;
7324
7325 myopt.title = _("Large objects");
7326 myopt.translate_header = true;
7327
7328 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7329
7330 PQclear(res);
7331 return true;
7332}
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:534
#define CppAsString2(x)
Definition: c.h:419
#define lengthof(array)
Definition: c.h:788
bool listUserMappings(const char *pattern, bool verbose)
Definition: describe.c:6051
bool listTSConfigs(const char *pattern, bool verbose)
Definition: describe.c:5700
bool describeRoles(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:3712
bool listOpFamilyFunctions(const char *access_method_pattern, const char *family_pattern, bool verbose)
Definition: describe.c:7205
bool listPublications(const char *pattern)
Definition: describe.c:6396
bool listTSParsers(const char *pattern, bool verbose)
Definition: describe.c:5323
bool listExtensionContents(const char *pattern)
Definition: describe.c:6232
bool describeAggregates(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:78
bool listForeignDataWrappers(const char *pattern, bool verbose)
Definition: describe.c:5904
bool listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
Definition: describe.c:4262
bool describeSubscriptions(const char *pattern, bool verbose)
Definition: describe.c:6742
bool describeRoleGrants(const char *pattern, bool showSystem)
Definition: describe.c:3928
bool listExtendedStats(const char *pattern)
Definition: describe.c:4859
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:7009
bool listForeignServers(const char *pattern, bool verbose)
Definition: describe.c:5975
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:4548
bool listTSDictionaries(const char *pattern, bool verbose)
Definition: describe.c:5570
bool listDbRoleSettings(const char *pattern, const char *pattern2)
Definition: describe.c:3859
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:3847
bool listCollations(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:5079
bool listSchemas(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:5202
bool listExtensions(const char *pattern)
Definition: describe.c:6178
static bool describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, const char *pnspname, const char *prsname)
Definition: describe.c:5833
static bool listOneExtensionContents(const char *extname, const char *oid)
Definition: describe.c:6299
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:5375
bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:4007
bool listTSTemplates(const char *pattern, bool verbose)
Definition: describe.c:5635
bool describeTablespaces(const char *pattern, bool verbose)
Definition: describe.c:222
bool listCasts(const char *pattern, bool verbose)
Definition: describe.c:4955
bool listOpFamilyOperators(const char *access_method_pattern, const char *family_pattern, bool verbose)
Definition: describe.c:7098
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:6908
static const char * map_typename_pattern(const char *pattern)
Definition: describe.c:744
bool listForeignTables(const char *pattern, bool verbose)
Definition: describe.c:6106
bool describeConfigurationParameters(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:4711
bool listEventTriggers(const char *pattern, bool verbose)
Definition: describe.c:4779
bool listDefaultACLs(const char *pattern)
Definition: describe.c:1214
static void printACLColumn(PQExpBuffer buf, const char *colname)
Definition: describe.c:6890
static bool addFooterToPublicationDesc(PQExpBuffer buf, const char *footermsg, bool as_schema, printTableContent *const cont)
Definition: describe.c:6481
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:6343
bool listAllDbs(const char *pattern, bool verbose)
Definition: describe.c:946
bool listConversions(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:4631
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:5749
bool listLargeObjects(bool verbose)
Definition: describe.c:7294
static bool describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
Definition: describe.c:5450
static void add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline)
Definition: describe.c:3647
bool listLanguages(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:4472
bool describePublications(const char *pattern)
Definition: describe.c:6527
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:7513
int PQfnumber(const PGresult *res, const char *field_name)
Definition: fe-exec.c:3600
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:3172
void printTableCleanup(printTableContent *const content)
Definition: print.c:3353
void printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, bool is_pager, FILE *flog)
Definition: print.c:3549
void printTableAddCell(printTableContent *const content, char *cell, const bool translate, const bool mustfree)
Definition: print.c:3260
void printTableSetFooter(printTableContent *const content, const char *footer)
Definition: print.c:3335
void printTable(const printTableContent *cont, FILE *fout, bool is_pager, FILE *flog)
Definition: print.c:3443
void printTableAddFooter(printTableContent *const content, const char *footer)
Definition: print.c:3310
void printTableAddHeader(printTableContent *const content, char *header, const bool translate, const char align)
Definition: print.c:3220
volatile sig_atomic_t cancel_pressed
Definition: print.c:43
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:190
printTableOpt topt
Definition: print.h:185
char * title
Definition: print.h:187
char ** footers
Definition: print.h:188
bool translate_header
Definition: print.h:189
int n_translate_columns
Definition: print.h:192
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