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