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')\n"
990 " THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n"
991 " ELSE 'No Access'\n"
992 " END as \"%s\""
993 ",\n t.spcname as \"%s\""
994 ",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"",
995 gettext_noop("Size"),
996 gettext_noop("Tablespace"),
997 gettext_noop("Description"));
999 "\nFROM pg_catalog.pg_database d\n");
1000 if (verbose)
1002 " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n");
1003
1004 if (pattern)
1005 {
1006 if (!validateSQLNamePattern(&buf, pattern, false, false,
1007 NULL, "d.datname", NULL, NULL,
1008 NULL, 1))
1009 {
1011 return false;
1012 }
1013 }
1014
1015 appendPQExpBufferStr(&buf, "ORDER BY 1;");
1016 res = PSQLexec(buf.data);
1018 if (!res)
1019 return false;
1020
1021 myopt.title = _("List of databases");
1022 myopt.translate_header = true;
1023
1024 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1025
1026 PQclear(res);
1027 return true;
1028}
1029
1030
1031/*
1032 * List Tables' Grant/Revoke Permissions
1033 * \z (now also \dp -- perhaps more mnemonic)
1034 */
1035bool
1036permissionsList(const char *pattern, bool showSystem)
1037{
1039 PGresult *res;
1041 static const bool translate_columns[] = {false, false, true, false, false, false};
1042
1044
1045 /*
1046 * we ignore indexes and toast tables since they have no meaningful rights
1047 */
1048 printfPQExpBuffer(&buf, "/* %s */\n",
1049 _("Get access privileges of matching relations"));
1051 "SELECT n.nspname as \"%s\",\n"
1052 " c.relname as \"%s\",\n"
1053 " CASE c.relkind"
1054 " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
1055 " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'"
1056 " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
1057 " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
1058 " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
1059 " WHEN " CppAsString2(RELKIND_PROPGRAPH) " THEN '%s'"
1060 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
1061 " END as \"%s\",\n"
1062 " ",
1063 gettext_noop("Schema"),
1064 gettext_noop("Name"),
1065 gettext_noop("table"),
1066 gettext_noop("view"),
1067 gettext_noop("materialized view"),
1068 gettext_noop("sequence"),
1069 gettext_noop("foreign table"),
1070 gettext_noop("property graph"),
1071 gettext_noop("partitioned table"),
1072 gettext_noop("Type"));
1073
1074 printACLColumn(&buf, "c.relacl");
1075
1076 /*
1077 * The formatting of attacl should match printACLColumn(). However, we
1078 * need no special case for an empty attacl, because the backend always
1079 * optimizes that back to NULL.
1080 */
1082 ",\n pg_catalog.array_to_string(ARRAY(\n"
1083 " SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n"
1084 " FROM pg_catalog.pg_attribute a\n"
1085 " WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n"
1086 " ), E'\\n') AS \"%s\"",
1087 gettext_noop("Column privileges"));
1088
1090 ",\n pg_catalog.array_to_string(ARRAY(\n"
1091 " SELECT polname\n"
1092 " || CASE WHEN NOT polpermissive THEN\n"
1093 " E' (RESTRICTIVE)'\n"
1094 " ELSE '' END\n"
1095 " || CASE WHEN polcmd != '*' THEN\n"
1096 " E' (' || polcmd::pg_catalog.text || E'):'\n"
1097 " ELSE E':'\n"
1098 " END\n"
1099 " || CASE WHEN polqual IS NOT NULL THEN\n"
1100 " E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
1101 " ELSE E''\n"
1102 " END\n"
1103 " || CASE WHEN polwithcheck IS NOT NULL THEN\n"
1104 " E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
1105 " ELSE E''\n"
1106 " END"
1107 " || CASE WHEN polroles <> '{0}' THEN\n"
1108 " E'\\n to: ' || pg_catalog.array_to_string(\n"
1109 " ARRAY(\n"
1110 " SELECT rolname\n"
1111 " FROM pg_catalog.pg_roles\n"
1112 " WHERE oid = ANY (polroles)\n"
1113 " ORDER BY 1\n"
1114 " ), E', ')\n"
1115 " ELSE E''\n"
1116 " END\n"
1117 " FROM pg_catalog.pg_policy pol\n"
1118 " WHERE polrelid = c.oid), E'\\n')\n"
1119 " AS \"%s\"",
1120 gettext_noop("Policies"));
1121
1122 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
1123 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
1124 "WHERE c.relkind IN ("
1132
1133 if (!showSystem && !pattern)
1134 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1135 " AND n.nspname <> 'information_schema'\n");
1136
1137 if (!validateSQLNamePattern(&buf, pattern, true, false,
1138 "n.nspname", "c.relname", NULL,
1139 "pg_catalog.pg_table_is_visible(c.oid)",
1140 NULL, 3))
1141 goto error_return;
1142
1143 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
1144
1145 res = PSQLexec(buf.data);
1146 if (!res)
1147 goto error_return;
1148
1149 printfPQExpBuffer(&buf, _("Access privileges"));
1150 myopt.title = buf.data;
1151 myopt.translate_header = true;
1152 myopt.translate_columns = translate_columns;
1153 myopt.n_translate_columns = lengthof(translate_columns);
1154
1155 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1156
1158 PQclear(res);
1159 return true;
1160
1163 return false;
1164}
1165
1166
1167/*
1168 * \ddp
1169 *
1170 * List Default ACLs. The pattern can match either schema or role name.
1171 */
1172bool
1173listDefaultACLs(const char *pattern)
1174{
1176 PGresult *res;
1178 static const bool translate_columns[] = {false, false, true, false};
1179
1181
1182 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching default ACLs"));
1184 "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
1185 " n.nspname AS \"%s\",\n"
1186 " CASE d.defaclobjtype "
1187 " WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s'"
1188 " WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n"
1189 " ",
1190 gettext_noop("Owner"),
1191 gettext_noop("Schema"),
1193 gettext_noop("table"),
1195 gettext_noop("sequence"),
1197 gettext_noop("function"),
1199 gettext_noop("type"),
1201 gettext_noop("schema"),
1203 gettext_noop("large object"),
1204 gettext_noop("Type"));
1205
1206 printACLColumn(&buf, "d.defaclacl");
1207
1208 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
1209 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
1210
1211 if (!validateSQLNamePattern(&buf, pattern, false, false,
1212 NULL,
1213 "n.nspname",
1214 "pg_catalog.pg_get_userbyid(d.defaclrole)",
1215 NULL,
1216 NULL, 3))
1217 goto error_return;
1218
1219 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
1220
1221 res = PSQLexec(buf.data);
1222 if (!res)
1223 goto error_return;
1224
1225 printfPQExpBuffer(&buf, _("Default access privileges"));
1226 myopt.title = buf.data;
1227 myopt.translate_header = true;
1228 myopt.translate_columns = translate_columns;
1229 myopt.n_translate_columns = lengthof(translate_columns);
1230
1231 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1232
1234 PQclear(res);
1235 return true;
1236
1239 return false;
1240}
1241
1242
1243/*
1244 * Get object comments
1245 *
1246 * \dd [foo]
1247 *
1248 * Note: This command only lists comments for object types which do not have
1249 * their comments displayed by their own backslash commands. The following
1250 * types of objects will be displayed: constraint, operator class,
1251 * operator family, rule, and trigger.
1252 *
1253 */
1254bool
1255objectDescription(const char *pattern, bool showSystem)
1256{
1258 PGresult *res;
1260 static const bool translate_columns[] = {false, false, true, false};
1261
1263
1264 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching object comments"));
1266 "SELECT DISTINCT tt.nspname AS \"%s\", tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n"
1267 "FROM (\n",
1268 gettext_noop("Schema"),
1269 gettext_noop("Name"),
1270 gettext_noop("Object"),
1271 gettext_noop("Description"));
1272
1273 /* Table constraint descriptions */
1275 " SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
1276 " n.nspname as nspname,\n"
1277 " CAST(pgc.conname AS pg_catalog.text) as name,"
1278 " CAST('%s' AS pg_catalog.text) as object\n"
1279 " FROM pg_catalog.pg_constraint pgc\n"
1280 " JOIN pg_catalog.pg_class c "
1281 "ON c.oid = pgc.conrelid\n"
1282 " LEFT JOIN pg_catalog.pg_namespace n "
1283 " ON n.oid = c.relnamespace\n",
1284 gettext_noop("table constraint"));
1285
1286 if (!showSystem && !pattern)
1287 appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1288 " AND n.nspname <> 'information_schema'\n");
1289
1290 if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern,
1291 false, "n.nspname", "pgc.conname", NULL,
1292 "pg_catalog.pg_table_is_visible(c.oid)",
1293 NULL, 3))
1294 goto error_return;
1295
1296 /* Domain constraint descriptions */
1298 "UNION ALL\n"
1299 " SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
1300 " n.nspname as nspname,\n"
1301 " CAST(pgc.conname AS pg_catalog.text) as name,"
1302 " CAST('%s' AS pg_catalog.text) as object\n"
1303 " FROM pg_catalog.pg_constraint pgc\n"
1304 " JOIN pg_catalog.pg_type t "
1305 "ON t.oid = pgc.contypid\n"
1306 " LEFT JOIN pg_catalog.pg_namespace n "
1307 " ON n.oid = t.typnamespace\n",
1308 gettext_noop("domain constraint"));
1309
1310 if (!showSystem && !pattern)
1311 appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1312 " AND n.nspname <> 'information_schema'\n");
1313
1314 if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern,
1315 false, "n.nspname", "pgc.conname", NULL,
1316 "pg_catalog.pg_type_is_visible(t.oid)",
1317 NULL, 3))
1318 goto error_return;
1319
1320 /* Operator class descriptions */
1322 "UNION ALL\n"
1323 " SELECT o.oid as oid, o.tableoid as tableoid,\n"
1324 " n.nspname as nspname,\n"
1325 " CAST(o.opcname AS pg_catalog.text) as name,\n"
1326 " CAST('%s' AS pg_catalog.text) as object\n"
1327 " FROM pg_catalog.pg_opclass o\n"
1328 " JOIN pg_catalog.pg_am am ON "
1329 "o.opcmethod = am.oid\n"
1330 " JOIN pg_catalog.pg_namespace n ON "
1331 "n.oid = o.opcnamespace\n",
1332 gettext_noop("operator class"));
1333
1334 if (!showSystem && !pattern)
1335 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1336 " AND n.nspname <> 'information_schema'\n");
1337
1338 if (!validateSQLNamePattern(&buf, pattern, true, false,
1339 "n.nspname", "o.opcname", NULL,
1340 "pg_catalog.pg_opclass_is_visible(o.oid)",
1341 NULL, 3))
1342 goto error_return;
1343
1344 /* Operator family descriptions */
1346 "UNION ALL\n"
1347 " SELECT opf.oid as oid, opf.tableoid as tableoid,\n"
1348 " n.nspname as nspname,\n"
1349 " CAST(opf.opfname AS pg_catalog.text) AS name,\n"
1350 " CAST('%s' AS pg_catalog.text) as object\n"
1351 " FROM pg_catalog.pg_opfamily opf\n"
1352 " JOIN pg_catalog.pg_am am "
1353 "ON opf.opfmethod = am.oid\n"
1354 " JOIN pg_catalog.pg_namespace n "
1355 "ON opf.opfnamespace = n.oid\n",
1356 gettext_noop("operator family"));
1357
1358 if (!showSystem && !pattern)
1359 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1360 " AND n.nspname <> 'information_schema'\n");
1361
1362 if (!validateSQLNamePattern(&buf, pattern, true, false,
1363 "n.nspname", "opf.opfname", NULL,
1364 "pg_catalog.pg_opfamily_is_visible(opf.oid)",
1365 NULL, 3))
1366 goto error_return;
1367
1368 /* Rule descriptions (ignore rules for views) */
1370 "UNION ALL\n"
1371 " SELECT r.oid as oid, r.tableoid as tableoid,\n"
1372 " n.nspname as nspname,\n"
1373 " CAST(r.rulename AS pg_catalog.text) as name,"
1374 " CAST('%s' AS pg_catalog.text) as object\n"
1375 " FROM pg_catalog.pg_rewrite r\n"
1376 " JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n"
1377 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
1378 " WHERE r.rulename != '_RETURN'\n",
1379 gettext_noop("rule"));
1380
1381 if (!showSystem && !pattern)
1382 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1383 " AND n.nspname <> 'information_schema'\n");
1384
1385 if (!validateSQLNamePattern(&buf, pattern, true, false,
1386 "n.nspname", "r.rulename", NULL,
1387 "pg_catalog.pg_table_is_visible(c.oid)",
1388 NULL, 3))
1389 goto error_return;
1390
1391 /* Trigger descriptions */
1393 "UNION ALL\n"
1394 " SELECT t.oid as oid, t.tableoid as tableoid,\n"
1395 " n.nspname as nspname,\n"
1396 " CAST(t.tgname AS pg_catalog.text) as name,"
1397 " CAST('%s' AS pg_catalog.text) as object\n"
1398 " FROM pg_catalog.pg_trigger t\n"
1399 " JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n"
1400 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n",
1401 gettext_noop("trigger"));
1402
1403 if (!showSystem && !pattern)
1404 appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1405 " AND n.nspname <> 'information_schema'\n");
1406
1407 if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern, false,
1408 "n.nspname", "t.tgname", NULL,
1409 "pg_catalog.pg_table_is_visible(c.oid)",
1410 NULL, 3))
1411 goto error_return;
1412
1414 ") AS tt\n"
1415 " JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n");
1416
1417 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
1418
1419 res = PSQLexec(buf.data);
1421 if (!res)
1422 return false;
1423
1424 myopt.title = _("Object descriptions");
1425 myopt.translate_header = true;
1426 myopt.translate_columns = translate_columns;
1427 myopt.n_translate_columns = lengthof(translate_columns);
1428
1429 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1430
1431 PQclear(res);
1432 return true;
1433
1436 return false;
1437}
1438
1439
1440/*
1441 * describeTableDetails (for \d)
1442 *
1443 * This routine finds the tables to be displayed, and calls
1444 * describeOneTableDetails for each one.
1445 *
1446 * verbose: if true, this is \d+
1447 */
1448bool
1449describeTableDetails(const char *pattern, bool verbose, bool showSystem)
1450{
1452 PGresult *res;
1453 int i;
1454
1456
1457 printfPQExpBuffer(&buf, "/* %s */\n",
1458 _("Get matching relations to describe"));
1460 "SELECT c.oid,\n"
1461 " n.nspname,\n"
1462 " c.relname\n"
1463 "FROM pg_catalog.pg_class c\n"
1464 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
1465
1466 if (!showSystem && !pattern)
1467 appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1468 " AND n.nspname <> 'information_schema'\n");
1469
1470 if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern, false,
1471 "n.nspname", "c.relname", NULL,
1472 "pg_catalog.pg_table_is_visible(c.oid)",
1473 NULL, 3))
1474 {
1476 return false;
1477 }
1478
1479 appendPQExpBufferStr(&buf, "ORDER BY 2, 3;");
1480
1481 res = PSQLexec(buf.data);
1483 if (!res)
1484 return false;
1485
1486 if (PQntuples(res) == 0)
1487 {
1488 if (!pset.quiet)
1489 {
1490 if (pattern)
1491 pg_log_error("Did not find any relation named \"%s\".",
1492 pattern);
1493 else
1494 pg_log_error("Did not find any relations.");
1495 }
1496 PQclear(res);
1497 return false;
1498 }
1499
1500 for (i = 0; i < PQntuples(res); i++)
1501 {
1502 const char *oid;
1503 const char *nspname;
1504 const char *relname;
1505
1506 oid = PQgetvalue(res, i, 0);
1507 nspname = PQgetvalue(res, i, 1);
1508 relname = PQgetvalue(res, i, 2);
1509
1510 if (!describeOneTableDetails(nspname, relname, oid, verbose))
1511 {
1512 PQclear(res);
1513 return false;
1514 }
1515 if (cancel_pressed)
1516 {
1517 PQclear(res);
1518 return false;
1519 }
1520 }
1521
1522 PQclear(res);
1523 return true;
1524}
1525
1526/*
1527 * describeOneTableDetails (for \d)
1528 *
1529 * Unfortunately, the information presented here is so complicated that it
1530 * cannot be done in a single query. So we have to assemble the printed table
1531 * by hand and pass it to the underlying printTable() function.
1532 */
1533static bool
1534describeOneTableDetails(const char *schemaname,
1535 const char *relationname,
1536 const char *oid,
1537 bool verbose)
1538{
1539 bool retval = false;
1541 PGresult *res = NULL;
1544 bool printTableInitialized = false;
1545 int i;
1546 char *view_def = NULL;
1547 char *headers[12];
1548 PQExpBufferData title;
1550 int cols;
1551 int attname_col = -1, /* column indexes in "res" */
1552 atttype_col = -1,
1553 attrdef_col = -1,
1554 attnotnull_col = -1,
1555 attcoll_col = -1,
1556 attidentity_col = -1,
1557 attgenerated_col = -1,
1558 isindexkey_col = -1,
1559 indexdef_col = -1,
1560 fdwopts_col = -1,
1561 attstorage_col = -1,
1562 attcompression_col = -1,
1563 attstattarget_col = -1,
1564 attdescr_col = -1;
1565 int numrows;
1566 struct
1567 {
1568 int16 checks;
1569 char relkind;
1570 bool hasindex;
1571 bool hasrules;
1572 bool hastriggers;
1573 bool rowsecurity;
1574 bool forcerowsecurity;
1575 bool hasoids;
1576 bool ispartition;
1578 char *reloptions;
1579 char *reloftype;
1580 char relpersistence;
1581 char relreplident;
1582 char *relam;
1583 } tableinfo;
1584 bool show_column_details = false;
1585
1586 myopt.default_footer = false;
1587 /* This output looks confusing in expanded mode. */
1588 myopt.expanded = false;
1589
1591 initPQExpBuffer(&title);
1593
1594 /* Get general table info */
1595 printfPQExpBuffer(&buf, "/* %s */\n",
1596 _("Get general information about one relation"));
1597 if (pset.sversion >= 120000)
1598 {
1600 "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1601 "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1602 "false AS relhasoids, c.relispartition, %s, c.reltablespace, "
1603 "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1604 "c.relpersistence, c.relreplident, am.amname\n"
1605 "FROM pg_catalog.pg_class c\n "
1606 "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1607 "LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid)\n"
1608 "WHERE c.oid = '%s';",
1609 (verbose ?
1610 "pg_catalog.array_to_string(c.reloptions || "
1611 "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1612 : "''"),
1613 oid);
1614 }
1615 else
1616 {
1618 "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1619 "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1620 "c.relhasoids, c.relispartition, %s, c.reltablespace, "
1621 "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1622 "c.relpersistence, c.relreplident\n"
1623 "FROM pg_catalog.pg_class c\n "
1624 "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1625 "WHERE c.oid = '%s';",
1626 (verbose ?
1627 "pg_catalog.array_to_string(c.reloptions || "
1628 "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1629 : "''"),
1630 oid);
1631 }
1632
1633 res = PSQLexec(buf.data);
1634 if (!res)
1635 goto error_return;
1636
1637 /* Did we get anything? */
1638 if (PQntuples(res) == 0)
1639 {
1640 if (!pset.quiet)
1641 pg_log_error("Did not find any relation with OID %s.", oid);
1642 goto error_return;
1643 }
1644
1645 tableinfo.checks = atoi(PQgetvalue(res, 0, 0));
1646 tableinfo.relkind = *(PQgetvalue(res, 0, 1));
1647 tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
1648 tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
1649 tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
1650 tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
1651 tableinfo.forcerowsecurity = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
1652 tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 7), "t") == 0;
1653 tableinfo.ispartition = strcmp(PQgetvalue(res, 0, 8), "t") == 0;
1654 tableinfo.reloptions = pg_strdup(PQgetvalue(res, 0, 9));
1655 tableinfo.tablespace = atooid(PQgetvalue(res, 0, 10));
1656 tableinfo.reloftype = (strcmp(PQgetvalue(res, 0, 11), "") != 0) ?
1657 pg_strdup(PQgetvalue(res, 0, 11)) : NULL;
1658 tableinfo.relpersistence = *(PQgetvalue(res, 0, 12));
1659 tableinfo.relreplident = *(PQgetvalue(res, 0, 13));
1660 if (pset.sversion >= 120000)
1661 tableinfo.relam = PQgetisnull(res, 0, 14) ?
1662 NULL : pg_strdup(PQgetvalue(res, 0, 14));
1663 else
1664 tableinfo.relam = NULL;
1665 PQclear(res);
1666 res = NULL;
1667
1668 /*
1669 * If it's a sequence, deal with it here separately.
1670 */
1671 if (tableinfo.relkind == RELKIND_SEQUENCE)
1672 {
1673 PGresult *result = NULL;
1675 char *footers[3] = {NULL, NULL, NULL};
1676
1677 printfPQExpBuffer(&buf, "/* %s */\n", _("Get sequence information"));
1679 "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
1680 " seqstart AS \"%s\",\n"
1681 " seqmin AS \"%s\",\n"
1682 " seqmax AS \"%s\",\n"
1683 " seqincrement AS \"%s\",\n"
1684 " CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
1685 " seqcache AS \"%s\"\n",
1686 gettext_noop("Type"),
1687 gettext_noop("Start"),
1688 gettext_noop("Minimum"),
1689 gettext_noop("Maximum"),
1690 gettext_noop("Increment"),
1691 gettext_noop("yes"),
1692 gettext_noop("no"),
1693 gettext_noop("Cycles?"),
1694 gettext_noop("Cache"));
1696 "FROM pg_catalog.pg_sequence\n"
1697 "WHERE seqrelid = '%s';",
1698 oid);
1699
1700 res = PSQLexec(buf.data);
1701 if (!res)
1702 goto error_return;
1703
1704 /* Get the column that owns this sequence */
1705 printfPQExpBuffer(&buf, "/* %s */\n",
1706 _("Get the column that owns this sequence"));
1707 appendPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
1708 "\n pg_catalog.quote_ident(relname) || '.' ||"
1709 "\n pg_catalog.quote_ident(attname),"
1710 "\n d.deptype"
1711 "\nFROM pg_catalog.pg_class c"
1712 "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
1713 "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
1714 "\nINNER JOIN pg_catalog.pg_attribute a ON ("
1715 "\n a.attrelid=c.oid AND"
1716 "\n a.attnum=d.refobjsubid)"
1717 "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
1718 "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
1719 "\n AND d.objid='%s'"
1720 "\n AND d.deptype IN ('a', 'i')",
1721 oid);
1722
1723 result = PSQLexec(buf.data);
1724
1725 /*
1726 * If we get no rows back, don't show anything (obviously). We should
1727 * never get more than one row back, but if we do, just ignore it and
1728 * don't print anything.
1729 */
1730 if (!result)
1731 goto error_return;
1732 else if (PQntuples(result) == 1)
1733 {
1734 switch (PQgetvalue(result, 0, 1)[0])
1735 {
1736 case 'a':
1737 footers[0] = psprintf(_("Owned by: %s"),
1738 PQgetvalue(result, 0, 0));
1739 break;
1740 case 'i':
1741 footers[0] = psprintf(_("Sequence for identity column: %s"),
1742 PQgetvalue(result, 0, 0));
1743 break;
1744 }
1745 }
1746 PQclear(result);
1747
1748 /* Print any publications */
1749 if (pset.sversion >= 190000)
1750 {
1751 printfPQExpBuffer(&buf, "/* %s */\n",
1752 _("Get publications containing this sequence"));
1753 appendPQExpBuffer(&buf, "SELECT pubname FROM pg_catalog.pg_publication p"
1754 "\nWHERE p.puballsequences"
1755 "\n AND pg_catalog.pg_relation_is_publishable('%s')"
1756 "\nORDER BY 1",
1757 oid);
1758
1759 result = PSQLexec(buf.data);
1760 if (result)
1761 {
1762 int nrows = PQntuples(result);
1763
1764 if (nrows > 0)
1765 {
1766 printfPQExpBuffer(&tmpbuf, _("Included in publications:"));
1767 for (i = 0; i < nrows; i++)
1768 appendPQExpBuffer(&tmpbuf, "\n \"%s\"", PQgetvalue(result, i, 0));
1769
1770 /* Store in the first available footer slot */
1771 if (footers[0] == NULL)
1772 footers[0] = pg_strdup(tmpbuf.data);
1773 else
1774 footers[1] = pg_strdup(tmpbuf.data);
1775
1777 }
1778
1779 PQclear(result);
1780 }
1781 }
1782
1783 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
1784 printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
1785 schemaname, relationname);
1786 else
1787 printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
1788 schemaname, relationname);
1789
1790 myopt.footers = footers;
1791 myopt.topt.default_footer = false;
1792 myopt.title = title.data;
1793 myopt.translate_header = true;
1794
1795 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1796
1797 pg_free(footers[0]);
1798 pg_free(footers[1]);
1799
1800 retval = true;
1801 goto error_return; /* not an error, just return early */
1802 }
1803
1804 /*
1805 * If it's a property graph, deal with it here separately.
1806 */
1807 if (tableinfo.relkind == RELKIND_PROPGRAPH)
1808 {
1809 printQueryOpt popt = pset.popt;
1810 char *footers[3] = {NULL, NULL, NULL};
1811
1812 printfPQExpBuffer(&buf, "/* %s */\n", _("Get property graph information"));
1814 "SELECT e.pgealias AS \"%s\","
1815 "\n pg_catalog.quote_ident(n.nspname) || '.' ||"
1816 "\n pg_catalog.quote_ident(c.relname) AS \"%s\","
1817 "\n case e.pgekind when " CppAsString2(PGEKIND_VERTEX) " then 'vertex'"
1818 "\n when " CppAsString2(PGEKIND_EDGE) " then 'edge' end AS \"%s\","
1819 "\n s.pgealias as \"%s\","
1820 "\n d.pgealias as \"%s\""
1821 "\n FROM pg_catalog.pg_propgraph_element e"
1822 "\n INNER JOIN pg_catalog.pg_class c ON c.oid = e.pgerelid"
1823 "\n INNER JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid"
1824 "\n LEFT JOIN pg_catalog.pg_propgraph_element s ON e.pgesrcvertexid = s.oid"
1825 "\n LEFT JOIN pg_catalog.pg_propgraph_element d ON e.pgedestvertexid = d.oid"
1826 "\n WHERE e.pgepgid = '%s'"
1827 "\n ORDER BY e.pgealias",
1828 gettext_noop("Element Alias"),
1829 gettext_noop("Element Table"),
1830 gettext_noop("Element Kind"),
1831 gettext_noop("Source Vertex Alias"),
1832 gettext_noop("Destination Vertex Alias"),
1833 oid);
1834
1835 res = PSQLexec(buf.data);
1836 if (!res)
1837 goto error_return;
1838
1839 printfPQExpBuffer(&title, _("Property Graph \"%s.%s\""),
1840 schemaname, relationname);
1841
1842 /* Add property graph definition in verbose mode */
1843 if (verbose)
1844 {
1846
1847 printfPQExpBuffer(&buf, "/* %s */\n", _("Get property graph definition"));
1849 "SELECT pg_catalog.pg_get_propgraphdef('%s'::pg_catalog.oid);",
1850 oid);
1851 result = PSQLexec(buf.data);
1852
1853 if (result)
1854 {
1855 if (PQntuples(result) > 0)
1856 {
1857 footers[0] = pg_strdup(_("Property graph definition:"));
1858 footers[1] = pg_strdup(PQgetvalue(result, 0, 0));
1859 }
1860 PQclear(result);
1861 }
1862 }
1863
1864 popt.footers = footers;
1865 popt.topt.default_footer = false;
1866 popt.title = title.data;
1867 popt.translate_header = true;
1868
1869 printQuery(res, &popt, pset.queryFout, false, pset.logfile);
1870
1871 pg_free(footers[0]);
1872 pg_free(footers[1]);
1873
1874 retval = true;
1875 goto error_return; /* not an error, just return early */
1876 }
1877
1878 /* Identify whether we should print collation, nullable, default vals */
1879 if (tableinfo.relkind == RELKIND_RELATION ||
1880 tableinfo.relkind == RELKIND_VIEW ||
1881 tableinfo.relkind == RELKIND_MATVIEW ||
1882 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1883 tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
1885 show_column_details = true;
1886
1887 /*
1888 * Get per-column info
1889 *
1890 * Since the set of query columns we need varies depending on relkind and
1891 * server version, we compute all the column numbers on-the-fly. Column
1892 * number variables for columns not fetched are left as -1; this avoids
1893 * duplicative test logic below.
1894 */
1895 cols = 0;
1896 printfPQExpBuffer(&buf, "/* %s */\n",
1897 _("Get per-column information for one relation"));
1898 appendPQExpBufferStr(&buf, "SELECT a.attname");
1899 attname_col = cols++;
1900 appendPQExpBufferStr(&buf, ",\n pg_catalog.format_type(a.atttypid, a.atttypmod)");
1901 atttype_col = cols++;
1902
1904 {
1905 /* use "pretty" mode for expression to avoid excessive parentheses */
1907 ",\n (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)"
1908 "\n FROM pg_catalog.pg_attrdef d"
1909 "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)"
1910 ",\n a.attnotnull");
1911 attrdef_col = cols++;
1912 attnotnull_col = cols++;
1913 appendPQExpBufferStr(&buf, ",\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
1914 " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
1915 attcoll_col = cols++;
1916 appendPQExpBufferStr(&buf, ",\n a.attidentity");
1917 attidentity_col = cols++;
1918 if (pset.sversion >= 120000)
1919 appendPQExpBufferStr(&buf, ",\n a.attgenerated");
1920 else
1921 appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attgenerated");
1922 attgenerated_col = cols++;
1923 }
1924 if (tableinfo.relkind == RELKIND_INDEX ||
1926 {
1927 if (pset.sversion >= 110000)
1928 {
1929 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",
1930 oid,
1931 gettext_noop("yes"),
1932 gettext_noop("no"));
1933 isindexkey_col = cols++;
1934 }
1935 appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
1936 indexdef_col = cols++;
1937 }
1938 /* FDW options for foreign table column */
1939 if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
1940 {
1941 appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE "
1942 " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM "
1943 " pg_catalog.pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions");
1944 fdwopts_col = cols++;
1945 }
1946 if (verbose)
1947 {
1948 appendPQExpBufferStr(&buf, ",\n a.attstorage");
1949 attstorage_col = cols++;
1950
1951 /* compression info, if relevant to relkind */
1952 if (pset.sversion >= 140000 &&
1954 (tableinfo.relkind == RELKIND_RELATION ||
1956 tableinfo.relkind == RELKIND_MATVIEW))
1957 {
1958 appendPQExpBufferStr(&buf, ",\n a.attcompression AS attcompression");
1959 attcompression_col = cols++;
1960 }
1961
1962 /* stats target, if relevant to relkind */
1963 if (tableinfo.relkind == RELKIND_RELATION ||
1964 tableinfo.relkind == RELKIND_INDEX ||
1966 tableinfo.relkind == RELKIND_MATVIEW ||
1967 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1969 {
1970 appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
1971 attstattarget_col = cols++;
1972 }
1973
1974 /*
1975 * In 9.0+, we have column comments for: relations, views, composite
1976 * types, and foreign tables (cf. CommentObject() in comment.c).
1977 */
1978 if (tableinfo.relkind == RELKIND_RELATION ||
1979 tableinfo.relkind == RELKIND_VIEW ||
1980 tableinfo.relkind == RELKIND_MATVIEW ||
1981 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1982 tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
1984 {
1985 appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)");
1986 attdescr_col = cols++;
1987 }
1988 }
1989
1990 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
1991 appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
1992 appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
1993
1994 res = PSQLexec(buf.data);
1995 if (!res)
1996 goto error_return;
1997 numrows = PQntuples(res);
1998
1999 /* Make title */
2000 switch (tableinfo.relkind)
2001 {
2002 case RELKIND_RELATION:
2003 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2004 printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
2005 schemaname, relationname);
2006 else
2007 printfPQExpBuffer(&title, _("Table \"%s.%s\""),
2008 schemaname, relationname);
2009 break;
2010 case RELKIND_VIEW:
2011 printfPQExpBuffer(&title, _("View \"%s.%s\""),
2012 schemaname, relationname);
2013 break;
2014 case RELKIND_MATVIEW:
2015 printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""),
2016 schemaname, relationname);
2017 break;
2018 case RELKIND_INDEX:
2019 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2020 printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
2021 schemaname, relationname);
2022 else
2023 printfPQExpBuffer(&title, _("Index \"%s.%s\""),
2024 schemaname, relationname);
2025 break;
2027 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2028 printfPQExpBuffer(&title, _("Unlogged partitioned index \"%s.%s\""),
2029 schemaname, relationname);
2030 else
2031 printfPQExpBuffer(&title, _("Partitioned index \"%s.%s\""),
2032 schemaname, relationname);
2033 break;
2034 case RELKIND_TOASTVALUE:
2035 printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
2036 schemaname, relationname);
2037 break;
2039 printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
2040 schemaname, relationname);
2041 break;
2043 printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
2044 schemaname, relationname);
2045 break;
2047 if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2048 printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""),
2049 schemaname, relationname);
2050 else
2051 printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""),
2052 schemaname, relationname);
2053 break;
2054 default:
2055 /* untranslated unknown relkind */
2056 printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
2057 tableinfo.relkind, schemaname, relationname);
2058 break;
2059 }
2060
2061 /* Fill headers[] with the names of the columns we will output */
2062 cols = 0;
2063 headers[cols++] = gettext_noop("Column");
2064 headers[cols++] = gettext_noop("Type");
2066 {
2067 headers[cols++] = gettext_noop("Collation");
2068 headers[cols++] = gettext_noop("Nullable");
2069 headers[cols++] = gettext_noop("Default");
2070 }
2071 if (isindexkey_col >= 0)
2072 headers[cols++] = gettext_noop("Key?");
2073 if (indexdef_col >= 0)
2074 headers[cols++] = gettext_noop("Definition");
2075 if (fdwopts_col >= 0)
2076 headers[cols++] = gettext_noop("FDW options");
2077 if (attstorage_col >= 0)
2078 headers[cols++] = gettext_noop("Storage");
2079 if (attcompression_col >= 0)
2080 headers[cols++] = gettext_noop("Compression");
2081 if (attstattarget_col >= 0)
2082 headers[cols++] = gettext_noop("Stats target");
2083 if (attdescr_col >= 0)
2084 headers[cols++] = gettext_noop("Description");
2085
2086 Assert(cols <= lengthof(headers));
2087
2088 printTableInit(&cont, &myopt, title.data, cols, numrows);
2089 printTableInitialized = true;
2090
2091 for (i = 0; i < cols; i++)
2092 printTableAddHeader(&cont, headers[i], true, 'l');
2093
2094 /* Generate table cells to be printed */
2095 for (i = 0; i < numrows; i++)
2096 {
2097 /* Column */
2098 printTableAddCell(&cont, PQgetvalue(res, i, attname_col), false, false);
2099
2100 /* Type */
2101 printTableAddCell(&cont, PQgetvalue(res, i, atttype_col), false, false);
2102
2103 /* Collation, Nullable, Default */
2105 {
2106 char *identity;
2107 char *generated;
2108 char *default_str;
2109 bool mustfree = false;
2110
2111 printTableAddCell(&cont, PQgetvalue(res, i, attcoll_col), false, false);
2112
2114 strcmp(PQgetvalue(res, i, attnotnull_col), "t") == 0 ? "not null" : "",
2115 false, false);
2116
2117 identity = PQgetvalue(res, i, attidentity_col);
2118 generated = PQgetvalue(res, i, attgenerated_col);
2119
2120 if (identity[0] == ATTRIBUTE_IDENTITY_ALWAYS)
2121 default_str = "generated always as identity";
2122 else if (identity[0] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
2123 default_str = "generated by default as identity";
2124 else if (generated[0] == ATTRIBUTE_GENERATED_STORED)
2125 {
2126 default_str = psprintf("generated always as (%s) stored",
2127 PQgetvalue(res, i, attrdef_col));
2128 mustfree = true;
2129 }
2130 else if (generated[0] == ATTRIBUTE_GENERATED_VIRTUAL)
2131 {
2132 default_str = psprintf("generated always as (%s)",
2133 PQgetvalue(res, i, attrdef_col));
2134 mustfree = true;
2135 }
2136 else
2138
2140 }
2141
2142 /* Info for index columns */
2143 if (isindexkey_col >= 0)
2144 printTableAddCell(&cont, PQgetvalue(res, i, isindexkey_col), true, false);
2145 if (indexdef_col >= 0)
2146 printTableAddCell(&cont, PQgetvalue(res, i, indexdef_col), false, false);
2147
2148 /* FDW options for foreign table columns */
2149 if (fdwopts_col >= 0)
2150 printTableAddCell(&cont, PQgetvalue(res, i, fdwopts_col), false, false);
2151
2152 /* Storage mode, if relevant */
2153 if (attstorage_col >= 0)
2154 {
2155 char *storage = PQgetvalue(res, i, attstorage_col);
2156
2157 /* these strings are literal in our syntax, so not translated. */
2158 printTableAddCell(&cont, (storage[0] == TYPSTORAGE_PLAIN ? "plain" :
2159 (storage[0] == TYPSTORAGE_MAIN ? "main" :
2160 (storage[0] == TYPSTORAGE_EXTENDED ? "extended" :
2161 (storage[0] == TYPSTORAGE_EXTERNAL ? "external" :
2162 "???")))),
2163 false, false);
2164 }
2165
2166 /* Column compression, if relevant */
2167 if (attcompression_col >= 0)
2168 {
2169 char *compression = PQgetvalue(res, i, attcompression_col);
2170
2171 /* these strings are literal in our syntax, so not translated. */
2172 printTableAddCell(&cont, (compression[0] == 'p' ? "pglz" :
2173 (compression[0] == 'l' ? "lz4" :
2174 (compression[0] == '\0' ? "" :
2175 "???"))),
2176 false, false);
2177 }
2178
2179 /* Statistics target, if the relkind supports this feature */
2180 if (attstattarget_col >= 0)
2182 false, false);
2183
2184 /* Column comments, if the relkind supports this feature */
2185 if (attdescr_col >= 0)
2187 false, false);
2188 }
2189
2190 /* Make footers */
2191
2192 if (tableinfo.ispartition)
2193 {
2194 /* Footer information for a partition child table */
2196
2197 printfPQExpBuffer(&buf, "/* %s */\n",
2198 _("Get partitioning information for this partition"));
2200 "SELECT inhparent::pg_catalog.regclass,\n"
2201 " pg_catalog.pg_get_expr(c.relpartbound, c.oid),\n ");
2202
2204 pset.sversion >= 140000 ? "inhdetachpending" :
2205 "false as inhdetachpending");
2206
2207 /* If verbose, also request the partition constraint definition */
2208 if (verbose)
2210 ",\n pg_catalog.pg_get_partition_constraintdef(c.oid)");
2212 "\nFROM pg_catalog.pg_class c"
2213 " JOIN pg_catalog.pg_inherits i"
2214 " ON c.oid = inhrelid"
2215 "\nWHERE c.oid = '%s';", oid);
2216 result = PSQLexec(buf.data);
2217 if (!result)
2218 goto error_return;
2219
2220 if (PQntuples(result) > 0)
2221 {
2222 char *parent_name = PQgetvalue(result, 0, 0);
2223 char *partdef = PQgetvalue(result, 0, 1);
2224 char *detached = PQgetvalue(result, 0, 2);
2225
2226 printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s%s"), parent_name,
2227 partdef,
2228 strcmp(detached, "t") == 0 ? " DETACH PENDING" : "");
2230
2231 if (verbose)
2232 {
2233 char *partconstraintdef = NULL;
2234
2235 if (!PQgetisnull(result, 0, 3))
2237 /* If there isn't any constraint, show that explicitly */
2238 if (partconstraintdef == NULL || partconstraintdef[0] == '\0')
2239 printfPQExpBuffer(&tmpbuf, _("No partition constraint"));
2240 else
2241 printfPQExpBuffer(&tmpbuf, _("Partition constraint: %s"),
2244 }
2245 }
2246 PQclear(result);
2247 }
2248
2249 if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2250 {
2251 /* Footer information for a partitioned table (partitioning parent) */
2253
2254 printfPQExpBuffer(&buf, "/* %s */\n",
2255 _("Get partitioning information for this table"));
2257 "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);",
2258 oid);
2259 result = PSQLexec(buf.data);
2260 if (!result)
2261 goto error_return;
2262
2263 if (PQntuples(result) == 1)
2264 {
2265 char *partkeydef = PQgetvalue(result, 0, 0);
2266
2267 printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef);
2269 }
2270 PQclear(result);
2271 }
2272
2273 if (tableinfo.relkind == RELKIND_TOASTVALUE)
2274 {
2275 /* For a TOAST table, print name of owning table */
2277
2278 printfPQExpBuffer(&buf, "/* %s */\n",
2279 _("Get the table that owns this TOAST table"));
2281 "SELECT n.nspname, c.relname\n"
2282 "FROM pg_catalog.pg_class c"
2283 " JOIN pg_catalog.pg_namespace n"
2284 " ON n.oid = c.relnamespace\n"
2285 "WHERE reltoastrelid = '%s';", oid);
2286 result = PSQLexec(buf.data);
2287 if (!result)
2288 goto error_return;
2289
2290 if (PQntuples(result) == 1)
2291 {
2292 char *schemaname = PQgetvalue(result, 0, 0);
2293 char *relname = PQgetvalue(result, 0, 1);
2294
2295 printfPQExpBuffer(&tmpbuf, _("Owning table: \"%s.%s\""),
2296 schemaname, relname);
2298 }
2299 PQclear(result);
2300 }
2301
2302 if (tableinfo.relkind == RELKIND_INDEX ||
2304 {
2305 /* Footer information about an index */
2307
2308 printfPQExpBuffer(&buf, "/* %s */\n", _("Get index details"));
2310 "SELECT i.indisunique, i.indisprimary, i.indisclustered, "
2311 "i.indisvalid,\n"
2312 " (NOT i.indimmediate) AND "
2313 "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2314 "WHERE conrelid = i.indrelid AND "
2315 "conindid = i.indexrelid AND "
2316 "contype IN (" CppAsString2(CONSTRAINT_PRIMARY) ","
2319 "condeferrable) AS condeferrable,\n"
2320 " (NOT i.indimmediate) AND "
2321 "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2322 "WHERE conrelid = i.indrelid AND "
2323 "conindid = i.indexrelid AND "
2324 "contype IN (" CppAsString2(CONSTRAINT_PRIMARY) ","
2327 "condeferred) AS condeferred,\n");
2328
2329 appendPQExpBufferStr(&buf, "i.indisreplident,\n");
2330
2331 if (pset.sversion >= 150000)
2332 appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
2333 else
2334 appendPQExpBufferStr(&buf, "false AS indnullsnotdistinct,\n");
2335
2336 appendPQExpBuffer(&buf, " a.amname, c2.relname, "
2337 "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
2338 "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
2339 "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
2340 "AND i.indrelid = c2.oid;",
2341 oid);
2342
2343 result = PSQLexec(buf.data);
2344 if (!result)
2345 goto error_return;
2346 else if (PQntuples(result) != 1)
2347 {
2348 PQclear(result);
2349 goto error_return;
2350 }
2351 else
2352 {
2353 char *indisunique = PQgetvalue(result, 0, 0);
2354 char *indisprimary = PQgetvalue(result, 0, 1);
2355 char *indisclustered = PQgetvalue(result, 0, 2);
2356 char *indisvalid = PQgetvalue(result, 0, 3);
2357 char *deferrable = PQgetvalue(result, 0, 4);
2358 char *deferred = PQgetvalue(result, 0, 5);
2359 char *indisreplident = PQgetvalue(result, 0, 6);
2360 char *indnullsnotdistinct = PQgetvalue(result, 0, 7);
2361 char *indamname = PQgetvalue(result, 0, 8);
2362 char *indtable = PQgetvalue(result, 0, 9);
2363 char *indpred = PQgetvalue(result, 0, 10);
2364
2365 if (strcmp(indisprimary, "t") == 0)
2366 printfPQExpBuffer(&tmpbuf, _("primary key, "));
2367 else if (strcmp(indisunique, "t") == 0)
2368 {
2369 if (strcmp(indnullsnotdistinct, "t") == 0)
2370 printfPQExpBuffer(&tmpbuf, _("unique nulls not distinct, "));
2371 else
2372 printfPQExpBuffer(&tmpbuf, _("unique, "));
2373 }
2374 else
2376
2377 /* we assume here that index and table are in same schema */
2378 /*- translator: the first %s is an index AM name (eg. btree) */
2379 appendPQExpBuffer(&tmpbuf, _("%s, for table \"%s.%s\""),
2380 indamname, schemaname, indtable);
2381
2382 if (strlen(indpred))
2383 appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
2384
2385 if (strcmp(indisclustered, "t") == 0)
2386 appendPQExpBufferStr(&tmpbuf, _(", clustered"));
2387
2388 if (strcmp(indisvalid, "t") != 0)
2389 appendPQExpBufferStr(&tmpbuf, _(", invalid"));
2390
2391 if (strcmp(deferrable, "t") == 0)
2392 appendPQExpBufferStr(&tmpbuf, _(", deferrable"));
2393
2394 if (strcmp(deferred, "t") == 0)
2395 appendPQExpBufferStr(&tmpbuf, _(", initially deferred"));
2396
2397 if (strcmp(indisreplident, "t") == 0)
2398 appendPQExpBufferStr(&tmpbuf, _(", replica identity"));
2399
2401
2402 /*
2403 * If it's a partitioned index, we'll print the tablespace below
2404 */
2405 if (tableinfo.relkind == RELKIND_INDEX)
2407 tableinfo.tablespace, true);
2408 }
2409
2410 PQclear(result);
2411 }
2412 /* If you add relkinds here, see also "Finish printing..." stanza below */
2413 else if (tableinfo.relkind == RELKIND_RELATION ||
2414 tableinfo.relkind == RELKIND_MATVIEW ||
2415 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2418 tableinfo.relkind == RELKIND_TOASTVALUE)
2419 {
2420 /* Footer information about a table */
2421 PGresult *result = NULL;
2422 int tuples = 0;
2423
2424 /* print indexes */
2425 if (tableinfo.hasindex)
2426 {
2427 printfPQExpBuffer(&buf, "/* %s */\n", _("Get indexes for this table"));
2429 "SELECT c2.relname, i.indisprimary, i.indisunique, "
2430 "i.indisclustered, i.indisvalid, "
2431 "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n "
2432 "pg_catalog.pg_get_constraintdef(con.oid, true), "
2433 "contype, condeferrable, condeferred");
2434 appendPQExpBufferStr(&buf, ", i.indisreplident");
2435 appendPQExpBufferStr(&buf, ", c2.reltablespace");
2436 if (pset.sversion >= 180000)
2437 appendPQExpBufferStr(&buf, ", con.conperiod");
2438 else
2439 appendPQExpBufferStr(&buf, ", false AS conperiod");
2441 "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
2442 " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ("
2446 "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
2447 "ORDER BY i.indisprimary DESC, c2.relname;",
2448 oid);
2449 result = PSQLexec(buf.data);
2450 if (!result)
2451 goto error_return;
2452 else
2453 tuples = PQntuples(result);
2454
2455 if (tuples > 0)
2456 {
2457 printTableAddFooter(&cont, _("Indexes:"));
2458 for (i = 0; i < tuples; i++)
2459 {
2460 /* untranslated index name */
2461 printfPQExpBuffer(&buf, " \"%s\"",
2462 PQgetvalue(result, i, 0));
2463
2464 /*
2465 * If exclusion constraint or PK/UNIQUE constraint WITHOUT
2466 * OVERLAPS, print the constraintdef
2467 */
2468 if (strcmp(PQgetvalue(result, i, 7), "x") == 0 ||
2469 strcmp(PQgetvalue(result, i, 12), "t") == 0)
2470 {
2471 appendPQExpBuffer(&buf, " %s",
2472 PQgetvalue(result, i, 6));
2473 }
2474 else
2475 {
2476 const char *indexdef;
2477 const char *usingpos;
2478
2479 /* Label as primary key or unique (but not both) */
2480 if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
2481 appendPQExpBufferStr(&buf, " PRIMARY KEY,");
2482 else if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
2483 {
2484 if (strcmp(PQgetvalue(result, i, 7), "u") == 0)
2485 appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,");
2486 else
2487 appendPQExpBufferStr(&buf, " UNIQUE,");
2488 }
2489
2490 /* Everything after "USING" is echoed verbatim */
2491 indexdef = PQgetvalue(result, i, 5);
2492 usingpos = strstr(indexdef, " USING ");
2493 if (usingpos)
2494 indexdef = usingpos + 7;
2495 appendPQExpBuffer(&buf, " %s", indexdef);
2496
2497 /* Need these for deferrable PK/UNIQUE indexes */
2498 if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
2499 appendPQExpBufferStr(&buf, " DEFERRABLE");
2500
2501 if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
2502 appendPQExpBufferStr(&buf, " INITIALLY DEFERRED");
2503 }
2504
2505 /* Add these for all cases */
2506 if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
2507 appendPQExpBufferStr(&buf, " CLUSTER");
2508
2509 if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
2510 appendPQExpBufferStr(&buf, " INVALID");
2511
2512 if (strcmp(PQgetvalue(result, i, 10), "t") == 0)
2513 appendPQExpBufferStr(&buf, " REPLICA IDENTITY");
2514
2516
2517 /* Print tablespace of the index on the same line */
2519 atooid(PQgetvalue(result, i, 11)),
2520 false);
2521 }
2522 }
2523 PQclear(result);
2524 }
2525
2526 /* print table (and column) check constraints */
2527 if (tableinfo.checks)
2528 {
2529 printfPQExpBuffer(&buf, "/* %s */\n",
2530 _("Get check constraints for this table"));
2532 "SELECT r.conname, "
2533 "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
2534 "FROM pg_catalog.pg_constraint r\n"
2535 "WHERE r.conrelid = '%s' "
2536 "AND r.contype = " CppAsString2(CONSTRAINT_CHECK) "\n"
2537 "ORDER BY 1;",
2538 oid);
2539 result = PSQLexec(buf.data);
2540 if (!result)
2541 goto error_return;
2542 else
2543 tuples = PQntuples(result);
2544
2545 if (tuples > 0)
2546 {
2547 printTableAddFooter(&cont, _("Check constraints:"));
2548 for (i = 0; i < tuples; i++)
2549 {
2550 /* untranslated constraint name and def */
2551 printfPQExpBuffer(&buf, " \"%s\" %s",
2552 PQgetvalue(result, i, 0),
2553 PQgetvalue(result, i, 1));
2554
2556 }
2557 }
2558 PQclear(result);
2559 }
2560
2561 /* Print foreign-key constraints */
2562 printfPQExpBuffer(&buf, "/* %s */\n",
2563 _("Get foreign key constraints for this table"));
2564 if (pset.sversion >= 120000 &&
2565 (tableinfo.ispartition || tableinfo.relkind == RELKIND_PARTITIONED_TABLE))
2566 {
2567 /*
2568 * Put the constraints defined in this table first, followed by
2569 * the constraints defined in ancestor partitioned tables.
2570 */
2572 "SELECT conrelid = '%s'::pg_catalog.regclass AS sametable,\n"
2573 " conname,\n"
2574 " pg_catalog.pg_get_constraintdef(oid, true) AS condef,\n"
2575 " conrelid::pg_catalog.regclass AS ontable\n"
2576 " FROM pg_catalog.pg_constraint,\n"
2577 " pg_catalog.pg_partition_ancestors('%s')\n"
2578 " WHERE conrelid = relid AND contype = " CppAsString2(CONSTRAINT_FOREIGN) " AND conparentid = 0\n"
2579 "ORDER BY sametable DESC, conname;",
2580 oid, oid);
2581 }
2582 else
2583 {
2585 "SELECT true as sametable, conname,\n"
2586 " pg_catalog.pg_get_constraintdef(r.oid, true) as condef,\n"
2587 " conrelid::pg_catalog.regclass AS ontable\n"
2588 "FROM pg_catalog.pg_constraint r\n"
2589 "WHERE r.conrelid = '%s' AND r.contype = " CppAsString2(CONSTRAINT_FOREIGN) "\n",
2590 oid);
2591
2592 if (pset.sversion >= 120000)
2593 appendPQExpBufferStr(&buf, " AND conparentid = 0\n");
2594 appendPQExpBufferStr(&buf, "ORDER BY conname");
2595 }
2596
2597 result = PSQLexec(buf.data);
2598 if (!result)
2599 goto error_return;
2600 else
2601 tuples = PQntuples(result);
2602
2603 if (tuples > 0)
2604 {
2605 int i_sametable = PQfnumber(result, "sametable"),
2606 i_conname = PQfnumber(result, "conname"),
2607 i_condef = PQfnumber(result, "condef"),
2608 i_ontable = PQfnumber(result, "ontable");
2609
2610 printTableAddFooter(&cont, _("Foreign-key constraints:"));
2611 for (i = 0; i < tuples; i++)
2612 {
2613 /*
2614 * Print untranslated constraint name and definition. Use a
2615 * "TABLE tab" prefix when the constraint is defined in a
2616 * parent partitioned table.
2617 */
2618 if (strcmp(PQgetvalue(result, i, i_sametable), "f") == 0)
2619 printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2623 else
2624 printfPQExpBuffer(&buf, " \"%s\" %s",
2627
2629 }
2630 }
2631 PQclear(result);
2632
2633 /* print incoming foreign-key references */
2634 printfPQExpBuffer(&buf, "/* %s */\n",
2635 _("Get foreign keys referencing this table"));
2636 if (pset.sversion >= 120000)
2637 {
2639 "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2640 " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2641 " FROM pg_catalog.pg_constraint c\n"
2642 " WHERE confrelid IN (SELECT pg_catalog.pg_partition_ancestors('%s')\n"
2643 " UNION ALL VALUES ('%s'::pg_catalog.regclass))\n"
2644 " AND contype = " CppAsString2(CONSTRAINT_FOREIGN) " AND conparentid = 0\n"
2645 "ORDER BY conname;",
2646 oid, oid);
2647 }
2648 else
2649 {
2651 "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2652 " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2653 " FROM pg_catalog.pg_constraint\n"
2654 " WHERE confrelid = %s AND contype = " CppAsString2(CONSTRAINT_FOREIGN) "\n"
2655 "ORDER BY conname;",
2656 oid);
2657 }
2658
2659 result = PSQLexec(buf.data);
2660 if (!result)
2661 goto error_return;
2662 else
2663 tuples = PQntuples(result);
2664
2665 if (tuples > 0)
2666 {
2667 int i_conname = PQfnumber(result, "conname"),
2668 i_ontable = PQfnumber(result, "ontable"),
2669 i_condef = PQfnumber(result, "condef");
2670
2671 printTableAddFooter(&cont, _("Referenced by:"));
2672 for (i = 0; i < tuples; i++)
2673 {
2674 printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2678
2680 }
2681 }
2682 PQclear(result);
2683
2684 /* print any row-level policies */
2685 printfPQExpBuffer(&buf, "/* %s */\n",
2686 _("Get row-level policies for this table"));
2687 appendPQExpBufferStr(&buf, "SELECT pol.polname,");
2689 " pol.polpermissive,\n");
2691 " 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"
2692 " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
2693 " pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
2694 " CASE pol.polcmd\n"
2695 " WHEN 'r' THEN 'SELECT'\n"
2696 " WHEN 'a' THEN 'INSERT'\n"
2697 " WHEN 'w' THEN 'UPDATE'\n"
2698 " WHEN 'd' THEN 'DELETE'\n"
2699 " END AS cmd\n"
2700 "FROM pg_catalog.pg_policy pol\n"
2701 "WHERE pol.polrelid = '%s' ORDER BY 1;",
2702 oid);
2703
2704 result = PSQLexec(buf.data);
2705 if (!result)
2706 goto error_return;
2707 else
2708 tuples = PQntuples(result);
2709
2710 /*
2711 * Handle cases where RLS is enabled and there are policies, or there
2712 * aren't policies, or RLS isn't enabled but there are policies
2713 */
2714 if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
2715 printTableAddFooter(&cont, _("Policies:"));
2716
2717 if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
2718 printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
2719
2720 if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
2721 printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
2722
2723 if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
2724 printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
2725
2726 if (!tableinfo.rowsecurity && tuples > 0)
2727 printTableAddFooter(&cont, _("Policies (row security disabled):"));
2728
2729 /* Might be an empty set - that's ok */
2730 for (i = 0; i < tuples; i++)
2731 {
2732 printfPQExpBuffer(&buf, " POLICY \"%s\"",
2733 PQgetvalue(result, i, 0));
2734
2735 if (*(PQgetvalue(result, i, 1)) == 'f')
2736 appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
2737
2738 if (!PQgetisnull(result, i, 5))
2739 appendPQExpBuffer(&buf, " FOR %s",
2740 PQgetvalue(result, i, 5));
2741
2742 if (!PQgetisnull(result, i, 2))
2743 {
2744 appendPQExpBuffer(&buf, "\n TO %s",
2745 PQgetvalue(result, i, 2));
2746 }
2747
2748 if (!PQgetisnull(result, i, 3))
2749 appendPQExpBuffer(&buf, "\n USING (%s)",
2750 PQgetvalue(result, i, 3));
2751
2752 if (!PQgetisnull(result, i, 4))
2753 appendPQExpBuffer(&buf, "\n WITH CHECK (%s)",
2754 PQgetvalue(result, i, 4));
2755
2757 }
2758 PQclear(result);
2759
2760 /* print any extended statistics */
2761 if (pset.sversion >= 140000)
2762 {
2763 printfPQExpBuffer(&buf, "/* %s */\n",
2764 _("Get extended statistics for this table"));
2766 "SELECT oid, "
2767 "stxrelid::pg_catalog.regclass, "
2768 "stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS nsp, "
2769 "stxname,\n"
2770 "pg_catalog.pg_get_statisticsobjdef_columns(oid) AS columns,\n"
2771 " " CppAsString2(STATS_EXT_NDISTINCT) " = any(stxkind) AS ndist_enabled,\n"
2772 " " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(stxkind) AS deps_enabled,\n"
2773 " " CppAsString2(STATS_EXT_MCV) " = any(stxkind) AS mcv_enabled,\n"
2774 "stxstattarget\n"
2775 "FROM pg_catalog.pg_statistic_ext\n"
2776 "WHERE stxrelid = '%s'\n"
2777 "ORDER BY nsp, stxname;",
2778 oid);
2779
2780 result = PSQLexec(buf.data);
2781 if (!result)
2782 goto error_return;
2783 else
2784 tuples = PQntuples(result);
2785
2786 if (tuples > 0)
2787 {
2788 printTableAddFooter(&cont, _("Statistics objects:"));
2789
2790 for (i = 0; i < tuples; i++)
2791 {
2792 bool gotone = false;
2793 bool has_ndistinct;
2794 bool has_dependencies;
2795 bool has_mcv;
2796 bool has_all;
2797 bool has_some;
2798
2799 has_ndistinct = (strcmp(PQgetvalue(result, i, 5), "t") == 0);
2800 has_dependencies = (strcmp(PQgetvalue(result, i, 6), "t") == 0);
2801 has_mcv = (strcmp(PQgetvalue(result, i, 7), "t") == 0);
2802
2803 printfPQExpBuffer(&buf, " ");
2804
2805 /* statistics object name (qualified with namespace) */
2806 appendPQExpBuffer(&buf, "\"%s.%s\"",
2807 PQgetvalue(result, i, 2),
2808 PQgetvalue(result, i, 3));
2809
2810 /*
2811 * When printing kinds we ignore expression statistics,
2812 * which are used only internally and can't be specified
2813 * by user. We don't print the kinds when none are
2814 * specified (in which case it has to be statistics on a
2815 * single expr) or when all are specified (in which case
2816 * we assume it's expanded by CREATE STATISTICS).
2817 */
2820
2821 if (has_some && !has_all)
2822 {
2823 appendPQExpBufferStr(&buf, " (");
2824
2825 /* options */
2826 if (has_ndistinct)
2827 {
2828 appendPQExpBufferStr(&buf, "ndistinct");
2829 gotone = true;
2830 }
2831
2832 if (has_dependencies)
2833 {
2834 appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2835 gotone = true;
2836 }
2837
2838 if (has_mcv)
2839 {
2840 appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2841 }
2842
2844 }
2845
2846 appendPQExpBuffer(&buf, " ON %s FROM %s",
2847 PQgetvalue(result, i, 4),
2848 PQgetvalue(result, i, 1));
2849
2850 /* Show the stats target if it's not default */
2851 if (!PQgetisnull(result, i, 8) &&
2852 strcmp(PQgetvalue(result, i, 8), "-1") != 0)
2853 appendPQExpBuffer(&buf, "; STATISTICS %s",
2854 PQgetvalue(result, i, 8));
2855
2857 }
2858 }
2859 PQclear(result);
2860 }
2861 else
2862 {
2863 printfPQExpBuffer(&buf, "/* %s */\n",
2864 _("Get extended statistics for this table"));
2866 "SELECT oid, "
2867 "stxrelid::pg_catalog.regclass, "
2868 "stxnamespace::pg_catalog.regnamespace AS nsp, "
2869 "stxname,\n"
2870 " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')\n"
2871 " FROM pg_catalog.unnest(stxkeys) s(attnum)\n"
2872 " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND\n"
2873 " a.attnum = s.attnum AND NOT attisdropped)) AS columns,\n"
2874 " " CppAsString2(STATS_EXT_NDISTINCT) " = any(stxkind) AS ndist_enabled,\n"
2875 " " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(stxkind) AS deps_enabled,\n"
2876 " " CppAsString2(STATS_EXT_MCV) " = any(stxkind) AS mcv_enabled,\n");
2877
2878 if (pset.sversion >= 130000)
2879 appendPQExpBufferStr(&buf, " stxstattarget\n");
2880 else
2881 appendPQExpBufferStr(&buf, " -1 AS stxstattarget\n");
2882 appendPQExpBuffer(&buf, "FROM pg_catalog.pg_statistic_ext\n"
2883 "WHERE stxrelid = '%s'\n"
2884 "ORDER BY 1;",
2885 oid);
2886
2887 result = PSQLexec(buf.data);
2888 if (!result)
2889 goto error_return;
2890 else
2891 tuples = PQntuples(result);
2892
2893 if (tuples > 0)
2894 {
2895 printTableAddFooter(&cont, _("Statistics objects:"));
2896
2897 for (i = 0; i < tuples; i++)
2898 {
2899 bool gotone = false;
2900
2901 printfPQExpBuffer(&buf, " ");
2902
2903 /* statistics object name (qualified with namespace) */
2904 appendPQExpBuffer(&buf, "\"%s.%s\" (",
2905 PQgetvalue(result, i, 2),
2906 PQgetvalue(result, i, 3));
2907
2908 /* options */
2909 if (strcmp(PQgetvalue(result, i, 5), "t") == 0)
2910 {
2911 appendPQExpBufferStr(&buf, "ndistinct");
2912 gotone = true;
2913 }
2914
2915 if (strcmp(PQgetvalue(result, i, 6), "t") == 0)
2916 {
2917 appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2918 gotone = true;
2919 }
2920
2921 if (strcmp(PQgetvalue(result, i, 7), "t") == 0)
2922 {
2923 appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2924 }
2925
2926 appendPQExpBuffer(&buf, ") ON %s FROM %s",
2927 PQgetvalue(result, i, 4),
2928 PQgetvalue(result, i, 1));
2929
2930 /* Show the stats target if it's not default */
2931 if (strcmp(PQgetvalue(result, i, 8), "-1") != 0)
2932 appendPQExpBuffer(&buf, "; STATISTICS %s",
2933 PQgetvalue(result, i, 8));
2934
2936 }
2937 }
2938 PQclear(result);
2939 }
2940
2941 /* print rules */
2942 if (tableinfo.hasrules && tableinfo.relkind != RELKIND_MATVIEW)
2943 {
2944 printfPQExpBuffer(&buf, "/* %s */\n",
2945 _("Get rules for this relation"));
2947 "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
2948 "ev_enabled\n"
2949 "FROM pg_catalog.pg_rewrite r\n"
2950 "WHERE r.ev_class = '%s' ORDER BY 1;",
2951 oid);
2952 result = PSQLexec(buf.data);
2953 if (!result)
2954 goto error_return;
2955 else
2956 tuples = PQntuples(result);
2957
2958 if (tuples > 0)
2959 {
2960 bool have_heading;
2961 int category;
2962
2963 for (category = 0; category < 4; category++)
2964 {
2965 have_heading = false;
2966
2967 for (i = 0; i < tuples; i++)
2968 {
2969 const char *ruledef;
2970 bool list_rule = false;
2971
2972 switch (category)
2973 {
2974 case 0:
2975 if (*PQgetvalue(result, i, 2) == 'O')
2976 list_rule = true;
2977 break;
2978 case 1:
2979 if (*PQgetvalue(result, i, 2) == 'D')
2980 list_rule = true;
2981 break;
2982 case 2:
2983 if (*PQgetvalue(result, i, 2) == 'A')
2984 list_rule = true;
2985 break;
2986 case 3:
2987 if (*PQgetvalue(result, i, 2) == 'R')
2988 list_rule = true;
2989 break;
2990 }
2991 if (!list_rule)
2992 continue;
2993
2994 if (!have_heading)
2995 {
2996 switch (category)
2997 {
2998 case 0:
2999 printfPQExpBuffer(&buf, _("Rules:"));
3000 break;
3001 case 1:
3002 printfPQExpBuffer(&buf, _("Disabled rules:"));
3003 break;
3004 case 2:
3005 printfPQExpBuffer(&buf, _("Rules firing always:"));
3006 break;
3007 case 3:
3008 printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
3009 break;
3010 }
3012 have_heading = true;
3013 }
3014
3015 /* Everything after "CREATE RULE" is echoed verbatim */
3016 ruledef = PQgetvalue(result, i, 1);
3017 ruledef += 12;
3018 printfPQExpBuffer(&buf, " %s", ruledef);
3020 }
3021 }
3022 }
3023 PQclear(result);
3024 }
3025
3026 /* print any publications */
3027 printfPQExpBuffer(&buf, "/* %s */\n",
3028 _("Get publications that publish this table"));
3029 if (pset.sversion >= 150000)
3030 {
3032 "SELECT pubname\n"
3033 " , NULL\n"
3034 " , NULL\n"
3035 "FROM pg_catalog.pg_publication p\n"
3036 " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
3037 " JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
3038 "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
3039 "UNION\n"
3040 "SELECT pubname\n"
3041 " , pg_catalog.pg_get_expr(pr.prqual, c.oid)\n"
3042 " , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
3043 " (SELECT pg_catalog.string_agg(attname, ', ')\n"
3044 " FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
3045 " pg_catalog.pg_attribute\n"
3046 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
3047 " ELSE NULL END) "
3048 "FROM pg_catalog.pg_publication p\n"
3049 " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3050 " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
3051 "WHERE pr.prrelid = '%s'\n",
3052 oid, oid, oid);
3053
3054 if (pset.sversion >= 190000)
3055 {
3056 /*
3057 * Skip entries where this relation appears in the
3058 * publication's EXCEPT list.
3059 */
3061 " AND NOT pr.prexcept\n"
3062 "UNION\n"
3063 "SELECT pubname\n"
3064 " , NULL\n"
3065 " , NULL\n"
3066 "FROM pg_catalog.pg_publication p\n"
3067 "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3068 " AND NOT EXISTS (\n"
3069 " SELECT 1\n"
3070 " FROM pg_catalog.pg_publication_rel pr\n"
3071 " WHERE pr.prpubid = p.oid AND\n"
3072 " (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s')))\n"
3073 "ORDER BY 1;",
3074 oid, oid, oid);
3075 }
3076 else
3077 {
3079 "UNION\n"
3080 "SELECT pubname\n"
3081 " , NULL\n"
3082 " , NULL\n"
3083 "FROM pg_catalog.pg_publication p\n"
3084 "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3085 "ORDER BY 1;",
3086 oid);
3087 }
3088 }
3089 else
3090 {
3092 "SELECT pubname\n"
3093 " , NULL\n"
3094 " , NULL\n"
3095 "FROM pg_catalog.pg_publication p\n"
3096 "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3097 "WHERE pr.prrelid = '%s'\n"
3098 "UNION ALL\n"
3099 "SELECT pubname\n"
3100 " , NULL\n"
3101 " , NULL\n"
3102 "FROM pg_catalog.pg_publication p\n"
3103 "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3104 "ORDER BY 1;",
3105 oid, oid);
3106 }
3107
3108 result = PSQLexec(buf.data);
3109 if (!result)
3110 goto error_return;
3111 else
3112 tuples = PQntuples(result);
3113
3114 if (tuples > 0)
3115 printTableAddFooter(&cont, _("Included in publications:"));
3116
3117 /* Might be an empty set - that's ok */
3118 for (i = 0; i < tuples; i++)
3119 {
3120 printfPQExpBuffer(&buf, " \"%s\"",
3121 PQgetvalue(result, i, 0));
3122
3123 /* column list (if any) */
3124 if (!PQgetisnull(result, i, 2))
3125 appendPQExpBuffer(&buf, " (%s)",
3126 PQgetvalue(result, i, 2));
3127
3128 /* row filter (if any) */
3129 if (!PQgetisnull(result, i, 1))
3130 appendPQExpBuffer(&buf, " WHERE %s",
3131 PQgetvalue(result, i, 1));
3132
3134 }
3135 PQclear(result);
3136
3137 /* Print publications where the table is in the EXCEPT clause */
3138 if (pset.sversion >= 190000)
3139 {
3140 printfPQExpBuffer(&buf, "/* %s */\n",
3141 _("Get publications that exclude this table"));
3143 "SELECT pubname\n"
3144 "FROM pg_catalog.pg_publication p\n"
3145 "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3146 "WHERE (pr.prrelid = '%s' OR pr.prrelid = pg_catalog.pg_partition_root('%s'))\n"
3147 "AND pr.prexcept\n"
3148 "ORDER BY 1;", oid, oid);
3149
3150 result = PSQLexec(buf.data);
3151 if (!result)
3152 goto error_return;
3153 else
3154 tuples = PQntuples(result);
3155
3156 if (tuples > 0)
3157 printTableAddFooter(&cont, _("Excluded from publications:"));
3158
3159 /* Might be an empty set - that's ok */
3160 for (i = 0; i < tuples; i++)
3161 {
3162 printfPQExpBuffer(&buf, " \"%s\"", PQgetvalue(result, i, 0));
3163
3165 }
3166 PQclear(result);
3167 }
3168
3169 /*
3170 * If verbose, print NOT NULL constraints.
3171 */
3172 if (verbose)
3173 {
3174 printfPQExpBuffer(&buf, "/* %s */\n",
3175 _("Get not-null constraints for this table"));
3177 "SELECT c.conname, a.attname, c.connoinherit,\n"
3178 " c.conislocal, c.coninhcount <> 0,\n"
3179 " c.convalidated\n"
3180 "FROM pg_catalog.pg_constraint c JOIN\n"
3181 " pg_catalog.pg_attribute a ON\n"
3182 " (a.attrelid = c.conrelid AND a.attnum = c.conkey[1])\n"
3183 "WHERE c.contype = " CppAsString2(CONSTRAINT_NOTNULL) " AND\n"
3184 " c.conrelid = '%s'::pg_catalog.regclass\n"
3185 "ORDER BY a.attnum",
3186 oid);
3187
3188 result = PSQLexec(buf.data);
3189 if (!result)
3190 goto error_return;
3191 else
3192 tuples = PQntuples(result);
3193
3194 if (tuples > 0)
3195 printTableAddFooter(&cont, _("Not-null constraints:"));
3196
3197 /* Might be an empty set - that's ok */
3198 for (i = 0; i < tuples; i++)
3199 {
3200 bool islocal = PQgetvalue(result, i, 3)[0] == 't';
3201 bool inherited = PQgetvalue(result, i, 4)[0] == 't';
3202 bool validated = PQgetvalue(result, i, 5)[0] == 't';
3203
3204 printfPQExpBuffer(&buf, " \"%s\" NOT NULL \"%s\"%s%s",
3205 PQgetvalue(result, i, 0),
3206 PQgetvalue(result, i, 1),
3207 PQgetvalue(result, i, 2)[0] == 't' ?
3208 " NO INHERIT" :
3209 islocal && inherited ? _(" (local, inherited)") :
3210 inherited ? _(" (inherited)") : "",
3211 !validated ? " NOT VALID" : "");
3212
3214 }
3215 PQclear(result);
3216 }
3217 }
3218
3219 /* Get view_def if table is a view or materialized view */
3220 if ((tableinfo.relkind == RELKIND_VIEW ||
3221 tableinfo.relkind == RELKIND_MATVIEW) && verbose)
3222 {
3224
3225 printfPQExpBuffer(&buf, "/* %s */\n", _("Get view's definition"));
3227 "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
3228 oid);
3229 result = PSQLexec(buf.data);
3230 if (!result)
3231 goto error_return;
3232
3233 if (PQntuples(result) > 0)
3235
3236 PQclear(result);
3237 }
3238
3239 if (view_def)
3240 {
3241 PGresult *result = NULL;
3242
3243 /* Footer information about a view */
3244 printTableAddFooter(&cont, _("View definition:"));
3246
3247 /* print rules */
3248 if (tableinfo.hasrules)
3249 {
3250 printfPQExpBuffer(&buf, "/* %s */\n", _("Get rules for this view"));
3252 "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
3253 "FROM pg_catalog.pg_rewrite r\n"
3254 "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
3255 oid);
3256 result = PSQLexec(buf.data);
3257 if (!result)
3258 goto error_return;
3259
3260 if (PQntuples(result) > 0)
3261 {
3262 printTableAddFooter(&cont, _("Rules:"));
3263 for (i = 0; i < PQntuples(result); i++)
3264 {
3265 const char *ruledef;
3266
3267 /* Everything after "CREATE RULE" is echoed verbatim */
3268 ruledef = PQgetvalue(result, i, 1);
3269 ruledef += 12;
3270
3271 printfPQExpBuffer(&buf, " %s", ruledef);
3273 }
3274 }
3275 PQclear(result);
3276 }
3277 }
3278
3279 /*
3280 * Print triggers next, if any (but only user-defined triggers). This
3281 * could apply to either a table or a view.
3282 */
3283 if (tableinfo.hastriggers)
3284 {
3286 int tuples;
3287
3288 printfPQExpBuffer(&buf, "/* %s */\n",
3289 _("Get triggers for this relation"));
3291 "SELECT t.tgname, "
3292 "pg_catalog.pg_get_triggerdef(t.oid, true), "
3293 "t.tgenabled, t.tgisinternal,\n");
3294
3295 /*
3296 * Detect whether each trigger is inherited, and if so, get the name
3297 * of the topmost table it's inherited from. We have no easy way to
3298 * do that pre-v13, for lack of the tgparentid column. Even with
3299 * tgparentid, a straightforward search for the topmost parent would
3300 * require a recursive CTE, which seems unduly expensive. We cheat a
3301 * bit by assuming parent triggers will match by tgname; then, joining
3302 * with pg_partition_ancestors() allows the planner to make use of
3303 * pg_trigger_tgrelid_tgname_index if it wishes. We ensure we find
3304 * the correct topmost parent by stopping at the first-in-partition-
3305 * ancestry-order trigger that has tgparentid = 0. (There might be
3306 * unrelated, non-inherited triggers with the same name further up the
3307 * stack, so this is important.)
3308 */
3309 if (pset.sversion >= 130000)
3311 " CASE WHEN t.tgparentid != 0 THEN\n"
3312 " (SELECT u.tgrelid::pg_catalog.regclass\n"
3313 " FROM pg_catalog.pg_trigger AS u,\n"
3314 " pg_catalog.pg_partition_ancestors(t.tgrelid) WITH ORDINALITY AS a(relid, depth)\n"
3315 " WHERE u.tgname = t.tgname AND u.tgrelid = a.relid\n"
3316 " AND u.tgparentid = 0\n"
3317 " ORDER BY a.depth LIMIT 1)\n"
3318 " END AS parent\n");
3319 else
3320 appendPQExpBufferStr(&buf, " NULL AS parent\n");
3321
3323 "FROM pg_catalog.pg_trigger t\n"
3324 "WHERE t.tgrelid = '%s' AND ",
3325 oid);
3326
3327 /*
3328 * tgisinternal is set true for inherited triggers of partitions in
3329 * servers between v11 and v14, though these must still be shown to
3330 * the user. So we use another property that is true for such
3331 * inherited triggers to avoid them being hidden, which is their
3332 * dependence on another trigger.
3333 */
3334 if (pset.sversion >= 110000 && pset.sversion < 150000)
3335 appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n"
3336 " OR EXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n"
3337 " AND refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass))");
3338 else
3339 /* display/warn about disabled internal triggers */
3340 appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))");
3341 appendPQExpBufferStr(&buf, "\nORDER BY 1;");
3342
3343 result = PSQLexec(buf.data);
3344 if (!result)
3345 goto error_return;
3346 else
3347 tuples = PQntuples(result);
3348
3349 if (tuples > 0)
3350 {
3351 bool have_heading;
3352 int category;
3353
3354 /*
3355 * split the output into 4 different categories. Enabled triggers,
3356 * disabled triggers and the two special ALWAYS and REPLICA
3357 * configurations.
3358 */
3359 for (category = 0; category <= 4; category++)
3360 {
3361 have_heading = false;
3362 for (i = 0; i < tuples; i++)
3363 {
3364 bool list_trigger;
3365 const char *tgdef;
3366 const char *usingpos;
3367 const char *tgenabled;
3368 const char *tgisinternal;
3369
3370 /*
3371 * Check if this trigger falls into the current category
3372 */
3373 tgenabled = PQgetvalue(result, i, 2);
3374 tgisinternal = PQgetvalue(result, i, 3);
3375 list_trigger = false;
3376 switch (category)
3377 {
3378 case 0:
3379 if (*tgenabled == 'O' || *tgenabled == 't')
3380 list_trigger = true;
3381 break;
3382 case 1:
3383 if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3384 *tgisinternal == 'f')
3385 list_trigger = true;
3386 break;
3387 case 2:
3388 if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3389 *tgisinternal == 't')
3390 list_trigger = true;
3391 break;
3392 case 3:
3393 if (*tgenabled == 'A')
3394 list_trigger = true;
3395 break;
3396 case 4:
3397 if (*tgenabled == 'R')
3398 list_trigger = true;
3399 break;
3400 }
3401 if (list_trigger == false)
3402 continue;
3403
3404 /* Print the category heading once */
3405 if (have_heading == false)
3406 {
3407 switch (category)
3408 {
3409 case 0:
3410 printfPQExpBuffer(&buf, _("Triggers:"));
3411 break;
3412 case 1:
3413 printfPQExpBuffer(&buf, _("Disabled user triggers:"));
3414 break;
3415 case 2:
3416 printfPQExpBuffer(&buf, _("Disabled internal triggers:"));
3417 break;
3418 case 3:
3419 printfPQExpBuffer(&buf, _("Triggers firing always:"));
3420 break;
3421 case 4:
3422 printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
3423 break;
3424 }
3426 have_heading = true;
3427 }
3428
3429 /* Everything after "TRIGGER" is echoed verbatim */
3430 tgdef = PQgetvalue(result, i, 1);
3431 usingpos = strstr(tgdef, " TRIGGER ");
3432 if (usingpos)
3433 tgdef = usingpos + 9;
3434
3435 printfPQExpBuffer(&buf, " %s", tgdef);
3436
3437 /* Visually distinguish inherited triggers */
3438 if (!PQgetisnull(result, i, 4))
3439 appendPQExpBuffer(&buf, ", ON TABLE %s",
3440 PQgetvalue(result, i, 4));
3441
3443 }
3444 }
3445 }
3446 PQclear(result);
3447 }
3448
3449 /*
3450 * Finish printing the footer information about a table.
3451 */
3452 if (tableinfo.relkind == RELKIND_RELATION ||
3453 tableinfo.relkind == RELKIND_MATVIEW ||
3454 tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
3457 tableinfo.relkind == RELKIND_TOASTVALUE)
3458 {
3459 bool is_partitioned;
3461 int tuples;
3462
3463 /* simplify some repeated tests below */
3466
3467 /* print foreign server name */
3468 if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
3469 {
3470 char *ftoptions;
3471
3472 /* Footer information about foreign table */
3473 printfPQExpBuffer(&buf, "/* %s */\n",
3474 _("Get foreign server for this table"));
3476 "SELECT s.srvname,\n"
3477 " pg_catalog.array_to_string(ARRAY(\n"
3478 " SELECT pg_catalog.quote_ident(option_name)"
3479 " || ' ' || pg_catalog.quote_literal(option_value)\n"
3480 " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ')\n"
3481 "FROM pg_catalog.pg_foreign_table f,\n"
3482 " pg_catalog.pg_foreign_server s\n"
3483 "WHERE f.ftrelid = '%s' AND s.oid = f.ftserver;",
3484 oid);
3485 result = PSQLexec(buf.data);
3486 if (!result)
3487 goto error_return;
3488 else if (PQntuples(result) != 1)
3489 {
3490 PQclear(result);
3491 goto error_return;
3492 }
3493
3494 /* Print server name */
3495 printfPQExpBuffer(&buf, _("Server: %s"),
3496 PQgetvalue(result, 0, 0));
3498
3499 /* Print per-table FDW options, if any */
3500 ftoptions = PQgetvalue(result, 0, 1);
3501 if (ftoptions && ftoptions[0] != '\0')
3502 {
3503 printfPQExpBuffer(&buf, _("FDW options: (%s)"), ftoptions);
3505 }
3506 PQclear(result);
3507 }
3508
3509 /* print tables inherited from (exclude partitioned parents) */
3510 printfPQExpBuffer(&buf, "/* %s */\n",
3511 _("Get inheritance parent tables"));
3513 "SELECT c.oid::pg_catalog.regclass\n"
3514 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3515 "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n"
3516 " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE)
3517 " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX)
3518 "\nORDER BY inhseqno;",
3519 oid);
3520
3521 result = PSQLexec(buf.data);
3522 if (!result)
3523 goto error_return;
3524 else
3525 {
3526 const char *s = _("Inherits");
3527
3528 tuples = PQntuples(result);
3529
3530 if (tuples > 0)
3531 {
3532 printfPQExpBuffer(&buf, "%s:", s);
3534 }
3535
3536 for (i = 0; i < tuples; i++)
3537 {
3538 printfPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 0));
3540 }
3541
3542 PQclear(result);
3543 }
3544
3545 /* print child tables (with additional info if partitions) */
3546 printfPQExpBuffer(&buf, "/* %s */\n", _("Get child tables"));
3547 if (pset.sversion >= 140000)
3549 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3550 " inhdetachpending,"
3551 " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3552 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3553 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3554 "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3555 " c.oid::pg_catalog.regclass::pg_catalog.text;",
3556 oid);
3557 else
3559 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3560 " false AS inhdetachpending,"
3561 " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3562 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3563 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3564 "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3565 " c.oid::pg_catalog.regclass::pg_catalog.text;",
3566 oid);
3567
3568 result = PSQLexec(buf.data);
3569 if (!result)
3570 goto error_return;
3571 tuples = PQntuples(result);
3572
3573 /*
3574 * For a partitioned table with no partitions, always print the number
3575 * of partitions as zero, even when verbose output is expected.
3576 * Otherwise, we will not print "Partitions" section for a partitioned
3577 * table without any partitions.
3578 */
3579 if (is_partitioned && tuples == 0)
3580 {
3581 printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples);
3583 }
3584 else if (!verbose)
3585 {
3586 /* print the number of child tables, if any */
3587 if (tuples > 0)
3588 {
3589 if (is_partitioned)
3590 printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
3591 else
3592 printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
3594 }
3595 }
3596 else
3597 {
3598 /* display the list of child tables */
3599 const char *ct = is_partitioned ? _("Partitions") : _("Child tables");
3600
3601 if (tuples > 0)
3602 {
3603 printfPQExpBuffer(&buf, "%s:", ct);
3605 }
3606
3607 for (i = 0; i < tuples; i++)
3608 {
3609 char child_relkind = *PQgetvalue(result, i, 1);
3610
3611 printfPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 0));
3612 if (!PQgetisnull(result, i, 3))
3613 appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 3));
3616 appendPQExpBufferStr(&buf, ", PARTITIONED");
3618 appendPQExpBufferStr(&buf, ", FOREIGN");
3619 if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
3620 appendPQExpBufferStr(&buf, " (DETACH PENDING)");
3621
3623 }
3624 }
3625 PQclear(result);
3626
3627 /* Table type */
3628 if (tableinfo.reloftype)
3629 {
3630 printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
3632 }
3633
3634 if (verbose &&
3635 (tableinfo.relkind == RELKIND_RELATION ||
3636 tableinfo.relkind == RELKIND_MATVIEW) &&
3637
3638 /*
3639 * No need to display default values; we already display a REPLICA
3640 * IDENTITY marker on indexes.
3641 */
3642 tableinfo.relreplident != REPLICA_IDENTITY_INDEX &&
3643 ((strcmp(schemaname, "pg_catalog") != 0 &&
3644 tableinfo.relreplident != REPLICA_IDENTITY_DEFAULT) ||
3645 (strcmp(schemaname, "pg_catalog") == 0 &&
3646 tableinfo.relreplident != REPLICA_IDENTITY_NOTHING)))
3647 {
3648 const char *s = _("Replica Identity");
3649
3650 printfPQExpBuffer(&buf, "%s: %s",
3651 s,
3652 tableinfo.relreplident == REPLICA_IDENTITY_FULL ? "FULL" :
3653 tableinfo.relreplident == REPLICA_IDENTITY_DEFAULT ? "NOTHING" :
3654 "???");
3655
3657 }
3658
3659 /* OIDs, if verbose and not a materialized view */
3660 if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids)
3661 printTableAddFooter(&cont, _("Has OIDs: yes"));
3662
3663 /* Tablespace info */
3664 add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
3665 true);
3666
3667 /* Access method info */
3668 if (verbose && tableinfo.relam != NULL && !pset.hide_tableam)
3669 {
3670 printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam);
3672 }
3673 }
3674
3675 /* reloptions, if verbose */
3676 if (verbose &&
3677 tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
3678 {
3679 const char *t = _("Options");
3680
3681 printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions);
3683 }
3684
3686
3687 retval = true;
3688
3690
3691 /* clean up */
3695 termPQExpBuffer(&title);
3697
3699
3700 PQclear(res);
3701
3702 return retval;
3703}
3704
3705/*
3706 * Add a tablespace description to a footer. If 'newline' is true, it is added
3707 * in a new line; otherwise it's appended to the current value of the last
3708 * footer.
3709 */
3710static void
3712 Oid tablespace, const bool newline)
3713{
3714 /* relkinds for which we support tablespaces */
3715 if (relkind == RELKIND_RELATION ||
3716 relkind == RELKIND_MATVIEW ||
3717 relkind == RELKIND_INDEX ||
3718 relkind == RELKIND_PARTITIONED_TABLE ||
3719 relkind == RELKIND_PARTITIONED_INDEX ||
3720 relkind == RELKIND_TOASTVALUE)
3721 {
3722 /*
3723 * We ignore the database default tablespace so that users not using
3724 * tablespaces don't need to know about them.
3725 */
3726 if (tablespace != 0)
3727 {
3728 PGresult *result = NULL;
3730
3732 printfPQExpBuffer(&buf, "/* %s */\n",
3733 _("Get tablespace information for this relation"));
3735 "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3736 "WHERE oid = '%u';", tablespace);
3737 result = PSQLexec(buf.data);
3738 if (!result)
3739 {
3741 return;
3742 }
3743 /* Should always be the case, but.... */
3744 if (PQntuples(result) > 0)
3745 {
3746 if (newline)
3747 {
3748 /* Add the tablespace as a new footer */
3749 printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3750 PQgetvalue(result, 0, 0));
3752 }
3753 else
3754 {
3755 /* Append the tablespace to the latest footer */
3756 printfPQExpBuffer(&buf, "%s", cont->footer->data);
3757
3758 /*-------
3759 translator: before this string there's an index description like
3760 '"foo_pkey" PRIMARY KEY, btree (a)' */
3761 appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3762 PQgetvalue(result, 0, 0));
3764 }
3765 }
3766 PQclear(result);
3768 }
3769 }
3770}
3771
3772/*
3773 * \du or \dg
3774 *
3775 * Describes roles. Any schema portion of the pattern is ignored.
3776 */
3777bool
3778describeRoles(const char *pattern, bool verbose, bool showSystem)
3779{
3781 PGresult *res;
3784 int ncols = 2;
3785 int nrows = 0;
3786 int i;
3787 int conns;
3788 const char align = 'l';
3789 char **attr;
3790
3791 myopt.default_footer = false;
3792
3794
3795 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching roles"));
3797 "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
3798 " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
3799 " r.rolconnlimit, r.rolvaliduntil");
3800
3801 if (verbose)
3802 {
3803 appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
3804 ncols++;
3805 }
3806 appendPQExpBufferStr(&buf, "\n, r.rolreplication");
3807 appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
3808
3809 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
3810
3811 if (!showSystem && !pattern)
3812 appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
3813
3814 if (!validateSQLNamePattern(&buf, pattern, false, false,
3815 NULL, "r.rolname", NULL, NULL,
3816 NULL, 1))
3817 {
3819 return false;
3820 }
3821
3822 appendPQExpBufferStr(&buf, "ORDER BY 1;");
3823
3824 res = PSQLexec(buf.data);
3825 if (!res)
3826 return false;
3827
3828 nrows = PQntuples(res);
3829 attr = pg_malloc0_array(char *, nrows + 1);
3830
3831 printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
3832
3833 printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
3834 printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
3835
3836 if (verbose)
3837 printTableAddHeader(&cont, gettext_noop("Description"), true, align);
3838
3839 for (i = 0; i < nrows; i++)
3840 {
3841 printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
3842
3844 if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
3845 add_role_attribute(&buf, _("Superuser"));
3846
3847 if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
3848 add_role_attribute(&buf, _("No inheritance"));
3849
3850 if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
3851 add_role_attribute(&buf, _("Create role"));
3852
3853 if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
3854 add_role_attribute(&buf, _("Create DB"));
3855
3856 if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
3857 add_role_attribute(&buf, _("Cannot login"));
3858
3859 if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
3860 add_role_attribute(&buf, _("Replication"));
3861
3862 if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
3863 add_role_attribute(&buf, _("Bypass RLS"));
3864
3865 conns = atoi(PQgetvalue(res, i, 6));
3866 if (conns >= 0)
3867 {
3868 if (buf.len > 0)
3869 appendPQExpBufferChar(&buf, '\n');
3870
3871 if (conns == 0)
3872 appendPQExpBufferStr(&buf, _("No connections"));
3873 else
3874 appendPQExpBuffer(&buf, ngettext("%d connection",
3875 "%d connections",
3876 conns),
3877 conns);
3878 }
3879
3880 if (strcmp(PQgetvalue(res, i, 7), "") != 0)
3881 {
3882 if (buf.len > 0)
3883 appendPQExpBufferChar(&buf, '\n');
3884 appendPQExpBufferStr(&buf, _("Password valid until "));
3886 }
3887
3888 attr[i] = pg_strdup(buf.data);
3889
3890 printTableAddCell(&cont, attr[i], false, false);
3891
3892 if (verbose)
3893 printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
3894 }
3896
3899
3900 for (i = 0; i < nrows; i++)
3901 pg_free(attr[i]);
3902 pg_free(attr);
3903
3904 PQclear(res);
3905 return true;
3906}
3907
3908static void
3910{
3911 if (buf->len > 0)
3913
3915}
3916
3917/*
3918 * \drds
3919 */
3920bool
3921listDbRoleSettings(const char *pattern, const char *pattern2)
3922{
3924 PGresult *res;
3926 bool havewhere;
3927
3929
3930 printfPQExpBuffer(&buf, "/* %s */\n", _("Get per-database and per-role settings"));
3931 appendPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
3932 "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
3933 "FROM pg_catalog.pg_db_role_setting s\n"
3934 "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n"
3935 "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n",
3936 gettext_noop("Role"),
3937 gettext_noop("Database"),
3938 gettext_noop("Settings"));
3939 if (!validateSQLNamePattern(&buf, pattern, false, false,
3940 NULL, "r.rolname", NULL, NULL, &havewhere, 1))
3941 goto error_return;
3943 NULL, "d.datname", NULL, NULL,
3944 NULL, 1))
3945 goto error_return;
3946 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3947
3948 res = PSQLexec(buf.data);
3950 if (!res)
3951 return false;
3952
3953 /*
3954 * Most functions in this file are content to print an empty table when
3955 * there are no matching objects. We intentionally deviate from that
3956 * here, but only in !quiet mode, because of the possibility that the user
3957 * is confused about what the two pattern arguments mean.
3958 */
3959 if (PQntuples(res) == 0 && !pset.quiet)
3960 {
3961 if (pattern && pattern2)
3962 pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".",
3963 pattern, pattern2);
3964 else if (pattern)
3965 pg_log_error("Did not find any settings for role \"%s\".",
3966 pattern);
3967 else
3968 pg_log_error("Did not find any settings.");
3969 }
3970 else
3971 {
3972 myopt.title = _("List of settings");
3973 myopt.translate_header = true;
3974
3975 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3976 }
3977
3978 PQclear(res);
3979 return true;
3980
3983 return false;
3984}
3985
3986/*
3987 * \drg
3988 * Describes role grants.
3989 */
3990bool
3991describeRoleGrants(const char *pattern, bool showSystem)
3992{
3994 PGresult *res;
3996
3998 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching role grants"));
4000 "SELECT m.rolname AS \"%s\", r.rolname AS \"%s\",\n"
4001 " pg_catalog.concat_ws(', ',\n",
4002 gettext_noop("Role name"),
4003 gettext_noop("Member of"));
4004
4005 if (pset.sversion >= 160000)
4007 " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
4008 " CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n"
4009 " CASE WHEN pam.set_option THEN 'SET' END\n");
4010 else
4012 " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
4013 " CASE WHEN m.rolinherit THEN 'INHERIT' END,\n"
4014 " 'SET'\n");
4015
4017 " ) AS \"%s\",\n"
4018 " g.rolname AS \"%s\"\n",
4019 gettext_noop("Options"),
4020 gettext_noop("Grantor"));
4021
4023 "FROM pg_catalog.pg_roles m\n"
4024 " JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n"
4025 " LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n"
4026 " LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n");
4027
4028 if (!showSystem && !pattern)
4029 appendPQExpBufferStr(&buf, "WHERE m.rolname !~ '^pg_'\n");
4030
4031 if (!validateSQLNamePattern(&buf, pattern, false, false,
4032 NULL, "m.rolname", NULL, NULL,
4033 NULL, 1))
4034 {
4036 return false;
4037 }
4038
4039 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;\n");
4040
4041 res = PSQLexec(buf.data);
4043 if (!res)
4044 return false;
4045
4046 myopt.title = _("List of role grants");
4047 myopt.translate_header = true;
4048
4049 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4050
4051 PQclear(res);
4052 return true;
4053}
4054
4055
4056/*
4057 * listTables()
4058 *
4059 * handler for \dt, \di, etc.
4060 *
4061 * tabtypes is an array of characters, specifying what info is desired:
4062 * t - tables
4063 * i - indexes
4064 * v - views
4065 * m - materialized views
4066 * s - sequences
4067 * E - foreign table (Note: different from 'f', the relkind value)
4068 * G - property graphs
4069 * (any order of the above is fine)
4070 */
4071bool
4072listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
4073{
4074 bool showTables = strchr(tabtypes, 't') != NULL;
4075 bool showIndexes = strchr(tabtypes, 'i') != NULL;
4076 bool showViews = strchr(tabtypes, 'v') != NULL;
4077 bool showMatViews = strchr(tabtypes, 'm') != NULL;
4078 bool showSeq = strchr(tabtypes, 's') != NULL;
4079 bool showForeign = strchr(tabtypes, 'E') != NULL;
4080 bool showPropGraphs = strchr(tabtypes, 'G') != NULL;
4081
4082 int ntypes;
4084 PGresult *res;
4086 int cols_so_far;
4087 bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
4088
4089 /* Count the number of explicitly-requested relation types */
4092 /* If none, we default to \dtvmsEG (but see also command.c) */
4093 if (ntypes == 0)
4095
4097
4098 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching relations"));
4100 "SELECT n.nspname as \"%s\",\n"
4101 " c.relname as \"%s\",\n"
4102 " CASE c.relkind"
4103 " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
4104 " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'"
4105 " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
4106 " WHEN " CppAsString2(RELKIND_INDEX) " THEN '%s'"
4107 " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
4108 " WHEN " CppAsString2(RELKIND_TOASTVALUE) " THEN '%s'"
4109 " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
4110 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4111 " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4112 " WHEN " CppAsString2(RELKIND_PROPGRAPH) " THEN '%s'"
4113 " END as \"%s\",\n"
4114 " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4115 gettext_noop("Schema"),
4116 gettext_noop("Name"),
4117 gettext_noop("table"),
4118 gettext_noop("view"),
4119 gettext_noop("materialized view"),
4120 gettext_noop("index"),
4121 gettext_noop("sequence"),
4122 gettext_noop("TOAST table"),
4123 gettext_noop("foreign table"),
4124 gettext_noop("partitioned table"),
4125 gettext_noop("partitioned index"),
4126 gettext_noop("property graph"),
4127 gettext_noop("Type"),
4128 gettext_noop("Owner"));
4129 cols_so_far = 4;
4130
4131 if (showIndexes)
4132 {
4134 ",\n c2.relname as \"%s\"",
4135 gettext_noop("Table"));
4136 cols_so_far++;
4137 }
4138
4139 if (verbose)
4140 {
4141 /*
4142 * Show whether a relation is permanent, temporary, or unlogged.
4143 */
4145 ",\n CASE c.relpersistence "
4146 "WHEN " CppAsString2(RELPERSISTENCE_PERMANENT) " THEN '%s' "
4147 "WHEN " CppAsString2(RELPERSISTENCE_TEMP) " THEN '%s' "
4148 "WHEN " CppAsString2(RELPERSISTENCE_UNLOGGED) " THEN '%s' "
4149 "END as \"%s\"",
4150 gettext_noop("permanent"),
4151 gettext_noop("temporary"),
4152 gettext_noop("unlogged"),
4153 gettext_noop("Persistence"));
4154 translate_columns[cols_so_far] = true;
4155
4156 /*
4157 * We don't bother to count cols_so_far below here, as there's no need
4158 * to; this might change with future additions to the output columns.
4159 */
4160
4161 /*
4162 * Access methods exist for tables, materialized views and indexes.
4163 * This has been introduced in PostgreSQL 12 for tables.
4164 */
4165 if (pset.sversion >= 120000 && !pset.hide_tableam &&
4168 ",\n am.amname as \"%s\"",
4169 gettext_noop("Access method"));
4170
4172 ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\""
4173 ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4174 gettext_noop("Size"),
4175 gettext_noop("Description"));
4176 }
4177
4179 "\nFROM pg_catalog.pg_class c"
4180 "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4181
4182 if (pset.sversion >= 120000 && !pset.hide_tableam &&
4185 "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam");
4186
4187 if (showIndexes)
4189 "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4190 "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4191
4192 appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4193 if (showTables)
4194 {
4197 /* with 'S' or a pattern, allow 't' to match TOAST tables too */
4198 if (showSystem || pattern)
4200 }
4201 if (showViews)
4203 if (showMatViews)
4205 if (showIndexes)
4208 if (showSeq)
4210 if (showSystem || pattern)
4211 appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
4212 if (showForeign)
4214 if (showPropGraphs)
4216
4217 appendPQExpBufferStr(&buf, "''"); /* dummy */
4218 appendPQExpBufferStr(&buf, ")\n");
4219
4220 if (!showSystem && !pattern)
4221 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4222 " AND n.nspname !~ '^pg_toast'\n"
4223 " AND n.nspname <> 'information_schema'\n");
4224
4225 if (!validateSQLNamePattern(&buf, pattern, true, false,
4226 "n.nspname", "c.relname", NULL,
4227 "pg_catalog.pg_table_is_visible(c.oid)",
4228 NULL, 3))
4229 {
4231 return false;
4232 }
4233
4234 appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
4235
4236 res = PSQLexec(buf.data);
4238 if (!res)
4239 return false;
4240
4241 /*
4242 * Most functions in this file are content to print an empty table when
4243 * there are no matching objects. We intentionally deviate from that
4244 * here, but only in !quiet mode, for historical reasons.
4245 */
4246 if (PQntuples(res) == 0 && !pset.quiet)
4247 {
4248 if (pattern)
4249 {
4250 if (ntypes != 1)
4251 pg_log_error("Did not find any relations named \"%s\".",
4252 pattern);
4253 else if (showTables)
4254 pg_log_error("Did not find any tables named \"%s\".",
4255 pattern);
4256 else if (showIndexes)
4257 pg_log_error("Did not find any indexes named \"%s\".",
4258 pattern);
4259 else if (showViews)
4260 pg_log_error("Did not find any views named \"%s\".",
4261 pattern);
4262 else if (showMatViews)
4263 pg_log_error("Did not find any materialized views named \"%s\".",
4264 pattern);
4265 else if (showSeq)
4266 pg_log_error("Did not find any sequences named \"%s\".",
4267 pattern);
4268 else if (showForeign)
4269 pg_log_error("Did not find any foreign tables named \"%s\".",
4270 pattern);
4271 else if (showPropGraphs)
4272 pg_log_error("Did not find any property graphs named \"%s\".",
4273 pattern);
4274 else /* should not get here */
4275 pg_log_error_internal("Did not find any ??? named \"%s\".",
4276 pattern);
4277 }
4278 else
4279 {
4280 if (ntypes != 1)
4281 pg_log_error("Did not find any relations.");
4282 else if (showTables)
4283 pg_log_error("Did not find any tables.");
4284 else if (showIndexes)
4285 pg_log_error("Did not find any indexes.");
4286 else if (showViews)
4287 pg_log_error("Did not find any views.");
4288 else if (showMatViews)
4289 pg_log_error("Did not find any materialized views.");
4290 else if (showSeq)
4291 pg_log_error("Did not find any sequences.");
4292 else if (showForeign)
4293 pg_log_error("Did not find any foreign tables.");
4294 else if (showPropGraphs)
4295 pg_log_error("Did not find any property graphs.");
4296 else /* should not get here */
4297 pg_log_error_internal("Did not find any ??? relations.");
4298 }
4299 }
4300 else
4301 {
4302 myopt.title =
4303 (ntypes != 1) ? _("List of relations") :
4304 (showTables) ? _("List of tables") :
4305 (showIndexes) ? _("List of indexes") :
4306 (showViews) ? _("List of views") :
4307 (showMatViews) ? _("List of materialized views") :
4308 (showSeq) ? _("List of sequences") :
4309 (showForeign) ? _("List of foreign tables") :
4310 (showPropGraphs) ? _("List of property graphs") :
4311 "List of ???"; /* should not get here */
4312 myopt.translate_header = true;
4313 myopt.translate_columns = translate_columns;
4314 myopt.n_translate_columns = lengthof(translate_columns);
4315
4316 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4317 }
4318
4319 PQclear(res);
4320 return true;
4321}
4322
4323/*
4324 * \dP
4325 * Takes an optional regexp to select particular relations
4326 *
4327 * As with \d, you can specify the kinds of relations you want:
4328 *
4329 * t for tables
4330 * i for indexes
4331 *
4332 * And there's additional flags:
4333 *
4334 * n to list non-leaf partitioned tables
4335 *
4336 * and you can mix and match these in any order.
4337 */
4338bool
4339listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
4340{
4341 bool showTables = strchr(reltypes, 't') != NULL;
4342 bool showIndexes = strchr(reltypes, 'i') != NULL;
4343 bool showNested = strchr(reltypes, 'n') != NULL;
4345 PQExpBufferData title;
4346 PGresult *res;
4348 bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
4349 const char *tabletitle;
4350 bool mixed_output = false;
4351
4352 /* If no relation kind was selected, show them all */
4353 if (!showTables && !showIndexes)
4354 showTables = showIndexes = true;
4355
4356 if (showIndexes && !showTables)
4357 tabletitle = _("List of partitioned indexes"); /* \dPi */
4358 else if (showTables && !showIndexes)
4359 tabletitle = _("List of partitioned tables"); /* \dPt */
4360 else
4361 {
4362 /* show all kinds */
4363 tabletitle = _("List of partitioned relations");
4364 mixed_output = true;
4365 }
4366
4368
4369 printfPQExpBuffer(&buf, "/* %s */\n",
4370 _("Get matching partitioned relations"));
4372 "SELECT n.nspname as \"%s\",\n"
4373 " c.relname as \"%s\",\n"
4374 " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4375 gettext_noop("Schema"),
4376 gettext_noop("Name"),
4377 gettext_noop("Owner"));
4378
4379 if (mixed_output)
4380 {
4382 ",\n CASE c.relkind"
4383 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4384 " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4385 " END as \"%s\"",
4386 gettext_noop("partitioned table"),
4387 gettext_noop("partitioned index"),
4388 gettext_noop("Type"));
4389
4390 translate_columns[3] = true;
4391 }
4392
4393 if (showNested || pattern)
4395 ",\n inh.inhparent::pg_catalog.regclass as \"%s\"",
4396 gettext_noop("Parent name"));
4397
4398 if (showIndexes)
4400 ",\n c2.oid::pg_catalog.regclass as \"%s\"",
4401 gettext_noop("Table"));
4402
4403 if (verbose)
4404 {
4405 /*
4406 * Table access methods were introduced in v12, and can be set on
4407 * partitioned tables since v17.
4408 */
4409 appendPQExpBuffer(&buf, ",\n am.amname as \"%s\"",
4410 gettext_noop("Access method"));
4411
4412 if (showNested)
4413 {
4415 ",\n s.dps as \"%s\"",
4416 gettext_noop("Leaf partition size"));
4418 ",\n s.tps as \"%s\"",
4419 gettext_noop("Total size"));
4420 }
4421 else
4422 /* Sizes of all partitions are considered in this case. */
4424 ",\n s.tps as \"%s\"",
4425 gettext_noop("Total size"));
4426
4428 ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4429 gettext_noop("Description"));
4430 }
4431
4433 "\nFROM pg_catalog.pg_class c"
4434 "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4435
4436 if (showIndexes)
4438 "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4439 "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4440
4441 if (showNested || pattern)
4443 "\n LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid");
4444
4445 if (verbose)
4446 {
4448 "\n LEFT JOIN pg_catalog.pg_am am ON c.relam = am.oid");
4449
4450 if (pset.sversion < 120000)
4451 {
4453 ",\n LATERAL (WITH RECURSIVE d\n"
4454 " AS (SELECT inhrelid AS oid, 1 AS level\n"
4455 " FROM pg_catalog.pg_inherits\n"
4456 " WHERE inhparent = c.oid\n"
4457 " UNION ALL\n"
4458 " SELECT inhrelid, level + 1\n"
4459 " FROM pg_catalog.pg_inherits i\n"
4460 " JOIN d ON i.inhparent = d.oid)\n"
4461 " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
4462 "d.oid))) AS tps,\n"
4463 " pg_catalog.pg_size_pretty(sum("
4464 "\n CASE WHEN d.level = 1"
4465 " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n"
4466 " FROM d) s");
4467 }
4468 else
4469 {
4470 /* PostgreSQL 12 has pg_partition_tree function */
4472 ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
4473 "\n CASE WHEN ppt.isleaf AND ppt.level = 1"
4474 "\n THEN pg_catalog.pg_table_size(ppt.relid)"
4475 " ELSE 0 END)) AS dps"
4476 ",\n pg_catalog.pg_size_pretty(sum("
4477 "pg_catalog.pg_table_size(ppt.relid))) AS tps"
4478 "\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s");
4479 }
4480 }
4481
4482 appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4483 if (showTables)
4485 if (showIndexes)
4487 appendPQExpBufferStr(&buf, "''"); /* dummy */
4488 appendPQExpBufferStr(&buf, ")\n");
4489
4490 appendPQExpBufferStr(&buf, !showNested && !pattern ?
4491 " AND NOT c.relispartition\n" : "");
4492
4493 if (!pattern)
4494 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4495 " AND n.nspname !~ '^pg_toast'\n"
4496 " AND n.nspname <> 'information_schema'\n");
4497
4498 if (!validateSQLNamePattern(&buf, pattern, true, false,
4499 "n.nspname", "c.relname", NULL,
4500 "pg_catalog.pg_table_is_visible(c.oid)",
4501 NULL, 3))
4502 {
4504 return false;
4505 }
4506
4507 appendPQExpBuffer(&buf, "ORDER BY \"Schema\", %s%s\"Name\";",
4508 mixed_output ? "\"Type\" DESC, " : "",
4509 showNested || pattern ? "\"Parent name\" NULLS FIRST, " : "");
4510
4511 res = PSQLexec(buf.data);
4513 if (!res)
4514 return false;
4515
4516 initPQExpBuffer(&title);
4518
4519 myopt.title = title.data;
4520 myopt.translate_header = true;
4521 myopt.translate_columns = translate_columns;
4522 myopt.n_translate_columns = lengthof(translate_columns);
4523
4524 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4525
4526 termPQExpBuffer(&title);
4527
4528 PQclear(res);
4529 return true;
4530}
4531
4532/*
4533 * \dL
4534 *
4535 * Describes languages.
4536 */
4537bool
4538listLanguages(const char *pattern, bool verbose, bool showSystem)
4539{
4541 PGresult *res;
4543
4545
4546 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching procedural languages"));
4548 "SELECT l.lanname AS \"%s\",\n"
4549 " pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n"
4550 " l.lanpltrusted AS \"%s\"",
4551 gettext_noop("Name"),
4552 gettext_noop("Owner"),
4553 gettext_noop("Trusted"));
4554
4555 if (verbose)
4556 {
4558 ",\n NOT l.lanispl AS \"%s\",\n"
4559 " l.lanplcallfoid::pg_catalog.regprocedure AS \"%s\",\n"
4560 " l.lanvalidator::pg_catalog.regprocedure AS \"%s\",\n "
4561 "l.laninline::pg_catalog.regprocedure AS \"%s\",\n ",
4562 gettext_noop("Internal language"),
4563 gettext_noop("Call handler"),
4564 gettext_noop("Validator"),
4565 gettext_noop("Inline handler"));
4566 printACLColumn(&buf, "l.lanacl");
4567 }
4568
4570 ",\n d.description AS \"%s\""
4571 "\nFROM pg_catalog.pg_language l\n"
4572 "LEFT JOIN pg_catalog.pg_description d\n"
4573 " ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
4574 " AND d.objsubid = 0\n",
4575 gettext_noop("Description"));
4576
4577 if (pattern)
4578 {
4579 if (!validateSQLNamePattern(&buf, pattern, false, false,
4580 NULL, "l.lanname", NULL, NULL,
4581 NULL, 2))
4582 {
4584 return false;
4585 }
4586 }
4587
4588 if (!showSystem && !pattern)
4589 appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n");
4590
4591
4592 appendPQExpBufferStr(&buf, "ORDER BY 1;");
4593
4594 res = PSQLexec(buf.data);
4596 if (!res)
4597 return false;
4598
4599 myopt.title = _("List of languages");
4600 myopt.translate_header = true;
4601
4602 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4603
4604 PQclear(res);
4605 return true;
4606}
4607
4608
4609/*
4610 * \dD
4611 *
4612 * Describes domains.
4613 */
4614bool
4615listDomains(const char *pattern, bool verbose, bool showSystem)
4616{
4618 PGresult *res;
4620
4622
4623 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching domains"));
4625 "SELECT n.nspname as \"%s\",\n"
4626 " t.typname as \"%s\",\n"
4627 " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
4628 " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
4629 " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4630 " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4631 " t.typdefault as \"%s\",\n"
4632 " pg_catalog.array_to_string(ARRAY(\n"
4633 " 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"
4634 " ), ' ') as \"%s\"",
4635 gettext_noop("Schema"),
4636 gettext_noop("Name"),
4637 gettext_noop("Type"),
4638 gettext_noop("Collation"),
4639 gettext_noop("Nullable"),
4640 gettext_noop("Default"),
4641 gettext_noop("Check"));
4642
4643 if (verbose)
4644 {
4645 appendPQExpBufferStr(&buf, ",\n ");
4646 printACLColumn(&buf, "t.typacl");
4648 ",\n d.description as \"%s\"",
4649 gettext_noop("Description"));
4650 }
4651
4653 "\nFROM pg_catalog.pg_type t\n"
4654 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
4655
4656 if (verbose)
4658 " LEFT JOIN pg_catalog.pg_description d "
4659 "ON d.classoid = t.tableoid AND d.objoid = t.oid "
4660 "AND d.objsubid = 0\n");
4661
4662 appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
4663
4664 if (!showSystem && !pattern)
4665 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4666 " AND n.nspname <> 'information_schema'\n");
4667
4668 if (!validateSQLNamePattern(&buf, pattern, true, false,
4669 "n.nspname", "t.typname", NULL,
4670 "pg_catalog.pg_type_is_visible(t.oid)",
4671 NULL, 3))
4672 {
4674 return false;
4675 }
4676
4677 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4678
4679 res = PSQLexec(buf.data);
4681 if (!res)
4682 return false;
4683
4684 myopt.title = _("List of domains");
4685 myopt.translate_header = true;
4686
4687 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4688
4689 PQclear(res);
4690 return true;
4691}
4692
4693/*
4694 * \dc
4695 *
4696 * Describes conversions.
4697 */
4698bool
4699listConversions(const char *pattern, bool verbose, bool showSystem)
4700{
4702 PGresult *res;
4704 static const bool translate_columns[] =
4705 {false, false, false, false, true, false};
4706
4708
4709 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching conversions"));
4711 "SELECT n.nspname AS \"%s\",\n"
4712 " c.conname AS \"%s\",\n"
4713 " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
4714 " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
4715 " CASE WHEN c.condefault THEN '%s'\n"
4716 " ELSE '%s' END AS \"%s\"",
4717 gettext_noop("Schema"),
4718 gettext_noop("Name"),
4719 gettext_noop("Source"),
4720 gettext_noop("Destination"),
4721 gettext_noop("yes"), gettext_noop("no"),
4722 gettext_noop("Default?"));
4723
4724 if (verbose)
4726 ",\n d.description AS \"%s\"",
4727 gettext_noop("Description"));
4728
4730 "\nFROM pg_catalog.pg_conversion c\n"
4731 " JOIN pg_catalog.pg_namespace n "
4732 "ON n.oid = c.connamespace\n");
4733
4734 if (verbose)
4736 "LEFT JOIN pg_catalog.pg_description d "
4737 "ON d.classoid = c.tableoid\n"
4738 " AND d.objoid = c.oid "
4739 "AND d.objsubid = 0\n");
4740
4741 appendPQExpBufferStr(&buf, "WHERE true\n");
4742
4743 if (!showSystem && !pattern)
4744 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4745 " AND n.nspname <> 'information_schema'\n");
4746
4747 if (!validateSQLNamePattern(&buf, pattern, true, false,
4748 "n.nspname", "c.conname", NULL,
4749 "pg_catalog.pg_conversion_is_visible(c.oid)",
4750 NULL, 3))
4751 {
4753 return false;
4754 }
4755
4756 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4757
4758 res = PSQLexec(buf.data);
4760 if (!res)
4761 return false;
4762
4763 myopt.title = _("List of conversions");
4764 myopt.translate_header = true;
4765 myopt.translate_columns = translate_columns;
4766 myopt.n_translate_columns = lengthof(translate_columns);
4767
4768 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4769
4770 PQclear(res);
4771 return true;
4772}
4773
4774/*
4775 * \dconfig
4776 *
4777 * Describes configuration parameters.
4778 */
4779bool
4781 bool showSystem)
4782{
4784 PGresult *res;
4786
4788
4789 printfPQExpBuffer(&buf, "/* %s */\n",
4790 _("Get matching configuration parameters"));
4792 "SELECT s.name AS \"%s\", "
4793 "pg_catalog.current_setting(s.name) AS \"%s\"",
4794 gettext_noop("Parameter"),
4795 gettext_noop("Value"));
4796
4797 if (verbose)
4798 {
4800 ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4801 gettext_noop("Type"),
4802 gettext_noop("Context"));
4803 if (pset.sversion >= 150000)
4804 printACLColumn(&buf, "p.paracl");
4805 else
4806 appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4807 gettext_noop("Access privileges"));
4808 }
4809
4810 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4811
4812 if (verbose && pset.sversion >= 150000)
4814 " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4815 " ON pg_catalog.lower(s.name) = p.parname\n");
4816
4817 if (pattern)
4818 processSQLNamePattern(pset.db, &buf, pattern,
4819 false, false,
4820 NULL, "pg_catalog.lower(s.name)", NULL,
4821 NULL, NULL, NULL);
4822 else
4823 appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
4824 " s.setting IS DISTINCT FROM s.boot_val\n");
4825
4826 appendPQExpBufferStr(&buf, "ORDER BY 1;");
4827
4828 res = PSQLexec(buf.data);
4830 if (!res)
4831 return false;
4832
4833 if (pattern)
4834 myopt.title = _("List of configuration parameters");
4835 else
4836 myopt.title = _("List of non-default configuration parameters");
4837 myopt.translate_header = true;
4838
4839 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4840
4841 PQclear(res);
4842 return true;
4843}
4844
4845/*
4846 * \dy
4847 *
4848 * Describes Event Triggers.
4849 */
4850bool
4851listEventTriggers(const char *pattern, bool verbose)
4852{
4854 PGresult *res;
4856 static const bool translate_columns[] =
4857 {false, false, false, true, false, false, false};
4858
4860
4861 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching event triggers"));
4863 "SELECT evtname as \"%s\", "
4864 "evtevent as \"%s\", "
4865 "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
4866 " case evtenabled when 'O' then '%s'"
4867 " when 'R' then '%s'"
4868 " when 'A' then '%s'"
4869 " when 'D' then '%s' end as \"%s\",\n"
4870 " e.evtfoid::pg_catalog.regproc as \"%s\", "
4871 "pg_catalog.array_to_string(array(select x"
4872 " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
4873 gettext_noop("Name"),
4874 gettext_noop("Event"),
4875 gettext_noop("Owner"),
4876 gettext_noop("enabled"),
4877 gettext_noop("replica"),
4878 gettext_noop("always"),
4879 gettext_noop("disabled"),
4880 gettext_noop("Enabled"),
4881 gettext_noop("Function"),
4882 gettext_noop("Tags"));
4883 if (verbose)
4885 ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
4886 gettext_noop("Description"));
4888 "\nFROM pg_catalog.pg_event_trigger e ");
4889
4890 if (!validateSQLNamePattern(&buf, pattern, false, false,
4891 NULL, "evtname", NULL, NULL,
4892 NULL, 1))
4893 {
4895 return false;
4896 }
4897
4898 appendPQExpBufferStr(&buf, "ORDER BY 1");
4899
4900 res = PSQLexec(buf.data);
4902 if (!res)
4903 return false;
4904
4905 myopt.title = _("List of event triggers");
4906 myopt.translate_header = true;
4907 myopt.translate_columns = translate_columns;
4908 myopt.n_translate_columns = lengthof(translate_columns);
4909
4910 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4911
4912 PQclear(res);
4913 return true;
4914}
4915
4916/*
4917 * \dX
4918 *
4919 * Describes extended statistics.
4920 */
4921bool
4922listExtendedStats(const char *pattern, bool verbose)
4923{
4925 PGresult *res;
4927
4929
4930 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching extended statistics"));
4932 "SELECT \n"
4933 "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS \"%s\", \n"
4934 "es.stxname AS \"%s\", \n",
4935 gettext_noop("Schema"),
4936 gettext_noop("Name"));
4937
4938 if (pset.sversion >= 140000)
4940 "pg_catalog.format('%%s FROM %%s', \n"
4941 " pg_catalog.pg_get_statisticsobjdef_columns(es.oid), \n"
4942 " es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4943 gettext_noop("Definition"));
4944 else
4946 "pg_catalog.format('%%s FROM %%s', \n"
4947 " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
4948 " FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
4949 " JOIN pg_catalog.pg_attribute a \n"
4950 " ON (es.stxrelid = a.attrelid \n"
4951 " AND a.attnum = s.attnum \n"
4952 " AND NOT a.attisdropped)), \n"
4953 "es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4954 gettext_noop("Definition"));
4955
4957 ",\nCASE WHEN " CppAsString2(STATS_EXT_NDISTINCT) " = any(es.stxkind) THEN 'defined' \n"
4958 "END AS \"%s\", \n"
4959 "CASE WHEN " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(es.stxkind) THEN 'defined' \n"
4960 "END AS \"%s\"",
4961 gettext_noop("Ndistinct"),
4962 gettext_noop("Dependencies"));
4963
4964 /*
4965 * Include the MCV statistics kind.
4966 */
4967 if (pset.sversion >= 120000)
4968 {
4970 ",\nCASE WHEN " CppAsString2(STATS_EXT_MCV) " = any(es.stxkind) THEN 'defined' \n"
4971 "END AS \"%s\" ",
4972 gettext_noop("MCV"));
4973 }
4974
4975 if (verbose)
4977 ", \npg_catalog.obj_description(oid, 'pg_statistic_ext') AS \"%s\"\n",
4978 gettext_noop("Description"));
4979
4981 " \nFROM pg_catalog.pg_statistic_ext es \n");
4982
4983 if (!validateSQLNamePattern(&buf, pattern,
4984 false, false,
4985 "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
4986 NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)",
4987 NULL, 3))
4988 {
4990 return false;
4991 }
4992
4993 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4994
4995 res = PSQLexec(buf.data);
4997 if (!res)
4998 return false;
4999
5000 myopt.title = _("List of extended statistics");
5001 myopt.translate_header = true;
5002
5003 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5004
5005 PQclear(res);
5006 return true;
5007}
5008
5009/*
5010 * \dC
5011 *
5012 * Describes casts.
5013 */
5014bool
5015listCasts(const char *pattern, bool verbose)
5016{
5018 PGresult *res;
5020 static const bool translate_columns[] = {false, false, false, true, true, false};
5021
5023
5024 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching casts"));
5026 "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
5027 " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n",
5028 gettext_noop("Source type"),
5029 gettext_noop("Target type"));
5030
5031 /*
5032 * We don't attempt to localize '(binary coercible)' or '(with inout)',
5033 * because there's too much risk of gettext translating a function name
5034 * that happens to match some string in the PO database.
5035 */
5037 " CASE WHEN c.castmethod = '%c' THEN '(binary coercible)'\n"
5038 " WHEN c.castmethod = '%c' THEN '(with inout)'\n"
5039 " ELSE p.proname\n"
5040 " END AS \"%s\",\n",
5043 gettext_noop("Function"));
5044
5046 " CASE WHEN c.castcontext = '%c' THEN '%s'\n"
5047 " WHEN c.castcontext = '%c' THEN '%s'\n"
5048 " ELSE '%s'\n"
5049 " END AS \"%s\"",
5051 gettext_noop("no"),
5053 gettext_noop("in assignment"),
5054 gettext_noop("yes"),
5055 gettext_noop("Implicit?"));
5056
5057 if (verbose)
5059 ",\n CASE WHEN p.proleakproof THEN '%s'\n"
5060 " ELSE '%s'\n"
5061 " END AS \"%s\",\n"
5062 " d.description AS \"%s\"",
5063 gettext_noop("yes"),
5064 gettext_noop("no"),
5065 gettext_noop("Leakproof?"),
5066 gettext_noop("Description"));
5067
5068 /*
5069 * We need a left join to pg_proc for binary casts; the others are just
5070 * paranoia.
5071 */
5073 "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
5074 " ON c.castfunc = p.oid\n"
5075 " LEFT JOIN pg_catalog.pg_type ts\n"
5076 " ON c.castsource = ts.oid\n"
5077 " LEFT JOIN pg_catalog.pg_namespace ns\n"
5078 " ON ns.oid = ts.typnamespace\n"
5079 " LEFT JOIN pg_catalog.pg_type tt\n"
5080 " ON c.casttarget = tt.oid\n"
5081 " LEFT JOIN pg_catalog.pg_namespace nt\n"
5082 " ON nt.oid = tt.typnamespace\n");
5083
5084 if (verbose)
5086 " LEFT JOIN pg_catalog.pg_description d\n"
5087 " ON d.classoid = c.tableoid AND d.objoid = "
5088 "c.oid AND d.objsubid = 0\n");
5089
5090 appendPQExpBufferStr(&buf, "WHERE ( (true");
5091
5092 /*
5093 * Match name pattern against either internal or external name of either
5094 * castsource or casttarget
5095 */
5096 if (!validateSQLNamePattern(&buf, pattern, true, false,
5097 "ns.nspname", "ts.typname",
5098 "pg_catalog.format_type(ts.oid, NULL)",
5099 "pg_catalog.pg_type_is_visible(ts.oid)",
5100 NULL, 3))
5101 goto error_return;
5102
5103 appendPQExpBufferStr(&buf, ") OR (true");
5104
5105 if (!validateSQLNamePattern(&buf, pattern, true, false,
5106 "nt.nspname", "tt.typname",
5107 "pg_catalog.format_type(tt.oid, NULL)",
5108 "pg_catalog.pg_type_is_visible(tt.oid)",
5109 NULL, 3))
5110 goto error_return;
5111
5112 appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
5113
5114 res = PSQLexec(buf.data);
5116 if (!res)
5117 return false;
5118
5119 myopt.title = _("List of casts");
5120 myopt.translate_header = true;
5121 myopt.translate_columns = translate_columns;
5122 myopt.n_translate_columns = lengthof(translate_columns);
5123
5124 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5125
5126 PQclear(res);
5127 return true;
5128
5131 return false;
5132}
5133
5134/*
5135 * \dO
5136 *
5137 * Describes collations.
5138 */
5139bool
5140listCollations(const char *pattern, bool verbose, bool showSystem)
5141{
5143 PGresult *res;
5145 static const bool translate_columns[] = {false, false, false, false, false, false, false, true, false};
5146
5148
5149 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching collations"));
5151 "SELECT\n"
5152 " n.nspname AS \"%s\",\n"
5153 " c.collname AS \"%s\",\n",
5154 gettext_noop("Schema"),
5155 gettext_noop("Name"));
5156
5158 " CASE c.collprovider "
5159 "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
5160 "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
5161 "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
5162 "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
5163 "END AS \"%s\",\n",
5164 gettext_noop("Provider"));
5165
5167 " c.collcollate AS \"%s\",\n"
5168 " c.collctype AS \"%s\",\n",
5169 gettext_noop("Collate"),
5170 gettext_noop("Ctype"));
5171
5172 if (pset.sversion >= 170000)
5174 " c.colllocale AS \"%s\",\n",
5175 gettext_noop("Locale"));
5176 else if (pset.sversion >= 150000)
5178 " c.colliculocale AS \"%s\",\n",
5179 gettext_noop("Locale"));
5180 else
5182 " c.collcollate AS \"%s\",\n",
5183 gettext_noop("Locale"));
5184
5185 if (pset.sversion >= 160000)
5187 " c.collicurules AS \"%s\",\n",
5188 gettext_noop("ICU Rules"));
5189 else
5191 " NULL AS \"%s\",\n",
5192 gettext_noop("ICU Rules"));
5193
5194 if (pset.sversion >= 120000)
5196 " CASE WHEN c.collisdeterministic THEN '%s' ELSE '%s' END AS \"%s\"",
5197 gettext_noop("yes"), gettext_noop("no"),
5198 gettext_noop("Deterministic?"));
5199 else
5201 " '%s' AS \"%s\"",
5202 gettext_noop("yes"),
5203 gettext_noop("Deterministic?"));
5204
5205 if (verbose)
5207 ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
5208 gettext_noop("Description"));
5209
5211 "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
5212 "WHERE n.oid = c.collnamespace\n");
5213
5214 if (!showSystem && !pattern)
5215 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
5216 " AND n.nspname <> 'information_schema'\n");
5217
5218 /*
5219 * Hide collations that aren't usable in the current database's encoding.
5220 * If you think to change this, note that pg_collation_is_visible rejects
5221 * unusable collations, so you will need to hack name pattern processing
5222 * somehow to avoid inconsistent behavior.
5223 */
5224 appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
5225
5226 if (!validateSQLNamePattern(&buf, pattern, true, false,
5227 "n.nspname", "c.collname", NULL,
5228 "pg_catalog.pg_collation_is_visible(c.oid)",
5229 NULL, 3))
5230 {
5232 return false;
5233 }
5234
5235 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5236
5237 res = PSQLexec(buf.data);
5239 if (!res)
5240 return false;
5241
5242 myopt.title = _("List of collations");
5243 myopt.translate_header = true;
5244 myopt.translate_columns = translate_columns;
5245 myopt.n_translate_columns = lengthof(translate_columns);
5246
5247 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5248
5249 PQclear(res);
5250 return true;
5251}
5252
5253/*
5254 * \dn
5255 *
5256 * Describes schemas (namespaces)
5257 */
5258bool
5259listSchemas(const char *pattern, bool verbose, bool showSystem)
5260{
5262 PGresult *res;
5264 int pub_schema_tuples = 0;
5265 char **footers = NULL;
5266
5268
5269 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching schemas"));
5271 "SELECT n.nspname AS \"%s\",\n"
5272 " pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
5273 gettext_noop("Name"),
5274 gettext_noop("Owner"));
5275
5276 if (verbose)
5277 {
5278 appendPQExpBufferStr(&buf, ",\n ");
5279 printACLColumn(&buf, "n.nspacl");
5281 ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
5282 gettext_noop("Description"));
5283 }
5284
5286 "\nFROM pg_catalog.pg_namespace n\n");
5287
5288 if (!showSystem && !pattern)
5290 "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
5291
5292 if (!validateSQLNamePattern(&buf, pattern,
5293 !showSystem && !pattern, false,
5294 NULL, "n.nspname", NULL,
5295 NULL,
5296 NULL, 2))
5297 goto error_return;
5298
5299 appendPQExpBufferStr(&buf, "ORDER BY 1;");
5300
5301 res = PSQLexec(buf.data);
5302 if (!res)
5303 goto error_return;
5304
5305 myopt.title = _("List of schemas");
5306 myopt.translate_header = true;
5307
5308 if (pattern && pset.sversion >= 150000)
5309 {
5311 int i;
5312
5313 printfPQExpBuffer(&buf, "/* %s */\n",
5314 _("Get publications that publish this schema"));
5316 "SELECT pubname \n"
5317 "FROM pg_catalog.pg_publication p\n"
5318 " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
5319 " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
5320 "WHERE n.nspname = '%s'\n"
5321 "ORDER BY 1",
5322 pattern);
5323 result = PSQLexec(buf.data);
5324 if (!result)
5325 goto error_return;
5326 else
5328
5329 if (pub_schema_tuples > 0)
5330 {
5331 /*
5332 * Allocate memory for footers. Size of footers will be 1 (for
5333 * storing "Included in publications:" string) + publication
5334 * schema mapping count + 1 (for storing NULL).
5335 */
5336 footers = pg_malloc_array(char *, 1 + pub_schema_tuples + 1);
5337 footers[0] = pg_strdup(_("Included in publications:"));
5338
5339 /* Might be an empty set - that's ok */
5340 for (i = 0; i < pub_schema_tuples; i++)
5341 {
5342 printfPQExpBuffer(&buf, " \"%s\"",
5343 PQgetvalue(result, i, 0));
5344
5345 footers[i + 1] = pg_strdup(buf.data);
5346 }
5347
5348 footers[i + 1] = NULL;
5349 myopt.footers = footers;
5350 }
5351
5352 PQclear(result);
5353 }
5354
5355 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5356
5358 PQclear(res);
5359
5360 /* Free the memory allocated for the footer */
5361 if (footers)
5362 {
5363 char **footer = NULL;
5364
5365 for (footer = footers; *footer; footer++)
5366 pg_free(*footer);
5367
5368 pg_free(footers);
5369 }
5370
5371 return true;
5372
5375 return false;
5376}
5377
5378
5379/*
5380 * \dFp
5381 * list text search parsers
5382 */
5383bool
5384listTSParsers(const char *pattern, bool verbose)
5385{
5387 PGresult *res;
5389
5390 if (verbose)
5391 return listTSParsersVerbose(pattern);
5392
5394
5395 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search parsers"));
5397 "SELECT\n"
5398 " n.nspname as \"%s\",\n"
5399 " p.prsname as \"%s\",\n"
5400 " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
5401 "FROM pg_catalog.pg_ts_parser p\n"
5402 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
5403 gettext_noop("Schema"),
5404 gettext_noop("Name"),
5405 gettext_noop("Description")
5406 );
5407
5408 if (!validateSQLNamePattern(&buf, pattern, false, false,
5409 "n.nspname", "p.prsname", NULL,
5410 "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5411 NULL, 3))
5412 {
5414 return false;
5415 }
5416
5417 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5418
5419 res = PSQLexec(buf.data);
5421 if (!res)
5422 return false;
5423
5424 myopt.title = _("List of text search parsers");
5425 myopt.translate_header = true;
5426
5427 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5428
5429 PQclear(res);
5430 return true;
5431}
5432
5433/*
5434 * full description of parsers
5435 */
5436static bool
5437listTSParsersVerbose(const char *pattern)
5438{
5440 PGresult *res;
5441 int i;
5442
5444
5445 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search parsers"));
5447 "SELECT p.oid,\n"
5448 " n.nspname,\n"
5449 " p.prsname\n"
5450 "FROM pg_catalog.pg_ts_parser p\n"
5451 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
5452 );
5453
5454 if (!validateSQLNamePattern(&buf, pattern, false, false,
5455 "n.nspname", "p.prsname", NULL,
5456 "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5457 NULL, 3))
5458 {
5460 return false;
5461 }
5462
5463 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5464
5465 res = PSQLexec(buf.data);
5467 if (!res)
5468 return false;
5469
5470 if (PQntuples(res) == 0)
5471 {
5472 if (!pset.quiet)
5473 {
5474 if (pattern)
5475 pg_log_error("Did not find any text search parser named \"%s\".",
5476 pattern);
5477 else
5478 pg_log_error("Did not find any text search parsers.");
5479 }
5480 PQclear(res);
5481 return false;
5482 }
5483
5484 for (i = 0; i < PQntuples(res); i++)
5485 {
5486 const char *oid;
5487 const char *nspname = NULL;
5488 const char *prsname;
5489
5490 oid = PQgetvalue(res, i, 0);
5491 if (!PQgetisnull(res, i, 1))
5492 nspname = PQgetvalue(res, i, 1);
5493 prsname = PQgetvalue(res, i, 2);
5494
5495 if (!describeOneTSParser(oid, nspname, prsname))
5496 {
5497 PQclear(res);
5498 return false;
5499 }
5500
5501 if (cancel_pressed)
5502 {
5503 PQclear(res);
5504 return false;
5505 }
5506 }
5507
5508 PQclear(res);
5509 return true;
5510}
5511
5512static bool
5513describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
5514{
5516 PGresult *res;
5517 PQExpBufferData title;
5519 static const bool translate_columns[] = {true, false, false};
5520
5522
5523 printfPQExpBuffer(&buf, "/* %s */\n", _("Get text search parser details"));
5525 "SELECT '%s' AS \"%s\",\n"
5526 " p.prsstart::pg_catalog.regproc AS \"%s\",\n"
5527 " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\"\n"
5528 " FROM pg_catalog.pg_ts_parser p\n"
5529 " WHERE p.oid = '%s'\n"
5530 "UNION ALL\n"
5531 "SELECT '%s',\n"
5532 " p.prstoken::pg_catalog.regproc,\n"
5533 " pg_catalog.obj_description(p.prstoken, 'pg_proc')\n"
5534 " FROM pg_catalog.pg_ts_parser p\n"
5535 " WHERE p.oid = '%s'\n"
5536 "UNION ALL\n"
5537 "SELECT '%s',\n"
5538 " p.prsend::pg_catalog.regproc,\n"
5539 " pg_catalog.obj_description(p.prsend, 'pg_proc')\n"
5540 " FROM pg_catalog.pg_ts_parser p\n"
5541 " WHERE p.oid = '%s'\n"
5542 "UNION ALL\n"
5543 "SELECT '%s',\n"
5544 " p.prsheadline::pg_catalog.regproc,\n"
5545 " pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n"
5546 " FROM pg_catalog.pg_ts_parser p\n"
5547 " WHERE p.oid = '%s'\n"
5548 "UNION ALL\n"
5549 "SELECT '%s',\n"
5550 " p.prslextype::pg_catalog.regproc,\n"
5551 " pg_catalog.obj_description(p.prslextype, 'pg_proc')\n"
5552 " FROM pg_catalog.pg_ts_parser p\n"
5553 " WHERE p.oid = '%s';",
5554 gettext_noop("Start parse"),
5555 gettext_noop("Method"),
5556 gettext_noop("Function"),
5557 gettext_noop("Description"),
5558 oid,
5559 gettext_noop("Get next token"),
5560 oid,
5561 gettext_noop("End parse"),
5562 oid,
5563 gettext_noop("Get headline"),
5564 oid,
5565 gettext_noop("Get token types"),
5566 oid);
5567
5568 res = PSQLexec(buf.data);
5570 if (!res)
5571 return false;
5572
5573 initPQExpBuffer(&title);
5574 if (nspname)
5575 printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""),
5576 nspname, prsname);
5577 else
5578 printfPQExpBuffer(&title, _("Text search parser \"%s\""), prsname);
5579 myopt.title = title.data;
5580 myopt.footers = NULL;
5581 myopt.topt.default_footer = false;
5582 myopt.translate_header = true;
5583 myopt.translate_columns = translate_columns;
5584 myopt.n_translate_columns = lengthof(translate_columns);
5585
5586 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5587
5588 PQclear(res);
5589
5591
5592 printfPQExpBuffer(&buf, "/* %s */\n",
5593 _("Get text search parser token types"));
5595 "SELECT t.alias as \"%s\",\n"
5596 " t.description as \"%s\"\n"
5597 "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t\n"
5598 "ORDER BY 1;",
5599 gettext_noop("Token name"),
5600 gettext_noop("Description"),
5601 oid);
5602
5603 res = PSQLexec(buf.data);
5605 if (!res)
5606 {
5607 termPQExpBuffer(&title);
5608 return false;
5609 }
5610
5611 if (nspname)
5612 printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""),
5613 nspname, prsname);
5614 else
5615 printfPQExpBuffer(&title, _("Token types for parser \"%s\""), prsname);
5616 myopt.title = title.data;
5617 myopt.footers = NULL;
5618 myopt.topt.default_footer = true;
5619 myopt.translate_header = true;
5620 myopt.translate_columns = NULL;
5621 myopt.n_translate_columns = 0;
5622
5623 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5624
5625 termPQExpBuffer(&title);
5626 PQclear(res);
5627 return true;
5628}
5629
5630
5631/*
5632 * \dFd
5633 * list text search dictionaries
5634 */
5635bool
5636listTSDictionaries(const char *pattern, bool verbose)
5637{
5639 PGresult *res;
5641
5643
5644 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search dictionaries"));
5646 "SELECT\n"
5647 " n.nspname as \"%s\",\n"
5648 " d.dictname as \"%s\",\n",
5649 gettext_noop("Schema"),
5650 gettext_noop("Name"));
5651
5652 if (verbose)
5653 {
5655 " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM\n"
5656 " pg_catalog.pg_ts_template t\n"
5657 " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace\n"
5658 " WHERE d.dicttemplate = t.oid ) AS \"%s\",\n"
5659 " d.dictinitoption as \"%s\",\n",
5660 gettext_noop("Template"),
5661 gettext_noop("Init options"));
5662 }
5663
5665 " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
5666 gettext_noop("Description"));
5667
5668 appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n"
5669 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
5670
5671 if (!validateSQLNamePattern(&buf, pattern, false, false,
5672 "n.nspname", "d.dictname", NULL,
5673 "pg_catalog.pg_ts_dict_is_visible(d.oid)",
5674 NULL, 3))
5675 {
5677 return false;
5678 }
5679
5680 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5681
5682 res = PSQLexec(buf.data);
5684 if (!res)
5685 return false;
5686
5687 myopt.title = _("List of text search dictionaries");
5688 myopt.translate_header = true;
5689
5690 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5691
5692 PQclear(res);
5693 return true;
5694}
5695
5696
5697/*
5698 * \dFt
5699 * list text search templates
5700 */
5701bool
5702listTSTemplates(const char *pattern, bool verbose)
5703{
5705 PGresult *res;
5707
5709
5710 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search templates"));
5711 if (verbose)
5713 "SELECT\n"
5714 " n.nspname AS \"%s\",\n"
5715 " t.tmplname AS \"%s\",\n"
5716 " t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
5717 " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
5718 " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5719 gettext_noop("Schema"),
5720 gettext_noop("Name"),
5721 gettext_noop("Init"),
5722 gettext_noop("Lexize"),
5723 gettext_noop("Description"));
5724 else
5726 "SELECT\n"
5727 " n.nspname AS \"%s\",\n"
5728 " t.tmplname 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("Description"));
5733
5734 appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n"
5735 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
5736
5737 if (!validateSQLNamePattern(&buf, pattern, false, false,
5738 "n.nspname", "t.tmplname", NULL,
5739 "pg_catalog.pg_ts_template_is_visible(t.oid)",
5740 NULL, 3))
5741 {
5743 return false;
5744 }
5745
5746 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5747
5748 res = PSQLexec(buf.data);
5750 if (!res)
5751 return false;
5752
5753 myopt.title = _("List of text search templates");
5754 myopt.translate_header = true;
5755
5756 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5757
5758 PQclear(res);
5759 return true;
5760}
5761
5762
5763/*
5764 * \dF
5765 * list text search configurations
5766 */
5767bool
5768listTSConfigs(const char *pattern, bool verbose)
5769{
5771 PGresult *res;
5773
5774 if (verbose)
5775 return listTSConfigsVerbose(pattern);
5776
5778
5779 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search configurations"));
5781 "SELECT\n"
5782 " n.nspname as \"%s\",\n"
5783 " c.cfgname as \"%s\",\n"
5784 " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
5785 "FROM pg_catalog.pg_ts_config c\n"
5786 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace\n",
5787 gettext_noop("Schema"),
5788 gettext_noop("Name"),
5789 gettext_noop("Description")
5790 );
5791
5792 if (!validateSQLNamePattern(&buf, pattern, false, false,
5793 "n.nspname", "c.cfgname", NULL,
5794 "pg_catalog.pg_ts_config_is_visible(c.oid)",
5795 NULL, 3))
5796 {
5798 return false;
5799 }
5800
5801 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5802
5803 res = PSQLexec(buf.data);
5805 if (!res)
5806 return false;
5807
5808 myopt.title = _("List of text search configurations");
5809 myopt.translate_header = true;
5810
5811 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5812
5813 PQclear(res);
5814 return true;
5815}
5816
5817static bool
5818listTSConfigsVerbose(const char *pattern)
5819{
5821 PGresult *res;
5822 int i;
5823
5825
5826 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search configurations"));
5828 "SELECT c.oid, c.cfgname,\n"
5829 " n.nspname,\n"
5830 " p.prsname,\n"
5831 " np.nspname as pnspname\n"
5832 "FROM pg_catalog.pg_ts_config c\n"
5833 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace,\n"
5834 " pg_catalog.pg_ts_parser p\n"
5835 " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace\n"
5836 "WHERE p.oid = c.cfgparser\n"
5837 );
5838
5839 if (!validateSQLNamePattern(&buf, pattern, true, false,
5840 "n.nspname", "c.cfgname", NULL,
5841 "pg_catalog.pg_ts_config_is_visible(c.oid)",
5842 NULL, 3))
5843 {
5845 return false;
5846 }
5847
5848 appendPQExpBufferStr(&buf, "ORDER BY 3, 2;");
5849
5850 res = PSQLexec(buf.data);
5852 if (!res)
5853 return false;
5854
5855 if (PQntuples(res) == 0)
5856 {
5857 if (!pset.quiet)
5858 {
5859 if (pattern)
5860 pg_log_error("Did not find any text search configuration named \"%s\".",
5861 pattern);
5862 else
5863 pg_log_error("Did not find any text search configurations.");
5864 }
5865 PQclear(res);
5866 return false;
5867 }
5868
5869 for (i = 0; i < PQntuples(res); i++)
5870 {
5871 const char *oid;
5872 const char *cfgname;
5873 const char *nspname = NULL;
5874 const char *prsname;
5875 const char *pnspname = NULL;
5876
5877 oid = PQgetvalue(res, i, 0);
5878 cfgname = PQgetvalue(res, i, 1);
5879 if (!PQgetisnull(res, i, 2))
5880 nspname = PQgetvalue(res, i, 2);
5881 prsname = PQgetvalue(res, i, 3);
5882 if (!PQgetisnull(res, i, 4))
5883 pnspname = PQgetvalue(res, i, 4);
5884
5885 if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
5886 {
5887 PQclear(res);
5888 return false;
5889 }
5890
5891 if (cancel_pressed)
5892 {
5893 PQclear(res);
5894 return false;
5895 }
5896 }
5897
5898 PQclear(res);
5899 return true;
5900}
5901
5902static bool
5903describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
5904 const char *pnspname, const char *prsname)
5905{
5907 title;
5908 PGresult *res;
5910
5912
5913 printfPQExpBuffer(&buf, "/* %s */\n", _("Get text search configuration details"));
5915 "SELECT\n"
5916 " ( SELECT t.alias FROM\n"
5917 " pg_catalog.ts_token_type(c.cfgparser) AS t\n"
5918 " WHERE t.tokid = m.maptokentype ) AS \"%s\",\n"
5919 " pg_catalog.btrim(\n"
5920 " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary\n"
5921 " FROM pg_catalog.pg_ts_config_map AS mm\n"
5922 " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype\n"
5923 " ORDER BY mapcfg, maptokentype, mapseqno\n"
5924 " ) :: pg_catalog.text,\n"
5925 " '{}') AS \"%s\"\n"
5926 "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m\n"
5927 "WHERE c.oid = '%s' AND m.mapcfg = c.oid\n"
5928 "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser\n"
5929 "ORDER BY 1;",
5930 gettext_noop("Token"),
5931 gettext_noop("Dictionaries"),
5932 oid);
5933
5934 res = PSQLexec(buf.data);
5936 if (!res)
5937 return false;
5938
5939 initPQExpBuffer(&title);
5940
5941 if (nspname)
5942 appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
5943 nspname, cfgname);
5944 else
5945 appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
5946 cfgname);
5947
5948 if (pnspname)
5949 appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
5950 pnspname, prsname);
5951 else
5952 appendPQExpBuffer(&title, _("\nParser: \"%s\""),
5953 prsname);
5954
5955 myopt.title = title.data;
5956 myopt.footers = NULL;
5957 myopt.topt.default_footer = false;
5958 myopt.translate_header = true;
5959
5960 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5961
5962 termPQExpBuffer(&title);
5963
5964 PQclear(res);
5965 return true;
5966}
5967
5968
5969/*
5970 * \dew
5971 *
5972 * Describes foreign-data wrappers
5973 */
5974bool
5975listForeignDataWrappers(const char *pattern, bool verbose)
5976{
5978 PGresult *res;
5980
5982
5983 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign-data wrappers"));
5985 "SELECT fdw.fdwname AS \"%s\",\n"
5986 " pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n"
5987 " fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n"
5988 " fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
5989 gettext_noop("Name"),
5990 gettext_noop("Owner"),
5991 gettext_noop("Handler"),
5992 gettext_noop("Validator"));
5993
5994 if (verbose)
5995 {
5996 appendPQExpBufferStr(&buf, ",\n ");
5997 printACLColumn(&buf, "fdwacl");
5999 ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE "
6000 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6001 " pg_catalog.quote_ident(option_name) || ' ' || "
6002 " pg_catalog.quote_literal(option_value) FROM "
6003 " pg_catalog.pg_options_to_table(fdwoptions)), ', ') || ')' "
6004 " END AS \"%s\""
6005 ",\n d.description AS \"%s\" ",
6006 gettext_noop("FDW options"),
6007 gettext_noop("Description"));
6008 }
6009
6010 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
6011
6012 if (verbose)
6014 "LEFT JOIN pg_catalog.pg_description d\n"
6015 " ON d.classoid = fdw.tableoid "
6016 "AND d.objoid = fdw.oid AND d.objsubid = 0\n");
6017
6018 if (!validateSQLNamePattern(&buf, pattern, false, false,
6019 NULL, "fdwname", NULL, NULL,
6020 NULL, 1))
6021 {
6023 return false;
6024 }
6025
6026 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6027
6028 res = PSQLexec(buf.data);
6030 if (!res)
6031 return false;
6032
6033 myopt.title = _("List of foreign-data wrappers");
6034 myopt.translate_header = true;
6035
6036 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6037
6038 PQclear(res);
6039 return true;
6040}
6041
6042/*
6043 * \des
6044 *
6045 * Describes foreign servers.
6046 */
6047bool
6048listForeignServers(const char *pattern, bool verbose)
6049{
6051 PGresult *res;
6053
6055
6056 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign servers"));
6058 "SELECT s.srvname AS \"%s\",\n"
6059 " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
6060 " f.fdwname AS \"%s\"",
6061 gettext_noop("Name"),
6062 gettext_noop("Owner"),
6063 gettext_noop("Foreign-data wrapper"));
6064
6065 if (verbose)
6066 {
6067 appendPQExpBufferStr(&buf, ",\n ");
6068 printACLColumn(&buf, "s.srvacl");
6070 ",\n"
6071 " s.srvtype AS \"%s\",\n"
6072 " s.srvversion AS \"%s\",\n"
6073 " CASE WHEN srvoptions IS NULL THEN '' ELSE "
6074 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6075 " pg_catalog.quote_ident(option_name) || ' ' || "
6076 " pg_catalog.quote_literal(option_value) FROM "
6077 " pg_catalog.pg_options_to_table(srvoptions)), ', ') || ')' "
6078 " END AS \"%s\",\n"
6079 " d.description AS \"%s\"",
6080 gettext_noop("Type"),
6081 gettext_noop("Version"),
6082 gettext_noop("FDW options"),
6083 gettext_noop("Description"));
6084 }
6085
6087 "\nFROM pg_catalog.pg_foreign_server s\n"
6088 " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
6089
6090 if (verbose)
6092 "LEFT JOIN pg_catalog.pg_description d\n "
6093 "ON d.classoid = s.tableoid AND d.objoid = s.oid "
6094 "AND d.objsubid = 0\n");
6095
6096 if (!validateSQLNamePattern(&buf, pattern, false, false,
6097 NULL, "s.srvname", NULL, NULL,
6098 NULL, 1))
6099 {
6101 return false;
6102 }
6103
6104 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6105
6106 res = PSQLexec(buf.data);
6108 if (!res)
6109 return false;
6110
6111 myopt.title = _("List of foreign servers");
6112 myopt.translate_header = true;
6113
6114 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6115
6116 PQclear(res);
6117 return true;
6118}
6119
6120/*
6121 * \deu
6122 *
6123 * Describes user mappings.
6124 */
6125bool
6126listUserMappings(const char *pattern, bool verbose)
6127{
6129 PGresult *res;
6131
6133
6134 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching user mappings"));
6136 "SELECT um.srvname AS \"%s\",\n"
6137 " um.usename AS \"%s\"",
6138 gettext_noop("Server"),
6139 gettext_noop("User name"));
6140
6141 if (verbose)
6143 ",\n CASE WHEN umoptions IS NULL THEN '' ELSE "
6144 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6145 " pg_catalog.quote_ident(option_name) || ' ' || "
6146 " pg_catalog.quote_literal(option_value) FROM "
6147 " pg_catalog.pg_options_to_table(umoptions)), ', ') || ')' "
6148 " END AS \"%s\"",
6149 gettext_noop("FDW options"));
6150
6151 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
6152
6153 if (!validateSQLNamePattern(&buf, pattern, false, false,
6154 NULL, "um.srvname", "um.usename", NULL,
6155 NULL, 1))
6156 {
6158 return false;
6159 }
6160
6161 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6162
6163 res = PSQLexec(buf.data);
6165 if (!res)
6166 return false;
6167
6168 myopt.title = _("List of user mappings");
6169 myopt.translate_header = true;
6170
6171 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6172
6173 PQclear(res);
6174 return true;
6175}
6176
6177/*
6178 * \det
6179 *
6180 * Describes foreign tables.
6181 */
6182bool
6183listForeignTables(const char *pattern, bool verbose)
6184{
6186 PGresult *res;
6188
6190
6191 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign tables"));
6193 "SELECT n.nspname AS \"%s\",\n"
6194 " c.relname AS \"%s\",\n"
6195 " s.srvname AS \"%s\"",
6196 gettext_noop("Schema"),
6197 gettext_noop("Table"),
6198 gettext_noop("Server"));
6199
6200 if (verbose)
6202 ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE "
6203 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6204 " pg_catalog.quote_ident(option_name) || ' ' || "
6205 " pg_catalog.quote_literal(option_value) FROM "
6206 " pg_catalog.pg_options_to_table(ftoptions)), ', ') || ')' "
6207 " END AS \"%s\",\n"
6208 " d.description AS \"%s\"",
6209 gettext_noop("FDW options"),
6210 gettext_noop("Description"));
6211
6213 "\nFROM pg_catalog.pg_foreign_table ft\n"
6214 " INNER JOIN pg_catalog.pg_class c"
6215 " ON c.oid = ft.ftrelid\n"
6216 " INNER JOIN pg_catalog.pg_namespace n"
6217 " ON n.oid = c.relnamespace\n"
6218 " INNER JOIN pg_catalog.pg_foreign_server s"
6219 " ON s.oid = ft.ftserver\n");
6220 if (verbose)
6222 " LEFT JOIN pg_catalog.pg_description d\n"
6223 " ON d.classoid = c.tableoid AND "
6224 "d.objoid = c.oid AND d.objsubid = 0\n");
6225
6226 if (!validateSQLNamePattern(&buf, pattern, false, false,
6227 "n.nspname", "c.relname", NULL,
6228 "pg_catalog.pg_table_is_visible(c.oid)",
6229 NULL, 3))
6230 {
6232 return false;
6233 }
6234
6235 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6236
6237 res = PSQLexec(buf.data);
6239 if (!res)
6240 return false;
6241
6242 myopt.title = _("List of foreign tables");
6243 myopt.translate_header = true;
6244
6245 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6246
6247 PQclear(res);
6248 return true;
6249}
6250
6251/*
6252 * \dx
6253 *
6254 * Briefly describes installed extensions.
6255 */
6256bool
6257listExtensions(const char *pattern)
6258{
6260 PGresult *res;
6262
6264
6265 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching installed extensions"));
6267 "SELECT e.extname AS \"%s\", "
6268 "e.extversion AS \"%s\", ae.default_version AS \"%s\","
6269 "n.nspname AS \"%s\", d.description AS \"%s\"\n"
6270 "FROM pg_catalog.pg_extension e "
6271 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
6272 "LEFT JOIN pg_catalog.pg_description d ON d.objoid = e.oid "
6273 "AND d.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
6274 "LEFT JOIN pg_catalog.pg_available_extensions() ae(name, default_version, comment) ON ae.name = e.extname\n",
6275 gettext_noop("Name"),
6276 gettext_noop("Version"),
6277 gettext_noop("Default version"),
6278 gettext_noop("Schema"),
6279 gettext_noop("Description"));
6280
6281 if (!validateSQLNamePattern(&buf, pattern,
6282 false, false,
6283 NULL, "e.extname", NULL,
6284 NULL,
6285 NULL, 1))
6286 {
6288 return false;
6289 }
6290
6291 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6292
6293 res = PSQLexec(buf.data);
6295 if (!res)
6296 return false;
6297
6298 myopt.title = _("List of installed extensions");
6299 myopt.translate_header = true;
6300
6301 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6302
6303 PQclear(res);
6304 return true;
6305}
6306
6307/*
6308 * \dx+
6309 *
6310 * List contents of installed extensions.
6311 */
6312bool
6313listExtensionContents(const char *pattern)
6314{
6316 PGresult *res;
6317 int i;
6318
6320
6321 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching installed extensions"));
6323 "SELECT e.extname, e.oid\n"
6324 "FROM pg_catalog.pg_extension e\n");
6325
6326 if (!validateSQLNamePattern(&buf, pattern,
6327 false, false,
6328 NULL, "e.extname", NULL,
6329 NULL,
6330 NULL, 1))
6331 {
6333 return false;
6334 }
6335
6336 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6337
6338 res = PSQLexec(buf.data);
6340 if (!res)
6341 return false;
6342
6343 if (PQntuples(res) == 0)
6344 {
6345 if (!pset.quiet)
6346 {
6347 if (pattern)
6348 pg_log_error("Did not find any extension named \"%s\".",
6349 pattern);
6350 else
6351 pg_log_error("Did not find any extensions.");
6352 }
6353 PQclear(res);
6354 return false;
6355 }
6356
6357 for (i = 0; i < PQntuples(res); i++)
6358 {
6359 const char *extname;
6360 const char *oid;
6361
6362 extname = PQgetvalue(res, i, 0);
6363 oid = PQgetvalue(res, i, 1);
6364
6365 if (!listOneExtensionContents(extname, oid))
6366 {
6367 PQclear(res);
6368 return false;
6369 }
6370 if (cancel_pressed)
6371 {
6372 PQclear(res);
6373 return false;
6374 }
6375 }
6376
6377 PQclear(res);
6378 return true;
6379}
6380
6381static bool
6382listOneExtensionContents(const char *extname, const char *oid)
6383{
6385 PGresult *res;
6386 PQExpBufferData title;
6388
6390
6391 printfPQExpBuffer(&buf, "/* %s */\n", _("Get installed extension's contents"));
6393 "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
6394 "FROM pg_catalog.pg_depend\n"
6395 "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
6396 "ORDER BY 1;",
6397 gettext_noop("Object description"),
6398 oid);
6399
6400 res = PSQLexec(buf.data);
6402 if (!res)
6403 return false;
6404
6405 initPQExpBuffer(&title);
6406 printfPQExpBuffer(&title, _("Objects in extension \"%s\""), extname);
6407 myopt.title = title.data;
6408 myopt.translate_header = true;
6409
6410 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6411
6412 termPQExpBuffer(&title);
6413 PQclear(res);
6414 return true;
6415}
6416
6417/*
6418 * validateSQLNamePattern
6419 *
6420 * Wrapper around string_utils's processSQLNamePattern which also checks the
6421 * pattern's validity. In addition to that function's parameters, takes a
6422 * 'maxparts' parameter specifying the maximum number of dotted names the
6423 * pattern is allowed to have, and a 'added_clause' parameter that returns by
6424 * reference whether a clause was added to 'buf'. Returns whether the pattern
6425 * passed validation, after logging any errors.
6426 */
6427static bool
6429 bool force_escape, const char *schemavar,
6430 const char *namevar, const char *altnamevar,
6431 const char *visibilityrule, bool *added_clause,
6432 int maxparts)
6433{
6435 int dotcnt;
6436 bool added;
6437
6442 if (added_clause != NULL)
6444
6445 if (dotcnt >= maxparts)
6446 {
6447 pg_log_error("improper qualified name (too many dotted names): %s",
6448 pattern);
6449 goto error_return;
6450 }
6451
6452 if (maxparts > 1 && dotcnt == maxparts - 1)
6453 {
6454 if (PQdb(pset.db) == NULL)
6455 {
6456 pg_log_error("You are currently not connected to a database.");
6457 goto error_return;
6458 }
6459 if (strcmp(PQdb(pset.db), dbbuf.data) != 0)
6460 {
6461 pg_log_error("cross-database references are not implemented: %s",
6462 pattern);
6463 goto error_return;
6464 }
6465 }
6467 return true;
6468
6471 return false;
6472}
6473
6474/*
6475 * \dRp
6476 * Lists publications.
6477 *
6478 * Takes an optional regexp to select particular publications
6479 */
6480bool
6481listPublications(const char *pattern)
6482{
6484 PGresult *res;
6486 static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
6487
6489
6490 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching publications"));
6492 "SELECT pubname AS \"%s\",\n"
6493 " pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
6494 " puballtables AS \"%s\"",
6495 gettext_noop("Name"),
6496 gettext_noop("Owner"),
6497 gettext_noop("All tables"));
6498
6499 if (pset.sversion >= 190000)
6501 ",\n puballsequences AS \"%s\"",
6502 gettext_noop("All sequences"));
6503
6505 ",\n pubinsert AS \"%s\",\n"
6506 " pubupdate AS \"%s\",\n"
6507 " pubdelete AS \"%s\"",
6508 gettext_noop("Inserts"),
6509 gettext_noop("Updates"),
6510 gettext_noop("Deletes"));
6511 if (pset.sversion >= 110000)
6513 ",\n pubtruncate AS \"%s\"",
6514 gettext_noop("Truncates"));
6515 if (pset.sversion >= 180000)
6517 ",\n (CASE pubgencols\n"
6518 " WHEN '%c' THEN 'none'\n"
6519 " WHEN '%c' THEN 'stored'\n"
6520 " END) AS \"%s\"",
6523 gettext_noop("Generated columns"));
6524 if (pset.sversion >= 130000)
6526 ",\n pubviaroot AS \"%s\"",
6527 gettext_noop("Via root"));
6528
6530 "\nFROM pg_catalog.pg_publication\n");
6531
6532 if (!validateSQLNamePattern(&buf, pattern, false, false,
6533 NULL, "pubname", NULL,
6534 NULL,
6535 NULL, 1))
6536 {
6538 return false;
6539 }
6540
6541 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6542
6543 res = PSQLexec(buf.data);
6545 if (!res)
6546 return false;
6547
6548 myopt.title = _("List of publications");
6549 myopt.translate_header = true;
6550 myopt.translate_columns = translate_columns;
6551 myopt.n_translate_columns = lengthof(translate_columns);
6552
6553 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6554
6555 PQclear(res);
6556
6557 return true;
6558}
6559
6560/*
6561 * Add footer to publication description.
6562 */
6563static bool
6565 bool as_schema, printTableContent *const cont)
6566{
6567 PGresult *res;
6568 int count = 0;
6569 int i = 0;
6570
6571 res = PSQLexec(buf->data);
6572 if (!res)
6573 return false;
6574 else
6575 count = PQntuples(res);
6576
6577 if (count > 0)
6579
6580 for (i = 0; i < count; i++)
6581 {
6582 if (as_schema)
6583 printfPQExpBuffer(buf, " \"%s\"", PQgetvalue(res, i, 0));
6584 else
6585 {
6586 printfPQExpBuffer(buf, " \"%s.%s\"", PQgetvalue(res, i, 0),
6587 PQgetvalue(res, i, 1));
6588
6589 if (!PQgetisnull(res, i, 3))
6590 appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
6591
6592 if (!PQgetisnull(res, i, 2))
6593 appendPQExpBuffer(buf, " WHERE %s", PQgetvalue(res, i, 2));
6594 }
6595
6597 }
6598
6599 PQclear(res);
6600 return true;
6601}
6602
6603/*
6604 * \dRp+
6605 * Describes publications including the contents.
6606 *
6607 * Takes an optional regexp to select particular publications
6608 */
6609bool
6610describePublications(const char *pattern)
6611{
6613 int i;
6614 PGresult *res;
6615 bool has_pubtruncate;
6616 bool has_pubgencols;
6617 bool has_pubviaroot;
6618 bool has_pubsequence;
6619 int ncols = 6;
6620 int nrows = 1;
6621
6622 PQExpBufferData title;
6624
6625 has_pubsequence = (pset.sversion >= 190000);
6626 has_pubtruncate = (pset.sversion >= 110000);
6627 has_pubgencols = (pset.sversion >= 180000);
6628 has_pubviaroot = (pset.sversion >= 130000);
6629
6631
6632 printfPQExpBuffer(&buf, "/* %s */\n", _("Get details about matching publications"));
6634 "SELECT oid, pubname,\n"
6635 " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
6636 " puballtables");
6637
6638 if (has_pubsequence)
6640 ", puballsequences");
6641 else
6643 ", false AS puballsequences");
6644
6646 ", pubinsert, pubupdate, pubdelete");
6647
6648 if (has_pubtruncate)
6650 ", pubtruncate");
6651 else
6653 ", false AS pubtruncate");
6654
6655 if (has_pubgencols)
6657 ", (CASE pubgencols\n"
6658 " WHEN '%c' THEN 'none'\n"
6659 " WHEN '%c' THEN 'stored'\n"
6660 " END) AS \"%s\"\n",
6663 gettext_noop("Generated columns"));
6664 else
6666 ", 'none' AS pubgencols");
6667
6668 if (has_pubviaroot)
6670 ", pubviaroot");
6671 else
6673 ", false AS pubviaroot");
6674
6676 ", pg_catalog.obj_description(oid, 'pg_publication')");
6677
6679 "\nFROM pg_catalog.pg_publication\n");
6680
6681 if (!validateSQLNamePattern(&buf, pattern, false, false,
6682 NULL, "pubname", NULL,
6683 NULL,
6684 NULL, 1))
6685 {
6687 return false;
6688 }
6689
6690 appendPQExpBufferStr(&buf, "ORDER BY 2;");
6691
6692 res = PSQLexec(buf.data);
6693 if (!res)
6694 {
6696 return false;
6697 }
6698
6699 if (PQntuples(res) == 0)
6700 {
6701 if (!pset.quiet)
6702 {
6703 if (pattern)
6704 pg_log_error("Did not find any publication named \"%s\".",
6705 pattern);
6706 else
6707 pg_log_error("Did not find any publications.");
6708 }
6709
6711 PQclear(res);
6712 return false;
6713 }
6714
6715 if (has_pubsequence)
6716 ncols++;
6717 if (has_pubtruncate)
6718 ncols++;
6719 if (has_pubgencols)
6720 ncols++;
6721 if (has_pubviaroot)
6722 ncols++;
6723
6724 for (i = 0; i < PQntuples(res); i++)
6725 {
6726 const char align = 'l';
6727 char *pubid = PQgetvalue(res, i, 0);
6728 char *pubname = PQgetvalue(res, i, 1);
6729 bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
6731
6732 initPQExpBuffer(&title);
6733 printfPQExpBuffer(&title, _("Publication %s"), pubname);
6734 printTableInit(&cont, &myopt, title.data, ncols, nrows);
6735
6736 printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
6737 printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
6738 if (has_pubsequence)
6739 printTableAddHeader(&cont, gettext_noop("All sequences"), true, align);
6740 printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
6741 printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
6742 printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
6743 if (has_pubtruncate)
6744 printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
6745 if (has_pubgencols)
6746 printTableAddHeader(&cont, gettext_noop("Generated columns"), true, align);
6747 if (has_pubviaroot)
6748 printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
6749 printTableAddHeader(&cont, gettext_noop("Description"), true, align);
6750
6751 printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
6752 printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
6753 if (has_pubsequence)
6754 printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
6755 printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
6756 printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
6757 printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
6758 if (has_pubtruncate)
6759 printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
6760 if (has_pubgencols)
6761 printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
6762 if (has_pubviaroot)
6763 printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false);
6764 printTableAddCell(&cont, PQgetvalue(res, i, 11), false, false);
6765
6766 if (!puballtables)
6767 {
6768 /* Get the tables for the specified publication */
6769 printfPQExpBuffer(&buf, "/* %s */\n",
6770 _("Get tables published by this publication"));
6771 appendPQExpBufferStr(&buf, "SELECT n.nspname, c.relname");
6772 if (pset.sversion >= 150000)
6773 {
6775 ", pg_catalog.pg_get_expr(pr.prqual, c.oid)");
6777 ", (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
6778 " pg_catalog.array_to_string("
6779 " ARRAY(SELECT attname\n"
6780 " FROM\n"
6781 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
6782 " pg_catalog.pg_attribute\n"
6783 " WHERE attrelid = c.oid AND attnum = prattrs[s]), ', ')\n"
6784 " ELSE NULL END)");
6785 }
6786 else
6788 ", NULL, NULL");
6790 "\nFROM pg_catalog.pg_class c,\n"
6791 " pg_catalog.pg_namespace n,\n"
6792 " pg_catalog.pg_publication_rel pr\n"
6793 "WHERE c.relnamespace = n.oid\n"
6794 " AND c.oid = pr.prrelid\n"
6795 " AND pr.prpubid = '%s'\n", pubid);
6796
6797 if (pset.sversion >= 190000)
6798 appendPQExpBufferStr(&buf, " AND NOT pr.prexcept\n");
6799
6800 appendPQExpBufferStr(&buf, "ORDER BY 1,2");
6801 if (!addFooterToPublicationDesc(&buf, _("Tables:"), false, &cont))
6802 goto error_return;
6803
6804 if (pset.sversion >= 150000)
6805 {
6806 /* Get the schemas for the specified publication */
6807 printfPQExpBuffer(&buf, "/* %s */\n",
6808 _("Get schemas published by this publication"));
6810 "SELECT n.nspname\n"
6811 "FROM pg_catalog.pg_namespace n\n"
6812 " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
6813 "WHERE pn.pnpubid = '%s'\n"
6814 "ORDER BY 1", pubid);
6815 if (!addFooterToPublicationDesc(&buf, _("Tables from schemas:"),
6816 true, &cont))
6817 goto error_return;
6818 }
6819 }
6820 else
6821 {
6822 if (pset.sversion >= 190000)
6823 {
6824 /* Get tables in the EXCEPT clause for this publication */
6825 printfPQExpBuffer(&buf, "/* %s */\n",
6826 _("Get tables excluded by this publication"));
6828 "SELECT n.nspname || '.' || c.relname\n"
6829 "FROM pg_catalog.pg_class c\n"
6830 " JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
6831 " JOIN pg_catalog.pg_publication_rel pr ON c.oid = pr.prrelid\n"
6832 "WHERE pr.prpubid = '%s' AND pr.prexcept\n"
6833 "ORDER BY 1", pubid);
6834 if (!addFooterToPublicationDesc(&buf, _("Except tables:"),
6835 true, &cont))
6836 goto error_return;
6837 }
6838 }
6839
6842
6843 termPQExpBuffer(&title);
6844 }
6845
6847 PQclear(res);
6848
6849 return true;
6850
6853 PQclear(res);
6855 termPQExpBuffer(&title);
6856 return false;
6857}
6858
6859/*
6860 * \dRs
6861 * Describes subscriptions.
6862 *
6863 * Takes an optional regexp to select particular subscriptions
6864 */
6865bool
6866describeSubscriptions(const char *pattern, bool verbose)
6867{
6869 PGresult *res;
6871 static const bool translate_columns[] = {false, false, false, false,
6872 false, false, false, false, false, false, false, false, false, false,
6873 false, false, false, false, false, false, false};
6874
6876
6877 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching subscriptions"));
6879 "SELECT subname AS \"%s\"\n"
6880 ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
6881 ", subenabled AS \"%s\"\n"
6882 ", subpublications AS \"%s\"\n",
6883 gettext_noop("Name"),
6884 gettext_noop("Owner"),
6885 gettext_noop("Enabled"),
6886 gettext_noop("Publication"));
6887
6888 if (verbose)
6889 {
6890 /* Binary mode and streaming are only supported in v14 and higher */
6891 if (pset.sversion >= 140000)
6892 {
6894 ", subbinary AS \"%s\"\n",
6895 gettext_noop("Binary"));
6896
6897 if (pset.sversion >= 160000)
6899 ", (CASE substream\n"
6900 " WHEN " CppAsString2(LOGICALREP_STREAM_OFF) " THEN 'off'\n"
6901 " WHEN " CppAsString2(LOGICALREP_STREAM_ON) " THEN 'on'\n"
6902 " WHEN " CppAsString2(LOGICALREP_STREAM_PARALLEL) " THEN 'parallel'\n"
6903 " END) AS \"%s\"\n",
6904 gettext_noop("Streaming"));
6905 else
6907 ", substream AS \"%s\"\n",
6908 gettext_noop("Streaming"));
6909 }
6910
6911 /* Two_phase and disable_on_error are only supported in v15 and higher */
6912 if (pset.sversion >= 150000)
6914 ", subtwophasestate AS \"%s\"\n"
6915 ", subdisableonerr AS \"%s\"\n",
6916 gettext_noop("Two-phase commit"),
6917 gettext_noop("Disable on error"));
6918
6919 if (pset.sversion >= 160000)
6921 ", suborigin AS \"%s\"\n"
6922 ", subpasswordrequired AS \"%s\"\n"
6923 ", subrunasowner AS \"%s\"\n",
6924 gettext_noop("Origin"),
6925 gettext_noop("Password required"),
6926 gettext_noop("Run as owner?"));
6927
6928 if (pset.sversion >= 170000)
6930 ", subfailover AS \"%s\"\n",
6931 gettext_noop("Failover"));
6932 if (pset.sversion >= 190000)
6933 {
6935 ", (select srvname from pg_catalog.pg_foreign_server where oid=subserver) AS \"%s\"\n",
6936 gettext_noop("Server"));
6937
6939 ", subretaindeadtuples AS \"%s\"\n",
6940 gettext_noop("Retain dead tuples"));
6941
6943 ", submaxretention AS \"%s\"\n",
6944 gettext_noop("Max retention duration"));
6945
6947 ", subretentionactive AS \"%s\"\n",
6948 gettext_noop("Retention active"));
6949 }
6950
6952 ", subsynccommit AS \"%s\"\n"
6953 ", subconninfo AS \"%s\"\n",
6954 gettext_noop("Synchronous commit"),
6955 gettext_noop("Conninfo"));
6956
6957 if (pset.sversion >= 190000)
6959 ", subwalrcvtimeout AS \"%s\"\n",
6960 gettext_noop("Receiver timeout"));
6961
6962 /* Skip LSN is only supported in v15 and higher */
6963 if (pset.sversion >= 150000)
6965 ", subskiplsn AS \"%s\"\n",
6966 gettext_noop("Skip LSN"));
6967
6969 ", pg_catalog.obj_description(oid, 'pg_subscription') AS \"%s\"\n",
6970 gettext_noop("Description"));
6971 }
6972
6973 /* Only display subscriptions in current database. */
6975 "FROM pg_catalog.pg_subscription\n"
6976 "WHERE subdbid = (SELECT oid\n"
6977 " FROM pg_catalog.pg_database\n"
6978 " WHERE datname = pg_catalog.current_database())");
6979
6980 if (!validateSQLNamePattern(&buf, pattern, true, false,
6981 NULL, "subname", NULL,
6982 NULL,
6983 NULL, 1))
6984 {
6986 return false;
6987 }
6988
6989 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6990
6991 res = PSQLexec(buf.data);
6993 if (!res)
6994 return false;
6995
6996 myopt.title = _("List of subscriptions");
6997 myopt.translate_header = true;
6998 myopt.translate_columns = translate_columns;
6999 myopt.n_translate_columns = lengthof(translate_columns);
7000
7001 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7002
7003 PQclear(res);
7004 return true;
7005}
7006
7007/*
7008 * printACLColumn
7009 *
7010 * Helper function for consistently formatting ACL (privilege) columns.
7011 * The proper targetlist entry is appended to buf. Note lack of any
7012 * whitespace or comma decoration.
7013 *
7014 * If you change this, see also the handling of attacl in permissionsList(),
7015 * which can't conveniently use this code.
7016 */
7017static void
7018printACLColumn(PQExpBuffer buf, const char *colname)
7019{
7021 "CASE"
7022 " WHEN pg_catalog.array_length(%s, 1) = 0 THEN '%s'"
7023 " ELSE pg_catalog.array_to_string(%s, E'\\n')"
7024 " END AS \"%s\"",
7025 colname, gettext_noop("(none)"),
7026 colname, gettext_noop("Access privileges"));
7027}
7028
7029/*
7030 * \dAc
7031 * Lists operator classes
7032 *
7033 * Takes optional regexps to filter by index access method and input data type.
7034 */
7035bool
7037 const char *type_pattern, bool verbose)
7038{
7040 PGresult *res;
7042 bool have_where = false;
7043 static const bool translate_columns[] = {false, false, false, false, false, false, false};
7044
7046
7047 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching operator classes"));
7049 "SELECT\n"
7050 " am.amname AS \"%s\",\n"
7051 " pg_catalog.format_type(c.opcintype, NULL) AS \"%s\",\n"
7052 " CASE\n"
7053 " WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n"
7054 " THEN pg_catalog.format_type(c.opckeytype, NULL)\n"
7055 " ELSE NULL\n"
7056 " END AS \"%s\",\n"
7057 " CASE\n"
7058 " WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n"
7059 " THEN pg_catalog.format('%%I', c.opcname)\n"
7060 " ELSE pg_catalog.format('%%I.%%I', n.nspname, c.opcname)\n"
7061 " END AS \"%s\",\n"
7062 " (CASE WHEN c.opcdefault\n"
7063 " THEN '%s'\n"
7064 " ELSE '%s'\n"
7065 " END) AS \"%s\"",
7066 gettext_noop("AM"),
7067 gettext_noop("Input type"),
7068 gettext_noop("Storage type"),
7069 gettext_noop("Operator class"),
7070 gettext_noop("yes"),
7071 gettext_noop("no"),
7072 gettext_noop("Default?"));
7073 if (verbose)
7075 ",\n CASE\n"
7076 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7077 " THEN pg_catalog.format('%%I', of.opfname)\n"
7078 " ELSE pg_catalog.format('%%I.%%I', ofn.nspname, of.opfname)\n"
7079 " END AS \"%s\",\n"
7080 " pg_catalog.pg_get_userbyid(c.opcowner) AS \"%s\"\n",
7081 gettext_noop("Operator family"),
7082 gettext_noop("Owner"));
7084 "\nFROM pg_catalog.pg_opclass c\n"
7085 " LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n"
7086 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n"
7087 " LEFT JOIN pg_catalog.pg_type t ON t.oid = c.opcintype\n"
7088 " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n");
7089 if (verbose)
7091 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n"
7092 " LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n");
7093
7096 false, false, NULL, "am.amname", NULL, NULL,
7097 &have_where, 1))
7098 goto error_return;
7099 if (type_pattern)
7100 {
7101 /* Match type name pattern against either internal or external name */
7103 "tn.nspname", "t.typname",
7104 "pg_catalog.format_type(t.oid, NULL)",
7105 "pg_catalog.pg_type_is_visible(t.oid)",
7106 NULL, 3))
7107 goto error_return;
7108 }
7109
7110 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
7111 res = PSQLexec(buf.data);
7113 if (!res)
7114 return false;
7115
7116 myopt.title = _("List of operator classes");
7117 myopt.translate_header = true;
7118 myopt.translate_columns = translate_columns;
7119 myopt.n_translate_columns = lengthof(translate_columns);
7120
7121 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7122
7123 PQclear(res);
7124 return true;
7125
7128 return false;
7129}
7130
7131/*
7132 * \dAf
7133 * Lists operator families
7134 *
7135 * Takes optional regexps to filter by index access method and input data type.
7136 */
7137bool
7139 const char *type_pattern, bool verbose)
7140{
7142 PGresult *res;
7144 bool have_where = false;
7145 static const bool translate_columns[] = {false, false, false, false};
7146
7148
7149 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching operator families"));
7151 "SELECT\n"
7152 " am.amname AS \"%s\",\n"
7153 " CASE\n"
7154 " WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n"
7155 " THEN pg_catalog.format('%%I', f.opfname)\n"
7156 " ELSE pg_catalog.format('%%I.%%I', n.nspname, f.opfname)\n"
7157 " END AS \"%s\",\n"
7158 " (SELECT\n"
7159 " pg_catalog.string_agg(pg_catalog.format_type(oc.opcintype, NULL), ', ')\n"
7160 " FROM pg_catalog.pg_opclass oc\n"
7161 " WHERE oc.opcfamily = f.oid) \"%s\"",
7162 gettext_noop("AM"),
7163 gettext_noop("Operator family"),
7164 gettext_noop("Applicable types"));
7165 if (verbose)
7167 ",\n pg_catalog.pg_get_userbyid(f.opfowner) AS \"%s\"\n",
7168 gettext_noop("Owner"));
7170 "\nFROM pg_catalog.pg_opfamily f\n"
7171 " LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n"
7172 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace\n");
7173
7176 false, false, NULL, "am.amname", NULL, NULL,
7177 &have_where, 1))
7178 goto error_return;
7179 if (type_pattern)
7180 {
7182 " %s EXISTS (\n"
7183 " SELECT 1\n"
7184 " FROM pg_catalog.pg_type t\n"
7185 " JOIN pg_catalog.pg_opclass oc ON oc.opcintype = t.oid\n"
7186 " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n"
7187 " WHERE oc.opcfamily = f.oid\n",
7188 have_where ? "AND" : "WHERE");
7189 /* Match type name pattern against either internal or external name */
7190 if (!validateSQLNamePattern(&buf, type_pattern, true, false,
7191 "tn.nspname", "t.typname",
7192 "pg_catalog.format_type(t.oid, NULL)",
7193 "pg_catalog.pg_type_is_visible(t.oid)",
7194 NULL, 3))
7195 goto error_return;
7196 appendPQExpBufferStr(&buf, " )\n");
7197 }
7198
7199 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
7200 res = PSQLexec(buf.data);
7202 if (!res)
7203 return false;
7204
7205 myopt.title = _("List of operator families");
7206 myopt.translate_header = true;
7207 myopt.translate_columns = translate_columns;
7208 myopt.n_translate_columns = lengthof(translate_columns);
7209
7210 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7211
7212 PQclear(res);
7213 return true;
7214
7217 return false;
7218}
7219
7220/*
7221 * \dAo
7222 * Lists operators of operator families
7223 *
7224 * Takes optional regexps to filter by index access method and operator
7225 * family.
7226 */
7227bool
7229 const char *family_pattern, bool verbose)
7230{
7232 PGresult *res;
7234 bool have_where = false;
7235
7236 static const bool translate_columns[] = {false, false, false, false, false, false, true};
7237
7239
7240 printfPQExpBuffer(&buf, "/* %s */\n", _("Get operators of matching operator families"));
7242 "SELECT\n"
7243 " am.amname AS \"%s\",\n"
7244 " CASE\n"
7245 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7246 " THEN pg_catalog.format('%%I', of.opfname)\n"
7247 " ELSE pg_catalog.format('%%I.%%I', nsf.nspname, of.opfname)\n"
7248 " END AS \"%s\",\n"
7249 " o.amopopr::pg_catalog.regoperator AS \"%s\"\n,"
7250 " o.amopstrategy AS \"%s\",\n"
7251 " CASE o.amoppurpose\n"
7252 " WHEN " CppAsString2(AMOP_ORDER) " THEN '%s'\n"
7253 " WHEN " CppAsString2(AMOP_SEARCH) " THEN '%s'\n"
7254 " END AS \"%s\"\n",
7255 gettext_noop("AM"),
7256 gettext_noop("Operator family"),
7257 gettext_noop("Operator"),
7258 gettext_noop("Strategy"),
7259 gettext_noop("ordering"),
7260 gettext_noop("search"),
7261 gettext_noop("Purpose"));
7262
7263 if (verbose)
7265 ", ofs.opfname AS \"%s\",\n"
7266 " CASE\n"
7267 " WHEN p.proleakproof THEN '%s'\n"
7268 " ELSE '%s'\n"
7269 " END AS \"%s\"\n",
7270 gettext_noop("Sort opfamily"),
7271 gettext_noop("yes"),
7272 gettext_noop("no"),
7273 gettext_noop("Leakproof?"));
7275 "FROM pg_catalog.pg_amop o\n"
7276 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n"
7277 " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n"
7278 " LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n");
7279 if (verbose)
7281 " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n"
7282 " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n"
7283 " LEFT JOIN pg_catalog.pg_proc p ON p.oid = op.oprcode\n");
7284
7286 {
7288 false, false, NULL, "am.amname",
7289 NULL, NULL,
7290 &have_where, 1))
7291 goto error_return;
7292 }
7293
7294 if (family_pattern)
7295 {
7297 "nsf.nspname", "of.opfname", NULL, NULL,
7298 NULL, 3))
7299 goto error_return;
7300 }
7301
7302 appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7303 " o.amoplefttype = o.amoprighttype DESC,\n"
7304 " pg_catalog.format_type(o.amoplefttype, NULL),\n"
7305 " pg_catalog.format_type(o.amoprighttype, NULL),\n"
7306 " o.amopstrategy;");
7307
7308 res = PSQLexec(buf.data);
7310 if (!res)
7311 return false;
7312
7313 myopt.title = _("List of operators of operator families");
7314 myopt.translate_header = true;
7315 myopt.translate_columns = translate_columns;
7316 myopt.n_translate_columns = lengthof(translate_columns);
7317
7318 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7319
7320 PQclear(res);
7321 return true;
7322
7325 return false;
7326}
7327
7328/*
7329 * \dAp
7330 * Lists support functions of operator families
7331 *
7332 * Takes optional regexps to filter by index access method and operator
7333 * family.
7334 */
7335bool
7337 const char *family_pattern, bool verbose)
7338{
7340 PGresult *res;
7342 bool have_where = false;
7343 static const bool translate_columns[] = {false, false, false, false, false, false};
7344
7346
7347 printfPQExpBuffer(&buf, "/* %s */\n",
7348 _("Get support functions of matching operator families"));
7350 "SELECT\n"
7351 " am.amname AS \"%s\",\n"
7352 " CASE\n"
7353 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7354 " THEN pg_catalog.format('%%I', of.opfname)\n"
7355 " ELSE pg_catalog.format('%%I.%%I', ns.nspname, of.opfname)\n"
7356 " END AS \"%s\",\n"
7357 " pg_catalog.format_type(ap.amproclefttype, NULL) AS \"%s\",\n"
7358 " pg_catalog.format_type(ap.amprocrighttype, NULL) AS \"%s\",\n"
7359 " ap.amprocnum AS \"%s\"\n",
7360 gettext_noop("AM"),
7361 gettext_noop("Operator family"),
7362 gettext_noop("Registered left type"),
7363 gettext_noop("Registered right type"),
7364 gettext_noop("Number"));
7365
7366 if (!verbose)
7368 ", p.proname AS \"%s\"\n",
7369 gettext_noop("Function"));
7370 else
7372 ", ap.amproc::pg_catalog.regprocedure AS \"%s\"\n",
7373 gettext_noop("Function"));
7374
7376 "FROM pg_catalog.pg_amproc ap\n"
7377 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n"
7378 " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n"
7379 " LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n"
7380 " LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.oid\n");
7381
7383 {
7385 false, false, NULL, "am.amname",
7386 NULL, NULL,
7387 &have_where, 1))
7388 goto error_return;
7389 }
7390 if (family_pattern)
7391 {
7393 "ns.nspname", "of.opfname", NULL, NULL,
7394 NULL, 3))
7395 goto error_return;
7396 }
7397
7398 appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7399 " ap.amproclefttype = ap.amprocrighttype DESC,\n"
7400 " 3, 4, 5;");
7401
7402 res = PSQLexec(buf.data);
7404 if (!res)
7405 return false;
7406
7407 myopt.title = _("List of support functions of operator families");
7408 myopt.translate_header = true;
7409 myopt.translate_columns = translate_columns;
7410 myopt.n_translate_columns = lengthof(translate_columns);
7411
7412 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7413
7414 PQclear(res);
7415 return true;
7416
7419 return false;
7420}
7421
7422/*
7423 * \dl or \lo_list
7424 * Lists large objects
7425 */
7426bool
7428{
7430 PGresult *res;
7432
7434
7435 printfPQExpBuffer(&buf, "/* %s */\n", _("Get large objects"));
7437 "SELECT oid as \"%s\",\n"
7438 " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n ",
7439 gettext_noop("ID"),
7440 gettext_noop("Owner"));
7441
7442 if (verbose)
7443 {
7444 printACLColumn(&buf, "lomacl");
7445 appendPQExpBufferStr(&buf, ",\n ");
7446 }
7447
7449 "pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
7450 "FROM pg_catalog.pg_largeobject_metadata\n"
7451 "ORDER BY oid",
7452 gettext_noop("Description"));
7453
7454 res = PSQLexec(buf.data);
7456 if (!res)
7457 return false;
7458
7459 myopt.title = _("Large objects");
7460 myopt.translate_header = true;
7461
7462 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7463
7464 PQclear(res);
7465 return true;
7466}
PGresult * PSQLexec(const char *query)
Definition common.c:657
#define ngettext(s, p, n)
Definition c.h:1329
#define gettext_noop(x)
Definition c.h:1344
#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:6126
bool listTSConfigs(const char *pattern, bool verbose)
Definition describe.c:5768
bool describeRoles(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:3778
bool listOpFamilyFunctions(const char *access_method_pattern, const char *family_pattern, bool verbose)
Definition describe.c:7336
bool listPublications(const char *pattern)
Definition describe.c:6481
bool listTSParsers(const char *pattern, bool verbose)
Definition describe.c:5384
bool listExtensionContents(const char *pattern)
Definition describe.c:6313
bool describeAggregates(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:79
bool listForeignDataWrappers(const char *pattern, bool verbose)
Definition describe.c:5975
bool listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
Definition describe.c:4339
bool describeSubscriptions(const char *pattern, bool verbose)
Definition describe.c:6866
bool describeRoleGrants(const char *pattern, bool showSystem)
Definition describe.c:3991
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:7138
bool listForeignServers(const char *pattern, bool verbose)
Definition describe.c:6048
bool describeTableDetails(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:1449
bool listDomains(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4615
bool listTSDictionaries(const char *pattern, bool verbose)
Definition describe.c:5636
bool listDbRoleSettings(const char *pattern, const char *pattern2)
Definition describe.c:3921
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:3909
bool listCollations(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:5140
bool listSchemas(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:5259
bool listExtensions(const char *pattern)
Definition describe.c:6257
static bool describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, const char *pnspname, const char *prsname)
Definition describe.c:5903
static bool listOneExtensionContents(const char *extname, const char *oid)
Definition describe.c:6382
static bool describeOneTableDetails(const char *schemaname, const char *relationname, const char *oid, bool verbose)
Definition describe.c:1534
static bool listTSParsersVerbose(const char *pattern)
Definition describe.c:5437
bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4072
bool listTSTemplates(const char *pattern, bool verbose)
Definition describe.c:5702
bool describeTablespaces(const char *pattern, bool verbose)
Definition describe.c:215
bool listCasts(const char *pattern, bool verbose)
Definition describe.c:5015
bool listOpFamilyOperators(const char *access_method_pattern, const char *family_pattern, bool verbose)
Definition describe.c:7228
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:7036
static const char * map_typename_pattern(const char *pattern)
Definition describe.c:728
bool listForeignTables(const char *pattern, bool verbose)
Definition describe.c:6183
bool describeConfigurationParameters(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4780
bool listEventTriggers(const char *pattern, bool verbose)
Definition describe.c:4851
bool listDefaultACLs(const char *pattern)
Definition describe.c:1173
static void printACLColumn(PQExpBuffer buf, const char *colname)
Definition describe.c:7018
static bool addFooterToPublicationDesc(PQExpBuffer buf, const char *footermsg, bool as_schema, printTableContent *const cont)
Definition describe.c:6564
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:6428
bool listAllDbs(const char *pattern, bool verbose)
Definition describe.c:931
bool listConversions(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4699
bool permissionsList(const char *pattern, bool showSystem)
Definition describe.c:1036
bool describeAccessMethods(const char *pattern, bool verbose)
Definition describe.c:150
static bool listTSConfigsVerbose(const char *pattern)
Definition describe.c:5818
bool listLargeObjects(bool verbose)
Definition describe.c:7427
static bool describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
Definition describe.c:5513
static void add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline)
Definition describe.c:3711
bool listLanguages(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4538
bool describePublications(const char *pattern)
Definition describe.c:6610
bool listExtendedStats(const char *pattern, bool verbose)
Definition describe.c:4922
bool objectDescription(const char *pattern, bool showSystem)
Definition describe.c:1255
#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