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