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

Generated by: LCOV version 2.0-1