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 {
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 appendPQExpBufferStr(&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 */
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))
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) */
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 */
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 */
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",
2764 else
2765 printfPQExpBuffer(&buf, " \"%s\" %s",
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",
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 appendPQExpBufferStr(&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 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 {
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)
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 {
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;
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
3680 tuples = PQntuples(result);
3681
3682 if (tuples > 0)
3683 {
3684 printfPQExpBuffer(&buf, "%s:", s);
3686 }
3687
3688 for (i = 0; i < tuples; i++)
3689 {
3690 printfPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 0));
3692 }
3693
3694 PQclear(result);
3695 }
3696
3697 /* print child tables (with additional info if partitions) */
3698 printfPQExpBuffer(&buf, "/* %s */\n", _("Get child tables"));
3699 if (pset.sversion >= 140000)
3701 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3702 " inhdetachpending,"
3703 " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3704 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3705 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3706 "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3707 " c.oid::pg_catalog.regclass::pg_catalog.text;",
3708 oid);
3709 else if (pset.sversion >= 100000)
3711 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3712 " false AS inhdetachpending,"
3713 " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3714 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3715 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3716 "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3717 " c.oid::pg_catalog.regclass::pg_catalog.text;",
3718 oid);
3719 else
3721 "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3722 " false AS inhdetachpending, NULL\n"
3723 "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3724 "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3725 "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;",
3726 oid);
3727
3728 result = PSQLexec(buf.data);
3729 if (!result)
3730 goto error_return;
3731 tuples = PQntuples(result);
3732
3733 /*
3734 * For a partitioned table with no partitions, always print the number
3735 * of partitions as zero, even when verbose output is expected.
3736 * Otherwise, we will not print "Partitions" section for a partitioned
3737 * table without any partitions.
3738 */
3739 if (is_partitioned && tuples == 0)
3740 {
3741 printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples);
3743 }
3744 else if (!verbose)
3745 {
3746 /* print the number of child tables, if any */
3747 if (tuples > 0)
3748 {
3749 if (is_partitioned)
3750 printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
3751 else
3752 printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
3754 }
3755 }
3756 else
3757 {
3758 /* display the list of child tables */
3759 const char *ct = is_partitioned ? _("Partitions") : _("Child tables");
3760
3761 if (tuples > 0)
3762 {
3763 printfPQExpBuffer(&buf, "%s:", ct);
3765 }
3766
3767 for (i = 0; i < tuples; i++)
3768 {
3769 char child_relkind = *PQgetvalue(result, i, 1);
3770
3771 printfPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 0));
3772 if (!PQgetisnull(result, i, 3))
3773 appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 3));
3776 appendPQExpBufferStr(&buf, ", PARTITIONED");
3778 appendPQExpBufferStr(&buf, ", FOREIGN");
3779 if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
3780 appendPQExpBufferStr(&buf, " (DETACH PENDING)");
3781
3783 }
3784 }
3785 PQclear(result);
3786
3787 /* Table type */
3788 if (tableinfo.reloftype)
3789 {
3790 printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
3792 }
3793
3794 if (verbose &&
3795 (tableinfo.relkind == RELKIND_RELATION ||
3796 tableinfo.relkind == RELKIND_MATVIEW) &&
3797
3798 /*
3799 * No need to display default values; we already display a REPLICA
3800 * IDENTITY marker on indexes.
3801 */
3802 tableinfo.relreplident != REPLICA_IDENTITY_INDEX &&
3803 ((strcmp(schemaname, "pg_catalog") != 0 &&
3804 tableinfo.relreplident != REPLICA_IDENTITY_DEFAULT) ||
3805 (strcmp(schemaname, "pg_catalog") == 0 &&
3806 tableinfo.relreplident != REPLICA_IDENTITY_NOTHING)))
3807 {
3808 const char *s = _("Replica Identity");
3809
3810 printfPQExpBuffer(&buf, "%s: %s",
3811 s,
3812 tableinfo.relreplident == REPLICA_IDENTITY_FULL ? "FULL" :
3813 tableinfo.relreplident == REPLICA_IDENTITY_DEFAULT ? "NOTHING" :
3814 "???");
3815
3817 }
3818
3819 /* OIDs, if verbose and not a materialized view */
3820 if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids)
3821 printTableAddFooter(&cont, _("Has OIDs: yes"));
3822
3823 /* Tablespace info */
3824 add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
3825 true);
3826
3827 /* Access method info */
3828 if (verbose && tableinfo.relam != NULL && !pset.hide_tableam)
3829 {
3830 printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam);
3832 }
3833 }
3834
3835 /* reloptions, if verbose */
3836 if (verbose &&
3837 tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
3838 {
3839 const char *t = _("Options");
3840
3841 printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions);
3843 }
3844
3846
3847 retval = true;
3848
3850
3851 /* clean up */
3855 termPQExpBuffer(&title);
3857
3858 free(view_def);
3859
3860 PQclear(res);
3861
3862 return retval;
3863}
3864
3865/*
3866 * Add a tablespace description to a footer. If 'newline' is true, it is added
3867 * in a new line; otherwise it's appended to the current value of the last
3868 * footer.
3869 */
3870static void
3872 Oid tablespace, const bool newline)
3873{
3874 /* relkinds for which we support tablespaces */
3875 if (relkind == RELKIND_RELATION ||
3876 relkind == RELKIND_MATVIEW ||
3877 relkind == RELKIND_INDEX ||
3878 relkind == RELKIND_PARTITIONED_TABLE ||
3879 relkind == RELKIND_PARTITIONED_INDEX ||
3880 relkind == RELKIND_TOASTVALUE)
3881 {
3882 /*
3883 * We ignore the database default tablespace so that users not using
3884 * tablespaces don't need to know about them.
3885 */
3886 if (tablespace != 0)
3887 {
3888 PGresult *result = NULL;
3890
3892 printfPQExpBuffer(&buf, "/* %s */\n",
3893 _("Get tablespace information for this relation"));
3895 "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3896 "WHERE oid = '%u';", tablespace);
3897 result = PSQLexec(buf.data);
3898 if (!result)
3899 {
3901 return;
3902 }
3903 /* Should always be the case, but.... */
3904 if (PQntuples(result) > 0)
3905 {
3906 if (newline)
3907 {
3908 /* Add the tablespace as a new footer */
3909 printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3910 PQgetvalue(result, 0, 0));
3912 }
3913 else
3914 {
3915 /* Append the tablespace to the latest footer */
3916 printfPQExpBuffer(&buf, "%s", cont->footer->data);
3917
3918 /*-------
3919 translator: before this string there's an index description like
3920 '"foo_pkey" PRIMARY KEY, btree (a)' */
3921 appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3922 PQgetvalue(result, 0, 0));
3924 }
3925 }
3926 PQclear(result);
3928 }
3929 }
3930}
3931
3932/*
3933 * \du or \dg
3934 *
3935 * Describes roles. Any schema portion of the pattern is ignored.
3936 */
3937bool
3938describeRoles(const char *pattern, bool verbose, bool showSystem)
3939{
3941 PGresult *res;
3944 int ncols = 2;
3945 int nrows = 0;
3946 int i;
3947 int conns;
3948 const char align = 'l';
3949 char **attr;
3950
3951 myopt.default_footer = false;
3952
3954
3955 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching roles"));
3957 "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
3958 " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
3959 " r.rolconnlimit, r.rolvaliduntil");
3960
3961 if (verbose)
3962 {
3963 appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
3964 ncols++;
3965 }
3966 appendPQExpBufferStr(&buf, "\n, r.rolreplication");
3967
3968 if (pset.sversion >= 90500)
3969 {
3970 appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
3971 }
3972
3973 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
3974
3975 if (!showSystem && !pattern)
3976 appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
3977
3978 if (!validateSQLNamePattern(&buf, pattern, false, false,
3979 NULL, "r.rolname", NULL, NULL,
3980 NULL, 1))
3981 {
3983 return false;
3984 }
3985
3986 appendPQExpBufferStr(&buf, "ORDER BY 1;");
3987
3988 res = PSQLexec(buf.data);
3989 if (!res)
3990 return false;
3991
3992 nrows = PQntuples(res);
3993 attr = pg_malloc0_array(char *, nrows + 1);
3994
3995 printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
3996
3997 printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
3998 printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
3999
4000 if (verbose)
4001 printTableAddHeader(&cont, gettext_noop("Description"), true, align);
4002
4003 for (i = 0; i < nrows; i++)
4004 {
4005 printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
4006
4008 if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
4009 add_role_attribute(&buf, _("Superuser"));
4010
4011 if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
4012 add_role_attribute(&buf, _("No inheritance"));
4013
4014 if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
4015 add_role_attribute(&buf, _("Create role"));
4016
4017 if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
4018 add_role_attribute(&buf, _("Create DB"));
4019
4020 if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
4021 add_role_attribute(&buf, _("Cannot login"));
4022
4023 if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
4024 add_role_attribute(&buf, _("Replication"));
4025
4026 if (pset.sversion >= 90500)
4027 if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
4028 add_role_attribute(&buf, _("Bypass RLS"));
4029
4030 conns = atoi(PQgetvalue(res, i, 6));
4031 if (conns >= 0)
4032 {
4033 if (buf.len > 0)
4034 appendPQExpBufferChar(&buf, '\n');
4035
4036 if (conns == 0)
4037 appendPQExpBufferStr(&buf, _("No connections"));
4038 else
4039 appendPQExpBuffer(&buf, ngettext("%d connection",
4040 "%d connections",
4041 conns),
4042 conns);
4043 }
4044
4045 if (strcmp(PQgetvalue(res, i, 7), "") != 0)
4046 {
4047 if (buf.len > 0)
4048 appendPQExpBufferChar(&buf, '\n');
4049 appendPQExpBufferStr(&buf, _("Password valid until "));
4051 }
4052
4053 attr[i] = pg_strdup(buf.data);
4054
4055 printTableAddCell(&cont, attr[i], false, false);
4056
4057 if (verbose)
4058 printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
4059 }
4061
4064
4065 for (i = 0; i < nrows; i++)
4066 free(attr[i]);
4067 free(attr);
4068
4069 PQclear(res);
4070 return true;
4071}
4072
4073static void
4075{
4076 if (buf->len > 0)
4078
4080}
4081
4082/*
4083 * \drds
4084 */
4085bool
4086listDbRoleSettings(const char *pattern, const char *pattern2)
4087{
4089 PGresult *res;
4091 bool havewhere;
4092
4094
4095 printfPQExpBuffer(&buf, "/* %s */\n", _("Get per-database and per-role settings"));
4096 appendPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
4097 "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
4098 "FROM pg_catalog.pg_db_role_setting s\n"
4099 "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n"
4100 "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n",
4101 gettext_noop("Role"),
4102 gettext_noop("Database"),
4103 gettext_noop("Settings"));
4104 if (!validateSQLNamePattern(&buf, pattern, false, false,
4105 NULL, "r.rolname", NULL, NULL, &havewhere, 1))
4106 goto error_return;
4108 NULL, "d.datname", NULL, NULL,
4109 NULL, 1))
4110 goto error_return;
4111 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4112
4113 res = PSQLexec(buf.data);
4115 if (!res)
4116 return false;
4117
4118 /*
4119 * Most functions in this file are content to print an empty table when
4120 * there are no matching objects. We intentionally deviate from that
4121 * here, but only in !quiet mode, because of the possibility that the user
4122 * is confused about what the two pattern arguments mean.
4123 */
4124 if (PQntuples(res) == 0 && !pset.quiet)
4125 {
4126 if (pattern && pattern2)
4127 pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".",
4128 pattern, pattern2);
4129 else if (pattern)
4130 pg_log_error("Did not find any settings for role \"%s\".",
4131 pattern);
4132 else
4133 pg_log_error("Did not find any settings.");
4134 }
4135 else
4136 {
4137 myopt.title = _("List of settings");
4138 myopt.translate_header = true;
4139
4140 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4141 }
4142
4143 PQclear(res);
4144 return true;
4145
4148 return false;
4149}
4150
4151/*
4152 * \drg
4153 * Describes role grants.
4154 */
4155bool
4156describeRoleGrants(const char *pattern, bool showSystem)
4157{
4159 PGresult *res;
4161
4163 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching role grants"));
4165 "SELECT m.rolname AS \"%s\", r.rolname AS \"%s\",\n"
4166 " pg_catalog.concat_ws(', ',\n",
4167 gettext_noop("Role name"),
4168 gettext_noop("Member of"));
4169
4170 if (pset.sversion >= 160000)
4172 " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
4173 " CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n"
4174 " CASE WHEN pam.set_option THEN 'SET' END\n");
4175 else
4177 " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
4178 " CASE WHEN m.rolinherit THEN 'INHERIT' END,\n"
4179 " 'SET'\n");
4180
4182 " ) AS \"%s\",\n"
4183 " g.rolname AS \"%s\"\n",
4184 gettext_noop("Options"),
4185 gettext_noop("Grantor"));
4186
4188 "FROM pg_catalog.pg_roles m\n"
4189 " JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n"
4190 " LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n"
4191 " LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n");
4192
4193 if (!showSystem && !pattern)
4194 appendPQExpBufferStr(&buf, "WHERE m.rolname !~ '^pg_'\n");
4195
4196 if (!validateSQLNamePattern(&buf, pattern, false, false,
4197 NULL, "m.rolname", NULL, NULL,
4198 NULL, 1))
4199 {
4201 return false;
4202 }
4203
4204 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;\n");
4205
4206 res = PSQLexec(buf.data);
4208 if (!res)
4209 return false;
4210
4211 myopt.title = _("List of role grants");
4212 myopt.translate_header = true;
4213
4214 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4215
4216 PQclear(res);
4217 return true;
4218}
4219
4220
4221/*
4222 * listTables()
4223 *
4224 * handler for \dt, \di, etc.
4225 *
4226 * tabtypes is an array of characters, specifying what info is desired:
4227 * t - tables
4228 * i - indexes
4229 * v - views
4230 * m - materialized views
4231 * s - sequences
4232 * E - foreign table (Note: different from 'f', the relkind value)
4233 * G - property graphs
4234 * (any order of the above is fine)
4235 */
4236bool
4237listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
4238{
4239 bool showTables = strchr(tabtypes, 't') != NULL;
4240 bool showIndexes = strchr(tabtypes, 'i') != NULL;
4241 bool showViews = strchr(tabtypes, 'v') != NULL;
4242 bool showMatViews = strchr(tabtypes, 'm') != NULL;
4243 bool showSeq = strchr(tabtypes, 's') != NULL;
4244 bool showForeign = strchr(tabtypes, 'E') != NULL;
4245 bool showPropGraphs = strchr(tabtypes, 'G') != NULL;
4246
4247 int ntypes;
4249 PGresult *res;
4251 int cols_so_far;
4252 bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
4253
4254 /* Count the number of explicitly-requested relation types */
4257 /* If none, we default to \dtvmsEG (but see also command.c) */
4258 if (ntypes == 0)
4260
4262
4263 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching relations"));
4265 "SELECT n.nspname as \"%s\",\n"
4266 " c.relname as \"%s\",\n"
4267 " CASE c.relkind"
4268 " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
4269 " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'"
4270 " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
4271 " WHEN " CppAsString2(RELKIND_INDEX) " THEN '%s'"
4272 " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
4273 " WHEN " CppAsString2(RELKIND_TOASTVALUE) " THEN '%s'"
4274 " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
4275 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4276 " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4277 " WHEN " CppAsString2(RELKIND_PROPGRAPH) " THEN '%s'"
4278 " END as \"%s\",\n"
4279 " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4280 gettext_noop("Schema"),
4281 gettext_noop("Name"),
4282 gettext_noop("table"),
4283 gettext_noop("view"),
4284 gettext_noop("materialized view"),
4285 gettext_noop("index"),
4286 gettext_noop("sequence"),
4287 gettext_noop("TOAST table"),
4288 gettext_noop("foreign table"),
4289 gettext_noop("partitioned table"),
4290 gettext_noop("partitioned index"),
4291 gettext_noop("property graph"),
4292 gettext_noop("Type"),
4293 gettext_noop("Owner"));
4294 cols_so_far = 4;
4295
4296 if (showIndexes)
4297 {
4299 ",\n c2.relname as \"%s\"",
4300 gettext_noop("Table"));
4301 cols_so_far++;
4302 }
4303
4304 if (verbose)
4305 {
4306 /*
4307 * Show whether a relation is permanent, temporary, or unlogged.
4308 */
4310 ",\n CASE c.relpersistence "
4311 "WHEN " CppAsString2(RELPERSISTENCE_PERMANENT) " THEN '%s' "
4312 "WHEN " CppAsString2(RELPERSISTENCE_TEMP) " THEN '%s' "
4313 "WHEN " CppAsString2(RELPERSISTENCE_UNLOGGED) " THEN '%s' "
4314 "END as \"%s\"",
4315 gettext_noop("permanent"),
4316 gettext_noop("temporary"),
4317 gettext_noop("unlogged"),
4318 gettext_noop("Persistence"));
4319 translate_columns[cols_so_far] = true;
4320
4321 /*
4322 * We don't bother to count cols_so_far below here, as there's no need
4323 * to; this might change with future additions to the output columns.
4324 */
4325
4326 /*
4327 * Access methods exist for tables, materialized views and indexes.
4328 * This has been introduced in PostgreSQL 12 for tables.
4329 */
4330 if (pset.sversion >= 120000 && !pset.hide_tableam &&
4333 ",\n am.amname as \"%s\"",
4334 gettext_noop("Access method"));
4335
4337 ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\""
4338 ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4339 gettext_noop("Size"),
4340 gettext_noop("Description"));
4341 }
4342
4344 "\nFROM pg_catalog.pg_class c"
4345 "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4346
4347 if (pset.sversion >= 120000 && !pset.hide_tableam &&
4350 "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam");
4351
4352 if (showIndexes)
4354 "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4355 "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4356
4357 appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4358 if (showTables)
4359 {
4362 /* with 'S' or a pattern, allow 't' to match TOAST tables too */
4363 if (showSystem || pattern)
4365 }
4366 if (showViews)
4368 if (showMatViews)
4370 if (showIndexes)
4373 if (showSeq)
4375 if (showSystem || pattern)
4376 appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
4377 if (showForeign)
4379 if (showPropGraphs)
4381
4382 appendPQExpBufferStr(&buf, "''"); /* dummy */
4383 appendPQExpBufferStr(&buf, ")\n");
4384
4385 if (!showSystem && !pattern)
4386 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4387 " AND n.nspname !~ '^pg_toast'\n"
4388 " AND n.nspname <> 'information_schema'\n");
4389
4390 if (!validateSQLNamePattern(&buf, pattern, true, false,
4391 "n.nspname", "c.relname", NULL,
4392 "pg_catalog.pg_table_is_visible(c.oid)",
4393 NULL, 3))
4394 {
4396 return false;
4397 }
4398
4399 appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
4400
4401 res = PSQLexec(buf.data);
4403 if (!res)
4404 return false;
4405
4406 /*
4407 * Most functions in this file are content to print an empty table when
4408 * there are no matching objects. We intentionally deviate from that
4409 * here, but only in !quiet mode, for historical reasons.
4410 */
4411 if (PQntuples(res) == 0 && !pset.quiet)
4412 {
4413 if (pattern)
4414 {
4415 if (ntypes != 1)
4416 pg_log_error("Did not find any relations named \"%s\".",
4417 pattern);
4418 else if (showTables)
4419 pg_log_error("Did not find any tables named \"%s\".",
4420 pattern);
4421 else if (showIndexes)
4422 pg_log_error("Did not find any indexes named \"%s\".",
4423 pattern);
4424 else if (showViews)
4425 pg_log_error("Did not find any views named \"%s\".",
4426 pattern);
4427 else if (showMatViews)
4428 pg_log_error("Did not find any materialized views named \"%s\".",
4429 pattern);
4430 else if (showSeq)
4431 pg_log_error("Did not find any sequences named \"%s\".",
4432 pattern);
4433 else if (showForeign)
4434 pg_log_error("Did not find any foreign tables named \"%s\".",
4435 pattern);
4436 else if (showPropGraphs)
4437 pg_log_error("Did not find any property graphs named \"%s\".",
4438 pattern);
4439 else /* should not get here */
4440 pg_log_error_internal("Did not find any ??? named \"%s\".",
4441 pattern);
4442 }
4443 else
4444 {
4445 if (ntypes != 1)
4446 pg_log_error("Did not find any relations.");
4447 else if (showTables)
4448 pg_log_error("Did not find any tables.");
4449 else if (showIndexes)
4450 pg_log_error("Did not find any indexes.");
4451 else if (showViews)
4452 pg_log_error("Did not find any views.");
4453 else if (showMatViews)
4454 pg_log_error("Did not find any materialized views.");
4455 else if (showSeq)
4456 pg_log_error("Did not find any sequences.");
4457 else if (showForeign)
4458 pg_log_error("Did not find any foreign tables.");
4459 else if (showPropGraphs)
4460 pg_log_error("Did not find any property graphs.");
4461 else /* should not get here */
4462 pg_log_error_internal("Did not find any ??? relations.");
4463 }
4464 }
4465 else
4466 {
4467 myopt.title =
4468 (ntypes != 1) ? _("List of relations") :
4469 (showTables) ? _("List of tables") :
4470 (showIndexes) ? _("List of indexes") :
4471 (showViews) ? _("List of views") :
4472 (showMatViews) ? _("List of materialized views") :
4473 (showSeq) ? _("List of sequences") :
4474 (showForeign) ? _("List of foreign tables") :
4475 (showPropGraphs) ? _("List of property graphs") :
4476 "List of ???"; /* should not get here */
4477 myopt.translate_header = true;
4478 myopt.translate_columns = translate_columns;
4479 myopt.n_translate_columns = lengthof(translate_columns);
4480
4481 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4482 }
4483
4484 PQclear(res);
4485 return true;
4486}
4487
4488/*
4489 * \dP
4490 * Takes an optional regexp to select particular relations
4491 *
4492 * As with \d, you can specify the kinds of relations you want:
4493 *
4494 * t for tables
4495 * i for indexes
4496 *
4497 * And there's additional flags:
4498 *
4499 * n to list non-leaf partitioned tables
4500 *
4501 * and you can mix and match these in any order.
4502 */
4503bool
4504listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
4505{
4506 bool showTables = strchr(reltypes, 't') != NULL;
4507 bool showIndexes = strchr(reltypes, 'i') != NULL;
4508 bool showNested = strchr(reltypes, 'n') != NULL;
4510 PQExpBufferData title;
4511 PGresult *res;
4513 bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
4514 const char *tabletitle;
4515 bool mixed_output = false;
4516
4517 /*
4518 * Note: Declarative table partitioning is only supported as of Pg 10.0.
4519 */
4520 if (pset.sversion < 100000)
4521 {
4522 char sverbuf[32];
4523
4524 pg_log_error("The server (version %s) does not support declarative table partitioning.",
4526 sverbuf, sizeof(sverbuf)));
4527 return true;
4528 }
4529
4530 /* If no relation kind was selected, show them all */
4531 if (!showTables && !showIndexes)
4532 showTables = showIndexes = true;
4533
4534 if (showIndexes && !showTables)
4535 tabletitle = _("List of partitioned indexes"); /* \dPi */
4536 else if (showTables && !showIndexes)
4537 tabletitle = _("List of partitioned tables"); /* \dPt */
4538 else
4539 {
4540 /* show all kinds */
4541 tabletitle = _("List of partitioned relations");
4542 mixed_output = true;
4543 }
4544
4546
4547 printfPQExpBuffer(&buf, "/* %s */\n",
4548 _("Get matching partitioned relations"));
4550 "SELECT n.nspname as \"%s\",\n"
4551 " c.relname as \"%s\",\n"
4552 " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4553 gettext_noop("Schema"),
4554 gettext_noop("Name"),
4555 gettext_noop("Owner"));
4556
4557 if (mixed_output)
4558 {
4560 ",\n CASE c.relkind"
4561 " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4562 " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4563 " END as \"%s\"",
4564 gettext_noop("partitioned table"),
4565 gettext_noop("partitioned index"),
4566 gettext_noop("Type"));
4567
4568 translate_columns[3] = true;
4569 }
4570
4571 if (showNested || pattern)
4573 ",\n inh.inhparent::pg_catalog.regclass as \"%s\"",
4574 gettext_noop("Parent name"));
4575
4576 if (showIndexes)
4578 ",\n c2.oid::pg_catalog.regclass as \"%s\"",
4579 gettext_noop("Table"));
4580
4581 if (verbose)
4582 {
4583 /*
4584 * Table access methods were introduced in v12, and can be set on
4585 * partitioned tables since v17.
4586 */
4587 appendPQExpBuffer(&buf, ",\n am.amname as \"%s\"",
4588 gettext_noop("Access method"));
4589
4590 if (showNested)
4591 {
4593 ",\n s.dps as \"%s\"",
4594 gettext_noop("Leaf partition size"));
4596 ",\n s.tps as \"%s\"",
4597 gettext_noop("Total size"));
4598 }
4599 else
4600 /* Sizes of all partitions are considered in this case. */
4602 ",\n s.tps as \"%s\"",
4603 gettext_noop("Total size"));
4604
4606 ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4607 gettext_noop("Description"));
4608 }
4609
4611 "\nFROM pg_catalog.pg_class c"
4612 "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4613
4614 if (showIndexes)
4616 "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4617 "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4618
4619 if (showNested || pattern)
4621 "\n LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid");
4622
4623 if (verbose)
4624 {
4626 "\n LEFT JOIN pg_catalog.pg_am am ON c.relam = am.oid");
4627
4628 if (pset.sversion < 120000)
4629 {
4631 ",\n LATERAL (WITH RECURSIVE d\n"
4632 " AS (SELECT inhrelid AS oid, 1 AS level\n"
4633 " FROM pg_catalog.pg_inherits\n"
4634 " WHERE inhparent = c.oid\n"
4635 " UNION ALL\n"
4636 " SELECT inhrelid, level + 1\n"
4637 " FROM pg_catalog.pg_inherits i\n"
4638 " JOIN d ON i.inhparent = d.oid)\n"
4639 " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
4640 "d.oid))) AS tps,\n"
4641 " pg_catalog.pg_size_pretty(sum("
4642 "\n CASE WHEN d.level = 1"
4643 " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n"
4644 " FROM d) s");
4645 }
4646 else
4647 {
4648 /* PostgreSQL 12 has pg_partition_tree function */
4650 ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
4651 "\n CASE WHEN ppt.isleaf AND ppt.level = 1"
4652 "\n THEN pg_catalog.pg_table_size(ppt.relid)"
4653 " ELSE 0 END)) AS dps"
4654 ",\n pg_catalog.pg_size_pretty(sum("
4655 "pg_catalog.pg_table_size(ppt.relid))) AS tps"
4656 "\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s");
4657 }
4658 }
4659
4660 appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4661 if (showTables)
4663 if (showIndexes)
4665 appendPQExpBufferStr(&buf, "''"); /* dummy */
4666 appendPQExpBufferStr(&buf, ")\n");
4667
4668 appendPQExpBufferStr(&buf, !showNested && !pattern ?
4669 " AND NOT c.relispartition\n" : "");
4670
4671 if (!pattern)
4672 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4673 " AND n.nspname !~ '^pg_toast'\n"
4674 " AND n.nspname <> 'information_schema'\n");
4675
4676 if (!validateSQLNamePattern(&buf, pattern, true, false,
4677 "n.nspname", "c.relname", NULL,
4678 "pg_catalog.pg_table_is_visible(c.oid)",
4679 NULL, 3))
4680 {
4682 return false;
4683 }
4684
4685 appendPQExpBuffer(&buf, "ORDER BY \"Schema\", %s%s\"Name\";",
4686 mixed_output ? "\"Type\" DESC, " : "",
4687 showNested || pattern ? "\"Parent name\" NULLS FIRST, " : "");
4688
4689 res = PSQLexec(buf.data);
4691 if (!res)
4692 return false;
4693
4694 initPQExpBuffer(&title);
4696
4697 myopt.title = title.data;
4698 myopt.translate_header = true;
4699 myopt.translate_columns = translate_columns;
4700 myopt.n_translate_columns = lengthof(translate_columns);
4701
4702 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4703
4704 termPQExpBuffer(&title);
4705
4706 PQclear(res);
4707 return true;
4708}
4709
4710/*
4711 * \dL
4712 *
4713 * Describes languages.
4714 */
4715bool
4716listLanguages(const char *pattern, bool verbose, bool showSystem)
4717{
4719 PGresult *res;
4721
4723
4724 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching procedural languages"));
4726 "SELECT l.lanname AS \"%s\",\n"
4727 " pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n"
4728 " l.lanpltrusted AS \"%s\"",
4729 gettext_noop("Name"),
4730 gettext_noop("Owner"),
4731 gettext_noop("Trusted"));
4732
4733 if (verbose)
4734 {
4736 ",\n NOT l.lanispl AS \"%s\",\n"
4737 " l.lanplcallfoid::pg_catalog.regprocedure AS \"%s\",\n"
4738 " l.lanvalidator::pg_catalog.regprocedure AS \"%s\",\n "
4739 "l.laninline::pg_catalog.regprocedure AS \"%s\",\n ",
4740 gettext_noop("Internal language"),
4741 gettext_noop("Call handler"),
4742 gettext_noop("Validator"),
4743 gettext_noop("Inline handler"));
4744 printACLColumn(&buf, "l.lanacl");
4745 }
4746
4748 ",\n d.description AS \"%s\""
4749 "\nFROM pg_catalog.pg_language l\n"
4750 "LEFT JOIN pg_catalog.pg_description d\n"
4751 " ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
4752 " AND d.objsubid = 0\n",
4753 gettext_noop("Description"));
4754
4755 if (pattern)
4756 {
4757 if (!validateSQLNamePattern(&buf, pattern, false, false,
4758 NULL, "l.lanname", NULL, NULL,
4759 NULL, 2))
4760 {
4762 return false;
4763 }
4764 }
4765
4766 if (!showSystem && !pattern)
4767 appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n");
4768
4769
4770 appendPQExpBufferStr(&buf, "ORDER BY 1;");
4771
4772 res = PSQLexec(buf.data);
4774 if (!res)
4775 return false;
4776
4777 myopt.title = _("List of languages");
4778 myopt.translate_header = true;
4779
4780 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4781
4782 PQclear(res);
4783 return true;
4784}
4785
4786
4787/*
4788 * \dD
4789 *
4790 * Describes domains.
4791 */
4792bool
4793listDomains(const char *pattern, bool verbose, bool showSystem)
4794{
4796 PGresult *res;
4798
4800
4801 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching domains"));
4803 "SELECT n.nspname as \"%s\",\n"
4804 " t.typname as \"%s\",\n"
4805 " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
4806 " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
4807 " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4808 " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4809 " t.typdefault as \"%s\",\n"
4810 " pg_catalog.array_to_string(ARRAY(\n"
4811 " 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"
4812 " ), ' ') as \"%s\"",
4813 gettext_noop("Schema"),
4814 gettext_noop("Name"),
4815 gettext_noop("Type"),
4816 gettext_noop("Collation"),
4817 gettext_noop("Nullable"),
4818 gettext_noop("Default"),
4819 gettext_noop("Check"));
4820
4821 if (verbose)
4822 {
4823 appendPQExpBufferStr(&buf, ",\n ");
4824 printACLColumn(&buf, "t.typacl");
4826 ",\n d.description as \"%s\"",
4827 gettext_noop("Description"));
4828 }
4829
4831 "\nFROM pg_catalog.pg_type t\n"
4832 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
4833
4834 if (verbose)
4836 " LEFT JOIN pg_catalog.pg_description d "
4837 "ON d.classoid = t.tableoid AND d.objoid = t.oid "
4838 "AND d.objsubid = 0\n");
4839
4840 appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
4841
4842 if (!showSystem && !pattern)
4843 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4844 " AND n.nspname <> 'information_schema'\n");
4845
4846 if (!validateSQLNamePattern(&buf, pattern, true, false,
4847 "n.nspname", "t.typname", NULL,
4848 "pg_catalog.pg_type_is_visible(t.oid)",
4849 NULL, 3))
4850 {
4852 return false;
4853 }
4854
4855 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4856
4857 res = PSQLexec(buf.data);
4859 if (!res)
4860 return false;
4861
4862 myopt.title = _("List of domains");
4863 myopt.translate_header = true;
4864
4865 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4866
4867 PQclear(res);
4868 return true;
4869}
4870
4871/*
4872 * \dc
4873 *
4874 * Describes conversions.
4875 */
4876bool
4877listConversions(const char *pattern, bool verbose, bool showSystem)
4878{
4880 PGresult *res;
4882 static const bool translate_columns[] =
4883 {false, false, false, false, true, false};
4884
4886
4887 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching conversions"));
4889 "SELECT n.nspname AS \"%s\",\n"
4890 " c.conname AS \"%s\",\n"
4891 " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
4892 " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
4893 " CASE WHEN c.condefault THEN '%s'\n"
4894 " ELSE '%s' END AS \"%s\"",
4895 gettext_noop("Schema"),
4896 gettext_noop("Name"),
4897 gettext_noop("Source"),
4898 gettext_noop("Destination"),
4899 gettext_noop("yes"), gettext_noop("no"),
4900 gettext_noop("Default?"));
4901
4902 if (verbose)
4904 ",\n d.description AS \"%s\"",
4905 gettext_noop("Description"));
4906
4908 "\nFROM pg_catalog.pg_conversion c\n"
4909 " JOIN pg_catalog.pg_namespace n "
4910 "ON n.oid = c.connamespace\n");
4911
4912 if (verbose)
4914 "LEFT JOIN pg_catalog.pg_description d "
4915 "ON d.classoid = c.tableoid\n"
4916 " AND d.objoid = c.oid "
4917 "AND d.objsubid = 0\n");
4918
4919 appendPQExpBufferStr(&buf, "WHERE true\n");
4920
4921 if (!showSystem && !pattern)
4922 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4923 " AND n.nspname <> 'information_schema'\n");
4924
4925 if (!validateSQLNamePattern(&buf, pattern, true, false,
4926 "n.nspname", "c.conname", NULL,
4927 "pg_catalog.pg_conversion_is_visible(c.oid)",
4928 NULL, 3))
4929 {
4931 return false;
4932 }
4933
4934 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4935
4936 res = PSQLexec(buf.data);
4938 if (!res)
4939 return false;
4940
4941 myopt.title = _("List of conversions");
4942 myopt.translate_header = true;
4943 myopt.translate_columns = translate_columns;
4944 myopt.n_translate_columns = lengthof(translate_columns);
4945
4946 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4947
4948 PQclear(res);
4949 return true;
4950}
4951
4952/*
4953 * \dconfig
4954 *
4955 * Describes configuration parameters.
4956 */
4957bool
4959 bool showSystem)
4960{
4962 PGresult *res;
4964
4966
4967 printfPQExpBuffer(&buf, "/* %s */\n",
4968 _("Get matching configuration parameters"));
4970 "SELECT s.name AS \"%s\", "
4971 "pg_catalog.current_setting(s.name) AS \"%s\"",
4972 gettext_noop("Parameter"),
4973 gettext_noop("Value"));
4974
4975 if (verbose)
4976 {
4978 ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4979 gettext_noop("Type"),
4980 gettext_noop("Context"));
4981 if (pset.sversion >= 150000)
4982 printACLColumn(&buf, "p.paracl");
4983 else
4984 appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4985 gettext_noop("Access privileges"));
4986 }
4987
4988 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4989
4990 if (verbose && pset.sversion >= 150000)
4992 " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4993 " ON pg_catalog.lower(s.name) = p.parname\n");
4994
4995 if (pattern)
4996 processSQLNamePattern(pset.db, &buf, pattern,
4997 false, false,
4998 NULL, "pg_catalog.lower(s.name)", NULL,
4999 NULL, NULL, NULL);
5000 else
5001 appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
5002 " s.setting IS DISTINCT FROM s.boot_val\n");
5003
5004 appendPQExpBufferStr(&buf, "ORDER BY 1;");
5005
5006 res = PSQLexec(buf.data);
5008 if (!res)
5009 return false;
5010
5011 if (pattern)
5012 myopt.title = _("List of configuration parameters");
5013 else
5014 myopt.title = _("List of non-default configuration parameters");
5015 myopt.translate_header = true;
5016
5017 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5018
5019 PQclear(res);
5020 return true;
5021}
5022
5023/*
5024 * \dy
5025 *
5026 * Describes Event Triggers.
5027 */
5028bool
5029listEventTriggers(const char *pattern, bool verbose)
5030{
5032 PGresult *res;
5034 static const bool translate_columns[] =
5035 {false, false, false, true, false, false, false};
5036
5037 if (pset.sversion < 90300)
5038 {
5039 char sverbuf[32];
5040
5041 pg_log_error("The server (version %s) does not support event triggers.",
5043 sverbuf, sizeof(sverbuf)));
5044 return true;
5045 }
5046
5048
5049 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching event triggers"));
5051 "SELECT evtname as \"%s\", "
5052 "evtevent as \"%s\", "
5053 "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
5054 " case evtenabled when 'O' then '%s'"
5055 " when 'R' then '%s'"
5056 " when 'A' then '%s'"
5057 " when 'D' then '%s' end as \"%s\",\n"
5058 " e.evtfoid::pg_catalog.regproc as \"%s\", "
5059 "pg_catalog.array_to_string(array(select x"
5060 " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
5061 gettext_noop("Name"),
5062 gettext_noop("Event"),
5063 gettext_noop("Owner"),
5064 gettext_noop("enabled"),
5065 gettext_noop("replica"),
5066 gettext_noop("always"),
5067 gettext_noop("disabled"),
5068 gettext_noop("Enabled"),
5069 gettext_noop("Function"),
5070 gettext_noop("Tags"));
5071 if (verbose)
5073 ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
5074 gettext_noop("Description"));
5076 "\nFROM pg_catalog.pg_event_trigger e ");
5077
5078 if (!validateSQLNamePattern(&buf, pattern, false, false,
5079 NULL, "evtname", NULL, NULL,
5080 NULL, 1))
5081 {
5083 return false;
5084 }
5085
5086 appendPQExpBufferStr(&buf, "ORDER BY 1");
5087
5088 res = PSQLexec(buf.data);
5090 if (!res)
5091 return false;
5092
5093 myopt.title = _("List of event triggers");
5094 myopt.translate_header = true;
5095 myopt.translate_columns = translate_columns;
5096 myopt.n_translate_columns = lengthof(translate_columns);
5097
5098 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5099
5100 PQclear(res);
5101 return true;
5102}
5103
5104/*
5105 * \dX
5106 *
5107 * Describes extended statistics.
5108 */
5109bool
5110listExtendedStats(const char *pattern, bool verbose)
5111{
5113 PGresult *res;
5115
5116 if (pset.sversion < 100000)
5117 {
5118 char sverbuf[32];
5119
5120 pg_log_error("The server (version %s) does not support extended statistics.",
5122 sverbuf, sizeof(sverbuf)));
5123 return true;
5124 }
5125
5127
5128 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching extended statistics"));
5130 "SELECT \n"
5131 "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS \"%s\", \n"
5132 "es.stxname AS \"%s\", \n",
5133 gettext_noop("Schema"),
5134 gettext_noop("Name"));
5135
5136 if (pset.sversion >= 140000)
5138 "pg_catalog.format('%%s FROM %%s', \n"
5139 " pg_catalog.pg_get_statisticsobjdef_columns(es.oid), \n"
5140 " es.stxrelid::pg_catalog.regclass) AS \"%s\"",
5141 gettext_noop("Definition"));
5142 else
5144 "pg_catalog.format('%%s FROM %%s', \n"
5145 " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
5146 " FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
5147 " JOIN pg_catalog.pg_attribute a \n"
5148 " ON (es.stxrelid = a.attrelid \n"
5149 " AND a.attnum = s.attnum \n"
5150 " AND NOT a.attisdropped)), \n"
5151 "es.stxrelid::pg_catalog.regclass) AS \"%s\"",
5152 gettext_noop("Definition"));
5153
5155 ",\nCASE WHEN " CppAsString2(STATS_EXT_NDISTINCT) " = any(es.stxkind) THEN 'defined' \n"
5156 "END AS \"%s\", \n"
5157 "CASE WHEN " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(es.stxkind) THEN 'defined' \n"
5158 "END AS \"%s\"",
5159 gettext_noop("Ndistinct"),
5160 gettext_noop("Dependencies"));
5161
5162 /*
5163 * Include the MCV statistics kind.
5164 */
5165 if (pset.sversion >= 120000)
5166 {
5168 ",\nCASE WHEN " CppAsString2(STATS_EXT_MCV) " = any(es.stxkind) THEN 'defined' \n"
5169 "END AS \"%s\" ",
5170 gettext_noop("MCV"));
5171 }
5172
5173 if (verbose)
5175 ", \npg_catalog.obj_description(oid, 'pg_statistic_ext') AS \"%s\"\n",
5176 gettext_noop("Description"));
5177
5179 " \nFROM pg_catalog.pg_statistic_ext es \n");
5180
5181 if (!validateSQLNamePattern(&buf, pattern,
5182 false, false,
5183 "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
5184 NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)",
5185 NULL, 3))
5186 {
5188 return false;
5189 }
5190
5191 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5192
5193 res = PSQLexec(buf.data);
5195 if (!res)
5196 return false;
5197
5198 myopt.title = _("List of extended statistics");
5199 myopt.translate_header = true;
5200
5201 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5202
5203 PQclear(res);
5204 return true;
5205}
5206
5207/*
5208 * \dC
5209 *
5210 * Describes casts.
5211 */
5212bool
5213listCasts(const char *pattern, bool verbose)
5214{
5216 PGresult *res;
5218 static const bool translate_columns[] = {false, false, false, true, true, false};
5219
5221
5222 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching casts"));
5224 "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
5225 " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n",
5226 gettext_noop("Source type"),
5227 gettext_noop("Target type"));
5228
5229 /*
5230 * We don't attempt to localize '(binary coercible)' or '(with inout)',
5231 * because there's too much risk of gettext translating a function name
5232 * that happens to match some string in the PO database.
5233 */
5235 " CASE WHEN c.castmethod = '%c' THEN '(binary coercible)'\n"
5236 " WHEN c.castmethod = '%c' THEN '(with inout)'\n"
5237 " ELSE p.proname\n"
5238 " END AS \"%s\",\n",
5241 gettext_noop("Function"));
5242
5244 " CASE WHEN c.castcontext = '%c' THEN '%s'\n"
5245 " WHEN c.castcontext = '%c' THEN '%s'\n"
5246 " ELSE '%s'\n"
5247 " END AS \"%s\"",
5249 gettext_noop("no"),
5251 gettext_noop("in assignment"),
5252 gettext_noop("yes"),
5253 gettext_noop("Implicit?"));
5254
5255 if (verbose)
5257 ",\n CASE WHEN p.proleakproof THEN '%s'\n"
5258 " ELSE '%s'\n"
5259 " END AS \"%s\",\n"
5260 " d.description AS \"%s\"",
5261 gettext_noop("yes"),
5262 gettext_noop("no"),
5263 gettext_noop("Leakproof?"),
5264 gettext_noop("Description"));
5265
5266 /*
5267 * We need a left join to pg_proc for binary casts; the others are just
5268 * paranoia.
5269 */
5271 "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
5272 " ON c.castfunc = p.oid\n"
5273 " LEFT JOIN pg_catalog.pg_type ts\n"
5274 " ON c.castsource = ts.oid\n"
5275 " LEFT JOIN pg_catalog.pg_namespace ns\n"
5276 " ON ns.oid = ts.typnamespace\n"
5277 " LEFT JOIN pg_catalog.pg_type tt\n"
5278 " ON c.casttarget = tt.oid\n"
5279 " LEFT JOIN pg_catalog.pg_namespace nt\n"
5280 " ON nt.oid = tt.typnamespace\n");
5281
5282 if (verbose)
5284 " LEFT JOIN pg_catalog.pg_description d\n"
5285 " ON d.classoid = c.tableoid AND d.objoid = "
5286 "c.oid AND d.objsubid = 0\n");
5287
5288 appendPQExpBufferStr(&buf, "WHERE ( (true");
5289
5290 /*
5291 * Match name pattern against either internal or external name of either
5292 * castsource or casttarget
5293 */
5294 if (!validateSQLNamePattern(&buf, pattern, true, false,
5295 "ns.nspname", "ts.typname",
5296 "pg_catalog.format_type(ts.oid, NULL)",
5297 "pg_catalog.pg_type_is_visible(ts.oid)",
5298 NULL, 3))
5299 goto error_return;
5300
5301 appendPQExpBufferStr(&buf, ") OR (true");
5302
5303 if (!validateSQLNamePattern(&buf, pattern, true, false,
5304 "nt.nspname", "tt.typname",
5305 "pg_catalog.format_type(tt.oid, NULL)",
5306 "pg_catalog.pg_type_is_visible(tt.oid)",
5307 NULL, 3))
5308 goto error_return;
5309
5310 appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
5311
5312 res = PSQLexec(buf.data);
5314 if (!res)
5315 return false;
5316
5317 myopt.title = _("List of casts");
5318 myopt.translate_header = true;
5319 myopt.translate_columns = translate_columns;
5320 myopt.n_translate_columns = lengthof(translate_columns);
5321
5322 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5323
5324 PQclear(res);
5325 return true;
5326
5329 return false;
5330}
5331
5332/*
5333 * \dO
5334 *
5335 * Describes collations.
5336 */
5337bool
5338listCollations(const char *pattern, bool verbose, bool showSystem)
5339{
5341 PGresult *res;
5343 static const bool translate_columns[] = {false, false, false, false, false, false, false, true, false};
5344
5346
5347 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching collations"));
5349 "SELECT\n"
5350 " n.nspname AS \"%s\",\n"
5351 " c.collname AS \"%s\",\n",
5352 gettext_noop("Schema"),
5353 gettext_noop("Name"));
5354
5355 if (pset.sversion >= 100000)
5357 " CASE c.collprovider "
5358 "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
5359 "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
5360 "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
5361 "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
5362 "END AS \"%s\",\n",
5363 gettext_noop("Provider"));
5364 else
5366 " 'libc' AS \"%s\",\n",
5367 gettext_noop("Provider"));
5368
5370 " c.collcollate AS \"%s\",\n"
5371 " c.collctype AS \"%s\",\n",
5372 gettext_noop("Collate"),
5373 gettext_noop("Ctype"));
5374
5375 if (pset.sversion >= 170000)
5377 " c.colllocale AS \"%s\",\n",
5378 gettext_noop("Locale"));
5379 else if (pset.sversion >= 150000)
5381 " c.colliculocale AS \"%s\",\n",
5382 gettext_noop("Locale"));
5383 else
5385 " c.collcollate AS \"%s\",\n",
5386 gettext_noop("Locale"));
5387
5388 if (pset.sversion >= 160000)
5390 " c.collicurules AS \"%s\",\n",
5391 gettext_noop("ICU Rules"));
5392 else
5394 " NULL AS \"%s\",\n",
5395 gettext_noop("ICU Rules"));
5396
5397 if (pset.sversion >= 120000)
5399 " CASE WHEN c.collisdeterministic THEN '%s' ELSE '%s' END AS \"%s\"",
5400 gettext_noop("yes"), gettext_noop("no"),
5401 gettext_noop("Deterministic?"));
5402 else
5404 " '%s' AS \"%s\"",
5405 gettext_noop("yes"),
5406 gettext_noop("Deterministic?"));
5407
5408 if (verbose)
5410 ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
5411 gettext_noop("Description"));
5412
5414 "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
5415 "WHERE n.oid = c.collnamespace\n");
5416
5417 if (!showSystem && !pattern)
5418 appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
5419 " AND n.nspname <> 'information_schema'\n");
5420
5421 /*
5422 * Hide collations that aren't usable in the current database's encoding.
5423 * If you think to change this, note that pg_collation_is_visible rejects
5424 * unusable collations, so you will need to hack name pattern processing
5425 * somehow to avoid inconsistent behavior.
5426 */
5427 appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
5428
5429 if (!validateSQLNamePattern(&buf, pattern, true, false,
5430 "n.nspname", "c.collname", NULL,
5431 "pg_catalog.pg_collation_is_visible(c.oid)",
5432 NULL, 3))
5433 {
5435 return false;
5436 }
5437
5438 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5439
5440 res = PSQLexec(buf.data);
5442 if (!res)
5443 return false;
5444
5445 myopt.title = _("List of collations");
5446 myopt.translate_header = true;
5447 myopt.translate_columns = translate_columns;
5448 myopt.n_translate_columns = lengthof(translate_columns);
5449
5450 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5451
5452 PQclear(res);
5453 return true;
5454}
5455
5456/*
5457 * \dn
5458 *
5459 * Describes schemas (namespaces)
5460 */
5461bool
5462listSchemas(const char *pattern, bool verbose, bool showSystem)
5463{
5465 PGresult *res;
5467 int pub_schema_tuples = 0;
5468 char **footers = NULL;
5469
5471
5472 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching schemas"));
5474 "SELECT n.nspname AS \"%s\",\n"
5475 " pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
5476 gettext_noop("Name"),
5477 gettext_noop("Owner"));
5478
5479 if (verbose)
5480 {
5481 appendPQExpBufferStr(&buf, ",\n ");
5482 printACLColumn(&buf, "n.nspacl");
5484 ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
5485 gettext_noop("Description"));
5486 }
5487
5489 "\nFROM pg_catalog.pg_namespace n\n");
5490
5491 if (!showSystem && !pattern)
5493 "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
5494
5495 if (!validateSQLNamePattern(&buf, pattern,
5496 !showSystem && !pattern, false,
5497 NULL, "n.nspname", NULL,
5498 NULL,
5499 NULL, 2))
5500 goto error_return;
5501
5502 appendPQExpBufferStr(&buf, "ORDER BY 1;");
5503
5504 res = PSQLexec(buf.data);
5505 if (!res)
5506 goto error_return;
5507
5508 myopt.title = _("List of schemas");
5509 myopt.translate_header = true;
5510
5511 if (pattern && pset.sversion >= 150000)
5512 {
5514 int i;
5515
5516 printfPQExpBuffer(&buf, "/* %s */\n",
5517 _("Get publications that publish this schema"));
5519 "SELECT pubname \n"
5520 "FROM pg_catalog.pg_publication p\n"
5521 " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
5522 " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
5523 "WHERE n.nspname = '%s'\n"
5524 "ORDER BY 1",
5525 pattern);
5526 result = PSQLexec(buf.data);
5527 if (!result)
5528 goto error_return;
5529 else
5531
5532 if (pub_schema_tuples > 0)
5533 {
5534 /*
5535 * Allocate memory for footers. Size of footers will be 1 (for
5536 * storing "Publications:" string) + publication schema mapping
5537 * count + 1 (for storing NULL).
5538 */
5539 footers = pg_malloc_array(char *, 1 + pub_schema_tuples + 1);
5540 footers[0] = pg_strdup(_("Publications:"));
5541
5542 /* Might be an empty set - that's ok */
5543 for (i = 0; i < pub_schema_tuples; i++)
5544 {
5545 printfPQExpBuffer(&buf, " \"%s\"",
5546 PQgetvalue(result, i, 0));
5547
5548 footers[i + 1] = pg_strdup(buf.data);
5549 }
5550
5551 footers[i + 1] = NULL;
5552 myopt.footers = footers;
5553 }
5554
5555 PQclear(result);
5556 }
5557
5558 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5559
5561 PQclear(res);
5562
5563 /* Free the memory allocated for the footer */
5564 if (footers)
5565 {
5566 char **footer = NULL;
5567
5568 for (footer = footers; *footer; footer++)
5569 pg_free(*footer);
5570
5571 pg_free(footers);
5572 }
5573
5574 return true;
5575
5578 return false;
5579}
5580
5581
5582/*
5583 * \dFp
5584 * list text search parsers
5585 */
5586bool
5587listTSParsers(const char *pattern, bool verbose)
5588{
5590 PGresult *res;
5592
5593 if (verbose)
5594 return listTSParsersVerbose(pattern);
5595
5597
5598 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search parsers"));
5600 "SELECT\n"
5601 " n.nspname as \"%s\",\n"
5602 " p.prsname as \"%s\",\n"
5603 " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
5604 "FROM pg_catalog.pg_ts_parser p\n"
5605 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
5606 gettext_noop("Schema"),
5607 gettext_noop("Name"),
5608 gettext_noop("Description")
5609 );
5610
5611 if (!validateSQLNamePattern(&buf, pattern, false, false,
5612 "n.nspname", "p.prsname", NULL,
5613 "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5614 NULL, 3))
5615 {
5617 return false;
5618 }
5619
5620 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5621
5622 res = PSQLexec(buf.data);
5624 if (!res)
5625 return false;
5626
5627 myopt.title = _("List of text search parsers");
5628 myopt.translate_header = true;
5629
5630 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5631
5632 PQclear(res);
5633 return true;
5634}
5635
5636/*
5637 * full description of parsers
5638 */
5639static bool
5640listTSParsersVerbose(const char *pattern)
5641{
5643 PGresult *res;
5644 int i;
5645
5647
5648 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search parsers"));
5650 "SELECT p.oid,\n"
5651 " n.nspname,\n"
5652 " p.prsname\n"
5653 "FROM pg_catalog.pg_ts_parser p\n"
5654 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
5655 );
5656
5657 if (!validateSQLNamePattern(&buf, pattern, false, false,
5658 "n.nspname", "p.prsname", NULL,
5659 "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5660 NULL, 3))
5661 {
5663 return false;
5664 }
5665
5666 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5667
5668 res = PSQLexec(buf.data);
5670 if (!res)
5671 return false;
5672
5673 if (PQntuples(res) == 0)
5674 {
5675 if (!pset.quiet)
5676 {
5677 if (pattern)
5678 pg_log_error("Did not find any text search parser named \"%s\".",
5679 pattern);
5680 else
5681 pg_log_error("Did not find any text search parsers.");
5682 }
5683 PQclear(res);
5684 return false;
5685 }
5686
5687 for (i = 0; i < PQntuples(res); i++)
5688 {
5689 const char *oid;
5690 const char *nspname = NULL;
5691 const char *prsname;
5692
5693 oid = PQgetvalue(res, i, 0);
5694 if (!PQgetisnull(res, i, 1))
5695 nspname = PQgetvalue(res, i, 1);
5696 prsname = PQgetvalue(res, i, 2);
5697
5698 if (!describeOneTSParser(oid, nspname, prsname))
5699 {
5700 PQclear(res);
5701 return false;
5702 }
5703
5704 if (cancel_pressed)
5705 {
5706 PQclear(res);
5707 return false;
5708 }
5709 }
5710
5711 PQclear(res);
5712 return true;
5713}
5714
5715static bool
5716describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
5717{
5719 PGresult *res;
5720 PQExpBufferData title;
5722 static const bool translate_columns[] = {true, false, false};
5723
5725
5726 printfPQExpBuffer(&buf, "/* %s */\n", _("Get text search parser details"));
5728 "SELECT '%s' AS \"%s\",\n"
5729 " p.prsstart::pg_catalog.regproc AS \"%s\",\n"
5730 " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\"\n"
5731 " FROM pg_catalog.pg_ts_parser p\n"
5732 " WHERE p.oid = '%s'\n"
5733 "UNION ALL\n"
5734 "SELECT '%s',\n"
5735 " p.prstoken::pg_catalog.regproc,\n"
5736 " pg_catalog.obj_description(p.prstoken, 'pg_proc')\n"
5737 " FROM pg_catalog.pg_ts_parser p\n"
5738 " WHERE p.oid = '%s'\n"
5739 "UNION ALL\n"
5740 "SELECT '%s',\n"
5741 " p.prsend::pg_catalog.regproc,\n"
5742 " pg_catalog.obj_description(p.prsend, 'pg_proc')\n"
5743 " FROM pg_catalog.pg_ts_parser p\n"
5744 " WHERE p.oid = '%s'\n"
5745 "UNION ALL\n"
5746 "SELECT '%s',\n"
5747 " p.prsheadline::pg_catalog.regproc,\n"
5748 " pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n"
5749 " FROM pg_catalog.pg_ts_parser p\n"
5750 " WHERE p.oid = '%s'\n"
5751 "UNION ALL\n"
5752 "SELECT '%s',\n"
5753 " p.prslextype::pg_catalog.regproc,\n"
5754 " pg_catalog.obj_description(p.prslextype, 'pg_proc')\n"
5755 " FROM pg_catalog.pg_ts_parser p\n"
5756 " WHERE p.oid = '%s';",
5757 gettext_noop("Start parse"),
5758 gettext_noop("Method"),
5759 gettext_noop("Function"),
5760 gettext_noop("Description"),
5761 oid,
5762 gettext_noop("Get next token"),
5763 oid,
5764 gettext_noop("End parse"),
5765 oid,
5766 gettext_noop("Get headline"),
5767 oid,
5768 gettext_noop("Get token types"),
5769 oid);
5770
5771 res = PSQLexec(buf.data);
5773 if (!res)
5774 return false;
5775
5776 initPQExpBuffer(&title);
5777 if (nspname)
5778 printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""),
5779 nspname, prsname);
5780 else
5781 printfPQExpBuffer(&title, _("Text search parser \"%s\""), prsname);
5782 myopt.title = title.data;
5783 myopt.footers = NULL;
5784 myopt.topt.default_footer = false;
5785 myopt.translate_header = true;
5786 myopt.translate_columns = translate_columns;
5787 myopt.n_translate_columns = lengthof(translate_columns);
5788
5789 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5790
5791 PQclear(res);
5792
5794
5795 printfPQExpBuffer(&buf, "/* %s */\n",
5796 _("Get text search parser token types"));
5798 "SELECT t.alias as \"%s\",\n"
5799 " t.description as \"%s\"\n"
5800 "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t\n"
5801 "ORDER BY 1;",
5802 gettext_noop("Token name"),
5803 gettext_noop("Description"),
5804 oid);
5805
5806 res = PSQLexec(buf.data);
5808 if (!res)
5809 {
5810 termPQExpBuffer(&title);
5811 return false;
5812 }
5813
5814 if (nspname)
5815 printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""),
5816 nspname, prsname);
5817 else
5818 printfPQExpBuffer(&title, _("Token types for parser \"%s\""), prsname);
5819 myopt.title = title.data;
5820 myopt.footers = NULL;
5821 myopt.topt.default_footer = true;
5822 myopt.translate_header = true;
5823 myopt.translate_columns = NULL;
5824 myopt.n_translate_columns = 0;
5825
5826 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5827
5828 termPQExpBuffer(&title);
5829 PQclear(res);
5830 return true;
5831}
5832
5833
5834/*
5835 * \dFd
5836 * list text search dictionaries
5837 */
5838bool
5839listTSDictionaries(const char *pattern, bool verbose)
5840{
5842 PGresult *res;
5844
5846
5847 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search dictionaries"));
5849 "SELECT\n"
5850 " n.nspname as \"%s\",\n"
5851 " d.dictname as \"%s\",\n",
5852 gettext_noop("Schema"),
5853 gettext_noop("Name"));
5854
5855 if (verbose)
5856 {
5858 " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM\n"
5859 " pg_catalog.pg_ts_template t\n"
5860 " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace\n"
5861 " WHERE d.dicttemplate = t.oid ) AS \"%s\",\n"
5862 " d.dictinitoption as \"%s\",\n",
5863 gettext_noop("Template"),
5864 gettext_noop("Init options"));
5865 }
5866
5868 " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
5869 gettext_noop("Description"));
5870
5871 appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n"
5872 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
5873
5874 if (!validateSQLNamePattern(&buf, pattern, false, false,
5875 "n.nspname", "d.dictname", NULL,
5876 "pg_catalog.pg_ts_dict_is_visible(d.oid)",
5877 NULL, 3))
5878 {
5880 return false;
5881 }
5882
5883 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5884
5885 res = PSQLexec(buf.data);
5887 if (!res)
5888 return false;
5889
5890 myopt.title = _("List of text search dictionaries");
5891 myopt.translate_header = true;
5892
5893 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5894
5895 PQclear(res);
5896 return true;
5897}
5898
5899
5900/*
5901 * \dFt
5902 * list text search templates
5903 */
5904bool
5905listTSTemplates(const char *pattern, bool verbose)
5906{
5908 PGresult *res;
5910
5912
5913 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search templates"));
5914 if (verbose)
5916 "SELECT\n"
5917 " n.nspname AS \"%s\",\n"
5918 " t.tmplname AS \"%s\",\n"
5919 " t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
5920 " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
5921 " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5922 gettext_noop("Schema"),
5923 gettext_noop("Name"),
5924 gettext_noop("Init"),
5925 gettext_noop("Lexize"),
5926 gettext_noop("Description"));
5927 else
5929 "SELECT\n"
5930 " n.nspname AS \"%s\",\n"
5931 " t.tmplname AS \"%s\",\n"
5932 " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5933 gettext_noop("Schema"),
5934 gettext_noop("Name"),
5935 gettext_noop("Description"));
5936
5937 appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n"
5938 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
5939
5940 if (!validateSQLNamePattern(&buf, pattern, false, false,
5941 "n.nspname", "t.tmplname", NULL,
5942 "pg_catalog.pg_ts_template_is_visible(t.oid)",
5943 NULL, 3))
5944 {
5946 return false;
5947 }
5948
5949 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5950
5951 res = PSQLexec(buf.data);
5953 if (!res)
5954 return false;
5955
5956 myopt.title = _("List of text search templates");
5957 myopt.translate_header = true;
5958
5959 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5960
5961 PQclear(res);
5962 return true;
5963}
5964
5965
5966/*
5967 * \dF
5968 * list text search configurations
5969 */
5970bool
5971listTSConfigs(const char *pattern, bool verbose)
5972{
5974 PGresult *res;
5976
5977 if (verbose)
5978 return listTSConfigsVerbose(pattern);
5979
5981
5982 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search configurations"));
5984 "SELECT\n"
5985 " n.nspname as \"%s\",\n"
5986 " c.cfgname as \"%s\",\n"
5987 " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
5988 "FROM pg_catalog.pg_ts_config c\n"
5989 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace\n",
5990 gettext_noop("Schema"),
5991 gettext_noop("Name"),
5992 gettext_noop("Description")
5993 );
5994
5995 if (!validateSQLNamePattern(&buf, pattern, false, false,
5996 "n.nspname", "c.cfgname", NULL,
5997 "pg_catalog.pg_ts_config_is_visible(c.oid)",
5998 NULL, 3))
5999 {
6001 return false;
6002 }
6003
6004 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6005
6006 res = PSQLexec(buf.data);
6008 if (!res)
6009 return false;
6010
6011 myopt.title = _("List of text search configurations");
6012 myopt.translate_header = true;
6013
6014 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6015
6016 PQclear(res);
6017 return true;
6018}
6019
6020static bool
6021listTSConfigsVerbose(const char *pattern)
6022{
6024 PGresult *res;
6025 int i;
6026
6028
6029 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching text search configurations"));
6031 "SELECT c.oid, c.cfgname,\n"
6032 " n.nspname,\n"
6033 " p.prsname,\n"
6034 " np.nspname as pnspname\n"
6035 "FROM pg_catalog.pg_ts_config c\n"
6036 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace,\n"
6037 " pg_catalog.pg_ts_parser p\n"
6038 " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace\n"
6039 "WHERE p.oid = c.cfgparser\n"
6040 );
6041
6042 if (!validateSQLNamePattern(&buf, pattern, true, false,
6043 "n.nspname", "c.cfgname", NULL,
6044 "pg_catalog.pg_ts_config_is_visible(c.oid)",
6045 NULL, 3))
6046 {
6048 return false;
6049 }
6050
6051 appendPQExpBufferStr(&buf, "ORDER BY 3, 2;");
6052
6053 res = PSQLexec(buf.data);
6055 if (!res)
6056 return false;
6057
6058 if (PQntuples(res) == 0)
6059 {
6060 if (!pset.quiet)
6061 {
6062 if (pattern)
6063 pg_log_error("Did not find any text search configuration named \"%s\".",
6064 pattern);
6065 else
6066 pg_log_error("Did not find any text search configurations.");
6067 }
6068 PQclear(res);
6069 return false;
6070 }
6071
6072 for (i = 0; i < PQntuples(res); i++)
6073 {
6074 const char *oid;
6075 const char *cfgname;
6076 const char *nspname = NULL;
6077 const char *prsname;
6078 const char *pnspname = NULL;
6079
6080 oid = PQgetvalue(res, i, 0);
6081 cfgname = PQgetvalue(res, i, 1);
6082 if (!PQgetisnull(res, i, 2))
6083 nspname = PQgetvalue(res, i, 2);
6084 prsname = PQgetvalue(res, i, 3);
6085 if (!PQgetisnull(res, i, 4))
6086 pnspname = PQgetvalue(res, i, 4);
6087
6088 if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
6089 {
6090 PQclear(res);
6091 return false;
6092 }
6093
6094 if (cancel_pressed)
6095 {
6096 PQclear(res);
6097 return false;
6098 }
6099 }
6100
6101 PQclear(res);
6102 return true;
6103}
6104
6105static bool
6106describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
6107 const char *pnspname, const char *prsname)
6108{
6110 title;
6111 PGresult *res;
6113
6115
6116 printfPQExpBuffer(&buf, "/* %s */\n", _("Get text search configuration details"));
6118 "SELECT\n"
6119 " ( SELECT t.alias FROM\n"
6120 " pg_catalog.ts_token_type(c.cfgparser) AS t\n"
6121 " WHERE t.tokid = m.maptokentype ) AS \"%s\",\n"
6122 " pg_catalog.btrim(\n"
6123 " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary\n"
6124 " FROM pg_catalog.pg_ts_config_map AS mm\n"
6125 " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype\n"
6126 " ORDER BY mapcfg, maptokentype, mapseqno\n"
6127 " ) :: pg_catalog.text,\n"
6128 " '{}') AS \"%s\"\n"
6129 "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m\n"
6130 "WHERE c.oid = '%s' AND m.mapcfg = c.oid\n"
6131 "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser\n"
6132 "ORDER BY 1;",
6133 gettext_noop("Token"),
6134 gettext_noop("Dictionaries"),
6135 oid);
6136
6137 res = PSQLexec(buf.data);
6139 if (!res)
6140 return false;
6141
6142 initPQExpBuffer(&title);
6143
6144 if (nspname)
6145 appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
6146 nspname, cfgname);
6147 else
6148 appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
6149 cfgname);
6150
6151 if (pnspname)
6152 appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
6153 pnspname, prsname);
6154 else
6155 appendPQExpBuffer(&title, _("\nParser: \"%s\""),
6156 prsname);
6157
6158 myopt.title = title.data;
6159 myopt.footers = NULL;
6160 myopt.topt.default_footer = false;
6161 myopt.translate_header = true;
6162
6163 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6164
6165 termPQExpBuffer(&title);
6166
6167 PQclear(res);
6168 return true;
6169}
6170
6171
6172/*
6173 * \dew
6174 *
6175 * Describes foreign-data wrappers
6176 */
6177bool
6178listForeignDataWrappers(const char *pattern, bool verbose)
6179{
6181 PGresult *res;
6183
6185
6186 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign-data wrappers"));
6188 "SELECT fdw.fdwname AS \"%s\",\n"
6189 " pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n"
6190 " fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n"
6191 " fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
6192 gettext_noop("Name"),
6193 gettext_noop("Owner"),
6194 gettext_noop("Handler"),
6195 gettext_noop("Validator"));
6196
6197 if (verbose)
6198 {
6199 appendPQExpBufferStr(&buf, ",\n ");
6200 printACLColumn(&buf, "fdwacl");
6202 ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE "
6203 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6204 " pg_catalog.quote_ident(option_name) || ' ' || "
6205 " pg_catalog.quote_literal(option_value) FROM "
6206 " pg_catalog.pg_options_to_table(fdwoptions)), ', ') || ')' "
6207 " END AS \"%s\""
6208 ",\n d.description AS \"%s\" ",
6209 gettext_noop("FDW options"),
6210 gettext_noop("Description"));
6211 }
6212
6213 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
6214
6215 if (verbose)
6217 "LEFT JOIN pg_catalog.pg_description d\n"
6218 " ON d.classoid = fdw.tableoid "
6219 "AND d.objoid = fdw.oid AND d.objsubid = 0\n");
6220
6221 if (!validateSQLNamePattern(&buf, pattern, false, false,
6222 NULL, "fdwname", NULL, NULL,
6223 NULL, 1))
6224 {
6226 return false;
6227 }
6228
6229 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6230
6231 res = PSQLexec(buf.data);
6233 if (!res)
6234 return false;
6235
6236 myopt.title = _("List of foreign-data wrappers");
6237 myopt.translate_header = true;
6238
6239 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6240
6241 PQclear(res);
6242 return true;
6243}
6244
6245/*
6246 * \des
6247 *
6248 * Describes foreign servers.
6249 */
6250bool
6251listForeignServers(const char *pattern, bool verbose)
6252{
6254 PGresult *res;
6256
6258
6259 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign servers"));
6261 "SELECT s.srvname AS \"%s\",\n"
6262 " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
6263 " f.fdwname AS \"%s\"",
6264 gettext_noop("Name"),
6265 gettext_noop("Owner"),
6266 gettext_noop("Foreign-data wrapper"));
6267
6268 if (verbose)
6269 {
6270 appendPQExpBufferStr(&buf, ",\n ");
6271 printACLColumn(&buf, "s.srvacl");
6273 ",\n"
6274 " s.srvtype AS \"%s\",\n"
6275 " s.srvversion AS \"%s\",\n"
6276 " CASE WHEN srvoptions IS NULL THEN '' ELSE "
6277 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6278 " pg_catalog.quote_ident(option_name) || ' ' || "
6279 " pg_catalog.quote_literal(option_value) FROM "
6280 " pg_catalog.pg_options_to_table(srvoptions)), ', ') || ')' "
6281 " END AS \"%s\",\n"
6282 " d.description AS \"%s\"",
6283 gettext_noop("Type"),
6284 gettext_noop("Version"),
6285 gettext_noop("FDW options"),
6286 gettext_noop("Description"));
6287 }
6288
6290 "\nFROM pg_catalog.pg_foreign_server s\n"
6291 " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
6292
6293 if (verbose)
6295 "LEFT JOIN pg_catalog.pg_description d\n "
6296 "ON d.classoid = s.tableoid AND d.objoid = s.oid "
6297 "AND d.objsubid = 0\n");
6298
6299 if (!validateSQLNamePattern(&buf, pattern, false, false,
6300 NULL, "s.srvname", NULL, NULL,
6301 NULL, 1))
6302 {
6304 return false;
6305 }
6306
6307 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6308
6309 res = PSQLexec(buf.data);
6311 if (!res)
6312 return false;
6313
6314 myopt.title = _("List of foreign servers");
6315 myopt.translate_header = true;
6316
6317 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6318
6319 PQclear(res);
6320 return true;
6321}
6322
6323/*
6324 * \deu
6325 *
6326 * Describes user mappings.
6327 */
6328bool
6329listUserMappings(const char *pattern, bool verbose)
6330{
6332 PGresult *res;
6334
6336
6337 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching user mappings"));
6339 "SELECT um.srvname AS \"%s\",\n"
6340 " um.usename AS \"%s\"",
6341 gettext_noop("Server"),
6342 gettext_noop("User name"));
6343
6344 if (verbose)
6346 ",\n CASE WHEN umoptions IS NULL THEN '' ELSE "
6347 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6348 " pg_catalog.quote_ident(option_name) || ' ' || "
6349 " pg_catalog.quote_literal(option_value) FROM "
6350 " pg_catalog.pg_options_to_table(umoptions)), ', ') || ')' "
6351 " END AS \"%s\"",
6352 gettext_noop("FDW options"));
6353
6354 appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
6355
6356 if (!validateSQLNamePattern(&buf, pattern, false, false,
6357 NULL, "um.srvname", "um.usename", NULL,
6358 NULL, 1))
6359 {
6361 return false;
6362 }
6363
6364 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6365
6366 res = PSQLexec(buf.data);
6368 if (!res)
6369 return false;
6370
6371 myopt.title = _("List of user mappings");
6372 myopt.translate_header = true;
6373
6374 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6375
6376 PQclear(res);
6377 return true;
6378}
6379
6380/*
6381 * \det
6382 *
6383 * Describes foreign tables.
6384 */
6385bool
6386listForeignTables(const char *pattern, bool verbose)
6387{
6389 PGresult *res;
6391
6393
6394 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching foreign tables"));
6396 "SELECT n.nspname AS \"%s\",\n"
6397 " c.relname AS \"%s\",\n"
6398 " s.srvname AS \"%s\"",
6399 gettext_noop("Schema"),
6400 gettext_noop("Table"),
6401 gettext_noop("Server"));
6402
6403 if (verbose)
6405 ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE "
6406 " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6407 " pg_catalog.quote_ident(option_name) || ' ' || "
6408 " pg_catalog.quote_literal(option_value) FROM "
6409 " pg_catalog.pg_options_to_table(ftoptions)), ', ') || ')' "
6410 " END AS \"%s\",\n"
6411 " d.description AS \"%s\"",
6412 gettext_noop("FDW options"),
6413 gettext_noop("Description"));
6414
6416 "\nFROM pg_catalog.pg_foreign_table ft\n"
6417 " INNER JOIN pg_catalog.pg_class c"
6418 " ON c.oid = ft.ftrelid\n"
6419 " INNER JOIN pg_catalog.pg_namespace n"
6420 " ON n.oid = c.relnamespace\n"
6421 " INNER JOIN pg_catalog.pg_foreign_server s"
6422 " ON s.oid = ft.ftserver\n");
6423 if (verbose)
6425 " LEFT JOIN pg_catalog.pg_description d\n"
6426 " ON d.classoid = c.tableoid AND "
6427 "d.objoid = c.oid AND d.objsubid = 0\n");
6428
6429 if (!validateSQLNamePattern(&buf, pattern, false, false,
6430 "n.nspname", "c.relname", NULL,
6431 "pg_catalog.pg_table_is_visible(c.oid)",
6432 NULL, 3))
6433 {
6435 return false;
6436 }
6437
6438 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6439
6440 res = PSQLexec(buf.data);
6442 if (!res)
6443 return false;
6444
6445 myopt.title = _("List of foreign tables");
6446 myopt.translate_header = true;
6447
6448 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6449
6450 PQclear(res);
6451 return true;
6452}
6453
6454/*
6455 * \dx
6456 *
6457 * Briefly describes installed extensions.
6458 */
6459bool
6460listExtensions(const char *pattern)
6461{
6463 PGresult *res;
6465
6467
6468 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching installed extensions"));
6470 "SELECT e.extname AS \"%s\", "
6471 "e.extversion AS \"%s\", ae.default_version AS \"%s\","
6472 "n.nspname AS \"%s\", d.description AS \"%s\"\n"
6473 "FROM pg_catalog.pg_extension e "
6474 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
6475 "LEFT JOIN pg_catalog.pg_description d ON d.objoid = e.oid "
6476 "AND d.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
6477 "LEFT JOIN pg_catalog.pg_available_extensions() ae(name, default_version, comment) ON ae.name = e.extname\n",
6478 gettext_noop("Name"),
6479 gettext_noop("Version"),
6480 gettext_noop("Default version"),
6481 gettext_noop("Schema"),
6482 gettext_noop("Description"));
6483
6484 if (!validateSQLNamePattern(&buf, pattern,
6485 false, false,
6486 NULL, "e.extname", NULL,
6487 NULL,
6488 NULL, 1))
6489 {
6491 return false;
6492 }
6493
6494 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6495
6496 res = PSQLexec(buf.data);
6498 if (!res)
6499 return false;
6500
6501 myopt.title = _("List of installed extensions");
6502 myopt.translate_header = true;
6503
6504 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6505
6506 PQclear(res);
6507 return true;
6508}
6509
6510/*
6511 * \dx+
6512 *
6513 * List contents of installed extensions.
6514 */
6515bool
6516listExtensionContents(const char *pattern)
6517{
6519 PGresult *res;
6520 int i;
6521
6523
6524 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching installed extensions"));
6526 "SELECT e.extname, e.oid\n"
6527 "FROM pg_catalog.pg_extension e\n");
6528
6529 if (!validateSQLNamePattern(&buf, pattern,
6530 false, false,
6531 NULL, "e.extname", NULL,
6532 NULL,
6533 NULL, 1))
6534 {
6536 return false;
6537 }
6538
6539 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6540
6541 res = PSQLexec(buf.data);
6543 if (!res)
6544 return false;
6545
6546 if (PQntuples(res) == 0)
6547 {
6548 if (!pset.quiet)
6549 {
6550 if (pattern)
6551 pg_log_error("Did not find any extension named \"%s\".",
6552 pattern);
6553 else
6554 pg_log_error("Did not find any extensions.");
6555 }
6556 PQclear(res);
6557 return false;
6558 }
6559
6560 for (i = 0; i < PQntuples(res); i++)
6561 {
6562 const char *extname;
6563 const char *oid;
6564
6565 extname = PQgetvalue(res, i, 0);
6566 oid = PQgetvalue(res, i, 1);
6567
6568 if (!listOneExtensionContents(extname, oid))
6569 {
6570 PQclear(res);
6571 return false;
6572 }
6573 if (cancel_pressed)
6574 {
6575 PQclear(res);
6576 return false;
6577 }
6578 }
6579
6580 PQclear(res);
6581 return true;
6582}
6583
6584static bool
6585listOneExtensionContents(const char *extname, const char *oid)
6586{
6588 PGresult *res;
6589 PQExpBufferData title;
6591
6593
6594 printfPQExpBuffer(&buf, "/* %s */\n", _("Get installed extension's contents"));
6596 "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
6597 "FROM pg_catalog.pg_depend\n"
6598 "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
6599 "ORDER BY 1;",
6600 gettext_noop("Object description"),
6601 oid);
6602
6603 res = PSQLexec(buf.data);
6605 if (!res)
6606 return false;
6607
6608 initPQExpBuffer(&title);
6609 printfPQExpBuffer(&title, _("Objects in extension \"%s\""), extname);
6610 myopt.title = title.data;
6611 myopt.translate_header = true;
6612
6613 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6614
6615 termPQExpBuffer(&title);
6616 PQclear(res);
6617 return true;
6618}
6619
6620/*
6621 * validateSQLNamePattern
6622 *
6623 * Wrapper around string_utils's processSQLNamePattern which also checks the
6624 * pattern's validity. In addition to that function's parameters, takes a
6625 * 'maxparts' parameter specifying the maximum number of dotted names the
6626 * pattern is allowed to have, and a 'added_clause' parameter that returns by
6627 * reference whether a clause was added to 'buf'. Returns whether the pattern
6628 * passed validation, after logging any errors.
6629 */
6630static bool
6632 bool force_escape, const char *schemavar,
6633 const char *namevar, const char *altnamevar,
6634 const char *visibilityrule, bool *added_clause,
6635 int maxparts)
6636{
6638 int dotcnt;
6639 bool added;
6640
6645 if (added_clause != NULL)
6647
6648 if (dotcnt >= maxparts)
6649 {
6650 pg_log_error("improper qualified name (too many dotted names): %s",
6651 pattern);
6652 goto error_return;
6653 }
6654
6655 if (maxparts > 1 && dotcnt == maxparts - 1)
6656 {
6657 if (PQdb(pset.db) == NULL)
6658 {
6659 pg_log_error("You are currently not connected to a database.");
6660 goto error_return;
6661 }
6662 if (strcmp(PQdb(pset.db), dbbuf.data) != 0)
6663 {
6664 pg_log_error("cross-database references are not implemented: %s",
6665 pattern);
6666 goto error_return;
6667 }
6668 }
6670 return true;
6671
6674 return false;
6675}
6676
6677/*
6678 * \dRp
6679 * Lists publications.
6680 *
6681 * Takes an optional regexp to select particular publications
6682 */
6683bool
6684listPublications(const char *pattern)
6685{
6687 PGresult *res;
6689 static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
6690
6691 if (pset.sversion < 100000)
6692 {
6693 char sverbuf[32];
6694
6695 pg_log_error("The server (version %s) does not support publications.",
6697 sverbuf, sizeof(sverbuf)));
6698 return true;
6699 }
6700
6702
6703 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching publications"));
6705 "SELECT pubname AS \"%s\",\n"
6706 " pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
6707 " puballtables AS \"%s\"",
6708 gettext_noop("Name"),
6709 gettext_noop("Owner"),
6710 gettext_noop("All tables"));
6711
6712 if (pset.sversion >= 190000)
6714 ",\n puballsequences AS \"%s\"",
6715 gettext_noop("All sequences"));
6716
6718 ",\n pubinsert AS \"%s\",\n"
6719 " pubupdate AS \"%s\",\n"
6720 " pubdelete AS \"%s\"",
6721 gettext_noop("Inserts"),
6722 gettext_noop("Updates"),
6723 gettext_noop("Deletes"));
6724 if (pset.sversion >= 110000)
6726 ",\n pubtruncate AS \"%s\"",
6727 gettext_noop("Truncates"));
6728 if (pset.sversion >= 180000)
6730 ",\n (CASE pubgencols\n"
6731 " WHEN '%c' THEN 'none'\n"
6732 " WHEN '%c' THEN 'stored'\n"
6733 " END) AS \"%s\"",
6736 gettext_noop("Generated columns"));
6737 if (pset.sversion >= 130000)
6739 ",\n pubviaroot AS \"%s\"",
6740 gettext_noop("Via root"));
6741
6743 "\nFROM pg_catalog.pg_publication\n");
6744
6745 if (!validateSQLNamePattern(&buf, pattern, false, false,
6746 NULL, "pubname", NULL,
6747 NULL,
6748 NULL, 1))
6749 {
6751 return false;
6752 }
6753
6754 appendPQExpBufferStr(&buf, "ORDER BY 1;");
6755
6756 res = PSQLexec(buf.data);
6758 if (!res)
6759 return false;
6760
6761 myopt.title = _("List of publications");
6762 myopt.translate_header = true;
6763 myopt.translate_columns = translate_columns;
6764 myopt.n_translate_columns = lengthof(translate_columns);
6765
6766 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6767
6768 PQclear(res);
6769
6770 return true;
6771}
6772
6773/*
6774 * Add footer to publication description.
6775 */
6776static bool
6778 bool as_schema, printTableContent *const cont)
6779{
6780 PGresult *res;
6781 int count = 0;
6782 int i = 0;
6783
6784 res = PSQLexec(buf->data);
6785 if (!res)
6786 return false;
6787 else
6788 count = PQntuples(res);
6789
6790 if (count > 0)
6792
6793 for (i = 0; i < count; i++)
6794 {
6795 if (as_schema)
6796 printfPQExpBuffer(buf, " \"%s\"", PQgetvalue(res, i, 0));
6797 else
6798 {
6799 printfPQExpBuffer(buf, " \"%s.%s\"", PQgetvalue(res, i, 0),
6800 PQgetvalue(res, i, 1));
6801
6802 if (!PQgetisnull(res, i, 3))
6803 appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
6804
6805 if (!PQgetisnull(res, i, 2))
6806 appendPQExpBuffer(buf, " WHERE %s", PQgetvalue(res, i, 2));
6807 }
6808
6810 }
6811
6812 PQclear(res);
6813 return true;
6814}
6815
6816/*
6817 * \dRp+
6818 * Describes publications including the contents.
6819 *
6820 * Takes an optional regexp to select particular publications
6821 */
6822bool
6823describePublications(const char *pattern)
6824{
6826 int i;
6827 PGresult *res;
6828 bool has_pubtruncate;
6829 bool has_pubgencols;
6830 bool has_pubviaroot;
6831 bool has_pubsequence;
6832 int ncols = 6;
6833 int nrows = 1;
6834
6835 PQExpBufferData title;
6837
6838 if (pset.sversion < 100000)
6839 {
6840 char sverbuf[32];
6841
6842 pg_log_error("The server (version %s) does not support publications.",
6844 sverbuf, sizeof(sverbuf)));
6845 return true;
6846 }
6847
6848 has_pubsequence = (pset.sversion >= 190000);
6849 has_pubtruncate = (pset.sversion >= 110000);
6850 has_pubgencols = (pset.sversion >= 180000);
6851 has_pubviaroot = (pset.sversion >= 130000);
6852
6854
6855 printfPQExpBuffer(&buf, "/* %s */\n", _("Get details about matching publications"));
6857 "SELECT oid, pubname,\n"
6858 " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
6859 " puballtables");
6860
6861 if (has_pubsequence)
6863 ", puballsequences");
6864 else
6866 ", false AS puballsequences");
6867
6869 ", pubinsert, pubupdate, pubdelete");
6870
6871 if (has_pubtruncate)
6873 ", pubtruncate");
6874 else
6876 ", false AS pubtruncate");
6877
6878 if (has_pubgencols)
6880 ", (CASE pubgencols\n"
6881 " WHEN '%c' THEN 'none'\n"
6882 " WHEN '%c' THEN 'stored'\n"
6883 " END) AS \"%s\"\n",
6886 gettext_noop("Generated columns"));
6887 else
6889 ", 'none' AS pubgencols");
6890
6891 if (has_pubviaroot)
6893 ", pubviaroot");
6894 else
6896 ", false AS pubviaroot");
6897
6899 ", pg_catalog.obj_description(oid, 'pg_publication')");
6900
6902 "\nFROM pg_catalog.pg_publication\n");
6903
6904 if (!validateSQLNamePattern(&buf, pattern, false, false,
6905 NULL, "pubname", NULL,
6906 NULL,
6907 NULL, 1))
6908 {
6910 return false;
6911 }
6912
6913 appendPQExpBufferStr(&buf, "ORDER BY 2;");
6914
6915 res = PSQLexec(buf.data);
6916 if (!res)
6917 {
6919 return false;
6920 }
6921
6922 if (PQntuples(res) == 0)
6923 {
6924 if (!pset.quiet)
6925 {
6926 if (pattern)
6927 pg_log_error("Did not find any publication named \"%s\".",
6928 pattern);
6929 else
6930 pg_log_error("Did not find any publications.");
6931 }
6932
6934 PQclear(res);
6935 return false;
6936 }
6937
6938 if (has_pubsequence)
6939 ncols++;
6940 if (has_pubtruncate)
6941 ncols++;
6942 if (has_pubgencols)
6943 ncols++;
6944 if (has_pubviaroot)
6945 ncols++;
6946
6947 for (i = 0; i < PQntuples(res); i++)
6948 {
6949 const char align = 'l';
6950 char *pubid = PQgetvalue(res, i, 0);
6951 char *pubname = PQgetvalue(res, i, 1);
6952 bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
6954
6955 initPQExpBuffer(&title);
6956 printfPQExpBuffer(&title, _("Publication %s"), pubname);
6957 printTableInit(&cont, &myopt, title.data, ncols, nrows);
6958
6959 printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
6960 printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
6961 if (has_pubsequence)
6962 printTableAddHeader(&cont, gettext_noop("All sequences"), true, align);
6963 printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
6964 printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
6965 printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
6966 if (has_pubtruncate)
6967 printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
6968 if (has_pubgencols)
6969 printTableAddHeader(&cont, gettext_noop("Generated columns"), true, align);
6970 if (has_pubviaroot)
6971 printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
6972 printTableAddHeader(&cont, gettext_noop("Description"), true, align);
6973
6974 printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
6975 printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
6976 if (has_pubsequence)
6977 printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
6978 printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
6979 printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
6980 printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
6981 if (has_pubtruncate)
6982 printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
6983 if (has_pubgencols)
6984 printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
6985 if (has_pubviaroot)
6986 printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false);
6987 printTableAddCell(&cont, PQgetvalue(res, i, 11), false, false);
6988
6989 if (!puballtables)
6990 {
6991 /* Get the tables for the specified publication */
6992 printfPQExpBuffer(&buf, "/* %s */\n",
6993 _("Get tables published by this publication"));
6994 appendPQExpBufferStr(&buf, "SELECT n.nspname, c.relname");
6995 if (pset.sversion >= 150000)
6996 {
6998 ", pg_get_expr(pr.prqual, c.oid)");
7000 ", (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
7001 " pg_catalog.array_to_string("
7002 " ARRAY(SELECT attname\n"
7003 " FROM\n"
7004 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
7005 " pg_catalog.pg_attribute\n"
7006 " WHERE attrelid = c.oid AND attnum = prattrs[s]), ', ')\n"
7007 " ELSE NULL END)");
7008 }
7009 else
7011 ", NULL, NULL");
7013 "\nFROM pg_catalog.pg_class c,\n"
7014 " pg_catalog.pg_namespace n,\n"
7015 " pg_catalog.pg_publication_rel pr\n"
7016 "WHERE c.relnamespace = n.oid\n"
7017 " AND c.oid = pr.prrelid\n"
7018 " AND pr.prpubid = '%s'\n", pubid);
7019
7020 if (pset.sversion >= 190000)
7021 appendPQExpBufferStr(&buf, " AND NOT pr.prexcept\n");
7022
7023 appendPQExpBufferStr(&buf, "ORDER BY 1,2");
7024 if (!addFooterToPublicationDesc(&buf, _("Tables:"), false, &cont))
7025 goto error_return;
7026
7027 if (pset.sversion >= 150000)
7028 {
7029 /* Get the schemas for the specified publication */
7030 printfPQExpBuffer(&buf, "/* %s */\n",
7031 _("Get schemas published by this publication"));
7033 "SELECT n.nspname\n"
7034 "FROM pg_catalog.pg_namespace n\n"
7035 " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
7036 "WHERE pn.pnpubid = '%s'\n"
7037 "ORDER BY 1", pubid);
7038 if (!addFooterToPublicationDesc(&buf, _("Tables from schemas:"),
7039 true, &cont))
7040 goto error_return;
7041 }
7042 }
7043 else
7044 {
7045 if (pset.sversion >= 190000)
7046 {
7047 /* Get tables in the EXCEPT clause for this publication */
7048 printfPQExpBuffer(&buf, "/* %s */\n",
7049 _("Get tables excluded by this publication"));
7051 "SELECT n.nspname || '.' || c.relname\n"
7052 "FROM pg_catalog.pg_class c\n"
7053 " JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
7054 " JOIN pg_catalog.pg_publication_rel pr ON c.oid = pr.prrelid\n"
7055 "WHERE pr.prpubid = '%s' AND pr.prexcept\n"
7056 "ORDER BY 1", pubid);
7057 if (!addFooterToPublicationDesc(&buf, _("Except tables:"),
7058 true, &cont))
7059 goto error_return;
7060 }
7061 }
7062
7065
7066 termPQExpBuffer(&title);
7067 }
7068
7070 PQclear(res);
7071
7072 return true;
7073
7076 PQclear(res);
7078 termPQExpBuffer(&title);
7079 return false;
7080}
7081
7082/*
7083 * \dRs
7084 * Describes subscriptions.
7085 *
7086 * Takes an optional regexp to select particular subscriptions
7087 */
7088bool
7089describeSubscriptions(const char *pattern, bool verbose)
7090{
7092 PGresult *res;
7094 static const bool translate_columns[] = {false, false, false, false,
7095 false, false, false, false, false, false, false, false, false, false,
7096 false, false, false, false, false, false, false};
7097
7098 if (pset.sversion < 100000)
7099 {
7100 char sverbuf[32];
7101
7102 pg_log_error("The server (version %s) does not support subscriptions.",
7104 sverbuf, sizeof(sverbuf)));
7105 return true;
7106 }
7107
7109
7110 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching subscriptions"));
7112 "SELECT subname AS \"%s\"\n"
7113 ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
7114 ", subenabled AS \"%s\"\n"
7115 ", subpublications AS \"%s\"\n",
7116 gettext_noop("Name"),
7117 gettext_noop("Owner"),
7118 gettext_noop("Enabled"),
7119 gettext_noop("Publication"));
7120
7121 if (verbose)
7122 {
7123 /* Binary mode and streaming are only supported in v14 and higher */
7124 if (pset.sversion >= 140000)
7125 {
7127 ", subbinary AS \"%s\"\n",
7128 gettext_noop("Binary"));
7129
7130 if (pset.sversion >= 160000)
7132 ", (CASE substream\n"
7133 " WHEN " CppAsString2(LOGICALREP_STREAM_OFF) " THEN 'off'\n"
7134 " WHEN " CppAsString2(LOGICALREP_STREAM_ON) " THEN 'on'\n"
7135 " WHEN " CppAsString2(LOGICALREP_STREAM_PARALLEL) " THEN 'parallel'\n"
7136 " END) AS \"%s\"\n",
7137 gettext_noop("Streaming"));
7138 else
7140 ", substream AS \"%s\"\n",
7141 gettext_noop("Streaming"));
7142 }
7143
7144 /* Two_phase and disable_on_error are only supported in v15 and higher */
7145 if (pset.sversion >= 150000)
7147 ", subtwophasestate AS \"%s\"\n"
7148 ", subdisableonerr AS \"%s\"\n",
7149 gettext_noop("Two-phase commit"),
7150 gettext_noop("Disable on error"));
7151
7152 if (pset.sversion >= 160000)
7154 ", suborigin AS \"%s\"\n"
7155 ", subpasswordrequired AS \"%s\"\n"
7156 ", subrunasowner AS \"%s\"\n",
7157 gettext_noop("Origin"),
7158 gettext_noop("Password required"),
7159 gettext_noop("Run as owner?"));
7160
7161 if (pset.sversion >= 170000)
7163 ", subfailover AS \"%s\"\n",
7164 gettext_noop("Failover"));
7165 if (pset.sversion >= 190000)
7166 {
7168 ", (select srvname from pg_foreign_server where oid=subserver) AS \"%s\"\n",
7169 gettext_noop("Server"));
7170
7172 ", subretaindeadtuples AS \"%s\"\n",
7173 gettext_noop("Retain dead tuples"));
7174
7176 ", submaxretention AS \"%s\"\n",
7177 gettext_noop("Max retention duration"));
7178
7180 ", subretentionactive AS \"%s\"\n",
7181 gettext_noop("Retention active"));
7182 }
7183
7185 ", subsynccommit AS \"%s\"\n"
7186 ", subconninfo AS \"%s\"\n",
7187 gettext_noop("Synchronous commit"),
7188 gettext_noop("Conninfo"));
7189
7190 if (pset.sversion >= 190000)
7192 ", subwalrcvtimeout AS \"%s\"\n",
7193 gettext_noop("Receiver timeout"));
7194
7195 /* Skip LSN is only supported in v15 and higher */
7196 if (pset.sversion >= 150000)
7198 ", subskiplsn AS \"%s\"\n",
7199 gettext_noop("Skip LSN"));
7200
7202 ", pg_catalog.obj_description(oid, 'pg_subscription') AS \"%s\"\n",
7203 gettext_noop("Description"));
7204 }
7205
7206 /* Only display subscriptions in current database. */
7208 "FROM pg_catalog.pg_subscription\n"
7209 "WHERE subdbid = (SELECT oid\n"
7210 " FROM pg_catalog.pg_database\n"
7211 " WHERE datname = pg_catalog.current_database())");
7212
7213 if (!validateSQLNamePattern(&buf, pattern, true, false,
7214 NULL, "subname", NULL,
7215 NULL,
7216 NULL, 1))
7217 {
7219 return false;
7220 }
7221
7222 appendPQExpBufferStr(&buf, "ORDER BY 1;");
7223
7224 res = PSQLexec(buf.data);
7226 if (!res)
7227 return false;
7228
7229 myopt.title = _("List of subscriptions");
7230 myopt.translate_header = true;
7231 myopt.translate_columns = translate_columns;
7232 myopt.n_translate_columns = lengthof(translate_columns);
7233
7234 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7235
7236 PQclear(res);
7237 return true;
7238}
7239
7240/*
7241 * printACLColumn
7242 *
7243 * Helper function for consistently formatting ACL (privilege) columns.
7244 * The proper targetlist entry is appended to buf. Note lack of any
7245 * whitespace or comma decoration.
7246 *
7247 * If you change this, see also the handling of attacl in permissionsList(),
7248 * which can't conveniently use this code.
7249 */
7250static void
7251printACLColumn(PQExpBuffer buf, const char *colname)
7252{
7254 "CASE"
7255 " WHEN pg_catalog.array_length(%s, 1) = 0 THEN '%s'"
7256 " ELSE pg_catalog.array_to_string(%s, E'\\n')"
7257 " END AS \"%s\"",
7258 colname, gettext_noop("(none)"),
7259 colname, gettext_noop("Access privileges"));
7260}
7261
7262/*
7263 * \dAc
7264 * Lists operator classes
7265 *
7266 * Takes optional regexps to filter by index access method and input data type.
7267 */
7268bool
7270 const char *type_pattern, bool verbose)
7271{
7273 PGresult *res;
7275 bool have_where = false;
7276 static const bool translate_columns[] = {false, false, false, false, false, false, false};
7277
7279
7280 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching operator classes"));
7282 "SELECT\n"
7283 " am.amname AS \"%s\",\n"
7284 " pg_catalog.format_type(c.opcintype, NULL) AS \"%s\",\n"
7285 " CASE\n"
7286 " WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n"
7287 " THEN pg_catalog.format_type(c.opckeytype, NULL)\n"
7288 " ELSE NULL\n"
7289 " END AS \"%s\",\n"
7290 " CASE\n"
7291 " WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n"
7292 " THEN pg_catalog.format('%%I', c.opcname)\n"
7293 " ELSE pg_catalog.format('%%I.%%I', n.nspname, c.opcname)\n"
7294 " END AS \"%s\",\n"
7295 " (CASE WHEN c.opcdefault\n"
7296 " THEN '%s'\n"
7297 " ELSE '%s'\n"
7298 " END) AS \"%s\"",
7299 gettext_noop("AM"),
7300 gettext_noop("Input type"),
7301 gettext_noop("Storage type"),
7302 gettext_noop("Operator class"),
7303 gettext_noop("yes"),
7304 gettext_noop("no"),
7305 gettext_noop("Default?"));
7306 if (verbose)
7308 ",\n CASE\n"
7309 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7310 " THEN pg_catalog.format('%%I', of.opfname)\n"
7311 " ELSE pg_catalog.format('%%I.%%I', ofn.nspname, of.opfname)\n"
7312 " END AS \"%s\",\n"
7313 " pg_catalog.pg_get_userbyid(c.opcowner) AS \"%s\"\n",
7314 gettext_noop("Operator family"),
7315 gettext_noop("Owner"));
7317 "\nFROM pg_catalog.pg_opclass c\n"
7318 " LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n"
7319 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n"
7320 " LEFT JOIN pg_catalog.pg_type t ON t.oid = c.opcintype\n"
7321 " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n");
7322 if (verbose)
7324 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n"
7325 " LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n");
7326
7329 false, false, NULL, "am.amname", NULL, NULL,
7330 &have_where, 1))
7331 goto error_return;
7332 if (type_pattern)
7333 {
7334 /* Match type name pattern against either internal or external name */
7336 "tn.nspname", "t.typname",
7337 "pg_catalog.format_type(t.oid, NULL)",
7338 "pg_catalog.pg_type_is_visible(t.oid)",
7339 NULL, 3))
7340 goto error_return;
7341 }
7342
7343 appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
7344 res = PSQLexec(buf.data);
7346 if (!res)
7347 return false;
7348
7349 myopt.title = _("List of operator classes");
7350 myopt.translate_header = true;
7351 myopt.translate_columns = translate_columns;
7352 myopt.n_translate_columns = lengthof(translate_columns);
7353
7354 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7355
7356 PQclear(res);
7357 return true;
7358
7361 return false;
7362}
7363
7364/*
7365 * \dAf
7366 * Lists operator families
7367 *
7368 * Takes optional regexps to filter by index access method and input data type.
7369 */
7370bool
7372 const char *type_pattern, bool verbose)
7373{
7375 PGresult *res;
7377 bool have_where = false;
7378 static const bool translate_columns[] = {false, false, false, false};
7379
7381
7382 printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching operator families"));
7384 "SELECT\n"
7385 " am.amname AS \"%s\",\n"
7386 " CASE\n"
7387 " WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n"
7388 " THEN pg_catalog.format('%%I', f.opfname)\n"
7389 " ELSE pg_catalog.format('%%I.%%I', n.nspname, f.opfname)\n"
7390 " END AS \"%s\",\n"
7391 " (SELECT\n"
7392 " pg_catalog.string_agg(pg_catalog.format_type(oc.opcintype, NULL), ', ')\n"
7393 " FROM pg_catalog.pg_opclass oc\n"
7394 " WHERE oc.opcfamily = f.oid) \"%s\"",
7395 gettext_noop("AM"),
7396 gettext_noop("Operator family"),
7397 gettext_noop("Applicable types"));
7398 if (verbose)
7400 ",\n pg_catalog.pg_get_userbyid(f.opfowner) AS \"%s\"\n",
7401 gettext_noop("Owner"));
7403 "\nFROM pg_catalog.pg_opfamily f\n"
7404 " LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n"
7405 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace\n");
7406
7409 false, false, NULL, "am.amname", NULL, NULL,
7410 &have_where, 1))
7411 goto error_return;
7412 if (type_pattern)
7413 {
7415 " %s EXISTS (\n"
7416 " SELECT 1\n"
7417 " FROM pg_catalog.pg_type t\n"
7418 " JOIN pg_catalog.pg_opclass oc ON oc.opcintype = t.oid\n"
7419 " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n"
7420 " WHERE oc.opcfamily = f.oid\n",
7421 have_where ? "AND" : "WHERE");
7422 /* Match type name pattern against either internal or external name */
7423 if (!validateSQLNamePattern(&buf, type_pattern, true, false,
7424 "tn.nspname", "t.typname",
7425 "pg_catalog.format_type(t.oid, NULL)",
7426 "pg_catalog.pg_type_is_visible(t.oid)",
7427 NULL, 3))
7428 goto error_return;
7429 appendPQExpBufferStr(&buf, " )\n");
7430 }
7431
7432 appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
7433 res = PSQLexec(buf.data);
7435 if (!res)
7436 return false;
7437
7438 myopt.title = _("List of operator families");
7439 myopt.translate_header = true;
7440 myopt.translate_columns = translate_columns;
7441 myopt.n_translate_columns = lengthof(translate_columns);
7442
7443 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7444
7445 PQclear(res);
7446 return true;
7447
7450 return false;
7451}
7452
7453/*
7454 * \dAo
7455 * Lists operators of operator families
7456 *
7457 * Takes optional regexps to filter by index access method and operator
7458 * family.
7459 */
7460bool
7462 const char *family_pattern, bool verbose)
7463{
7465 PGresult *res;
7467 bool have_where = false;
7468
7469 static const bool translate_columns[] = {false, false, false, false, false, false, true};
7470
7472
7473 printfPQExpBuffer(&buf, "/* %s */\n", _("Get operators of matching operator families"));
7475 "SELECT\n"
7476 " am.amname AS \"%s\",\n"
7477 " CASE\n"
7478 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7479 " THEN pg_catalog.format('%%I', of.opfname)\n"
7480 " ELSE pg_catalog.format('%%I.%%I', nsf.nspname, of.opfname)\n"
7481 " END AS \"%s\",\n"
7482 " o.amopopr::pg_catalog.regoperator AS \"%s\"\n,"
7483 " o.amopstrategy AS \"%s\",\n"
7484 " CASE o.amoppurpose\n"
7485 " WHEN " CppAsString2(AMOP_ORDER) " THEN '%s'\n"
7486 " WHEN " CppAsString2(AMOP_SEARCH) " THEN '%s'\n"
7487 " END AS \"%s\"\n",
7488 gettext_noop("AM"),
7489 gettext_noop("Operator family"),
7490 gettext_noop("Operator"),
7491 gettext_noop("Strategy"),
7492 gettext_noop("ordering"),
7493 gettext_noop("search"),
7494 gettext_noop("Purpose"));
7495
7496 if (verbose)
7498 ", ofs.opfname AS \"%s\",\n"
7499 " CASE\n"
7500 " WHEN p.proleakproof THEN '%s'\n"
7501 " ELSE '%s'\n"
7502 " END AS \"%s\"\n",
7503 gettext_noop("Sort opfamily"),
7504 gettext_noop("yes"),
7505 gettext_noop("no"),
7506 gettext_noop("Leakproof?"));
7508 "FROM pg_catalog.pg_amop o\n"
7509 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n"
7510 " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n"
7511 " LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n");
7512 if (verbose)
7514 " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n"
7515 " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n"
7516 " LEFT JOIN pg_catalog.pg_proc p ON p.oid = op.oprcode\n");
7517
7519 {
7521 false, false, NULL, "am.amname",
7522 NULL, NULL,
7523 &have_where, 1))
7524 goto error_return;
7525 }
7526
7527 if (family_pattern)
7528 {
7530 "nsf.nspname", "of.opfname", NULL, NULL,
7531 NULL, 3))
7532 goto error_return;
7533 }
7534
7535 appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7536 " o.amoplefttype = o.amoprighttype DESC,\n"
7537 " pg_catalog.format_type(o.amoplefttype, NULL),\n"
7538 " pg_catalog.format_type(o.amoprighttype, NULL),\n"
7539 " o.amopstrategy;");
7540
7541 res = PSQLexec(buf.data);
7543 if (!res)
7544 return false;
7545
7546 myopt.title = _("List of operators of operator families");
7547 myopt.translate_header = true;
7548 myopt.translate_columns = translate_columns;
7549 myopt.n_translate_columns = lengthof(translate_columns);
7550
7551 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7552
7553 PQclear(res);
7554 return true;
7555
7558 return false;
7559}
7560
7561/*
7562 * \dAp
7563 * Lists support functions of operator families
7564 *
7565 * Takes optional regexps to filter by index access method and operator
7566 * family.
7567 */
7568bool
7570 const char *family_pattern, bool verbose)
7571{
7573 PGresult *res;
7575 bool have_where = false;
7576 static const bool translate_columns[] = {false, false, false, false, false, false};
7577
7579
7580 printfPQExpBuffer(&buf, "/* %s */\n",
7581 _("Get support functions of matching operator families"));
7583 "SELECT\n"
7584 " am.amname AS \"%s\",\n"
7585 " CASE\n"
7586 " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7587 " THEN pg_catalog.format('%%I', of.opfname)\n"
7588 " ELSE pg_catalog.format('%%I.%%I', ns.nspname, of.opfname)\n"
7589 " END AS \"%s\",\n"
7590 " pg_catalog.format_type(ap.amproclefttype, NULL) AS \"%s\",\n"
7591 " pg_catalog.format_type(ap.amprocrighttype, NULL) AS \"%s\",\n"
7592 " ap.amprocnum AS \"%s\"\n",
7593 gettext_noop("AM"),
7594 gettext_noop("Operator family"),
7595 gettext_noop("Registered left type"),
7596 gettext_noop("Registered right type"),
7597 gettext_noop("Number"));
7598
7599 if (!verbose)
7601 ", p.proname AS \"%s\"\n",
7602 gettext_noop("Function"));
7603 else
7605 ", ap.amproc::pg_catalog.regprocedure AS \"%s\"\n",
7606 gettext_noop("Function"));
7607
7609 "FROM pg_catalog.pg_amproc ap\n"
7610 " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n"
7611 " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n"
7612 " LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n"
7613 " LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.oid\n");
7614
7616 {
7618 false, false, NULL, "am.amname",
7619 NULL, NULL,
7620 &have_where, 1))
7621 goto error_return;
7622 }
7623 if (family_pattern)
7624 {
7626 "ns.nspname", "of.opfname", NULL, NULL,
7627 NULL, 3))
7628 goto error_return;
7629 }
7630
7631 appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7632 " ap.amproclefttype = ap.amprocrighttype DESC,\n"
7633 " 3, 4, 5;");
7634
7635 res = PSQLexec(buf.data);
7637 if (!res)
7638 return false;
7639
7640 myopt.title = _("List of support functions of operator families");
7641 myopt.translate_header = true;
7642 myopt.translate_columns = translate_columns;
7643 myopt.n_translate_columns = lengthof(translate_columns);
7644
7645 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7646
7647 PQclear(res);
7648 return true;
7649
7652 return false;
7653}
7654
7655/*
7656 * \dl or \lo_list
7657 * Lists large objects
7658 */
7659bool
7661{
7663 PGresult *res;
7665
7667
7668 printfPQExpBuffer(&buf, "/* %s */\n", _("Get large objects"));
7670 "SELECT oid as \"%s\",\n"
7671 " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n ",
7672 gettext_noop("ID"),
7673 gettext_noop("Owner"));
7674
7675 if (verbose)
7676 {
7677 printACLColumn(&buf, "lomacl");
7678 appendPQExpBufferStr(&buf, ",\n ");
7679 }
7680
7682 "pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
7683 "FROM pg_catalog.pg_largeobject_metadata\n"
7684 "ORDER BY oid",
7685 gettext_noop("Description"));
7686
7687 res = PSQLexec(buf.data);
7689 if (!res)
7690 return false;
7691
7692 myopt.title = _("Large objects");
7693 myopt.translate_header = true;
7694
7695 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7696
7697 PQclear(res);
7698 return true;
7699}
PGresult * PSQLexec(const char *query)
Definition common.c:655
#define ngettext(s, p, n)
Definition c.h:1270
#define gettext_noop(x)
Definition c.h:1285
#define Assert(condition)
Definition c.h:943
int16_t int16
Definition c.h:619
#define CppAsString2(x)
Definition c.h:506
#define lengthof(array)
Definition c.h:873
uint32 result
bool listUserMappings(const char *pattern, bool verbose)
Definition describe.c:6329
bool listTSConfigs(const char *pattern, bool verbose)
Definition describe.c:5971
bool describeRoles(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:3938
bool listOpFamilyFunctions(const char *access_method_pattern, const char *family_pattern, bool verbose)
Definition describe.c:7569
bool listPublications(const char *pattern)
Definition describe.c:6684
bool listTSParsers(const char *pattern, bool verbose)
Definition describe.c:5587
bool listExtensionContents(const char *pattern)
Definition describe.c:6516
bool describeAggregates(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:79
bool listForeignDataWrappers(const char *pattern, bool verbose)
Definition describe.c:6178
bool listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
Definition describe.c:4504
bool describeSubscriptions(const char *pattern, bool verbose)
Definition describe.c:7089
bool describeRoleGrants(const char *pattern, bool showSystem)
Definition describe.c:4156
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:7371
bool listForeignServers(const char *pattern, bool verbose)
Definition describe.c:6251
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:4793
bool listTSDictionaries(const char *pattern, bool verbose)
Definition describe.c:5839
bool listDbRoleSettings(const char *pattern, const char *pattern2)
Definition describe.c:4086
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:4074
bool listCollations(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:5338
bool listSchemas(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:5462
bool listExtensions(const char *pattern)
Definition describe.c:6460
static bool describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, const char *pnspname, const char *prsname)
Definition describe.c:6106
static bool listOneExtensionContents(const char *extname, const char *oid)
Definition describe.c:6585
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:5640
bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4237
bool listTSTemplates(const char *pattern, bool verbose)
Definition describe.c:5905
bool describeTablespaces(const char *pattern, bool verbose)
Definition describe.c:225
bool listCasts(const char *pattern, bool verbose)
Definition describe.c:5213
bool listOpFamilyOperators(const char *access_method_pattern, const char *family_pattern, bool verbose)
Definition describe.c:7461
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:7269
static const char * map_typename_pattern(const char *pattern)
Definition describe.c:750
bool listForeignTables(const char *pattern, bool verbose)
Definition describe.c:6386
bool describeConfigurationParameters(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4958
bool listEventTriggers(const char *pattern, bool verbose)
Definition describe.c:5029
bool listDefaultACLs(const char *pattern)
Definition describe.c:1227
static void printACLColumn(PQExpBuffer buf, const char *colname)
Definition describe.c:7251
static bool addFooterToPublicationDesc(PQExpBuffer buf, const char *footermsg, bool as_schema, printTableContent *const cont)
Definition describe.c:6777
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:6631
bool listAllDbs(const char *pattern, bool verbose)
Definition describe.c:953
bool listConversions(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4877
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:6021
bool listLargeObjects(bool verbose)
Definition describe.c:7660
static bool describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
Definition describe.c:5716
static void add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline)
Definition describe.c:3871
bool listLanguages(const char *pattern, bool verbose, bool showSystem)
Definition describe.c:4716
bool describePublications(const char *pattern)
Definition describe.c:6823
bool listExtendedStats(const char *pattern, bool verbose)
Definition describe.c:5110
bool objectDescription(const char *pattern, bool showSystem)
Definition describe.c:1309
#define _(x)
Definition elog.c:96
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:162
#define pg_log_error(...)
Definition logging.h:108
static int verbose
NameData relname
Definition pg_class.h:40
static char buf[DEFAULT_XLOG_SEG_SIZE]
NameData typname
Definition pg_type.h:43
static char * tablespace
Definition pgbench.c:217
int pg_strcasecmp(const char *s1, const char *s2)
#define snprintf
Definition port.h: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:33
#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:195