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