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

Generated by: LCOV version 2.0-1