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