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

Generated by: LCOV version 1.16