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