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