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