PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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 7.4 and up. It's okay to omit irrelevant
7  * information for an old server, but not to fail outright.
8  *
9  * Copyright (c) 2000-2017, PostgreSQL Global Development Group
10  *
11  * src/bin/psql/describe.c
12  */
13 #include "postgres_fe.h"
14 
15 #include <ctype.h>
16 
17 #include "catalog/pg_default_acl.h"
18 #include "fe_utils/string_utils.h"
19 
20 #include "common.h"
21 #include "describe.h"
22 #include "fe_utils/mbprint.h"
23 #include "fe_utils/print.h"
24 #include "settings.h"
25 #include "variables.h"
26 
27 
28 static bool describeOneTableDetails(const char *schemaname,
29  const char *relationname,
30  const char *oid,
31  bool verbose);
32 static void add_tablespace_footer(printTableContent *const cont, char relkind,
33  Oid tablespace, const bool newline);
34 static void add_role_attribute(PQExpBuffer buf, const char *const str);
35 static bool listTSParsersVerbose(const char *pattern);
36 static bool describeOneTSParser(const char *oid, const char *nspname,
37  const char *prsname);
38 static bool listTSConfigsVerbose(const char *pattern);
39 static bool describeOneTSConfig(const char *oid, const char *nspname,
40  const char *cfgname,
41  const char *pnspname, const char *prsname);
42 static void printACLColumn(PQExpBuffer buf, const char *colname);
43 static bool listOneExtensionContents(const char *extname, const char *oid);
44 
45 
46 /*----------------
47  * Handlers for various slash commands displaying some sort of list
48  * of things in the database.
49  *
50  * Note: try to format the queries to look nice in -E output.
51  *----------------
52  */
53 
54 
55 /* \da
56  * Takes an optional regexp to select particular aggregates
57  */
58 bool
59 describeAggregates(const char *pattern, bool verbose, bool showSystem)
60 {
62  PGresult *res;
63  printQueryOpt myopt = pset.popt;
64 
65  initPQExpBuffer(&buf);
66 
67  printfPQExpBuffer(&buf,
68  "SELECT n.nspname as \"%s\",\n"
69  " p.proname AS \"%s\",\n"
70  " pg_catalog.format_type(p.prorettype, NULL) AS \"%s\",\n",
71  gettext_noop("Schema"),
72  gettext_noop("Name"),
73  gettext_noop("Result data type"));
74 
75  if (pset.sversion >= 80400)
76  appendPQExpBuffer(&buf,
77  " CASE WHEN p.pronargs = 0\n"
78  " THEN CAST('*' AS pg_catalog.text)\n"
79  " ELSE pg_catalog.pg_get_function_arguments(p.oid)\n"
80  " END AS \"%s\",\n",
81  gettext_noop("Argument data types"));
82  else if (pset.sversion >= 80200)
83  appendPQExpBuffer(&buf,
84  " CASE WHEN p.pronargs = 0\n"
85  " THEN CAST('*' AS pg_catalog.text)\n"
86  " ELSE\n"
87  " pg_catalog.array_to_string(ARRAY(\n"
88  " SELECT\n"
89  " pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
90  " FROM\n"
91  " pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
92  " ), ', ')\n"
93  " END AS \"%s\",\n",
94  gettext_noop("Argument data types"));
95  else
96  appendPQExpBuffer(&buf,
97  " pg_catalog.format_type(p.proargtypes[0], NULL) AS \"%s\",\n",
98  gettext_noop("Argument data types"));
99 
100  appendPQExpBuffer(&buf,
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  processSQLNamePattern(pset.db, &buf, pattern, true, false,
112  "n.nspname", "p.proname", NULL,
113  "pg_catalog.pg_function_is_visible(p.oid)");
114 
115  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
116 
117  res = PSQLexec(buf.data);
118  termPQExpBuffer(&buf);
119  if (!res)
120  return false;
121 
122  myopt.nullPrint = NULL;
123  myopt.title = _("List of aggregate functions");
124  myopt.translate_header = true;
125 
126  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
127 
128  PQclear(res);
129  return true;
130 }
131 
132 /* \dA
133  * Takes an optional regexp to select particular access methods
134  */
135 bool
136 describeAccessMethods(const char *pattern, bool verbose)
137 {
139  PGresult *res;
140  printQueryOpt myopt = pset.popt;
141  static const bool translate_columns[] = {false, true, false, false};
142 
143  if (pset.sversion < 90600)
144  {
145  char sverbuf[32];
146 
147  psql_error("The server (version %s) does not support access methods.\n",
149  sverbuf, sizeof(sverbuf)));
150  return true;
151  }
152 
153  initPQExpBuffer(&buf);
154 
155  printfPQExpBuffer(&buf,
156  "SELECT amname AS \"%s\",\n"
157  " CASE amtype"
158  " WHEN 'i' THEN '%s'"
159  " END AS \"%s\"",
160  gettext_noop("Name"),
161  gettext_noop("Index"),
162  gettext_noop("Type"));
163 
164  if (verbose)
165  {
166  appendPQExpBuffer(&buf,
167  ",\n amhandler AS \"%s\",\n"
168  " pg_catalog.obj_description(oid, 'pg_am') AS \"%s\"",
169  gettext_noop("Handler"),
170  gettext_noop("Description"));
171  }
172 
174  "\nFROM pg_catalog.pg_am\n");
175 
176  processSQLNamePattern(pset.db, &buf, pattern, false, false,
177  NULL, "amname", NULL,
178  NULL);
179 
180  appendPQExpBufferStr(&buf, "ORDER BY 1;");
181 
182  res = PSQLexec(buf.data);
183  termPQExpBuffer(&buf);
184  if (!res)
185  return false;
186 
187  myopt.nullPrint = NULL;
188  myopt.title = _("List of access methods");
189  myopt.translate_header = true;
190  myopt.translate_columns = translate_columns;
191  myopt.n_translate_columns = lengthof(translate_columns);
192 
193  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
194 
195  PQclear(res);
196  return true;
197 }
198 
199 /* \db
200  * Takes an optional regexp to select particular tablespaces
201  */
202 bool
203 describeTablespaces(const char *pattern, bool verbose)
204 {
206  PGresult *res;
207  printQueryOpt myopt = pset.popt;
208 
209  if (pset.sversion < 80000)
210  {
211  char sverbuf[32];
212 
213  psql_error("The server (version %s) does not support tablespaces.\n",
215  sverbuf, sizeof(sverbuf)));
216  return true;
217  }
218 
219  initPQExpBuffer(&buf);
220 
221  if (pset.sversion >= 90200)
222  printfPQExpBuffer(&buf,
223  "SELECT spcname AS \"%s\",\n"
224  " pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n"
225  " pg_catalog.pg_tablespace_location(oid) AS \"%s\"",
226  gettext_noop("Name"),
227  gettext_noop("Owner"),
228  gettext_noop("Location"));
229  else
230  printfPQExpBuffer(&buf,
231  "SELECT spcname AS \"%s\",\n"
232  " pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n"
233  " spclocation AS \"%s\"",
234  gettext_noop("Name"),
235  gettext_noop("Owner"),
236  gettext_noop("Location"));
237 
238  if (verbose)
239  {
240  appendPQExpBufferStr(&buf, ",\n ");
241  printACLColumn(&buf, "spcacl");
242  }
243 
244  if (verbose && pset.sversion >= 90000)
245  appendPQExpBuffer(&buf,
246  ",\n spcoptions AS \"%s\"",
247  gettext_noop("Options"));
248 
249  if (verbose && pset.sversion >= 90200)
250  appendPQExpBuffer(&buf,
251  ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"%s\"",
252  gettext_noop("Size"));
253 
254  if (verbose && pset.sversion >= 80200)
255  appendPQExpBuffer(&buf,
256  ",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
257  gettext_noop("Description"));
258 
260  "\nFROM pg_catalog.pg_tablespace\n");
261 
262  processSQLNamePattern(pset.db, &buf, pattern, false, false,
263  NULL, "spcname", NULL,
264  NULL);
265 
266  appendPQExpBufferStr(&buf, "ORDER BY 1;");
267 
268  res = PSQLexec(buf.data);
269  termPQExpBuffer(&buf);
270  if (!res)
271  return false;
272 
273  myopt.nullPrint = NULL;
274  myopt.title = _("List of tablespaces");
275  myopt.translate_header = true;
276 
277  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
278 
279  PQclear(res);
280  return true;
281 }
282 
283 
284 /* \df
285  * Takes an optional regexp to select particular functions.
286  *
287  * As with \d, you can specify the kinds of functions you want:
288  *
289  * a for aggregates
290  * n for normal
291  * t for trigger
292  * w for window
293  *
294  * and you can mix and match these in any order.
295  */
296 bool
297 describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem)
298 {
299  bool showAggregate = strchr(functypes, 'a') != NULL;
300  bool showNormal = strchr(functypes, 'n') != NULL;
301  bool showTrigger = strchr(functypes, 't') != NULL;
302  bool showWindow = strchr(functypes, 'w') != NULL;
303  bool have_where;
305  PGresult *res;
306  printQueryOpt myopt = pset.popt;
307  static const bool translate_columns[] = {false, false, false, false, true, true, true, false, true, false, false, false, false};
308 
309  /* No "Parallel" column before 9.6 */
310  static const bool translate_columns_pre_96[] = {false, false, false, false, true, true, false, true, false, false, false, false};
311 
312  if (strlen(functypes) != strspn(functypes, "antwS+"))
313  {
314  psql_error("\\df only takes [antwS+] as options\n");
315  return true;
316  }
317 
318  if (showWindow && pset.sversion < 80400)
319  {
320  char sverbuf[32];
321 
322  psql_error("\\df does not take a \"w\" option with server version %s\n",
324  sverbuf, sizeof(sverbuf)));
325  return true;
326  }
327 
328  if (!showAggregate && !showNormal && !showTrigger && !showWindow)
329  {
330  showAggregate = showNormal = showTrigger = true;
331  if (pset.sversion >= 80400)
332  showWindow = true;
333  }
334 
335  initPQExpBuffer(&buf);
336 
337  printfPQExpBuffer(&buf,
338  "SELECT n.nspname as \"%s\",\n"
339  " p.proname as \"%s\",\n",
340  gettext_noop("Schema"),
341  gettext_noop("Name"));
342 
343  if (pset.sversion >= 80400)
344  appendPQExpBuffer(&buf,
345  " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
346  " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
347  " CASE\n"
348  " WHEN p.proisagg THEN '%s'\n"
349  " WHEN p.proiswindow THEN '%s'\n"
350  " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
351  " ELSE '%s'\n"
352  " END as \"%s\"",
353  gettext_noop("Result data type"),
354  gettext_noop("Argument data types"),
355  /* translator: "agg" is short for "aggregate" */
356  gettext_noop("agg"),
357  gettext_noop("window"),
358  gettext_noop("trigger"),
359  gettext_noop("normal"),
360  gettext_noop("Type"));
361  else if (pset.sversion >= 80100)
362  appendPQExpBuffer(&buf,
363  " CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
364  " pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
365  " CASE WHEN proallargtypes IS NOT NULL THEN\n"
366  " pg_catalog.array_to_string(ARRAY(\n"
367  " SELECT\n"
368  " CASE\n"
369  " WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
370  " WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n"
371  " WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
372  " WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n"
373  " END ||\n"
374  " CASE\n"
375  " WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n"
376  " ELSE p.proargnames[s.i] || ' ' \n"
377  " END ||\n"
378  " pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n"
379  " FROM\n"
380  " pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n"
381  " ), ', ')\n"
382  " ELSE\n"
383  " pg_catalog.array_to_string(ARRAY(\n"
384  " SELECT\n"
385  " CASE\n"
386  " WHEN COALESCE(p.proargnames[s.i+1], '') = '' THEN ''\n"
387  " ELSE p.proargnames[s.i+1] || ' '\n"
388  " END ||\n"
389  " pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
390  " FROM\n"
391  " pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
392  " ), ', ')\n"
393  " END AS \"%s\",\n"
394  " CASE\n"
395  " WHEN p.proisagg THEN '%s'\n"
396  " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
397  " ELSE '%s'\n"
398  " END AS \"%s\"",
399  gettext_noop("Result data type"),
400  gettext_noop("Argument data types"),
401  /* translator: "agg" is short for "aggregate" */
402  gettext_noop("agg"),
403  gettext_noop("trigger"),
404  gettext_noop("normal"),
405  gettext_noop("Type"));
406  else
407  appendPQExpBuffer(&buf,
408  " CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
409  " pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
410  " pg_catalog.oidvectortypes(p.proargtypes) as \"%s\",\n"
411  " CASE\n"
412  " WHEN p.proisagg THEN '%s'\n"
413  " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
414  " ELSE '%s'\n"
415  " END AS \"%s\"",
416  gettext_noop("Result data type"),
417  gettext_noop("Argument data types"),
418  /* translator: "agg" is short for "aggregate" */
419  gettext_noop("agg"),
420  gettext_noop("trigger"),
421  gettext_noop("normal"),
422  gettext_noop("Type"));
423 
424  if (verbose)
425  {
426  appendPQExpBuffer(&buf,
427  ",\n CASE\n"
428  " WHEN p.provolatile = 'i' THEN '%s'\n"
429  " WHEN p.provolatile = 's' THEN '%s'\n"
430  " WHEN p.provolatile = 'v' THEN '%s'\n"
431  " END as \"%s\"",
432  gettext_noop("immutable"),
433  gettext_noop("stable"),
434  gettext_noop("volatile"),
435  gettext_noop("Volatility"));
436  if (pset.sversion >= 90600)
437  appendPQExpBuffer(&buf,
438  ",\n CASE\n"
439  " WHEN p.proparallel = 'r' THEN '%s'\n"
440  " WHEN p.proparallel = 's' THEN '%s'\n"
441  " WHEN p.proparallel = 'u' THEN '%s'\n"
442  " END as \"%s\"",
443  gettext_noop("restricted"),
444  gettext_noop("safe"),
445  gettext_noop("unsafe"),
446  gettext_noop("Parallel"));
447  appendPQExpBuffer(&buf,
448  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
449  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\"",
450  gettext_noop("Owner"),
451  gettext_noop("definer"),
452  gettext_noop("invoker"),
453  gettext_noop("Security"));
454  appendPQExpBufferStr(&buf, ",\n ");
455  printACLColumn(&buf, "p.proacl");
456  appendPQExpBuffer(&buf,
457  ",\n l.lanname as \"%s\""
458  ",\n p.prosrc as \"%s\""
459  ",\n pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"",
460  gettext_noop("Language"),
461  gettext_noop("Source code"),
462  gettext_noop("Description"));
463  }
464 
466  "\nFROM pg_catalog.pg_proc p"
467  "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
468 
469  if (verbose)
471  " LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n");
472 
473  have_where = false;
474 
475  /* filter by function type, if requested */
476  if (showNormal && showAggregate && showTrigger && showWindow)
477  /* Do nothing */ ;
478  else if (showNormal)
479  {
480  if (!showAggregate)
481  {
482  if (have_where)
483  appendPQExpBufferStr(&buf, " AND ");
484  else
485  {
486  appendPQExpBufferStr(&buf, "WHERE ");
487  have_where = true;
488  }
489  appendPQExpBufferStr(&buf, "NOT p.proisagg\n");
490  }
491  if (!showTrigger)
492  {
493  if (have_where)
494  appendPQExpBufferStr(&buf, " AND ");
495  else
496  {
497  appendPQExpBufferStr(&buf, "WHERE ");
498  have_where = true;
499  }
500  appendPQExpBufferStr(&buf, "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n");
501  }
502  if (!showWindow && pset.sversion >= 80400)
503  {
504  if (have_where)
505  appendPQExpBufferStr(&buf, " AND ");
506  else
507  {
508  appendPQExpBufferStr(&buf, "WHERE ");
509  have_where = true;
510  }
511  appendPQExpBufferStr(&buf, "NOT p.proiswindow\n");
512  }
513  }
514  else
515  {
516  bool needs_or = false;
517 
518  appendPQExpBufferStr(&buf, "WHERE (\n ");
519  have_where = true;
520  /* Note: at least one of these must be true ... */
521  if (showAggregate)
522  {
523  appendPQExpBufferStr(&buf, "p.proisagg\n");
524  needs_or = true;
525  }
526  if (showTrigger)
527  {
528  if (needs_or)
529  appendPQExpBufferStr(&buf, " OR ");
531  "p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype\n");
532  needs_or = true;
533  }
534  if (showWindow)
535  {
536  if (needs_or)
537  appendPQExpBufferStr(&buf, " OR ");
538  appendPQExpBufferStr(&buf, "p.proiswindow\n");
539  needs_or = true;
540  }
541  appendPQExpBufferStr(&buf, " )\n");
542  }
543 
544  processSQLNamePattern(pset.db, &buf, pattern, have_where, false,
545  "n.nspname", "p.proname", NULL,
546  "pg_catalog.pg_function_is_visible(p.oid)");
547 
548  if (!showSystem && !pattern)
549  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
550  " AND n.nspname <> 'information_schema'\n");
551 
552  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
553 
554  res = PSQLexec(buf.data);
555  termPQExpBuffer(&buf);
556  if (!res)
557  return false;
558 
559  myopt.nullPrint = NULL;
560  myopt.title = _("List of functions");
561  myopt.translate_header = true;
562  if (pset.sversion >= 90600)
563  {
564  myopt.translate_columns = translate_columns;
565  myopt.n_translate_columns = lengthof(translate_columns);
566  }
567  else
568  {
569  myopt.translate_columns = translate_columns_pre_96;
570  myopt.n_translate_columns = lengthof(translate_columns_pre_96);
571  }
572 
573  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
574 
575  PQclear(res);
576  return true;
577 }
578 
579 
580 
581 /*
582  * \dT
583  * describe types
584  */
585 bool
586 describeTypes(const char *pattern, bool verbose, bool showSystem)
587 {
589  PGresult *res;
590  printQueryOpt myopt = pset.popt;
591 
592  initPQExpBuffer(&buf);
593 
594  printfPQExpBuffer(&buf,
595  "SELECT n.nspname as \"%s\",\n"
596  " pg_catalog.format_type(t.oid, NULL) AS \"%s\",\n",
597  gettext_noop("Schema"),
598  gettext_noop("Name"));
599  if (verbose)
600  appendPQExpBuffer(&buf,
601  " t.typname AS \"%s\",\n"
602  " CASE WHEN t.typrelid != 0\n"
603  " THEN CAST('tuple' AS pg_catalog.text)\n"
604  " WHEN t.typlen < 0\n"
605  " THEN CAST('var' AS pg_catalog.text)\n"
606  " ELSE CAST(t.typlen AS pg_catalog.text)\n"
607  " END AS \"%s\",\n",
608  gettext_noop("Internal name"),
609  gettext_noop("Size"));
610  if (verbose && pset.sversion >= 80300)
611  {
613  " pg_catalog.array_to_string(\n"
614  " ARRAY(\n"
615  " SELECT e.enumlabel\n"
616  " FROM pg_catalog.pg_enum e\n"
617  " WHERE e.enumtypid = t.oid\n");
618 
619  if (pset.sversion >= 90100)
621  " ORDER BY e.enumsortorder\n");
622  else
624  " ORDER BY e.oid\n");
625 
626  appendPQExpBuffer(&buf,
627  " ),\n"
628  " E'\\n'\n"
629  " ) AS \"%s\",\n",
630  gettext_noop("Elements"));
631  }
632  if (verbose)
633  {
634  appendPQExpBuffer(&buf,
635  " pg_catalog.pg_get_userbyid(t.typowner) AS \"%s\",\n",
636  gettext_noop("Owner"));
637  }
638  if (verbose && pset.sversion >= 90200)
639  {
640  printACLColumn(&buf, "t.typacl");
641  appendPQExpBufferStr(&buf, ",\n ");
642  }
643 
644  appendPQExpBuffer(&buf,
645  " pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
646  gettext_noop("Description"));
647 
648  appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_type t\n"
649  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
650 
651  /*
652  * do not include complex types (typrelid!=0) unless they are standalone
653  * composite types
654  */
655  appendPQExpBufferStr(&buf, "WHERE (t.typrelid = 0 ");
656  appendPQExpBufferStr(&buf, "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c "
657  "WHERE c.oid = t.typrelid))\n");
658 
659  /*
660  * do not include array types (before 8.3 we have to use the assumption
661  * that their names start with underscore)
662  */
663  if (pset.sversion >= 80300)
664  appendPQExpBufferStr(&buf, " AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)\n");
665  else
666  appendPQExpBufferStr(&buf, " AND t.typname !~ '^_'\n");
667 
668  if (!showSystem && !pattern)
669  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
670  " AND n.nspname <> 'information_schema'\n");
671 
672  /* Match name pattern against either internal or external name */
673  processSQLNamePattern(pset.db, &buf, pattern, true, false,
674  "n.nspname", "t.typname",
675  "pg_catalog.format_type(t.oid, NULL)",
676  "pg_catalog.pg_type_is_visible(t.oid)");
677 
678  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
679 
680  res = PSQLexec(buf.data);
681  termPQExpBuffer(&buf);
682  if (!res)
683  return false;
684 
685  myopt.nullPrint = NULL;
686  myopt.title = _("List of data types");
687  myopt.translate_header = true;
688 
689  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
690 
691  PQclear(res);
692  return true;
693 }
694 
695 
696 /* \do
697  * Describe operators
698  */
699 bool
700 describeOperators(const char *pattern, bool verbose, bool showSystem)
701 {
703  PGresult *res;
704  printQueryOpt myopt = pset.popt;
705 
706  initPQExpBuffer(&buf);
707 
708  /*
709  * Note: before Postgres 9.1, we did not assign comments to any built-in
710  * operators, preferring to let the comment on the underlying function
711  * suffice. The coalesce() on the obj_description() calls below supports
712  * this convention by providing a fallback lookup of a comment on the
713  * operator's function. As of 9.1 there is a policy that every built-in
714  * operator should have a comment; so the coalesce() is no longer
715  * necessary so far as built-in operators are concerned. We keep it
716  * anyway, for now, because (1) third-party modules may still be following
717  * the old convention, and (2) we'd need to do it anyway when talking to a
718  * pre-9.1 server.
719  */
720 
721  printfPQExpBuffer(&buf,
722  "SELECT n.nspname as \"%s\",\n"
723  " o.oprname AS \"%s\",\n"
724  " CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n"
725  " CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n"
726  " pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n",
727  gettext_noop("Schema"),
728  gettext_noop("Name"),
729  gettext_noop("Left arg type"),
730  gettext_noop("Right arg type"),
731  gettext_noop("Result type"));
732 
733  if (verbose)
734  appendPQExpBuffer(&buf,
735  " o.oprcode AS \"%s\",\n",
736  gettext_noop("Function"));
737 
738  appendPQExpBuffer(&buf,
739  " coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n"
740  " pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
741  "FROM pg_catalog.pg_operator o\n"
742  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
743  gettext_noop("Description"));
744 
745  if (!showSystem && !pattern)
746  appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
747  " AND n.nspname <> 'information_schema'\n");
748 
749  processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, true,
750  "n.nspname", "o.oprname", NULL,
751  "pg_catalog.pg_operator_is_visible(o.oid)");
752 
753  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3, 4;");
754 
755  res = PSQLexec(buf.data);
756  termPQExpBuffer(&buf);
757  if (!res)
758  return false;
759 
760  myopt.nullPrint = NULL;
761  myopt.title = _("List of operators");
762  myopt.translate_header = true;
763 
764  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
765 
766  PQclear(res);
767  return true;
768 }
769 
770 
771 /*
772  * listAllDbs
773  *
774  * for \l, \list, and -l switch
775  */
776 bool
777 listAllDbs(const char *pattern, bool verbose)
778 {
779  PGresult *res;
781  printQueryOpt myopt = pset.popt;
782 
783  initPQExpBuffer(&buf);
784 
785  printfPQExpBuffer(&buf,
786  "SELECT d.datname as \"%s\",\n"
787  " pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n"
788  " pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n",
789  gettext_noop("Name"),
790  gettext_noop("Owner"),
791  gettext_noop("Encoding"));
792  if (pset.sversion >= 80400)
793  appendPQExpBuffer(&buf,
794  " d.datcollate as \"%s\",\n"
795  " d.datctype as \"%s\",\n",
796  gettext_noop("Collate"),
797  gettext_noop("Ctype"));
798  appendPQExpBufferStr(&buf, " ");
799  printACLColumn(&buf, "d.datacl");
800  if (verbose && pset.sversion >= 80200)
801  appendPQExpBuffer(&buf,
802  ",\n CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n"
803  " THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n"
804  " ELSE 'No Access'\n"
805  " END as \"%s\"",
806  gettext_noop("Size"));
807  if (verbose && pset.sversion >= 80000)
808  appendPQExpBuffer(&buf,
809  ",\n t.spcname as \"%s\"",
810  gettext_noop("Tablespace"));
811  if (verbose && pset.sversion >= 80200)
812  appendPQExpBuffer(&buf,
813  ",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"",
814  gettext_noop("Description"));
816  "\nFROM pg_catalog.pg_database d\n");
817  if (verbose && pset.sversion >= 80000)
819  " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n");
820 
821  if (pattern)
822  processSQLNamePattern(pset.db, &buf, pattern, false, false,
823  NULL, "d.datname", NULL, NULL);
824 
825  appendPQExpBufferStr(&buf, "ORDER BY 1;");
826  res = PSQLexec(buf.data);
827  termPQExpBuffer(&buf);
828  if (!res)
829  return false;
830 
831  myopt.nullPrint = NULL;
832  myopt.title = _("List of databases");
833  myopt.translate_header = true;
834 
835  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
836 
837  PQclear(res);
838  return true;
839 }
840 
841 
842 /*
843  * List Tables' Grant/Revoke Permissions
844  * \z (now also \dp -- perhaps more mnemonic)
845  */
846 bool
847 permissionsList(const char *pattern)
848 {
850  PGresult *res;
851  printQueryOpt myopt = pset.popt;
852  static const bool translate_columns[] = {false, false, true, false, false, false};
853 
854  initPQExpBuffer(&buf);
855 
856  /*
857  * we ignore indexes and toast tables since they have no meaningful rights
858  */
859  printfPQExpBuffer(&buf,
860  "SELECT n.nspname as \"%s\",\n"
861  " c.relname as \"%s\",\n"
862  " CASE c.relkind"
863  " WHEN 'r' THEN '%s'"
864  " WHEN 'v' THEN '%s'"
865  " WHEN 'm' THEN '%s'"
866  " WHEN 'S' THEN '%s'"
867  " WHEN 'f' THEN '%s'"
868  " WHEN 'P' THEN '%s'"
869  " END as \"%s\",\n"
870  " ",
871  gettext_noop("Schema"),
872  gettext_noop("Name"),
873  gettext_noop("table"),
874  gettext_noop("view"),
875  gettext_noop("materialized view"),
876  gettext_noop("sequence"),
877  gettext_noop("foreign table"),
878  gettext_noop("table"), /* partitioned table */
879  gettext_noop("Type"));
880 
881  printACLColumn(&buf, "c.relacl");
882 
883  if (pset.sversion >= 80400)
884  appendPQExpBuffer(&buf,
885  ",\n pg_catalog.array_to_string(ARRAY(\n"
886  " SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n"
887  " FROM pg_catalog.pg_attribute a\n"
888  " WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n"
889  " ), E'\\n') AS \"%s\"",
890  gettext_noop("Column privileges"));
891 
892  if (pset.sversion >= 90500 && pset.sversion < 100000)
893  appendPQExpBuffer(&buf,
894  ",\n pg_catalog.array_to_string(ARRAY(\n"
895  " SELECT polname\n"
896  " || CASE WHEN polcmd != '*' THEN\n"
897  " E' (' || polcmd || E'):'\n"
898  " ELSE E':' \n"
899  " END\n"
900  " || CASE WHEN polqual IS NOT NULL THEN\n"
901  " E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
902  " ELSE E''\n"
903  " END\n"
904  " || CASE WHEN polwithcheck IS NOT NULL THEN\n"
905  " E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
906  " ELSE E''\n"
907  " END"
908  " || CASE WHEN polroles <> '{0}' THEN\n"
909  " E'\\n to: ' || pg_catalog.array_to_string(\n"
910  " ARRAY(\n"
911  " SELECT rolname\n"
912  " FROM pg_catalog.pg_roles\n"
913  " WHERE oid = ANY (polroles)\n"
914  " ORDER BY 1\n"
915  " ), E', ')\n"
916  " ELSE E''\n"
917  " END\n"
918  " FROM pg_catalog.pg_policy pol\n"
919  " WHERE polrelid = c.oid), E'\\n')\n"
920  " AS \"%s\"",
921  gettext_noop("Policies"));
922 
923  if (pset.sversion >= 100000)
924  appendPQExpBuffer(&buf,
925  ",\n pg_catalog.array_to_string(ARRAY(\n"
926  " SELECT polname\n"
927  " || CASE WHEN NOT polpermissive THEN\n"
928  " E' (RESTRICTIVE)'\n"
929  " ELSE '' END\n"
930  " || CASE WHEN polcmd != '*' THEN\n"
931  " E' (' || polcmd || E'):'\n"
932  " ELSE E':' \n"
933  " END\n"
934  " || CASE WHEN polqual IS NOT NULL THEN\n"
935  " E'\\n (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)\n"
936  " ELSE E''\n"
937  " END\n"
938  " || CASE WHEN polwithcheck IS NOT NULL THEN\n"
939  " E'\\n (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)\n"
940  " ELSE E''\n"
941  " END"
942  " || CASE WHEN polroles <> '{0}' THEN\n"
943  " E'\\n to: ' || pg_catalog.array_to_string(\n"
944  " ARRAY(\n"
945  " SELECT rolname\n"
946  " FROM pg_catalog.pg_roles\n"
947  " WHERE oid = ANY (polroles)\n"
948  " ORDER BY 1\n"
949  " ), E', ')\n"
950  " ELSE E''\n"
951  " END\n"
952  " FROM pg_catalog.pg_policy pol\n"
953  " WHERE polrelid = c.oid), E'\\n')\n"
954  " AS \"%s\"",
955  gettext_noop("Policies"));
956 
957  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
958  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
959  "WHERE c.relkind IN ('r', 'v', 'm', 'S', 'f', 'P')\n");
960 
961  /*
962  * Unless a schema pattern is specified, we suppress system and temp
963  * tables, since they normally aren't very interesting from a permissions
964  * point of view. You can see 'em by explicit request though, eg with \z
965  * pg_catalog.*
966  */
967  processSQLNamePattern(pset.db, &buf, pattern, true, false,
968  "n.nspname", "c.relname", NULL,
969  "n.nspname !~ '^pg_' AND pg_catalog.pg_table_is_visible(c.oid)");
970 
971  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
972 
973  res = PSQLexec(buf.data);
974  if (!res)
975  {
976  termPQExpBuffer(&buf);
977  return false;
978  }
979 
980  myopt.nullPrint = NULL;
981  printfPQExpBuffer(&buf, _("Access privileges"));
982  myopt.title = buf.data;
983  myopt.translate_header = true;
984  myopt.translate_columns = translate_columns;
985  myopt.n_translate_columns = lengthof(translate_columns);
986 
987  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
988 
989  termPQExpBuffer(&buf);
990  PQclear(res);
991  return true;
992 }
993 
994 
995 /*
996  * \ddp
997  *
998  * List Default ACLs. The pattern can match either schema or role name.
999  */
1000 bool
1001 listDefaultACLs(const char *pattern)
1002 {
1004  PGresult *res;
1005  printQueryOpt myopt = pset.popt;
1006  static const bool translate_columns[] = {false, false, true, false};
1007 
1008  if (pset.sversion < 90000)
1009  {
1010  char sverbuf[32];
1011 
1012  psql_error("The server (version %s) does not support altering default privileges.\n",
1014  sverbuf, sizeof(sverbuf)));
1015  return true;
1016  }
1017 
1018  initPQExpBuffer(&buf);
1019 
1020  printfPQExpBuffer(&buf,
1021  "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
1022  " n.nspname AS \"%s\",\n"
1023  " CASE d.defaclobjtype WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n"
1024  " ",
1025  gettext_noop("Owner"),
1026  gettext_noop("Schema"),
1028  gettext_noop("table"),
1030  gettext_noop("sequence"),
1032  gettext_noop("function"),
1034  gettext_noop("type"),
1035  gettext_noop("Type"));
1036 
1037  printACLColumn(&buf, "d.defaclacl");
1038 
1039  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
1040  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
1041 
1042  processSQLNamePattern(pset.db, &buf, pattern, false, false,
1043  NULL,
1044  "n.nspname",
1045  "pg_catalog.pg_get_userbyid(d.defaclrole)",
1046  NULL);
1047 
1048  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
1049 
1050  res = PSQLexec(buf.data);
1051  if (!res)
1052  {
1053  termPQExpBuffer(&buf);
1054  return false;
1055  }
1056 
1057  myopt.nullPrint = NULL;
1058  printfPQExpBuffer(&buf, _("Default access privileges"));
1059  myopt.title = buf.data;
1060  myopt.translate_header = true;
1061  myopt.translate_columns = translate_columns;
1062  myopt.n_translate_columns = lengthof(translate_columns);
1063 
1064  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1065 
1066  termPQExpBuffer(&buf);
1067  PQclear(res);
1068  return true;
1069 }
1070 
1071 
1072 /*
1073  * Get object comments
1074  *
1075  * \dd [foo]
1076  *
1077  * Note: This command only lists comments for object types which do not have
1078  * their comments displayed by their own backslash commands. The following
1079  * types of objects will be displayed: constraint, operator class,
1080  * operator family, rule, and trigger.
1081  *
1082  */
1083 bool
1084 objectDescription(const char *pattern, bool showSystem)
1085 {
1087  PGresult *res;
1088  printQueryOpt myopt = pset.popt;
1089  static const bool translate_columns[] = {false, false, true, false};
1090 
1091  initPQExpBuffer(&buf);
1092 
1093  appendPQExpBuffer(&buf,
1094  "SELECT DISTINCT tt.nspname AS \"%s\", tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n"
1095  "FROM (\n",
1096  gettext_noop("Schema"),
1097  gettext_noop("Name"),
1098  gettext_noop("Object"),
1099  gettext_noop("Description"));
1100 
1101  /* Table constraint descriptions */
1102  appendPQExpBuffer(&buf,
1103  " SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
1104  " n.nspname as nspname,\n"
1105  " CAST(pgc.conname AS pg_catalog.text) as name,"
1106  " CAST('%s' AS pg_catalog.text) as object\n"
1107  " FROM pg_catalog.pg_constraint pgc\n"
1108  " JOIN pg_catalog.pg_class c "
1109  "ON c.oid = pgc.conrelid\n"
1110  " LEFT JOIN pg_catalog.pg_namespace n "
1111  " ON n.oid = c.relnamespace\n",
1112  gettext_noop("table constraint"));
1113 
1114  if (!showSystem && !pattern)
1115  appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1116  " AND n.nspname <> 'information_schema'\n");
1117 
1118  processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern,
1119  false, "n.nspname", "pgc.conname", NULL,
1120  "pg_catalog.pg_table_is_visible(c.oid)");
1121 
1122  /* Domain constraint descriptions */
1123  appendPQExpBuffer(&buf,
1124  "UNION ALL\n"
1125  " SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
1126  " n.nspname as nspname,\n"
1127  " CAST(pgc.conname AS pg_catalog.text) as name,"
1128  " CAST('%s' AS pg_catalog.text) as object\n"
1129  " FROM pg_catalog.pg_constraint pgc\n"
1130  " JOIN pg_catalog.pg_type t "
1131  "ON t.oid = pgc.contypid\n"
1132  " LEFT JOIN pg_catalog.pg_namespace n "
1133  " ON n.oid = t.typnamespace\n",
1134  gettext_noop("domain constraint"));
1135 
1136  if (!showSystem && !pattern)
1137  appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1138  " AND n.nspname <> 'information_schema'\n");
1139 
1140  processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern,
1141  false, "n.nspname", "pgc.conname", NULL,
1142  "pg_catalog.pg_type_is_visible(t.oid)");
1143 
1144 
1145  /*
1146  * pg_opclass.opcmethod only available in 8.3+
1147  */
1148  if (pset.sversion >= 80300)
1149  {
1150  /* Operator class descriptions */
1151  appendPQExpBuffer(&buf,
1152  "UNION ALL\n"
1153  " SELECT o.oid as oid, o.tableoid as tableoid,\n"
1154  " n.nspname as nspname,\n"
1155  " CAST(o.opcname AS pg_catalog.text) as name,\n"
1156  " CAST('%s' AS pg_catalog.text) as object\n"
1157  " FROM pg_catalog.pg_opclass o\n"
1158  " JOIN pg_catalog.pg_am am ON "
1159  "o.opcmethod = am.oid\n"
1160  " JOIN pg_catalog.pg_namespace n ON "
1161  "n.oid = o.opcnamespace\n",
1162  gettext_noop("operator class"));
1163 
1164  if (!showSystem && !pattern)
1165  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1166  " AND n.nspname <> 'information_schema'\n");
1167 
1168  processSQLNamePattern(pset.db, &buf, pattern, true, false,
1169  "n.nspname", "o.opcname", NULL,
1170  "pg_catalog.pg_opclass_is_visible(o.oid)");
1171  }
1172 
1173  /*
1174  * although operator family comments have been around since 8.3,
1175  * pg_opfamily_is_visible is only available in 9.2+
1176  */
1177  if (pset.sversion >= 90200)
1178  {
1179  /* Operator family descriptions */
1180  appendPQExpBuffer(&buf,
1181  "UNION ALL\n"
1182  " SELECT opf.oid as oid, opf.tableoid as tableoid,\n"
1183  " n.nspname as nspname,\n"
1184  " CAST(opf.opfname AS pg_catalog.text) AS name,\n"
1185  " CAST('%s' AS pg_catalog.text) as object\n"
1186  " FROM pg_catalog.pg_opfamily opf\n"
1187  " JOIN pg_catalog.pg_am am "
1188  "ON opf.opfmethod = am.oid\n"
1189  " JOIN pg_catalog.pg_namespace n "
1190  "ON opf.opfnamespace = n.oid\n",
1191  gettext_noop("operator family"));
1192 
1193  if (!showSystem && !pattern)
1194  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1195  " AND n.nspname <> 'information_schema'\n");
1196 
1197  processSQLNamePattern(pset.db, &buf, pattern, true, false,
1198  "n.nspname", "opf.opfname", NULL,
1199  "pg_catalog.pg_opfamily_is_visible(opf.oid)");
1200  }
1201 
1202  /* Rule descriptions (ignore rules for views) */
1203  appendPQExpBuffer(&buf,
1204  "UNION ALL\n"
1205  " SELECT r.oid as oid, r.tableoid as tableoid,\n"
1206  " n.nspname as nspname,\n"
1207  " CAST(r.rulename AS pg_catalog.text) as name,"
1208  " CAST('%s' AS pg_catalog.text) as object\n"
1209  " FROM pg_catalog.pg_rewrite r\n"
1210  " JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n"
1211  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
1212  " WHERE r.rulename != '_RETURN'\n",
1213  gettext_noop("rule"));
1214 
1215  if (!showSystem && !pattern)
1216  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
1217  " AND n.nspname <> 'information_schema'\n");
1218 
1219  processSQLNamePattern(pset.db, &buf, pattern, true, false,
1220  "n.nspname", "r.rulename", NULL,
1221  "pg_catalog.pg_table_is_visible(c.oid)");
1222 
1223  /* Trigger descriptions */
1224  appendPQExpBuffer(&buf,
1225  "UNION ALL\n"
1226  " SELECT t.oid as oid, t.tableoid as tableoid,\n"
1227  " n.nspname as nspname,\n"
1228  " CAST(t.tgname AS pg_catalog.text) as name,"
1229  " CAST('%s' AS pg_catalog.text) as object\n"
1230  " FROM pg_catalog.pg_trigger t\n"
1231  " JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n"
1232  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n",
1233  gettext_noop("trigger"));
1234 
1235  if (!showSystem && !pattern)
1236  appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1237  " AND n.nspname <> 'information_schema'\n");
1238 
1239  processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
1240  "n.nspname", "t.tgname", NULL,
1241  "pg_catalog.pg_table_is_visible(c.oid)");
1242 
1243  appendPQExpBufferStr(&buf,
1244  ") AS tt\n"
1245  " JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n");
1246 
1247  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
1248 
1249  res = PSQLexec(buf.data);
1250  termPQExpBuffer(&buf);
1251  if (!res)
1252  return false;
1253 
1254  myopt.nullPrint = NULL;
1255  myopt.title = _("Object descriptions");
1256  myopt.translate_header = true;
1257  myopt.translate_columns = translate_columns;
1258  myopt.n_translate_columns = lengthof(translate_columns);
1259 
1260  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1261 
1262  PQclear(res);
1263  return true;
1264 }
1265 
1266 
1267 /*
1268  * describeTableDetails (for \d)
1269  *
1270  * This routine finds the tables to be displayed, and calls
1271  * describeOneTableDetails for each one.
1272  *
1273  * verbose: if true, this is \d+
1274  */
1275 bool
1276 describeTableDetails(const char *pattern, bool verbose, bool showSystem)
1277 {
1279  PGresult *res;
1280  int i;
1281 
1282  initPQExpBuffer(&buf);
1283 
1284  printfPQExpBuffer(&buf,
1285  "SELECT c.oid,\n"
1286  " n.nspname,\n"
1287  " c.relname\n"
1288  "FROM pg_catalog.pg_class c\n"
1289  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
1290 
1291  if (!showSystem && !pattern)
1292  appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1293  " AND n.nspname <> 'information_schema'\n");
1294 
1295  processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
1296  "n.nspname", "c.relname", NULL,
1297  "pg_catalog.pg_table_is_visible(c.oid)");
1298 
1299  appendPQExpBufferStr(&buf, "ORDER BY 2, 3;");
1300 
1301  res = PSQLexec(buf.data);
1302  termPQExpBuffer(&buf);
1303  if (!res)
1304  return false;
1305 
1306  if (PQntuples(res) == 0)
1307  {
1308  if (!pset.quiet)
1309  psql_error("Did not find any relation named \"%s\".\n",
1310  pattern);
1311  PQclear(res);
1312  return false;
1313  }
1314 
1315  for (i = 0; i < PQntuples(res); i++)
1316  {
1317  const char *oid;
1318  const char *nspname;
1319  const char *relname;
1320 
1321  oid = PQgetvalue(res, i, 0);
1322  nspname = PQgetvalue(res, i, 1);
1323  relname = PQgetvalue(res, i, 2);
1324 
1325  if (!describeOneTableDetails(nspname, relname, oid, verbose))
1326  {
1327  PQclear(res);
1328  return false;
1329  }
1330  if (cancel_pressed)
1331  {
1332  PQclear(res);
1333  return false;
1334  }
1335  }
1336 
1337  PQclear(res);
1338  return true;
1339 }
1340 
1341 /*
1342  * describeOneTableDetails (for \d)
1343  *
1344  * Unfortunately, the information presented here is so complicated that it
1345  * cannot be done in a single query. So we have to assemble the printed table
1346  * by hand and pass it to the underlying printTable() function.
1347  */
1348 static bool
1349 describeOneTableDetails(const char *schemaname,
1350  const char *relationname,
1351  const char *oid,
1352  bool verbose)
1353 {
1355  PGresult *res = NULL;
1356  printTableOpt myopt = pset.popt.topt;
1357  printTableContent cont;
1358  bool printTableInitialized = false;
1359  int i;
1360  char *view_def = NULL;
1361  char *headers[11];
1362  char **seq_values = NULL;
1363  char **ptr;
1364  PQExpBufferData title;
1366  int cols;
1367  int numrows = 0;
1368  struct
1369  {
1370  int16 checks;
1371  char relkind;
1372  bool hasindex;
1373  bool hasrules;
1374  bool hastriggers;
1375  bool rowsecurity;
1376  bool forcerowsecurity;
1377  bool hasoids;
1378  Oid tablespace;
1379  char *reloptions;
1380  char *reloftype;
1381  char relpersistence;
1382  char relreplident;
1383  } tableinfo;
1384  bool show_column_details = false;
1385  bool retval;
1386 
1387  retval = false;
1388 
1389  myopt.default_footer = false;
1390  /* This output looks confusing in expanded mode. */
1391  myopt.expanded = false;
1392 
1393  initPQExpBuffer(&buf);
1394  initPQExpBuffer(&title);
1395  initPQExpBuffer(&tmpbuf);
1396 
1397  /* Get general table info */
1398  if (pset.sversion >= 90500)
1399  {
1400  printfPQExpBuffer(&buf,
1401  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1402  "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1403  "c.relhasoids, %s, c.reltablespace, "
1404  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1405  "c.relpersistence, c.relreplident\n"
1406  "FROM pg_catalog.pg_class c\n "
1407  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1408  "WHERE c.oid = '%s';",
1409  (verbose ?
1410  "pg_catalog.array_to_string(c.reloptions || "
1411  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1412  : "''"),
1413  oid);
1414  }
1415  else if (pset.sversion >= 90400)
1416  {
1417  printfPQExpBuffer(&buf,
1418  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1419  "c.relhastriggers, false, false, c.relhasoids, "
1420  "%s, c.reltablespace, "
1421  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1422  "c.relpersistence, c.relreplident\n"
1423  "FROM pg_catalog.pg_class c\n "
1424  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1425  "WHERE c.oid = '%s';",
1426  (verbose ?
1427  "pg_catalog.array_to_string(c.reloptions || "
1428  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1429  : "''"),
1430  oid);
1431  }
1432  else if (pset.sversion >= 90100)
1433  {
1434  printfPQExpBuffer(&buf,
1435  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1436  "c.relhastriggers, false, false, c.relhasoids, "
1437  "%s, c.reltablespace, "
1438  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1439  "c.relpersistence\n"
1440  "FROM pg_catalog.pg_class c\n "
1441  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1442  "WHERE c.oid = '%s';",
1443  (verbose ?
1444  "pg_catalog.array_to_string(c.reloptions || "
1445  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1446  : "''"),
1447  oid);
1448  }
1449  else if (pset.sversion >= 90000)
1450  {
1451  printfPQExpBuffer(&buf,
1452  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1453  "c.relhastriggers, false, false, c.relhasoids, "
1454  "%s, c.reltablespace, "
1455  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n"
1456  "FROM pg_catalog.pg_class c\n "
1457  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1458  "WHERE c.oid = '%s';",
1459  (verbose ?
1460  "pg_catalog.array_to_string(c.reloptions || "
1461  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1462  : "''"),
1463  oid);
1464  }
1465  else if (pset.sversion >= 80400)
1466  {
1467  printfPQExpBuffer(&buf,
1468  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1469  "c.relhastriggers, false, false, c.relhasoids, "
1470  "%s, c.reltablespace\n"
1471  "FROM pg_catalog.pg_class c\n "
1472  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1473  "WHERE c.oid = '%s';",
1474  (verbose ?
1475  "pg_catalog.array_to_string(c.reloptions || "
1476  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1477  : "''"),
1478  oid);
1479  }
1480  else if (pset.sversion >= 80200)
1481  {
1482  printfPQExpBuffer(&buf,
1483  "SELECT relchecks, relkind, relhasindex, relhasrules, "
1484  "reltriggers <> 0, false, false, relhasoids, "
1485  "%s, reltablespace\n"
1486  "FROM pg_catalog.pg_class WHERE oid = '%s';",
1487  (verbose ?
1488  "pg_catalog.array_to_string(reloptions, E', ')" : "''"),
1489  oid);
1490  }
1491  else if (pset.sversion >= 80000)
1492  {
1493  printfPQExpBuffer(&buf,
1494  "SELECT relchecks, relkind, relhasindex, relhasrules, "
1495  "reltriggers <> 0, false, false, relhasoids, "
1496  "'', reltablespace\n"
1497  "FROM pg_catalog.pg_class WHERE oid = '%s';",
1498  oid);
1499  }
1500  else
1501  {
1502  printfPQExpBuffer(&buf,
1503  "SELECT relchecks, relkind, relhasindex, relhasrules, "
1504  "reltriggers <> 0, false, false, relhasoids, "
1505  "'', ''\n"
1506  "FROM pg_catalog.pg_class WHERE oid = '%s';",
1507  oid);
1508  }
1509 
1510  res = PSQLexec(buf.data);
1511  if (!res)
1512  goto error_return;
1513 
1514  /* Did we get anything? */
1515  if (PQntuples(res) == 0)
1516  {
1517  if (!pset.quiet)
1518  psql_error("Did not find any relation with OID %s.\n", oid);
1519  goto error_return;
1520  }
1521 
1522  tableinfo.checks = atoi(PQgetvalue(res, 0, 0));
1523  tableinfo.relkind = *(PQgetvalue(res, 0, 1));
1524  tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
1525  tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
1526  tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
1527  tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
1528  tableinfo.forcerowsecurity = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
1529  tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 7), "t") == 0;
1530  tableinfo.reloptions = (pset.sversion >= 80200) ?
1531  pg_strdup(PQgetvalue(res, 0, 8)) : NULL;
1532  tableinfo.tablespace = (pset.sversion >= 80000) ?
1533  atooid(PQgetvalue(res, 0, 9)) : 0;
1534  tableinfo.reloftype = (pset.sversion >= 90000 &&
1535  strcmp(PQgetvalue(res, 0, 10), "") != 0) ?
1536  pg_strdup(PQgetvalue(res, 0, 10)) : NULL;
1537  tableinfo.relpersistence = (pset.sversion >= 90100) ?
1538  *(PQgetvalue(res, 0, 11)) : 0;
1539  tableinfo.relreplident = (pset.sversion >= 90400) ?
1540  *(PQgetvalue(res, 0, 12)) : 'd';
1541  PQclear(res);
1542  res = NULL;
1543 
1544  /*
1545  * If it's a sequence, fetch its values and store into an array that will
1546  * be used later.
1547  */
1548  if (tableinfo.relkind == 'S')
1549  {
1550  printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
1551  /* must be separate because fmtId isn't reentrant */
1552  appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
1553 
1554  res = PSQLexec(buf.data);
1555  if (!res)
1556  goto error_return;
1557 
1558  seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
1559 
1560  for (i = 0; i < PQnfields(res); i++)
1561  seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
1562  seq_values[i] = NULL;
1563 
1564  PQclear(res);
1565  res = NULL;
1566  }
1567 
1568  /*
1569  * Get column info
1570  *
1571  * You need to modify value of "firstvcol" which will be defined below if
1572  * you are adding column(s) preceding to verbose-only columns.
1573  */
1574  printfPQExpBuffer(&buf, "SELECT a.attname,");
1575  appendPQExpBufferStr(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod),"
1576  "\n (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
1577  "\n FROM pg_catalog.pg_attrdef d"
1578  "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),"
1579  "\n a.attnotnull, a.attnum,");
1580  if (pset.sversion >= 90100)
1581  appendPQExpBufferStr(&buf, "\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
1582  " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
1583  else
1584  appendPQExpBufferStr(&buf, "\n NULL AS attcollation");
1585  if (tableinfo.relkind == 'i')
1586  appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
1587  else
1588  appendPQExpBufferStr(&buf, ",\n NULL AS indexdef");
1589  if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
1590  appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE "
1591  " '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value) FROM "
1592  " pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions");
1593  else
1594  appendPQExpBufferStr(&buf, ",\n NULL AS attfdwoptions");
1595  if (verbose)
1596  {
1597  appendPQExpBufferStr(&buf, ",\n a.attstorage");
1598  appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
1599 
1600  /*
1601  * In 9.0+, we have column comments for: relations, views, composite
1602  * types, and foreign tables (c.f. CommentObject() in comment.c).
1603  */
1604  if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
1605  tableinfo.relkind == 'm' || tableinfo.relkind == 'f' ||
1606  tableinfo.relkind == 'c' || tableinfo.relkind == 'P')
1607  appendPQExpBufferStr(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)");
1608  }
1609 
1610  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
1611  appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
1612  appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
1613 
1614  res = PSQLexec(buf.data);
1615  if (!res)
1616  goto error_return;
1617  numrows = PQntuples(res);
1618 
1619  /* Make title */
1620  switch (tableinfo.relkind)
1621  {
1622  case 'r':
1623  if (tableinfo.relpersistence == 'u')
1624  printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
1625  schemaname, relationname);
1626  else
1627  printfPQExpBuffer(&title, _("Table \"%s.%s\""),
1628  schemaname, relationname);
1629  break;
1630  case 'v':
1631  printfPQExpBuffer(&title, _("View \"%s.%s\""),
1632  schemaname, relationname);
1633  break;
1634  case 'm':
1635  if (tableinfo.relpersistence == 'u')
1636  printfPQExpBuffer(&title, _("Unlogged materialized view \"%s.%s\""),
1637  schemaname, relationname);
1638  else
1639  printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""),
1640  schemaname, relationname);
1641  break;
1642  case 'S':
1643  printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
1644  schemaname, relationname);
1645  break;
1646  case 'i':
1647  if (tableinfo.relpersistence == 'u')
1648  printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
1649  schemaname, relationname);
1650  else
1651  printfPQExpBuffer(&title, _("Index \"%s.%s\""),
1652  schemaname, relationname);
1653  break;
1654  case 's':
1655  /* not used as of 8.2, but keep it for backwards compatibility */
1656  printfPQExpBuffer(&title, _("Special relation \"%s.%s\""),
1657  schemaname, relationname);
1658  break;
1659  case 't':
1660  printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
1661  schemaname, relationname);
1662  break;
1663  case 'c':
1664  printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
1665  schemaname, relationname);
1666  break;
1667  case 'f':
1668  printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
1669  schemaname, relationname);
1670  break;
1671  case 'P':
1672  if (tableinfo.relpersistence == 'u')
1673  printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
1674  schemaname, relationname);
1675  else
1676  printfPQExpBuffer(&title, _("Table \"%s.%s\""),
1677  schemaname, relationname);
1678  break;
1679  default:
1680  /* untranslated unknown relkind */
1681  printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
1682  tableinfo.relkind, schemaname, relationname);
1683  break;
1684  }
1685 
1686  /* Set the number of columns, and their names */
1687  headers[0] = gettext_noop("Column");
1688  headers[1] = gettext_noop("Type");
1689  cols = 2;
1690 
1691  if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
1692  tableinfo.relkind == 'm' || tableinfo.relkind == 'f' ||
1693  tableinfo.relkind == 'c' || tableinfo.relkind == 'P')
1694  {
1695  headers[cols++] = gettext_noop("Collation");
1696  headers[cols++] = gettext_noop("Nullable");
1697  headers[cols++] = gettext_noop("Default");
1698  show_column_details = true;
1699  }
1700 
1701  if (tableinfo.relkind == 'S')
1702  headers[cols++] = gettext_noop("Value");
1703 
1704  if (tableinfo.relkind == 'i')
1705  headers[cols++] = gettext_noop("Definition");
1706 
1707  if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
1708  headers[cols++] = gettext_noop("FDW Options");
1709 
1710  if (verbose)
1711  {
1712  headers[cols++] = gettext_noop("Storage");
1713  if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
1714  tableinfo.relkind == 'f' || tableinfo.relkind == 'P')
1715  headers[cols++] = gettext_noop("Stats target");
1716  /* Column comments, if the relkind supports this feature. */
1717  if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
1718  tableinfo.relkind == 'm' || tableinfo.relkind == 'c' ||
1719  tableinfo.relkind == 'f' || tableinfo.relkind == 'P')
1720  headers[cols++] = gettext_noop("Description");
1721  }
1722 
1723  printTableInit(&cont, &myopt, title.data, cols, numrows);
1724  printTableInitialized = true;
1725 
1726  for (i = 0; i < cols; i++)
1727  printTableAddHeader(&cont, headers[i], true, 'l');
1728 
1729  /* Check if table is a view or materialized view */
1730  if ((tableinfo.relkind == 'v' || tableinfo.relkind == 'm') && verbose)
1731  {
1732  PGresult *result;
1733 
1734  printfPQExpBuffer(&buf,
1735  "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
1736  oid);
1737  result = PSQLexec(buf.data);
1738  if (!result)
1739  goto error_return;
1740 
1741  if (PQntuples(result) > 0)
1742  view_def = pg_strdup(PQgetvalue(result, 0, 0));
1743 
1744  PQclear(result);
1745  }
1746 
1747  /* Generate table cells to be printed */
1748  for (i = 0; i < numrows; i++)
1749  {
1750  /* Column */
1751  printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
1752 
1753  /* Type */
1754  printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
1755 
1756  /* Collation, Nullable, Default */
1757  if (show_column_details)
1758  {
1759  printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
1760 
1761  printTableAddCell(&cont, strcmp(PQgetvalue(res, i, 3), "t") == 0 ? "not null" : "", false, false);
1762 
1763  /* (note: above we cut off the 'default' string at 128) */
1764  printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
1765  }
1766 
1767  /* Value: for sequences only */
1768  if (tableinfo.relkind == 'S')
1769  printTableAddCell(&cont, seq_values[i], false, false);
1770 
1771  /* Expression for index column */
1772  if (tableinfo.relkind == 'i')
1773  printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
1774 
1775  /* FDW options for foreign table column, only for 9.2 or later */
1776  if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
1777  printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
1778 
1779  /* Storage and Description */
1780  if (verbose)
1781  {
1782  int firstvcol = 8;
1783  char *storage = PQgetvalue(res, i, firstvcol);
1784 
1785  /* these strings are literal in our syntax, so not translated. */
1786  printTableAddCell(&cont, (storage[0] == 'p' ? "plain" :
1787  (storage[0] == 'm' ? "main" :
1788  (storage[0] == 'x' ? "extended" :
1789  (storage[0] == 'e' ? "external" :
1790  "???")))),
1791  false, false);
1792 
1793  /* Statistics target, if the relkind supports this feature */
1794  if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
1795  tableinfo.relkind == 'f' || tableinfo.relkind == 'P')
1796  {
1797  printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
1798  false, false);
1799  }
1800 
1801  /* Column comments, if the relkind supports this feature. */
1802  if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
1803  tableinfo.relkind == 'm' || tableinfo.relkind == 'c' ||
1804  tableinfo.relkind == 'f' || tableinfo.relkind == 'P')
1805  printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
1806  false, false);
1807  }
1808  }
1809 
1810  /* Make footers */
1811  if (pset.sversion >= 100000)
1812  {
1813  /* Get the partition information */
1814  PGresult *result;
1815  char *parent_name;
1816  char *partdef;
1817 
1818  printfPQExpBuffer(&buf,
1819  "SELECT inhparent::pg_catalog.regclass, pg_get_expr(c.relpartbound, inhrelid)"
1820  " FROM pg_catalog.pg_class c"
1821  " JOIN pg_catalog.pg_inherits"
1822  " ON c.oid = inhrelid"
1823  " WHERE c.oid = '%s' AND c.relispartition;", oid);
1824  result = PSQLexec(buf.data);
1825  if (!result)
1826  goto error_return;
1827 
1828  if (PQntuples(result) > 0)
1829  {
1830  parent_name = PQgetvalue(result, 0, 0);
1831  partdef = PQgetvalue(result, 0, 1);
1832  printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s"), parent_name,
1833  partdef);
1834  printTableAddFooter(&cont, tmpbuf.data);
1835  PQclear(result);
1836  }
1837  }
1838 
1839  if (tableinfo.relkind == 'P')
1840  {
1841  /* Get the partition key information */
1842  PGresult *result;
1843  char *partkeydef;
1844 
1845  printfPQExpBuffer(&buf,
1846  "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);",
1847  oid);
1848  result = PSQLexec(buf.data);
1849  if (!result || PQntuples(result) != 1)
1850  goto error_return;
1851 
1852  partkeydef = PQgetvalue(result, 0, 0);
1853  printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef);
1854  printTableAddFooter(&cont, tmpbuf.data);
1855  PQclear(result);
1856  }
1857 
1858  if (tableinfo.relkind == 'i')
1859  {
1860  /* Footer information about an index */
1861  PGresult *result;
1862 
1863  printfPQExpBuffer(&buf,
1864  "SELECT i.indisunique, i.indisprimary, i.indisclustered, ");
1865  if (pset.sversion >= 80200)
1866  appendPQExpBufferStr(&buf, "i.indisvalid,\n");
1867  else
1868  appendPQExpBufferStr(&buf, "true AS indisvalid,\n");
1869  if (pset.sversion >= 90000)
1870  appendPQExpBufferStr(&buf,
1871  " (NOT i.indimmediate) AND "
1872  "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
1873  "WHERE conrelid = i.indrelid AND "
1874  "conindid = i.indexrelid AND "
1875  "contype IN ('p','u','x') AND "
1876  "condeferrable) AS condeferrable,\n"
1877  " (NOT i.indimmediate) AND "
1878  "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
1879  "WHERE conrelid = i.indrelid AND "
1880  "conindid = i.indexrelid AND "
1881  "contype IN ('p','u','x') AND "
1882  "condeferred) AS condeferred,\n");
1883  else
1884  appendPQExpBufferStr(&buf,
1885  " false AS condeferrable, false AS condeferred,\n");
1886 
1887  if (pset.sversion >= 90400)
1888  appendPQExpBuffer(&buf, "i.indisreplident,\n");
1889  else
1890  appendPQExpBuffer(&buf, "false AS indisreplident,\n");
1891 
1892  appendPQExpBuffer(&buf, " a.amname, c2.relname, "
1893  "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
1894  "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
1895  "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
1896  "AND i.indrelid = c2.oid;",
1897  oid);
1898 
1899  result = PSQLexec(buf.data);
1900  if (!result)
1901  goto error_return;
1902  else if (PQntuples(result) != 1)
1903  {
1904  PQclear(result);
1905  goto error_return;
1906  }
1907  else
1908  {
1909  char *indisunique = PQgetvalue(result, 0, 0);
1910  char *indisprimary = PQgetvalue(result, 0, 1);
1911  char *indisclustered = PQgetvalue(result, 0, 2);
1912  char *indisvalid = PQgetvalue(result, 0, 3);
1913  char *deferrable = PQgetvalue(result, 0, 4);
1914  char *deferred = PQgetvalue(result, 0, 5);
1915  char *indisreplident = PQgetvalue(result, 0, 6);
1916  char *indamname = PQgetvalue(result, 0, 7);
1917  char *indtable = PQgetvalue(result, 0, 8);
1918  char *indpred = PQgetvalue(result, 0, 9);
1919 
1920  if (strcmp(indisprimary, "t") == 0)
1921  printfPQExpBuffer(&tmpbuf, _("primary key, "));
1922  else if (strcmp(indisunique, "t") == 0)
1923  printfPQExpBuffer(&tmpbuf, _("unique, "));
1924  else
1925  resetPQExpBuffer(&tmpbuf);
1926  appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
1927 
1928  /* we assume here that index and table are in same schema */
1929  appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
1930  schemaname, indtable);
1931 
1932  if (strlen(indpred))
1933  appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
1934 
1935  if (strcmp(indisclustered, "t") == 0)
1936  appendPQExpBufferStr(&tmpbuf, _(", clustered"));
1937 
1938  if (strcmp(indisvalid, "t") != 0)
1939  appendPQExpBufferStr(&tmpbuf, _(", invalid"));
1940 
1941  if (strcmp(deferrable, "t") == 0)
1942  appendPQExpBufferStr(&tmpbuf, _(", deferrable"));
1943 
1944  if (strcmp(deferred, "t") == 0)
1945  appendPQExpBufferStr(&tmpbuf, _(", initially deferred"));
1946 
1947  if (strcmp(indisreplident, "t") == 0)
1948  appendPQExpBuffer(&tmpbuf, _(", replica identity"));
1949 
1950  printTableAddFooter(&cont, tmpbuf.data);
1951  add_tablespace_footer(&cont, tableinfo.relkind,
1952  tableinfo.tablespace, true);
1953  }
1954 
1955  PQclear(result);
1956  }
1957  else if (tableinfo.relkind == 'S')
1958  {
1959  /* Footer information about a sequence */
1960  PGresult *result = NULL;
1961 
1962  /* Get the column that owns this sequence */
1963  printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
1964  "\n pg_catalog.quote_ident(relname) || '.' ||"
1965  "\n pg_catalog.quote_ident(attname)"
1966  "\nFROM pg_catalog.pg_class c"
1967  "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
1968  "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
1969  "\nINNER JOIN pg_catalog.pg_attribute a ON ("
1970  "\n a.attrelid=c.oid AND"
1971  "\n a.attnum=d.refobjsubid)"
1972  "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
1973  "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
1974  "\n AND d.objid=%s"
1975  "\n AND d.deptype='a'",
1976  oid);
1977 
1978  result = PSQLexec(buf.data);
1979  if (!result)
1980  goto error_return;
1981  else if (PQntuples(result) == 1)
1982  {
1983  printfPQExpBuffer(&buf, _("Owned by: %s"),
1984  PQgetvalue(result, 0, 0));
1985  printTableAddFooter(&cont, buf.data);
1986  }
1987 
1988  /*
1989  * If we get no rows back, don't show anything (obviously). We should
1990  * never get more than one row back, but if we do, just ignore it and
1991  * don't print anything.
1992  */
1993  PQclear(result);
1994  }
1995  else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
1996  tableinfo.relkind == 'f' || tableinfo.relkind == 'P')
1997  {
1998  /* Footer information about a table */
1999  PGresult *result = NULL;
2000  int tuples = 0;
2001 
2002  /* print indexes */
2003  if (tableinfo.hasindex)
2004  {
2005  printfPQExpBuffer(&buf,
2006  "SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, ");
2007  if (pset.sversion >= 80200)
2008  appendPQExpBufferStr(&buf, "i.indisvalid, ");
2009  else
2010  appendPQExpBufferStr(&buf, "true as indisvalid, ");
2011  appendPQExpBufferStr(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n ");
2012  if (pset.sversion >= 90000)
2013  appendPQExpBufferStr(&buf,
2014  "pg_catalog.pg_get_constraintdef(con.oid, true), "
2015  "contype, condeferrable, condeferred");
2016  else
2017  appendPQExpBufferStr(&buf,
2018  "null AS constraintdef, null AS contype, "
2019  "false AS condeferrable, false AS condeferred");
2020  if (pset.sversion >= 90400)
2021  appendPQExpBufferStr(&buf, ", i.indisreplident");
2022  else
2023  appendPQExpBufferStr(&buf, ", false AS indisreplident");
2024  if (pset.sversion >= 80000)
2025  appendPQExpBufferStr(&buf, ", c2.reltablespace");
2026  appendPQExpBufferStr(&buf,
2027  "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n");
2028  if (pset.sversion >= 90000)
2029  appendPQExpBufferStr(&buf,
2030  " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n");
2031  appendPQExpBuffer(&buf,
2032  "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
2033  "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname;",
2034  oid);
2035  result = PSQLexec(buf.data);
2036  if (!result)
2037  goto error_return;
2038  else
2039  tuples = PQntuples(result);
2040 
2041  if (tuples > 0)
2042  {
2043  printTableAddFooter(&cont, _("Indexes:"));
2044  for (i = 0; i < tuples; i++)
2045  {
2046  /* untranslated index name */
2047  printfPQExpBuffer(&buf, " \"%s\"",
2048  PQgetvalue(result, i, 0));
2049 
2050  /* If exclusion constraint, print the constraintdef */
2051  if (strcmp(PQgetvalue(result, i, 7), "x") == 0)
2052  {
2053  appendPQExpBuffer(&buf, " %s",
2054  PQgetvalue(result, i, 6));
2055  }
2056  else
2057  {
2058  const char *indexdef;
2059  const char *usingpos;
2060 
2061  /* Label as primary key or unique (but not both) */
2062  if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
2063  appendPQExpBufferStr(&buf, " PRIMARY KEY,");
2064  else if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
2065  {
2066  if (strcmp(PQgetvalue(result, i, 7), "u") == 0)
2067  appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,");
2068  else
2069  appendPQExpBufferStr(&buf, " UNIQUE,");
2070  }
2071 
2072  /* Everything after "USING" is echoed verbatim */
2073  indexdef = PQgetvalue(result, i, 5);
2074  usingpos = strstr(indexdef, " USING ");
2075  if (usingpos)
2076  indexdef = usingpos + 7;
2077  appendPQExpBuffer(&buf, " %s", indexdef);
2078 
2079  /* Need these for deferrable PK/UNIQUE indexes */
2080  if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
2081  appendPQExpBufferStr(&buf, " DEFERRABLE");
2082 
2083  if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
2084  appendPQExpBufferStr(&buf, " INITIALLY DEFERRED");
2085  }
2086 
2087  /* Add these for all cases */
2088  if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
2089  appendPQExpBufferStr(&buf, " CLUSTER");
2090 
2091  if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
2092  appendPQExpBufferStr(&buf, " INVALID");
2093 
2094  if (strcmp(PQgetvalue(result, i, 10), "t") == 0)
2095  appendPQExpBuffer(&buf, " REPLICA IDENTITY");
2096 
2097  printTableAddFooter(&cont, buf.data);
2098 
2099  /* Print tablespace of the index on the same line */
2100  if (pset.sversion >= 80000)
2101  add_tablespace_footer(&cont, 'i',
2102  atooid(PQgetvalue(result, i, 11)),
2103  false);
2104  }
2105  }
2106  PQclear(result);
2107  }
2108 
2109  /* print table (and column) check constraints */
2110  if (tableinfo.checks)
2111  {
2112  printfPQExpBuffer(&buf,
2113  "SELECT r.conname, "
2114  "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
2115  "FROM pg_catalog.pg_constraint r\n"
2116  "WHERE r.conrelid = '%s' AND r.contype = 'c'\n"
2117  "ORDER BY 1;",
2118  oid);
2119  result = PSQLexec(buf.data);
2120  if (!result)
2121  goto error_return;
2122  else
2123  tuples = PQntuples(result);
2124 
2125  if (tuples > 0)
2126  {
2127  printTableAddFooter(&cont, _("Check constraints:"));
2128  for (i = 0; i < tuples; i++)
2129  {
2130  /* untranslated constraint name and def */
2131  printfPQExpBuffer(&buf, " \"%s\" %s",
2132  PQgetvalue(result, i, 0),
2133  PQgetvalue(result, i, 1));
2134 
2135  printTableAddFooter(&cont, buf.data);
2136  }
2137  }
2138  PQclear(result);
2139  }
2140 
2141  /* print foreign-key constraints (there are none if no triggers) */
2142  if (tableinfo.hastriggers)
2143  {
2144  printfPQExpBuffer(&buf,
2145  "SELECT conname,\n"
2146  " pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n"
2147  "FROM pg_catalog.pg_constraint r\n"
2148  "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1;",
2149  oid);
2150  result = PSQLexec(buf.data);
2151  if (!result)
2152  goto error_return;
2153  else
2154  tuples = PQntuples(result);
2155 
2156  if (tuples > 0)
2157  {
2158  printTableAddFooter(&cont, _("Foreign-key constraints:"));
2159  for (i = 0; i < tuples; i++)
2160  {
2161  /* untranslated constraint name and def */
2162  printfPQExpBuffer(&buf, " \"%s\" %s",
2163  PQgetvalue(result, i, 0),
2164  PQgetvalue(result, i, 1));
2165 
2166  printTableAddFooter(&cont, buf.data);
2167  }
2168  }
2169  PQclear(result);
2170  }
2171 
2172  /* print incoming foreign-key references (none if no triggers) */
2173  if (tableinfo.hastriggers)
2174  {
2175  printfPQExpBuffer(&buf,
2176  "SELECT conname, conrelid::pg_catalog.regclass,\n"
2177  " pg_catalog.pg_get_constraintdef(c.oid, true) as condef\n"
2178  "FROM pg_catalog.pg_constraint c\n"
2179  "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1;",
2180  oid);
2181  result = PSQLexec(buf.data);
2182  if (!result)
2183  goto error_return;
2184  else
2185  tuples = PQntuples(result);
2186 
2187  if (tuples > 0)
2188  {
2189  printTableAddFooter(&cont, _("Referenced by:"));
2190  for (i = 0; i < tuples; i++)
2191  {
2192  printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2193  PQgetvalue(result, i, 1),
2194  PQgetvalue(result, i, 0),
2195  PQgetvalue(result, i, 2));
2196 
2197  printTableAddFooter(&cont, buf.data);
2198  }
2199  }
2200  PQclear(result);
2201  }
2202 
2203  /* print any row-level policies */
2204  if (pset.sversion >= 90500)
2205  {
2206  if (pset.sversion >= 100000)
2207  printfPQExpBuffer(&buf,
2208  "SELECT pol.polname, pol.polpermissive,\n"
2209  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE array_to_string(array(select rolname from pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
2210  "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
2211  "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
2212  "CASE pol.polcmd \n"
2213  "WHEN 'r' THEN 'SELECT'\n"
2214  "WHEN 'a' THEN 'INSERT'\n"
2215  "WHEN 'w' THEN 'UPDATE'\n"
2216  "WHEN 'd' THEN 'DELETE'\n"
2217  "END AS cmd\n"
2218  "FROM pg_catalog.pg_policy pol\n"
2219  "WHERE pol.polrelid = '%s' ORDER BY 1;",
2220  oid);
2221  else
2222  printfPQExpBuffer(&buf,
2223  "SELECT pol.polname, 't' as polpermissive,\n"
2224  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE array_to_string(array(select rolname from pg_roles where oid = any (pol.polroles) order by 1),',') END,\n"
2225  "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
2226  "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
2227  "CASE pol.polcmd \n"
2228  "WHEN 'r' THEN 'SELECT'\n"
2229  "WHEN 'a' THEN 'INSERT'\n"
2230  "WHEN 'w' THEN 'UPDATE'\n"
2231  "WHEN 'd' THEN 'DELETE'\n"
2232  "END AS cmd\n"
2233  "FROM pg_catalog.pg_policy pol\n"
2234  "WHERE pol.polrelid = '%s' ORDER BY 1;",
2235  oid);
2236 
2237  result = PSQLexec(buf.data);
2238  if (!result)
2239  goto error_return;
2240  else
2241  tuples = PQntuples(result);
2242 
2243  /*
2244  * Handle cases where RLS is enabled and there are policies, or
2245  * there aren't policies, or RLS isn't enabled but there are
2246  * policies
2247  */
2248  if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
2249  printTableAddFooter(&cont, _("Policies:"));
2250 
2251  if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
2252  printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
2253 
2254  if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
2255  printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
2256 
2257  if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
2258  printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
2259 
2260  if (!tableinfo.rowsecurity && tuples > 0)
2261  printTableAddFooter(&cont, _("Policies (row security disabled):"));
2262 
2263  /* Might be an empty set - that's ok */
2264  for (i = 0; i < tuples; i++)
2265  {
2266  printfPQExpBuffer(&buf, " POLICY \"%s\"",
2267  PQgetvalue(result, i, 0));
2268 
2269  if (*(PQgetvalue(result, i, 1)) == 'f')
2270  appendPQExpBuffer(&buf, " AS RESTRICTIVE");
2271 
2272  if (!PQgetisnull(result, i, 5))
2273  appendPQExpBuffer(&buf, " FOR %s",
2274  PQgetvalue(result, i, 5));
2275 
2276  if (!PQgetisnull(result, i, 2))
2277  {
2278  appendPQExpBuffer(&buf, "\n TO %s",
2279  PQgetvalue(result, i, 2));
2280  }
2281 
2282  if (!PQgetisnull(result, i, 3))
2283  appendPQExpBuffer(&buf, "\n USING (%s)",
2284  PQgetvalue(result, i, 3));
2285 
2286  if (!PQgetisnull(result, i, 4))
2287  appendPQExpBuffer(&buf, "\n WITH CHECK (%s)",
2288  PQgetvalue(result, i, 4));
2289 
2290  printTableAddFooter(&cont, buf.data);
2291 
2292  }
2293  PQclear(result);
2294  }
2295 
2296  /* print rules */
2297  if (tableinfo.hasrules && tableinfo.relkind != 'm')
2298  {
2299  if (pset.sversion >= 80300)
2300  {
2301  printfPQExpBuffer(&buf,
2302  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
2303  "ev_enabled\n"
2304  "FROM pg_catalog.pg_rewrite r\n"
2305  "WHERE r.ev_class = '%s' ORDER BY 1;",
2306  oid);
2307  }
2308  else
2309  {
2310  printfPQExpBuffer(&buf,
2311  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
2312  "'O'::char AS ev_enabled\n"
2313  "FROM pg_catalog.pg_rewrite r\n"
2314  "WHERE r.ev_class = '%s' ORDER BY 1;",
2315  oid);
2316  }
2317  result = PSQLexec(buf.data);
2318  if (!result)
2319  goto error_return;
2320  else
2321  tuples = PQntuples(result);
2322 
2323  if (tuples > 0)
2324  {
2325  bool have_heading;
2326  int category;
2327 
2328  for (category = 0; category < 4; category++)
2329  {
2330  have_heading = false;
2331 
2332  for (i = 0; i < tuples; i++)
2333  {
2334  const char *ruledef;
2335  bool list_rule = false;
2336 
2337  switch (category)
2338  {
2339  case 0:
2340  if (*PQgetvalue(result, i, 2) == 'O')
2341  list_rule = true;
2342  break;
2343  case 1:
2344  if (*PQgetvalue(result, i, 2) == 'D')
2345  list_rule = true;
2346  break;
2347  case 2:
2348  if (*PQgetvalue(result, i, 2) == 'A')
2349  list_rule = true;
2350  break;
2351  case 3:
2352  if (*PQgetvalue(result, i, 2) == 'R')
2353  list_rule = true;
2354  break;
2355  }
2356  if (!list_rule)
2357  continue;
2358 
2359  if (!have_heading)
2360  {
2361  switch (category)
2362  {
2363  case 0:
2364  printfPQExpBuffer(&buf, _("Rules:"));
2365  break;
2366  case 1:
2367  printfPQExpBuffer(&buf, _("Disabled rules:"));
2368  break;
2369  case 2:
2370  printfPQExpBuffer(&buf, _("Rules firing always:"));
2371  break;
2372  case 3:
2373  printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
2374  break;
2375  }
2376  printTableAddFooter(&cont, buf.data);
2377  have_heading = true;
2378  }
2379 
2380  /* Everything after "CREATE RULE" is echoed verbatim */
2381  ruledef = PQgetvalue(result, i, 1);
2382  ruledef += 12;
2383  printfPQExpBuffer(&buf, " %s", ruledef);
2384  printTableAddFooter(&cont, buf.data);
2385  }
2386  }
2387  }
2388  PQclear(result);
2389  }
2390 
2391  /* print any publications */
2392  if (pset.sversion >= 100000)
2393  {
2394  printfPQExpBuffer(&buf,
2395  "SELECT pub.pubname\n"
2396  " FROM pg_catalog.pg_publication pub\n"
2397  " LEFT JOIN pg_publication_rel pr\n"
2398  " ON (pr.prpubid = pub.oid)\n"
2399  "WHERE pr.prrelid = '%s' OR pub.puballtables\n"
2400  "ORDER BY 1;",
2401  oid);
2402 
2403  result = PSQLexec(buf.data);
2404  if (!result)
2405  goto error_return;
2406  else
2407  tuples = PQntuples(result);
2408 
2409  if (tuples > 0)
2410  printTableAddFooter(&cont, _("Publications:"));
2411 
2412  /* Might be an empty set - that's ok */
2413  for (i = 0; i < tuples; i++)
2414  {
2415  printfPQExpBuffer(&buf, " \"%s\"",
2416  PQgetvalue(result, i, 0));
2417 
2418  printTableAddFooter(&cont, buf.data);
2419  }
2420  PQclear(result);
2421  }
2422  }
2423 
2424  if (view_def)
2425  {
2426  PGresult *result = NULL;
2427 
2428  /* Footer information about a view */
2429  printTableAddFooter(&cont, _("View definition:"));
2430  printTableAddFooter(&cont, view_def);
2431 
2432  /* print rules */
2433  if (tableinfo.hasrules)
2434  {
2435  printfPQExpBuffer(&buf,
2436  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
2437  "FROM pg_catalog.pg_rewrite r\n"
2438  "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
2439  oid);
2440  result = PSQLexec(buf.data);
2441  if (!result)
2442  goto error_return;
2443 
2444  if (PQntuples(result) > 0)
2445  {
2446  printTableAddFooter(&cont, _("Rules:"));
2447  for (i = 0; i < PQntuples(result); i++)
2448  {
2449  const char *ruledef;
2450 
2451  /* Everything after "CREATE RULE" is echoed verbatim */
2452  ruledef = PQgetvalue(result, i, 1);
2453  ruledef += 12;
2454 
2455  printfPQExpBuffer(&buf, " %s", ruledef);
2456  printTableAddFooter(&cont, buf.data);
2457  }
2458  }
2459  PQclear(result);
2460  }
2461  }
2462 
2463  /*
2464  * Print triggers next, if any (but only user-defined triggers). This
2465  * could apply to either a table or a view.
2466  */
2467  if (tableinfo.hastriggers)
2468  {
2469  PGresult *result;
2470  int tuples;
2471 
2472  printfPQExpBuffer(&buf,
2473  "SELECT t.tgname, "
2474  "pg_catalog.pg_get_triggerdef(t.oid%s), "
2475  "t.tgenabled, %s\n"
2476  "FROM pg_catalog.pg_trigger t\n"
2477  "WHERE t.tgrelid = '%s' AND ",
2478  (pset.sversion >= 90000 ? ", true" : ""),
2479  (pset.sversion >= 90000 ? "t.tgisinternal" :
2480  pset.sversion >= 80300 ?
2481  "t.tgconstraint <> 0 AS tgisinternal" :
2482  "false AS tgisinternal"), oid);
2483  if (pset.sversion >= 90000)
2484  /* display/warn about disabled internal triggers */
2485  appendPQExpBuffer(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))");
2486  else if (pset.sversion >= 80300)
2487  appendPQExpBufferStr(&buf, "(t.tgconstraint = 0 OR (t.tgconstraint <> 0 AND t.tgenabled = 'D'))");
2488  else
2489  appendPQExpBufferStr(&buf,
2490  "(NOT tgisconstraint "
2491  " OR NOT EXISTS"
2492  " (SELECT 1 FROM pg_catalog.pg_depend d "
2493  " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
2494  " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))");
2495  appendPQExpBufferStr(&buf, "\nORDER BY 1;");
2496 
2497  result = PSQLexec(buf.data);
2498  if (!result)
2499  goto error_return;
2500  else
2501  tuples = PQntuples(result);
2502 
2503  if (tuples > 0)
2504  {
2505  bool have_heading;
2506  int category;
2507 
2508  /*
2509  * split the output into 4 different categories. Enabled triggers,
2510  * disabled triggers and the two special ALWAYS and REPLICA
2511  * configurations.
2512  */
2513  for (category = 0; category <= 4; category++)
2514  {
2515  have_heading = false;
2516  for (i = 0; i < tuples; i++)
2517  {
2518  bool list_trigger;
2519  const char *tgdef;
2520  const char *usingpos;
2521  const char *tgenabled;
2522  const char *tgisinternal;
2523 
2524  /*
2525  * Check if this trigger falls into the current category
2526  */
2527  tgenabled = PQgetvalue(result, i, 2);
2528  tgisinternal = PQgetvalue(result, i, 3);
2529  list_trigger = false;
2530  switch (category)
2531  {
2532  case 0:
2533  if (*tgenabled == 'O' || *tgenabled == 't')
2534  list_trigger = true;
2535  break;
2536  case 1:
2537  if ((*tgenabled == 'D' || *tgenabled == 'f') &&
2538  *tgisinternal == 'f')
2539  list_trigger = true;
2540  break;
2541  case 2:
2542  if ((*tgenabled == 'D' || *tgenabled == 'f') &&
2543  *tgisinternal == 't')
2544  list_trigger = true;
2545  break;
2546  case 3:
2547  if (*tgenabled == 'A')
2548  list_trigger = true;
2549  break;
2550  case 4:
2551  if (*tgenabled == 'R')
2552  list_trigger = true;
2553  break;
2554  }
2555  if (list_trigger == false)
2556  continue;
2557 
2558  /* Print the category heading once */
2559  if (have_heading == false)
2560  {
2561  switch (category)
2562  {
2563  case 0:
2564  printfPQExpBuffer(&buf, _("Triggers:"));
2565  break;
2566  case 1:
2567  if (pset.sversion >= 80300)
2568  printfPQExpBuffer(&buf, _("Disabled user triggers:"));
2569  else
2570  printfPQExpBuffer(&buf, _("Disabled triggers:"));
2571  break;
2572  case 2:
2573  printfPQExpBuffer(&buf, _("Disabled internal triggers:"));
2574  break;
2575  case 3:
2576  printfPQExpBuffer(&buf, _("Triggers firing always:"));
2577  break;
2578  case 4:
2579  printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
2580  break;
2581 
2582  }
2583  printTableAddFooter(&cont, buf.data);
2584  have_heading = true;
2585  }
2586 
2587  /* Everything after "TRIGGER" is echoed verbatim */
2588  tgdef = PQgetvalue(result, i, 1);
2589  usingpos = strstr(tgdef, " TRIGGER ");
2590  if (usingpos)
2591  tgdef = usingpos + 9;
2592 
2593  printfPQExpBuffer(&buf, " %s", tgdef);
2594  printTableAddFooter(&cont, buf.data);
2595  }
2596  }
2597  }
2598  PQclear(result);
2599  }
2600 
2601  /*
2602  * Finish printing the footer information about a table.
2603  */
2604  if (tableinfo.relkind == 'r' || tableinfo.relkind == 'm' ||
2605  tableinfo.relkind == 'f' || tableinfo.relkind == 'P')
2606  {
2607  PGresult *result;
2608  int tuples;
2609 
2610  /* print foreign server name */
2611  if (tableinfo.relkind == 'f')
2612  {
2613  char *ftoptions;
2614 
2615  /* Footer information about foreign table */
2616  printfPQExpBuffer(&buf,
2617  "SELECT s.srvname,\n"
2618  " array_to_string(ARRAY(SELECT "
2619  " quote_ident(option_name) || ' ' || "
2620  " quote_literal(option_value) FROM "
2621  " pg_options_to_table(ftoptions)), ', ') "
2622  "FROM pg_catalog.pg_foreign_table f,\n"
2623  " pg_catalog.pg_foreign_server s\n"
2624  "WHERE f.ftrelid = %s AND s.oid = f.ftserver;",
2625  oid);
2626  result = PSQLexec(buf.data);
2627  if (!result)
2628  goto error_return;
2629  else if (PQntuples(result) != 1)
2630  {
2631  PQclear(result);
2632  goto error_return;
2633  }
2634 
2635  /* Print server name */
2636  printfPQExpBuffer(&buf, "Server: %s",
2637  PQgetvalue(result, 0, 0));
2638  printTableAddFooter(&cont, buf.data);
2639 
2640  /* Print per-table FDW options, if any */
2641  ftoptions = PQgetvalue(result, 0, 1);
2642  if (ftoptions && ftoptions[0] != '\0')
2643  {
2644  printfPQExpBuffer(&buf, "FDW Options: (%s)", ftoptions);
2645  printTableAddFooter(&cont, buf.data);
2646  }
2647  PQclear(result);
2648  }
2649 
2650  /* print inherited tables (exclude, if parent is a partitioned table) */
2651  printfPQExpBuffer(&buf,
2652  "SELECT c.oid::pg_catalog.regclass"
2653  " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i"
2654  " WHERE c.oid=i.inhparent AND i.inhrelid = '%s'"
2655  " AND c.relkind != 'P' ORDER BY inhseqno;", oid);
2656 
2657  result = PSQLexec(buf.data);
2658  if (!result)
2659  goto error_return;
2660  else
2661  {
2662  const char *s = _("Inherits");
2663  int sw = pg_wcswidth(s, strlen(s), pset.encoding);
2664 
2665  tuples = PQntuples(result);
2666 
2667  for (i = 0; i < tuples; i++)
2668  {
2669  if (i == 0)
2670  printfPQExpBuffer(&buf, "%s: %s",
2671  s, PQgetvalue(result, i, 0));
2672  else
2673  printfPQExpBuffer(&buf, "%*s %s",
2674  sw, "", PQgetvalue(result, i, 0));
2675  if (i < tuples - 1)
2676  appendPQExpBufferChar(&buf, ',');
2677 
2678  printTableAddFooter(&cont, buf.data);
2679  }
2680 
2681  PQclear(result);
2682  }
2683 
2684  /* print child tables (with additional info if partitions) */
2685  if (pset.sversion >= 100000)
2686  printfPQExpBuffer(&buf,
2687  "SELECT c.oid::pg_catalog.regclass, pg_get_expr(c.relpartbound, c.oid)"
2688  " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i"
2689  " WHERE c.oid=i.inhrelid AND"
2690  " i.inhparent = '%s' AND"
2691  " EXISTS (SELECT 1 FROM pg_class c WHERE c.oid = '%s')"
2692  " ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid, oid);
2693  else if (pset.sversion >= 80300)
2694  printfPQExpBuffer(&buf,
2695  "SELECT c.oid::pg_catalog.regclass"
2696  " FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i"
2697  " WHERE c.oid=i.inhrelid AND"
2698  " i.inhparent = '%s' AND"
2699  " EXISTS (SELECT 1 FROM pg_class c WHERE c.oid = '%s')"
2700  " ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid, oid);
2701  else
2702  printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.relname;", oid);
2703 
2704  result = PSQLexec(buf.data);
2705  if (!result)
2706  goto error_return;
2707  else
2708  tuples = PQntuples(result);
2709 
2710  if (!verbose)
2711  {
2712  /* print the number of child tables, if any */
2713  if (tuples > 0)
2714  {
2715  if (tableinfo.relkind != 'P')
2716  printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
2717  else
2718  printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
2719  printTableAddFooter(&cont, buf.data);
2720  }
2721  }
2722  else
2723  {
2724  /* display the list of child tables */
2725  const char *ct = tableinfo.relkind != 'P' ? _("Child tables") : _("Partitions");
2726  int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding);
2727 
2728  for (i = 0; i < tuples; i++)
2729  {
2730  if (tableinfo.relkind != 'P')
2731  {
2732  if (i == 0)
2733  printfPQExpBuffer(&buf, "%s: %s",
2734  ct, PQgetvalue(result, i, 0));
2735  else
2736  printfPQExpBuffer(&buf, "%*s %s",
2737  ctw, "", PQgetvalue(result, i, 0));
2738  }
2739  else
2740  {
2741  if (i == 0)
2742  printfPQExpBuffer(&buf, "%s: %s %s",
2743  ct, PQgetvalue(result, i, 0), PQgetvalue(result, i, 1));
2744  else
2745  printfPQExpBuffer(&buf, "%*s %s %s",
2746  ctw, "", PQgetvalue(result, i, 0), PQgetvalue(result, i, 1));
2747  }
2748  if (i < tuples - 1)
2749  appendPQExpBufferChar(&buf, ',');
2750 
2751  printTableAddFooter(&cont, buf.data);
2752  }
2753  }
2754  PQclear(result);
2755 
2756  /* Table type */
2757  if (tableinfo.reloftype)
2758  {
2759  printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
2760  printTableAddFooter(&cont, buf.data);
2761  }
2762 
2763  if (verbose && (tableinfo.relkind == 'r' || tableinfo.relkind == 'm') &&
2764 
2765  /*
2766  * No need to display default values; we already display a REPLICA
2767  * IDENTITY marker on indexes.
2768  */
2769  tableinfo.relreplident != 'i' &&
2770  ((strcmp(schemaname, "pg_catalog") != 0 && tableinfo.relreplident != 'd') ||
2771  (strcmp(schemaname, "pg_catalog") == 0 && tableinfo.relreplident != 'n')))
2772  {
2773  const char *s = _("Replica Identity");
2774 
2775  printfPQExpBuffer(&buf, "%s: %s",
2776  s,
2777  tableinfo.relreplident == 'f' ? "FULL" :
2778  tableinfo.relreplident == 'n' ? "NOTHING" :
2779  "???");
2780 
2781  printTableAddFooter(&cont, buf.data);
2782  }
2783 
2784  /* OIDs, if verbose and not a materialized view */
2785  if (verbose && tableinfo.relkind != 'm' && tableinfo.hasoids)
2786  printTableAddFooter(&cont, _("Has OIDs: yes"));
2787 
2788  /* Tablespace info */
2789  add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
2790  true);
2791  }
2792 
2793  /* reloptions, if verbose */
2794  if (verbose &&
2795  tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
2796  {
2797  const char *t = _("Options");
2798 
2799  printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions);
2800  printTableAddFooter(&cont, buf.data);
2801  }
2802 
2803  printTable(&cont, pset.queryFout, false, pset.logfile);
2804 
2805  retval = true;
2806 
2807 error_return:
2808 
2809  /* clean up */
2810  if (printTableInitialized)
2811  printTableCleanup(&cont);
2812  termPQExpBuffer(&buf);
2813  termPQExpBuffer(&title);
2814  termPQExpBuffer(&tmpbuf);
2815 
2816  if (seq_values)
2817  {
2818  for (ptr = seq_values; *ptr; ptr++)
2819  free(*ptr);
2820  free(seq_values);
2821  }
2822 
2823  if (view_def)
2824  free(view_def);
2825 
2826  if (res)
2827  PQclear(res);
2828 
2829  return retval;
2830 }
2831 
2832 /*
2833  * Add a tablespace description to a footer. If 'newline' is true, it is added
2834  * in a new line; otherwise it's appended to the current value of the last
2835  * footer.
2836  */
2837 static void
2838 add_tablespace_footer(printTableContent *const cont, char relkind,
2839  Oid tablespace, const bool newline)
2840 {
2841  /* relkinds for which we support tablespaces */
2842  if (relkind == 'r' || relkind == 'm' || relkind == 'i' || relkind == 'P')
2843  {
2844  /*
2845  * We ignore the database default tablespace so that users not using
2846  * tablespaces don't need to know about them. This case also covers
2847  * pre-8.0 servers, for which tablespace will always be 0.
2848  */
2849  if (tablespace != 0)
2850  {
2851  PGresult *result = NULL;
2853 
2854  initPQExpBuffer(&buf);
2855  printfPQExpBuffer(&buf,
2856  "SELECT spcname FROM pg_catalog.pg_tablespace\n"
2857  "WHERE oid = '%u';", tablespace);
2858  result = PSQLexec(buf.data);
2859  if (!result)
2860  return;
2861  /* Should always be the case, but.... */
2862  if (PQntuples(result) > 0)
2863  {
2864  if (newline)
2865  {
2866  /* Add the tablespace as a new footer */
2867  printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
2868  PQgetvalue(result, 0, 0));
2869  printTableAddFooter(cont, buf.data);
2870  }
2871  else
2872  {
2873  /* Append the tablespace to the latest footer */
2874  printfPQExpBuffer(&buf, "%s", cont->footer->data);
2875 
2876  /*-------
2877  translator: before this string there's an index description like
2878  '"foo_pkey" PRIMARY KEY, btree (a)' */
2879  appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
2880  PQgetvalue(result, 0, 0));
2881  printTableSetFooter(cont, buf.data);
2882  }
2883  }
2884  PQclear(result);
2885  termPQExpBuffer(&buf);
2886  }
2887  }
2888 }
2889 
2890 /*
2891  * \du or \dg
2892  *
2893  * Describes roles. Any schema portion of the pattern is ignored.
2894  */
2895 bool
2896 describeRoles(const char *pattern, bool verbose, bool showSystem)
2897 {
2899  PGresult *res;
2900  printTableContent cont;
2901  printTableOpt myopt = pset.popt.topt;
2902  int ncols = 3;
2903  int nrows = 0;
2904  int i;
2905  int conns;
2906  const char align = 'l';
2907  char **attr;
2908 
2909  myopt.default_footer = false;
2910 
2911  initPQExpBuffer(&buf);
2912 
2913  if (pset.sversion >= 80100)
2914  {
2915  printfPQExpBuffer(&buf,
2916  "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
2917  " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
2918  " r.rolconnlimit, r.rolvaliduntil,\n"
2919  " ARRAY(SELECT b.rolname\n"
2920  " FROM pg_catalog.pg_auth_members m\n"
2921  " JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)\n"
2922  " WHERE m.member = r.oid) as memberof");
2923 
2924  if (verbose && pset.sversion >= 80200)
2925  {
2926  appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
2927  ncols++;
2928  }
2929  if (pset.sversion >= 90100)
2930  {
2931  appendPQExpBufferStr(&buf, "\n, r.rolreplication");
2932  }
2933 
2934  if (pset.sversion >= 90500)
2935  {
2936  appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
2937  }
2938 
2939  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
2940 
2941  if (!showSystem && !pattern)
2942  appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
2943 
2944  processSQLNamePattern(pset.db, &buf, pattern, false, false,
2945  NULL, "r.rolname", NULL, NULL);
2946  }
2947  else
2948  {
2949  printfPQExpBuffer(&buf,
2950  "SELECT u.usename AS rolname,\n"
2951  " u.usesuper AS rolsuper,\n"
2952  " true AS rolinherit, false AS rolcreaterole,\n"
2953  " u.usecreatedb AS rolcreatedb, true AS rolcanlogin,\n"
2954  " -1 AS rolconnlimit,"
2955  " u.valuntil as rolvaliduntil,\n"
2956  " ARRAY(SELECT g.groname FROM pg_catalog.pg_group g WHERE u.usesysid = ANY(g.grolist)) as memberof"
2957  "\nFROM pg_catalog.pg_user u\n");
2958 
2959  processSQLNamePattern(pset.db, &buf, pattern, false, false,
2960  NULL, "u.usename", NULL, NULL);
2961  }
2962 
2963  appendPQExpBufferStr(&buf, "ORDER BY 1;");
2964 
2965  res = PSQLexec(buf.data);
2966  if (!res)
2967  return false;
2968 
2969  nrows = PQntuples(res);
2970  attr = pg_malloc0((nrows + 1) * sizeof(*attr));
2971 
2972  printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
2973 
2974  printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
2975  printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
2976  printTableAddHeader(&cont, gettext_noop("Member of"), true, align);
2977 
2978  if (verbose && pset.sversion >= 80200)
2979  printTableAddHeader(&cont, gettext_noop("Description"), true, align);
2980 
2981  for (i = 0; i < nrows; i++)
2982  {
2983  printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
2984 
2985  resetPQExpBuffer(&buf);
2986  if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
2987  add_role_attribute(&buf, _("Superuser"));
2988 
2989  if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
2990  add_role_attribute(&buf, _("No inheritance"));
2991 
2992  if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
2993  add_role_attribute(&buf, _("Create role"));
2994 
2995  if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
2996  add_role_attribute(&buf, _("Create DB"));
2997 
2998  if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
2999  add_role_attribute(&buf, _("Cannot login"));
3000 
3001  if (pset.sversion >= 90100)
3002  if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
3003  add_role_attribute(&buf, _("Replication"));
3004 
3005  if (pset.sversion >= 90500)
3006  if (strcmp(PQgetvalue(res, i, (verbose ? 11 : 10)), "t") == 0)
3007  add_role_attribute(&buf, _("Bypass RLS"));
3008 
3009  conns = atoi(PQgetvalue(res, i, 6));
3010  if (conns >= 0)
3011  {
3012  if (buf.len > 0)
3013  appendPQExpBufferChar(&buf, '\n');
3014 
3015  if (conns == 0)
3016  appendPQExpBufferStr(&buf, _("No connections"));
3017  else
3018  appendPQExpBuffer(&buf, ngettext("%d connection",
3019  "%d connections",
3020  conns),
3021  conns);
3022  }
3023 
3024  if (strcmp(PQgetvalue(res, i, 7), "") != 0)
3025  {
3026  if (buf.len > 0)
3027  appendPQExpBufferStr(&buf, "\n");
3028  appendPQExpBufferStr(&buf, _("Password valid until "));
3029  appendPQExpBufferStr(&buf, PQgetvalue(res, i, 7));
3030  }
3031 
3032  attr[i] = pg_strdup(buf.data);
3033 
3034  printTableAddCell(&cont, attr[i], false, false);
3035 
3036  printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
3037 
3038  if (verbose && pset.sversion >= 80200)
3039  printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
3040  }
3041  termPQExpBuffer(&buf);
3042 
3043  printTable(&cont, pset.queryFout, false, pset.logfile);
3044  printTableCleanup(&cont);
3045 
3046  for (i = 0; i < nrows; i++)
3047  free(attr[i]);
3048  free(attr);
3049 
3050  PQclear(res);
3051  return true;
3052 }
3053 
3054 static void
3055 add_role_attribute(PQExpBuffer buf, const char *const str)
3056 {
3057  if (buf->len > 0)
3058  appendPQExpBufferStr(buf, ", ");
3059 
3060  appendPQExpBufferStr(buf, str);
3061 }
3062 
3063 /*
3064  * \drds
3065  */
3066 bool
3067 listDbRoleSettings(const char *pattern, const char *pattern2)
3068 {
3070  PGresult *res;
3071  printQueryOpt myopt = pset.popt;
3072 
3073  initPQExpBuffer(&buf);
3074 
3075  if (pset.sversion >= 90000)
3076  {
3077  bool havewhere;
3078 
3079  printfPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
3080  "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
3081  "FROM pg_db_role_setting AS s\n"
3082  "LEFT JOIN pg_database ON pg_database.oid = setdatabase\n"
3083  "LEFT JOIN pg_roles ON pg_roles.oid = setrole\n",
3084  gettext_noop("Role"),
3085  gettext_noop("Database"),
3086  gettext_noop("Settings"));
3087  havewhere = processSQLNamePattern(pset.db, &buf, pattern, false, false,
3088  NULL, "pg_roles.rolname", NULL, NULL);
3089  processSQLNamePattern(pset.db, &buf, pattern2, havewhere, false,
3090  NULL, "pg_database.datname", NULL, NULL);
3091  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3092  }
3093  else
3094  {
3095  fprintf(pset.queryFout,
3096  _("No per-database role settings support in this server version.\n"));
3097  return false;
3098  }
3099 
3100  res = PSQLexec(buf.data);
3101  if (!res)
3102  return false;
3103 
3104  if (PQntuples(res) == 0 && !pset.quiet)
3105  {
3106  if (pattern)
3107  fprintf(pset.queryFout, _("No matching settings found.\n"));
3108  else
3109  fprintf(pset.queryFout, _("No settings found.\n"));
3110  }
3111  else
3112  {
3113  myopt.nullPrint = NULL;
3114  myopt.title = _("List of settings");
3115  myopt.translate_header = true;
3116 
3117  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3118  }
3119 
3120  PQclear(res);
3121  resetPQExpBuffer(&buf);
3122  return true;
3123 }
3124 
3125 
3126 /*
3127  * listTables()
3128  *
3129  * handler for \dt, \di, etc.
3130  *
3131  * tabtypes is an array of characters, specifying what info is desired:
3132  * t - tables
3133  * i - indexes
3134  * v - views
3135  * m - materialized views
3136  * s - sequences
3137  * E - foreign table (Note: different from 'f', the relkind value)
3138  * (any order of the above is fine)
3139  * If tabtypes is empty, we default to \dtvsE.
3140  */
3141 bool
3142 listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
3143 {
3144  bool showTables = strchr(tabtypes, 't') != NULL;
3145  bool showIndexes = strchr(tabtypes, 'i') != NULL;
3146  bool showViews = strchr(tabtypes, 'v') != NULL;
3147  bool showMatViews = strchr(tabtypes, 'm') != NULL;
3148  bool showSeq = strchr(tabtypes, 's') != NULL;
3149  bool showForeign = strchr(tabtypes, 'E') != NULL;
3150 
3152  PGresult *res;
3153  printQueryOpt myopt = pset.popt;
3154  static const bool translate_columns[] = {false, false, true, false, false, false, false};
3155 
3156  if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign))
3157  showTables = showViews = showMatViews = showSeq = showForeign = true;
3158 
3159  initPQExpBuffer(&buf);
3160 
3161  /*
3162  * Note: as of Pg 8.2, we no longer use relkind 's', but we keep it here
3163  * for backwards compatibility.
3164  */
3165  printfPQExpBuffer(&buf,
3166  "SELECT n.nspname as \"%s\",\n"
3167  " c.relname as \"%s\",\n"
3168  " CASE c.relkind"
3169  " WHEN 'r' THEN '%s'"
3170  " WHEN 'v' THEN '%s'"
3171  " WHEN 'm' THEN '%s'"
3172  " WHEN 'i' THEN '%s'"
3173  " WHEN 'S' THEN '%s'"
3174  " WHEN 's' THEN '%s'"
3175  " WHEN 'f' THEN '%s'"
3176  " WHEN 'P' THEN '%s'"
3177  " END as \"%s\",\n"
3178  " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
3179  gettext_noop("Schema"),
3180  gettext_noop("Name"),
3181  gettext_noop("table"),
3182  gettext_noop("view"),
3183  gettext_noop("materialized view"),
3184  gettext_noop("index"),
3185  gettext_noop("sequence"),
3186  gettext_noop("special"),
3187  gettext_noop("foreign table"),
3188  gettext_noop("table"), /* partitioned table */
3189  gettext_noop("Type"),
3190  gettext_noop("Owner"));
3191 
3192  if (showIndexes)
3193  appendPQExpBuffer(&buf,
3194  ",\n c2.relname as \"%s\"",
3195  gettext_noop("Table"));
3196 
3197  if (verbose)
3198  {
3199  /*
3200  * As of PostgreSQL 9.0, use pg_table_size() to show a more accurate
3201  * size of a table, including FSM, VM and TOAST tables.
3202  */
3203  if (pset.sversion >= 90000)
3204  appendPQExpBuffer(&buf,
3205  ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\"",
3206  gettext_noop("Size"));
3207  else if (pset.sversion >= 80100)
3208  appendPQExpBuffer(&buf,
3209  ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_relation_size(c.oid)) as \"%s\"",
3210  gettext_noop("Size"));
3211 
3212  appendPQExpBuffer(&buf,
3213  ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
3214  gettext_noop("Description"));
3215  }
3216 
3217  appendPQExpBufferStr(&buf,
3218  "\nFROM pg_catalog.pg_class c"
3219  "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
3220  if (showIndexes)
3221  appendPQExpBufferStr(&buf,
3222  "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
3223  "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
3224 
3225  appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
3226  if (showTables)
3227  appendPQExpBufferStr(&buf, "'r', 'P',");
3228  if (showViews)
3229  appendPQExpBufferStr(&buf, "'v',");
3230  if (showMatViews)
3231  appendPQExpBufferStr(&buf, "'m',");
3232  if (showIndexes)
3233  appendPQExpBufferStr(&buf, "'i',");
3234  if (showSeq)
3235  appendPQExpBufferStr(&buf, "'S',");
3236  if (showSystem || pattern)
3237  appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL in <=
3238  * 8.1 */
3239  if (showForeign)
3240  appendPQExpBufferStr(&buf, "'f',");
3241 
3242  appendPQExpBufferStr(&buf, "''"); /* dummy */
3243  appendPQExpBufferStr(&buf, ")\n");
3244 
3245  if (!showSystem && !pattern)
3246  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
3247  " AND n.nspname <> 'information_schema'\n");
3248 
3249  /*
3250  * TOAST objects are suppressed unconditionally. Since we don't provide
3251  * any way to select relkind 't' above, we would never show toast tables
3252  * in any case; it seems a bit confusing to allow their indexes to be
3253  * shown. Use plain \d if you really need to look at a TOAST table/index.
3254  */
3255  appendPQExpBufferStr(&buf, " AND n.nspname !~ '^pg_toast'\n");
3256 
3257  processSQLNamePattern(pset.db, &buf, pattern, true, false,
3258  "n.nspname", "c.relname", NULL,
3259  "pg_catalog.pg_table_is_visible(c.oid)");
3260 
3261  appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
3262 
3263  res = PSQLexec(buf.data);
3264  termPQExpBuffer(&buf);
3265  if (!res)
3266  return false;
3267 
3268  if (PQntuples(res) == 0 && !pset.quiet)
3269  {
3270  if (pattern)
3271  fprintf(pset.queryFout, _("No matching relations found.\n"));
3272  else
3273  fprintf(pset.queryFout, _("No relations found.\n"));
3274  }
3275  else
3276  {
3277  myopt.nullPrint = NULL;
3278  myopt.title = _("List of relations");
3279  myopt.translate_header = true;
3280  myopt.translate_columns = translate_columns;
3281  myopt.n_translate_columns = lengthof(translate_columns);
3282 
3283  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3284  }
3285 
3286  PQclear(res);
3287  return true;
3288 }
3289 
3290 
3291 /*
3292  * \dL
3293  *
3294  * Describes languages.
3295  */
3296 bool
3297 listLanguages(const char *pattern, bool verbose, bool showSystem)
3298 {
3300  PGresult *res;
3301  printQueryOpt myopt = pset.popt;
3302 
3303  initPQExpBuffer(&buf);
3304 
3305  printfPQExpBuffer(&buf,
3306  "SELECT l.lanname AS \"%s\",\n",
3307  gettext_noop("Name"));
3308  if (pset.sversion >= 80300)
3309  appendPQExpBuffer(&buf,
3310  " pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n",
3311  gettext_noop("Owner"));
3312 
3313  appendPQExpBuffer(&buf,
3314  " l.lanpltrusted AS \"%s\"",
3315  gettext_noop("Trusted"));
3316 
3317  if (verbose)
3318  {
3319  appendPQExpBuffer(&buf,
3320  ",\n NOT l.lanispl AS \"%s\",\n"
3321  " l.lanplcallfoid::regprocedure AS \"%s\",\n"
3322  " l.lanvalidator::regprocedure AS \"%s\",\n ",
3323  gettext_noop("Internal Language"),
3324  gettext_noop("Call Handler"),
3325  gettext_noop("Validator"));
3326  if (pset.sversion >= 90000)
3327  appendPQExpBuffer(&buf, "l.laninline::regprocedure AS \"%s\",\n ",
3328  gettext_noop("Inline Handler"));
3329  printACLColumn(&buf, "l.lanacl");
3330  }
3331 
3332  appendPQExpBuffer(&buf,
3333  ",\n d.description AS \"%s\""
3334  "\nFROM pg_catalog.pg_language l\n"
3335  "LEFT JOIN pg_catalog.pg_description d\n"
3336  " ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
3337  " AND d.objsubid = 0\n",
3338  gettext_noop("Description"));
3339 
3340  if (pattern)
3341  processSQLNamePattern(pset.db, &buf, pattern, false, false,
3342  NULL, "l.lanname", NULL, NULL);
3343 
3344  if (!showSystem && !pattern)
3345  appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n");
3346 
3347 
3348  appendPQExpBufferStr(&buf, "ORDER BY 1;");
3349 
3350  res = PSQLexec(buf.data);
3351  termPQExpBuffer(&buf);
3352  if (!res)
3353  return false;
3354 
3355  myopt.nullPrint = NULL;
3356  myopt.title = _("List of languages");
3357  myopt.translate_header = true;
3358 
3359  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3360 
3361  PQclear(res);
3362  return true;
3363 }
3364 
3365 
3366 /*
3367  * \dD
3368  *
3369  * Describes domains.
3370  */
3371 bool
3372 listDomains(const char *pattern, bool verbose, bool showSystem)
3373 {
3375  PGresult *res;
3376  printQueryOpt myopt = pset.popt;
3377 
3378  initPQExpBuffer(&buf);
3379 
3380  printfPQExpBuffer(&buf,
3381  "SELECT n.nspname as \"%s\",\n"
3382  " t.typname as \"%s\",\n"
3383  " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n",
3384  gettext_noop("Schema"),
3385  gettext_noop("Name"),
3386  gettext_noop("Type"));
3387 
3388  if (pset.sversion >= 90100)
3389  appendPQExpBuffer(&buf,
3390  " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
3391  " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n",
3392  gettext_noop("Collation"));
3393  appendPQExpBuffer(&buf,
3394  " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
3395  " t.typdefault as \"%s\",\n"
3396  " pg_catalog.array_to_string(ARRAY(\n"
3397  " SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid\n"
3398  " ), ' ') as \"%s\"",
3399  gettext_noop("Nullable"),
3400  gettext_noop("Default"),
3401  gettext_noop("Check"));
3402 
3403  if (verbose)
3404  {
3405  if (pset.sversion >= 90200)
3406  {
3407  appendPQExpBufferStr(&buf, ",\n ");
3408  printACLColumn(&buf, "t.typacl");
3409  }
3410  appendPQExpBuffer(&buf,
3411  ",\n d.description as \"%s\"",
3412  gettext_noop("Description"));
3413  }
3414 
3415  appendPQExpBufferStr(&buf,
3416  "\nFROM pg_catalog.pg_type t\n"
3417  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
3418 
3419  if (verbose)
3420  appendPQExpBufferStr(&buf,
3421  " LEFT JOIN pg_catalog.pg_description d "
3422  "ON d.classoid = t.tableoid AND d.objoid = t.oid "
3423  "AND d.objsubid = 0\n");
3424 
3425  appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
3426 
3427  if (!showSystem && !pattern)
3428  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
3429  " AND n.nspname <> 'information_schema'\n");
3430 
3431  processSQLNamePattern(pset.db, &buf, pattern, true, false,
3432  "n.nspname", "t.typname", NULL,
3433  "pg_catalog.pg_type_is_visible(t.oid)");
3434 
3435  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3436 
3437  res = PSQLexec(buf.data);
3438  termPQExpBuffer(&buf);
3439  if (!res)
3440  return false;
3441 
3442  myopt.nullPrint = NULL;
3443  myopt.title = _("List of domains");
3444  myopt.translate_header = true;
3445 
3446  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3447 
3448  PQclear(res);
3449  return true;
3450 }
3451 
3452 /*
3453  * \dc
3454  *
3455  * Describes conversions.
3456  */
3457 bool
3458 listConversions(const char *pattern, bool verbose, bool showSystem)
3459 {
3461  PGresult *res;
3462  printQueryOpt myopt = pset.popt;
3463  static const bool translate_columns[] =
3464  {false, false, false, false, true, false};
3465 
3466  initPQExpBuffer(&buf);
3467 
3468  printfPQExpBuffer(&buf,
3469  "SELECT n.nspname AS \"%s\",\n"
3470  " c.conname AS \"%s\",\n"
3471  " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
3472  " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
3473  " CASE WHEN c.condefault THEN '%s'\n"
3474  " ELSE '%s' END AS \"%s\"",
3475  gettext_noop("Schema"),
3476  gettext_noop("Name"),
3477  gettext_noop("Source"),
3478  gettext_noop("Destination"),
3479  gettext_noop("yes"), gettext_noop("no"),
3480  gettext_noop("Default?"));
3481 
3482  if (verbose)
3483  appendPQExpBuffer(&buf,
3484  ",\n d.description AS \"%s\"",
3485  gettext_noop("Description"));
3486 
3487  appendPQExpBufferStr(&buf,
3488  "\nFROM pg_catalog.pg_conversion c\n"
3489  " JOIN pg_catalog.pg_namespace n "
3490  "ON n.oid = c.connamespace\n");
3491 
3492  if (verbose)
3493  appendPQExpBufferStr(&buf,
3494  "LEFT JOIN pg_catalog.pg_description d "
3495  "ON d.classoid = c.tableoid\n"
3496  " AND d.objoid = c.oid "
3497  "AND d.objsubid = 0\n");
3498 
3499  appendPQExpBufferStr(&buf, "WHERE true\n");
3500 
3501  if (!showSystem && !pattern)
3502  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
3503  " AND n.nspname <> 'information_schema'\n");
3504 
3505  processSQLNamePattern(pset.db, &buf, pattern, true, false,
3506  "n.nspname", "c.conname", NULL,
3507  "pg_catalog.pg_conversion_is_visible(c.oid)");
3508 
3509  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3510 
3511  res = PSQLexec(buf.data);
3512  termPQExpBuffer(&buf);
3513  if (!res)
3514  return false;
3515 
3516  myopt.nullPrint = NULL;
3517  myopt.title = _("List of conversions");
3518  myopt.translate_header = true;
3519  myopt.translate_columns = translate_columns;
3520  myopt.n_translate_columns = lengthof(translate_columns);
3521 
3522  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3523 
3524  PQclear(res);
3525  return true;
3526 }
3527 
3528 /*
3529  * \dy
3530  *
3531  * Describes Event Triggers.
3532  */
3533 bool
3534 listEventTriggers(const char *pattern, bool verbose)
3535 {
3537  PGresult *res;
3538  printQueryOpt myopt = pset.popt;
3539  static const bool translate_columns[] =
3540  {false, false, false, true, false, false, false};
3541 
3542  initPQExpBuffer(&buf);
3543 
3544  printfPQExpBuffer(&buf,
3545  "SELECT evtname as \"%s\", "
3546  "evtevent as \"%s\", "
3547  "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
3548  " case evtenabled when 'O' then '%s'"
3549  " when 'R' then '%s'"
3550  " when 'A' then '%s'"
3551  " when 'D' then '%s' end as \"%s\",\n"
3552  " e.evtfoid::pg_catalog.regproc as \"%s\", "
3553  "pg_catalog.array_to_string(array(select x"
3554  " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
3555  gettext_noop("Name"),
3556  gettext_noop("Event"),
3557  gettext_noop("Owner"),
3558  gettext_noop("enabled"),
3559  gettext_noop("replica"),
3560  gettext_noop("always"),
3561  gettext_noop("disabled"),
3562  gettext_noop("Enabled"),
3563  gettext_noop("Procedure"),
3564  gettext_noop("Tags"));
3565  if (verbose)
3566  appendPQExpBuffer(&buf,
3567  ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
3568  gettext_noop("Description"));
3569  appendPQExpBufferStr(&buf,
3570  "\nFROM pg_catalog.pg_event_trigger e ");
3571 
3572  processSQLNamePattern(pset.db, &buf, pattern, false, false,
3573  NULL, "evtname", NULL, NULL);
3574 
3575  appendPQExpBufferStr(&buf, "ORDER BY 1");
3576 
3577  res = PSQLexec(buf.data);
3578  termPQExpBuffer(&buf);
3579  if (!res)
3580  return false;
3581 
3582  myopt.nullPrint = NULL;
3583  myopt.title = _("List of event triggers");
3584  myopt.translate_header = true;
3585  myopt.translate_columns = translate_columns;
3586  myopt.n_translate_columns = lengthof(translate_columns);
3587 
3588  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3589 
3590  PQclear(res);
3591  return true;
3592 }
3593 
3594 /*
3595  * \dC
3596  *
3597  * Describes casts.
3598  */
3599 bool
3600 listCasts(const char *pattern, bool verbose)
3601 {
3603  PGresult *res;
3604  printQueryOpt myopt = pset.popt;
3605  static const bool translate_columns[] = {false, false, false, true, false};
3606 
3607  initPQExpBuffer(&buf);
3608 
3609  /*
3610  * We need a left join to pg_proc for binary casts; the others are just
3611  * paranoia. Also note that we don't attempt to localize '(binary
3612  * coercible)', because there's too much risk of gettext translating a
3613  * function name that happens to match some string in the PO database.
3614  */
3615  printfPQExpBuffer(&buf,
3616  "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
3617  " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n"
3618  " CASE WHEN castfunc = 0 THEN '(binary coercible)'\n"
3619  " ELSE p.proname\n"
3620  " END as \"%s\",\n"
3621  " CASE WHEN c.castcontext = 'e' THEN '%s'\n"
3622  " WHEN c.castcontext = 'a' THEN '%s'\n"
3623  " ELSE '%s'\n"
3624  " END as \"%s\"",
3625  gettext_noop("Source type"),
3626  gettext_noop("Target type"),
3627  gettext_noop("Function"),
3628  gettext_noop("no"),
3629  gettext_noop("in assignment"),
3630  gettext_noop("yes"),
3631  gettext_noop("Implicit?"));
3632 
3633  if (verbose)
3634  appendPQExpBuffer(&buf,
3635  ",\n d.description AS \"%s\"\n",
3636  gettext_noop("Description"));
3637 
3638  appendPQExpBufferStr(&buf,
3639  "FROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
3640  " ON c.castfunc = p.oid\n"
3641  " LEFT JOIN pg_catalog.pg_type ts\n"
3642  " ON c.castsource = ts.oid\n"
3643  " LEFT JOIN pg_catalog.pg_namespace ns\n"
3644  " ON ns.oid = ts.typnamespace\n"
3645  " LEFT JOIN pg_catalog.pg_type tt\n"
3646  " ON c.casttarget = tt.oid\n"
3647  " LEFT JOIN pg_catalog.pg_namespace nt\n"
3648  " ON nt.oid = tt.typnamespace\n");
3649 
3650  if (verbose)
3651  appendPQExpBufferStr(&buf,
3652  " LEFT JOIN pg_catalog.pg_description d\n"
3653  " ON d.classoid = c.tableoid AND d.objoid = "
3654  "c.oid AND d.objsubid = 0\n");
3655 
3656  appendPQExpBufferStr(&buf, "WHERE ( (true");
3657 
3658  /*
3659  * Match name pattern against either internal or external name of either
3660  * castsource or casttarget
3661  */
3662  processSQLNamePattern(pset.db, &buf, pattern, true, false,
3663  "ns.nspname", "ts.typname",
3664  "pg_catalog.format_type(ts.oid, NULL)",
3665  "pg_catalog.pg_type_is_visible(ts.oid)");
3666 
3667  appendPQExpBufferStr(&buf, ") OR (true");
3668 
3669  processSQLNamePattern(pset.db, &buf, pattern, true, false,
3670  "nt.nspname", "tt.typname",
3671  "pg_catalog.format_type(tt.oid, NULL)",
3672  "pg_catalog.pg_type_is_visible(tt.oid)");
3673 
3674  appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
3675 
3676  res = PSQLexec(buf.data);
3677  termPQExpBuffer(&buf);
3678  if (!res)
3679  return false;
3680 
3681  myopt.nullPrint = NULL;
3682  myopt.title = _("List of casts");
3683  myopt.translate_header = true;
3684  myopt.translate_columns = translate_columns;
3685  myopt.n_translate_columns = lengthof(translate_columns);
3686 
3687  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3688 
3689  PQclear(res);
3690  return true;
3691 }
3692 
3693 /*
3694  * \dO
3695  *
3696  * Describes collations.
3697  */
3698 bool
3699 listCollations(const char *pattern, bool verbose, bool showSystem)
3700 {
3702  PGresult *res;
3703  printQueryOpt myopt = pset.popt;
3704  static const bool translate_columns[] = {false, false, false, false, false};
3705 
3706  if (pset.sversion < 90100)
3707  {
3708  char sverbuf[32];
3709 
3710  psql_error("The server (version %s) does not support collations.\n",
3712  sverbuf, sizeof(sverbuf)));
3713  return true;
3714  }
3715 
3716  initPQExpBuffer(&buf);
3717 
3718  printfPQExpBuffer(&buf,
3719  "SELECT n.nspname AS \"%s\",\n"
3720  " c.collname AS \"%s\",\n"
3721  " c.collcollate AS \"%s\",\n"
3722  " c.collctype AS \"%s\"",
3723  gettext_noop("Schema"),
3724  gettext_noop("Name"),
3725  gettext_noop("Collate"),
3726  gettext_noop("Ctype"));
3727 
3728  if (verbose)
3729  appendPQExpBuffer(&buf,
3730  ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
3731  gettext_noop("Description"));
3732 
3733  appendPQExpBufferStr(&buf,
3734  "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
3735  "WHERE n.oid = c.collnamespace\n");
3736 
3737  if (!showSystem && !pattern)
3738  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
3739  " AND n.nspname <> 'information_schema'\n");
3740 
3741  /*
3742  * Hide collations that aren't usable in the current database's encoding.
3743  * If you think to change this, note that pg_collation_is_visible rejects
3744  * unusable collations, so you will need to hack name pattern processing
3745  * somehow to avoid inconsistent behavior.
3746  */
3747  appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
3748 
3749  processSQLNamePattern(pset.db, &buf, pattern, true, false,
3750  "n.nspname", "c.collname", NULL,
3751  "pg_catalog.pg_collation_is_visible(c.oid)");
3752 
3753  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3754 
3755  res = PSQLexec(buf.data);
3756  termPQExpBuffer(&buf);
3757  if (!res)
3758  return false;
3759 
3760  myopt.nullPrint = NULL;
3761  myopt.title = _("List of collations");
3762  myopt.translate_header = true;
3763  myopt.translate_columns = translate_columns;
3764  myopt.n_translate_columns = lengthof(translate_columns);
3765 
3766  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3767 
3768  PQclear(res);
3769  return true;
3770 }
3771 
3772 /*
3773  * \dn
3774  *
3775  * Describes schemas (namespaces)
3776  */
3777 bool
3778 listSchemas(const char *pattern, bool verbose, bool showSystem)
3779 {
3781  PGresult *res;
3782  printQueryOpt myopt = pset.popt;
3783 
3784  initPQExpBuffer(&buf);
3785  printfPQExpBuffer(&buf,
3786  "SELECT n.nspname AS \"%s\",\n"
3787  " pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
3788  gettext_noop("Name"),
3789  gettext_noop("Owner"));
3790 
3791  if (verbose)
3792  {
3793  appendPQExpBufferStr(&buf, ",\n ");
3794  printACLColumn(&buf, "n.nspacl");
3795  appendPQExpBuffer(&buf,
3796  ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
3797  gettext_noop("Description"));
3798  }
3799 
3800  appendPQExpBuffer(&buf,
3801  "\nFROM pg_catalog.pg_namespace n\n");
3802 
3803  if (!showSystem && !pattern)
3804  appendPQExpBufferStr(&buf,
3805  "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
3806 
3807  processSQLNamePattern(pset.db, &buf, pattern,
3808  !showSystem && !pattern, false,
3809  NULL, "n.nspname", NULL,
3810  NULL);
3811 
3812  appendPQExpBufferStr(&buf, "ORDER BY 1;");
3813 
3814  res = PSQLexec(buf.data);
3815  termPQExpBuffer(&buf);
3816  if (!res)
3817  return false;
3818 
3819  myopt.nullPrint = NULL;
3820  myopt.title = _("List of schemas");
3821  myopt.translate_header = true;
3822 
3823  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3824 
3825  PQclear(res);
3826  return true;
3827 }
3828 
3829 
3830 /*
3831  * \dFp
3832  * list text search parsers
3833  */
3834 bool
3835 listTSParsers(const char *pattern, bool verbose)
3836 {
3838  PGresult *res;
3839  printQueryOpt myopt = pset.popt;
3840 
3841  if (pset.sversion < 80300)
3842  {
3843  char sverbuf[32];
3844 
3845  psql_error("The server (version %s) does not support full text search.\n",
3847  sverbuf, sizeof(sverbuf)));
3848  return true;
3849  }
3850 
3851  if (verbose)
3852  return listTSParsersVerbose(pattern);
3853 
3854  initPQExpBuffer(&buf);
3855 
3856  printfPQExpBuffer(&buf,
3857  "SELECT \n"
3858  " n.nspname as \"%s\",\n"
3859  " p.prsname as \"%s\",\n"
3860  " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
3861  "FROM pg_catalog.pg_ts_parser p \n"
3862  "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
3863  gettext_noop("Schema"),
3864  gettext_noop("Name"),
3865  gettext_noop("Description")
3866  );
3867 
3868  processSQLNamePattern(pset.db, &buf, pattern, false, false,
3869  "n.nspname", "p.prsname", NULL,
3870  "pg_catalog.pg_ts_parser_is_visible(p.oid)");
3871 
3872  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3873 
3874  res = PSQLexec(buf.data);
3875  termPQExpBuffer(&buf);
3876  if (!res)
3877  return false;
3878 
3879  myopt.nullPrint = NULL;
3880  myopt.title = _("List of text search parsers");
3881  myopt.translate_header = true;
3882 
3883  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3884 
3885  PQclear(res);
3886  return true;
3887 }
3888 
3889 /*
3890  * full description of parsers
3891  */
3892 static bool
3893 listTSParsersVerbose(const char *pattern)
3894 {
3896  PGresult *res;
3897  int i;
3898 
3899  initPQExpBuffer(&buf);
3900 
3901  printfPQExpBuffer(&buf,
3902  "SELECT p.oid, \n"
3903  " n.nspname, \n"
3904  " p.prsname \n"
3905  "FROM pg_catalog.pg_ts_parser p\n"
3906  "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
3907  );
3908 
3909  processSQLNamePattern(pset.db, &buf, pattern, false, false,
3910  "n.nspname", "p.prsname", NULL,
3911  "pg_catalog.pg_ts_parser_is_visible(p.oid)");
3912 
3913  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3914 
3915  res = PSQLexec(buf.data);
3916  termPQExpBuffer(&buf);
3917  if (!res)
3918  return false;
3919 
3920  if (PQntuples(res) == 0)
3921  {
3922  if (!pset.quiet)
3923  psql_error("Did not find any text search parser named \"%s\".\n",
3924  pattern);
3925  PQclear(res);
3926  return false;
3927  }
3928 
3929  for (i = 0; i < PQntuples(res); i++)
3930  {
3931  const char *oid;
3932  const char *nspname = NULL;
3933  const char *prsname;
3934 
3935  oid = PQgetvalue(res, i, 0);
3936  if (!PQgetisnull(res, i, 1))
3937  nspname = PQgetvalue(res, i, 1);
3938  prsname = PQgetvalue(res, i, 2);
3939 
3940  if (!describeOneTSParser(oid, nspname, prsname))
3941  {
3942  PQclear(res);
3943  return false;
3944  }
3945 
3946  if (cancel_pressed)
3947  {
3948  PQclear(res);
3949  return false;
3950  }
3951  }
3952 
3953  PQclear(res);
3954  return true;
3955 }
3956 
3957 static bool
3958 describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
3959 {
3961  PGresult *res;
3962  char title[1024];
3963  printQueryOpt myopt = pset.popt;
3964  static const bool translate_columns[] = {true, false, false};
3965 
3966  initPQExpBuffer(&buf);
3967 
3968  printfPQExpBuffer(&buf,
3969  "SELECT '%s' AS \"%s\", \n"
3970  " p.prsstart::pg_catalog.regproc AS \"%s\", \n"
3971  " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\" \n"
3972  " FROM pg_catalog.pg_ts_parser p \n"
3973  " WHERE p.oid = '%s' \n"
3974  "UNION ALL \n"
3975  "SELECT '%s', \n"
3976  " p.prstoken::pg_catalog.regproc, \n"
3977  " pg_catalog.obj_description(p.prstoken, 'pg_proc') \n"
3978  " FROM pg_catalog.pg_ts_parser p \n"
3979  " WHERE p.oid = '%s' \n"
3980  "UNION ALL \n"
3981  "SELECT '%s', \n"
3982  " p.prsend::pg_catalog.regproc, \n"
3983  " pg_catalog.obj_description(p.prsend, 'pg_proc') \n"
3984  " FROM pg_catalog.pg_ts_parser p \n"
3985  " WHERE p.oid = '%s' \n"
3986  "UNION ALL \n"
3987  "SELECT '%s', \n"
3988  " p.prsheadline::pg_catalog.regproc, \n"
3989  " pg_catalog.obj_description(p.prsheadline, 'pg_proc') \n"
3990  " FROM pg_catalog.pg_ts_parser p \n"
3991  " WHERE p.oid = '%s' \n"
3992  "UNION ALL \n"
3993  "SELECT '%s', \n"
3994  " p.prslextype::pg_catalog.regproc, \n"
3995  " pg_catalog.obj_description(p.prslextype, 'pg_proc') \n"
3996  " FROM pg_catalog.pg_ts_parser p \n"
3997  " WHERE p.oid = '%s';",
3998  gettext_noop("Start parse"),
3999  gettext_noop("Method"),
4000  gettext_noop("Function"),
4001  gettext_noop("Description"),
4002  oid,
4003  gettext_noop("Get next token"),
4004  oid,
4005  gettext_noop("End parse"),
4006  oid,
4007  gettext_noop("Get headline"),
4008  oid,
4009  gettext_noop("Get token types"),
4010  oid);
4011 
4012  res = PSQLexec(buf.data);
4013  termPQExpBuffer(&buf);
4014  if (!res)
4015  return false;
4016 
4017  myopt.nullPrint = NULL;
4018  if (nspname)
4019  sprintf(title, _("Text search parser \"%s.%s\""), nspname, prsname);
4020  else
4021  sprintf(title, _("Text search parser \"%s\""), prsname);
4022  myopt.title = title;
4023  myopt.footers = NULL;
4024  myopt.topt.default_footer = false;
4025  myopt.translate_header = true;
4026  myopt.translate_columns = translate_columns;
4027  myopt.n_translate_columns = lengthof(translate_columns);
4028 
4029  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4030 
4031  PQclear(res);
4032 
4033  initPQExpBuffer(&buf);
4034 
4035  printfPQExpBuffer(&buf,
4036  "SELECT t.alias as \"%s\", \n"
4037  " t.description as \"%s\" \n"
4038  "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t \n"
4039  "ORDER BY 1;",
4040  gettext_noop("Token name"),
4041  gettext_noop("Description"),
4042  oid);
4043 
4044  res = PSQLexec(buf.data);
4045  termPQExpBuffer(&buf);
4046  if (!res)
4047  return false;
4048 
4049  myopt.nullPrint = NULL;
4050  if (nspname)
4051  sprintf(title, _("Token types for parser \"%s.%s\""), nspname, prsname);
4052  else
4053  sprintf(title, _("Token types for parser \"%s\""), prsname);
4054  myopt.title = title;
4055  myopt.footers = NULL;
4056  myopt.topt.default_footer = true;
4057  myopt.translate_header = true;
4058  myopt.translate_columns = NULL;
4059  myopt.n_translate_columns = 0;
4060 
4061  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4062 
4063  PQclear(res);
4064  return true;
4065 }
4066 
4067 
4068 /*
4069  * \dFd
4070  * list text search dictionaries
4071  */
4072 bool
4073 listTSDictionaries(const char *pattern, bool verbose)
4074 {
4076  PGresult *res;
4077  printQueryOpt myopt = pset.popt;
4078 
4079  if (pset.sversion < 80300)
4080  {
4081  char sverbuf[32];
4082 
4083  psql_error("The server (version %s) does not support full text search.\n",
4085  sverbuf, sizeof(sverbuf)));
4086  return true;
4087  }
4088 
4089  initPQExpBuffer(&buf);
4090 
4091  printfPQExpBuffer(&buf,
4092  "SELECT \n"
4093  " n.nspname as \"%s\",\n"
4094  " d.dictname as \"%s\",\n",
4095  gettext_noop("Schema"),
4096  gettext_noop("Name"));
4097 
4098  if (verbose)
4099  {
4100  appendPQExpBuffer(&buf,
4101  " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM \n"
4102  " pg_catalog.pg_ts_template t \n"
4103  " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace \n"
4104  " WHERE d.dicttemplate = t.oid ) AS \"%s\", \n"
4105  " d.dictinitoption as \"%s\", \n",
4106  gettext_noop("Template"),
4107  gettext_noop("Init options"));
4108  }
4109 
4110  appendPQExpBuffer(&buf,
4111  " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
4112  gettext_noop("Description"));
4113 
4114  appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n"
4115  "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
4116 
4117  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4118  "n.nspname", "d.dictname", NULL,
4119  "pg_catalog.pg_ts_dict_is_visible(d.oid)");
4120 
4121  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4122 
4123  res = PSQLexec(buf.data);
4124  termPQExpBuffer(&buf);
4125  if (!res)
4126  return false;
4127 
4128  myopt.nullPrint = NULL;
4129  myopt.title = _("List of text search dictionaries");
4130  myopt.translate_header = true;
4131 
4132  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4133 
4134  PQclear(res);
4135  return true;
4136 }
4137 
4138 
4139 /*
4140  * \dFt
4141  * list text search templates
4142  */
4143 bool
4144 listTSTemplates(const char *pattern, bool verbose)
4145 {
4147  PGresult *res;
4148  printQueryOpt myopt = pset.popt;
4149 
4150  if (pset.sversion < 80300)
4151  {
4152  char sverbuf[32];
4153 
4154  psql_error("The server (version %s) does not support full text search.\n",
4156  sverbuf, sizeof(sverbuf)));
4157  return true;
4158  }
4159 
4160  initPQExpBuffer(&buf);
4161 
4162  if (verbose)
4163  printfPQExpBuffer(&buf,
4164  "SELECT \n"
4165  " n.nspname AS \"%s\",\n"
4166  " t.tmplname AS \"%s\",\n"
4167  " t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
4168  " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
4169  " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
4170  gettext_noop("Schema"),
4171  gettext_noop("Name"),
4172  gettext_noop("Init"),
4173  gettext_noop("Lexize"),
4174  gettext_noop("Description"));
4175  else
4176  printfPQExpBuffer(&buf,
4177  "SELECT \n"
4178  " n.nspname AS \"%s\",\n"
4179  " t.tmplname AS \"%s\",\n"
4180  " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
4181  gettext_noop("Schema"),
4182  gettext_noop("Name"),
4183  gettext_noop("Description"));
4184 
4185  appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n"
4186  "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
4187 
4188  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4189  "n.nspname", "t.tmplname", NULL,
4190  "pg_catalog.pg_ts_template_is_visible(t.oid)");
4191 
4192  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4193 
4194  res = PSQLexec(buf.data);
4195  termPQExpBuffer(&buf);
4196  if (!res)
4197  return false;
4198 
4199  myopt.nullPrint = NULL;
4200  myopt.title = _("List of text search templates");
4201  myopt.translate_header = true;
4202 
4203  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4204 
4205  PQclear(res);
4206  return true;
4207 }
4208 
4209 
4210 /*
4211  * \dF
4212  * list text search configurations
4213  */
4214 bool
4215 listTSConfigs(const char *pattern, bool verbose)
4216 {
4218  PGresult *res;
4219  printQueryOpt myopt = pset.popt;
4220 
4221  if (pset.sversion < 80300)
4222  {
4223  char sverbuf[32];
4224 
4225  psql_error("The server (version %s) does not support full text search.\n",
4227  sverbuf, sizeof(sverbuf)));
4228  return true;
4229  }
4230 
4231  if (verbose)
4232  return listTSConfigsVerbose(pattern);
4233 
4234  initPQExpBuffer(&buf);
4235 
4236  printfPQExpBuffer(&buf,
4237  "SELECT \n"
4238  " n.nspname as \"%s\",\n"
4239  " c.cfgname as \"%s\",\n"
4240  " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
4241  "FROM pg_catalog.pg_ts_config c\n"
4242  "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace \n",
4243  gettext_noop("Schema"),
4244  gettext_noop("Name"),
4245  gettext_noop("Description")
4246  );
4247 
4248  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4249  "n.nspname", "c.cfgname", NULL,
4250  "pg_catalog.pg_ts_config_is_visible(c.oid)");
4251 
4252  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4253 
4254  res = PSQLexec(buf.data);
4255  termPQExpBuffer(&buf);
4256  if (!res)
4257  return false;
4258 
4259  myopt.nullPrint = NULL;
4260  myopt.title = _("List of text search configurations");
4261  myopt.translate_header = true;
4262 
4263  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4264 
4265  PQclear(res);
4266  return true;
4267 }
4268 
4269 static bool
4270 listTSConfigsVerbose(const char *pattern)
4271 {
4273  PGresult *res;
4274  int i;
4275 
4276  initPQExpBuffer(&buf);
4277 
4278  printfPQExpBuffer(&buf,
4279  "SELECT c.oid, c.cfgname,\n"
4280  " n.nspname, \n"
4281  " p.prsname, \n"
4282  " np.nspname as pnspname \n"
4283  "FROM pg_catalog.pg_ts_config c \n"
4284  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace, \n"
4285  " pg_catalog.pg_ts_parser p \n"
4286  " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace \n"
4287  "WHERE p.oid = c.cfgparser\n"
4288  );
4289 
4290  processSQLNamePattern(pset.db, &buf, pattern, true, false,
4291  "n.nspname", "c.cfgname", NULL,
4292  "pg_catalog.pg_ts_config_is_visible(c.oid)");
4293 
4294  appendPQExpBufferStr(&buf, "ORDER BY 3, 2;");
4295 
4296  res = PSQLexec(buf.data);
4297  termPQExpBuffer(&buf);
4298  if (!res)
4299  return false;
4300 
4301  if (PQntuples(res) == 0)
4302  {
4303  if (!pset.quiet)
4304  psql_error("Did not find any text search configuration named \"%s\".\n",
4305  pattern);
4306  PQclear(res);
4307  return false;
4308  }
4309 
4310  for (i = 0; i < PQntuples(res); i++)
4311  {
4312  const char *oid;
4313  const char *cfgname;
4314  const char *nspname = NULL;
4315  const char *prsname;
4316  const char *pnspname = NULL;
4317 
4318  oid = PQgetvalue(res, i, 0);
4319  cfgname = PQgetvalue(res, i, 1);
4320  if (!PQgetisnull(res, i, 2))
4321  nspname = PQgetvalue(res, i, 2);
4322  prsname = PQgetvalue(res, i, 3);
4323  if (!PQgetisnull(res, i, 4))
4324  pnspname = PQgetvalue(res, i, 4);
4325 
4326  if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
4327  {
4328  PQclear(res);
4329  return false;
4330  }
4331 
4332  if (cancel_pressed)
4333  {
4334  PQclear(res);
4335  return false;
4336  }
4337  }
4338 
4339  PQclear(res);
4340  return true;
4341 }
4342 
4343 static bool
4344 describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
4345  const char *pnspname, const char *prsname)
4346 {
4348  title;
4349  PGresult *res;
4350  printQueryOpt myopt = pset.popt;
4351 
4352  initPQExpBuffer(&buf);
4353 
4354  printfPQExpBuffer(&buf,
4355  "SELECT \n"
4356  " ( SELECT t.alias FROM \n"
4357  " pg_catalog.ts_token_type(c.cfgparser) AS t \n"
4358  " WHERE t.tokid = m.maptokentype ) AS \"%s\", \n"
4359  " pg_catalog.btrim( \n"
4360  " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary \n"
4361  " FROM pg_catalog.pg_ts_config_map AS mm \n"
4362  " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype \n"
4363  " ORDER BY mapcfg, maptokentype, mapseqno \n"
4364  " ) :: pg_catalog.text , \n"
4365  " '{}') AS \"%s\" \n"
4366  "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m \n"
4367  "WHERE c.oid = '%s' AND m.mapcfg = c.oid \n"
4368  "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser \n"
4369  "ORDER BY 1;",
4370  gettext_noop("Token"),
4371  gettext_noop("Dictionaries"),
4372  oid);
4373 
4374  res = PSQLexec(buf.data);
4375  termPQExpBuffer(&buf);
4376  if (!res)
4377  return false;
4378 
4379  initPQExpBuffer(&title);
4380 
4381  if (nspname)
4382  appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
4383  nspname, cfgname);
4384  else
4385  appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
4386  cfgname);
4387 
4388  if (pnspname)
4389  appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
4390  pnspname, prsname);
4391  else
4392  appendPQExpBuffer(&title, _("\nParser: \"%s\""),
4393  prsname);
4394 
4395  myopt.nullPrint = NULL;
4396  myopt.title = title.data;
4397  myopt.footers = NULL;
4398  myopt.topt.default_footer = false;
4399  myopt.translate_header = true;
4400 
4401  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4402 
4403  termPQExpBuffer(&title);
4404 
4405  PQclear(res);
4406  return true;
4407 }
4408 
4409 
4410 /*
4411  * \dew
4412  *
4413  * Describes foreign-data wrappers
4414  */
4415 bool
4416 listForeignDataWrappers(const char *pattern, bool verbose)
4417 {
4419  PGresult *res;
4420  printQueryOpt myopt = pset.popt;
4421 
4422  if (pset.sversion < 80400)
4423  {
4424  char sverbuf[32];
4425 
4426  psql_error("The server (version %s) does not support foreign-data wrappers.\n",
4428  sverbuf, sizeof(sverbuf)));
4429  return true;
4430  }
4431 
4432  initPQExpBuffer(&buf);
4433  printfPQExpBuffer(&buf,
4434  "SELECT fdw.fdwname AS \"%s\",\n"
4435  " pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n",
4436  gettext_noop("Name"),
4437  gettext_noop("Owner"));
4438  if (pset.sversion >= 90100)
4439  appendPQExpBuffer(&buf,
4440  " fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n",
4441  gettext_noop("Handler"));
4442  appendPQExpBuffer(&buf,
4443  " fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
4444  gettext_noop("Validator"));
4445 
4446  if (verbose)
4447  {
4448  appendPQExpBufferStr(&buf, ",\n ");
4449  printACLColumn(&buf, "fdwacl");
4450  appendPQExpBuffer(&buf,
4451  ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE "
4452  " '(' || array_to_string(ARRAY(SELECT "
4453  " quote_ident(option_name) || ' ' || "
4454  " quote_literal(option_value) FROM "
4455  " pg_options_to_table(fdwoptions)), ', ') || ')' "
4456  " END AS \"%s\"",
4457  gettext_noop("FDW Options"));
4458 
4459  if (pset.sversion >= 90100)
4460  appendPQExpBuffer(&buf,
4461  ",\n d.description AS \"%s\" ",
4462  gettext_noop("Description"));
4463  }
4464 
4465  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
4466 
4467  if (verbose && pset.sversion >= 90100)
4468  appendPQExpBufferStr(&buf,
4469  "LEFT JOIN pg_catalog.pg_description d\n"
4470  " ON d.classoid = fdw.tableoid "
4471  "AND d.objoid = fdw.oid AND d.objsubid = 0\n");
4472 
4473  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4474  NULL, "fdwname", NULL, NULL);
4475 
4476  appendPQExpBufferStr(&buf, "ORDER BY 1;");
4477 
4478  res = PSQLexec(buf.data);
4479  termPQExpBuffer(&buf);
4480  if (!res)
4481  return false;
4482 
4483  myopt.nullPrint = NULL;
4484  myopt.title = _("List of foreign-data wrappers");
4485  myopt.translate_header = true;
4486 
4487  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4488 
4489  PQclear(res);
4490  return true;
4491 }
4492 
4493 /*
4494  * \des
4495  *
4496  * Describes foreign servers.
4497  */
4498 bool
4499 listForeignServers(const char *pattern, bool verbose)
4500 {
4502  PGresult *res;
4503  printQueryOpt myopt = pset.popt;
4504 
4505  if (pset.sversion < 80400)
4506  {
4507  char sverbuf[32];
4508 
4509  psql_error("The server (version %s) does not support foreign servers.\n",
4511  sverbuf, sizeof(sverbuf)));
4512  return true;
4513  }
4514 
4515  initPQExpBuffer(&buf);
4516  printfPQExpBuffer(&buf,
4517  "SELECT s.srvname AS \"%s\",\n"
4518  " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
4519  " f.fdwname AS \"%s\"",
4520  gettext_noop("Name"),
4521  gettext_noop("Owner"),
4522  gettext_noop("Foreign-data wrapper"));
4523 
4524  if (verbose)
4525  {
4526  appendPQExpBufferStr(&buf, ",\n ");
4527  printACLColumn(&buf, "s.srvacl");
4528  appendPQExpBuffer(&buf,
4529  ",\n"
4530  " s.srvtype AS \"%s\",\n"
4531  " s.srvversion AS \"%s\",\n"
4532  " CASE WHEN srvoptions IS NULL THEN '' ELSE "
4533  " '(' || array_to_string(ARRAY(SELECT "
4534  " quote_ident(option_name) || ' ' || "
4535  " quote_literal(option_value) FROM "
4536  " pg_options_to_table(srvoptions)), ', ') || ')' "
4537  " END AS \"%s\",\n"
4538  " d.description AS \"%s\"",
4539  gettext_noop("Type"),
4540  gettext_noop("Version"),
4541  gettext_noop("FDW Options"),
4542  gettext_noop("Description"));
4543  }
4544 
4545  appendPQExpBufferStr(&buf,
4546  "\nFROM pg_catalog.pg_foreign_server s\n"
4547  " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
4548 
4549  if (verbose)
4550  appendPQExpBufferStr(&buf,
4551  "LEFT JOIN pg_description d\n "
4552  "ON d.classoid = s.tableoid AND d.objoid = s.oid "
4553  "AND d.objsubid = 0\n");
4554 
4555  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4556  NULL, "s.srvname", NULL, NULL);
4557 
4558  appendPQExpBufferStr(&buf, "ORDER BY 1;");
4559 
4560  res = PSQLexec(buf.data);
4561  termPQExpBuffer(&buf);
4562  if (!res)
4563  return false;
4564 
4565  myopt.nullPrint = NULL;
4566  myopt.title = _("List of foreign servers");
4567  myopt.translate_header = true;
4568 
4569  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4570 
4571  PQclear(res);
4572  return true;
4573 }
4574 
4575 /*
4576  * \deu
4577  *
4578  * Describes user mappings.
4579  */
4580 bool
4581 listUserMappings(const char *pattern, bool verbose)
4582 {
4584  PGresult *res;
4585  printQueryOpt myopt = pset.popt;
4586 
4587  if (pset.sversion < 80400)
4588  {
4589  char sverbuf[32];
4590 
4591  psql_error("The server (version %s) does not support user mappings.\n",
4593  sverbuf, sizeof(sverbuf)));
4594  return true;
4595  }
4596 
4597  initPQExpBuffer(&buf);
4598  printfPQExpBuffer(&buf,
4599  "SELECT um.srvname AS \"%s\",\n"
4600  " um.usename AS \"%s\"",
4601  gettext_noop("Server"),
4602  gettext_noop("User name"));
4603 
4604  if (verbose)
4605  appendPQExpBuffer(&buf,
4606  ",\n CASE WHEN umoptions IS NULL THEN '' ELSE "
4607  " '(' || array_to_string(ARRAY(SELECT "
4608  " quote_ident(option_name) || ' ' || "
4609  " quote_literal(option_value) FROM "
4610  " pg_options_to_table(umoptions)), ', ') || ')' "
4611  " END AS \"%s\"",
4612  gettext_noop("FDW Options"));
4613 
4614  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
4615 
4616  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4617  NULL, "um.srvname", "um.usename", NULL);
4618 
4619  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4620 
4621  res = PSQLexec(buf.data);
4622  termPQExpBuffer(&buf);
4623  if (!res)
4624  return false;
4625 
4626  myopt.nullPrint = NULL;
4627  myopt.title = _("List of user mappings");
4628  myopt.translate_header = true;
4629 
4630  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4631 
4632  PQclear(res);
4633  return true;
4634 }
4635 
4636 /*
4637  * \det
4638  *
4639  * Describes foreign tables.
4640  */
4641 bool
4642 listForeignTables(const char *pattern, bool verbose)
4643 {
4645  PGresult *res;
4646  printQueryOpt myopt = pset.popt;
4647 
4648  if (pset.sversion < 90100)
4649  {
4650  char sverbuf[32];
4651 
4652  psql_error("The server (version %s) does not support foreign tables.\n",
4654  sverbuf, sizeof(sverbuf)));
4655  return true;
4656  }
4657 
4658  initPQExpBuffer(&buf);
4659  printfPQExpBuffer(&buf,
4660  "SELECT n.nspname AS \"%s\",\n"
4661  " c.relname AS \"%s\",\n"
4662  " s.srvname AS \"%s\"",
4663  gettext_noop("Schema"),
4664  gettext_noop("Table"),
4665  gettext_noop("Server"));
4666 
4667  if (verbose)
4668  appendPQExpBuffer(&buf,
4669  ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE "
4670  " '(' || array_to_string(ARRAY(SELECT "
4671  " quote_ident(option_name) || ' ' || "
4672  " quote_literal(option_value) FROM "
4673  " pg_options_to_table(ftoptions)), ', ') || ')' "
4674  " END AS \"%s\",\n"
4675  " d.description AS \"%s\"",
4676  gettext_noop("FDW Options"),
4677  gettext_noop("Description"));
4678 
4679  appendPQExpBufferStr(&buf,
4680  "\nFROM pg_catalog.pg_foreign_table ft\n"
4681  " INNER JOIN pg_catalog.pg_class c"
4682  " ON c.oid = ft.ftrelid\n"
4683  " INNER JOIN pg_catalog.pg_namespace n"
4684  " ON n.oid = c.relnamespace\n"
4685  " INNER JOIN pg_catalog.pg_foreign_server s"
4686  " ON s.oid = ft.ftserver\n");
4687  if (verbose)
4688  appendPQExpBufferStr(&buf,
4689  " LEFT JOIN pg_catalog.pg_description d\n"
4690  " ON d.classoid = c.tableoid AND "
4691  "d.objoid = c.oid AND d.objsubid = 0\n");
4692 
4693  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4694  "n.nspname", "c.relname", NULL,
4695  "pg_catalog.pg_table_is_visible(c.oid)");
4696 
4697  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4698 
4699  res = PSQLexec(buf.data);
4700  termPQExpBuffer(&buf);
4701  if (!res)
4702  return false;
4703 
4704  myopt.nullPrint = NULL;
4705  myopt.title = _("List of foreign tables");
4706  myopt.translate_header = true;
4707 
4708  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4709 
4710  PQclear(res);
4711  return true;
4712 }
4713 
4714 /*
4715  * \dx
4716  *
4717  * Briefly describes installed extensions.
4718  */
4719 bool
4720 listExtensions(const char *pattern)
4721 {
4723  PGresult *res;
4724  printQueryOpt myopt = pset.popt;
4725 
4726  if (pset.sversion < 90100)
4727  {
4728  char sverbuf[32];
4729 
4730  psql_error("The server (version %s) does not support extensions.\n",
4732  sverbuf, sizeof(sverbuf)));
4733  return true;
4734  }
4735 
4736  initPQExpBuffer(&buf);
4737  printfPQExpBuffer(&buf,
4738  "SELECT e.extname AS \"%s\", "
4739  "e.extversion AS \"%s\", n.nspname AS \"%s\", c.description AS \"%s\"\n"
4740  "FROM pg_catalog.pg_extension e "
4741  "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
4742  "LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid "
4743  "AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n",
4744  gettext_noop("Name"),
4745  gettext_noop("Version"),
4746  gettext_noop("Schema"),
4747  gettext_noop("Description"));
4748 
4749  processSQLNamePattern(pset.db, &buf, pattern,
4750  false, false,
4751  NULL, "e.extname", NULL,
4752  NULL);
4753 
4754  appendPQExpBufferStr(&buf, "ORDER BY 1;");
4755 
4756  res = PSQLexec(buf.data);
4757  termPQExpBuffer(&buf);
4758  if (!res)
4759  return false;
4760 
4761  myopt.nullPrint = NULL;
4762  myopt.title = _("List of installed extensions");
4763  myopt.translate_header = true;
4764 
4765  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4766 
4767  PQclear(res);
4768  return true;
4769 }
4770 
4771 /*
4772  * \dx+
4773  *
4774  * List contents of installed extensions.
4775  */
4776 bool
4777 listExtensionContents(const char *pattern)
4778 {
4780  PGresult *res;
4781  int i;
4782 
4783  if (pset.sversion < 90100)
4784  {
4785  char sverbuf[32];
4786 
4787  psql_error("The server (version %s) does not support extensions.\n",
4789  sverbuf, sizeof(sverbuf)));
4790  return true;
4791  }
4792 
4793  initPQExpBuffer(&buf);
4794  printfPQExpBuffer(&buf,
4795  "SELECT e.extname, e.oid\n"
4796  "FROM pg_catalog.pg_extension e\n");
4797 
4798  processSQLNamePattern(pset.db, &buf, pattern,
4799  false, false,
4800  NULL, "e.extname", NULL,
4801  NULL);
4802 
4803  appendPQExpBufferStr(&buf, "ORDER BY 1;");
4804 
4805  res = PSQLexec(buf.data);
4806  termPQExpBuffer(&buf);
4807  if (!res)
4808  return false;
4809 
4810  if (PQntuples(res) == 0)
4811  {
4812  if (!pset.quiet)
4813  {
4814  if (pattern)
4815  psql_error("Did not find any extension named \"%s\".\n",
4816  pattern);
4817  else
4818  psql_error("Did not find any extensions.\n");
4819  }
4820  PQclear(res);
4821  return false;
4822  }
4823 
4824  for (i = 0; i < PQntuples(res); i++)
4825  {
4826  const char *extname;
4827  const char *oid;
4828 
4829  extname = PQgetvalue(res, i, 0);
4830  oid = PQgetvalue(res, i, 1);
4831 
4832  if (!listOneExtensionContents(extname, oid))
4833  {
4834  PQclear(res);
4835  return false;
4836  }
4837  if (cancel_pressed)
4838  {
4839  PQclear(res);
4840  return false;
4841  }
4842  }
4843 
4844  PQclear(res);
4845  return true;
4846 }
4847 
4848 static bool
4849 listOneExtensionContents(const char *extname, const char *oid)
4850 {
4852  PGresult *res;
4853  char title[1024];
4854  printQueryOpt myopt = pset.popt;
4855 
4856  initPQExpBuffer(&buf);
4857  printfPQExpBuffer(&buf,
4858  "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
4859  "FROM pg_catalog.pg_depend\n"
4860  "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
4861  "ORDER BY 1;",
4862  gettext_noop("Object Description"),
4863  oid);
4864 
4865  res = PSQLexec(buf.data);
4866  termPQExpBuffer(&buf);
4867  if (!res)
4868  return false;
4869 
4870  myopt.nullPrint = NULL;
4871  snprintf(title, sizeof(title), _("Objects in extension \"%s\""), extname);
4872  myopt.title = title;
4873  myopt.translate_header = true;
4874 
4875  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4876 
4877  PQclear(res);
4878  return true;
4879 }
4880 
4881 /* \dRp
4882  * Lists publications.
4883  *
4884  * Takes an optional regexp to select particular publications
4885  */
4886 bool
4887 listPublications(const char *pattern)
4888 {
4890  PGresult *res;
4891  printQueryOpt myopt = pset.popt;
4892  static const bool translate_columns[] = {false, false, false, false, false};
4893 
4894  if (pset.sversion < 100000)
4895  {
4896  char sverbuf[32];
4897  psql_error("The server (version %s) does not support publications.\n",
4899  sverbuf, sizeof(sverbuf)));
4900  return true;
4901  }
4902 
4903  initPQExpBuffer(&buf);
4904 
4905  printfPQExpBuffer(&buf,
4906  "SELECT pubname AS \"%s\",\n"
4907  " pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
4908  " pubinsert AS \"%s\",\n"
4909  " pubupdate AS \"%s\",\n"
4910  " pubdelete AS \"%s\"\n",
4911  gettext_noop("Name"),
4912  gettext_noop("Owner"),
4913  gettext_noop("Inserts"),
4914  gettext_noop("Updates"),
4915  gettext_noop("Deletes"));
4916 
4917  appendPQExpBufferStr(&buf,
4918  "\nFROM pg_catalog.pg_publication\n");
4919 
4920  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4921  NULL, "pubname", NULL,
4922  NULL);
4923 
4924  appendPQExpBufferStr(&buf, "ORDER BY 1;");
4925 
4926  res = PSQLexec(buf.data);
4927  termPQExpBuffer(&buf);
4928  if (!res)
4929  return false;
4930 
4931  myopt.nullPrint = NULL;
4932  myopt.title = _("List of publications");
4933  myopt.translate_header = true;
4934  myopt.translate_columns = translate_columns;
4935  myopt.n_translate_columns = lengthof(translate_columns);
4936 
4937  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4938 
4939  PQclear(res);
4940 
4941  return true;
4942 }
4943 
4944 /* \dRp+
4945  * Describes publications including the contents.
4946  *
4947  * Takes an optional regexp to select particular publications
4948  */
4949 bool
4950 describePublications(const char *pattern)
4951 {
4953  int i;
4954  PGresult *res;
4955 
4956  if (pset.sversion < 100000)
4957  {
4958  char sverbuf[32];
4959  psql_error("The server (version %s) does not support publications.\n",
4961  sverbuf, sizeof(sverbuf)));
4962  return true;
4963  }
4964 
4965  initPQExpBuffer(&buf);
4966 
4967  printfPQExpBuffer(&buf,
4968  "SELECT oid, pubname, puballtables, pubinsert,\n"
4969  " pubupdate, pubdelete\n"
4970  "FROM pg_catalog.pg_publication\n");
4971 
4972  processSQLNamePattern(pset.db, &buf, pattern, false, false,
4973  NULL, "pubname", NULL,
4974  NULL);
4975 
4976  appendPQExpBufferStr(&buf, "ORDER BY 2;");
4977 
4978  res = PSQLexec(buf.data);
4979  if (!res)
4980  {
4981  termPQExpBuffer(&buf);
4982  return false;
4983  }
4984 
4985  for (i = 0; i < PQntuples(res); i++)
4986  {
4987  const char align = 'l';
4988  int ncols = 3;
4989  int nrows = 1;
4990  int tables = 0;
4991  PGresult *tabres;
4992  char *pubid = PQgetvalue(res, i, 0);
4993  char *pubname = PQgetvalue(res, i, 1);
4994  bool puballtables = strcmp(PQgetvalue(res, i, 2), "t") == 0;
4995  int j;
4996  PQExpBufferData title;
4997  printTableOpt myopt = pset.popt.topt;
4998  printTableContent cont;
4999 
5000  initPQExpBuffer(&title);
5001  printfPQExpBuffer(&title, _("Publication %s"), pubname);
5002  printTableInit(&cont, &myopt, title.data, ncols, nrows);
5003 
5004  printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
5005  printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
5006  printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
5007 
5008  printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
5009  printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
5010  printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
5011 
5012  if (puballtables)
5013  printfPQExpBuffer(&buf,
5014  "SELECT n.nspname, c.relname\n"
5015  "FROM pg_catalog.pg_class c,\n"
5016  " pg_catalog.pg_namespace n\n"
5017  "WHERE c.relnamespace = n.oid\n"
5018  " AND c.relkind = 'r'\n"
5019  " AND n.nspname <> 'pg_catalog'\n"
5020  " AND n.nspname <> 'information_schema'\n"
5021  "ORDER BY 1,2");
5022  else
5023  printfPQExpBuffer(&buf,
5024  "SELECT n.nspname, c.relname\n"
5025  "FROM pg_catalog.pg_class c,\n"
5026  " pg_catalog.pg_namespace n,\n"
5027  " pg_catalog.pg_publication_rel pr\n"
5028  "WHERE c.relnamespace = n.oid\n"
5029  " AND c.oid = pr.prrelid\n"
5030  " AND pr.prpubid = '%s'\n"
5031  "ORDER BY 1,2", pubid);
5032 
5033  tabres = PSQLexec(buf.data);
5034  if (!tabres)
5035  {
5036  printTableCleanup(&cont);
5037  PQclear(res);
5038  termPQExpBuffer(&buf);
5039  termPQExpBuffer(&title);
5040  return false;
5041  }
5042  else
5043  tables = PQntuples(tabres);
5044 
5045  if (tables > 0)
5046  printTableAddFooter(&cont, _("Tables:"));
5047 
5048  for (j = 0; j < tables; j++)
5049  {
5050  printfPQExpBuffer(&buf, " \"%s.%s\"",
5051  PQgetvalue(tabres, j, 0),
5052  PQgetvalue(tabres, j, 1));
5053 
5054  printTableAddFooter(&cont, buf.data);
5055  }
5056  PQclear(tabres);
5057 
5058  printTable(&cont, pset.queryFout, false, pset.logfile);
5059  printTableCleanup(&cont);
5060 
5061  termPQExpBuffer(&title);
5062  }
5063 
5064  termPQExpBuffer(&buf);
5065  PQclear(res);
5066 
5067  return true;
5068 }
5069 
5070 /* \dRs
5071  * Describes subscriptions.
5072  *
5073  * Takes an optional regexp to select particular subscriptions
5074  */
5075 bool
5076 describeSubscriptions(const char *pattern, bool verbose)
5077 {
5079  PGresult *res;
5080  printQueryOpt myopt = pset.popt;
5081  static const bool translate_columns[] = {false, false, false, false, false};
5082 
5083  if (pset.sversion < 100000)
5084  {
5085  char sverbuf[32];
5086  psql_error("The server (version %s) does not support subscriptions.\n",
5088  sverbuf, sizeof(sverbuf)));
5089  return true;
5090  }
5091 
5092  initPQExpBuffer(&buf);
5093 
5094  printfPQExpBuffer(&buf,
5095  "SELECT subname AS \"%s\"\n"
5096  ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
5097  ", subenabled AS \"%s\"\n"
5098  ", subpublications AS \"%s\"\n",
5099  gettext_noop("Name"),
5100  gettext_noop("Owner"),
5101  gettext_noop("Enabled"),
5102  gettext_noop("Publication"));
5103 
5104  if (verbose)
5105  {
5106  appendPQExpBuffer(&buf,
5107  ", subconninfo AS \"%s\"\n",
5108  gettext_noop("Conninfo"));
5109  }
5110 
5111  /* Only display subscriptions in current database. */
5112  appendPQExpBufferStr(&buf,
5113  "FROM pg_catalog.pg_subscription\n"
5114  "WHERE subdbid = (SELECT oid\n"
5115  " FROM pg_catalog.pg_database\n"
5116  " WHERE datname = current_database())");
5117 
5118  processSQLNamePattern(pset.db, &buf, pattern, true, false,
5119  NULL, "subname", NULL,
5120  NULL);
5121 
5122  appendPQExpBufferStr(&buf, "ORDER BY 1;");
5123 
5124  res = PSQLexec(buf.data);
5125  termPQExpBuffer(&buf);
5126  if (!res)
5127  return false;
5128 
5129  myopt.nullPrint = NULL;
5130  myopt.title = _("List of subscriptions");
5131  myopt.translate_header = true;
5132  myopt.translate_columns = translate_columns;
5133  myopt.n_translate_columns = lengthof(translate_columns);
5134 
5135  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5136 
5137  PQclear(res);
5138  return true;
5139 }
5140 
5141 /*
5142  * printACLColumn
5143  *
5144  * Helper function for consistently formatting ACL (privilege) columns.
5145  * The proper targetlist entry is appended to buf. Note lack of any
5146  * whitespace or comma decoration.
5147  */
5148 static void
5149 printACLColumn(PQExpBuffer buf, const char *colname)
5150 {
5151  if (pset.sversion >= 80100)
5152  appendPQExpBuffer(buf,
5153  "pg_catalog.array_to_string(%s, E'\\n') AS \"%s\"",
5154  colname, gettext_noop("Access privileges"));
5155  else
5156  appendPQExpBuffer(buf,
5157  "pg_catalog.array_to_string(%s, '\\n') AS \"%s\"",
5158  colname, gettext_noop("Access privileges"));
5159 }
signed short int16
Definition: c.h:252
void printTableSetFooter(printTableContent *const content, const char *footer)
Definition: print.c:3082
int PQnfields(const PGresult *res)
Definition: fe-exec.c:2681
static bool listTSParsersVerbose(const char *pattern)
Definition: describe.c:3893
PGconn * db
Definition: settings.h:82
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:234
static bool describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, const char *pnspname, const char *prsname)
Definition: describe.c:4344
char * nullPrint
Definition: print.h:166
PsqlSettings pset
Definition: startup.c:37
void printTableCleanup(printTableContent *const content)
Definition: print.c:3100
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3067
bool listTSConfigs(const char *pattern, bool verbose)
Definition: describe.c:4215
bool objectDescription(const char *pattern, bool showSystem)
Definition: describe.c:1084
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
bool listExtensionContents(const char *pattern)
Definition: describe.c:4777
const char * fmtId(const char *rawid)
Definition: string_utils.c:66
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:128
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:385
printTableOpt topt
Definition: print.h:165
bool listEventTriggers(const char *pattern, bool verbose)
Definition: describe.c:3534
bool listCollations(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:3699
bool listLanguages(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:3297
#define gettext_noop(x)
Definition: c.h:139
FILE * queryFout
Definition: settings.h:84
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
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)
Definition: string_utils.c:817
static bool listTSConfigsVerbose(const char *pattern)
Definition: describe.c:4270
#define lengthof(array)
Definition: c.h:558
unsigned int Oid
Definition: postgres_ext.h:31
bool listDomains(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:3372
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2673
static chr newline(void)
Definition: regc_lex.c:1137
bool listSchemas(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:3778
static bool describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
Definition: describe.c:3958
static void add_role_attribute(PQExpBuffer buf, const char *const str)
Definition: describe.c:3055
bool describeTablespaces(const char *pattern, bool verbose)
Definition: describe.c:203
bool describeSubscriptions(const char *pattern, bool verbose)
Definition: describe.c:5076
volatile bool cancel_pressed
Definition: print.c:47
void printTableAddHeader(printTableContent *const content, char *header, const bool translate, const char align)
Definition: print.c:2969
bool describeAggregates(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:59
unsigned short int expanded
Definition: print.h:99
static PGconn ** conns
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
void printTableAddCell(printTableContent *const content, char *cell, const bool translate, const bool mustfree)
Definition: print.c:3009
bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:3142
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:262
bool describeTableDetails(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:1276
#define DEFACLOBJ_SEQUENCE
int pg_wcswidth(const char *pwcs, size_t len, int encoding)
Definition: mbprint.c:177
static char * buf
Definition: pg_test_fsync.c:65
bool listConversions(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:3458
char * tablespace
Definition: pgbench.c:146
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
bool translate_header
Definition: print.h:169
static bool describeOneTableDetails(const char *schemaname, const char *relationname, const char *oid, bool verbose)
Definition: describe.c:1349
void printTable(const printTableContent *cont, FILE *fout, bool is_pager, FILE *flog)
Definition: print.c:3189
static bool listOneExtensionContents(const char *extname, const char *oid)
Definition: describe.c:4849
bool listForeignTables(const char *pattern, bool verbose)
Definition: describe.c:4642
bool describePublications(const char *pattern)
Definition: describe.c:4950
static int verbose
Definition: pg_basebackup.c:87
#define ngettext(s, p, n)
Definition: c.h:127
#define DEFACLOBJ_TYPE
void psql_error(const char *fmt,...)
Definition: common.c:177
void printTableAddFooter(printTableContent *const content, const char *footer)
Definition: print.c:3057
bool listTSDictionaries(const char *pattern, bool verbose)
Definition: describe.c:4073
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:396
printTableFooter * footer
Definition: print.h:157
FILE * logfile
Definition: settings.h:113
bool listPublications(const char *pattern)
Definition: describe.c:4887
bool listForeignServers(const char *pattern, bool verbose)
Definition: describe.c:4499
void PQclear(PGresult *res)
Definition: fe-exec.c:650
#define free(a)
Definition: header.h:60
bool describeTypes(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:586
#define DEFACLOBJ_RELATION
bool describeAccessMethods(const char *pattern, bool verbose)
Definition: describe.c:136
char * title
Definition: print.h:167
char ** footers
Definition: print.h:168
bool default_footer
Definition: print.h:110
#define NULL
Definition: c.h:226
static void add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline)
Definition: describe.c:2838
#define DEFACLOBJ_FUNCTION
bool listForeignDataWrappers(const char *pattern, bool verbose)
Definition: describe.c:4416
printQueryOpt popt
Definition: settings.h:91
bool listUserMappings(const char *pattern, bool verbose)
Definition: describe.c:4581
int n_translate_columns
Definition: print.h:172
void printTableInit(printTableContent *const content, const printTableOpt *opt, const char *title, const int ncolumns, const int nrows)
Definition: print.c:2932
void printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, bool is_pager, FILE *flog)
Definition: print.c:3286
bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:297
static StringInfoData tmpbuf
Definition: walsender.c:153
bool listExtensions(const char *pattern)
Definition: describe.c:4720
#define atooid(x)
Definition: lo.c:17
bool describeOperators(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:700
bool listCasts(const char *pattern, bool verbose)
Definition: describe.c:3600
int i
char * data
Definition: print.h:135
bool listDefaultACLs(const char *pattern)
Definition: describe.c:1001
bool permissionsList(const char *pattern)
Definition: describe.c:847
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:145
int encoding
Definition: settings.h:83
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3092
const bool * translate_columns
Definition: print.h:170
bool listDbRoleSettings(const char *pattern, const char *pattern2)
Definition: describe.c:3067
bool listTSTemplates(const char *pattern, bool verbose)
Definition: describe.c:4144
bool describeRoles(const char *pattern, bool verbose, bool showSystem)
Definition: describe.c:2896
static void printACLColumn(PQExpBuffer buf, const char *colname)
Definition: describe.c:5149
#define _(x)
Definition: elog.c:84
bool listTSParsers(const char *pattern, bool verbose)
Definition: describe.c:3835
bool listAllDbs(const char *pattern, bool verbose)
Definition: describe.c:777
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:89
char * formatPGVersionNumber(int version_number, bool include_minor, char *buf, size_t buflen)
Definition: string_utils.c:182
PGresult * PSQLexec(const char *query)
Definition: common.c:599