LCOV - code coverage report
Current view: top level - src/bin/psql - describe.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 1497 2515 59.5 %
Date: 2021-12-04 21:09:12 Functions: 29 51 56.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14