~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/bin/psql/describe.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * psql - the PostgreSQL interactive terminal
 
3
 *
 
4
 * Copyright (c) 2000-2005, PostgreSQL Global Development Group
 
5
 *
 
6
 * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.111 2005-01-01 05:43:08 momjian Exp $
 
7
 */
 
8
#include "postgres_fe.h"
 
9
#include "describe.h"
 
10
 
 
11
#include "libpq-fe.h"
 
12
#include "pqexpbuffer.h"
 
13
 
 
14
#include "common.h"
 
15
#include "settings.h"
 
16
#include "print.h"
 
17
#include "variables.h"
 
18
 
 
19
#include <ctype.h>
 
20
 
 
21
#ifdef WIN32
 
22
/*
 
23
 * mbvalidate() is used in function describeOneTableDetails() to make sure
 
24
 * all characters of the cells will be printed to the DOS console in a
 
25
 * correct way
 
26
 */
 
27
#include "mbprint.h"
 
28
#endif
 
29
 
 
30
 
 
31
#define _(x) gettext((x))
 
32
 
 
33
static bool describeOneTableDetails(const char *schemaname,
 
34
                                                const char *relationname,
 
35
                                                const char *oid,
 
36
                                                bool verbose);
 
37
static void processNamePattern(PQExpBuffer buf, const char *pattern,
 
38
                                   bool have_where, bool force_escape,
 
39
                                   const char *schemavar, const char *namevar,
 
40
                                   const char *altnamevar, const char *visibilityrule);
 
41
 
 
42
static void add_tablespace_footer(char relkind, Oid tablespace,
 
43
                                          char **footers, int *count, PQExpBufferData buf);
 
44
 
 
45
/*----------------
 
46
 * Handlers for various slash commands displaying some sort of list
 
47
 * of things in the database.
 
48
 *
 
49
 * If you add something here, try to format the query to look nice in -E output.
 
50
 *----------------
 
51
 */
 
52
 
 
53
 
 
54
/* \da
 
55
 * Takes an optional regexp to select particular aggregates
 
56
 */
 
57
bool
 
58
describeAggregates(const char *pattern, bool verbose)
 
59
{
 
60
        PQExpBufferData buf;
 
61
        PGresult   *res;
 
62
        printQueryOpt myopt = pset.popt;
 
63
 
 
64
        initPQExpBuffer(&buf);
 
65
 
 
66
        /*
 
67
         * There are two kinds of aggregates: ones that work on particular
 
68
         * types and ones that work on all (denoted by input type = "any")
 
69
         */
 
70
        printfPQExpBuffer(&buf,
 
71
                                          "SELECT n.nspname as \"%s\",\n"
 
72
                                          "  p.proname AS \"%s\",\n"
 
73
                                          "  CASE p.proargtypes[0]\n"
 
74
                                        "    WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype\n"
 
75
                                          "    THEN CAST('%s' AS pg_catalog.text)\n"
 
76
                          "    ELSE pg_catalog.format_type(p.proargtypes[0], NULL)\n"
 
77
                                          "  END AS \"%s\",\n"
 
78
                         "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
 
79
                                          "FROM pg_catalog.pg_proc p\n"
 
80
        "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
 
81
                                          "WHERE p.proisagg\n",
 
82
                                          _("Schema"), _("Name"), _("(all types)"),
 
83
                                          _("Data type"), _("Description"));
 
84
 
 
85
        processNamePattern(&buf, pattern, true, false,
 
86
                                           "n.nspname", "p.proname", NULL,
 
87
                                           "pg_catalog.pg_function_is_visible(p.oid)");
 
88
 
 
89
        appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
 
90
 
 
91
        res = PSQLexec(buf.data, false);
 
92
        termPQExpBuffer(&buf);
 
93
        if (!res)
 
94
                return false;
 
95
 
 
96
        myopt.nullPrint = NULL;
 
97
        myopt.title = _("List of aggregate functions");
 
98
 
 
99
        printQuery(res, &myopt, pset.queryFout);
 
100
 
 
101
        PQclear(res);
 
102
        return true;
 
103
}
 
104
 
 
105
/* \db
 
106
 * Takes an optional regexp to select particular tablespaces
 
107
 */
 
108
bool
 
109
describeTablespaces(const char *pattern, bool verbose)
 
110
{
 
111
        PQExpBufferData buf;
 
112
        PGresult   *res;
 
113
        printQueryOpt myopt = pset.popt;
 
114
 
 
115
        if (pset.sversion < 80000)
 
116
        {
 
117
                fprintf(stderr, _("The server version (%d) does not support tablespaces.\n"),
 
118
                                pset.sversion);
 
119
                return true;
 
120
        }
 
121
 
 
122
        initPQExpBuffer(&buf);
 
123
 
 
124
        printfPQExpBuffer(&buf,
 
125
                                          "SELECT spcname AS \"%s\",\n"
 
126
                                        "  pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n"
 
127
                                          "  spclocation AS \"%s\"",
 
128
                                          _("Name"), _("Owner"), _("Location"));
 
129
 
 
130
        if (verbose)
 
131
                appendPQExpBuffer(&buf,
 
132
                                                  ",\n  spcacl as \"%s\"",
 
133
                                                  _("Access privileges"));
 
134
 
 
135
        appendPQExpBuffer(&buf,
 
136
                                          "\nFROM pg_catalog.pg_tablespace\n");
 
137
 
 
138
        processNamePattern(&buf, pattern, false, false,
 
139
                                           NULL, "spcname", NULL,
 
140
                                           NULL);
 
141
 
 
142
        appendPQExpBuffer(&buf, "ORDER BY 1;");
 
143
 
 
144
        res = PSQLexec(buf.data, false);
 
145
        termPQExpBuffer(&buf);
 
146
        if (!res)
 
147
                return false;
 
148
 
 
149
        myopt.nullPrint = NULL;
 
150
        myopt.title = _("List of tablespaces");
 
151
 
 
152
        printQuery(res, &myopt, pset.queryFout);
 
153
 
 
154
        PQclear(res);
 
155
        return true;
 
156
}
 
157
 
 
158
 
 
159
/* \df
 
160
 * Takes an optional regexp to select particular functions
 
161
 */
 
162
bool
 
163
describeFunctions(const char *pattern, bool verbose)
 
164
{
 
165
        PQExpBufferData buf;
 
166
        PGresult   *res;
 
167
        printQueryOpt myopt = pset.popt;
 
168
 
 
169
        initPQExpBuffer(&buf);
 
170
 
 
171
        printfPQExpBuffer(&buf,
 
172
                                          "SELECT n.nspname as \"%s\",\n"
 
173
                                          "  p.proname as \"%s\",\n"
 
174
                                          "  CASE WHEN p.proretset THEN 'setof ' ELSE '' END ||\n"
 
175
                                          "  pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
 
176
                                          "  pg_catalog.oidvectortypes(p.proargtypes) as \"%s\"",
 
177
                                          _("Schema"), _("Name"), _("Result data type"), 
 
178
                                          _("Argument data types"));
 
179
 
 
180
        if (verbose)
 
181
                appendPQExpBuffer(&buf,
 
182
                                                  ",\n  u.usename as \"%s\",\n"
 
183
                                                  "  l.lanname as \"%s\",\n"
 
184
                                                  "  p.prosrc as \"%s\",\n"
 
185
                          "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"",
 
186
                                                  _("Owner"), _("Language"),
 
187
                                                  _("Source code"), _("Description"));
 
188
 
 
189
        if (!verbose)
 
190
                appendPQExpBuffer(&buf,
 
191
                                                  "\nFROM pg_catalog.pg_proc p"
 
192
                                                  "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
 
193
        else
 
194
                appendPQExpBuffer(&buf,
 
195
                                                  "\nFROM pg_catalog.pg_proc p"
 
196
                                                  "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace"
 
197
                 "\n     LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang"
 
198
                                                  "\n     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner\n");
 
199
 
 
200
        /*
 
201
         * we skip in/out funcs by excluding functions that take or return
 
202
         * cstring
 
203
         */
 
204
        appendPQExpBuffer(&buf,
 
205
           "WHERE p.prorettype <> 'pg_catalog.cstring'::pg_catalog.regtype\n"
 
206
                                          "      AND p.proargtypes[0] <> 'pg_catalog.cstring'::pg_catalog.regtype\n"
 
207
                                          "      AND NOT p.proisagg\n");
 
208
 
 
209
        processNamePattern(&buf, pattern, true, false,
 
210
                                           "n.nspname", "p.proname", NULL,
 
211
                                           "pg_catalog.pg_function_is_visible(p.oid)");
 
212
 
 
213
        appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3, 4;");
 
214
 
 
215
        res = PSQLexec(buf.data, false);
 
216
        termPQExpBuffer(&buf);
 
217
        if (!res)
 
218
                return false;
 
219
 
 
220
        myopt.nullPrint = NULL;
 
221
        myopt.title = _("List of functions");
 
222
 
 
223
        printQuery(res, &myopt, pset.queryFout);
 
224
 
 
225
        PQclear(res);
 
226
        return true;
 
227
}
 
228
 
 
229
 
 
230
 
 
231
/*
 
232
 * \dT
 
233
 * describe types
 
234
 */
 
235
bool
 
236
describeTypes(const char *pattern, bool verbose)
 
237
{
 
238
        PQExpBufferData buf;
 
239
        PGresult   *res;
 
240
        printQueryOpt myopt = pset.popt;
 
241
 
 
242
        initPQExpBuffer(&buf);
 
243
 
 
244
        printfPQExpBuffer(&buf,
 
245
                                          "SELECT n.nspname as \"%s\",\n"
 
246
                                        "  pg_catalog.format_type(t.oid, NULL) AS \"%s\",\n",
 
247
                                          _("Schema"), _("Name"));
 
248
        if (verbose)
 
249
                appendPQExpBuffer(&buf,
 
250
                                                  "  t.typname AS \"%s\",\n"
 
251
                                                  "  CASE WHEN t.typrelid != 0\n"
 
252
                                                  "      THEN CAST('tuple' AS pg_catalog.text)\n"
 
253
                                                  "    WHEN t.typlen < 0\n"
 
254
                                                  "      THEN CAST('var' AS pg_catalog.text)\n"
 
255
                                                  "    ELSE CAST(t.typlen AS pg_catalog.text)\n"
 
256
                                                  "  END AS \"%s\",\n",
 
257
                                                  _("Internal name"), _("Size"));
 
258
        appendPQExpBuffer(&buf,
 
259
                        "  pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
 
260
                                          _("Description"));
 
261
 
 
262
        appendPQExpBuffer(&buf, "FROM pg_catalog.pg_type t\n"
 
263
                                          "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
 
264
 
 
265
        /*
 
266
         * do not include array types (start with underscore); do not include
 
267
         * complex types (typrelid!=0) unless they are standalone composite
 
268
         * types
 
269
         */
 
270
        appendPQExpBuffer(&buf, "WHERE (t.typrelid = 0 ");
 
271
        appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c "
 
272
                                          "WHERE c.oid = t.typrelid)) ");
 
273
        appendPQExpBuffer(&buf, "AND t.typname !~ '^_'\n");
 
274
 
 
275
        /* Match name pattern against either internal or external name */
 
276
        processNamePattern(&buf, pattern, true, false,
 
277
                                           "n.nspname", "t.typname",
 
278
                                           "pg_catalog.format_type(t.oid, NULL)",
 
279
                                           "pg_catalog.pg_type_is_visible(t.oid)");
 
280
 
 
281
        appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
 
282
 
 
283
        res = PSQLexec(buf.data, false);
 
284
        termPQExpBuffer(&buf);
 
285
        if (!res)
 
286
                return false;
 
287
 
 
288
        myopt.nullPrint = NULL;
 
289
        myopt.title = _("List of data types");
 
290
 
 
291
        printQuery(res, &myopt, pset.queryFout);
 
292
 
 
293
        PQclear(res);
 
294
        return true;
 
295
}
 
296
 
 
297
 
 
298
 
 
299
/* \do
 
300
 */
 
301
bool
 
302
describeOperators(const char *pattern)
 
303
{
 
304
        PQExpBufferData buf;
 
305
        PGresult   *res;
 
306
        printQueryOpt myopt = pset.popt;
 
307
 
 
308
        initPQExpBuffer(&buf);
 
309
 
 
310
        printfPQExpBuffer(&buf,
 
311
                                          "SELECT n.nspname as \"%s\",\n"
 
312
                                          "  o.oprname AS \"%s\",\n"
 
313
                                          "  CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n"
 
314
                                          "  CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n"
 
315
                           "  pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n"
 
316
                 "  coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n"
 
317
                                          "           pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
 
318
                                          "FROM pg_catalog.pg_operator o\n"
 
319
                                          "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
 
320
                                          _("Schema"), _("Name"),
 
321
                                          _("Left arg type"), _("Right arg type"),
 
322
                                          _("Result type"), _("Description"));
 
323
 
 
324
        processNamePattern(&buf, pattern, false, true,
 
325
                                           "n.nspname", "o.oprname", NULL,
 
326
                                           "pg_catalog.pg_operator_is_visible(o.oid)");
 
327
 
 
328
        appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3, 4;");
 
329
 
 
330
        res = PSQLexec(buf.data, false);
 
331
        termPQExpBuffer(&buf);
 
332
        if (!res)
 
333
                return false;
 
334
 
 
335
        myopt.nullPrint = NULL;
 
336
        myopt.title = _("List of operators");
 
337
 
 
338
        printQuery(res, &myopt, pset.queryFout);
 
339
 
 
340
        PQclear(res);
 
341
        return true;
 
342
}
 
343
 
 
344
 
 
345
/*
 
346
 * listAllDbs
 
347
 *
 
348
 * for \l, \list, and -l switch
 
349
 */
 
350
bool
 
351
listAllDbs(bool verbose)
 
352
{
 
353
        PGresult   *res;
 
354
        PQExpBufferData buf;
 
355
        printQueryOpt myopt = pset.popt;
 
356
 
 
357
        initPQExpBuffer(&buf);
 
358
 
 
359
        printfPQExpBuffer(&buf,
 
360
                                          "SELECT d.datname as \"%s\",\n"
 
361
                                          "       u.usename as \"%s\"",
 
362
                                          _("Name"), _("Owner"));
 
363
        appendPQExpBuffer(&buf,
 
364
                ",\n       pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\"",
 
365
                                          _("Encoding"));
 
366
        if (verbose)
 
367
                appendPQExpBuffer(&buf,
 
368
                                                  ",\n       pg_catalog.obj_description(d.oid, 'pg_database') as \"%s\"",
 
369
                                                  _("Description"));
 
370
        appendPQExpBuffer(&buf,
 
371
                                          "\nFROM pg_catalog.pg_database d"
 
372
                  "\n  LEFT JOIN pg_catalog.pg_user u ON d.datdba = u.usesysid\n"
 
373
                                          "ORDER BY 1;");
 
374
 
 
375
        res = PSQLexec(buf.data, false);
 
376
        termPQExpBuffer(&buf);
 
377
        if (!res)
 
378
                return false;
 
379
 
 
380
        myopt.nullPrint = NULL;
 
381
        myopt.title = _("List of databases");
 
382
 
 
383
        printQuery(res, &myopt, pset.queryFout);
 
384
 
 
385
        PQclear(res);
 
386
        return true;
 
387
}
 
388
 
 
389
 
 
390
/*
 
391
 * List Tables Grant/Revoke Permissions
 
392
 * \z (now also \dp -- perhaps more mnemonic)
 
393
 */
 
394
bool
 
395
permissionsList(const char *pattern)
 
396
{
 
397
        PQExpBufferData buf;
 
398
        PGresult   *res;
 
399
        printQueryOpt myopt = pset.popt;
 
400
 
 
401
        initPQExpBuffer(&buf);
 
402
 
 
403
        /*
 
404
         * we ignore indexes and toast tables since they have no meaningful
 
405
         * rights
 
406
         */
 
407
        printfPQExpBuffer(&buf,
 
408
                                          "SELECT n.nspname as \"%s\",\n"
 
409
                                          "  c.relname as \"%s\",\n"
 
410
                                          "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' END as \"%s\",\n"
 
411
                                          "  c.relacl as \"%s\"\n"
 
412
                                          "FROM pg_catalog.pg_class c\n"
 
413
        "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
 
414
                                          "WHERE c.relkind IN ('r', 'v', 'S')\n",
 
415
                                          _("Schema"), _("Name"), _("table"), _("view"), _("sequence"), _("Type"), _("Access privileges"));
 
416
 
 
417
        /*
 
418
         * Unless a schema pattern is specified, we suppress system and temp
 
419
         * tables, since they normally aren't very interesting from a
 
420
         * permissions point of view.  You can see 'em by explicit request
 
421
         * though, eg with \z pg_catalog.*
 
422
         */
 
423
        processNamePattern(&buf, pattern, true, false,
 
424
                                           "n.nspname", "c.relname", NULL,
 
425
                "pg_catalog.pg_table_is_visible(c.oid) AND n.nspname !~ '^pg_'");
 
426
 
 
427
        appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
 
428
 
 
429
        res = PSQLexec(buf.data, false);
 
430
        if (!res)
 
431
        {
 
432
                termPQExpBuffer(&buf);
 
433
                return false;
 
434
        }
 
435
 
 
436
        myopt.nullPrint = NULL;
 
437
        printfPQExpBuffer(&buf, _("Access privileges for database \"%s\""), PQdb(pset.db));
 
438
        myopt.title = buf.data;
 
439
 
 
440
        printQuery(res, &myopt, pset.queryFout);
 
441
 
 
442
        termPQExpBuffer(&buf);
 
443
        PQclear(res);
 
444
        return true;
 
445
}
 
446
 
 
447
 
 
448
 
 
449
/*
 
450
 * Get object comments
 
451
 *
 
452
 * \dd [foo]
 
453
 *
 
454
 * Note: This only lists things that actually have a description. For complete
 
455
 * lists of things, there are other \d? commands.
 
456
 */
 
457
bool
 
458
objectDescription(const char *pattern)
 
459
{
 
460
        PQExpBufferData buf;
 
461
        PGresult   *res;
 
462
        printQueryOpt myopt = pset.popt;
 
463
 
 
464
        initPQExpBuffer(&buf);
 
465
 
 
466
        appendPQExpBuffer(&buf,
 
467
                                          "SELECT DISTINCT tt.nspname AS \"%s\", tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n"
 
468
                                          "FROM (\n",
 
469
                                  _("Schema"), _("Name"), _("Object"), _("Description"));
 
470
 
 
471
        /* Aggregate descriptions */
 
472
        appendPQExpBuffer(&buf,
 
473
                                          "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
 
474
                                          "  n.nspname as nspname,\n"
 
475
                                          "  CAST(p.proname AS pg_catalog.text) as name,"
 
476
                                          "  CAST('%s' AS pg_catalog.text) as object\n"
 
477
                                          "  FROM pg_catalog.pg_proc p\n"
 
478
                                          "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
 
479
                                          "  WHERE p.proisagg\n",
 
480
                                          _("aggregate"));
 
481
        processNamePattern(&buf, pattern, true, false,
 
482
                                           "n.nspname", "p.proname", NULL,
 
483
                                           "pg_catalog.pg_function_is_visible(p.oid)");
 
484
 
 
485
        /* Function descriptions (except in/outs for datatypes) */
 
486
        appendPQExpBuffer(&buf,
 
487
                                          "UNION ALL\n"
 
488
                                          "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
 
489
                                          "  n.nspname as nspname,\n"
 
490
                                          "  CAST(p.proname AS pg_catalog.text) as name,"
 
491
                                          "  CAST('%s' AS pg_catalog.text) as object\n"
 
492
                                          "  FROM pg_catalog.pg_proc p\n"
 
493
                                          "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
 
494
 
 
495
         "  WHERE p.prorettype <> 'pg_catalog.cstring'::pg_catalog.regtype\n"
 
496
                                          "      AND p.proargtypes[0] <> 'pg_catalog.cstring'::pg_catalog.regtype\n"
 
497
                                          "      AND NOT p.proisagg\n",
 
498
                                          _("function"));
 
499
        processNamePattern(&buf, pattern, true, false,
 
500
                                           "n.nspname", "p.proname", NULL,
 
501
                                           "pg_catalog.pg_function_is_visible(p.oid)");
 
502
 
 
503
        /* Operator descriptions (only if operator has its own comment) */
 
504
        appendPQExpBuffer(&buf,
 
505
                                          "UNION ALL\n"
 
506
                                          "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
 
507
                                          "  n.nspname as nspname,\n"
 
508
                                          "  CAST(o.oprname AS pg_catalog.text) as name,"
 
509
                                          "  CAST('%s' AS pg_catalog.text) as object\n"
 
510
                                          "  FROM pg_catalog.pg_operator o\n"
 
511
                                          "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
 
512
                                          _("operator"));
 
513
        processNamePattern(&buf, pattern, false, false,
 
514
                                           "n.nspname", "o.oprname", NULL,
 
515
                                           "pg_catalog.pg_operator_is_visible(o.oid)");
 
516
 
 
517
        /* Type description */
 
518
        appendPQExpBuffer(&buf,
 
519
                                          "UNION ALL\n"
 
520
                                          "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
 
521
                                          "  n.nspname as nspname,\n"
 
522
                                          "  pg_catalog.format_type(t.oid, NULL) as name,"
 
523
                                          "  CAST('%s' AS pg_catalog.text) as object\n"
 
524
                                          "  FROM pg_catalog.pg_type t\n"
 
525
                                          "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
 
526
                                          _("data type"));
 
527
        processNamePattern(&buf, pattern, false, false,
 
528
                                "n.nspname", "pg_catalog.format_type(t.oid, NULL)", NULL,
 
529
                                           "pg_catalog.pg_type_is_visible(t.oid)");
 
530
 
 
531
        /* Relation (tables, views, indexes, sequences) descriptions */
 
532
        appendPQExpBuffer(&buf,
 
533
                                          "UNION ALL\n"
 
534
                                          "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
 
535
                                          "  n.nspname as nspname,\n"
 
536
                                          "  CAST(c.relname AS pg_catalog.text) as name,\n"
 
537
                                          "  CAST(\n"
 
538
                                          "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' END"
 
539
                                          "  AS pg_catalog.text) as object\n"
 
540
                                          "  FROM pg_catalog.pg_class c\n"
 
541
                                          "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
 
542
                                          "  WHERE c.relkind IN ('r', 'v', 'i', 'S')\n",
 
543
                                          _("table"), _("view"), _("index"), _("sequence"));
 
544
        processNamePattern(&buf, pattern, true, false,
 
545
                                           "n.nspname", "c.relname", NULL,
 
546
                                           "pg_catalog.pg_table_is_visible(c.oid)");
 
547
 
 
548
        /* Rule description (ignore rules for views) */
 
549
        appendPQExpBuffer(&buf,
 
550
                                          "UNION ALL\n"
 
551
                                          "  SELECT r.oid as oid, r.tableoid as tableoid,\n"
 
552
                                          "  n.nspname as nspname,\n"
 
553
                                          "  CAST(r.rulename AS pg_catalog.text) as name,"
 
554
                                          "  CAST('%s' AS pg_catalog.text) as object\n"
 
555
                                          "  FROM pg_catalog.pg_rewrite r\n"
 
556
                          "       JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n"
 
557
                                          "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
 
558
                                          "  WHERE r.rulename != '_RETURN'\n",
 
559
                                          _("rule"));
 
560
        /* XXX not sure what to do about visibility rule here? */
 
561
        processNamePattern(&buf, pattern, true, false,
 
562
                                           "n.nspname", "r.rulename", NULL,
 
563
                                           "pg_catalog.pg_table_is_visible(c.oid)");
 
564
 
 
565
        /* Trigger description */
 
566
        appendPQExpBuffer(&buf,
 
567
                                          "UNION ALL\n"
 
568
                                          "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
 
569
                                          "  n.nspname as nspname,\n"
 
570
                                          "  CAST(t.tgname AS pg_catalog.text) as name,"
 
571
                                          "  CAST('%s' AS pg_catalog.text) as object\n"
 
572
                                          "  FROM pg_catalog.pg_trigger t\n"
 
573
                           "       JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n"
 
574
                                          "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n",
 
575
                                          _("trigger"));
 
576
        /* XXX not sure what to do about visibility rule here? */
 
577
        processNamePattern(&buf, pattern, false, false,
 
578
                                           "n.nspname", "t.tgname", NULL,
 
579
                                           "pg_catalog.pg_table_is_visible(c.oid)");
 
580
 
 
581
        appendPQExpBuffer(&buf,
 
582
                                          ") AS tt\n"
 
583
                                          "  JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n");
 
584
 
 
585
        appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
 
586
 
 
587
        res = PSQLexec(buf.data, false);
 
588
        termPQExpBuffer(&buf);
 
589
        if (!res)
 
590
                return false;
 
591
 
 
592
        myopt.nullPrint = NULL;
 
593
        myopt.title = _("Object descriptions");
 
594
 
 
595
        printQuery(res, &myopt, pset.queryFout);
 
596
 
 
597
        PQclear(res);
 
598
        return true;
 
599
}
 
600
 
 
601
 
 
602
 
 
603
/*
 
604
 * describeTableDetails (for \d)
 
605
 *
 
606
 * This routine finds the tables to be displayed, and calls
 
607
 * describeOneTableDetails for each one.
 
608
 *
 
609
 * verbose: if true, this is \d+
 
610
 */
 
611
bool
 
612
describeTableDetails(const char *pattern, bool verbose)
 
613
{
 
614
        PQExpBufferData buf;
 
615
        PGresult   *res;
 
616
        int                     i;
 
617
 
 
618
        initPQExpBuffer(&buf);
 
619
 
 
620
        printfPQExpBuffer(&buf,
 
621
                                          "SELECT c.oid,\n"
 
622
                                          "  n.nspname,\n"
 
623
                                          "  c.relname\n"
 
624
                                          "FROM pg_catalog.pg_class c\n"
 
625
                                          "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
 
626
 
 
627
        processNamePattern(&buf, pattern, false, false,
 
628
                                           "n.nspname", "c.relname", NULL,
 
629
                                           "pg_catalog.pg_table_is_visible(c.oid)");
 
630
 
 
631
        appendPQExpBuffer(&buf, "ORDER BY 2, 3;");
 
632
 
 
633
        res = PSQLexec(buf.data, false);
 
634
        termPQExpBuffer(&buf);
 
635
        if (!res)
 
636
                return false;
 
637
 
 
638
        if (PQntuples(res) == 0)
 
639
        {
 
640
                if (!QUIET())
 
641
                        fprintf(stderr, _("Did not find any relation named \"%s\".\n"),
 
642
                                        pattern);
 
643
                PQclear(res);
 
644
                return false;
 
645
        }
 
646
 
 
647
        for (i = 0; i < PQntuples(res); i++)
 
648
        {
 
649
                const char *oid;
 
650
                const char *nspname;
 
651
                const char *relname;
 
652
 
 
653
                oid = PQgetvalue(res, i, 0);
 
654
                nspname = PQgetvalue(res, i, 1);
 
655
                relname = PQgetvalue(res, i, 2);
 
656
 
 
657
                if (!describeOneTableDetails(nspname, relname, oid, verbose))
 
658
                {
 
659
                        PQclear(res);
 
660
                        return false;
 
661
                }
 
662
        }
 
663
 
 
664
        PQclear(res);
 
665
        return true;
 
666
}
 
667
 
 
668
/*
 
669
 * describeOneTableDetails (for \d)
 
670
 *
 
671
 * Unfortunately, the information presented here is so complicated that it
 
672
 * cannot be done in a single query. So we have to assemble the printed table
 
673
 * by hand and pass it to the underlying printTable() function.
 
674
 */
 
675
static bool
 
676
describeOneTableDetails(const char *schemaname,
 
677
                                                const char *relationname,
 
678
                                                const char *oid,
 
679
                                                bool verbose)
 
680
{
 
681
        PQExpBufferData buf;
 
682
        PGresult   *res = NULL;
 
683
        printTableOpt myopt = pset.popt.topt;
 
684
        int                     i;
 
685
        char       *view_def = NULL;
 
686
        const char *headers[5];
 
687
        char      **cells = NULL;
 
688
        char      **footers = NULL;
 
689
        char      **ptr;
 
690
        PQExpBufferData title;
 
691
        PQExpBufferData tmpbuf;
 
692
        int                     cols = 0;
 
693
        int                     numrows = 0;
 
694
        struct
 
695
        {
 
696
                int16           checks;
 
697
                int16           triggers;
 
698
                char            relkind;
 
699
                bool            hasindex;
 
700
                bool            hasrules;
 
701
                bool            hasoids;
 
702
                Oid                     tablespace;
 
703
        }                       tableinfo;
 
704
        bool            show_modifiers = false;
 
705
        bool            retval;
 
706
 
 
707
        retval = false;
 
708
 
 
709
        initPQExpBuffer(&buf);
 
710
        initPQExpBuffer(&title);
 
711
        initPQExpBuffer(&tmpbuf);
 
712
 
 
713
        /* Get general table info */
 
714
        printfPQExpBuffer(&buf,
 
715
        "SELECT relhasindex, relkind, relchecks, reltriggers, relhasrules, \n"
 
716
                                          "relhasoids %s \n"
 
717
                                          "FROM pg_catalog.pg_class WHERE oid = '%s'",
 
718
                                          pset.sversion >= 80000 ? ", reltablespace" : "",
 
719
                                          oid);
 
720
        res = PSQLexec(buf.data, false);
 
721
        if (!res)
 
722
                goto error_return;
 
723
 
 
724
        /* Did we get anything? */
 
725
        if (PQntuples(res) == 0)
 
726
        {
 
727
                if (!QUIET())
 
728
                        fprintf(stderr, _("Did not find any relation with OID %s.\n"),
 
729
                                        oid);
 
730
                goto error_return;
 
731
        }
 
732
 
 
733
        /* FIXME: check for null pointers here? */
 
734
        tableinfo.checks = atoi(PQgetvalue(res, 0, 2));
 
735
        tableinfo.triggers = atoi(PQgetvalue(res, 0, 3));
 
736
        tableinfo.relkind = *(PQgetvalue(res, 0, 1));
 
737
        tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 0), "t") == 0;
 
738
        tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
 
739
        tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
 
740
        tableinfo.tablespace = (pset.sversion >= 80000) ?
 
741
                atooid(PQgetvalue(res, 0, 6)) : 0;
 
742
        PQclear(res);
 
743
 
 
744
        headers[0] = _("Column");
 
745
        headers[1] = _("Type");
 
746
        cols = 2;
 
747
 
 
748
        if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v')
 
749
        {
 
750
                show_modifiers = true;
 
751
                cols++;
 
752
                headers[cols - 1] = _("Modifiers");
 
753
        }
 
754
 
 
755
        if (verbose)
 
756
        {
 
757
                cols++;
 
758
                headers[cols - 1] = _("Description");
 
759
        }
 
760
 
 
761
        headers[cols] = NULL;
 
762
 
 
763
        /* Get column info (index requires additional checks) */
 
764
        printfPQExpBuffer(&buf, "SELECT a.attname,");
 
765
        appendPQExpBuffer(&buf, "\n  pg_catalog.format_type(a.atttypid, a.atttypmod),"
 
766
        "\n  (SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d"
 
767
                                          "\n   WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),"
 
768
                                          "\n  a.attnotnull, a.attnum");
 
769
        if (verbose)
 
770
                appendPQExpBuffer(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)");
 
771
        appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_attribute a");
 
772
        if (tableinfo.relkind == 'i')
 
773
                appendPQExpBuffer(&buf, ", pg_catalog.pg_index i");
 
774
        appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
 
775
        if (tableinfo.relkind == 'i')
 
776
                appendPQExpBuffer(&buf, " AND a.attrelid = i.indexrelid");
 
777
        appendPQExpBuffer(&buf, "\nORDER BY a.attnum");
 
778
 
 
779
        res = PSQLexec(buf.data, false);
 
780
        if (!res)
 
781
                goto error_return;
 
782
        numrows = PQntuples(res);
 
783
 
 
784
        /* Check if table is a view */
 
785
        if (tableinfo.relkind == 'v')
 
786
        {
 
787
                PGresult   *result;
 
788
 
 
789
                printfPQExpBuffer(&buf, "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true)", oid);
 
790
                result = PSQLexec(buf.data, false);
 
791
                if (!result)
 
792
                        goto error_return;
 
793
 
 
794
                if (PQntuples(result) > 0)
 
795
                        view_def = pg_strdup(PQgetvalue(result, 0, 0));
 
796
 
 
797
                PQclear(result);
 
798
        }
 
799
 
 
800
        /* Generate table cells to be printed */
 
801
        /* note: initialize all cells[] to NULL in case of error exit */
 
802
        cells = pg_malloc_zero((numrows * cols + 1) * sizeof(*cells));
 
803
 
 
804
        for (i = 0; i < numrows; i++)
 
805
        {
 
806
                /* Name */
 
807
#ifdef WIN32
 
808
                cells[i * cols + 0] = mbvalidate(PQgetvalue(res, i, 0), myopt.encoding);
 
809
#else
 
810
                cells[i * cols + 0] = PQgetvalue(res, i, 0);    /* don't free this
 
811
                                                                                                                 * afterwards */
 
812
#endif
 
813
 
 
814
                /* Type */
 
815
#ifdef WIN32
 
816
                cells[i * cols + 1] = mbvalidate(PQgetvalue(res, i, 1), myopt.encoding);
 
817
#else
 
818
                cells[i * cols + 1] = PQgetvalue(res, i, 1);    /* don't free this
 
819
                                                                                                                 * either */
 
820
#endif
 
821
 
 
822
                /* Extra: not null and default */
 
823
                if (show_modifiers)
 
824
                {
 
825
                        resetPQExpBuffer(&tmpbuf);
 
826
                        if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
 
827
                                appendPQExpBufferStr(&tmpbuf, "not null");
 
828
 
 
829
                        /* handle "default" here */
 
830
                        /* (note: above we cut off the 'default' string at 128) */
 
831
                        if (strlen(PQgetvalue(res, i, 2)) != 0)
 
832
                        {
 
833
                                if (tmpbuf.len > 0)
 
834
                                        appendPQExpBufferStr(&tmpbuf, " ");
 
835
                                appendPQExpBuffer(&tmpbuf, "default %s",
 
836
                                                                  PQgetvalue(res, i, 2));
 
837
                        }
 
838
 
 
839
#ifdef WIN32
 
840
                        cells[i * cols + 2] = pg_strdup(mbvalidate(tmpbuf.data, myopt.encoding));
 
841
#else
 
842
                        cells[i * cols + 2] = pg_strdup(tmpbuf.data);
 
843
#endif
 
844
                }
 
845
 
 
846
                /* Description */
 
847
                if (verbose)
 
848
#ifdef WIN32
 
849
                        cells[i * cols + cols - 1] = mbvalidate(PQgetvalue(res, i, 5), myopt.encoding);
 
850
#else
 
851
                        cells[i * cols + cols - 1] = PQgetvalue(res, i, 5);
 
852
#endif
 
853
        }
 
854
 
 
855
        /* Make title */
 
856
        switch (tableinfo.relkind)
 
857
        {
 
858
                case 'r':
 
859
                        printfPQExpBuffer(&title, _("Table \"%s.%s\""),
 
860
                                                          schemaname, relationname);
 
861
                        break;
 
862
                case 'v':
 
863
                        printfPQExpBuffer(&title, _("View \"%s.%s\""),
 
864
                                                          schemaname, relationname);
 
865
                        break;
 
866
                case 'S':
 
867
                        printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
 
868
                                                          schemaname, relationname);
 
869
                        break;
 
870
                case 'i':
 
871
                        printfPQExpBuffer(&title, _("Index \"%s.%s\""),
 
872
                                                          schemaname, relationname);
 
873
                        break;
 
874
                case 's':
 
875
                        printfPQExpBuffer(&title, _("Special relation \"%s.%s\""),
 
876
                                                          schemaname, relationname);
 
877
                        break;
 
878
                case 't':
 
879
                        printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
 
880
                                                          schemaname, relationname);
 
881
                        break;
 
882
                case 'c':
 
883
                        printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
 
884
                                                          schemaname, relationname);
 
885
                        break;
 
886
                default:
 
887
                        printfPQExpBuffer(&title, _("?%c? \"%s.%s\""),
 
888
                                                        tableinfo.relkind, schemaname, relationname);
 
889
                        break;
 
890
        }
 
891
 
 
892
        /* Make footers */
 
893
        if (tableinfo.relkind == 'i')
 
894
        {
 
895
                /* Footer information about an index */
 
896
                PGresult   *result;
 
897
 
 
898
                printfPQExpBuffer(&buf,
 
899
                                                  "SELECT i.indisunique, i.indisprimary, i.indisclustered, a.amname, c2.relname,\n"
 
900
                                "  pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
 
901
                                                  "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
 
902
                                                  "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
 
903
                                                  "AND i.indrelid = c2.oid",
 
904
                                                  oid);
 
905
 
 
906
                result = PSQLexec(buf.data, false);
 
907
                if (!result)
 
908
                        goto error_return;
 
909
                else if (PQntuples(result) != 1)
 
910
                {
 
911
                        PQclear(result);
 
912
                        goto error_return;
 
913
                }
 
914
                else
 
915
                {
 
916
                        char       *indisunique = PQgetvalue(result, 0, 0);
 
917
                        char       *indisprimary = PQgetvalue(result, 0, 1);
 
918
                        char       *indisclustered = PQgetvalue(result, 0, 2);
 
919
                        char       *indamname = PQgetvalue(result, 0, 3);
 
920
                        char       *indtable = PQgetvalue(result, 0, 4);
 
921
                        char       *indpred = PQgetvalue(result, 0, 5);
 
922
                        int                     count_footers = 0;
 
923
 
 
924
                        if (strcmp(indisprimary, "t") == 0)
 
925
                                printfPQExpBuffer(&tmpbuf, _("primary key, "));
 
926
                        else if (strcmp(indisunique, "t") == 0)
 
927
                                printfPQExpBuffer(&tmpbuf, _("unique, "));
 
928
                        else
 
929
                                resetPQExpBuffer(&tmpbuf);
 
930
                        appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
 
931
 
 
932
                        /* we assume here that index and table are in same schema */
 
933
                        appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
 
934
                                                          schemaname, indtable);
 
935
 
 
936
                        if (strlen(indpred))
 
937
                                appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
 
938
 
 
939
                        if (strcmp(indisclustered, "t") == 0)
 
940
                                appendPQExpBuffer(&tmpbuf, _(", clustered"));
 
941
 
 
942
                        footers = pg_malloc_zero(4 * sizeof(*footers));
 
943
                        footers[count_footers++] = pg_strdup(tmpbuf.data);
 
944
                        add_tablespace_footer(tableinfo.relkind, tableinfo.tablespace,
 
945
                                                                  footers, &count_footers, tmpbuf);
 
946
                        footers[count_footers] = NULL;
 
947
 
 
948
                }
 
949
 
 
950
                PQclear(result);
 
951
        }
 
952
        else if (view_def)
 
953
        {
 
954
                PGresult   *result = NULL;
 
955
                int                     rule_count = 0;
 
956
                int                     count_footers = 0;
 
957
 
 
958
                /* count rules other than the view rule */
 
959
                if (tableinfo.hasrules)
 
960
                {
 
961
                        printfPQExpBuffer(&buf,
 
962
                                                          "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
 
963
                                                          "FROM pg_catalog.pg_rewrite r\n"
 
964
                                   "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN'",
 
965
                                                          oid);
 
966
                        result = PSQLexec(buf.data, false);
 
967
                        if (!result)
 
968
                                goto error_return;
 
969
                        else
 
970
                                rule_count = PQntuples(result);
 
971
                }
 
972
 
 
973
                /* Footer information about a view */
 
974
                footers = pg_malloc_zero((rule_count + 3) * sizeof(*footers));
 
975
                footers[count_footers] = pg_malloc(64 + strlen(view_def));
 
976
                snprintf(footers[count_footers], 64 + strlen(view_def),
 
977
                                 _("View definition:\n%s"), view_def);
 
978
                count_footers++;
 
979
 
 
980
                /* print rules */
 
981
                if (rule_count > 0)
 
982
                {
 
983
                        printfPQExpBuffer(&buf, _("Rules:"));
 
984
                        footers[count_footers++] = pg_strdup(buf.data);
 
985
                        for (i = 0; i < rule_count; i++)
 
986
                        {
 
987
                                const char *ruledef;
 
988
 
 
989
                                /* Everything after "CREATE RULE" is echoed verbatim */
 
990
                                ruledef = PQgetvalue(result, i, 1);
 
991
                                ruledef += 12;
 
992
 
 
993
                                printfPQExpBuffer(&buf, " %s", ruledef);
 
994
 
 
995
                                footers[count_footers++] = pg_strdup(buf.data);
 
996
                        }
 
997
                        PQclear(result);
 
998
                }
 
999
 
 
1000
                footers[count_footers] = NULL;
 
1001
 
 
1002
        }
 
1003
        else if (tableinfo.relkind == 'r')
 
1004
        {
 
1005
                /* Footer information about a table */
 
1006
                PGresult   *result1 = NULL,
 
1007
                                   *result2 = NULL,
 
1008
                                   *result3 = NULL,
 
1009
                                   *result4 = NULL,
 
1010
                                   *result5 = NULL,
 
1011
                                   *result6 = NULL;
 
1012
                int                     check_count = 0,
 
1013
                                        index_count = 0,
 
1014
                                        foreignkey_count = 0,
 
1015
                                        rule_count = 0,
 
1016
                                        trigger_count = 0,
 
1017
                                        inherits_count = 0;
 
1018
                int                     count_footers = 0;
 
1019
 
 
1020
                /* count indexes */
 
1021
                if (tableinfo.hasindex)
 
1022
                {
 
1023
                        printfPQExpBuffer(&buf,
 
1024
                                                          "SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, "
 
1025
                                        "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)\n"
 
1026
                                                          "FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
 
1027
                                                          "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
 
1028
                                                          "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname",
 
1029
                                                          oid);
 
1030
                        result1 = PSQLexec(buf.data, false);
 
1031
                        if (!result1)
 
1032
                                goto error_return;
 
1033
                        else
 
1034
                                index_count = PQntuples(result1);
 
1035
                }
 
1036
 
 
1037
                /* count table (and column) check constraints */
 
1038
                if (tableinfo.checks)
 
1039
                {
 
1040
                        printfPQExpBuffer(&buf,
 
1041
                                                          "SELECT "
 
1042
                                                 "pg_catalog.pg_get_constraintdef(r.oid, true), "
 
1043
                                                          "conname\n"
 
1044
                                                          "FROM pg_catalog.pg_constraint r\n"
 
1045
                                                   "WHERE r.conrelid = '%s' AND r.contype = 'c'",
 
1046
                                                          oid);
 
1047
                        result2 = PSQLexec(buf.data, false);
 
1048
                        if (!result2)
 
1049
                        {
 
1050
                                PQclear(result1);
 
1051
                                goto error_return;
 
1052
                        }
 
1053
                        else
 
1054
                                check_count = PQntuples(result2);
 
1055
                }
 
1056
 
 
1057
                /* count rules */
 
1058
                if (tableinfo.hasrules)
 
1059
                {
 
1060
                        printfPQExpBuffer(&buf,
 
1061
                                                          "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
 
1062
                                                          "FROM pg_catalog.pg_rewrite r\n"
 
1063
                                                          "WHERE r.ev_class = '%s'",
 
1064
                                                          oid);
 
1065
                        result3 = PSQLexec(buf.data, false);
 
1066
                        if (!result3)
 
1067
                        {
 
1068
                                PQclear(result1);
 
1069
                                PQclear(result2);
 
1070
                                goto error_return;
 
1071
                        }
 
1072
                        else
 
1073
                                rule_count = PQntuples(result3);
 
1074
                }
 
1075
 
 
1076
                /* count triggers (but ignore foreign-key triggers) */
 
1077
                if (tableinfo.triggers)
 
1078
                {
 
1079
                        printfPQExpBuffer(&buf,
 
1080
                                 "SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid)\n"
 
1081
                                                          "FROM pg_catalog.pg_trigger t\n"
 
1082
                                                          "WHERE t.tgrelid = '%s' "
 
1083
                                                          "AND (not tgisconstraint "
 
1084
                                                          " OR NOT EXISTS"
 
1085
                                                          "  (SELECT 1 FROM pg_catalog.pg_depend d "
 
1086
                                                          "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
 
1087
                                                          "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
 
1088
                                                          oid);
 
1089
                        result4 = PSQLexec(buf.data, false);
 
1090
                        if (!result4)
 
1091
                        {
 
1092
                                PQclear(result1);
 
1093
                                PQclear(result2);
 
1094
                                PQclear(result3);
 
1095
                                goto error_return;
 
1096
                        }
 
1097
                        else
 
1098
                                trigger_count = PQntuples(result4);
 
1099
                }
 
1100
 
 
1101
                /* count foreign-key constraints (there are none if no triggers) */
 
1102
                if (tableinfo.triggers)
 
1103
                {
 
1104
                        printfPQExpBuffer(&buf,
 
1105
                                                          "SELECT conname,\n"
 
1106
                           "  pg_catalog.pg_get_constraintdef(oid, true) as condef\n"
 
1107
                                                          "FROM pg_catalog.pg_constraint r\n"
 
1108
                                                   "WHERE r.conrelid = '%s' AND r.contype = 'f'",
 
1109
                                                          oid);
 
1110
                        result5 = PSQLexec(buf.data, false);
 
1111
                        if (!result5)
 
1112
                        {
 
1113
                                PQclear(result1);
 
1114
                                PQclear(result2);
 
1115
                                PQclear(result3);
 
1116
                                PQclear(result4);
 
1117
                                goto error_return;
 
1118
                        }
 
1119
                        else
 
1120
                                foreignkey_count = PQntuples(result5);
 
1121
                }
 
1122
 
 
1123
                /* count inherited tables */
 
1124
                printfPQExpBuffer(&buf, "SELECT c.relname FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno ASC", oid);
 
1125
 
 
1126
                result6 = PSQLexec(buf.data, false);
 
1127
                if (!result6)
 
1128
                        goto error_return;
 
1129
                else
 
1130
                        inherits_count = PQntuples(result6);
 
1131
 
 
1132
                footers = pg_malloc_zero((index_count + check_count + rule_count + trigger_count + foreignkey_count + inherits_count + 7 + 1)
 
1133
                                                                 * sizeof(*footers));
 
1134
 
 
1135
                /* print indexes */
 
1136
                if (index_count > 0)
 
1137
                {
 
1138
                        printfPQExpBuffer(&buf, _("Indexes:"));
 
1139
                        footers[count_footers++] = pg_strdup(buf.data);
 
1140
                        for (i = 0; i < index_count; i++)
 
1141
                        {
 
1142
                                const char *indexdef;
 
1143
                                const char *usingpos;
 
1144
 
 
1145
                                /* Output index name */
 
1146
                                printfPQExpBuffer(&buf, _("    \"%s\""),
 
1147
                                                                  PQgetvalue(result1, i, 0));
 
1148
 
 
1149
                                /* Label as primary key or unique (but not both) */
 
1150
                                appendPQExpBuffer(&buf,
 
1151
                                                          strcmp(PQgetvalue(result1, i, 1), "t") == 0
 
1152
                                                                  ? " PRIMARY KEY," :
 
1153
                                                         (strcmp(PQgetvalue(result1, i, 2), "t") == 0
 
1154
                                                          ? " UNIQUE,"
 
1155
                                                          : ""));
 
1156
                                /* Everything after "USING" is echoed verbatim */
 
1157
                                indexdef = PQgetvalue(result1, i, 4);
 
1158
                                usingpos = strstr(indexdef, " USING ");
 
1159
                                if (usingpos)
 
1160
                                        indexdef = usingpos + 7;
 
1161
 
 
1162
                                appendPQExpBuffer(&buf, " %s", indexdef);
 
1163
 
 
1164
                                if (strcmp(PQgetvalue(result1, i, 3), "t") == 0)
 
1165
                                        appendPQExpBuffer(&buf, " CLUSTER");
 
1166
 
 
1167
                                footers[count_footers++] = pg_strdup(buf.data);
 
1168
                        }
 
1169
                }
 
1170
 
 
1171
                /* print check constraints */
 
1172
                if (check_count > 0)
 
1173
                {
 
1174
                        printfPQExpBuffer(&buf, _("Check constraints:"));
 
1175
                        footers[count_footers++] = pg_strdup(buf.data);
 
1176
                        for (i = 0; i < check_count; i++)
 
1177
                        {
 
1178
                                printfPQExpBuffer(&buf, _("    \"%s\" %s"),
 
1179
                                                                  PQgetvalue(result2, i, 1),
 
1180
                                                                  PQgetvalue(result2, i, 0));
 
1181
 
 
1182
                                footers[count_footers++] = pg_strdup(buf.data);
 
1183
                        }
 
1184
                }
 
1185
 
 
1186
                /* print foreign key constraints */
 
1187
                if (foreignkey_count > 0)
 
1188
                {
 
1189
                        printfPQExpBuffer(&buf, _("Foreign-key constraints:"));
 
1190
                        footers[count_footers++] = pg_strdup(buf.data);
 
1191
                        for (i = 0; i < foreignkey_count; i++)
 
1192
                        {
 
1193
                                printfPQExpBuffer(&buf, _("    \"%s\" %s"),
 
1194
                                                                  PQgetvalue(result5, i, 0),
 
1195
                                                                  PQgetvalue(result5, i, 1));
 
1196
 
 
1197
                                footers[count_footers++] = pg_strdup(buf.data);
 
1198
                        }
 
1199
                }
 
1200
 
 
1201
                /* print rules */
 
1202
                if (rule_count > 0)
 
1203
                {
 
1204
                        printfPQExpBuffer(&buf, _("Rules:"));
 
1205
                        footers[count_footers++] = pg_strdup(buf.data);
 
1206
                        for (i = 0; i < rule_count; i++)
 
1207
                        {
 
1208
                                const char *ruledef;
 
1209
 
 
1210
                                /* Everything after "CREATE RULE" is echoed verbatim */
 
1211
                                ruledef = PQgetvalue(result3, i, 1);
 
1212
                                ruledef += 12;
 
1213
 
 
1214
                                printfPQExpBuffer(&buf, "    %s", ruledef);
 
1215
 
 
1216
                                footers[count_footers++] = pg_strdup(buf.data);
 
1217
                        }
 
1218
                }
 
1219
 
 
1220
                /* print triggers */
 
1221
                if (trigger_count > 0)
 
1222
                {
 
1223
                        printfPQExpBuffer(&buf, _("Triggers:"));
 
1224
                        footers[count_footers++] = pg_strdup(buf.data);
 
1225
                        for (i = 0; i < trigger_count; i++)
 
1226
                        {
 
1227
                                const char *tgdef;
 
1228
                                const char *usingpos;
 
1229
 
 
1230
                                /* Everything after "TRIGGER" is echoed verbatim */
 
1231
                                tgdef = PQgetvalue(result4, i, 1);
 
1232
                                usingpos = strstr(tgdef, " TRIGGER ");
 
1233
                                if (usingpos)
 
1234
                                        tgdef = usingpos + 9;
 
1235
 
 
1236
                                printfPQExpBuffer(&buf, "    %s", tgdef);
 
1237
 
 
1238
                                footers[count_footers++] = pg_strdup(buf.data);
 
1239
                        }
 
1240
                }
 
1241
 
 
1242
                /* print inherits */
 
1243
                for (i = 0; i < inherits_count; i++)
 
1244
                {
 
1245
                        char       *s = _("Inherits");
 
1246
 
 
1247
                        if (i == 0)
 
1248
                                printfPQExpBuffer(&buf, "%s: %s", s, PQgetvalue(result6, i, 0));
 
1249
                        else
 
1250
                                printfPQExpBuffer(&buf, "%*s  %s", (int) strlen(s), "", PQgetvalue(result6, i, 0));
 
1251
                        if (i < inherits_count - 1)
 
1252
                                appendPQExpBuffer(&buf, ",");
 
1253
 
 
1254
                        footers[count_footers++] = pg_strdup(buf.data);
 
1255
                }
 
1256
 
 
1257
                if (verbose)
 
1258
                {
 
1259
                        char       *s = _("Has OIDs");
 
1260
 
 
1261
                        printfPQExpBuffer(&buf, "%s: %s", s,
 
1262
                                                          (tableinfo.hasoids ? _("yes") : _("no")));
 
1263
                        footers[count_footers++] = pg_strdup(buf.data);
 
1264
                }
 
1265
 
 
1266
                add_tablespace_footer(tableinfo.relkind, tableinfo.tablespace,
 
1267
                                                          footers, &count_footers, buf);
 
1268
                /* end of list marker */
 
1269
                footers[count_footers] = NULL;
 
1270
 
 
1271
                PQclear(result1);
 
1272
                PQclear(result2);
 
1273
                PQclear(result3);
 
1274
                PQclear(result4);
 
1275
                PQclear(result5);
 
1276
                PQclear(result6);
 
1277
        }
 
1278
 
 
1279
        printTable(title.data, headers,
 
1280
                           (const char **) cells, (const char **) footers,
 
1281
                           "llll", &myopt, pset.queryFout);
 
1282
 
 
1283
        retval = true;
 
1284
 
 
1285
error_return:
 
1286
 
 
1287
        /* clean up */
 
1288
        termPQExpBuffer(&buf);
 
1289
        termPQExpBuffer(&title);
 
1290
        termPQExpBuffer(&tmpbuf);
 
1291
 
 
1292
        if (cells)
 
1293
        {
 
1294
                for (i = 0; i < numrows; i++)
 
1295
                {
 
1296
                        if (show_modifiers)
 
1297
                                free(cells[i * cols + 2]);
 
1298
                }
 
1299
                free(cells);
 
1300
        }
 
1301
 
 
1302
        if (footers)
 
1303
        {
 
1304
                for (ptr = footers; *ptr; ptr++)
 
1305
                        free(*ptr);
 
1306
                free(footers);
 
1307
        }
 
1308
 
 
1309
        if (view_def)
 
1310
                free(view_def);
 
1311
 
 
1312
        if (res)
 
1313
                PQclear(res);
 
1314
 
 
1315
        return retval;
 
1316
}
 
1317
 
 
1318
 
 
1319
static void
 
1320
add_tablespace_footer(char relkind, Oid tablespace, char **footers,
 
1321
                                          int *count, PQExpBufferData buf)
 
1322
{
 
1323
        /* relkinds for which we support tablespaces */
 
1324
        if (relkind == 'r' || relkind == 'i')
 
1325
        {
 
1326
                /*
 
1327
                 * We ignore the database default tablespace so that users not
 
1328
                 * using tablespaces don't need to know about them.
 
1329
                 */
 
1330
                if (tablespace != 0)
 
1331
                {
 
1332
                        PGresult   *result1 = NULL;
 
1333
 
 
1334
                        printfPQExpBuffer(&buf, "SELECT spcname FROM pg_tablespace \n"
 
1335
                                                          "WHERE oid = '%u';", tablespace);
 
1336
                        result1 = PSQLexec(buf.data, false);
 
1337
                        if (!result1)
 
1338
                                return;
 
1339
                        /* Should always be the case, but.... */
 
1340
                        if (PQntuples(result1) > 0)
 
1341
                        {
 
1342
                                printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
 
1343
                                                                  PQgetvalue(result1, 0, 0));
 
1344
                                footers[(*count)++] = pg_strdup(buf.data);
 
1345
                        }
 
1346
                        PQclear(result1);
 
1347
                }
 
1348
        }
 
1349
}
 
1350
 
 
1351
/*
 
1352
 * \du
 
1353
 *
 
1354
 * Describes users.  Any schema portion of the pattern is ignored.
 
1355
 */
 
1356
bool
 
1357
describeUsers(const char *pattern)
 
1358
{
 
1359
        PQExpBufferData buf;
 
1360
        PGresult   *res;
 
1361
        printQueryOpt myopt = pset.popt;
 
1362
 
 
1363
        initPQExpBuffer(&buf);
 
1364
 
 
1365
        printfPQExpBuffer(&buf,
 
1366
                                          "SELECT u.usename AS \"%s\",\n"
 
1367
                                          "  u.usesysid AS \"%s\",\n"
 
1368
                                          "  CASE WHEN u.usesuper AND u.usecreatedb THEN CAST('%s' AS pg_catalog.text)\n"
 
1369
                        "       WHEN u.usesuper THEN CAST('%s' AS pg_catalog.text)\n"
 
1370
                 "       WHEN u.usecreatedb THEN CAST('%s' AS pg_catalog.text)\n"
 
1371
                                          "       ELSE CAST('' AS pg_catalog.text)\n"
 
1372
                                          "  END AS \"%s\",\n"
 
1373
                                          "  ARRAY(SELECT g.groname FROM pg_catalog.pg_group g WHERE u.usesysid = ANY(g.grolist)) as \"%s\"\n"
 
1374
                                          "FROM pg_catalog.pg_user u\n",
 
1375
                                          _("User name"), _("User ID"),
 
1376
                                          _("superuser, create database"),
 
1377
                                          _("superuser"), _("create database"),
 
1378
                                          _("Attributes"), _("Groups"));
 
1379
 
 
1380
        processNamePattern(&buf, pattern, false, false,
 
1381
                                           NULL, "u.usename", NULL, NULL);
 
1382
 
 
1383
        appendPQExpBuffer(&buf, "ORDER BY 1;");
 
1384
 
 
1385
        res = PSQLexec(buf.data, false);
 
1386
        termPQExpBuffer(&buf);
 
1387
        if (!res)
 
1388
                return false;
 
1389
 
 
1390
        myopt.nullPrint = NULL;
 
1391
        myopt.title = _("List of users");
 
1392
 
 
1393
        printQuery(res, &myopt, pset.queryFout);
 
1394
 
 
1395
        PQclear(res);
 
1396
        return true;
 
1397
}
 
1398
 
 
1399
 
 
1400
/*
 
1401
 * \dg
 
1402
 *
 
1403
 * Describes groups.
 
1404
 */
 
1405
bool
 
1406
describeGroups(const char *pattern)
 
1407
{
 
1408
        PQExpBufferData buf;
 
1409
        PGresult   *res;
 
1410
        printQueryOpt myopt = pset.popt;
 
1411
 
 
1412
        initPQExpBuffer(&buf);
 
1413
 
 
1414
        printfPQExpBuffer(&buf,
 
1415
                                          "SELECT g.groname AS \"%s\",\n"
 
1416
                                          "  g.grosysid AS \"%s\"\n"
 
1417
                                          "FROM pg_catalog.pg_group g\n",
 
1418
                                          _("Group name"), _("Group ID"));
 
1419
 
 
1420
        processNamePattern(&buf, pattern, false, false,
 
1421
                                           NULL, "g.groname", NULL, NULL);
 
1422
 
 
1423
        appendPQExpBuffer(&buf, "ORDER BY 1;");
 
1424
 
 
1425
        res = PSQLexec(buf.data, false);
 
1426
        termPQExpBuffer(&buf);
 
1427
        if (!res)
 
1428
                return false;
 
1429
 
 
1430
        myopt.nullPrint = NULL;
 
1431
        myopt.title = _("List of groups");
 
1432
 
 
1433
        printQuery(res, &myopt, pset.queryFout);
 
1434
 
 
1435
        PQclear(res);
 
1436
        return true;
 
1437
}
 
1438
 
 
1439
 
 
1440
/*
 
1441
 * listTables()
 
1442
 *
 
1443
 * handler for \d, \dt, etc.
 
1444
 *
 
1445
 * tabtypes is an array of characters, specifying what info is desired:
 
1446
 * t - tables
 
1447
 * i - indexes
 
1448
 * v - views
 
1449
 * s - sequences
 
1450
 * S - system tables (pg_catalog)
 
1451
 * (any order of the above is fine)
 
1452
 */
 
1453
bool
 
1454
listTables(const char *tabtypes, const char *pattern, bool verbose)
 
1455
{
 
1456
        bool            showTables = strchr(tabtypes, 't') != NULL;
 
1457
        bool            showIndexes = strchr(tabtypes, 'i') != NULL;
 
1458
        bool            showViews = strchr(tabtypes, 'v') != NULL;
 
1459
        bool            showSeq = strchr(tabtypes, 's') != NULL;
 
1460
        bool            showSystem = strchr(tabtypes, 'S') != NULL;
 
1461
 
 
1462
        PQExpBufferData buf;
 
1463
        PGresult   *res;
 
1464
        printQueryOpt myopt = pset.popt;
 
1465
 
 
1466
        if (!(showTables || showIndexes || showViews || showSeq))
 
1467
                showTables = showViews = showSeq = true;
 
1468
 
 
1469
        initPQExpBuffer(&buf);
 
1470
 
 
1471
        printfPQExpBuffer(&buf,
 
1472
                                          "SELECT n.nspname as \"%s\",\n"
 
1473
                                          "  c.relname as \"%s\",\n"
 
1474
                                          "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' END as \"%s\",\n"
 
1475
                                          "  u.usename as \"%s\"",
 
1476
                                          _("Schema"), _("Name"),
 
1477
                                          _("table"), _("view"), _("index"), _("sequence"),
 
1478
                                          _("special"), _("Type"), _("Owner"));
 
1479
 
 
1480
        if (showIndexes)
 
1481
                appendPQExpBuffer(&buf,
 
1482
                                                  ",\n c2.relname as \"%s\"",
 
1483
                                                  _("Table"));
 
1484
 
 
1485
        if (verbose)
 
1486
                appendPQExpBuffer(&buf,
 
1487
                  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
 
1488
                                                  _("Description"));
 
1489
 
 
1490
        if (showIndexes)
 
1491
                appendPQExpBuffer(&buf,
 
1492
                                                  "\nFROM pg_catalog.pg_class c"
 
1493
                          "\n     JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
 
1494
                          "\n     JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid"
 
1495
                "\n     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner"
 
1496
                                                  "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
 
1497
        else
 
1498
                appendPQExpBuffer(&buf,
 
1499
                                                  "\nFROM pg_catalog.pg_class c"
 
1500
                "\n     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner"
 
1501
                                                  "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
 
1502
 
 
1503
        appendPQExpBuffer(&buf, "WHERE c.relkind IN (");
 
1504
        if (showTables)
 
1505
                appendPQExpBuffer(&buf, "'r',");
 
1506
        if (showViews)
 
1507
                appendPQExpBuffer(&buf, "'v',");
 
1508
        if (showIndexes)
 
1509
                appendPQExpBuffer(&buf, "'i',");
 
1510
        if (showSeq)
 
1511
                appendPQExpBuffer(&buf, "'S',");
 
1512
        if (showSystem && showTables)
 
1513
                appendPQExpBuffer(&buf, "'s',");
 
1514
        appendPQExpBuffer(&buf, "''");          /* dummy */
 
1515
        appendPQExpBuffer(&buf, ")\n");
 
1516
 
 
1517
        /*
 
1518
         * If showSystem is specified, show only system objects (those in
 
1519
         * pg_catalog).  Otherwise, suppress system objects, including those
 
1520
         * in pg_catalog and pg_toast.  (We don't want to hide temp tables
 
1521
         * though.)
 
1522
         */
 
1523
        if (showSystem)
 
1524
                appendPQExpBuffer(&buf, "      AND n.nspname = 'pg_catalog'\n");
 
1525
        else
 
1526
                appendPQExpBuffer(&buf, "      AND n.nspname NOT IN ('pg_catalog', 'pg_toast')\n");
 
1527
 
 
1528
        processNamePattern(&buf, pattern, true, false,
 
1529
                                           "n.nspname", "c.relname", NULL,
 
1530
                                           "pg_catalog.pg_table_is_visible(c.oid)");
 
1531
 
 
1532
        appendPQExpBuffer(&buf, "ORDER BY 1,2;");
 
1533
 
 
1534
        res = PSQLexec(buf.data, false);
 
1535
        termPQExpBuffer(&buf);
 
1536
        if (!res)
 
1537
                return false;
 
1538
 
 
1539
        if (PQntuples(res) == 0 && !QUIET())
 
1540
        {
 
1541
                if (pattern)
 
1542
                        fprintf(pset.queryFout, _("No matching relations found.\n"));
 
1543
                else
 
1544
                        fprintf(pset.queryFout, _("No relations found.\n"));
 
1545
        }
 
1546
        else
 
1547
        {
 
1548
                myopt.nullPrint = NULL;
 
1549
                myopt.title = _("List of relations");
 
1550
 
 
1551
                printQuery(res, &myopt, pset.queryFout);
 
1552
        }
 
1553
 
 
1554
        PQclear(res);
 
1555
        return true;
 
1556
}
 
1557
 
 
1558
 
 
1559
/*
 
1560
 * \dD
 
1561
 *
 
1562
 * Describes domains.
 
1563
 */
 
1564
bool
 
1565
listDomains(const char *pattern)
 
1566
{
 
1567
        PQExpBufferData buf;
 
1568
        PGresult   *res;
 
1569
        printQueryOpt myopt = pset.popt;
 
1570
 
 
1571
        initPQExpBuffer(&buf);
 
1572
 
 
1573
        printfPQExpBuffer(&buf,
 
1574
                                          "SELECT n.nspname as \"%s\",\n"
 
1575
                                          "       t.typname as \"%s\",\n"
 
1576
                                          "       pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
 
1577
                                          "       CASE WHEN t.typnotnull AND t.typdefault IS NOT NULL THEN 'not null default '||t.typdefault\n"
 
1578
                                          "            WHEN t.typnotnull AND t.typdefault IS NULL THEN 'not null'\n"
 
1579
                                          "            WHEN NOT t.typnotnull AND t.typdefault IS NOT NULL THEN 'default '||t.typdefault\n"
 
1580
                                          "            ELSE ''\n"
 
1581
                                          "       END as \"%s\"\n"
 
1582
                                          "FROM pg_catalog.pg_type t\n"
 
1583
        "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
 
1584
                                          "WHERE t.typtype = 'd'\n",
 
1585
                                          _("Schema"),
 
1586
                                          _("Name"),
 
1587
                                          _("Type"),
 
1588
                                          _("Modifier"));
 
1589
 
 
1590
        processNamePattern(&buf, pattern, true, false,
 
1591
                                           "n.nspname", "t.typname", NULL,
 
1592
                                           "pg_catalog.pg_type_is_visible(t.oid)");
 
1593
 
 
1594
        appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
 
1595
 
 
1596
        res = PSQLexec(buf.data, false);
 
1597
        termPQExpBuffer(&buf);
 
1598
        if (!res)
 
1599
                return false;
 
1600
 
 
1601
        myopt.nullPrint = NULL;
 
1602
        myopt.title = _("List of domains");
 
1603
 
 
1604
        printQuery(res, &myopt, pset.queryFout);
 
1605
 
 
1606
        PQclear(res);
 
1607
        return true;
 
1608
}
 
1609
 
 
1610
/*
 
1611
 * \dc
 
1612
 *
 
1613
 * Describes conversions.
 
1614
 */
 
1615
bool
 
1616
listConversions(const char *pattern)
 
1617
{
 
1618
        PQExpBufferData buf;
 
1619
        PGresult   *res;
 
1620
        printQueryOpt myopt = pset.popt;
 
1621
 
 
1622
        initPQExpBuffer(&buf);
 
1623
 
 
1624
        printfPQExpBuffer(&buf,
 
1625
                                          "SELECT n.nspname AS \"%s\",\n"
 
1626
                                          "       c.conname AS \"%s\",\n"
 
1627
        "       pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
 
1628
        "       pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
 
1629
                                          "       CASE WHEN c.condefault THEN '%s'\n"
 
1630
                                          "       ELSE '%s' END AS \"%s\"\n"
 
1631
                   "FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n\n"
 
1632
                                          "WHERE n.oid = c.connamespace\n",
 
1633
                                          _("Schema"),
 
1634
                                          _("Name"),
 
1635
                                          _("Source"),
 
1636
                                          _("Destination"),
 
1637
                                          _("yes"),
 
1638
                                          _("no"),
 
1639
                                          _("Default?"));
 
1640
 
 
1641
        processNamePattern(&buf, pattern, true, false,
 
1642
                                           "n.nspname", "c.conname", NULL,
 
1643
                                           "pg_catalog.pg_conversion_is_visible(c.oid)");
 
1644
 
 
1645
        appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
 
1646
 
 
1647
        res = PSQLexec(buf.data, false);
 
1648
        termPQExpBuffer(&buf);
 
1649
        if (!res)
 
1650
                return false;
 
1651
 
 
1652
        myopt.nullPrint = NULL;
 
1653
        myopt.title = _("List of conversions");
 
1654
 
 
1655
        printQuery(res, &myopt, pset.queryFout);
 
1656
 
 
1657
        PQclear(res);
 
1658
        return true;
 
1659
}
 
1660
 
 
1661
/*
 
1662
 * \dC
 
1663
 *
 
1664
 * Describes casts.
 
1665
 */
 
1666
bool
 
1667
listCasts(const char *pattern)
 
1668
{
 
1669
        PQExpBufferData buf;
 
1670
        PGresult   *res;
 
1671
        printQueryOpt myopt = pset.popt;
 
1672
 
 
1673
        initPQExpBuffer(&buf);
 
1674
/* NEED LEFT JOIN FOR BINARY CASTS */
 
1675
        printfPQExpBuffer(&buf,
 
1676
                   "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
 
1677
                   "       pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n"
 
1678
                                          "       CASE WHEN castfunc = 0 THEN '%s'\n"
 
1679
                                          "            ELSE p.proname\n"
 
1680
                                          "       END as \"%s\",\n"
 
1681
                                          "       CASE WHEN c.castcontext = 'e' THEN '%s'\n"
 
1682
                                          "            WHEN c.castcontext = 'a' THEN '%s'\n"
 
1683
                                          "            ELSE '%s'\n"
 
1684
                                          "       END as \"%s\"\n"
 
1685
                         "FROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
 
1686
                                          "     ON c.castfunc = p.oid\n"
 
1687
                                          "ORDER BY 1, 2",
 
1688
                                          _("Source type"),
 
1689
                                          _("Target type"),
 
1690
                                          _("(binary compatible)"),
 
1691
                                          _("Function"),
 
1692
                                          _("no"),
 
1693
                                          _("in assignment"),
 
1694
                                          _("yes"),
 
1695
                                          _("Implicit?"));
 
1696
 
 
1697
        res = PSQLexec(buf.data, false);
 
1698
        termPQExpBuffer(&buf);
 
1699
        if (!res)
 
1700
                return false;
 
1701
 
 
1702
        myopt.nullPrint = NULL;
 
1703
        myopt.title = _("List of casts");
 
1704
 
 
1705
        printQuery(res, &myopt, pset.queryFout);
 
1706
 
 
1707
        PQclear(res);
 
1708
        return true;
 
1709
}
 
1710
 
 
1711
/*
 
1712
 * \dn
 
1713
 *
 
1714
 * Describes schemas (namespaces)
 
1715
 */
 
1716
bool
 
1717
listSchemas(const char *pattern, bool verbose)
 
1718
{
 
1719
        PQExpBufferData buf;
 
1720
        PGresult   *res;
 
1721
        printQueryOpt myopt = pset.popt;
 
1722
 
 
1723
        initPQExpBuffer(&buf);
 
1724
        printfPQExpBuffer(&buf,
 
1725
                                          "SELECT n.nspname AS \"%s\",\n"
 
1726
                                          "       u.usename AS \"%s\"",
 
1727
                                          _("Name"), _("Owner"));
 
1728
 
 
1729
        if (verbose)
 
1730
                appendPQExpBuffer(&buf,
 
1731
                                                  ",\n  n.nspacl as \"%s\","
 
1732
                 "  pg_catalog.obj_description(n.oid, 'pg_namespace') as \"%s\"",
 
1733
                                                  _("Access privileges"), _("Description"));
 
1734
 
 
1735
        appendPQExpBuffer(&buf,
 
1736
          "\nFROM pg_catalog.pg_namespace n LEFT JOIN pg_catalog.pg_user u\n"
 
1737
                                          "       ON n.nspowner=u.usesysid\n"
 
1738
                                 "WHERE (n.nspname NOT LIKE 'pg\\\\_temp\\\\_%%' OR\n"
 
1739
           "             n.nspname = (pg_catalog.current_schemas(true))[1])\n");                /* temp schema is first */
 
1740
 
 
1741
        processNamePattern(&buf, pattern, true, false,
 
1742
                                           NULL, "n.nspname", NULL,
 
1743
                                           NULL);
 
1744
 
 
1745
        appendPQExpBuffer(&buf, "ORDER BY 1;");
 
1746
 
 
1747
        res = PSQLexec(buf.data, false);
 
1748
        termPQExpBuffer(&buf);
 
1749
        if (!res)
 
1750
                return false;
 
1751
 
 
1752
        myopt.nullPrint = NULL;
 
1753
        myopt.title = _("List of schemas");
 
1754
 
 
1755
        printQuery(res, &myopt, pset.queryFout);
 
1756
 
 
1757
        PQclear(res);
 
1758
        return true;
 
1759
}
 
1760
 
 
1761
 
 
1762
/*
 
1763
 * processNamePattern
 
1764
 *
 
1765
 * Scan a wildcard-pattern option and generate appropriate WHERE clauses
 
1766
 * to limit the set of objects returned.  The WHERE clauses are appended
 
1767
 * to buf.
 
1768
 *
 
1769
 * pattern: user-specified pattern option to a \d command, or NULL if none.
 
1770
 * have_where: true if caller already emitted WHERE.
 
1771
 * force_escape: always quote regexp special characters, even outside quotes.
 
1772
 * schemavar: name of WHERE variable to match against a schema-name pattern.
 
1773
 * Can be NULL if no schema.
 
1774
 * namevar: name of WHERE variable to match against an object-name pattern.
 
1775
 * altnamevar: NULL, or name of an alternate variable to match against name.
 
1776
 * visibilityrule: clause to use if we want to restrict to visible objects
 
1777
 * (for example, "pg_catalog.pg_table_is_visible(p.oid)").      Can be NULL.
 
1778
 */
 
1779
static void
 
1780
processNamePattern(PQExpBuffer buf, const char *pattern,
 
1781
                                   bool have_where, bool force_escape,
 
1782
                                   const char *schemavar, const char *namevar,
 
1783
                                   const char *altnamevar, const char *visibilityrule)
 
1784
{
 
1785
        PQExpBufferData schemabuf;
 
1786
        PQExpBufferData namebuf;
 
1787
        bool            inquotes;
 
1788
        const char *cp;
 
1789
        int                     i;
 
1790
 
 
1791
#define WHEREAND() \
 
1792
        (appendPQExpBuffer(buf, have_where ? "      AND " : "WHERE "), have_where = true)
 
1793
 
 
1794
        if (pattern == NULL)
 
1795
        {
 
1796
                /* Default: select all visible objects */
 
1797
                if (visibilityrule)
 
1798
                {
 
1799
                        WHEREAND();
 
1800
                        appendPQExpBuffer(buf, "%s\n", visibilityrule);
 
1801
                }
 
1802
                return;
 
1803
        }
 
1804
 
 
1805
        initPQExpBuffer(&schemabuf);
 
1806
        initPQExpBuffer(&namebuf);
 
1807
 
 
1808
        /*
 
1809
         * Parse the pattern, converting quotes and lower-casing unquoted
 
1810
         * letters; we assume this was NOT done by scan_option.  Also, adjust
 
1811
         * shell-style wildcard characters into regexp notation.
 
1812
         */
 
1813
        inquotes = false;
 
1814
        cp = pattern;
 
1815
 
 
1816
        while (*cp)
 
1817
        {
 
1818
                if (*cp == '"')
 
1819
                {
 
1820
                        if (inquotes && cp[1] == '"')
 
1821
                        {
 
1822
                                /* emit one quote */
 
1823
                                appendPQExpBufferChar(&namebuf, '"');
 
1824
                                cp++;
 
1825
                        }
 
1826
                        inquotes = !inquotes;
 
1827
                        cp++;
 
1828
                }
 
1829
                else if (!inquotes && isupper((unsigned char) *cp))
 
1830
                {
 
1831
                        appendPQExpBufferChar(&namebuf,
 
1832
                                                                  pg_tolower((unsigned char) *cp));
 
1833
                        cp++;
 
1834
                }
 
1835
                else if (!inquotes && *cp == '*')
 
1836
                {
 
1837
                        appendPQExpBuffer(&namebuf, ".*");
 
1838
                        cp++;
 
1839
                }
 
1840
                else if (!inquotes && *cp == '?')
 
1841
                {
 
1842
                        appendPQExpBufferChar(&namebuf, '.');
 
1843
                        cp++;
 
1844
                }
 
1845
                else if (!inquotes && *cp == '.')
 
1846
                {
 
1847
                        /* Found schema/name separator, move current pattern to schema */
 
1848
                        resetPQExpBuffer(&schemabuf);
 
1849
                        appendPQExpBufferStr(&schemabuf, namebuf.data);
 
1850
                        resetPQExpBuffer(&namebuf);
 
1851
                        cp++;
 
1852
                }
 
1853
                else
 
1854
                {
 
1855
                        /*
 
1856
                         * Ordinary data character, transfer to pattern
 
1857
                         *
 
1858
                         * Inside double quotes, or at all times if parsing an operator
 
1859
                         * name, quote regexp special characters with a backslash to
 
1860
                         * avoid regexp errors.  Outside quotes, however, let them
 
1861
                         * pass through as-is; this lets knowledgeable users build
 
1862
                         * regexp expressions that are more powerful than shell-style
 
1863
                         * patterns.
 
1864
                         */
 
1865
                        if ((inquotes || force_escape) &&
 
1866
                                strchr("|*+?()[]{}.^$\\", *cp))
 
1867
                                appendPQExpBuffer(&namebuf, "\\\\");
 
1868
 
 
1869
                        /* Ensure chars special to string literals are passed properly */
 
1870
                        if (*cp == '\'' || *cp == '\\')
 
1871
                                appendPQExpBufferChar(&namebuf, *cp);
 
1872
 
 
1873
                        i = PQmblen(cp, pset.encoding);
 
1874
                        while (i--)
 
1875
                        {
 
1876
                                appendPQExpBufferChar(&namebuf, *cp);
 
1877
                                cp++;
 
1878
                        }
 
1879
                }
 
1880
        }
 
1881
 
 
1882
        /*
 
1883
         * Now decide what we need to emit.
 
1884
         */
 
1885
        if (schemabuf.len > 0)
 
1886
        {
 
1887
                /* We have a schema pattern, so constrain the schemavar */
 
1888
 
 
1889
                appendPQExpBufferChar(&schemabuf, '$');
 
1890
                /* Optimize away ".*$", and possibly the whole pattern */
 
1891
                if (schemabuf.len >= 3 &&
 
1892
                        strcmp(schemabuf.data + (schemabuf.len - 3), ".*$") == 0)
 
1893
                        schemabuf.data[schemabuf.len - 3] = '\0';
 
1894
 
 
1895
                if (schemabuf.data[0] && schemavar)
 
1896
                {
 
1897
                        WHEREAND();
 
1898
                        appendPQExpBuffer(buf, "%s ~ '^%s'\n",
 
1899
                                                          schemavar, schemabuf.data);
 
1900
                }
 
1901
        }
 
1902
        else
 
1903
        {
 
1904
                /* No schema pattern given, so select only visible objects */
 
1905
                if (visibilityrule)
 
1906
                {
 
1907
                        WHEREAND();
 
1908
                        appendPQExpBuffer(buf, "%s\n", visibilityrule);
 
1909
                }
 
1910
        }
 
1911
 
 
1912
        if (namebuf.len > 0)
 
1913
        {
 
1914
                /* We have a name pattern, so constrain the namevar(s) */
 
1915
 
 
1916
                appendPQExpBufferChar(&namebuf, '$');
 
1917
                /* Optimize away ".*$", and possibly the whole pattern */
 
1918
                if (namebuf.len >= 3 &&
 
1919
                        strcmp(namebuf.data + (namebuf.len - 3), ".*$") == 0)
 
1920
                        namebuf.data[namebuf.len - 3] = '\0';
 
1921
 
 
1922
                if (namebuf.data[0])
 
1923
                {
 
1924
                        WHEREAND();
 
1925
                        if (altnamevar)
 
1926
                                appendPQExpBuffer(buf,
 
1927
                                                                  "(%s ~ '^%s'\n"
 
1928
                                                                  "        OR %s ~ '^%s')\n",
 
1929
                                                                  namevar, namebuf.data,
 
1930
                                                                  altnamevar, namebuf.data);
 
1931
                        else
 
1932
                                appendPQExpBuffer(buf,
 
1933
                                                                  "%s ~ '^%s'\n",
 
1934
                                                                  namevar, namebuf.data);
 
1935
                }
 
1936
        }
 
1937
 
 
1938
        termPQExpBuffer(&schemabuf);
 
1939
        termPQExpBuffer(&namebuf);
 
1940
 
 
1941
#undef WHEREAND
 
1942
}