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