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

Generated by: LCOV version 1.13