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

Generated by: LCOV version 1.13