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