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