LCOV - code coverage report
Current view: top level - src/bin/psql - describe.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 2044 2608 78.4 %
Date: 2024-11-21 08:14:44 Functions: 50 55 90.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14