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

Generated by: LCOV version 1.14