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