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

Generated by: LCOV version 1.14