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

Generated by: LCOV version 2.0-1