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