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