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

Generated by: LCOV version 1.13