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