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

Generated by: LCOV version 1.13