LCOV - code coverage report
Current view: top level - src/bin/psql - describe.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 908 1964 46.2 %
Date: 2017-09-29 13:40:31 Functions: 15 43 34.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11