YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

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