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 4002 : describeTableDetails(const char *pattern, bool verbose, bool showSystem)
1489 : {
1490 : PQExpBufferData buf;
1491 : PGresult *res;
1492 : int i;
1493 :
1494 4002 : initPQExpBuffer(&buf);
1495 :
1496 4002 : 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 4002 : if (!showSystem && !pattern)
1504 0 : appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1505 : " AND n.nspname <> 'information_schema'\n");
1506 :
1507 4002 : 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 4002 : appendPQExpBufferStr(&buf, "ORDER BY 2, 3;");
1517 :
1518 4002 : res = PSQLexec(buf.data);
1519 4002 : termPQExpBuffer(&buf);
1520 4002 : if (!res)
1521 0 : return false;
1522 :
1523 4002 : if (PQntuples(res) == 0)
1524 : {
1525 44 : 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 44 : PQclear(res);
1534 44 : return false;
1535 : }
1536 :
1537 8048 : for (i = 0; i < PQntuples(res); i++)
1538 : {
1539 : const char *oid;
1540 : const char *nspname;
1541 : const char *relname;
1542 :
1543 4090 : oid = PQgetvalue(res, i, 0);
1544 4090 : nspname = PQgetvalue(res, i, 1);
1545 4090 : relname = PQgetvalue(res, i, 2);
1546 :
1547 4090 : if (!describeOneTableDetails(nspname, relname, oid, verbose))
1548 : {
1549 0 : PQclear(res);
1550 0 : return false;
1551 : }
1552 4090 : if (cancel_pressed)
1553 : {
1554 0 : PQclear(res);
1555 0 : return false;
1556 : }
1557 : }
1558 :
1559 3958 : PQclear(res);
1560 3958 : 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 4090 : describeOneTableDetails(const char *schemaname,
1572 : const char *relationname,
1573 : const char *oid,
1574 : bool verbose)
1575 : {
1576 4090 : bool retval = false;
1577 : PQExpBufferData buf;
1578 4090 : PGresult *res = NULL;
1579 4090 : printTableOpt myopt = pset.popt.topt;
1580 : printTableContent cont;
1581 4090 : bool printTableInitialized = false;
1582 : int i;
1583 4090 : char *view_def = NULL;
1584 : char *headers[12];
1585 : PQExpBufferData title;
1586 : PQExpBufferData tmpbuf;
1587 : int cols;
1588 4090 : int attname_col = -1, /* column indexes in "res" */
1589 4090 : atttype_col = -1,
1590 4090 : attrdef_col = -1,
1591 4090 : attnotnull_col = -1,
1592 4090 : attcoll_col = -1,
1593 4090 : attidentity_col = -1,
1594 4090 : attgenerated_col = -1,
1595 4090 : isindexkey_col = -1,
1596 4090 : indexdef_col = -1,
1597 4090 : fdwopts_col = -1,
1598 4090 : attstorage_col = -1,
1599 4090 : attcompression_col = -1,
1600 4090 : attstattarget_col = -1,
1601 4090 : 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 4090 : bool show_column_details = false;
1622 :
1623 4090 : myopt.default_footer = false;
1624 : /* This output looks confusing in expanded mode. */
1625 4090 : myopt.expanded = false;
1626 :
1627 4090 : initPQExpBuffer(&buf);
1628 4090 : initPQExpBuffer(&title);
1629 4090 : initPQExpBuffer(&tmpbuf);
1630 :
1631 : /* Get general table info */
1632 4090 : if (pset.sversion >= 120000)
1633 : {
1634 4090 : 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 4090 : res = PSQLexec(buf.data);
1720 4090 : if (!res)
1721 0 : goto error_return;
1722 :
1723 : /* Did we get anything? */
1724 4090 : 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 4090 : tableinfo.checks = atoi(PQgetvalue(res, 0, 0));
1732 4090 : tableinfo.relkind = *(PQgetvalue(res, 0, 1));
1733 4090 : tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
1734 4090 : tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
1735 4090 : tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
1736 4090 : tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
1737 4090 : tableinfo.forcerowsecurity = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
1738 4090 : tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 7), "t") == 0;
1739 4090 : tableinfo.ispartition = strcmp(PQgetvalue(res, 0, 8), "t") == 0;
1740 4090 : tableinfo.reloptions = pg_strdup(PQgetvalue(res, 0, 9));
1741 4090 : tableinfo.tablespace = atooid(PQgetvalue(res, 0, 10));
1742 4090 : tableinfo.reloftype = (strcmp(PQgetvalue(res, 0, 11), "") != 0) ?
1743 4090 : pg_strdup(PQgetvalue(res, 0, 11)) : NULL;
1744 4090 : tableinfo.relpersistence = *(PQgetvalue(res, 0, 12));
1745 8180 : tableinfo.relreplident = (pset.sversion >= 90400) ?
1746 4090 : *(PQgetvalue(res, 0, 13)) : 'd';
1747 4090 : if (pset.sversion >= 120000)
1748 8180 : tableinfo.relam = PQgetisnull(res, 0, 14) ?
1749 4090 : NULL : pg_strdup(PQgetvalue(res, 0, 14));
1750 : else
1751 0 : tableinfo.relam = NULL;
1752 4090 : PQclear(res);
1753 4090 : res = NULL;
1754 :
1755 : /*
1756 : * If it's a sequence, deal with it here separately.
1757 : */
1758 4090 : if (tableinfo.relkind == RELKIND_SEQUENCE)
1759 : {
1760 204 : PGresult *result = NULL;
1761 204 : printQueryOpt myopt = pset.popt;
1762 204 : char *footers[3] = {NULL, NULL, NULL};
1763 :
1764 204 : if (pset.sversion >= 100000)
1765 : {
1766 204 : 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 204 : 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 204 : res = PSQLexec(buf.data);
1813 204 : if (!res)
1814 0 : goto error_return;
1815 :
1816 : /* Get the column that owns this sequence */
1817 204 : 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 204 : 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 204 : if (!result)
1841 0 : goto error_return;
1842 204 : 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 204 : PQclear(result);
1857 :
1858 : /* Print any publications */
1859 204 : if (pset.sversion >= 190000)
1860 : {
1861 204 : printfPQExpBuffer(&buf, "SELECT pubname FROM pg_catalog.pg_publication p"
1862 : "\nWHERE p.puballsequences"
1863 : "\n AND pg_catalog.pg_relation_is_publishable('%s')"
1864 : "\nORDER BY 1",
1865 : oid);
1866 :
1867 204 : result = PSQLexec(buf.data);
1868 204 : if (result)
1869 : {
1870 204 : int nrows = PQntuples(result);
1871 :
1872 204 : if (nrows > 0)
1873 : {
1874 12 : printfPQExpBuffer(&tmpbuf, _("Publications:"));
1875 30 : for (i = 0; i < nrows; i++)
1876 18 : appendPQExpBuffer(&tmpbuf, "\n \"%s\"", PQgetvalue(result, i, 0));
1877 :
1878 : /* Store in the first available footer slot */
1879 12 : if (footers[0] == NULL)
1880 12 : footers[0] = pg_strdup(tmpbuf.data);
1881 : else
1882 0 : footers[1] = pg_strdup(tmpbuf.data);
1883 :
1884 12 : resetPQExpBuffer(&tmpbuf);
1885 : }
1886 :
1887 204 : PQclear(result);
1888 : }
1889 : }
1890 :
1891 204 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
1892 30 : printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
1893 : schemaname, relationname);
1894 : else
1895 174 : printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
1896 : schemaname, relationname);
1897 :
1898 204 : myopt.footers = footers;
1899 204 : myopt.topt.default_footer = false;
1900 204 : myopt.title = title.data;
1901 204 : myopt.translate_header = true;
1902 :
1903 204 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1904 :
1905 204 : free(footers[0]);
1906 204 : free(footers[1]);
1907 :
1908 204 : retval = true;
1909 204 : goto error_return; /* not an error, just return early */
1910 : }
1911 :
1912 : /* Identify whether we should print collation, nullable, default vals */
1913 3886 : if (tableinfo.relkind == RELKIND_RELATION ||
1914 1412 : tableinfo.relkind == RELKIND_VIEW ||
1915 1032 : tableinfo.relkind == RELKIND_MATVIEW ||
1916 972 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1917 774 : tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
1918 696 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
1919 3480 : show_column_details = true;
1920 :
1921 : /*
1922 : * Get per-column info
1923 : *
1924 : * Since the set of query columns we need varies depending on relkind and
1925 : * server version, we compute all the column numbers on-the-fly. Column
1926 : * number variables for columns not fetched are left as -1; this avoids
1927 : * duplicative test logic below.
1928 : */
1929 3886 : cols = 0;
1930 3886 : printfPQExpBuffer(&buf, "SELECT a.attname");
1931 3886 : attname_col = cols++;
1932 3886 : appendPQExpBufferStr(&buf, ",\n pg_catalog.format_type(a.atttypid, a.atttypmod)");
1933 3886 : atttype_col = cols++;
1934 :
1935 3886 : if (show_column_details)
1936 : {
1937 : /* use "pretty" mode for expression to avoid excessive parentheses */
1938 3480 : appendPQExpBufferStr(&buf,
1939 : ",\n (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)"
1940 : "\n FROM pg_catalog.pg_attrdef d"
1941 : "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)"
1942 : ",\n a.attnotnull");
1943 3480 : attrdef_col = cols++;
1944 3480 : attnotnull_col = cols++;
1945 3480 : appendPQExpBufferStr(&buf, ",\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
1946 : " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
1947 3480 : attcoll_col = cols++;
1948 3480 : if (pset.sversion >= 100000)
1949 3480 : appendPQExpBufferStr(&buf, ",\n a.attidentity");
1950 : else
1951 0 : appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attidentity");
1952 3480 : attidentity_col = cols++;
1953 3480 : if (pset.sversion >= 120000)
1954 3480 : appendPQExpBufferStr(&buf, ",\n a.attgenerated");
1955 : else
1956 0 : appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attgenerated");
1957 3480 : attgenerated_col = cols++;
1958 : }
1959 3886 : if (tableinfo.relkind == RELKIND_INDEX ||
1960 3618 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
1961 : {
1962 400 : if (pset.sversion >= 110000)
1963 : {
1964 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",
1965 : oid,
1966 : gettext_noop("yes"),
1967 : gettext_noop("no"));
1968 400 : isindexkey_col = cols++;
1969 : }
1970 400 : appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
1971 400 : indexdef_col = cols++;
1972 : }
1973 : /* FDW options for foreign table column */
1974 3886 : if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
1975 : {
1976 198 : appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE "
1977 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM "
1978 : " pg_catalog.pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions");
1979 198 : fdwopts_col = cols++;
1980 : }
1981 3886 : if (verbose)
1982 : {
1983 1744 : appendPQExpBufferStr(&buf, ",\n a.attstorage");
1984 1744 : attstorage_col = cols++;
1985 :
1986 : /* compression info, if relevant to relkind */
1987 1744 : if (pset.sversion >= 140000 &&
1988 1744 : !pset.hide_compression &&
1989 74 : (tableinfo.relkind == RELKIND_RELATION ||
1990 14 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
1991 14 : tableinfo.relkind == RELKIND_MATVIEW))
1992 : {
1993 72 : appendPQExpBufferStr(&buf, ",\n a.attcompression AS attcompression");
1994 72 : attcompression_col = cols++;
1995 : }
1996 :
1997 : /* stats target, if relevant to relkind */
1998 1744 : if (tableinfo.relkind == RELKIND_RELATION ||
1999 744 : tableinfo.relkind == RELKIND_INDEX ||
2000 698 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
2001 692 : tableinfo.relkind == RELKIND_MATVIEW ||
2002 632 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2003 482 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2004 : {
2005 1384 : appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
2006 1384 : attstattarget_col = cols++;
2007 : }
2008 :
2009 : /*
2010 : * In 9.0+, we have column comments for: relations, views, composite
2011 : * types, and foreign tables (cf. CommentObject() in comment.c).
2012 : */
2013 1744 : if (tableinfo.relkind == RELKIND_RELATION ||
2014 744 : tableinfo.relkind == RELKIND_VIEW ||
2015 384 : tableinfo.relkind == RELKIND_MATVIEW ||
2016 324 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2017 174 : tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
2018 174 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2019 : {
2020 1692 : appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)");
2021 1692 : attdescr_col = cols++;
2022 : }
2023 : }
2024 :
2025 3886 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
2026 3886 : appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
2027 3886 : appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
2028 :
2029 3886 : res = PSQLexec(buf.data);
2030 3886 : if (!res)
2031 0 : goto error_return;
2032 3886 : numrows = PQntuples(res);
2033 :
2034 : /* Make title */
2035 3886 : switch (tableinfo.relkind)
2036 : {
2037 2474 : case RELKIND_RELATION:
2038 2474 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2039 18 : printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
2040 : schemaname, relationname);
2041 : else
2042 2456 : printfPQExpBuffer(&title, _("Table \"%s.%s\""),
2043 : schemaname, relationname);
2044 2474 : break;
2045 380 : case RELKIND_VIEW:
2046 380 : printfPQExpBuffer(&title, _("View \"%s.%s\""),
2047 : schemaname, relationname);
2048 380 : break;
2049 60 : case RELKIND_MATVIEW:
2050 60 : printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""),
2051 : schemaname, relationname);
2052 60 : break;
2053 268 : case RELKIND_INDEX:
2054 268 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2055 0 : printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
2056 : schemaname, relationname);
2057 : else
2058 268 : printfPQExpBuffer(&title, _("Index \"%s.%s\""),
2059 : schemaname, relationname);
2060 268 : break;
2061 132 : case RELKIND_PARTITIONED_INDEX:
2062 132 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2063 0 : printfPQExpBuffer(&title, _("Unlogged partitioned index \"%s.%s\""),
2064 : schemaname, relationname);
2065 : else
2066 132 : printfPQExpBuffer(&title, _("Partitioned index \"%s.%s\""),
2067 : schemaname, relationname);
2068 132 : break;
2069 6 : case RELKIND_TOASTVALUE:
2070 6 : printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
2071 : schemaname, relationname);
2072 6 : break;
2073 78 : case RELKIND_COMPOSITE_TYPE:
2074 78 : printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
2075 : schemaname, relationname);
2076 78 : break;
2077 198 : case RELKIND_FOREIGN_TABLE:
2078 198 : printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
2079 : schemaname, relationname);
2080 198 : break;
2081 290 : case RELKIND_PARTITIONED_TABLE:
2082 290 : if (tableinfo.relpersistence == RELPERSISTENCE_UNLOGGED)
2083 0 : printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""),
2084 : schemaname, relationname);
2085 : else
2086 290 : printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""),
2087 : schemaname, relationname);
2088 290 : break;
2089 0 : default:
2090 : /* untranslated unknown relkind */
2091 0 : printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
2092 0 : tableinfo.relkind, schemaname, relationname);
2093 0 : break;
2094 : }
2095 :
2096 : /* Fill headers[] with the names of the columns we will output */
2097 3886 : cols = 0;
2098 3886 : headers[cols++] = gettext_noop("Column");
2099 3886 : headers[cols++] = gettext_noop("Type");
2100 3886 : if (show_column_details)
2101 : {
2102 3480 : headers[cols++] = gettext_noop("Collation");
2103 3480 : headers[cols++] = gettext_noop("Nullable");
2104 3480 : headers[cols++] = gettext_noop("Default");
2105 : }
2106 3886 : if (isindexkey_col >= 0)
2107 400 : headers[cols++] = gettext_noop("Key?");
2108 3886 : if (indexdef_col >= 0)
2109 400 : headers[cols++] = gettext_noop("Definition");
2110 3886 : if (fdwopts_col >= 0)
2111 198 : headers[cols++] = gettext_noop("FDW options");
2112 3886 : if (attstorage_col >= 0)
2113 1744 : headers[cols++] = gettext_noop("Storage");
2114 3886 : if (attcompression_col >= 0)
2115 72 : headers[cols++] = gettext_noop("Compression");
2116 3886 : if (attstattarget_col >= 0)
2117 1384 : headers[cols++] = gettext_noop("Stats target");
2118 3886 : if (attdescr_col >= 0)
2119 1692 : headers[cols++] = gettext_noop("Description");
2120 :
2121 : Assert(cols <= lengthof(headers));
2122 :
2123 3886 : printTableInit(&cont, &myopt, title.data, cols, numrows);
2124 3886 : printTableInitialized = true;
2125 :
2126 27988 : for (i = 0; i < cols; i++)
2127 24102 : printTableAddHeader(&cont, headers[i], true, 'l');
2128 :
2129 : /* Generate table cells to be printed */
2130 13238 : for (i = 0; i < numrows; i++)
2131 : {
2132 : /* Column */
2133 9352 : printTableAddCell(&cont, PQgetvalue(res, i, attname_col), false, false);
2134 :
2135 : /* Type */
2136 9352 : printTableAddCell(&cont, PQgetvalue(res, i, atttype_col), false, false);
2137 :
2138 : /* Collation, Nullable, Default */
2139 9352 : if (show_column_details)
2140 : {
2141 : char *identity;
2142 : char *generated;
2143 : char *default_str;
2144 8872 : bool mustfree = false;
2145 :
2146 8872 : printTableAddCell(&cont, PQgetvalue(res, i, attcoll_col), false, false);
2147 :
2148 8872 : printTableAddCell(&cont,
2149 8872 : strcmp(PQgetvalue(res, i, attnotnull_col), "t") == 0 ? "not null" : "",
2150 : false, false);
2151 :
2152 8872 : identity = PQgetvalue(res, i, attidentity_col);
2153 8872 : generated = PQgetvalue(res, i, attgenerated_col);
2154 :
2155 8872 : if (identity[0] == ATTRIBUTE_IDENTITY_ALWAYS)
2156 66 : default_str = "generated always as identity";
2157 8806 : else if (identity[0] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
2158 24 : default_str = "generated by default as identity";
2159 8782 : else if (generated[0] == ATTRIBUTE_GENERATED_STORED)
2160 : {
2161 232 : default_str = psprintf("generated always as (%s) stored",
2162 : PQgetvalue(res, i, attrdef_col));
2163 232 : mustfree = true;
2164 : }
2165 8550 : else if (generated[0] == ATTRIBUTE_GENERATED_VIRTUAL)
2166 : {
2167 192 : default_str = psprintf("generated always as (%s)",
2168 : PQgetvalue(res, i, attrdef_col));
2169 192 : mustfree = true;
2170 : }
2171 : else
2172 8358 : default_str = PQgetvalue(res, i, attrdef_col);
2173 :
2174 8872 : printTableAddCell(&cont, default_str, false, mustfree);
2175 : }
2176 :
2177 : /* Info for index columns */
2178 9352 : if (isindexkey_col >= 0)
2179 462 : printTableAddCell(&cont, PQgetvalue(res, i, isindexkey_col), true, false);
2180 9352 : if (indexdef_col >= 0)
2181 462 : printTableAddCell(&cont, PQgetvalue(res, i, indexdef_col), false, false);
2182 :
2183 : /* FDW options for foreign table columns */
2184 9352 : if (fdwopts_col >= 0)
2185 750 : printTableAddCell(&cont, PQgetvalue(res, i, fdwopts_col), false, false);
2186 :
2187 : /* Storage mode, if relevant */
2188 9352 : if (attstorage_col >= 0)
2189 : {
2190 4228 : char *storage = PQgetvalue(res, i, attstorage_col);
2191 :
2192 : /* these strings are literal in our syntax, so not translated. */
2193 5470 : printTableAddCell(&cont, (storage[0] == TYPSTORAGE_PLAIN ? "plain" :
2194 2340 : (storage[0] == TYPSTORAGE_MAIN ? "main" :
2195 1140 : (storage[0] == TYPSTORAGE_EXTENDED ? "extended" :
2196 42 : (storage[0] == TYPSTORAGE_EXTERNAL ? "external" :
2197 : "???")))),
2198 : false, false);
2199 : }
2200 :
2201 : /* Column compression, if relevant */
2202 9352 : if (attcompression_col >= 0)
2203 : {
2204 72 : char *compression = PQgetvalue(res, i, attcompression_col);
2205 :
2206 : /* these strings are literal in our syntax, so not translated. */
2207 126 : printTableAddCell(&cont, (compression[0] == 'p' ? "pglz" :
2208 96 : (compression[0] == 'l' ? "lz4" :
2209 42 : (compression[0] == '\0' ? "" :
2210 : "???"))),
2211 : false, false);
2212 : }
2213 :
2214 : /* Statistics target, if the relkind supports this feature */
2215 9352 : if (attstattarget_col >= 0)
2216 3272 : printTableAddCell(&cont, PQgetvalue(res, i, attstattarget_col),
2217 : false, false);
2218 :
2219 : /* Column comments, if the relkind supports this feature */
2220 9352 : if (attdescr_col >= 0)
2221 4144 : printTableAddCell(&cont, PQgetvalue(res, i, attdescr_col),
2222 : false, false);
2223 : }
2224 :
2225 : /* Make footers */
2226 :
2227 3886 : if (tableinfo.ispartition)
2228 : {
2229 : /* Footer information for a partition child table */
2230 : PGresult *result;
2231 :
2232 542 : printfPQExpBuffer(&buf,
2233 : "SELECT inhparent::pg_catalog.regclass,\n"
2234 : " pg_catalog.pg_get_expr(c.relpartbound, c.oid),\n ");
2235 :
2236 542 : appendPQExpBufferStr(&buf,
2237 542 : pset.sversion >= 140000 ? "inhdetachpending" :
2238 : "false as inhdetachpending");
2239 :
2240 : /* If verbose, also request the partition constraint definition */
2241 542 : if (verbose)
2242 182 : appendPQExpBufferStr(&buf,
2243 : ",\n pg_catalog.pg_get_partition_constraintdef(c.oid)");
2244 542 : appendPQExpBuffer(&buf,
2245 : "\nFROM pg_catalog.pg_class c"
2246 : " JOIN pg_catalog.pg_inherits i"
2247 : " ON c.oid = inhrelid"
2248 : "\nWHERE c.oid = '%s';", oid);
2249 542 : result = PSQLexec(buf.data);
2250 542 : if (!result)
2251 0 : goto error_return;
2252 :
2253 542 : if (PQntuples(result) > 0)
2254 : {
2255 542 : char *parent_name = PQgetvalue(result, 0, 0);
2256 542 : char *partdef = PQgetvalue(result, 0, 1);
2257 542 : char *detached = PQgetvalue(result, 0, 2);
2258 :
2259 542 : printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s%s"), parent_name,
2260 : partdef,
2261 542 : strcmp(detached, "t") == 0 ? " DETACH PENDING" : "");
2262 542 : printTableAddFooter(&cont, tmpbuf.data);
2263 :
2264 542 : if (verbose)
2265 : {
2266 182 : char *partconstraintdef = NULL;
2267 :
2268 182 : if (!PQgetisnull(result, 0, 3))
2269 164 : partconstraintdef = PQgetvalue(result, 0, 3);
2270 : /* If there isn't any constraint, show that explicitly */
2271 182 : if (partconstraintdef == NULL || partconstraintdef[0] == '\0')
2272 18 : printfPQExpBuffer(&tmpbuf, _("No partition constraint"));
2273 : else
2274 164 : printfPQExpBuffer(&tmpbuf, _("Partition constraint: %s"),
2275 : partconstraintdef);
2276 182 : printTableAddFooter(&cont, tmpbuf.data);
2277 : }
2278 : }
2279 542 : PQclear(result);
2280 : }
2281 :
2282 3886 : if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2283 : {
2284 : /* Footer information for a partitioned table (partitioning parent) */
2285 : PGresult *result;
2286 :
2287 290 : printfPQExpBuffer(&buf,
2288 : "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);",
2289 : oid);
2290 290 : result = PSQLexec(buf.data);
2291 290 : if (!result)
2292 0 : goto error_return;
2293 :
2294 290 : if (PQntuples(result) == 1)
2295 : {
2296 290 : char *partkeydef = PQgetvalue(result, 0, 0);
2297 :
2298 290 : printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef);
2299 290 : printTableAddFooter(&cont, tmpbuf.data);
2300 : }
2301 290 : PQclear(result);
2302 : }
2303 :
2304 3886 : if (tableinfo.relkind == RELKIND_TOASTVALUE)
2305 : {
2306 : /* For a TOAST table, print name of owning table */
2307 : PGresult *result;
2308 :
2309 6 : printfPQExpBuffer(&buf,
2310 : "SELECT n.nspname, c.relname\n"
2311 : "FROM pg_catalog.pg_class c"
2312 : " JOIN pg_catalog.pg_namespace n"
2313 : " ON n.oid = c.relnamespace\n"
2314 : "WHERE reltoastrelid = '%s';", oid);
2315 6 : result = PSQLexec(buf.data);
2316 6 : if (!result)
2317 0 : goto error_return;
2318 :
2319 6 : if (PQntuples(result) == 1)
2320 : {
2321 6 : char *schemaname = PQgetvalue(result, 0, 0);
2322 6 : char *relname = PQgetvalue(result, 0, 1);
2323 :
2324 6 : printfPQExpBuffer(&tmpbuf, _("Owning table: \"%s.%s\""),
2325 : schemaname, relname);
2326 6 : printTableAddFooter(&cont, tmpbuf.data);
2327 : }
2328 6 : PQclear(result);
2329 : }
2330 :
2331 3886 : if (tableinfo.relkind == RELKIND_INDEX ||
2332 3618 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
2333 400 : {
2334 : /* Footer information about an index */
2335 : PGresult *result;
2336 :
2337 400 : printfPQExpBuffer(&buf,
2338 : "SELECT i.indisunique, i.indisprimary, i.indisclustered, "
2339 : "i.indisvalid,\n"
2340 : " (NOT i.indimmediate) AND "
2341 : "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2342 : "WHERE conrelid = i.indrelid AND "
2343 : "conindid = i.indexrelid AND "
2344 : "contype IN (" CppAsString2(CONSTRAINT_PRIMARY) ","
2345 : CppAsString2(CONSTRAINT_UNIQUE) ","
2346 : CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
2347 : "condeferrable) AS condeferrable,\n"
2348 : " (NOT i.indimmediate) AND "
2349 : "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2350 : "WHERE conrelid = i.indrelid AND "
2351 : "conindid = i.indexrelid AND "
2352 : "contype IN (" CppAsString2(CONSTRAINT_PRIMARY) ","
2353 : CppAsString2(CONSTRAINT_UNIQUE) ","
2354 : CppAsString2(CONSTRAINT_EXCLUSION) ") AND "
2355 : "condeferred) AS condeferred,\n");
2356 :
2357 400 : if (pset.sversion >= 90400)
2358 400 : appendPQExpBufferStr(&buf, "i.indisreplident,\n");
2359 : else
2360 0 : appendPQExpBufferStr(&buf, "false AS indisreplident,\n");
2361 :
2362 400 : if (pset.sversion >= 150000)
2363 400 : appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
2364 : else
2365 0 : appendPQExpBufferStr(&buf, "false AS indnullsnotdistinct,\n");
2366 :
2367 400 : appendPQExpBuffer(&buf, " a.amname, c2.relname, "
2368 : "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
2369 : "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
2370 : "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
2371 : "AND i.indrelid = c2.oid;",
2372 : oid);
2373 :
2374 400 : result = PSQLexec(buf.data);
2375 400 : if (!result)
2376 0 : goto error_return;
2377 400 : else if (PQntuples(result) != 1)
2378 : {
2379 0 : PQclear(result);
2380 0 : goto error_return;
2381 : }
2382 : else
2383 : {
2384 400 : char *indisunique = PQgetvalue(result, 0, 0);
2385 400 : char *indisprimary = PQgetvalue(result, 0, 1);
2386 400 : char *indisclustered = PQgetvalue(result, 0, 2);
2387 400 : char *indisvalid = PQgetvalue(result, 0, 3);
2388 400 : char *deferrable = PQgetvalue(result, 0, 4);
2389 400 : char *deferred = PQgetvalue(result, 0, 5);
2390 400 : char *indisreplident = PQgetvalue(result, 0, 6);
2391 400 : char *indnullsnotdistinct = PQgetvalue(result, 0, 7);
2392 400 : char *indamname = PQgetvalue(result, 0, 8);
2393 400 : char *indtable = PQgetvalue(result, 0, 9);
2394 400 : char *indpred = PQgetvalue(result, 0, 10);
2395 :
2396 400 : if (strcmp(indisprimary, "t") == 0)
2397 96 : printfPQExpBuffer(&tmpbuf, _("primary key, "));
2398 304 : else if (strcmp(indisunique, "t") == 0)
2399 : {
2400 102 : printfPQExpBuffer(&tmpbuf, _("unique"));
2401 102 : if (strcmp(indnullsnotdistinct, "t") == 0)
2402 6 : appendPQExpBufferStr(&tmpbuf, _(" nulls not distinct"));
2403 102 : appendPQExpBufferStr(&tmpbuf, _(", "));
2404 : }
2405 : else
2406 202 : resetPQExpBuffer(&tmpbuf);
2407 400 : appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
2408 :
2409 : /* we assume here that index and table are in same schema */
2410 400 : appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
2411 : schemaname, indtable);
2412 :
2413 400 : if (strlen(indpred))
2414 0 : appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
2415 :
2416 400 : if (strcmp(indisclustered, "t") == 0)
2417 0 : appendPQExpBufferStr(&tmpbuf, _(", clustered"));
2418 :
2419 400 : if (strcmp(indisvalid, "t") != 0)
2420 0 : appendPQExpBufferStr(&tmpbuf, _(", invalid"));
2421 :
2422 400 : if (strcmp(deferrable, "t") == 0)
2423 0 : appendPQExpBufferStr(&tmpbuf, _(", deferrable"));
2424 :
2425 400 : if (strcmp(deferred, "t") == 0)
2426 0 : appendPQExpBufferStr(&tmpbuf, _(", initially deferred"));
2427 :
2428 400 : if (strcmp(indisreplident, "t") == 0)
2429 0 : appendPQExpBufferStr(&tmpbuf, _(", replica identity"));
2430 :
2431 400 : printTableAddFooter(&cont, tmpbuf.data);
2432 :
2433 : /*
2434 : * If it's a partitioned index, we'll print the tablespace below
2435 : */
2436 400 : if (tableinfo.relkind == RELKIND_INDEX)
2437 268 : add_tablespace_footer(&cont, tableinfo.relkind,
2438 : tableinfo.tablespace, true);
2439 : }
2440 :
2441 400 : PQclear(result);
2442 : }
2443 : /* If you add relkinds here, see also "Finish printing..." stanza below */
2444 3486 : else if (tableinfo.relkind == RELKIND_RELATION ||
2445 1012 : tableinfo.relkind == RELKIND_MATVIEW ||
2446 952 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2447 754 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
2448 464 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
2449 464 : tableinfo.relkind == RELKIND_TOASTVALUE)
2450 : {
2451 : /* Footer information about a table */
2452 3028 : PGresult *result = NULL;
2453 3028 : int tuples = 0;
2454 :
2455 : /* print indexes */
2456 3028 : if (tableinfo.hasindex)
2457 : {
2458 976 : printfPQExpBuffer(&buf,
2459 : "SELECT c2.relname, i.indisprimary, i.indisunique, "
2460 : "i.indisclustered, i.indisvalid, "
2461 : "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n "
2462 : "pg_catalog.pg_get_constraintdef(con.oid, true), "
2463 : "contype, condeferrable, condeferred");
2464 976 : if (pset.sversion >= 90400)
2465 976 : appendPQExpBufferStr(&buf, ", i.indisreplident");
2466 : else
2467 0 : appendPQExpBufferStr(&buf, ", false AS indisreplident");
2468 976 : appendPQExpBufferStr(&buf, ", c2.reltablespace");
2469 976 : if (pset.sversion >= 180000)
2470 976 : appendPQExpBufferStr(&buf, ", con.conperiod");
2471 : else
2472 0 : appendPQExpBufferStr(&buf, ", false AS conperiod");
2473 976 : appendPQExpBuffer(&buf,
2474 : "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
2475 : " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ("
2476 : CppAsString2(CONSTRAINT_PRIMARY) ","
2477 : CppAsString2(CONSTRAINT_UNIQUE) ","
2478 : CppAsString2(CONSTRAINT_EXCLUSION) "))\n"
2479 : "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
2480 : "ORDER BY i.indisprimary DESC, c2.relname;",
2481 : oid);
2482 976 : result = PSQLexec(buf.data);
2483 976 : if (!result)
2484 0 : goto error_return;
2485 : else
2486 976 : tuples = PQntuples(result);
2487 :
2488 976 : if (tuples > 0)
2489 : {
2490 934 : printTableAddFooter(&cont, _("Indexes:"));
2491 2428 : for (i = 0; i < tuples; i++)
2492 : {
2493 : /* untranslated index name */
2494 1494 : printfPQExpBuffer(&buf, " \"%s\"",
2495 : PQgetvalue(result, i, 0));
2496 :
2497 : /*
2498 : * If exclusion constraint or PK/UNIQUE constraint WITHOUT
2499 : * OVERLAPS, print the constraintdef
2500 : */
2501 1494 : if (strcmp(PQgetvalue(result, i, 7), "x") == 0 ||
2502 1440 : strcmp(PQgetvalue(result, i, 12), "t") == 0)
2503 : {
2504 148 : appendPQExpBuffer(&buf, " %s",
2505 : PQgetvalue(result, i, 6));
2506 : }
2507 : else
2508 : {
2509 : const char *indexdef;
2510 : const char *usingpos;
2511 :
2512 : /* Label as primary key or unique (but not both) */
2513 1346 : if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
2514 434 : appendPQExpBufferStr(&buf, " PRIMARY KEY,");
2515 912 : else if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
2516 : {
2517 336 : if (strcmp(PQgetvalue(result, i, 7), "u") == 0)
2518 150 : appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,");
2519 : else
2520 186 : appendPQExpBufferStr(&buf, " UNIQUE,");
2521 : }
2522 :
2523 : /* Everything after "USING" is echoed verbatim */
2524 1346 : indexdef = PQgetvalue(result, i, 5);
2525 1346 : usingpos = strstr(indexdef, " USING ");
2526 1346 : if (usingpos)
2527 1346 : indexdef = usingpos + 7;
2528 1346 : appendPQExpBuffer(&buf, " %s", indexdef);
2529 :
2530 : /* Need these for deferrable PK/UNIQUE indexes */
2531 1346 : if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
2532 48 : appendPQExpBufferStr(&buf, " DEFERRABLE");
2533 :
2534 1346 : if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
2535 18 : appendPQExpBufferStr(&buf, " INITIALLY DEFERRED");
2536 : }
2537 :
2538 : /* Add these for all cases */
2539 1494 : if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
2540 0 : appendPQExpBufferStr(&buf, " CLUSTER");
2541 :
2542 1494 : if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
2543 42 : appendPQExpBufferStr(&buf, " INVALID");
2544 :
2545 1494 : if (strcmp(PQgetvalue(result, i, 10), "t") == 0)
2546 60 : appendPQExpBufferStr(&buf, " REPLICA IDENTITY");
2547 :
2548 1494 : printTableAddFooter(&cont, buf.data);
2549 :
2550 : /* Print tablespace of the index on the same line */
2551 1494 : add_tablespace_footer(&cont, RELKIND_INDEX,
2552 1494 : atooid(PQgetvalue(result, i, 11)),
2553 : false);
2554 : }
2555 : }
2556 976 : PQclear(result);
2557 : }
2558 :
2559 : /* print table (and column) check constraints */
2560 3028 : if (tableinfo.checks)
2561 : {
2562 414 : printfPQExpBuffer(&buf,
2563 : "SELECT r.conname, "
2564 : "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
2565 : "FROM pg_catalog.pg_constraint r\n"
2566 : "WHERE r.conrelid = '%s' "
2567 : "AND r.contype = " CppAsString2(CONSTRAINT_CHECK) "\n"
2568 : "ORDER BY 1;",
2569 : oid);
2570 414 : result = PSQLexec(buf.data);
2571 414 : if (!result)
2572 0 : goto error_return;
2573 : else
2574 414 : tuples = PQntuples(result);
2575 :
2576 414 : if (tuples > 0)
2577 : {
2578 414 : printTableAddFooter(&cont, _("Check constraints:"));
2579 1098 : for (i = 0; i < tuples; i++)
2580 : {
2581 : /* untranslated constraint name and def */
2582 684 : printfPQExpBuffer(&buf, " \"%s\" %s",
2583 : PQgetvalue(result, i, 0),
2584 : PQgetvalue(result, i, 1));
2585 :
2586 684 : printTableAddFooter(&cont, buf.data);
2587 : }
2588 : }
2589 414 : PQclear(result);
2590 : }
2591 :
2592 : /* Print foreign-key constraints */
2593 3028 : if (pset.sversion >= 120000 &&
2594 3028 : (tableinfo.ispartition || tableinfo.relkind == RELKIND_PARTITIONED_TABLE))
2595 : {
2596 : /*
2597 : * Put the constraints defined in this table first, followed by
2598 : * the constraints defined in ancestor partitioned tables.
2599 : */
2600 748 : printfPQExpBuffer(&buf,
2601 : "SELECT conrelid = '%s'::pg_catalog.regclass AS sametable,\n"
2602 : " conname,\n"
2603 : " pg_catalog.pg_get_constraintdef(oid, true) AS condef,\n"
2604 : " conrelid::pg_catalog.regclass AS ontable\n"
2605 : " FROM pg_catalog.pg_constraint,\n"
2606 : " pg_catalog.pg_partition_ancestors('%s')\n"
2607 : " WHERE conrelid = relid AND contype = " CppAsString2(CONSTRAINT_FOREIGN) " AND conparentid = 0\n"
2608 : "ORDER BY sametable DESC, conname;",
2609 : oid, oid);
2610 : }
2611 : else
2612 : {
2613 2280 : printfPQExpBuffer(&buf,
2614 : "SELECT true as sametable, conname,\n"
2615 : " pg_catalog.pg_get_constraintdef(r.oid, true) as condef,\n"
2616 : " conrelid::pg_catalog.regclass AS ontable\n"
2617 : "FROM pg_catalog.pg_constraint r\n"
2618 : "WHERE r.conrelid = '%s' AND r.contype = " CppAsString2(CONSTRAINT_FOREIGN) "\n",
2619 : oid);
2620 :
2621 2280 : if (pset.sversion >= 120000)
2622 2280 : appendPQExpBufferStr(&buf, " AND conparentid = 0\n");
2623 2280 : appendPQExpBufferStr(&buf, "ORDER BY conname");
2624 : }
2625 :
2626 3028 : result = PSQLexec(buf.data);
2627 3028 : if (!result)
2628 0 : goto error_return;
2629 : else
2630 3028 : tuples = PQntuples(result);
2631 :
2632 3028 : if (tuples > 0)
2633 : {
2634 194 : int i_sametable = PQfnumber(result, "sametable"),
2635 194 : i_conname = PQfnumber(result, "conname"),
2636 194 : i_condef = PQfnumber(result, "condef"),
2637 194 : i_ontable = PQfnumber(result, "ontable");
2638 :
2639 194 : printTableAddFooter(&cont, _("Foreign-key constraints:"));
2640 424 : for (i = 0; i < tuples; i++)
2641 : {
2642 : /*
2643 : * Print untranslated constraint name and definition. Use a
2644 : * "TABLE tab" prefix when the constraint is defined in a
2645 : * parent partitioned table.
2646 : */
2647 230 : if (strcmp(PQgetvalue(result, i, i_sametable), "f") == 0)
2648 102 : printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2649 : PQgetvalue(result, i, i_ontable),
2650 : PQgetvalue(result, i, i_conname),
2651 : PQgetvalue(result, i, i_condef));
2652 : else
2653 128 : printfPQExpBuffer(&buf, " \"%s\" %s",
2654 : PQgetvalue(result, i, i_conname),
2655 : PQgetvalue(result, i, i_condef));
2656 :
2657 230 : printTableAddFooter(&cont, buf.data);
2658 : }
2659 : }
2660 3028 : PQclear(result);
2661 :
2662 : /* print incoming foreign-key references */
2663 3028 : if (pset.sversion >= 120000)
2664 : {
2665 3028 : printfPQExpBuffer(&buf,
2666 : "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2667 : " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2668 : " FROM pg_catalog.pg_constraint c\n"
2669 : " WHERE confrelid IN (SELECT pg_catalog.pg_partition_ancestors('%s')\n"
2670 : " UNION ALL VALUES ('%s'::pg_catalog.regclass))\n"
2671 : " AND contype = " CppAsString2(CONSTRAINT_FOREIGN) " AND conparentid = 0\n"
2672 : "ORDER BY conname;",
2673 : oid, oid);
2674 : }
2675 : else
2676 : {
2677 0 : printfPQExpBuffer(&buf,
2678 : "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2679 : " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2680 : " FROM pg_catalog.pg_constraint\n"
2681 : " WHERE confrelid = %s AND contype = " CppAsString2(CONSTRAINT_FOREIGN) "\n"
2682 : "ORDER BY conname;",
2683 : oid);
2684 : }
2685 :
2686 3028 : result = PSQLexec(buf.data);
2687 3028 : if (!result)
2688 0 : goto error_return;
2689 : else
2690 3028 : tuples = PQntuples(result);
2691 :
2692 3028 : if (tuples > 0)
2693 : {
2694 60 : int i_conname = PQfnumber(result, "conname"),
2695 60 : i_ontable = PQfnumber(result, "ontable"),
2696 60 : i_condef = PQfnumber(result, "condef");
2697 :
2698 60 : printTableAddFooter(&cont, _("Referenced by:"));
2699 120 : for (i = 0; i < tuples; i++)
2700 : {
2701 60 : printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2702 : PQgetvalue(result, i, i_ontable),
2703 : PQgetvalue(result, i, i_conname),
2704 : PQgetvalue(result, i, i_condef));
2705 :
2706 60 : printTableAddFooter(&cont, buf.data);
2707 : }
2708 : }
2709 3028 : PQclear(result);
2710 :
2711 : /* print any row-level policies */
2712 3028 : if (pset.sversion >= 90500)
2713 : {
2714 3028 : printfPQExpBuffer(&buf, "SELECT pol.polname,");
2715 3028 : if (pset.sversion >= 100000)
2716 3028 : appendPQExpBufferStr(&buf,
2717 : " pol.polpermissive,\n");
2718 : else
2719 0 : appendPQExpBufferStr(&buf,
2720 : " 't' as polpermissive,\n");
2721 3028 : appendPQExpBuffer(&buf,
2722 : " 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"
2723 : " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
2724 : " pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
2725 : " CASE pol.polcmd\n"
2726 : " WHEN 'r' THEN 'SELECT'\n"
2727 : " WHEN 'a' THEN 'INSERT'\n"
2728 : " WHEN 'w' THEN 'UPDATE'\n"
2729 : " WHEN 'd' THEN 'DELETE'\n"
2730 : " END AS cmd\n"
2731 : "FROM pg_catalog.pg_policy pol\n"
2732 : "WHERE pol.polrelid = '%s' ORDER BY 1;",
2733 : oid);
2734 :
2735 3028 : result = PSQLexec(buf.data);
2736 3028 : if (!result)
2737 0 : goto error_return;
2738 : else
2739 3028 : tuples = PQntuples(result);
2740 :
2741 : /*
2742 : * Handle cases where RLS is enabled and there are policies, or
2743 : * there aren't policies, or RLS isn't enabled but there are
2744 : * policies
2745 : */
2746 3028 : if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
2747 12 : printTableAddFooter(&cont, _("Policies:"));
2748 :
2749 3028 : if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
2750 0 : printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
2751 :
2752 3028 : if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
2753 0 : printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
2754 :
2755 3028 : if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
2756 0 : printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
2757 :
2758 3028 : if (!tableinfo.rowsecurity && tuples > 0)
2759 0 : printTableAddFooter(&cont, _("Policies (row security disabled):"));
2760 :
2761 : /* Might be an empty set - that's ok */
2762 3058 : for (i = 0; i < tuples; i++)
2763 : {
2764 30 : printfPQExpBuffer(&buf, " POLICY \"%s\"",
2765 : PQgetvalue(result, i, 0));
2766 :
2767 30 : if (*(PQgetvalue(result, i, 1)) == 'f')
2768 18 : appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
2769 :
2770 30 : if (!PQgetisnull(result, i, 5))
2771 0 : appendPQExpBuffer(&buf, " FOR %s",
2772 : PQgetvalue(result, i, 5));
2773 :
2774 30 : if (!PQgetisnull(result, i, 2))
2775 : {
2776 18 : appendPQExpBuffer(&buf, "\n TO %s",
2777 : PQgetvalue(result, i, 2));
2778 : }
2779 :
2780 30 : if (!PQgetisnull(result, i, 3))
2781 30 : appendPQExpBuffer(&buf, "\n USING (%s)",
2782 : PQgetvalue(result, i, 3));
2783 :
2784 30 : if (!PQgetisnull(result, i, 4))
2785 0 : appendPQExpBuffer(&buf, "\n WITH CHECK (%s)",
2786 : PQgetvalue(result, i, 4));
2787 :
2788 30 : printTableAddFooter(&cont, buf.data);
2789 : }
2790 3028 : PQclear(result);
2791 : }
2792 :
2793 : /* print any extended statistics */
2794 3028 : if (pset.sversion >= 140000)
2795 : {
2796 3028 : printfPQExpBuffer(&buf,
2797 : "SELECT oid, "
2798 : "stxrelid::pg_catalog.regclass, "
2799 : "stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS nsp, "
2800 : "stxname,\n"
2801 : "pg_catalog.pg_get_statisticsobjdef_columns(oid) AS columns,\n"
2802 : " " CppAsString2(STATS_EXT_NDISTINCT) " = any(stxkind) AS ndist_enabled,\n"
2803 : " " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(stxkind) AS deps_enabled,\n"
2804 : " " CppAsString2(STATS_EXT_MCV) " = any(stxkind) AS mcv_enabled,\n"
2805 : "stxstattarget\n"
2806 : "FROM pg_catalog.pg_statistic_ext\n"
2807 : "WHERE stxrelid = '%s'\n"
2808 : "ORDER BY nsp, stxname;",
2809 : oid);
2810 :
2811 3028 : result = PSQLexec(buf.data);
2812 3028 : if (!result)
2813 0 : goto error_return;
2814 : else
2815 3028 : tuples = PQntuples(result);
2816 :
2817 3028 : if (tuples > 0)
2818 : {
2819 54 : printTableAddFooter(&cont, _("Statistics objects:"));
2820 :
2821 126 : for (i = 0; i < tuples; i++)
2822 : {
2823 72 : bool gotone = false;
2824 : bool has_ndistinct;
2825 : bool has_dependencies;
2826 : bool has_mcv;
2827 : bool has_all;
2828 : bool has_some;
2829 :
2830 72 : has_ndistinct = (strcmp(PQgetvalue(result, i, 5), "t") == 0);
2831 72 : has_dependencies = (strcmp(PQgetvalue(result, i, 6), "t") == 0);
2832 72 : has_mcv = (strcmp(PQgetvalue(result, i, 7), "t") == 0);
2833 :
2834 72 : printfPQExpBuffer(&buf, " ");
2835 :
2836 : /* statistics object name (qualified with namespace) */
2837 72 : appendPQExpBuffer(&buf, "\"%s.%s\"",
2838 : PQgetvalue(result, i, 2),
2839 : PQgetvalue(result, i, 3));
2840 :
2841 : /*
2842 : * When printing kinds we ignore expression statistics,
2843 : * which are used only internally and can't be specified
2844 : * by user. We don't print the kinds when none are
2845 : * specified (in which case it has to be statistics on a
2846 : * single expr) or when all are specified (in which case
2847 : * we assume it's expanded by CREATE STATISTICS).
2848 : */
2849 72 : has_all = (has_ndistinct && has_dependencies && has_mcv);
2850 72 : has_some = (has_ndistinct || has_dependencies || has_mcv);
2851 :
2852 72 : if (has_some && !has_all)
2853 : {
2854 0 : appendPQExpBufferStr(&buf, " (");
2855 :
2856 : /* options */
2857 0 : if (has_ndistinct)
2858 : {
2859 0 : appendPQExpBufferStr(&buf, "ndistinct");
2860 0 : gotone = true;
2861 : }
2862 :
2863 0 : if (has_dependencies)
2864 : {
2865 0 : appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2866 0 : gotone = true;
2867 : }
2868 :
2869 0 : if (has_mcv)
2870 : {
2871 0 : appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2872 : }
2873 :
2874 0 : appendPQExpBufferChar(&buf, ')');
2875 : }
2876 :
2877 72 : appendPQExpBuffer(&buf, " ON %s FROM %s",
2878 : PQgetvalue(result, i, 4),
2879 : PQgetvalue(result, i, 1));
2880 :
2881 : /* Show the stats target if it's not default */
2882 72 : if (!PQgetisnull(result, i, 8) &&
2883 6 : strcmp(PQgetvalue(result, i, 8), "-1") != 0)
2884 6 : appendPQExpBuffer(&buf, "; STATISTICS %s",
2885 : PQgetvalue(result, i, 8));
2886 :
2887 72 : printTableAddFooter(&cont, buf.data);
2888 : }
2889 : }
2890 3028 : PQclear(result);
2891 : }
2892 0 : else if (pset.sversion >= 100000)
2893 : {
2894 0 : printfPQExpBuffer(&buf,
2895 : "SELECT oid, "
2896 : "stxrelid::pg_catalog.regclass, "
2897 : "stxnamespace::pg_catalog.regnamespace AS nsp, "
2898 : "stxname,\n"
2899 : " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')\n"
2900 : " FROM pg_catalog.unnest(stxkeys) s(attnum)\n"
2901 : " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND\n"
2902 : " a.attnum = s.attnum AND NOT attisdropped)) AS columns,\n"
2903 : " " CppAsString2(STATS_EXT_NDISTINCT) " = any(stxkind) AS ndist_enabled,\n"
2904 : " " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(stxkind) AS deps_enabled,\n"
2905 : " " CppAsString2(STATS_EXT_MCV) " = any(stxkind) AS mcv_enabled,\n");
2906 :
2907 0 : if (pset.sversion >= 130000)
2908 0 : appendPQExpBufferStr(&buf, " stxstattarget\n");
2909 : else
2910 0 : appendPQExpBufferStr(&buf, " -1 AS stxstattarget\n");
2911 0 : appendPQExpBuffer(&buf, "FROM pg_catalog.pg_statistic_ext\n"
2912 : "WHERE stxrelid = '%s'\n"
2913 : "ORDER BY 1;",
2914 : oid);
2915 :
2916 0 : result = PSQLexec(buf.data);
2917 0 : if (!result)
2918 0 : goto error_return;
2919 : else
2920 0 : tuples = PQntuples(result);
2921 :
2922 0 : if (tuples > 0)
2923 : {
2924 0 : printTableAddFooter(&cont, _("Statistics objects:"));
2925 :
2926 0 : for (i = 0; i < tuples; i++)
2927 : {
2928 0 : bool gotone = false;
2929 :
2930 0 : printfPQExpBuffer(&buf, " ");
2931 :
2932 : /* statistics object name (qualified with namespace) */
2933 0 : appendPQExpBuffer(&buf, "\"%s.%s\" (",
2934 : PQgetvalue(result, i, 2),
2935 : PQgetvalue(result, i, 3));
2936 :
2937 : /* options */
2938 0 : if (strcmp(PQgetvalue(result, i, 5), "t") == 0)
2939 : {
2940 0 : appendPQExpBufferStr(&buf, "ndistinct");
2941 0 : gotone = true;
2942 : }
2943 :
2944 0 : if (strcmp(PQgetvalue(result, i, 6), "t") == 0)
2945 : {
2946 0 : appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2947 0 : gotone = true;
2948 : }
2949 :
2950 0 : if (strcmp(PQgetvalue(result, i, 7), "t") == 0)
2951 : {
2952 0 : appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2953 : }
2954 :
2955 0 : appendPQExpBuffer(&buf, ") ON %s FROM %s",
2956 : PQgetvalue(result, i, 4),
2957 : PQgetvalue(result, i, 1));
2958 :
2959 : /* Show the stats target if it's not default */
2960 0 : if (strcmp(PQgetvalue(result, i, 8), "-1") != 0)
2961 0 : appendPQExpBuffer(&buf, "; STATISTICS %s",
2962 : PQgetvalue(result, i, 8));
2963 :
2964 0 : printTableAddFooter(&cont, buf.data);
2965 : }
2966 : }
2967 0 : PQclear(result);
2968 : }
2969 :
2970 : /* print rules */
2971 3028 : if (tableinfo.hasrules && tableinfo.relkind != RELKIND_MATVIEW)
2972 : {
2973 36 : printfPQExpBuffer(&buf,
2974 : "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
2975 : "ev_enabled\n"
2976 : "FROM pg_catalog.pg_rewrite r\n"
2977 : "WHERE r.ev_class = '%s' ORDER BY 1;",
2978 : oid);
2979 36 : result = PSQLexec(buf.data);
2980 36 : if (!result)
2981 0 : goto error_return;
2982 : else
2983 36 : tuples = PQntuples(result);
2984 :
2985 36 : if (tuples > 0)
2986 : {
2987 : bool have_heading;
2988 : int category;
2989 :
2990 180 : for (category = 0; category < 4; category++)
2991 : {
2992 144 : have_heading = false;
2993 :
2994 528 : for (i = 0; i < tuples; i++)
2995 : {
2996 : const char *ruledef;
2997 384 : bool list_rule = false;
2998 :
2999 384 : switch (category)
3000 : {
3001 96 : case 0:
3002 96 : if (*PQgetvalue(result, i, 2) == 'O')
3003 96 : list_rule = true;
3004 96 : break;
3005 96 : case 1:
3006 96 : if (*PQgetvalue(result, i, 2) == 'D')
3007 0 : list_rule = true;
3008 96 : break;
3009 96 : case 2:
3010 96 : if (*PQgetvalue(result, i, 2) == 'A')
3011 0 : list_rule = true;
3012 96 : break;
3013 96 : case 3:
3014 96 : if (*PQgetvalue(result, i, 2) == 'R')
3015 0 : list_rule = true;
3016 96 : break;
3017 : }
3018 384 : if (!list_rule)
3019 288 : continue;
3020 :
3021 96 : if (!have_heading)
3022 : {
3023 36 : switch (category)
3024 : {
3025 36 : case 0:
3026 36 : printfPQExpBuffer(&buf, _("Rules:"));
3027 36 : break;
3028 0 : case 1:
3029 0 : printfPQExpBuffer(&buf, _("Disabled rules:"));
3030 0 : break;
3031 0 : case 2:
3032 0 : printfPQExpBuffer(&buf, _("Rules firing always:"));
3033 0 : break;
3034 0 : case 3:
3035 0 : printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
3036 0 : break;
3037 : }
3038 36 : printTableAddFooter(&cont, buf.data);
3039 36 : have_heading = true;
3040 : }
3041 :
3042 : /* Everything after "CREATE RULE" is echoed verbatim */
3043 96 : ruledef = PQgetvalue(result, i, 1);
3044 96 : ruledef += 12;
3045 96 : printfPQExpBuffer(&buf, " %s", ruledef);
3046 96 : printTableAddFooter(&cont, buf.data);
3047 : }
3048 : }
3049 : }
3050 36 : PQclear(result);
3051 : }
3052 :
3053 : /* print any publications */
3054 3028 : if (pset.sversion >= 100000)
3055 : {
3056 3028 : if (pset.sversion >= 150000)
3057 : {
3058 3028 : printfPQExpBuffer(&buf,
3059 : "SELECT pubname\n"
3060 : " , NULL\n"
3061 : " , NULL\n"
3062 : "FROM pg_catalog.pg_publication p\n"
3063 : " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
3064 : " JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
3065 : "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
3066 : "UNION\n"
3067 : "SELECT pubname\n"
3068 : " , pg_get_expr(pr.prqual, c.oid)\n"
3069 : " , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
3070 : " (SELECT string_agg(attname, ', ')\n"
3071 : " FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
3072 : " pg_catalog.pg_attribute\n"
3073 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
3074 : " ELSE NULL END) "
3075 : "FROM pg_catalog.pg_publication p\n"
3076 : " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3077 : " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
3078 : "WHERE pr.prrelid = '%s'\n"
3079 : "UNION\n"
3080 : "SELECT pubname\n"
3081 : " , NULL\n"
3082 : " , NULL\n"
3083 : "FROM pg_catalog.pg_publication p\n"
3084 : "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3085 : "ORDER BY 1;",
3086 : oid, oid, oid, oid);
3087 : }
3088 : else
3089 : {
3090 0 : printfPQExpBuffer(&buf,
3091 : "SELECT pubname\n"
3092 : " , NULL\n"
3093 : " , NULL\n"
3094 : "FROM pg_catalog.pg_publication p\n"
3095 : "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
3096 : "WHERE pr.prrelid = '%s'\n"
3097 : "UNION ALL\n"
3098 : "SELECT pubname\n"
3099 : " , NULL\n"
3100 : " , NULL\n"
3101 : "FROM pg_catalog.pg_publication p\n"
3102 : "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
3103 : "ORDER BY 1;",
3104 : oid, oid);
3105 : }
3106 :
3107 3028 : result = PSQLexec(buf.data);
3108 3028 : if (!result)
3109 0 : goto error_return;
3110 : else
3111 3028 : tuples = PQntuples(result);
3112 :
3113 3028 : if (tuples > 0)
3114 72 : printTableAddFooter(&cont, _("Publications:"));
3115 :
3116 : /* Might be an empty set - that's ok */
3117 3136 : for (i = 0; i < tuples; i++)
3118 : {
3119 108 : printfPQExpBuffer(&buf, " \"%s\"",
3120 : PQgetvalue(result, i, 0));
3121 :
3122 : /* column list (if any) */
3123 108 : if (!PQgetisnull(result, i, 2))
3124 24 : appendPQExpBuffer(&buf, " (%s)",
3125 : PQgetvalue(result, i, 2));
3126 :
3127 : /* row filter (if any) */
3128 108 : if (!PQgetisnull(result, i, 1))
3129 24 : appendPQExpBuffer(&buf, " WHERE %s",
3130 : PQgetvalue(result, i, 1));
3131 :
3132 108 : printTableAddFooter(&cont, buf.data);
3133 : }
3134 3028 : PQclear(result);
3135 : }
3136 :
3137 : /*
3138 : * If verbose, print NOT NULL constraints.
3139 : */
3140 3028 : if (verbose)
3141 : {
3142 1332 : printfPQExpBuffer(&buf,
3143 : "SELECT c.conname, a.attname, c.connoinherit,\n"
3144 : " c.conislocal, c.coninhcount <> 0,\n"
3145 : " c.convalidated\n"
3146 : "FROM pg_catalog.pg_constraint c JOIN\n"
3147 : " pg_catalog.pg_attribute a ON\n"
3148 : " (a.attrelid = c.conrelid AND a.attnum = c.conkey[1])\n"
3149 : "WHERE c.contype = " CppAsString2(CONSTRAINT_NOTNULL) " AND\n"
3150 : " c.conrelid = '%s'::pg_catalog.regclass\n"
3151 : "ORDER BY a.attnum",
3152 : oid);
3153 :
3154 1332 : result = PSQLexec(buf.data);
3155 1332 : if (!result)
3156 0 : goto error_return;
3157 : else
3158 1332 : tuples = PQntuples(result);
3159 :
3160 1332 : if (tuples > 0)
3161 800 : printTableAddFooter(&cont, _("Not-null constraints:"));
3162 :
3163 : /* Might be an empty set - that's ok */
3164 2356 : for (i = 0; i < tuples; i++)
3165 : {
3166 1024 : bool islocal = PQgetvalue(result, i, 3)[0] == 't';
3167 1024 : bool inherited = PQgetvalue(result, i, 4)[0] == 't';
3168 1024 : bool validated = PQgetvalue(result, i, 5)[0] == 't';
3169 :
3170 2048 : printfPQExpBuffer(&buf, " \"%s\" NOT NULL \"%s\"%s%s",
3171 : PQgetvalue(result, i, 0),
3172 : PQgetvalue(result, i, 1),
3173 1024 : PQgetvalue(result, i, 2)[0] == 't' ?
3174 : " NO INHERIT" :
3175 1886 : islocal && inherited ? _(" (local, inherited)") :
3176 880 : inherited ? _(" (inherited)") : "",
3177 1024 : !validated ? " NOT VALID" : "");
3178 :
3179 1024 : printTableAddFooter(&cont, buf.data);
3180 : }
3181 1332 : PQclear(result);
3182 : }
3183 : }
3184 :
3185 : /* Get view_def if table is a view or materialized view */
3186 3886 : if ((tableinfo.relkind == RELKIND_VIEW ||
3187 3886 : tableinfo.relkind == RELKIND_MATVIEW) && verbose)
3188 : {
3189 : PGresult *result;
3190 :
3191 420 : printfPQExpBuffer(&buf,
3192 : "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
3193 : oid);
3194 420 : result = PSQLexec(buf.data);
3195 420 : if (!result)
3196 0 : goto error_return;
3197 :
3198 420 : if (PQntuples(result) > 0)
3199 420 : view_def = pg_strdup(PQgetvalue(result, 0, 0));
3200 :
3201 420 : PQclear(result);
3202 : }
3203 :
3204 3886 : if (view_def)
3205 : {
3206 420 : PGresult *result = NULL;
3207 :
3208 : /* Footer information about a view */
3209 420 : printTableAddFooter(&cont, _("View definition:"));
3210 420 : printTableAddFooter(&cont, view_def);
3211 :
3212 : /* print rules */
3213 420 : if (tableinfo.hasrules)
3214 : {
3215 420 : printfPQExpBuffer(&buf,
3216 : "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
3217 : "FROM pg_catalog.pg_rewrite r\n"
3218 : "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
3219 : oid);
3220 420 : result = PSQLexec(buf.data);
3221 420 : if (!result)
3222 0 : goto error_return;
3223 :
3224 420 : if (PQntuples(result) > 0)
3225 : {
3226 12 : printTableAddFooter(&cont, _("Rules:"));
3227 24 : for (i = 0; i < PQntuples(result); i++)
3228 : {
3229 : const char *ruledef;
3230 :
3231 : /* Everything after "CREATE RULE" is echoed verbatim */
3232 12 : ruledef = PQgetvalue(result, i, 1);
3233 12 : ruledef += 12;
3234 :
3235 12 : printfPQExpBuffer(&buf, " %s", ruledef);
3236 12 : printTableAddFooter(&cont, buf.data);
3237 : }
3238 : }
3239 420 : PQclear(result);
3240 : }
3241 : }
3242 :
3243 : /*
3244 : * Print triggers next, if any (but only user-defined triggers). This
3245 : * could apply to either a table or a view.
3246 : */
3247 3886 : if (tableinfo.hastriggers)
3248 : {
3249 : PGresult *result;
3250 : int tuples;
3251 :
3252 296 : printfPQExpBuffer(&buf,
3253 : "SELECT t.tgname, "
3254 : "pg_catalog.pg_get_triggerdef(t.oid, true), "
3255 : "t.tgenabled, t.tgisinternal,\n");
3256 :
3257 : /*
3258 : * Detect whether each trigger is inherited, and if so, get the name
3259 : * of the topmost table it's inherited from. We have no easy way to
3260 : * do that pre-v13, for lack of the tgparentid column. Even with
3261 : * tgparentid, a straightforward search for the topmost parent would
3262 : * require a recursive CTE, which seems unduly expensive. We cheat a
3263 : * bit by assuming parent triggers will match by tgname; then, joining
3264 : * with pg_partition_ancestors() allows the planner to make use of
3265 : * pg_trigger_tgrelid_tgname_index if it wishes. We ensure we find
3266 : * the correct topmost parent by stopping at the first-in-partition-
3267 : * ancestry-order trigger that has tgparentid = 0. (There might be
3268 : * unrelated, non-inherited triggers with the same name further up the
3269 : * stack, so this is important.)
3270 : */
3271 296 : if (pset.sversion >= 130000)
3272 296 : appendPQExpBufferStr(&buf,
3273 : " CASE WHEN t.tgparentid != 0 THEN\n"
3274 : " (SELECT u.tgrelid::pg_catalog.regclass\n"
3275 : " FROM pg_catalog.pg_trigger AS u,\n"
3276 : " pg_catalog.pg_partition_ancestors(t.tgrelid) WITH ORDINALITY AS a(relid, depth)\n"
3277 : " WHERE u.tgname = t.tgname AND u.tgrelid = a.relid\n"
3278 : " AND u.tgparentid = 0\n"
3279 : " ORDER BY a.depth LIMIT 1)\n"
3280 : " END AS parent\n");
3281 : else
3282 0 : appendPQExpBufferStr(&buf, " NULL AS parent\n");
3283 :
3284 296 : appendPQExpBuffer(&buf,
3285 : "FROM pg_catalog.pg_trigger t\n"
3286 : "WHERE t.tgrelid = '%s' AND ",
3287 : oid);
3288 :
3289 : /*
3290 : * tgisinternal is set true for inherited triggers of partitions in
3291 : * servers between v11 and v14, though these must still be shown to
3292 : * the user. So we use another property that is true for such
3293 : * inherited triggers to avoid them being hidden, which is their
3294 : * dependence on another trigger.
3295 : */
3296 296 : if (pset.sversion >= 110000 && pset.sversion < 150000)
3297 0 : appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n"
3298 : " OR EXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n"
3299 : " AND refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass))");
3300 : else
3301 : /* display/warn about disabled internal triggers */
3302 296 : appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))");
3303 296 : appendPQExpBufferStr(&buf, "\nORDER BY 1;");
3304 :
3305 296 : result = PSQLexec(buf.data);
3306 296 : if (!result)
3307 0 : goto error_return;
3308 : else
3309 296 : tuples = PQntuples(result);
3310 :
3311 296 : if (tuples > 0)
3312 : {
3313 : bool have_heading;
3314 : int category;
3315 :
3316 : /*
3317 : * split the output into 4 different categories. Enabled triggers,
3318 : * disabled triggers and the two special ALWAYS and REPLICA
3319 : * configurations.
3320 : */
3321 216 : for (category = 0; category <= 4; category++)
3322 : {
3323 180 : have_heading = false;
3324 810 : for (i = 0; i < tuples; i++)
3325 : {
3326 : bool list_trigger;
3327 : const char *tgdef;
3328 : const char *usingpos;
3329 : const char *tgenabled;
3330 : const char *tgisinternal;
3331 :
3332 : /*
3333 : * Check if this trigger falls into the current category
3334 : */
3335 630 : tgenabled = PQgetvalue(result, i, 2);
3336 630 : tgisinternal = PQgetvalue(result, i, 3);
3337 630 : list_trigger = false;
3338 630 : switch (category)
3339 : {
3340 126 : case 0:
3341 126 : if (*tgenabled == 'O' || *tgenabled == 't')
3342 126 : list_trigger = true;
3343 126 : break;
3344 126 : case 1:
3345 126 : if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3346 0 : *tgisinternal == 'f')
3347 0 : list_trigger = true;
3348 126 : break;
3349 126 : case 2:
3350 126 : if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3351 0 : *tgisinternal == 't')
3352 0 : list_trigger = true;
3353 126 : break;
3354 126 : case 3:
3355 126 : if (*tgenabled == 'A')
3356 0 : list_trigger = true;
3357 126 : break;
3358 126 : case 4:
3359 126 : if (*tgenabled == 'R')
3360 0 : list_trigger = true;
3361 126 : break;
3362 : }
3363 630 : if (list_trigger == false)
3364 504 : continue;
3365 :
3366 : /* Print the category heading once */
3367 126 : if (have_heading == false)
3368 : {
3369 36 : switch (category)
3370 : {
3371 36 : case 0:
3372 36 : printfPQExpBuffer(&buf, _("Triggers:"));
3373 36 : break;
3374 0 : case 1:
3375 0 : printfPQExpBuffer(&buf, _("Disabled user triggers:"));
3376 0 : break;
3377 0 : case 2:
3378 0 : printfPQExpBuffer(&buf, _("Disabled internal triggers:"));
3379 0 : break;
3380 0 : case 3:
3381 0 : printfPQExpBuffer(&buf, _("Triggers firing always:"));
3382 0 : break;
3383 0 : case 4:
3384 0 : printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
3385 0 : break;
3386 : }
3387 36 : printTableAddFooter(&cont, buf.data);
3388 36 : have_heading = true;
3389 : }
3390 :
3391 : /* Everything after "TRIGGER" is echoed verbatim */
3392 126 : tgdef = PQgetvalue(result, i, 1);
3393 126 : usingpos = strstr(tgdef, " TRIGGER ");
3394 126 : if (usingpos)
3395 126 : tgdef = usingpos + 9;
3396 :
3397 126 : printfPQExpBuffer(&buf, " %s", tgdef);
3398 :
3399 : /* Visually distinguish inherited triggers */
3400 126 : if (!PQgetisnull(result, i, 4))
3401 12 : appendPQExpBuffer(&buf, ", ON TABLE %s",
3402 : PQgetvalue(result, i, 4));
3403 :
3404 126 : printTableAddFooter(&cont, buf.data);
3405 : }
3406 : }
3407 : }
3408 296 : PQclear(result);
3409 : }
3410 :
3411 : /*
3412 : * Finish printing the footer information about a table.
3413 : */
3414 3886 : if (tableinfo.relkind == RELKIND_RELATION ||
3415 1412 : tableinfo.relkind == RELKIND_MATVIEW ||
3416 1352 : tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
3417 1154 : tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3418 864 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
3419 732 : tableinfo.relkind == RELKIND_TOASTVALUE)
3420 : {
3421 : bool is_partitioned;
3422 : PGresult *result;
3423 : int tuples;
3424 :
3425 : /* simplify some repeated tests below */
3426 6030 : is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3427 2870 : tableinfo.relkind == RELKIND_PARTITIONED_INDEX);
3428 :
3429 : /* print foreign server name */
3430 3160 : if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
3431 : {
3432 : char *ftoptions;
3433 :
3434 : /* Footer information about foreign table */
3435 198 : printfPQExpBuffer(&buf,
3436 : "SELECT s.srvname,\n"
3437 : " pg_catalog.array_to_string(ARRAY(\n"
3438 : " SELECT pg_catalog.quote_ident(option_name)"
3439 : " || ' ' || pg_catalog.quote_literal(option_value)\n"
3440 : " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ')\n"
3441 : "FROM pg_catalog.pg_foreign_table f,\n"
3442 : " pg_catalog.pg_foreign_server s\n"
3443 : "WHERE f.ftrelid = '%s' AND s.oid = f.ftserver;",
3444 : oid);
3445 198 : result = PSQLexec(buf.data);
3446 198 : if (!result)
3447 0 : goto error_return;
3448 198 : else if (PQntuples(result) != 1)
3449 : {
3450 0 : PQclear(result);
3451 0 : goto error_return;
3452 : }
3453 :
3454 : /* Print server name */
3455 198 : printfPQExpBuffer(&buf, _("Server: %s"),
3456 : PQgetvalue(result, 0, 0));
3457 198 : printTableAddFooter(&cont, buf.data);
3458 :
3459 : /* Print per-table FDW options, if any */
3460 198 : ftoptions = PQgetvalue(result, 0, 1);
3461 198 : if (ftoptions && ftoptions[0] != '\0')
3462 : {
3463 174 : printfPQExpBuffer(&buf, _("FDW options: (%s)"), ftoptions);
3464 174 : printTableAddFooter(&cont, buf.data);
3465 : }
3466 198 : PQclear(result);
3467 : }
3468 :
3469 : /* print tables inherited from (exclude partitioned parents) */
3470 3160 : printfPQExpBuffer(&buf,
3471 : "SELECT c.oid::pg_catalog.regclass\n"
3472 : "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3473 : "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n"
3474 : " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE)
3475 : " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX)
3476 : "\nORDER BY inhseqno;",
3477 : oid);
3478 :
3479 3160 : result = PSQLexec(buf.data);
3480 3160 : if (!result)
3481 0 : goto error_return;
3482 : else
3483 : {
3484 3160 : const char *s = _("Inherits");
3485 3160 : int sw = pg_wcswidth(s, strlen(s), pset.encoding);
3486 :
3487 3160 : tuples = PQntuples(result);
3488 :
3489 3874 : for (i = 0; i < tuples; i++)
3490 : {
3491 714 : if (i == 0)
3492 552 : printfPQExpBuffer(&buf, "%s: %s",
3493 : s, PQgetvalue(result, i, 0));
3494 : else
3495 162 : printfPQExpBuffer(&buf, "%*s %s",
3496 : sw, "", PQgetvalue(result, i, 0));
3497 714 : if (i < tuples - 1)
3498 162 : appendPQExpBufferChar(&buf, ',');
3499 :
3500 714 : printTableAddFooter(&cont, buf.data);
3501 : }
3502 :
3503 3160 : PQclear(result);
3504 : }
3505 :
3506 : /* print child tables (with additional info if partitions) */
3507 3160 : if (pset.sversion >= 140000)
3508 3160 : printfPQExpBuffer(&buf,
3509 : "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3510 : " inhdetachpending,"
3511 : " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3512 : "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3513 : "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3514 : "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3515 : " c.oid::pg_catalog.regclass::pg_catalog.text;",
3516 : oid);
3517 0 : else if (pset.sversion >= 100000)
3518 0 : printfPQExpBuffer(&buf,
3519 : "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3520 : " false AS inhdetachpending,"
3521 : " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3522 : "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3523 : "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3524 : "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3525 : " c.oid::pg_catalog.regclass::pg_catalog.text;",
3526 : oid);
3527 : else
3528 0 : printfPQExpBuffer(&buf,
3529 : "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3530 : " false AS inhdetachpending, NULL\n"
3531 : "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3532 : "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3533 : "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;",
3534 : oid);
3535 :
3536 3160 : result = PSQLexec(buf.data);
3537 3160 : if (!result)
3538 0 : goto error_return;
3539 3160 : tuples = PQntuples(result);
3540 :
3541 : /*
3542 : * For a partitioned table with no partitions, always print the number
3543 : * of partitions as zero, even when verbose output is expected.
3544 : * Otherwise, we will not print "Partitions" section for a partitioned
3545 : * table without any partitions.
3546 : */
3547 3160 : if (is_partitioned && tuples == 0)
3548 : {
3549 66 : printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples);
3550 66 : printTableAddFooter(&cont, buf.data);
3551 : }
3552 3094 : else if (!verbose)
3553 : {
3554 : /* print the number of child tables, if any */
3555 1792 : if (tuples > 0)
3556 : {
3557 366 : if (is_partitioned)
3558 264 : printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
3559 : else
3560 102 : printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
3561 366 : printTableAddFooter(&cont, buf.data);
3562 : }
3563 : }
3564 : else
3565 : {
3566 : /* display the list of child tables */
3567 1302 : const char *ct = is_partitioned ? _("Partitions") : _("Child tables");
3568 1302 : int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding);
3569 :
3570 1816 : for (i = 0; i < tuples; i++)
3571 : {
3572 514 : char child_relkind = *PQgetvalue(result, i, 1);
3573 :
3574 514 : if (i == 0)
3575 314 : printfPQExpBuffer(&buf, "%s: %s",
3576 : ct, PQgetvalue(result, i, 0));
3577 : else
3578 200 : printfPQExpBuffer(&buf, "%*s %s",
3579 : ctw, "", PQgetvalue(result, i, 0));
3580 514 : if (!PQgetisnull(result, i, 3))
3581 208 : appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 3));
3582 514 : if (child_relkind == RELKIND_PARTITIONED_TABLE ||
3583 : child_relkind == RELKIND_PARTITIONED_INDEX)
3584 24 : appendPQExpBufferStr(&buf, ", PARTITIONED");
3585 490 : else if (child_relkind == RELKIND_FOREIGN_TABLE)
3586 108 : appendPQExpBufferStr(&buf, ", FOREIGN");
3587 514 : if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
3588 0 : appendPQExpBufferStr(&buf, " (DETACH PENDING)");
3589 514 : if (i < tuples - 1)
3590 200 : appendPQExpBufferChar(&buf, ',');
3591 :
3592 514 : printTableAddFooter(&cont, buf.data);
3593 : }
3594 : }
3595 3160 : PQclear(result);
3596 :
3597 : /* Table type */
3598 3160 : if (tableinfo.reloftype)
3599 : {
3600 60 : printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
3601 60 : printTableAddFooter(&cont, buf.data);
3602 : }
3603 :
3604 3160 : if (verbose &&
3605 1338 : (tableinfo.relkind == RELKIND_RELATION ||
3606 338 : tableinfo.relkind == RELKIND_MATVIEW) &&
3607 :
3608 : /*
3609 : * No need to display default values; we already display a REPLICA
3610 : * IDENTITY marker on indexes.
3611 : */
3612 1060 : tableinfo.relreplident != REPLICA_IDENTITY_INDEX &&
3613 1054 : ((strcmp(schemaname, "pg_catalog") != 0 &&
3614 1054 : tableinfo.relreplident != REPLICA_IDENTITY_DEFAULT) ||
3615 1048 : (strcmp(schemaname, "pg_catalog") == 0 &&
3616 0 : tableinfo.relreplident != REPLICA_IDENTITY_NOTHING)))
3617 : {
3618 6 : const char *s = _("Replica Identity");
3619 :
3620 6 : printfPQExpBuffer(&buf, "%s: %s",
3621 : s,
3622 6 : tableinfo.relreplident == REPLICA_IDENTITY_FULL ? "FULL" :
3623 0 : tableinfo.relreplident == REPLICA_IDENTITY_DEFAULT ? "NOTHING" :
3624 : "???");
3625 :
3626 6 : printTableAddFooter(&cont, buf.data);
3627 : }
3628 :
3629 : /* OIDs, if verbose and not a materialized view */
3630 3160 : if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids)
3631 0 : printTableAddFooter(&cont, _("Has OIDs: yes"));
3632 :
3633 : /* Tablespace info */
3634 3160 : add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
3635 : true);
3636 :
3637 : /* Access method info */
3638 3160 : if (verbose && tableinfo.relam != NULL && !pset.hide_tableam)
3639 : {
3640 12 : printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam);
3641 12 : printTableAddFooter(&cont, buf.data);
3642 : }
3643 : }
3644 :
3645 : /* reloptions, if verbose */
3646 3886 : if (verbose &&
3647 1744 : tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
3648 : {
3649 34 : const char *t = _("Options");
3650 :
3651 34 : printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions);
3652 34 : printTableAddFooter(&cont, buf.data);
3653 : }
3654 :
3655 3886 : printTable(&cont, pset.queryFout, false, pset.logfile);
3656 :
3657 3886 : retval = true;
3658 :
3659 4090 : error_return:
3660 :
3661 : /* clean up */
3662 4090 : if (printTableInitialized)
3663 3886 : printTableCleanup(&cont);
3664 4090 : termPQExpBuffer(&buf);
3665 4090 : termPQExpBuffer(&title);
3666 4090 : termPQExpBuffer(&tmpbuf);
3667 :
3668 4090 : free(view_def);
3669 :
3670 4090 : PQclear(res);
3671 :
3672 4090 : return retval;
3673 : }
3674 :
3675 : /*
3676 : * Add a tablespace description to a footer. If 'newline' is true, it is added
3677 : * in a new line; otherwise it's appended to the current value of the last
3678 : * footer.
3679 : */
3680 : static void
3681 4922 : add_tablespace_footer(printTableContent *const cont, char relkind,
3682 : Oid tablespace, const bool newline)
3683 : {
3684 : /* relkinds for which we support tablespaces */
3685 4922 : if (relkind == RELKIND_RELATION ||
3686 2388 : relkind == RELKIND_MATVIEW ||
3687 626 : relkind == RELKIND_INDEX ||
3688 336 : relkind == RELKIND_PARTITIONED_TABLE ||
3689 204 : relkind == RELKIND_PARTITIONED_INDEX ||
3690 : relkind == RELKIND_TOASTVALUE)
3691 : {
3692 : /*
3693 : * We ignore the database default tablespace so that users not using
3694 : * tablespaces don't need to know about them.
3695 : */
3696 4724 : if (tablespace != 0)
3697 : {
3698 204 : PGresult *result = NULL;
3699 : PQExpBufferData buf;
3700 :
3701 204 : initPQExpBuffer(&buf);
3702 204 : printfPQExpBuffer(&buf,
3703 : "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3704 : "WHERE oid = '%u';", tablespace);
3705 204 : result = PSQLexec(buf.data);
3706 204 : if (!result)
3707 : {
3708 0 : termPQExpBuffer(&buf);
3709 0 : return;
3710 : }
3711 : /* Should always be the case, but.... */
3712 204 : if (PQntuples(result) > 0)
3713 : {
3714 204 : if (newline)
3715 : {
3716 : /* Add the tablespace as a new footer */
3717 174 : printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3718 : PQgetvalue(result, 0, 0));
3719 174 : printTableAddFooter(cont, buf.data);
3720 : }
3721 : else
3722 : {
3723 : /* Append the tablespace to the latest footer */
3724 30 : printfPQExpBuffer(&buf, "%s", cont->footer->data);
3725 :
3726 : /*-------
3727 : translator: before this string there's an index description like
3728 : '"foo_pkey" PRIMARY KEY, btree (a)' */
3729 30 : appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3730 : PQgetvalue(result, 0, 0));
3731 30 : printTableSetFooter(cont, buf.data);
3732 : }
3733 : }
3734 204 : PQclear(result);
3735 204 : termPQExpBuffer(&buf);
3736 : }
3737 : }
3738 : }
3739 :
3740 : /*
3741 : * \du or \dg
3742 : *
3743 : * Describes roles. Any schema portion of the pattern is ignored.
3744 : */
3745 : bool
3746 30 : describeRoles(const char *pattern, bool verbose, bool showSystem)
3747 : {
3748 : PQExpBufferData buf;
3749 : PGresult *res;
3750 : printTableContent cont;
3751 30 : printTableOpt myopt = pset.popt.topt;
3752 30 : int ncols = 2;
3753 30 : int nrows = 0;
3754 : int i;
3755 : int conns;
3756 30 : const char align = 'l';
3757 : char **attr;
3758 :
3759 30 : myopt.default_footer = false;
3760 :
3761 30 : initPQExpBuffer(&buf);
3762 :
3763 30 : printfPQExpBuffer(&buf,
3764 : "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
3765 : " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
3766 : " r.rolconnlimit, r.rolvaliduntil");
3767 :
3768 30 : if (verbose)
3769 : {
3770 0 : appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
3771 0 : ncols++;
3772 : }
3773 30 : appendPQExpBufferStr(&buf, "\n, r.rolreplication");
3774 :
3775 30 : if (pset.sversion >= 90500)
3776 : {
3777 30 : appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
3778 : }
3779 :
3780 30 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
3781 :
3782 30 : if (!showSystem && !pattern)
3783 0 : appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
3784 :
3785 30 : if (!validateSQLNamePattern(&buf, pattern, false, false,
3786 : NULL, "r.rolname", NULL, NULL,
3787 : NULL, 1))
3788 : {
3789 18 : termPQExpBuffer(&buf);
3790 18 : return false;
3791 : }
3792 :
3793 12 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
3794 :
3795 12 : res = PSQLexec(buf.data);
3796 12 : if (!res)
3797 0 : return false;
3798 :
3799 12 : nrows = PQntuples(res);
3800 12 : attr = pg_malloc0((nrows + 1) * sizeof(*attr));
3801 :
3802 12 : printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
3803 :
3804 12 : printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
3805 12 : printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
3806 :
3807 12 : if (verbose)
3808 0 : printTableAddHeader(&cont, gettext_noop("Description"), true, align);
3809 :
3810 30 : for (i = 0; i < nrows; i++)
3811 : {
3812 18 : printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
3813 :
3814 18 : resetPQExpBuffer(&buf);
3815 18 : if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
3816 0 : add_role_attribute(&buf, _("Superuser"));
3817 :
3818 18 : if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
3819 0 : add_role_attribute(&buf, _("No inheritance"));
3820 :
3821 18 : if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
3822 0 : add_role_attribute(&buf, _("Create role"));
3823 :
3824 18 : if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
3825 0 : add_role_attribute(&buf, _("Create DB"));
3826 :
3827 18 : if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
3828 18 : add_role_attribute(&buf, _("Cannot login"));
3829 :
3830 18 : if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
3831 0 : add_role_attribute(&buf, _("Replication"));
3832 :
3833 18 : if (pset.sversion >= 90500)
3834 18 : if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
3835 0 : add_role_attribute(&buf, _("Bypass RLS"));
3836 :
3837 18 : conns = atoi(PQgetvalue(res, i, 6));
3838 18 : if (conns >= 0)
3839 : {
3840 0 : if (buf.len > 0)
3841 0 : appendPQExpBufferChar(&buf, '\n');
3842 :
3843 0 : if (conns == 0)
3844 0 : appendPQExpBufferStr(&buf, _("No connections"));
3845 : else
3846 0 : appendPQExpBuffer(&buf, ngettext("%d connection",
3847 : "%d connections",
3848 : conns),
3849 : conns);
3850 : }
3851 :
3852 18 : if (strcmp(PQgetvalue(res, i, 7), "") != 0)
3853 : {
3854 0 : if (buf.len > 0)
3855 0 : appendPQExpBufferChar(&buf, '\n');
3856 0 : appendPQExpBufferStr(&buf, _("Password valid until "));
3857 0 : appendPQExpBufferStr(&buf, PQgetvalue(res, i, 7));
3858 : }
3859 :
3860 18 : attr[i] = pg_strdup(buf.data);
3861 :
3862 18 : printTableAddCell(&cont, attr[i], false, false);
3863 :
3864 18 : if (verbose)
3865 0 : printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
3866 : }
3867 12 : termPQExpBuffer(&buf);
3868 :
3869 12 : printTable(&cont, pset.queryFout, false, pset.logfile);
3870 12 : printTableCleanup(&cont);
3871 :
3872 30 : for (i = 0; i < nrows; i++)
3873 18 : free(attr[i]);
3874 12 : free(attr);
3875 :
3876 12 : PQclear(res);
3877 12 : return true;
3878 : }
3879 :
3880 : static void
3881 18 : add_role_attribute(PQExpBuffer buf, const char *const str)
3882 : {
3883 18 : if (buf->len > 0)
3884 0 : appendPQExpBufferStr(buf, ", ");
3885 :
3886 18 : appendPQExpBufferStr(buf, str);
3887 18 : }
3888 :
3889 : /*
3890 : * \drds
3891 : */
3892 : bool
3893 26 : listDbRoleSettings(const char *pattern, const char *pattern2)
3894 : {
3895 : PQExpBufferData buf;
3896 : PGresult *res;
3897 26 : printQueryOpt myopt = pset.popt;
3898 : bool havewhere;
3899 :
3900 26 : initPQExpBuffer(&buf);
3901 :
3902 26 : printfPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
3903 : "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
3904 : "FROM pg_catalog.pg_db_role_setting s\n"
3905 : "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n"
3906 : "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n",
3907 : gettext_noop("Role"),
3908 : gettext_noop("Database"),
3909 : gettext_noop("Settings"));
3910 26 : if (!validateSQLNamePattern(&buf, pattern, false, false,
3911 : NULL, "r.rolname", NULL, NULL, &havewhere, 1))
3912 18 : goto error_return;
3913 8 : if (!validateSQLNamePattern(&buf, pattern2, havewhere, false,
3914 : NULL, "d.datname", NULL, NULL,
3915 : NULL, 1))
3916 0 : goto error_return;
3917 8 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3918 :
3919 8 : res = PSQLexec(buf.data);
3920 8 : termPQExpBuffer(&buf);
3921 8 : if (!res)
3922 0 : return false;
3923 :
3924 : /*
3925 : * Most functions in this file are content to print an empty table when
3926 : * there are no matching objects. We intentionally deviate from that
3927 : * here, but only in !quiet mode, because of the possibility that the user
3928 : * is confused about what the two pattern arguments mean.
3929 : */
3930 8 : if (PQntuples(res) == 0 && !pset.quiet)
3931 : {
3932 2 : if (pattern && pattern2)
3933 0 : pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".",
3934 : pattern, pattern2);
3935 2 : else if (pattern)
3936 0 : pg_log_error("Did not find any settings for role \"%s\".",
3937 : pattern);
3938 : else
3939 2 : pg_log_error("Did not find any settings.");
3940 : }
3941 : else
3942 : {
3943 6 : myopt.title = _("List of settings");
3944 6 : myopt.translate_header = true;
3945 :
3946 6 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3947 : }
3948 :
3949 8 : PQclear(res);
3950 8 : return true;
3951 :
3952 18 : error_return:
3953 18 : termPQExpBuffer(&buf);
3954 18 : return false;
3955 : }
3956 :
3957 : /*
3958 : * \drg
3959 : * Describes role grants.
3960 : */
3961 : bool
3962 6 : describeRoleGrants(const char *pattern, bool showSystem)
3963 : {
3964 : PQExpBufferData buf;
3965 : PGresult *res;
3966 6 : printQueryOpt myopt = pset.popt;
3967 :
3968 6 : initPQExpBuffer(&buf);
3969 6 : printfPQExpBuffer(&buf,
3970 : "SELECT m.rolname AS \"%s\", r.rolname AS \"%s\",\n"
3971 : " pg_catalog.concat_ws(', ',\n",
3972 : gettext_noop("Role name"),
3973 : gettext_noop("Member of"));
3974 :
3975 6 : if (pset.sversion >= 160000)
3976 6 : appendPQExpBufferStr(&buf,
3977 : " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
3978 : " CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n"
3979 : " CASE WHEN pam.set_option THEN 'SET' END\n");
3980 : else
3981 0 : appendPQExpBufferStr(&buf,
3982 : " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
3983 : " CASE WHEN m.rolinherit THEN 'INHERIT' END,\n"
3984 : " 'SET'\n");
3985 :
3986 6 : appendPQExpBuffer(&buf,
3987 : " ) AS \"%s\",\n"
3988 : " g.rolname AS \"%s\"\n",
3989 : gettext_noop("Options"),
3990 : gettext_noop("Grantor"));
3991 :
3992 6 : appendPQExpBufferStr(&buf,
3993 : "FROM pg_catalog.pg_roles m\n"
3994 : " JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n"
3995 : " LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n"
3996 : " LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n");
3997 :
3998 6 : if (!showSystem && !pattern)
3999 0 : appendPQExpBufferStr(&buf, "WHERE m.rolname !~ '^pg_'\n");
4000 :
4001 6 : if (!validateSQLNamePattern(&buf, pattern, false, false,
4002 : NULL, "m.rolname", NULL, NULL,
4003 : NULL, 1))
4004 : {
4005 0 : termPQExpBuffer(&buf);
4006 0 : return false;
4007 : }
4008 :
4009 6 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;\n");
4010 :
4011 6 : res = PSQLexec(buf.data);
4012 6 : termPQExpBuffer(&buf);
4013 6 : if (!res)
4014 0 : return false;
4015 :
4016 6 : myopt.title = _("List of role grants");
4017 6 : myopt.translate_header = true;
4018 :
4019 6 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4020 :
4021 6 : PQclear(res);
4022 6 : return true;
4023 : }
4024 :
4025 :
4026 : /*
4027 : * listTables()
4028 : *
4029 : * handler for \dt, \di, etc.
4030 : *
4031 : * tabtypes is an array of characters, specifying what info is desired:
4032 : * t - tables
4033 : * i - indexes
4034 : * v - views
4035 : * m - materialized views
4036 : * s - sequences
4037 : * E - foreign table (Note: different from 'f', the relkind value)
4038 : * (any order of the above is fine)
4039 : */
4040 : bool
4041 312 : listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
4042 : {
4043 312 : bool showTables = strchr(tabtypes, 't') != NULL;
4044 312 : bool showIndexes = strchr(tabtypes, 'i') != NULL;
4045 312 : bool showViews = strchr(tabtypes, 'v') != NULL;
4046 312 : bool showMatViews = strchr(tabtypes, 'm') != NULL;
4047 312 : bool showSeq = strchr(tabtypes, 's') != NULL;
4048 312 : bool showForeign = strchr(tabtypes, 'E') != NULL;
4049 :
4050 : int ntypes;
4051 : PQExpBufferData buf;
4052 : PGresult *res;
4053 312 : printQueryOpt myopt = pset.popt;
4054 : int cols_so_far;
4055 312 : bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
4056 :
4057 : /* Count the number of explicitly-requested relation types */
4058 312 : ntypes = showTables + showIndexes + showViews + showMatViews +
4059 312 : showSeq + showForeign;
4060 : /* If none, we default to \dtvmsE (but see also command.c) */
4061 312 : if (ntypes == 0)
4062 0 : showTables = showViews = showMatViews = showSeq = showForeign = true;
4063 :
4064 312 : initPQExpBuffer(&buf);
4065 :
4066 312 : printfPQExpBuffer(&buf,
4067 : "SELECT n.nspname as \"%s\",\n"
4068 : " c.relname as \"%s\",\n"
4069 : " CASE c.relkind"
4070 : " WHEN " CppAsString2(RELKIND_RELATION) " THEN '%s'"
4071 : " WHEN " CppAsString2(RELKIND_VIEW) " THEN '%s'"
4072 : " WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
4073 : " WHEN " CppAsString2(RELKIND_INDEX) " THEN '%s'"
4074 : " WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
4075 : " WHEN " CppAsString2(RELKIND_TOASTVALUE) " THEN '%s'"
4076 : " WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
4077 : " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4078 : " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4079 : " END as \"%s\",\n"
4080 : " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4081 : gettext_noop("Schema"),
4082 : gettext_noop("Name"),
4083 : gettext_noop("table"),
4084 : gettext_noop("view"),
4085 : gettext_noop("materialized view"),
4086 : gettext_noop("index"),
4087 : gettext_noop("sequence"),
4088 : gettext_noop("TOAST table"),
4089 : gettext_noop("foreign table"),
4090 : gettext_noop("partitioned table"),
4091 : gettext_noop("partitioned index"),
4092 : gettext_noop("Type"),
4093 : gettext_noop("Owner"));
4094 312 : cols_so_far = 4;
4095 :
4096 312 : if (showIndexes)
4097 : {
4098 42 : appendPQExpBuffer(&buf,
4099 : ",\n c2.relname as \"%s\"",
4100 : gettext_noop("Table"));
4101 42 : cols_so_far++;
4102 : }
4103 :
4104 312 : if (verbose)
4105 : {
4106 : /*
4107 : * Show whether a relation is permanent, temporary, or unlogged.
4108 : */
4109 36 : appendPQExpBuffer(&buf,
4110 : ",\n CASE c.relpersistence "
4111 : "WHEN " CppAsString2(RELPERSISTENCE_PERMANENT) " THEN '%s' "
4112 : "WHEN " CppAsString2(RELPERSISTENCE_TEMP) " THEN '%s' "
4113 : "WHEN " CppAsString2(RELPERSISTENCE_UNLOGGED) " THEN '%s' "
4114 : "END as \"%s\"",
4115 : gettext_noop("permanent"),
4116 : gettext_noop("temporary"),
4117 : gettext_noop("unlogged"),
4118 : gettext_noop("Persistence"));
4119 36 : translate_columns[cols_so_far] = true;
4120 :
4121 : /*
4122 : * We don't bother to count cols_so_far below here, as there's no need
4123 : * to; this might change with future additions to the output columns.
4124 : */
4125 :
4126 : /*
4127 : * Access methods exist for tables, materialized views and indexes.
4128 : * This has been introduced in PostgreSQL 12 for tables.
4129 : */
4130 36 : if (pset.sversion >= 120000 && !pset.hide_tableam &&
4131 12 : (showTables || showMatViews || showIndexes))
4132 18 : appendPQExpBuffer(&buf,
4133 : ",\n am.amname as \"%s\"",
4134 : gettext_noop("Access method"));
4135 :
4136 36 : appendPQExpBuffer(&buf,
4137 : ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\""
4138 : ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4139 : gettext_noop("Size"),
4140 : gettext_noop("Description"));
4141 : }
4142 :
4143 312 : appendPQExpBufferStr(&buf,
4144 : "\nFROM pg_catalog.pg_class c"
4145 : "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4146 :
4147 312 : if (pset.sversion >= 120000 && !pset.hide_tableam &&
4148 12 : (showTables || showMatViews || showIndexes))
4149 18 : appendPQExpBufferStr(&buf,
4150 : "\n LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam");
4151 :
4152 312 : if (showIndexes)
4153 42 : appendPQExpBufferStr(&buf,
4154 : "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4155 : "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4156 :
4157 312 : appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4158 312 : if (showTables)
4159 : {
4160 114 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_RELATION) ","
4161 : CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
4162 : /* with 'S' or a pattern, allow 't' to match TOAST tables too */
4163 114 : if (showSystem || pattern)
4164 90 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_TOASTVALUE) ",");
4165 : }
4166 312 : if (showViews)
4167 66 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_VIEW) ",");
4168 312 : if (showMatViews)
4169 66 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_MATVIEW) ",");
4170 312 : if (showIndexes)
4171 42 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_INDEX) ","
4172 : CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
4173 312 : if (showSeq)
4174 60 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_SEQUENCE) ",");
4175 312 : if (showSystem || pattern)
4176 276 : appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
4177 312 : if (showForeign)
4178 36 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_FOREIGN_TABLE) ",");
4179 :
4180 312 : appendPQExpBufferStr(&buf, "''"); /* dummy */
4181 312 : appendPQExpBufferStr(&buf, ")\n");
4182 :
4183 312 : if (!showSystem && !pattern)
4184 36 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4185 : " AND n.nspname !~ '^pg_toast'\n"
4186 : " AND n.nspname <> 'information_schema'\n");
4187 :
4188 312 : if (!validateSQLNamePattern(&buf, pattern, true, false,
4189 : "n.nspname", "c.relname", NULL,
4190 : "pg_catalog.pg_table_is_visible(c.oid)",
4191 : NULL, 3))
4192 : {
4193 162 : termPQExpBuffer(&buf);
4194 162 : return false;
4195 : }
4196 :
4197 150 : appendPQExpBufferStr(&buf, "ORDER BY 1,2;");
4198 :
4199 150 : res = PSQLexec(buf.data);
4200 150 : termPQExpBuffer(&buf);
4201 150 : if (!res)
4202 0 : return false;
4203 :
4204 : /*
4205 : * Most functions in this file are content to print an empty table when
4206 : * there are no matching objects. We intentionally deviate from that
4207 : * here, but only in !quiet mode, for historical reasons.
4208 : */
4209 150 : if (PQntuples(res) == 0 && !pset.quiet)
4210 : {
4211 0 : if (pattern)
4212 : {
4213 0 : if (ntypes != 1)
4214 0 : pg_log_error("Did not find any relations named \"%s\".",
4215 : pattern);
4216 0 : else if (showTables)
4217 0 : pg_log_error("Did not find any tables named \"%s\".",
4218 : pattern);
4219 0 : else if (showIndexes)
4220 0 : pg_log_error("Did not find any indexes named \"%s\".",
4221 : pattern);
4222 0 : else if (showViews)
4223 0 : pg_log_error("Did not find any views named \"%s\".",
4224 : pattern);
4225 0 : else if (showMatViews)
4226 0 : pg_log_error("Did not find any materialized views named \"%s\".",
4227 : pattern);
4228 0 : else if (showSeq)
4229 0 : pg_log_error("Did not find any sequences named \"%s\".",
4230 : pattern);
4231 0 : else if (showForeign)
4232 0 : pg_log_error("Did not find any foreign tables named \"%s\".",
4233 : pattern);
4234 : else /* should not get here */
4235 0 : pg_log_error_internal("Did not find any ??? named \"%s\".",
4236 : pattern);
4237 : }
4238 : else
4239 : {
4240 0 : if (ntypes != 1)
4241 0 : pg_log_error("Did not find any relations.");
4242 0 : else if (showTables)
4243 0 : pg_log_error("Did not find any tables.");
4244 0 : else if (showIndexes)
4245 0 : pg_log_error("Did not find any indexes.");
4246 0 : else if (showViews)
4247 0 : pg_log_error("Did not find any views.");
4248 0 : else if (showMatViews)
4249 0 : pg_log_error("Did not find any materialized views.");
4250 0 : else if (showSeq)
4251 0 : pg_log_error("Did not find any sequences.");
4252 0 : else if (showForeign)
4253 0 : pg_log_error("Did not find any foreign tables.");
4254 : else /* should not get here */
4255 0 : pg_log_error_internal("Did not find any ??? relations.");
4256 : }
4257 : }
4258 : else
4259 : {
4260 150 : myopt.title =
4261 282 : (ntypes != 1) ? _("List of relations") :
4262 216 : (showTables) ? _("List of tables") :
4263 150 : (showIndexes) ? _("List of indexes") :
4264 108 : (showViews) ? _("List of views") :
4265 60 : (showMatViews) ? _("List of materialized views") :
4266 18 : (showSeq) ? _("List of sequences") :
4267 0 : (showForeign) ? _("List of foreign tables") :
4268 : "List of ???"; /* should not get here */
4269 150 : myopt.translate_header = true;
4270 150 : myopt.translate_columns = translate_columns;
4271 150 : myopt.n_translate_columns = lengthof(translate_columns);
4272 :
4273 150 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4274 : }
4275 :
4276 150 : PQclear(res);
4277 150 : return true;
4278 : }
4279 :
4280 : /*
4281 : * \dP
4282 : * Takes an optional regexp to select particular relations
4283 : *
4284 : * As with \d, you can specify the kinds of relations you want:
4285 : *
4286 : * t for tables
4287 : * i for indexes
4288 : *
4289 : * And there's additional flags:
4290 : *
4291 : * n to list non-leaf partitioned tables
4292 : *
4293 : * and you can mix and match these in any order.
4294 : */
4295 : bool
4296 108 : listPartitionedTables(const char *reltypes, const char *pattern, bool verbose)
4297 : {
4298 108 : bool showTables = strchr(reltypes, 't') != NULL;
4299 108 : bool showIndexes = strchr(reltypes, 'i') != NULL;
4300 108 : bool showNested = strchr(reltypes, 'n') != NULL;
4301 : PQExpBufferData buf;
4302 : PQExpBufferData title;
4303 : PGresult *res;
4304 108 : printQueryOpt myopt = pset.popt;
4305 108 : bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
4306 : const char *tabletitle;
4307 108 : bool mixed_output = false;
4308 :
4309 : /*
4310 : * Note: Declarative table partitioning is only supported as of Pg 10.0.
4311 : */
4312 108 : if (pset.sversion < 100000)
4313 : {
4314 : char sverbuf[32];
4315 :
4316 0 : pg_log_error("The server (version %s) does not support declarative table partitioning.",
4317 : formatPGVersionNumber(pset.sversion, false,
4318 : sverbuf, sizeof(sverbuf)));
4319 0 : return true;
4320 : }
4321 :
4322 : /* If no relation kind was selected, show them all */
4323 108 : if (!showTables && !showIndexes)
4324 72 : showTables = showIndexes = true;
4325 :
4326 108 : if (showIndexes && !showTables)
4327 18 : tabletitle = _("List of partitioned indexes"); /* \dPi */
4328 90 : else if (showTables && !showIndexes)
4329 18 : tabletitle = _("List of partitioned tables"); /* \dPt */
4330 : else
4331 : {
4332 : /* show all kinds */
4333 72 : tabletitle = _("List of partitioned relations");
4334 72 : mixed_output = true;
4335 : }
4336 :
4337 108 : initPQExpBuffer(&buf);
4338 :
4339 108 : printfPQExpBuffer(&buf,
4340 : "SELECT n.nspname as \"%s\",\n"
4341 : " c.relname as \"%s\",\n"
4342 : " pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
4343 : gettext_noop("Schema"),
4344 : gettext_noop("Name"),
4345 : gettext_noop("Owner"));
4346 :
4347 108 : if (mixed_output)
4348 : {
4349 72 : appendPQExpBuffer(&buf,
4350 : ",\n CASE c.relkind"
4351 : " WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
4352 : " WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
4353 : " END as \"%s\"",
4354 : gettext_noop("partitioned table"),
4355 : gettext_noop("partitioned index"),
4356 : gettext_noop("Type"));
4357 :
4358 72 : translate_columns[3] = true;
4359 : }
4360 :
4361 108 : if (showNested || pattern)
4362 90 : appendPQExpBuffer(&buf,
4363 : ",\n inh.inhparent::pg_catalog.regclass as \"%s\"",
4364 : gettext_noop("Parent name"));
4365 :
4366 108 : if (showIndexes)
4367 90 : appendPQExpBuffer(&buf,
4368 : ",\n c2.oid::pg_catalog.regclass as \"%s\"",
4369 : gettext_noop("Table"));
4370 :
4371 108 : if (verbose)
4372 : {
4373 : /*
4374 : * Table access methods were introduced in v12, and can be set on
4375 : * partitioned tables since v17.
4376 : */
4377 0 : appendPQExpBuffer(&buf, ",\n am.amname as \"%s\"",
4378 : gettext_noop("Access method"));
4379 :
4380 0 : if (showNested)
4381 : {
4382 0 : appendPQExpBuffer(&buf,
4383 : ",\n s.dps as \"%s\"",
4384 : gettext_noop("Leaf partition size"));
4385 0 : appendPQExpBuffer(&buf,
4386 : ",\n s.tps as \"%s\"",
4387 : gettext_noop("Total size"));
4388 : }
4389 : else
4390 : /* Sizes of all partitions are considered in this case. */
4391 0 : appendPQExpBuffer(&buf,
4392 : ",\n s.tps as \"%s\"",
4393 : gettext_noop("Total size"));
4394 :
4395 0 : appendPQExpBuffer(&buf,
4396 : ",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
4397 : gettext_noop("Description"));
4398 : }
4399 :
4400 108 : appendPQExpBufferStr(&buf,
4401 : "\nFROM pg_catalog.pg_class c"
4402 : "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
4403 :
4404 108 : if (showIndexes)
4405 90 : appendPQExpBufferStr(&buf,
4406 : "\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
4407 : "\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
4408 :
4409 108 : if (showNested || pattern)
4410 90 : appendPQExpBufferStr(&buf,
4411 : "\n LEFT JOIN pg_catalog.pg_inherits inh ON c.oid = inh.inhrelid");
4412 :
4413 108 : if (verbose)
4414 : {
4415 0 : appendPQExpBufferStr(&buf,
4416 : "\n LEFT JOIN pg_catalog.pg_am am ON c.relam = am.oid");
4417 :
4418 0 : if (pset.sversion < 120000)
4419 : {
4420 0 : appendPQExpBufferStr(&buf,
4421 : ",\n LATERAL (WITH RECURSIVE d\n"
4422 : " AS (SELECT inhrelid AS oid, 1 AS level\n"
4423 : " FROM pg_catalog.pg_inherits\n"
4424 : " WHERE inhparent = c.oid\n"
4425 : " UNION ALL\n"
4426 : " SELECT inhrelid, level + 1\n"
4427 : " FROM pg_catalog.pg_inherits i\n"
4428 : " JOIN d ON i.inhparent = d.oid)\n"
4429 : " SELECT pg_catalog.pg_size_pretty(sum(pg_catalog.pg_table_size("
4430 : "d.oid))) AS tps,\n"
4431 : " pg_catalog.pg_size_pretty(sum("
4432 : "\n CASE WHEN d.level = 1"
4433 : " THEN pg_catalog.pg_table_size(d.oid) ELSE 0 END)) AS dps\n"
4434 : " FROM d) s");
4435 : }
4436 : else
4437 : {
4438 : /* PostgreSQL 12 has pg_partition_tree function */
4439 0 : appendPQExpBufferStr(&buf,
4440 : ",\n LATERAL (SELECT pg_catalog.pg_size_pretty(sum("
4441 : "\n CASE WHEN ppt.isleaf AND ppt.level = 1"
4442 : "\n THEN pg_catalog.pg_table_size(ppt.relid)"
4443 : " ELSE 0 END)) AS dps"
4444 : ",\n pg_catalog.pg_size_pretty(sum("
4445 : "pg_catalog.pg_table_size(ppt.relid))) AS tps"
4446 : "\n FROM pg_catalog.pg_partition_tree(c.oid) ppt) s");
4447 : }
4448 : }
4449 :
4450 108 : appendPQExpBufferStr(&buf, "\nWHERE c.relkind IN (");
4451 108 : if (showTables)
4452 90 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_TABLE) ",");
4453 108 : if (showIndexes)
4454 90 : appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PARTITIONED_INDEX) ",");
4455 108 : appendPQExpBufferStr(&buf, "''"); /* dummy */
4456 108 : appendPQExpBufferStr(&buf, ")\n");
4457 :
4458 108 : appendPQExpBufferStr(&buf, !showNested && !pattern ?
4459 : " AND NOT c.relispartition\n" : "");
4460 :
4461 108 : if (!pattern)
4462 36 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4463 : " AND n.nspname !~ '^pg_toast'\n"
4464 : " AND n.nspname <> 'information_schema'\n");
4465 :
4466 108 : if (!validateSQLNamePattern(&buf, pattern, true, false,
4467 : "n.nspname", "c.relname", NULL,
4468 : "pg_catalog.pg_table_is_visible(c.oid)",
4469 : NULL, 3))
4470 : {
4471 24 : termPQExpBuffer(&buf);
4472 24 : return false;
4473 : }
4474 :
4475 144 : appendPQExpBuffer(&buf, "ORDER BY \"Schema\", %s%s\"Name\";",
4476 : mixed_output ? "\"Type\" DESC, " : "",
4477 60 : showNested || pattern ? "\"Parent name\" NULLS FIRST, " : "");
4478 :
4479 84 : res = PSQLexec(buf.data);
4480 84 : termPQExpBuffer(&buf);
4481 84 : if (!res)
4482 0 : return false;
4483 :
4484 84 : initPQExpBuffer(&title);
4485 84 : appendPQExpBufferStr(&title, tabletitle);
4486 :
4487 84 : myopt.title = title.data;
4488 84 : myopt.translate_header = true;
4489 84 : myopt.translate_columns = translate_columns;
4490 84 : myopt.n_translate_columns = lengthof(translate_columns);
4491 :
4492 84 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4493 :
4494 84 : termPQExpBuffer(&title);
4495 :
4496 84 : PQclear(res);
4497 84 : return true;
4498 : }
4499 :
4500 : /*
4501 : * \dL
4502 : *
4503 : * Describes languages.
4504 : */
4505 : bool
4506 30 : listLanguages(const char *pattern, bool verbose, bool showSystem)
4507 : {
4508 : PQExpBufferData buf;
4509 : PGresult *res;
4510 30 : printQueryOpt myopt = pset.popt;
4511 :
4512 30 : initPQExpBuffer(&buf);
4513 :
4514 30 : printfPQExpBuffer(&buf,
4515 : "SELECT l.lanname AS \"%s\",\n"
4516 : " pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n"
4517 : " l.lanpltrusted AS \"%s\"",
4518 : gettext_noop("Name"),
4519 : gettext_noop("Owner"),
4520 : gettext_noop("Trusted"));
4521 :
4522 30 : if (verbose)
4523 : {
4524 0 : appendPQExpBuffer(&buf,
4525 : ",\n NOT l.lanispl AS \"%s\",\n"
4526 : " l.lanplcallfoid::pg_catalog.regprocedure AS \"%s\",\n"
4527 : " l.lanvalidator::pg_catalog.regprocedure AS \"%s\",\n "
4528 : "l.laninline::pg_catalog.regprocedure AS \"%s\",\n ",
4529 : gettext_noop("Internal language"),
4530 : gettext_noop("Call handler"),
4531 : gettext_noop("Validator"),
4532 : gettext_noop("Inline handler"));
4533 0 : printACLColumn(&buf, "l.lanacl");
4534 : }
4535 :
4536 30 : appendPQExpBuffer(&buf,
4537 : ",\n d.description AS \"%s\""
4538 : "\nFROM pg_catalog.pg_language l\n"
4539 : "LEFT JOIN pg_catalog.pg_description d\n"
4540 : " ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
4541 : " AND d.objsubid = 0\n",
4542 : gettext_noop("Description"));
4543 :
4544 30 : if (pattern)
4545 : {
4546 30 : if (!validateSQLNamePattern(&buf, pattern, false, false,
4547 : NULL, "l.lanname", NULL, NULL,
4548 : NULL, 2))
4549 : {
4550 24 : termPQExpBuffer(&buf);
4551 24 : return false;
4552 : }
4553 : }
4554 :
4555 6 : if (!showSystem && !pattern)
4556 0 : appendPQExpBufferStr(&buf, "WHERE l.lanplcallfoid != 0\n");
4557 :
4558 :
4559 6 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
4560 :
4561 6 : res = PSQLexec(buf.data);
4562 6 : termPQExpBuffer(&buf);
4563 6 : if (!res)
4564 0 : return false;
4565 :
4566 6 : myopt.title = _("List of languages");
4567 6 : myopt.translate_header = true;
4568 :
4569 6 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4570 :
4571 6 : PQclear(res);
4572 6 : return true;
4573 : }
4574 :
4575 :
4576 : /*
4577 : * \dD
4578 : *
4579 : * Describes domains.
4580 : */
4581 : bool
4582 60 : listDomains(const char *pattern, bool verbose, bool showSystem)
4583 : {
4584 : PQExpBufferData buf;
4585 : PGresult *res;
4586 60 : printQueryOpt myopt = pset.popt;
4587 :
4588 60 : initPQExpBuffer(&buf);
4589 :
4590 60 : printfPQExpBuffer(&buf,
4591 : "SELECT n.nspname as \"%s\",\n"
4592 : " t.typname as \"%s\",\n"
4593 : " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
4594 : " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
4595 : " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4596 : " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4597 : " t.typdefault as \"%s\",\n"
4598 : " pg_catalog.array_to_string(ARRAY(\n"
4599 : " 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"
4600 : " ), ' ') as \"%s\"",
4601 : gettext_noop("Schema"),
4602 : gettext_noop("Name"),
4603 : gettext_noop("Type"),
4604 : gettext_noop("Collation"),
4605 : gettext_noop("Nullable"),
4606 : gettext_noop("Default"),
4607 : gettext_noop("Check"));
4608 :
4609 60 : if (verbose)
4610 : {
4611 6 : appendPQExpBufferStr(&buf, ",\n ");
4612 6 : printACLColumn(&buf, "t.typacl");
4613 6 : appendPQExpBuffer(&buf,
4614 : ",\n d.description as \"%s\"",
4615 : gettext_noop("Description"));
4616 : }
4617 :
4618 60 : appendPQExpBufferStr(&buf,
4619 : "\nFROM pg_catalog.pg_type t\n"
4620 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
4621 :
4622 60 : if (verbose)
4623 6 : appendPQExpBufferStr(&buf,
4624 : " LEFT JOIN pg_catalog.pg_description d "
4625 : "ON d.classoid = t.tableoid AND d.objoid = t.oid "
4626 : "AND d.objsubid = 0\n");
4627 :
4628 60 : appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
4629 :
4630 60 : if (!showSystem && !pattern)
4631 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4632 : " AND n.nspname <> 'information_schema'\n");
4633 :
4634 60 : if (!validateSQLNamePattern(&buf, pattern, true, false,
4635 : "n.nspname", "t.typname", NULL,
4636 : "pg_catalog.pg_type_is_visible(t.oid)",
4637 : NULL, 3))
4638 : {
4639 24 : termPQExpBuffer(&buf);
4640 24 : return false;
4641 : }
4642 :
4643 36 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4644 :
4645 36 : res = PSQLexec(buf.data);
4646 36 : termPQExpBuffer(&buf);
4647 36 : if (!res)
4648 0 : return false;
4649 :
4650 36 : myopt.title = _("List of domains");
4651 36 : myopt.translate_header = true;
4652 :
4653 36 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4654 :
4655 36 : PQclear(res);
4656 36 : return true;
4657 : }
4658 :
4659 : /*
4660 : * \dc
4661 : *
4662 : * Describes conversions.
4663 : */
4664 : bool
4665 42 : listConversions(const char *pattern, bool verbose, bool showSystem)
4666 : {
4667 : PQExpBufferData buf;
4668 : PGresult *res;
4669 42 : printQueryOpt myopt = pset.popt;
4670 : static const bool translate_columns[] =
4671 : {false, false, false, false, true, false};
4672 :
4673 42 : initPQExpBuffer(&buf);
4674 :
4675 42 : printfPQExpBuffer(&buf,
4676 : "SELECT n.nspname AS \"%s\",\n"
4677 : " c.conname AS \"%s\",\n"
4678 : " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
4679 : " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
4680 : " CASE WHEN c.condefault THEN '%s'\n"
4681 : " ELSE '%s' END AS \"%s\"",
4682 : gettext_noop("Schema"),
4683 : gettext_noop("Name"),
4684 : gettext_noop("Source"),
4685 : gettext_noop("Destination"),
4686 : gettext_noop("yes"), gettext_noop("no"),
4687 : gettext_noop("Default?"));
4688 :
4689 42 : if (verbose)
4690 0 : appendPQExpBuffer(&buf,
4691 : ",\n d.description AS \"%s\"",
4692 : gettext_noop("Description"));
4693 :
4694 42 : appendPQExpBufferStr(&buf,
4695 : "\nFROM pg_catalog.pg_conversion c\n"
4696 : " JOIN pg_catalog.pg_namespace n "
4697 : "ON n.oid = c.connamespace\n");
4698 :
4699 42 : if (verbose)
4700 0 : appendPQExpBufferStr(&buf,
4701 : "LEFT JOIN pg_catalog.pg_description d "
4702 : "ON d.classoid = c.tableoid\n"
4703 : " AND d.objoid = c.oid "
4704 : "AND d.objsubid = 0\n");
4705 :
4706 42 : appendPQExpBufferStr(&buf, "WHERE true\n");
4707 :
4708 42 : if (!showSystem && !pattern)
4709 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4710 : " AND n.nspname <> 'information_schema'\n");
4711 :
4712 42 : if (!validateSQLNamePattern(&buf, pattern, true, false,
4713 : "n.nspname", "c.conname", NULL,
4714 : "pg_catalog.pg_conversion_is_visible(c.oid)",
4715 : NULL, 3))
4716 : {
4717 24 : termPQExpBuffer(&buf);
4718 24 : return false;
4719 : }
4720 :
4721 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4722 :
4723 18 : res = PSQLexec(buf.data);
4724 18 : termPQExpBuffer(&buf);
4725 18 : if (!res)
4726 0 : return false;
4727 :
4728 18 : myopt.title = _("List of conversions");
4729 18 : myopt.translate_header = true;
4730 18 : myopt.translate_columns = translate_columns;
4731 18 : myopt.n_translate_columns = lengthof(translate_columns);
4732 :
4733 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4734 :
4735 18 : PQclear(res);
4736 18 : return true;
4737 : }
4738 :
4739 : /*
4740 : * \dconfig
4741 : *
4742 : * Describes configuration parameters.
4743 : */
4744 : bool
4745 12 : describeConfigurationParameters(const char *pattern, bool verbose,
4746 : bool showSystem)
4747 : {
4748 : PQExpBufferData buf;
4749 : PGresult *res;
4750 12 : printQueryOpt myopt = pset.popt;
4751 :
4752 12 : initPQExpBuffer(&buf);
4753 12 : printfPQExpBuffer(&buf,
4754 : "SELECT s.name AS \"%s\", "
4755 : "pg_catalog.current_setting(s.name) AS \"%s\"",
4756 : gettext_noop("Parameter"),
4757 : gettext_noop("Value"));
4758 :
4759 12 : if (verbose)
4760 : {
4761 6 : appendPQExpBuffer(&buf,
4762 : ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4763 : gettext_noop("Type"),
4764 : gettext_noop("Context"));
4765 6 : if (pset.sversion >= 150000)
4766 6 : printACLColumn(&buf, "p.paracl");
4767 : else
4768 0 : appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4769 : gettext_noop("Access privileges"));
4770 : }
4771 :
4772 12 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4773 :
4774 12 : if (verbose && pset.sversion >= 150000)
4775 6 : appendPQExpBufferStr(&buf,
4776 : " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4777 : " ON pg_catalog.lower(s.name) = p.parname\n");
4778 :
4779 12 : if (pattern)
4780 12 : processSQLNamePattern(pset.db, &buf, pattern,
4781 : false, false,
4782 : NULL, "pg_catalog.lower(s.name)", NULL,
4783 : NULL, NULL, NULL);
4784 : else
4785 0 : appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
4786 : " s.setting IS DISTINCT FROM s.boot_val\n");
4787 :
4788 12 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
4789 :
4790 12 : res = PSQLexec(buf.data);
4791 12 : termPQExpBuffer(&buf);
4792 12 : if (!res)
4793 0 : return false;
4794 :
4795 12 : if (pattern)
4796 12 : myopt.title = _("List of configuration parameters");
4797 : else
4798 0 : myopt.title = _("List of non-default configuration parameters");
4799 12 : myopt.translate_header = true;
4800 :
4801 12 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4802 :
4803 12 : PQclear(res);
4804 12 : return true;
4805 : }
4806 :
4807 : /*
4808 : * \dy
4809 : *
4810 : * Describes Event Triggers.
4811 : */
4812 : bool
4813 24 : listEventTriggers(const char *pattern, bool verbose)
4814 : {
4815 : PQExpBufferData buf;
4816 : PGresult *res;
4817 24 : printQueryOpt myopt = pset.popt;
4818 : static const bool translate_columns[] =
4819 : {false, false, false, true, false, false, false};
4820 :
4821 24 : if (pset.sversion < 90300)
4822 : {
4823 : char sverbuf[32];
4824 :
4825 0 : pg_log_error("The server (version %s) does not support event triggers.",
4826 : formatPGVersionNumber(pset.sversion, false,
4827 : sverbuf, sizeof(sverbuf)));
4828 0 : return true;
4829 : }
4830 :
4831 24 : initPQExpBuffer(&buf);
4832 :
4833 24 : printfPQExpBuffer(&buf,
4834 : "SELECT evtname as \"%s\", "
4835 : "evtevent as \"%s\", "
4836 : "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
4837 : " case evtenabled when 'O' then '%s'"
4838 : " when 'R' then '%s'"
4839 : " when 'A' then '%s'"
4840 : " when 'D' then '%s' end as \"%s\",\n"
4841 : " e.evtfoid::pg_catalog.regproc as \"%s\", "
4842 : "pg_catalog.array_to_string(array(select x"
4843 : " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
4844 : gettext_noop("Name"),
4845 : gettext_noop("Event"),
4846 : gettext_noop("Owner"),
4847 : gettext_noop("enabled"),
4848 : gettext_noop("replica"),
4849 : gettext_noop("always"),
4850 : gettext_noop("disabled"),
4851 : gettext_noop("Enabled"),
4852 : gettext_noop("Function"),
4853 : gettext_noop("Tags"));
4854 24 : if (verbose)
4855 0 : appendPQExpBuffer(&buf,
4856 : ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
4857 : gettext_noop("Description"));
4858 24 : appendPQExpBufferStr(&buf,
4859 : "\nFROM pg_catalog.pg_event_trigger e ");
4860 :
4861 24 : if (!validateSQLNamePattern(&buf, pattern, false, false,
4862 : NULL, "evtname", NULL, NULL,
4863 : NULL, 1))
4864 : {
4865 18 : termPQExpBuffer(&buf);
4866 18 : return false;
4867 : }
4868 :
4869 6 : appendPQExpBufferStr(&buf, "ORDER BY 1");
4870 :
4871 6 : res = PSQLexec(buf.data);
4872 6 : termPQExpBuffer(&buf);
4873 6 : if (!res)
4874 0 : return false;
4875 :
4876 6 : myopt.title = _("List of event triggers");
4877 6 : myopt.translate_header = true;
4878 6 : myopt.translate_columns = translate_columns;
4879 6 : myopt.n_translate_columns = lengthof(translate_columns);
4880 :
4881 6 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4882 :
4883 6 : PQclear(res);
4884 6 : return true;
4885 : }
4886 :
4887 : /*
4888 : * \dX
4889 : *
4890 : * Describes extended statistics.
4891 : */
4892 : bool
4893 102 : listExtendedStats(const char *pattern)
4894 : {
4895 : PQExpBufferData buf;
4896 : PGresult *res;
4897 102 : printQueryOpt myopt = pset.popt;
4898 :
4899 102 : if (pset.sversion < 100000)
4900 : {
4901 : char sverbuf[32];
4902 :
4903 0 : pg_log_error("The server (version %s) does not support extended statistics.",
4904 : formatPGVersionNumber(pset.sversion, false,
4905 : sverbuf, sizeof(sverbuf)));
4906 0 : return true;
4907 : }
4908 :
4909 102 : initPQExpBuffer(&buf);
4910 102 : printfPQExpBuffer(&buf,
4911 : "SELECT \n"
4912 : "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS \"%s\", \n"
4913 : "es.stxname AS \"%s\", \n",
4914 : gettext_noop("Schema"),
4915 : gettext_noop("Name"));
4916 :
4917 102 : if (pset.sversion >= 140000)
4918 102 : appendPQExpBuffer(&buf,
4919 : "pg_catalog.format('%%s FROM %%s', \n"
4920 : " pg_catalog.pg_get_statisticsobjdef_columns(es.oid), \n"
4921 : " es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4922 : gettext_noop("Definition"));
4923 : else
4924 0 : appendPQExpBuffer(&buf,
4925 : "pg_catalog.format('%%s FROM %%s', \n"
4926 : " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
4927 : " FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
4928 : " JOIN pg_catalog.pg_attribute a \n"
4929 : " ON (es.stxrelid = a.attrelid \n"
4930 : " AND a.attnum = s.attnum \n"
4931 : " AND NOT a.attisdropped)), \n"
4932 : "es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4933 : gettext_noop("Definition"));
4934 :
4935 102 : appendPQExpBuffer(&buf,
4936 : ",\nCASE WHEN " CppAsString2(STATS_EXT_NDISTINCT) " = any(es.stxkind) THEN 'defined' \n"
4937 : "END AS \"%s\", \n"
4938 : "CASE WHEN " CppAsString2(STATS_EXT_DEPENDENCIES) " = any(es.stxkind) THEN 'defined' \n"
4939 : "END AS \"%s\"",
4940 : gettext_noop("Ndistinct"),
4941 : gettext_noop("Dependencies"));
4942 :
4943 : /*
4944 : * Include the MCV statistics kind.
4945 : */
4946 102 : if (pset.sversion >= 120000)
4947 : {
4948 102 : appendPQExpBuffer(&buf,
4949 : ",\nCASE WHEN " CppAsString2(STATS_EXT_MCV) " = any(es.stxkind) THEN 'defined' \n"
4950 : "END AS \"%s\" ",
4951 : gettext_noop("MCV"));
4952 : }
4953 :
4954 102 : appendPQExpBufferStr(&buf,
4955 : " \nFROM pg_catalog.pg_statistic_ext es \n");
4956 :
4957 102 : if (!validateSQLNamePattern(&buf, pattern,
4958 : false, false,
4959 : "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
4960 : NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)",
4961 : NULL, 3))
4962 : {
4963 24 : termPQExpBuffer(&buf);
4964 24 : return false;
4965 : }
4966 :
4967 78 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4968 :
4969 78 : res = PSQLexec(buf.data);
4970 78 : termPQExpBuffer(&buf);
4971 78 : if (!res)
4972 0 : return false;
4973 :
4974 78 : myopt.title = _("List of extended statistics");
4975 78 : myopt.translate_header = true;
4976 :
4977 78 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4978 :
4979 78 : PQclear(res);
4980 78 : return true;
4981 : }
4982 :
4983 : /*
4984 : * \dC
4985 : *
4986 : * Describes casts.
4987 : */
4988 : bool
4989 42 : listCasts(const char *pattern, bool verbose)
4990 : {
4991 : PQExpBufferData buf;
4992 : PGresult *res;
4993 42 : printQueryOpt myopt = pset.popt;
4994 : static const bool translate_columns[] = {false, false, false, true, true, false};
4995 :
4996 42 : initPQExpBuffer(&buf);
4997 :
4998 42 : printfPQExpBuffer(&buf,
4999 : "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
5000 : " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n",
5001 : gettext_noop("Source type"),
5002 : gettext_noop("Target type"));
5003 :
5004 : /*
5005 : * We don't attempt to localize '(binary coercible)' or '(with inout)',
5006 : * because there's too much risk of gettext translating a function name
5007 : * that happens to match some string in the PO database.
5008 : */
5009 42 : appendPQExpBuffer(&buf,
5010 : " CASE WHEN c.castmethod = '%c' THEN '(binary coercible)'\n"
5011 : " WHEN c.castmethod = '%c' THEN '(with inout)'\n"
5012 : " ELSE p.proname\n"
5013 : " END AS \"%s\",\n",
5014 : COERCION_METHOD_BINARY,
5015 : COERCION_METHOD_INOUT,
5016 : gettext_noop("Function"));
5017 :
5018 42 : appendPQExpBuffer(&buf,
5019 : " CASE WHEN c.castcontext = '%c' THEN '%s'\n"
5020 : " WHEN c.castcontext = '%c' THEN '%s'\n"
5021 : " ELSE '%s'\n"
5022 : " END AS \"%s\"",
5023 : COERCION_CODE_EXPLICIT,
5024 : gettext_noop("no"),
5025 : COERCION_CODE_ASSIGNMENT,
5026 : gettext_noop("in assignment"),
5027 : gettext_noop("yes"),
5028 : gettext_noop("Implicit?"));
5029 :
5030 42 : if (verbose)
5031 0 : appendPQExpBuffer(&buf,
5032 : ",\n CASE WHEN p.proleakproof THEN '%s'\n"
5033 : " ELSE '%s'\n"
5034 : " END AS \"%s\",\n"
5035 : " d.description AS \"%s\"",
5036 : gettext_noop("yes"),
5037 : gettext_noop("no"),
5038 : gettext_noop("Leakproof?"),
5039 : gettext_noop("Description"));
5040 :
5041 : /*
5042 : * We need a left join to pg_proc for binary casts; the others are just
5043 : * paranoia.
5044 : */
5045 42 : appendPQExpBufferStr(&buf,
5046 : "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
5047 : " ON c.castfunc = p.oid\n"
5048 : " LEFT JOIN pg_catalog.pg_type ts\n"
5049 : " ON c.castsource = ts.oid\n"
5050 : " LEFT JOIN pg_catalog.pg_namespace ns\n"
5051 : " ON ns.oid = ts.typnamespace\n"
5052 : " LEFT JOIN pg_catalog.pg_type tt\n"
5053 : " ON c.casttarget = tt.oid\n"
5054 : " LEFT JOIN pg_catalog.pg_namespace nt\n"
5055 : " ON nt.oid = tt.typnamespace\n");
5056 :
5057 42 : if (verbose)
5058 0 : appendPQExpBufferStr(&buf,
5059 : " LEFT JOIN pg_catalog.pg_description d\n"
5060 : " ON d.classoid = c.tableoid AND d.objoid = "
5061 : "c.oid AND d.objsubid = 0\n");
5062 :
5063 42 : appendPQExpBufferStr(&buf, "WHERE ( (true");
5064 :
5065 : /*
5066 : * Match name pattern against either internal or external name of either
5067 : * castsource or casttarget
5068 : */
5069 42 : if (!validateSQLNamePattern(&buf, pattern, true, false,
5070 : "ns.nspname", "ts.typname",
5071 : "pg_catalog.format_type(ts.oid, NULL)",
5072 : "pg_catalog.pg_type_is_visible(ts.oid)",
5073 : NULL, 3))
5074 24 : goto error_return;
5075 :
5076 18 : appendPQExpBufferStr(&buf, ") OR (true");
5077 :
5078 18 : if (!validateSQLNamePattern(&buf, pattern, true, false,
5079 : "nt.nspname", "tt.typname",
5080 : "pg_catalog.format_type(tt.oid, NULL)",
5081 : "pg_catalog.pg_type_is_visible(tt.oid)",
5082 : NULL, 3))
5083 0 : goto error_return;
5084 :
5085 18 : appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
5086 :
5087 18 : res = PSQLexec(buf.data);
5088 18 : termPQExpBuffer(&buf);
5089 18 : if (!res)
5090 0 : return false;
5091 :
5092 18 : myopt.title = _("List of casts");
5093 18 : myopt.translate_header = true;
5094 18 : myopt.translate_columns = translate_columns;
5095 18 : myopt.n_translate_columns = lengthof(translate_columns);
5096 :
5097 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5098 :
5099 18 : PQclear(res);
5100 18 : return true;
5101 :
5102 24 : error_return:
5103 24 : termPQExpBuffer(&buf);
5104 24 : return false;
5105 : }
5106 :
5107 : /*
5108 : * \dO
5109 : *
5110 : * Describes collations.
5111 : */
5112 : bool
5113 42 : listCollations(const char *pattern, bool verbose, bool showSystem)
5114 : {
5115 : PQExpBufferData buf;
5116 : PGresult *res;
5117 42 : printQueryOpt myopt = pset.popt;
5118 : static const bool translate_columns[] = {false, false, false, false, false, false, false, true, false};
5119 :
5120 42 : initPQExpBuffer(&buf);
5121 :
5122 42 : printfPQExpBuffer(&buf,
5123 : "SELECT\n"
5124 : " n.nspname AS \"%s\",\n"
5125 : " c.collname AS \"%s\",\n",
5126 : gettext_noop("Schema"),
5127 : gettext_noop("Name"));
5128 :
5129 42 : if (pset.sversion >= 100000)
5130 42 : appendPQExpBuffer(&buf,
5131 : " CASE c.collprovider "
5132 : "WHEN " CppAsString2(COLLPROVIDER_DEFAULT) " THEN 'default' "
5133 : "WHEN " CppAsString2(COLLPROVIDER_BUILTIN) " THEN 'builtin' "
5134 : "WHEN " CppAsString2(COLLPROVIDER_LIBC) " THEN 'libc' "
5135 : "WHEN " CppAsString2(COLLPROVIDER_ICU) " THEN 'icu' "
5136 : "END AS \"%s\",\n",
5137 : gettext_noop("Provider"));
5138 : else
5139 0 : appendPQExpBuffer(&buf,
5140 : " 'libc' AS \"%s\",\n",
5141 : gettext_noop("Provider"));
5142 :
5143 42 : appendPQExpBuffer(&buf,
5144 : " c.collcollate AS \"%s\",\n"
5145 : " c.collctype AS \"%s\",\n",
5146 : gettext_noop("Collate"),
5147 : gettext_noop("Ctype"));
5148 :
5149 42 : if (pset.sversion >= 170000)
5150 42 : appendPQExpBuffer(&buf,
5151 : " c.colllocale AS \"%s\",\n",
5152 : gettext_noop("Locale"));
5153 0 : else if (pset.sversion >= 150000)
5154 0 : appendPQExpBuffer(&buf,
5155 : " c.colliculocale AS \"%s\",\n",
5156 : gettext_noop("Locale"));
5157 : else
5158 0 : appendPQExpBuffer(&buf,
5159 : " c.collcollate AS \"%s\",\n",
5160 : gettext_noop("Locale"));
5161 :
5162 42 : if (pset.sversion >= 160000)
5163 42 : appendPQExpBuffer(&buf,
5164 : " c.collicurules AS \"%s\",\n",
5165 : gettext_noop("ICU Rules"));
5166 : else
5167 0 : appendPQExpBuffer(&buf,
5168 : " NULL AS \"%s\",\n",
5169 : gettext_noop("ICU Rules"));
5170 :
5171 42 : if (pset.sversion >= 120000)
5172 42 : appendPQExpBuffer(&buf,
5173 : " CASE WHEN c.collisdeterministic THEN '%s' ELSE '%s' END AS \"%s\"",
5174 : gettext_noop("yes"), gettext_noop("no"),
5175 : gettext_noop("Deterministic?"));
5176 : else
5177 0 : appendPQExpBuffer(&buf,
5178 : " '%s' AS \"%s\"",
5179 : gettext_noop("yes"),
5180 : gettext_noop("Deterministic?"));
5181 :
5182 42 : if (verbose)
5183 0 : appendPQExpBuffer(&buf,
5184 : ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
5185 : gettext_noop("Description"));
5186 :
5187 42 : appendPQExpBufferStr(&buf,
5188 : "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
5189 : "WHERE n.oid = c.collnamespace\n");
5190 :
5191 42 : if (!showSystem && !pattern)
5192 0 : appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
5193 : " AND n.nspname <> 'information_schema'\n");
5194 :
5195 : /*
5196 : * Hide collations that aren't usable in the current database's encoding.
5197 : * If you think to change this, note that pg_collation_is_visible rejects
5198 : * unusable collations, so you will need to hack name pattern processing
5199 : * somehow to avoid inconsistent behavior.
5200 : */
5201 42 : appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
5202 :
5203 42 : if (!validateSQLNamePattern(&buf, pattern, true, false,
5204 : "n.nspname", "c.collname", NULL,
5205 : "pg_catalog.pg_collation_is_visible(c.oid)",
5206 : NULL, 3))
5207 : {
5208 24 : termPQExpBuffer(&buf);
5209 24 : return false;
5210 : }
5211 :
5212 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5213 :
5214 18 : res = PSQLexec(buf.data);
5215 18 : termPQExpBuffer(&buf);
5216 18 : if (!res)
5217 0 : return false;
5218 :
5219 18 : myopt.title = _("List of collations");
5220 18 : myopt.translate_header = true;
5221 18 : myopt.translate_columns = translate_columns;
5222 18 : myopt.n_translate_columns = lengthof(translate_columns);
5223 :
5224 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5225 :
5226 18 : PQclear(res);
5227 18 : return true;
5228 : }
5229 :
5230 : /*
5231 : * \dn
5232 : *
5233 : * Describes schemas (namespaces)
5234 : */
5235 : bool
5236 24 : listSchemas(const char *pattern, bool verbose, bool showSystem)
5237 : {
5238 : PQExpBufferData buf;
5239 : PGresult *res;
5240 24 : printQueryOpt myopt = pset.popt;
5241 24 : int pub_schema_tuples = 0;
5242 24 : char **footers = NULL;
5243 :
5244 24 : initPQExpBuffer(&buf);
5245 24 : printfPQExpBuffer(&buf,
5246 : "SELECT n.nspname AS \"%s\",\n"
5247 : " pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
5248 : gettext_noop("Name"),
5249 : gettext_noop("Owner"));
5250 :
5251 24 : if (verbose)
5252 : {
5253 0 : appendPQExpBufferStr(&buf, ",\n ");
5254 0 : printACLColumn(&buf, "n.nspacl");
5255 0 : appendPQExpBuffer(&buf,
5256 : ",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
5257 : gettext_noop("Description"));
5258 : }
5259 :
5260 24 : appendPQExpBufferStr(&buf,
5261 : "\nFROM pg_catalog.pg_namespace n\n");
5262 :
5263 24 : if (!showSystem && !pattern)
5264 0 : appendPQExpBufferStr(&buf,
5265 : "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
5266 :
5267 24 : if (!validateSQLNamePattern(&buf, pattern,
5268 24 : !showSystem && !pattern, false,
5269 : NULL, "n.nspname", NULL,
5270 : NULL,
5271 : NULL, 2))
5272 18 : goto error_return;
5273 :
5274 6 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
5275 :
5276 6 : res = PSQLexec(buf.data);
5277 6 : if (!res)
5278 0 : goto error_return;
5279 :
5280 6 : myopt.title = _("List of schemas");
5281 6 : myopt.translate_header = true;
5282 :
5283 6 : if (pattern && pset.sversion >= 150000)
5284 : {
5285 : PGresult *result;
5286 : int i;
5287 :
5288 6 : printfPQExpBuffer(&buf,
5289 : "SELECT pubname \n"
5290 : "FROM pg_catalog.pg_publication p\n"
5291 : " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
5292 : " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n"
5293 : "WHERE n.nspname = '%s'\n"
5294 : "ORDER BY 1",
5295 : pattern);
5296 6 : result = PSQLexec(buf.data);
5297 6 : if (!result)
5298 0 : goto error_return;
5299 : else
5300 6 : pub_schema_tuples = PQntuples(result);
5301 :
5302 6 : if (pub_schema_tuples > 0)
5303 : {
5304 : /*
5305 : * Allocate memory for footers. Size of footers will be 1 (for
5306 : * storing "Publications:" string) + publication schema mapping
5307 : * count + 1 (for storing NULL).
5308 : */
5309 0 : footers = (char **) pg_malloc((1 + pub_schema_tuples + 1) * sizeof(char *));
5310 0 : footers[0] = pg_strdup(_("Publications:"));
5311 :
5312 : /* Might be an empty set - that's ok */
5313 0 : for (i = 0; i < pub_schema_tuples; i++)
5314 : {
5315 0 : printfPQExpBuffer(&buf, " \"%s\"",
5316 : PQgetvalue(result, i, 0));
5317 :
5318 0 : footers[i + 1] = pg_strdup(buf.data);
5319 : }
5320 :
5321 0 : footers[i + 1] = NULL;
5322 0 : myopt.footers = footers;
5323 : }
5324 :
5325 6 : PQclear(result);
5326 : }
5327 :
5328 6 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5329 :
5330 6 : termPQExpBuffer(&buf);
5331 6 : PQclear(res);
5332 :
5333 : /* Free the memory allocated for the footer */
5334 6 : if (footers)
5335 : {
5336 0 : char **footer = NULL;
5337 :
5338 0 : for (footer = footers; *footer; footer++)
5339 0 : pg_free(*footer);
5340 :
5341 0 : pg_free(footers);
5342 : }
5343 :
5344 6 : return true;
5345 :
5346 18 : error_return:
5347 18 : termPQExpBuffer(&buf);
5348 18 : return false;
5349 : }
5350 :
5351 :
5352 : /*
5353 : * \dFp
5354 : * list text search parsers
5355 : */
5356 : bool
5357 42 : listTSParsers(const char *pattern, bool verbose)
5358 : {
5359 : PQExpBufferData buf;
5360 : PGresult *res;
5361 42 : printQueryOpt myopt = pset.popt;
5362 :
5363 42 : if (verbose)
5364 0 : return listTSParsersVerbose(pattern);
5365 :
5366 42 : initPQExpBuffer(&buf);
5367 :
5368 42 : printfPQExpBuffer(&buf,
5369 : "SELECT\n"
5370 : " n.nspname as \"%s\",\n"
5371 : " p.prsname as \"%s\",\n"
5372 : " pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
5373 : "FROM pg_catalog.pg_ts_parser p\n"
5374 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
5375 : gettext_noop("Schema"),
5376 : gettext_noop("Name"),
5377 : gettext_noop("Description")
5378 : );
5379 :
5380 42 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5381 : "n.nspname", "p.prsname", NULL,
5382 : "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5383 : NULL, 3))
5384 : {
5385 24 : termPQExpBuffer(&buf);
5386 24 : return false;
5387 : }
5388 :
5389 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5390 :
5391 18 : res = PSQLexec(buf.data);
5392 18 : termPQExpBuffer(&buf);
5393 18 : if (!res)
5394 0 : return false;
5395 :
5396 18 : myopt.title = _("List of text search parsers");
5397 18 : myopt.translate_header = true;
5398 :
5399 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5400 :
5401 18 : PQclear(res);
5402 18 : return true;
5403 : }
5404 :
5405 : /*
5406 : * full description of parsers
5407 : */
5408 : static bool
5409 0 : listTSParsersVerbose(const char *pattern)
5410 : {
5411 : PQExpBufferData buf;
5412 : PGresult *res;
5413 : int i;
5414 :
5415 0 : initPQExpBuffer(&buf);
5416 :
5417 0 : printfPQExpBuffer(&buf,
5418 : "SELECT p.oid,\n"
5419 : " n.nspname,\n"
5420 : " p.prsname\n"
5421 : "FROM pg_catalog.pg_ts_parser p\n"
5422 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
5423 : );
5424 :
5425 0 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5426 : "n.nspname", "p.prsname", NULL,
5427 : "pg_catalog.pg_ts_parser_is_visible(p.oid)",
5428 : NULL, 3))
5429 : {
5430 0 : termPQExpBuffer(&buf);
5431 0 : return false;
5432 : }
5433 :
5434 0 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5435 :
5436 0 : res = PSQLexec(buf.data);
5437 0 : termPQExpBuffer(&buf);
5438 0 : if (!res)
5439 0 : return false;
5440 :
5441 0 : if (PQntuples(res) == 0)
5442 : {
5443 0 : if (!pset.quiet)
5444 : {
5445 0 : if (pattern)
5446 0 : pg_log_error("Did not find any text search parser named \"%s\".",
5447 : pattern);
5448 : else
5449 0 : pg_log_error("Did not find any text search parsers.");
5450 : }
5451 0 : PQclear(res);
5452 0 : return false;
5453 : }
5454 :
5455 0 : for (i = 0; i < PQntuples(res); i++)
5456 : {
5457 : const char *oid;
5458 0 : const char *nspname = NULL;
5459 : const char *prsname;
5460 :
5461 0 : oid = PQgetvalue(res, i, 0);
5462 0 : if (!PQgetisnull(res, i, 1))
5463 0 : nspname = PQgetvalue(res, i, 1);
5464 0 : prsname = PQgetvalue(res, i, 2);
5465 :
5466 0 : if (!describeOneTSParser(oid, nspname, prsname))
5467 : {
5468 0 : PQclear(res);
5469 0 : return false;
5470 : }
5471 :
5472 0 : if (cancel_pressed)
5473 : {
5474 0 : PQclear(res);
5475 0 : return false;
5476 : }
5477 : }
5478 :
5479 0 : PQclear(res);
5480 0 : return true;
5481 : }
5482 :
5483 : static bool
5484 0 : describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
5485 : {
5486 : PQExpBufferData buf;
5487 : PGresult *res;
5488 : PQExpBufferData title;
5489 0 : printQueryOpt myopt = pset.popt;
5490 : static const bool translate_columns[] = {true, false, false};
5491 :
5492 0 : initPQExpBuffer(&buf);
5493 :
5494 0 : printfPQExpBuffer(&buf,
5495 : "SELECT '%s' AS \"%s\",\n"
5496 : " p.prsstart::pg_catalog.regproc AS \"%s\",\n"
5497 : " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\"\n"
5498 : " FROM pg_catalog.pg_ts_parser p\n"
5499 : " WHERE p.oid = '%s'\n"
5500 : "UNION ALL\n"
5501 : "SELECT '%s',\n"
5502 : " p.prstoken::pg_catalog.regproc,\n"
5503 : " pg_catalog.obj_description(p.prstoken, 'pg_proc')\n"
5504 : " FROM pg_catalog.pg_ts_parser p\n"
5505 : " WHERE p.oid = '%s'\n"
5506 : "UNION ALL\n"
5507 : "SELECT '%s',\n"
5508 : " p.prsend::pg_catalog.regproc,\n"
5509 : " pg_catalog.obj_description(p.prsend, 'pg_proc')\n"
5510 : " FROM pg_catalog.pg_ts_parser p\n"
5511 : " WHERE p.oid = '%s'\n"
5512 : "UNION ALL\n"
5513 : "SELECT '%s',\n"
5514 : " p.prsheadline::pg_catalog.regproc,\n"
5515 : " pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n"
5516 : " FROM pg_catalog.pg_ts_parser p\n"
5517 : " WHERE p.oid = '%s'\n"
5518 : "UNION ALL\n"
5519 : "SELECT '%s',\n"
5520 : " p.prslextype::pg_catalog.regproc,\n"
5521 : " pg_catalog.obj_description(p.prslextype, 'pg_proc')\n"
5522 : " FROM pg_catalog.pg_ts_parser p\n"
5523 : " WHERE p.oid = '%s';",
5524 : gettext_noop("Start parse"),
5525 : gettext_noop("Method"),
5526 : gettext_noop("Function"),
5527 : gettext_noop("Description"),
5528 : oid,
5529 : gettext_noop("Get next token"),
5530 : oid,
5531 : gettext_noop("End parse"),
5532 : oid,
5533 : gettext_noop("Get headline"),
5534 : oid,
5535 : gettext_noop("Get token types"),
5536 : oid);
5537 :
5538 0 : res = PSQLexec(buf.data);
5539 0 : termPQExpBuffer(&buf);
5540 0 : if (!res)
5541 0 : return false;
5542 :
5543 0 : initPQExpBuffer(&title);
5544 0 : if (nspname)
5545 0 : printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""),
5546 : nspname, prsname);
5547 : else
5548 0 : printfPQExpBuffer(&title, _("Text search parser \"%s\""), prsname);
5549 0 : myopt.title = title.data;
5550 0 : myopt.footers = NULL;
5551 0 : myopt.topt.default_footer = false;
5552 0 : myopt.translate_header = true;
5553 0 : myopt.translate_columns = translate_columns;
5554 0 : myopt.n_translate_columns = lengthof(translate_columns);
5555 :
5556 0 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5557 :
5558 0 : PQclear(res);
5559 :
5560 0 : initPQExpBuffer(&buf);
5561 :
5562 0 : printfPQExpBuffer(&buf,
5563 : "SELECT t.alias as \"%s\",\n"
5564 : " t.description as \"%s\"\n"
5565 : "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t\n"
5566 : "ORDER BY 1;",
5567 : gettext_noop("Token name"),
5568 : gettext_noop("Description"),
5569 : oid);
5570 :
5571 0 : res = PSQLexec(buf.data);
5572 0 : termPQExpBuffer(&buf);
5573 0 : if (!res)
5574 : {
5575 0 : termPQExpBuffer(&title);
5576 0 : return false;
5577 : }
5578 :
5579 0 : if (nspname)
5580 0 : printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""),
5581 : nspname, prsname);
5582 : else
5583 0 : printfPQExpBuffer(&title, _("Token types for parser \"%s\""), prsname);
5584 0 : myopt.title = title.data;
5585 0 : myopt.footers = NULL;
5586 0 : myopt.topt.default_footer = true;
5587 0 : myopt.translate_header = true;
5588 0 : myopt.translate_columns = NULL;
5589 0 : myopt.n_translate_columns = 0;
5590 :
5591 0 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5592 :
5593 0 : termPQExpBuffer(&title);
5594 0 : PQclear(res);
5595 0 : return true;
5596 : }
5597 :
5598 :
5599 : /*
5600 : * \dFd
5601 : * list text search dictionaries
5602 : */
5603 : bool
5604 42 : listTSDictionaries(const char *pattern, bool verbose)
5605 : {
5606 : PQExpBufferData buf;
5607 : PGresult *res;
5608 42 : printQueryOpt myopt = pset.popt;
5609 :
5610 42 : initPQExpBuffer(&buf);
5611 :
5612 42 : printfPQExpBuffer(&buf,
5613 : "SELECT\n"
5614 : " n.nspname as \"%s\",\n"
5615 : " d.dictname as \"%s\",\n",
5616 : gettext_noop("Schema"),
5617 : gettext_noop("Name"));
5618 :
5619 42 : if (verbose)
5620 : {
5621 0 : appendPQExpBuffer(&buf,
5622 : " ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM\n"
5623 : " pg_catalog.pg_ts_template t\n"
5624 : " LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace\n"
5625 : " WHERE d.dicttemplate = t.oid ) AS \"%s\",\n"
5626 : " d.dictinitoption as \"%s\",\n",
5627 : gettext_noop("Template"),
5628 : gettext_noop("Init options"));
5629 : }
5630 :
5631 42 : appendPQExpBuffer(&buf,
5632 : " pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
5633 : gettext_noop("Description"));
5634 :
5635 42 : appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_dict d\n"
5636 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
5637 :
5638 42 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5639 : "n.nspname", "d.dictname", NULL,
5640 : "pg_catalog.pg_ts_dict_is_visible(d.oid)",
5641 : NULL, 3))
5642 : {
5643 24 : termPQExpBuffer(&buf);
5644 24 : return false;
5645 : }
5646 :
5647 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5648 :
5649 18 : res = PSQLexec(buf.data);
5650 18 : termPQExpBuffer(&buf);
5651 18 : if (!res)
5652 0 : return false;
5653 :
5654 18 : myopt.title = _("List of text search dictionaries");
5655 18 : myopt.translate_header = true;
5656 :
5657 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5658 :
5659 18 : PQclear(res);
5660 18 : return true;
5661 : }
5662 :
5663 :
5664 : /*
5665 : * \dFt
5666 : * list text search templates
5667 : */
5668 : bool
5669 42 : listTSTemplates(const char *pattern, bool verbose)
5670 : {
5671 : PQExpBufferData buf;
5672 : PGresult *res;
5673 42 : printQueryOpt myopt = pset.popt;
5674 :
5675 42 : initPQExpBuffer(&buf);
5676 :
5677 42 : if (verbose)
5678 0 : printfPQExpBuffer(&buf,
5679 : "SELECT\n"
5680 : " n.nspname AS \"%s\",\n"
5681 : " t.tmplname AS \"%s\",\n"
5682 : " t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
5683 : " t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
5684 : " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5685 : gettext_noop("Schema"),
5686 : gettext_noop("Name"),
5687 : gettext_noop("Init"),
5688 : gettext_noop("Lexize"),
5689 : gettext_noop("Description"));
5690 : else
5691 42 : printfPQExpBuffer(&buf,
5692 : "SELECT\n"
5693 : " n.nspname AS \"%s\",\n"
5694 : " t.tmplname AS \"%s\",\n"
5695 : " pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
5696 : gettext_noop("Schema"),
5697 : gettext_noop("Name"),
5698 : gettext_noop("Description"));
5699 :
5700 42 : appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_ts_template t\n"
5701 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
5702 :
5703 42 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5704 : "n.nspname", "t.tmplname", NULL,
5705 : "pg_catalog.pg_ts_template_is_visible(t.oid)",
5706 : NULL, 3))
5707 : {
5708 24 : termPQExpBuffer(&buf);
5709 24 : return false;
5710 : }
5711 :
5712 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5713 :
5714 18 : res = PSQLexec(buf.data);
5715 18 : termPQExpBuffer(&buf);
5716 18 : if (!res)
5717 0 : return false;
5718 :
5719 18 : myopt.title = _("List of text search templates");
5720 18 : myopt.translate_header = true;
5721 :
5722 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5723 :
5724 18 : PQclear(res);
5725 18 : return true;
5726 : }
5727 :
5728 :
5729 : /*
5730 : * \dF
5731 : * list text search configurations
5732 : */
5733 : bool
5734 42 : listTSConfigs(const char *pattern, bool verbose)
5735 : {
5736 : PQExpBufferData buf;
5737 : PGresult *res;
5738 42 : printQueryOpt myopt = pset.popt;
5739 :
5740 42 : if (verbose)
5741 0 : return listTSConfigsVerbose(pattern);
5742 :
5743 42 : initPQExpBuffer(&buf);
5744 :
5745 42 : printfPQExpBuffer(&buf,
5746 : "SELECT\n"
5747 : " n.nspname as \"%s\",\n"
5748 : " c.cfgname as \"%s\",\n"
5749 : " pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
5750 : "FROM pg_catalog.pg_ts_config c\n"
5751 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace\n",
5752 : gettext_noop("Schema"),
5753 : gettext_noop("Name"),
5754 : gettext_noop("Description")
5755 : );
5756 :
5757 42 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5758 : "n.nspname", "c.cfgname", NULL,
5759 : "pg_catalog.pg_ts_config_is_visible(c.oid)",
5760 : NULL, 3))
5761 : {
5762 24 : termPQExpBuffer(&buf);
5763 24 : return false;
5764 : }
5765 :
5766 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5767 :
5768 18 : res = PSQLexec(buf.data);
5769 18 : termPQExpBuffer(&buf);
5770 18 : if (!res)
5771 0 : return false;
5772 :
5773 18 : myopt.title = _("List of text search configurations");
5774 18 : myopt.translate_header = true;
5775 :
5776 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5777 :
5778 18 : PQclear(res);
5779 18 : return true;
5780 : }
5781 :
5782 : static bool
5783 0 : listTSConfigsVerbose(const char *pattern)
5784 : {
5785 : PQExpBufferData buf;
5786 : PGresult *res;
5787 : int i;
5788 :
5789 0 : initPQExpBuffer(&buf);
5790 :
5791 0 : printfPQExpBuffer(&buf,
5792 : "SELECT c.oid, c.cfgname,\n"
5793 : " n.nspname,\n"
5794 : " p.prsname,\n"
5795 : " np.nspname as pnspname\n"
5796 : "FROM pg_catalog.pg_ts_config c\n"
5797 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace,\n"
5798 : " pg_catalog.pg_ts_parser p\n"
5799 : " LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace\n"
5800 : "WHERE p.oid = c.cfgparser\n"
5801 : );
5802 :
5803 0 : if (!validateSQLNamePattern(&buf, pattern, true, false,
5804 : "n.nspname", "c.cfgname", NULL,
5805 : "pg_catalog.pg_ts_config_is_visible(c.oid)",
5806 : NULL, 3))
5807 : {
5808 0 : termPQExpBuffer(&buf);
5809 0 : return false;
5810 : }
5811 :
5812 0 : appendPQExpBufferStr(&buf, "ORDER BY 3, 2;");
5813 :
5814 0 : res = PSQLexec(buf.data);
5815 0 : termPQExpBuffer(&buf);
5816 0 : if (!res)
5817 0 : return false;
5818 :
5819 0 : if (PQntuples(res) == 0)
5820 : {
5821 0 : if (!pset.quiet)
5822 : {
5823 0 : if (pattern)
5824 0 : pg_log_error("Did not find any text search configuration named \"%s\".",
5825 : pattern);
5826 : else
5827 0 : pg_log_error("Did not find any text search configurations.");
5828 : }
5829 0 : PQclear(res);
5830 0 : return false;
5831 : }
5832 :
5833 0 : for (i = 0; i < PQntuples(res); i++)
5834 : {
5835 : const char *oid;
5836 : const char *cfgname;
5837 0 : const char *nspname = NULL;
5838 : const char *prsname;
5839 0 : const char *pnspname = NULL;
5840 :
5841 0 : oid = PQgetvalue(res, i, 0);
5842 0 : cfgname = PQgetvalue(res, i, 1);
5843 0 : if (!PQgetisnull(res, i, 2))
5844 0 : nspname = PQgetvalue(res, i, 2);
5845 0 : prsname = PQgetvalue(res, i, 3);
5846 0 : if (!PQgetisnull(res, i, 4))
5847 0 : pnspname = PQgetvalue(res, i, 4);
5848 :
5849 0 : if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
5850 : {
5851 0 : PQclear(res);
5852 0 : return false;
5853 : }
5854 :
5855 0 : if (cancel_pressed)
5856 : {
5857 0 : PQclear(res);
5858 0 : return false;
5859 : }
5860 : }
5861 :
5862 0 : PQclear(res);
5863 0 : return true;
5864 : }
5865 :
5866 : static bool
5867 0 : describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
5868 : const char *pnspname, const char *prsname)
5869 : {
5870 : PQExpBufferData buf,
5871 : title;
5872 : PGresult *res;
5873 0 : printQueryOpt myopt = pset.popt;
5874 :
5875 0 : initPQExpBuffer(&buf);
5876 :
5877 0 : printfPQExpBuffer(&buf,
5878 : "SELECT\n"
5879 : " ( SELECT t.alias FROM\n"
5880 : " pg_catalog.ts_token_type(c.cfgparser) AS t\n"
5881 : " WHERE t.tokid = m.maptokentype ) AS \"%s\",\n"
5882 : " pg_catalog.btrim(\n"
5883 : " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary\n"
5884 : " FROM pg_catalog.pg_ts_config_map AS mm\n"
5885 : " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype\n"
5886 : " ORDER BY mapcfg, maptokentype, mapseqno\n"
5887 : " ) :: pg_catalog.text,\n"
5888 : " '{}') AS \"%s\"\n"
5889 : "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m\n"
5890 : "WHERE c.oid = '%s' AND m.mapcfg = c.oid\n"
5891 : "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser\n"
5892 : "ORDER BY 1;",
5893 : gettext_noop("Token"),
5894 : gettext_noop("Dictionaries"),
5895 : oid);
5896 :
5897 0 : res = PSQLexec(buf.data);
5898 0 : termPQExpBuffer(&buf);
5899 0 : if (!res)
5900 0 : return false;
5901 :
5902 0 : initPQExpBuffer(&title);
5903 :
5904 0 : if (nspname)
5905 0 : appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
5906 : nspname, cfgname);
5907 : else
5908 0 : appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
5909 : cfgname);
5910 :
5911 0 : if (pnspname)
5912 0 : appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
5913 : pnspname, prsname);
5914 : else
5915 0 : appendPQExpBuffer(&title, _("\nParser: \"%s\""),
5916 : prsname);
5917 :
5918 0 : myopt.title = title.data;
5919 0 : myopt.footers = NULL;
5920 0 : myopt.topt.default_footer = false;
5921 0 : myopt.translate_header = true;
5922 :
5923 0 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5924 :
5925 0 : termPQExpBuffer(&title);
5926 :
5927 0 : PQclear(res);
5928 0 : return true;
5929 : }
5930 :
5931 :
5932 : /*
5933 : * \dew
5934 : *
5935 : * Describes foreign-data wrappers
5936 : */
5937 : bool
5938 114 : listForeignDataWrappers(const char *pattern, bool verbose)
5939 : {
5940 : PQExpBufferData buf;
5941 : PGresult *res;
5942 114 : printQueryOpt myopt = pset.popt;
5943 :
5944 114 : initPQExpBuffer(&buf);
5945 114 : printfPQExpBuffer(&buf,
5946 : "SELECT fdw.fdwname AS \"%s\",\n"
5947 : " pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n"
5948 : " fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n"
5949 : " fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
5950 : gettext_noop("Name"),
5951 : gettext_noop("Owner"),
5952 : gettext_noop("Handler"),
5953 : gettext_noop("Validator"));
5954 :
5955 114 : if (verbose)
5956 : {
5957 84 : appendPQExpBufferStr(&buf, ",\n ");
5958 84 : printACLColumn(&buf, "fdwacl");
5959 84 : appendPQExpBuffer(&buf,
5960 : ",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE "
5961 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
5962 : " pg_catalog.quote_ident(option_name) || ' ' || "
5963 : " pg_catalog.quote_literal(option_value) FROM "
5964 : " pg_catalog.pg_options_to_table(fdwoptions)), ', ') || ')' "
5965 : " END AS \"%s\""
5966 : ",\n d.description AS \"%s\" ",
5967 : gettext_noop("FDW options"),
5968 : gettext_noop("Description"));
5969 : }
5970 :
5971 114 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
5972 :
5973 114 : if (verbose)
5974 84 : appendPQExpBufferStr(&buf,
5975 : "LEFT JOIN pg_catalog.pg_description d\n"
5976 : " ON d.classoid = fdw.tableoid "
5977 : "AND d.objoid = fdw.oid AND d.objsubid = 0\n");
5978 :
5979 114 : if (!validateSQLNamePattern(&buf, pattern, false, false,
5980 : NULL, "fdwname", NULL, NULL,
5981 : NULL, 1))
5982 : {
5983 18 : termPQExpBuffer(&buf);
5984 18 : return false;
5985 : }
5986 :
5987 96 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
5988 :
5989 96 : res = PSQLexec(buf.data);
5990 96 : termPQExpBuffer(&buf);
5991 96 : if (!res)
5992 0 : return false;
5993 :
5994 96 : myopt.title = _("List of foreign-data wrappers");
5995 96 : myopt.translate_header = true;
5996 :
5997 96 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5998 :
5999 96 : PQclear(res);
6000 96 : return true;
6001 : }
6002 :
6003 : /*
6004 : * \des
6005 : *
6006 : * Describes foreign servers.
6007 : */
6008 : bool
6009 120 : listForeignServers(const char *pattern, bool verbose)
6010 : {
6011 : PQExpBufferData buf;
6012 : PGresult *res;
6013 120 : printQueryOpt myopt = pset.popt;
6014 :
6015 120 : initPQExpBuffer(&buf);
6016 120 : printfPQExpBuffer(&buf,
6017 : "SELECT s.srvname AS \"%s\",\n"
6018 : " pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
6019 : " f.fdwname AS \"%s\"",
6020 : gettext_noop("Name"),
6021 : gettext_noop("Owner"),
6022 : gettext_noop("Foreign-data wrapper"));
6023 :
6024 120 : if (verbose)
6025 : {
6026 48 : appendPQExpBufferStr(&buf, ",\n ");
6027 48 : printACLColumn(&buf, "s.srvacl");
6028 48 : appendPQExpBuffer(&buf,
6029 : ",\n"
6030 : " s.srvtype AS \"%s\",\n"
6031 : " s.srvversion AS \"%s\",\n"
6032 : " CASE WHEN srvoptions IS NULL THEN '' ELSE "
6033 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6034 : " pg_catalog.quote_ident(option_name) || ' ' || "
6035 : " pg_catalog.quote_literal(option_value) FROM "
6036 : " pg_catalog.pg_options_to_table(srvoptions)), ', ') || ')' "
6037 : " END AS \"%s\",\n"
6038 : " d.description AS \"%s\"",
6039 : gettext_noop("Type"),
6040 : gettext_noop("Version"),
6041 : gettext_noop("FDW options"),
6042 : gettext_noop("Description"));
6043 : }
6044 :
6045 120 : appendPQExpBufferStr(&buf,
6046 : "\nFROM pg_catalog.pg_foreign_server s\n"
6047 : " JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
6048 :
6049 120 : if (verbose)
6050 48 : appendPQExpBufferStr(&buf,
6051 : "LEFT JOIN pg_catalog.pg_description d\n "
6052 : "ON d.classoid = s.tableoid AND d.objoid = s.oid "
6053 : "AND d.objsubid = 0\n");
6054 :
6055 120 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6056 : NULL, "s.srvname", NULL, NULL,
6057 : NULL, 1))
6058 : {
6059 42 : termPQExpBuffer(&buf);
6060 42 : return false;
6061 : }
6062 :
6063 78 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6064 :
6065 78 : res = PSQLexec(buf.data);
6066 78 : termPQExpBuffer(&buf);
6067 78 : if (!res)
6068 0 : return false;
6069 :
6070 78 : myopt.title = _("List of foreign servers");
6071 78 : myopt.translate_header = true;
6072 :
6073 78 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6074 :
6075 78 : PQclear(res);
6076 78 : return true;
6077 : }
6078 :
6079 : /*
6080 : * \deu
6081 : *
6082 : * Describes user mappings.
6083 : */
6084 : bool
6085 60 : listUserMappings(const char *pattern, bool verbose)
6086 : {
6087 : PQExpBufferData buf;
6088 : PGresult *res;
6089 60 : printQueryOpt myopt = pset.popt;
6090 :
6091 60 : initPQExpBuffer(&buf);
6092 60 : printfPQExpBuffer(&buf,
6093 : "SELECT um.srvname AS \"%s\",\n"
6094 : " um.usename AS \"%s\"",
6095 : gettext_noop("Server"),
6096 : gettext_noop("User name"));
6097 :
6098 60 : if (verbose)
6099 36 : appendPQExpBuffer(&buf,
6100 : ",\n CASE WHEN umoptions IS NULL THEN '' ELSE "
6101 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6102 : " pg_catalog.quote_ident(option_name) || ' ' || "
6103 : " pg_catalog.quote_literal(option_value) FROM "
6104 : " pg_catalog.pg_options_to_table(umoptions)), ', ') || ')' "
6105 : " END AS \"%s\"",
6106 : gettext_noop("FDW options"));
6107 :
6108 60 : appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
6109 :
6110 60 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6111 : NULL, "um.srvname", "um.usename", NULL,
6112 : NULL, 1))
6113 : {
6114 0 : termPQExpBuffer(&buf);
6115 0 : return false;
6116 : }
6117 :
6118 60 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6119 :
6120 60 : res = PSQLexec(buf.data);
6121 60 : termPQExpBuffer(&buf);
6122 60 : if (!res)
6123 0 : return false;
6124 :
6125 60 : myopt.title = _("List of user mappings");
6126 60 : myopt.translate_header = true;
6127 :
6128 60 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6129 :
6130 60 : PQclear(res);
6131 60 : return true;
6132 : }
6133 :
6134 : /*
6135 : * \det
6136 : *
6137 : * Describes foreign tables.
6138 : */
6139 : bool
6140 18 : listForeignTables(const char *pattern, bool verbose)
6141 : {
6142 : PQExpBufferData buf;
6143 : PGresult *res;
6144 18 : printQueryOpt myopt = pset.popt;
6145 :
6146 18 : initPQExpBuffer(&buf);
6147 18 : printfPQExpBuffer(&buf,
6148 : "SELECT n.nspname AS \"%s\",\n"
6149 : " c.relname AS \"%s\",\n"
6150 : " s.srvname AS \"%s\"",
6151 : gettext_noop("Schema"),
6152 : gettext_noop("Table"),
6153 : gettext_noop("Server"));
6154 :
6155 18 : if (verbose)
6156 18 : appendPQExpBuffer(&buf,
6157 : ",\n CASE WHEN ftoptions IS NULL THEN '' ELSE "
6158 : " '(' || pg_catalog.array_to_string(ARRAY(SELECT "
6159 : " pg_catalog.quote_ident(option_name) || ' ' || "
6160 : " pg_catalog.quote_literal(option_value) FROM "
6161 : " pg_catalog.pg_options_to_table(ftoptions)), ', ') || ')' "
6162 : " END AS \"%s\",\n"
6163 : " d.description AS \"%s\"",
6164 : gettext_noop("FDW options"),
6165 : gettext_noop("Description"));
6166 :
6167 18 : appendPQExpBufferStr(&buf,
6168 : "\nFROM pg_catalog.pg_foreign_table ft\n"
6169 : " INNER JOIN pg_catalog.pg_class c"
6170 : " ON c.oid = ft.ftrelid\n"
6171 : " INNER JOIN pg_catalog.pg_namespace n"
6172 : " ON n.oid = c.relnamespace\n"
6173 : " INNER JOIN pg_catalog.pg_foreign_server s"
6174 : " ON s.oid = ft.ftserver\n");
6175 18 : if (verbose)
6176 18 : appendPQExpBufferStr(&buf,
6177 : " LEFT JOIN pg_catalog.pg_description d\n"
6178 : " ON d.classoid = c.tableoid AND "
6179 : "d.objoid = c.oid AND d.objsubid = 0\n");
6180 :
6181 18 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6182 : "n.nspname", "c.relname", NULL,
6183 : "pg_catalog.pg_table_is_visible(c.oid)",
6184 : NULL, 3))
6185 : {
6186 0 : termPQExpBuffer(&buf);
6187 0 : return false;
6188 : }
6189 :
6190 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
6191 :
6192 18 : res = PSQLexec(buf.data);
6193 18 : termPQExpBuffer(&buf);
6194 18 : if (!res)
6195 0 : return false;
6196 :
6197 18 : myopt.title = _("List of foreign tables");
6198 18 : myopt.translate_header = true;
6199 :
6200 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6201 :
6202 18 : PQclear(res);
6203 18 : return true;
6204 : }
6205 :
6206 : /*
6207 : * \dx
6208 : *
6209 : * Briefly describes installed extensions.
6210 : */
6211 : bool
6212 24 : listExtensions(const char *pattern)
6213 : {
6214 : PQExpBufferData buf;
6215 : PGresult *res;
6216 24 : printQueryOpt myopt = pset.popt;
6217 :
6218 24 : initPQExpBuffer(&buf);
6219 24 : printfPQExpBuffer(&buf,
6220 : "SELECT e.extname AS \"%s\", "
6221 : "e.extversion AS \"%s\", ae.default_version AS \"%s\","
6222 : "n.nspname AS \"%s\", d.description AS \"%s\"\n"
6223 : "FROM pg_catalog.pg_extension e "
6224 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
6225 : "LEFT JOIN pg_catalog.pg_description d ON d.objoid = e.oid "
6226 : "AND d.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass "
6227 : "LEFT JOIN pg_catalog.pg_available_extensions() ae(name, default_version, comment) ON ae.name = e.extname\n",
6228 : gettext_noop("Name"),
6229 : gettext_noop("Version"),
6230 : gettext_noop("Default version"),
6231 : gettext_noop("Schema"),
6232 : gettext_noop("Description"));
6233 :
6234 24 : if (!validateSQLNamePattern(&buf, pattern,
6235 : false, false,
6236 : NULL, "e.extname", NULL,
6237 : NULL,
6238 : NULL, 1))
6239 : {
6240 18 : termPQExpBuffer(&buf);
6241 18 : return false;
6242 : }
6243 :
6244 6 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6245 :
6246 6 : res = PSQLexec(buf.data);
6247 6 : termPQExpBuffer(&buf);
6248 6 : if (!res)
6249 0 : return false;
6250 :
6251 6 : myopt.title = _("List of installed extensions");
6252 6 : myopt.translate_header = true;
6253 :
6254 6 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6255 :
6256 6 : PQclear(res);
6257 6 : return true;
6258 : }
6259 :
6260 : /*
6261 : * \dx+
6262 : *
6263 : * List contents of installed extensions.
6264 : */
6265 : bool
6266 32 : listExtensionContents(const char *pattern)
6267 : {
6268 : PQExpBufferData buf;
6269 : PGresult *res;
6270 : int i;
6271 :
6272 32 : initPQExpBuffer(&buf);
6273 32 : printfPQExpBuffer(&buf,
6274 : "SELECT e.extname, e.oid\n"
6275 : "FROM pg_catalog.pg_extension e\n");
6276 :
6277 32 : if (!validateSQLNamePattern(&buf, pattern,
6278 : false, false,
6279 : NULL, "e.extname", NULL,
6280 : NULL,
6281 : NULL, 1))
6282 : {
6283 0 : termPQExpBuffer(&buf);
6284 0 : return false;
6285 : }
6286 :
6287 32 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6288 :
6289 32 : res = PSQLexec(buf.data);
6290 32 : termPQExpBuffer(&buf);
6291 32 : if (!res)
6292 0 : return false;
6293 :
6294 32 : if (PQntuples(res) == 0)
6295 : {
6296 0 : if (!pset.quiet)
6297 : {
6298 0 : if (pattern)
6299 0 : pg_log_error("Did not find any extension named \"%s\".",
6300 : pattern);
6301 : else
6302 0 : pg_log_error("Did not find any extensions.");
6303 : }
6304 0 : PQclear(res);
6305 0 : return false;
6306 : }
6307 :
6308 64 : for (i = 0; i < PQntuples(res); i++)
6309 : {
6310 : const char *extname;
6311 : const char *oid;
6312 :
6313 32 : extname = PQgetvalue(res, i, 0);
6314 32 : oid = PQgetvalue(res, i, 1);
6315 :
6316 32 : if (!listOneExtensionContents(extname, oid))
6317 : {
6318 0 : PQclear(res);
6319 0 : return false;
6320 : }
6321 32 : if (cancel_pressed)
6322 : {
6323 0 : PQclear(res);
6324 0 : return false;
6325 : }
6326 : }
6327 :
6328 32 : PQclear(res);
6329 32 : return true;
6330 : }
6331 :
6332 : static bool
6333 32 : listOneExtensionContents(const char *extname, const char *oid)
6334 : {
6335 : PQExpBufferData buf;
6336 : PGresult *res;
6337 : PQExpBufferData title;
6338 32 : printQueryOpt myopt = pset.popt;
6339 :
6340 32 : initPQExpBuffer(&buf);
6341 32 : printfPQExpBuffer(&buf,
6342 : "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
6343 : "FROM pg_catalog.pg_depend\n"
6344 : "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
6345 : "ORDER BY 1;",
6346 : gettext_noop("Object description"),
6347 : oid);
6348 :
6349 32 : res = PSQLexec(buf.data);
6350 32 : termPQExpBuffer(&buf);
6351 32 : if (!res)
6352 0 : return false;
6353 :
6354 32 : initPQExpBuffer(&title);
6355 32 : printfPQExpBuffer(&title, _("Objects in extension \"%s\""), extname);
6356 32 : myopt.title = title.data;
6357 32 : myopt.translate_header = true;
6358 :
6359 32 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6360 :
6361 32 : termPQExpBuffer(&title);
6362 32 : PQclear(res);
6363 32 : return true;
6364 : }
6365 :
6366 : /*
6367 : * validateSQLNamePattern
6368 : *
6369 : * Wrapper around string_utils's processSQLNamePattern which also checks the
6370 : * pattern's validity. In addition to that function's parameters, takes a
6371 : * 'maxparts' parameter specifying the maximum number of dotted names the
6372 : * pattern is allowed to have, and a 'added_clause' parameter that returns by
6373 : * reference whether a clause was added to 'buf'. Returns whether the pattern
6374 : * passed validation, after logging any errors.
6375 : */
6376 : static bool
6377 7076 : validateSQLNamePattern(PQExpBuffer buf, const char *pattern, bool have_where,
6378 : bool force_escape, const char *schemavar,
6379 : const char *namevar, const char *altnamevar,
6380 : const char *visibilityrule, bool *added_clause,
6381 : int maxparts)
6382 : {
6383 : PQExpBufferData dbbuf;
6384 : int dotcnt;
6385 : bool added;
6386 :
6387 7076 : initPQExpBuffer(&dbbuf);
6388 7076 : added = processSQLNamePattern(pset.db, buf, pattern, have_where, force_escape,
6389 : schemavar, namevar, altnamevar,
6390 : visibilityrule, &dbbuf, &dotcnt);
6391 7076 : if (added_clause != NULL)
6392 170 : *added_clause = added;
6393 :
6394 7076 : if (dotcnt >= maxparts)
6395 : {
6396 438 : pg_log_error("improper qualified name (too many dotted names): %s",
6397 : pattern);
6398 438 : goto error_return;
6399 : }
6400 :
6401 6638 : if (maxparts > 1 && dotcnt == maxparts - 1)
6402 : {
6403 618 : if (PQdb(pset.db) == NULL)
6404 : {
6405 0 : pg_log_error("You are currently not connected to a database.");
6406 0 : goto error_return;
6407 : }
6408 618 : if (strcmp(PQdb(pset.db), dbbuf.data) != 0)
6409 : {
6410 450 : pg_log_error("cross-database references are not implemented: %s",
6411 : pattern);
6412 450 : goto error_return;
6413 : }
6414 : }
6415 6188 : termPQExpBuffer(&dbbuf);
6416 6188 : return true;
6417 :
6418 888 : error_return:
6419 888 : termPQExpBuffer(&dbbuf);
6420 888 : return false;
6421 : }
6422 :
6423 : /*
6424 : * \dRp
6425 : * Lists publications.
6426 : *
6427 : * Takes an optional regexp to select particular publications
6428 : */
6429 : bool
6430 48 : listPublications(const char *pattern)
6431 : {
6432 : PQExpBufferData buf;
6433 : PGresult *res;
6434 48 : printQueryOpt myopt = pset.popt;
6435 : static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
6436 :
6437 48 : if (pset.sversion < 100000)
6438 : {
6439 : char sverbuf[32];
6440 :
6441 0 : pg_log_error("The server (version %s) does not support publications.",
6442 : formatPGVersionNumber(pset.sversion, false,
6443 : sverbuf, sizeof(sverbuf)));
6444 0 : return true;
6445 : }
6446 :
6447 48 : initPQExpBuffer(&buf);
6448 :
6449 48 : printfPQExpBuffer(&buf,
6450 : "SELECT pubname AS \"%s\",\n"
6451 : " pg_catalog.pg_get_userbyid(pubowner) AS \"%s\",\n"
6452 : " puballtables AS \"%s\"",
6453 : gettext_noop("Name"),
6454 : gettext_noop("Owner"),
6455 : gettext_noop("All tables"));
6456 :
6457 48 : if (pset.sversion >= 190000)
6458 48 : appendPQExpBuffer(&buf,
6459 : ",\n puballsequences AS \"%s\"",
6460 : gettext_noop("All sequences"));
6461 :
6462 48 : appendPQExpBuffer(&buf,
6463 : ",\n pubinsert AS \"%s\",\n"
6464 : " pubupdate AS \"%s\",\n"
6465 : " pubdelete AS \"%s\"",
6466 : gettext_noop("Inserts"),
6467 : gettext_noop("Updates"),
6468 : gettext_noop("Deletes"));
6469 48 : if (pset.sversion >= 110000)
6470 48 : appendPQExpBuffer(&buf,
6471 : ",\n pubtruncate AS \"%s\"",
6472 : gettext_noop("Truncates"));
6473 48 : if (pset.sversion >= 180000)
6474 48 : appendPQExpBuffer(&buf,
6475 : ",\n (CASE pubgencols\n"
6476 : " WHEN '%c' THEN 'none'\n"
6477 : " WHEN '%c' THEN 'stored'\n"
6478 : " END) AS \"%s\"",
6479 : PUBLISH_GENCOLS_NONE,
6480 : PUBLISH_GENCOLS_STORED,
6481 : gettext_noop("Generated columns"));
6482 48 : if (pset.sversion >= 130000)
6483 48 : appendPQExpBuffer(&buf,
6484 : ",\n pubviaroot AS \"%s\"",
6485 : gettext_noop("Via root"));
6486 :
6487 48 : appendPQExpBufferStr(&buf,
6488 : "\nFROM pg_catalog.pg_publication\n");
6489 :
6490 48 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6491 : NULL, "pubname", NULL,
6492 : NULL,
6493 : NULL, 1))
6494 : {
6495 18 : termPQExpBuffer(&buf);
6496 18 : return false;
6497 : }
6498 :
6499 30 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6500 :
6501 30 : res = PSQLexec(buf.data);
6502 30 : termPQExpBuffer(&buf);
6503 30 : if (!res)
6504 0 : return false;
6505 :
6506 30 : myopt.title = _("List of publications");
6507 30 : myopt.translate_header = true;
6508 30 : myopt.translate_columns = translate_columns;
6509 30 : myopt.n_translate_columns = lengthof(translate_columns);
6510 :
6511 30 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6512 :
6513 30 : PQclear(res);
6514 :
6515 30 : return true;
6516 : }
6517 :
6518 : /*
6519 : * Add footer to publication description.
6520 : */
6521 : static bool
6522 636 : addFooterToPublicationDesc(PQExpBuffer buf, const char *footermsg,
6523 : bool as_schema, printTableContent *const cont)
6524 : {
6525 : PGresult *res;
6526 636 : int count = 0;
6527 636 : int i = 0;
6528 :
6529 636 : res = PSQLexec(buf->data);
6530 636 : if (!res)
6531 0 : return false;
6532 : else
6533 636 : count = PQntuples(res);
6534 :
6535 636 : if (count > 0)
6536 330 : printTableAddFooter(cont, footermsg);
6537 :
6538 1092 : for (i = 0; i < count; i++)
6539 : {
6540 456 : if (as_schema)
6541 234 : printfPQExpBuffer(buf, " \"%s\"", PQgetvalue(res, i, 0));
6542 : else
6543 : {
6544 222 : printfPQExpBuffer(buf, " \"%s.%s\"", PQgetvalue(res, i, 0),
6545 : PQgetvalue(res, i, 1));
6546 :
6547 222 : if (!PQgetisnull(res, i, 3))
6548 42 : appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
6549 :
6550 222 : if (!PQgetisnull(res, i, 2))
6551 54 : appendPQExpBuffer(buf, " WHERE %s", PQgetvalue(res, i, 2));
6552 : }
6553 :
6554 456 : printTableAddFooter(cont, buf->data);
6555 : }
6556 :
6557 636 : PQclear(res);
6558 636 : return true;
6559 : }
6560 :
6561 : /*
6562 : * \dRp+
6563 : * Describes publications including the contents.
6564 : *
6565 : * Takes an optional regexp to select particular publications
6566 : */
6567 : bool
6568 342 : describePublications(const char *pattern)
6569 : {
6570 : PQExpBufferData buf;
6571 : int i;
6572 : PGresult *res;
6573 : bool has_pubtruncate;
6574 : bool has_pubgencols;
6575 : bool has_pubviaroot;
6576 : bool has_pubsequence;
6577 :
6578 : PQExpBufferData title;
6579 : printTableContent cont;
6580 :
6581 342 : if (pset.sversion < 100000)
6582 : {
6583 : char sverbuf[32];
6584 :
6585 0 : pg_log_error("The server (version %s) does not support publications.",
6586 : formatPGVersionNumber(pset.sversion, false,
6587 : sverbuf, sizeof(sverbuf)));
6588 0 : return true;
6589 : }
6590 :
6591 342 : has_pubsequence = (pset.sversion >= 190000);
6592 342 : has_pubtruncate = (pset.sversion >= 110000);
6593 342 : has_pubgencols = (pset.sversion >= 180000);
6594 342 : has_pubviaroot = (pset.sversion >= 130000);
6595 :
6596 342 : initPQExpBuffer(&buf);
6597 :
6598 342 : printfPQExpBuffer(&buf,
6599 : "SELECT oid, pubname,\n"
6600 : " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
6601 : " puballtables");
6602 :
6603 342 : if (has_pubsequence)
6604 342 : appendPQExpBufferStr(&buf,
6605 : ", puballsequences");
6606 : else
6607 0 : appendPQExpBufferStr(&buf,
6608 : ", false AS puballsequences");
6609 :
6610 342 : appendPQExpBufferStr(&buf,
6611 : ", pubinsert, pubupdate, pubdelete");
6612 :
6613 342 : if (has_pubtruncate)
6614 342 : appendPQExpBufferStr(&buf,
6615 : ", pubtruncate");
6616 : else
6617 0 : appendPQExpBufferStr(&buf,
6618 : ", false AS pubtruncate");
6619 :
6620 342 : if (has_pubgencols)
6621 342 : appendPQExpBuffer(&buf,
6622 : ", (CASE pubgencols\n"
6623 : " WHEN '%c' THEN 'none'\n"
6624 : " WHEN '%c' THEN 'stored'\n"
6625 : " END) AS \"%s\"\n",
6626 : PUBLISH_GENCOLS_NONE,
6627 : PUBLISH_GENCOLS_STORED,
6628 : gettext_noop("Generated columns"));
6629 : else
6630 0 : appendPQExpBufferStr(&buf,
6631 : ", 'none' AS pubgencols");
6632 :
6633 342 : if (has_pubviaroot)
6634 342 : appendPQExpBufferStr(&buf,
6635 : ", pubviaroot");
6636 : else
6637 0 : appendPQExpBufferStr(&buf,
6638 : ", false AS pubviaroot");
6639 :
6640 342 : appendPQExpBufferStr(&buf,
6641 : "\nFROM pg_catalog.pg_publication\n");
6642 :
6643 342 : if (!validateSQLNamePattern(&buf, pattern, false, false,
6644 : NULL, "pubname", NULL,
6645 : NULL,
6646 : NULL, 1))
6647 : {
6648 0 : termPQExpBuffer(&buf);
6649 0 : return false;
6650 : }
6651 :
6652 342 : appendPQExpBufferStr(&buf, "ORDER BY 2;");
6653 :
6654 342 : res = PSQLexec(buf.data);
6655 342 : if (!res)
6656 : {
6657 0 : termPQExpBuffer(&buf);
6658 0 : return false;
6659 : }
6660 :
6661 342 : if (PQntuples(res) == 0)
6662 : {
6663 0 : if (!pset.quiet)
6664 : {
6665 0 : if (pattern)
6666 0 : pg_log_error("Did not find any publication named \"%s\".",
6667 : pattern);
6668 : else
6669 0 : pg_log_error("Did not find any publications.");
6670 : }
6671 :
6672 0 : termPQExpBuffer(&buf);
6673 0 : PQclear(res);
6674 0 : return false;
6675 : }
6676 :
6677 684 : for (i = 0; i < PQntuples(res); i++)
6678 : {
6679 342 : const char align = 'l';
6680 342 : int ncols = 5;
6681 342 : int nrows = 1;
6682 342 : char *pubid = PQgetvalue(res, i, 0);
6683 342 : char *pubname = PQgetvalue(res, i, 1);
6684 342 : bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
6685 342 : printTableOpt myopt = pset.popt.topt;
6686 :
6687 342 : if (has_pubsequence)
6688 342 : ncols++;
6689 342 : if (has_pubtruncate)
6690 342 : ncols++;
6691 342 : if (has_pubgencols)
6692 342 : ncols++;
6693 342 : if (has_pubviaroot)
6694 342 : ncols++;
6695 :
6696 342 : initPQExpBuffer(&title);
6697 342 : printfPQExpBuffer(&title, _("Publication %s"), pubname);
6698 342 : printTableInit(&cont, &myopt, title.data, ncols, nrows);
6699 :
6700 342 : printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
6701 342 : printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
6702 342 : if (has_pubsequence)
6703 342 : printTableAddHeader(&cont, gettext_noop("All sequences"), true, align);
6704 342 : printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
6705 342 : printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
6706 342 : printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
6707 342 : if (has_pubtruncate)
6708 342 : printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
6709 342 : if (has_pubgencols)
6710 342 : printTableAddHeader(&cont, gettext_noop("Generated columns"), true, align);
6711 342 : if (has_pubviaroot)
6712 342 : printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
6713 :
6714 342 : printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
6715 342 : printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
6716 342 : if (has_pubsequence)
6717 342 : printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
6718 342 : printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
6719 342 : printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
6720 342 : printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
6721 342 : if (has_pubtruncate)
6722 342 : printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
6723 342 : if (has_pubgencols)
6724 342 : printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
6725 342 : if (has_pubviaroot)
6726 342 : printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false);
6727 :
6728 342 : if (!puballtables)
6729 : {
6730 : /* Get the tables for the specified publication */
6731 318 : printfPQExpBuffer(&buf,
6732 : "SELECT n.nspname, c.relname");
6733 318 : if (pset.sversion >= 150000)
6734 : {
6735 318 : appendPQExpBufferStr(&buf,
6736 : ", pg_get_expr(pr.prqual, c.oid)");
6737 318 : appendPQExpBufferStr(&buf,
6738 : ", (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
6739 : " pg_catalog.array_to_string("
6740 : " ARRAY(SELECT attname\n"
6741 : " FROM\n"
6742 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
6743 : " pg_catalog.pg_attribute\n"
6744 : " WHERE attrelid = c.oid AND attnum = prattrs[s]), ', ')\n"
6745 : " ELSE NULL END)");
6746 : }
6747 : else
6748 0 : appendPQExpBufferStr(&buf,
6749 : ", NULL, NULL");
6750 318 : appendPQExpBuffer(&buf,
6751 : "\nFROM pg_catalog.pg_class c,\n"
6752 : " pg_catalog.pg_namespace n,\n"
6753 : " pg_catalog.pg_publication_rel pr\n"
6754 : "WHERE c.relnamespace = n.oid\n"
6755 : " AND c.oid = pr.prrelid\n"
6756 : " AND pr.prpubid = '%s'\n"
6757 : "ORDER BY 1,2", pubid);
6758 318 : if (!addFooterToPublicationDesc(&buf, _("Tables:"), false, &cont))
6759 0 : goto error_return;
6760 :
6761 318 : if (pset.sversion >= 150000)
6762 : {
6763 : /* Get the schemas for the specified publication */
6764 318 : printfPQExpBuffer(&buf,
6765 : "SELECT n.nspname\n"
6766 : "FROM pg_catalog.pg_namespace n\n"
6767 : " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
6768 : "WHERE pn.pnpubid = '%s'\n"
6769 : "ORDER BY 1", pubid);
6770 318 : if (!addFooterToPublicationDesc(&buf, _("Tables from schemas:"),
6771 : true, &cont))
6772 0 : goto error_return;
6773 : }
6774 : }
6775 :
6776 342 : printTable(&cont, pset.queryFout, false, pset.logfile);
6777 342 : printTableCleanup(&cont);
6778 :
6779 342 : termPQExpBuffer(&title);
6780 : }
6781 :
6782 342 : termPQExpBuffer(&buf);
6783 342 : PQclear(res);
6784 :
6785 342 : return true;
6786 :
6787 0 : error_return:
6788 0 : printTableCleanup(&cont);
6789 0 : PQclear(res);
6790 0 : termPQExpBuffer(&buf);
6791 0 : termPQExpBuffer(&title);
6792 0 : return false;
6793 : }
6794 :
6795 : /*
6796 : * \dRs
6797 : * Describes subscriptions.
6798 : *
6799 : * Takes an optional regexp to select particular subscriptions
6800 : */
6801 : bool
6802 168 : describeSubscriptions(const char *pattern, bool verbose)
6803 : {
6804 : PQExpBufferData buf;
6805 : PGresult *res;
6806 168 : printQueryOpt myopt = pset.popt;
6807 : static const bool translate_columns[] = {false, false, false, false,
6808 : false, false, false, false, false, false, false, false, false, false,
6809 : false, false, false, false};
6810 :
6811 168 : if (pset.sversion < 100000)
6812 : {
6813 : char sverbuf[32];
6814 :
6815 0 : pg_log_error("The server (version %s) does not support subscriptions.",
6816 : formatPGVersionNumber(pset.sversion, false,
6817 : sverbuf, sizeof(sverbuf)));
6818 0 : return true;
6819 : }
6820 :
6821 168 : initPQExpBuffer(&buf);
6822 :
6823 168 : printfPQExpBuffer(&buf,
6824 : "SELECT subname AS \"%s\"\n"
6825 : ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
6826 : ", subenabled AS \"%s\"\n"
6827 : ", subpublications AS \"%s\"\n",
6828 : gettext_noop("Name"),
6829 : gettext_noop("Owner"),
6830 : gettext_noop("Enabled"),
6831 : gettext_noop("Publication"));
6832 :
6833 168 : if (verbose)
6834 : {
6835 : /* Binary mode and streaming are only supported in v14 and higher */
6836 132 : if (pset.sversion >= 140000)
6837 : {
6838 132 : appendPQExpBuffer(&buf,
6839 : ", subbinary AS \"%s\"\n",
6840 : gettext_noop("Binary"));
6841 :
6842 132 : if (pset.sversion >= 160000)
6843 132 : appendPQExpBuffer(&buf,
6844 : ", (CASE substream\n"
6845 : " WHEN " CppAsString2(LOGICALREP_STREAM_OFF) " THEN 'off'\n"
6846 : " WHEN " CppAsString2(LOGICALREP_STREAM_ON) " THEN 'on'\n"
6847 : " WHEN " CppAsString2(LOGICALREP_STREAM_PARALLEL) " THEN 'parallel'\n"
6848 : " END) AS \"%s\"\n",
6849 : gettext_noop("Streaming"));
6850 : else
6851 0 : appendPQExpBuffer(&buf,
6852 : ", substream AS \"%s\"\n",
6853 : gettext_noop("Streaming"));
6854 : }
6855 :
6856 : /* Two_phase and disable_on_error are only supported in v15 and higher */
6857 132 : if (pset.sversion >= 150000)
6858 132 : appendPQExpBuffer(&buf,
6859 : ", subtwophasestate AS \"%s\"\n"
6860 : ", subdisableonerr AS \"%s\"\n",
6861 : gettext_noop("Two-phase commit"),
6862 : gettext_noop("Disable on error"));
6863 :
6864 132 : if (pset.sversion >= 160000)
6865 132 : appendPQExpBuffer(&buf,
6866 : ", suborigin AS \"%s\"\n"
6867 : ", subpasswordrequired AS \"%s\"\n"
6868 : ", subrunasowner AS \"%s\"\n",
6869 : gettext_noop("Origin"),
6870 : gettext_noop("Password required"),
6871 : gettext_noop("Run as owner?"));
6872 :
6873 132 : if (pset.sversion >= 170000)
6874 132 : appendPQExpBuffer(&buf,
6875 : ", subfailover AS \"%s\"\n",
6876 : gettext_noop("Failover"));
6877 132 : if (pset.sversion >= 190000)
6878 : {
6879 132 : appendPQExpBuffer(&buf,
6880 : ", subretaindeadtuples AS \"%s\"\n",
6881 : gettext_noop("Retain dead tuples"));
6882 :
6883 132 : appendPQExpBuffer(&buf,
6884 : ", submaxretention AS \"%s\"\n",
6885 : gettext_noop("Max retention duration"));
6886 :
6887 132 : appendPQExpBuffer(&buf,
6888 : ", subretentionactive AS \"%s\"\n",
6889 : gettext_noop("Retention active"));
6890 : }
6891 :
6892 132 : appendPQExpBuffer(&buf,
6893 : ", subsynccommit AS \"%s\"\n"
6894 : ", subconninfo AS \"%s\"\n",
6895 : gettext_noop("Synchronous commit"),
6896 : gettext_noop("Conninfo"));
6897 :
6898 : /* Skip LSN is only supported in v15 and higher */
6899 132 : if (pset.sversion >= 150000)
6900 132 : appendPQExpBuffer(&buf,
6901 : ", subskiplsn AS \"%s\"\n",
6902 : gettext_noop("Skip LSN"));
6903 : }
6904 :
6905 : /* Only display subscriptions in current database. */
6906 168 : appendPQExpBufferStr(&buf,
6907 : "FROM pg_catalog.pg_subscription\n"
6908 : "WHERE subdbid = (SELECT oid\n"
6909 : " FROM pg_catalog.pg_database\n"
6910 : " WHERE datname = pg_catalog.current_database())");
6911 :
6912 168 : if (!validateSQLNamePattern(&buf, pattern, true, false,
6913 : NULL, "subname", NULL,
6914 : NULL,
6915 : NULL, 1))
6916 : {
6917 18 : termPQExpBuffer(&buf);
6918 18 : return false;
6919 : }
6920 :
6921 150 : appendPQExpBufferStr(&buf, "ORDER BY 1;");
6922 :
6923 150 : res = PSQLexec(buf.data);
6924 150 : termPQExpBuffer(&buf);
6925 150 : if (!res)
6926 0 : return false;
6927 :
6928 150 : myopt.title = _("List of subscriptions");
6929 150 : myopt.translate_header = true;
6930 150 : myopt.translate_columns = translate_columns;
6931 150 : myopt.n_translate_columns = lengthof(translate_columns);
6932 :
6933 150 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6934 :
6935 150 : PQclear(res);
6936 150 : return true;
6937 : }
6938 :
6939 : /*
6940 : * printACLColumn
6941 : *
6942 : * Helper function for consistently formatting ACL (privilege) columns.
6943 : * The proper targetlist entry is appended to buf. Note lack of any
6944 : * whitespace or comma decoration.
6945 : *
6946 : * If you change this, see also the handling of attacl in permissionsList(),
6947 : * which can't conveniently use this code.
6948 : */
6949 : static void
6950 306 : printACLColumn(PQExpBuffer buf, const char *colname)
6951 : {
6952 306 : appendPQExpBuffer(buf,
6953 : "CASE"
6954 : " WHEN pg_catalog.array_length(%s, 1) = 0 THEN '%s'"
6955 : " ELSE pg_catalog.array_to_string(%s, E'\\n')"
6956 : " END AS \"%s\"",
6957 : colname, gettext_noop("(none)"),
6958 : colname, gettext_noop("Access privileges"));
6959 306 : }
6960 :
6961 : /*
6962 : * \dAc
6963 : * Lists operator classes
6964 : *
6965 : * Takes optional regexps to filter by index access method and input data type.
6966 : */
6967 : bool
6968 30 : listOperatorClasses(const char *access_method_pattern,
6969 : const char *type_pattern, bool verbose)
6970 : {
6971 : PQExpBufferData buf;
6972 : PGresult *res;
6973 30 : printQueryOpt myopt = pset.popt;
6974 30 : bool have_where = false;
6975 : static const bool translate_columns[] = {false, false, false, false, false, false, false};
6976 :
6977 30 : initPQExpBuffer(&buf);
6978 :
6979 30 : printfPQExpBuffer(&buf,
6980 : "SELECT\n"
6981 : " am.amname AS \"%s\",\n"
6982 : " pg_catalog.format_type(c.opcintype, NULL) AS \"%s\",\n"
6983 : " CASE\n"
6984 : " WHEN c.opckeytype <> 0 AND c.opckeytype <> c.opcintype\n"
6985 : " THEN pg_catalog.format_type(c.opckeytype, NULL)\n"
6986 : " ELSE NULL\n"
6987 : " END AS \"%s\",\n"
6988 : " CASE\n"
6989 : " WHEN pg_catalog.pg_opclass_is_visible(c.oid)\n"
6990 : " THEN pg_catalog.format('%%I', c.opcname)\n"
6991 : " ELSE pg_catalog.format('%%I.%%I', n.nspname, c.opcname)\n"
6992 : " END AS \"%s\",\n"
6993 : " (CASE WHEN c.opcdefault\n"
6994 : " THEN '%s'\n"
6995 : " ELSE '%s'\n"
6996 : " END) AS \"%s\"",
6997 : gettext_noop("AM"),
6998 : gettext_noop("Input type"),
6999 : gettext_noop("Storage type"),
7000 : gettext_noop("Operator class"),
7001 : gettext_noop("yes"),
7002 : gettext_noop("no"),
7003 : gettext_noop("Default?"));
7004 30 : if (verbose)
7005 0 : appendPQExpBuffer(&buf,
7006 : ",\n CASE\n"
7007 : " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7008 : " THEN pg_catalog.format('%%I', of.opfname)\n"
7009 : " ELSE pg_catalog.format('%%I.%%I', ofn.nspname, of.opfname)\n"
7010 : " END AS \"%s\",\n"
7011 : " pg_catalog.pg_get_userbyid(c.opcowner) AS \"%s\"\n",
7012 : gettext_noop("Operator family"),
7013 : gettext_noop("Owner"));
7014 30 : appendPQExpBufferStr(&buf,
7015 : "\nFROM pg_catalog.pg_opclass c\n"
7016 : " LEFT JOIN pg_catalog.pg_am am on am.oid = c.opcmethod\n"
7017 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace\n"
7018 : " LEFT JOIN pg_catalog.pg_type t ON t.oid = c.opcintype\n"
7019 : " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n");
7020 30 : if (verbose)
7021 0 : appendPQExpBufferStr(&buf,
7022 : " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = c.opcfamily\n"
7023 : " LEFT JOIN pg_catalog.pg_namespace ofn ON ofn.oid = of.opfnamespace\n");
7024 :
7025 30 : if (access_method_pattern)
7026 30 : if (!validateSQLNamePattern(&buf, access_method_pattern,
7027 : false, false, NULL, "am.amname", NULL, NULL,
7028 : &have_where, 1))
7029 18 : goto error_return;
7030 12 : if (type_pattern)
7031 : {
7032 : /* Match type name pattern against either internal or external name */
7033 6 : if (!validateSQLNamePattern(&buf, type_pattern, have_where, false,
7034 : "tn.nspname", "t.typname",
7035 : "pg_catalog.format_type(t.oid, NULL)",
7036 : "pg_catalog.pg_type_is_visible(t.oid)",
7037 : NULL, 3))
7038 0 : goto error_return;
7039 : }
7040 :
7041 12 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
7042 12 : res = PSQLexec(buf.data);
7043 12 : termPQExpBuffer(&buf);
7044 12 : if (!res)
7045 0 : return false;
7046 :
7047 12 : myopt.title = _("List of operator classes");
7048 12 : myopt.translate_header = true;
7049 12 : myopt.translate_columns = translate_columns;
7050 12 : myopt.n_translate_columns = lengthof(translate_columns);
7051 :
7052 12 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7053 :
7054 12 : PQclear(res);
7055 12 : return true;
7056 :
7057 18 : error_return:
7058 18 : termPQExpBuffer(&buf);
7059 18 : return false;
7060 : }
7061 :
7062 : /*
7063 : * \dAf
7064 : * Lists operator families
7065 : *
7066 : * Takes optional regexps to filter by index access method and input data type.
7067 : */
7068 : bool
7069 36 : listOperatorFamilies(const char *access_method_pattern,
7070 : const char *type_pattern, bool verbose)
7071 : {
7072 : PQExpBufferData buf;
7073 : PGresult *res;
7074 36 : printQueryOpt myopt = pset.popt;
7075 36 : bool have_where = false;
7076 : static const bool translate_columns[] = {false, false, false, false};
7077 :
7078 36 : initPQExpBuffer(&buf);
7079 :
7080 36 : printfPQExpBuffer(&buf,
7081 : "SELECT\n"
7082 : " am.amname AS \"%s\",\n"
7083 : " CASE\n"
7084 : " WHEN pg_catalog.pg_opfamily_is_visible(f.oid)\n"
7085 : " THEN pg_catalog.format('%%I', f.opfname)\n"
7086 : " ELSE pg_catalog.format('%%I.%%I', n.nspname, f.opfname)\n"
7087 : " END AS \"%s\",\n"
7088 : " (SELECT\n"
7089 : " pg_catalog.string_agg(pg_catalog.format_type(oc.opcintype, NULL), ', ')\n"
7090 : " FROM pg_catalog.pg_opclass oc\n"
7091 : " WHERE oc.opcfamily = f.oid) \"%s\"",
7092 : gettext_noop("AM"),
7093 : gettext_noop("Operator family"),
7094 : gettext_noop("Applicable types"));
7095 36 : if (verbose)
7096 0 : appendPQExpBuffer(&buf,
7097 : ",\n pg_catalog.pg_get_userbyid(f.opfowner) AS \"%s\"\n",
7098 : gettext_noop("Owner"));
7099 36 : appendPQExpBufferStr(&buf,
7100 : "\nFROM pg_catalog.pg_opfamily f\n"
7101 : " LEFT JOIN pg_catalog.pg_am am on am.oid = f.opfmethod\n"
7102 : " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace\n");
7103 :
7104 36 : if (access_method_pattern)
7105 36 : if (!validateSQLNamePattern(&buf, access_method_pattern,
7106 : false, false, NULL, "am.amname", NULL, NULL,
7107 : &have_where, 1))
7108 18 : goto error_return;
7109 18 : if (type_pattern)
7110 : {
7111 6 : appendPQExpBuffer(&buf,
7112 : " %s EXISTS (\n"
7113 : " SELECT 1\n"
7114 : " FROM pg_catalog.pg_type t\n"
7115 : " JOIN pg_catalog.pg_opclass oc ON oc.opcintype = t.oid\n"
7116 : " LEFT JOIN pg_catalog.pg_namespace tn ON tn.oid = t.typnamespace\n"
7117 : " WHERE oc.opcfamily = f.oid\n",
7118 6 : have_where ? "AND" : "WHERE");
7119 : /* Match type name pattern against either internal or external name */
7120 6 : if (!validateSQLNamePattern(&buf, type_pattern, true, false,
7121 : "tn.nspname", "t.typname",
7122 : "pg_catalog.format_type(t.oid, NULL)",
7123 : "pg_catalog.pg_type_is_visible(t.oid)",
7124 : NULL, 3))
7125 0 : goto error_return;
7126 6 : appendPQExpBufferStr(&buf, " )\n");
7127 : }
7128 :
7129 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
7130 18 : res = PSQLexec(buf.data);
7131 18 : termPQExpBuffer(&buf);
7132 18 : if (!res)
7133 0 : return false;
7134 :
7135 18 : myopt.title = _("List of operator families");
7136 18 : myopt.translate_header = true;
7137 18 : myopt.translate_columns = translate_columns;
7138 18 : myopt.n_translate_columns = lengthof(translate_columns);
7139 :
7140 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7141 :
7142 18 : PQclear(res);
7143 18 : return true;
7144 :
7145 18 : error_return:
7146 18 : termPQExpBuffer(&buf);
7147 18 : return false;
7148 : }
7149 :
7150 : /*
7151 : * \dAo
7152 : * Lists operators of operator families
7153 : *
7154 : * Takes optional regexps to filter by index access method and operator
7155 : * family.
7156 : */
7157 : bool
7158 36 : listOpFamilyOperators(const char *access_method_pattern,
7159 : const char *family_pattern, bool verbose)
7160 : {
7161 : PQExpBufferData buf;
7162 : PGresult *res;
7163 36 : printQueryOpt myopt = pset.popt;
7164 36 : bool have_where = false;
7165 :
7166 : static const bool translate_columns[] = {false, false, false, false, false, false, true};
7167 :
7168 36 : initPQExpBuffer(&buf);
7169 :
7170 36 : printfPQExpBuffer(&buf,
7171 : "SELECT\n"
7172 : " am.amname AS \"%s\",\n"
7173 : " CASE\n"
7174 : " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7175 : " THEN pg_catalog.format('%%I', of.opfname)\n"
7176 : " ELSE pg_catalog.format('%%I.%%I', nsf.nspname, of.opfname)\n"
7177 : " END AS \"%s\",\n"
7178 : " o.amopopr::pg_catalog.regoperator AS \"%s\"\n,"
7179 : " o.amopstrategy AS \"%s\",\n"
7180 : " CASE o.amoppurpose\n"
7181 : " WHEN " CppAsString2(AMOP_ORDER) " THEN '%s'\n"
7182 : " WHEN " CppAsString2(AMOP_SEARCH) " THEN '%s'\n"
7183 : " END AS \"%s\"\n",
7184 : gettext_noop("AM"),
7185 : gettext_noop("Operator family"),
7186 : gettext_noop("Operator"),
7187 : gettext_noop("Strategy"),
7188 : gettext_noop("ordering"),
7189 : gettext_noop("search"),
7190 : gettext_noop("Purpose"));
7191 :
7192 36 : if (verbose)
7193 6 : appendPQExpBuffer(&buf,
7194 : ", ofs.opfname AS \"%s\",\n"
7195 : " CASE\n"
7196 : " WHEN p.proleakproof THEN '%s'\n"
7197 : " ELSE '%s'\n"
7198 : " END AS \"%s\"\n",
7199 : gettext_noop("Sort opfamily"),
7200 : gettext_noop("yes"),
7201 : gettext_noop("no"),
7202 : gettext_noop("Leakproof?"));
7203 36 : appendPQExpBufferStr(&buf,
7204 : "FROM pg_catalog.pg_amop o\n"
7205 : " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = o.amopfamily\n"
7206 : " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod AND am.oid = o.amopmethod\n"
7207 : " LEFT JOIN pg_catalog.pg_namespace nsf ON of.opfnamespace = nsf.oid\n");
7208 36 : if (verbose)
7209 6 : appendPQExpBufferStr(&buf,
7210 : " LEFT JOIN pg_catalog.pg_opfamily ofs ON ofs.oid = o.amopsortfamily\n"
7211 : " LEFT JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr\n"
7212 : " LEFT JOIN pg_catalog.pg_proc p ON p.oid = op.oprcode\n");
7213 :
7214 36 : if (access_method_pattern)
7215 : {
7216 36 : if (!validateSQLNamePattern(&buf, access_method_pattern,
7217 : false, false, NULL, "am.amname",
7218 : NULL, NULL,
7219 : &have_where, 1))
7220 18 : goto error_return;
7221 : }
7222 :
7223 18 : if (family_pattern)
7224 : {
7225 12 : if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
7226 : "nsf.nspname", "of.opfname", NULL, NULL,
7227 : NULL, 3))
7228 0 : goto error_return;
7229 : }
7230 :
7231 18 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7232 : " o.amoplefttype = o.amoprighttype DESC,\n"
7233 : " pg_catalog.format_type(o.amoplefttype, NULL),\n"
7234 : " pg_catalog.format_type(o.amoprighttype, NULL),\n"
7235 : " o.amopstrategy;");
7236 :
7237 18 : res = PSQLexec(buf.data);
7238 18 : termPQExpBuffer(&buf);
7239 18 : if (!res)
7240 0 : return false;
7241 :
7242 18 : myopt.title = _("List of operators of operator families");
7243 18 : myopt.translate_header = true;
7244 18 : myopt.translate_columns = translate_columns;
7245 18 : myopt.n_translate_columns = lengthof(translate_columns);
7246 :
7247 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7248 :
7249 18 : PQclear(res);
7250 18 : return true;
7251 :
7252 18 : error_return:
7253 18 : termPQExpBuffer(&buf);
7254 18 : return false;
7255 : }
7256 :
7257 : /*
7258 : * \dAp
7259 : * Lists support functions of operator families
7260 : *
7261 : * Takes optional regexps to filter by index access method and operator
7262 : * family.
7263 : */
7264 : bool
7265 42 : listOpFamilyFunctions(const char *access_method_pattern,
7266 : const char *family_pattern, bool verbose)
7267 : {
7268 : PQExpBufferData buf;
7269 : PGresult *res;
7270 42 : printQueryOpt myopt = pset.popt;
7271 42 : bool have_where = false;
7272 : static const bool translate_columns[] = {false, false, false, false, false, false};
7273 :
7274 42 : initPQExpBuffer(&buf);
7275 :
7276 42 : printfPQExpBuffer(&buf,
7277 : "SELECT\n"
7278 : " am.amname AS \"%s\",\n"
7279 : " CASE\n"
7280 : " WHEN pg_catalog.pg_opfamily_is_visible(of.oid)\n"
7281 : " THEN pg_catalog.format('%%I', of.opfname)\n"
7282 : " ELSE pg_catalog.format('%%I.%%I', ns.nspname, of.opfname)\n"
7283 : " END AS \"%s\",\n"
7284 : " pg_catalog.format_type(ap.amproclefttype, NULL) AS \"%s\",\n"
7285 : " pg_catalog.format_type(ap.amprocrighttype, NULL) AS \"%s\",\n"
7286 : " ap.amprocnum AS \"%s\"\n",
7287 : gettext_noop("AM"),
7288 : gettext_noop("Operator family"),
7289 : gettext_noop("Registered left type"),
7290 : gettext_noop("Registered right type"),
7291 : gettext_noop("Number"));
7292 :
7293 42 : if (!verbose)
7294 30 : appendPQExpBuffer(&buf,
7295 : ", p.proname AS \"%s\"\n",
7296 : gettext_noop("Function"));
7297 : else
7298 12 : appendPQExpBuffer(&buf,
7299 : ", ap.amproc::pg_catalog.regprocedure AS \"%s\"\n",
7300 : gettext_noop("Function"));
7301 :
7302 42 : appendPQExpBufferStr(&buf,
7303 : "FROM pg_catalog.pg_amproc ap\n"
7304 : " LEFT JOIN pg_catalog.pg_opfamily of ON of.oid = ap.amprocfamily\n"
7305 : " LEFT JOIN pg_catalog.pg_am am ON am.oid = of.opfmethod\n"
7306 : " LEFT JOIN pg_catalog.pg_namespace ns ON of.opfnamespace = ns.oid\n"
7307 : " LEFT JOIN pg_catalog.pg_proc p ON ap.amproc = p.oid\n");
7308 :
7309 42 : if (access_method_pattern)
7310 : {
7311 42 : if (!validateSQLNamePattern(&buf, access_method_pattern,
7312 : false, false, NULL, "am.amname",
7313 : NULL, NULL,
7314 : &have_where, 1))
7315 18 : goto error_return;
7316 : }
7317 24 : if (family_pattern)
7318 : {
7319 18 : if (!validateSQLNamePattern(&buf, family_pattern, have_where, false,
7320 : "ns.nspname", "of.opfname", NULL, NULL,
7321 : NULL, 3))
7322 0 : goto error_return;
7323 : }
7324 :
7325 24 : appendPQExpBufferStr(&buf, "ORDER BY 1, 2,\n"
7326 : " ap.amproclefttype = ap.amprocrighttype DESC,\n"
7327 : " 3, 4, 5;");
7328 :
7329 24 : res = PSQLexec(buf.data);
7330 24 : termPQExpBuffer(&buf);
7331 24 : if (!res)
7332 0 : return false;
7333 :
7334 24 : myopt.title = _("List of support functions of operator families");
7335 24 : myopt.translate_header = true;
7336 24 : myopt.translate_columns = translate_columns;
7337 24 : myopt.n_translate_columns = lengthof(translate_columns);
7338 :
7339 24 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7340 :
7341 24 : PQclear(res);
7342 24 : return true;
7343 :
7344 18 : error_return:
7345 18 : termPQExpBuffer(&buf);
7346 18 : return false;
7347 : }
7348 :
7349 : /*
7350 : * \dl or \lo_list
7351 : * Lists large objects
7352 : */
7353 : bool
7354 18 : listLargeObjects(bool verbose)
7355 : {
7356 : PQExpBufferData buf;
7357 : PGresult *res;
7358 18 : printQueryOpt myopt = pset.popt;
7359 :
7360 18 : initPQExpBuffer(&buf);
7361 :
7362 18 : printfPQExpBuffer(&buf,
7363 : "SELECT oid as \"%s\",\n"
7364 : " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n ",
7365 : gettext_noop("ID"),
7366 : gettext_noop("Owner"));
7367 :
7368 18 : if (verbose)
7369 : {
7370 6 : printACLColumn(&buf, "lomacl");
7371 6 : appendPQExpBufferStr(&buf, ",\n ");
7372 : }
7373 :
7374 18 : appendPQExpBuffer(&buf,
7375 : "pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
7376 : "FROM pg_catalog.pg_largeobject_metadata\n"
7377 : "ORDER BY oid",
7378 : gettext_noop("Description"));
7379 :
7380 18 : res = PSQLexec(buf.data);
7381 18 : termPQExpBuffer(&buf);
7382 18 : if (!res)
7383 0 : return false;
7384 :
7385 18 : myopt.title = _("Large objects");
7386 18 : myopt.translate_header = true;
7387 :
7388 18 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
7389 :
7390 18 : PQclear(res);
7391 18 : return true;
7392 : }
|