PostgreSQL Source Code  git master
describe.c File Reference
#include "postgres_fe.h"
#include <ctype.h>
#include "catalog/pg_am.h"
#include "catalog/pg_attribute_d.h"
#include "catalog/pg_cast_d.h"
#include "catalog/pg_class_d.h"
#include "catalog/pg_default_acl_d.h"
#include "common.h"
#include "common/logging.h"
#include "describe.h"
#include "fe_utils/mbprint.h"
#include "fe_utils/print.h"
#include "fe_utils/string_utils.h"
#include "settings.h"
#include "variables.h"
Include dependency graph for describe.c:

Go to the source code of this file.

Functions

static const char * map_typename_pattern (const char *pattern)
 
static bool describeOneTableDetails (const char *schemaname, const char *relationname, const char *oid, bool verbose)
 
static void add_tablespace_footer (printTableContent *const cont, char relkind, Oid tablespace, const bool newline)
 
static void add_role_attribute (PQExpBuffer buf, const char *const str)
 
static bool listTSParsersVerbose (const char *pattern)
 
static bool describeOneTSParser (const char *oid, const char *nspname, const char *prsname)
 
static bool listTSConfigsVerbose (const char *pattern)
 
static bool describeOneTSConfig (const char *oid, const char *nspname, const char *cfgname, const char *pnspname, const char *prsname)
 
static void printACLColumn (PQExpBuffer buf, const char *colname)
 
static bool listOneExtensionContents (const char *extname, const char *oid)
 
static bool validateSQLNamePattern (PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule, bool *added_clause, int maxparts)
 
bool describeAggregates (const char *pattern, bool verbose, bool showSystem)
 
bool describeAccessMethods (const char *pattern, bool verbose)
 
bool describeTablespaces (const char *pattern, bool verbose)
 
bool describeFunctions (const char *functypes, const char *func_pattern, char **arg_patterns, int num_arg_patterns, bool verbose, bool showSystem)
 
bool describeTypes (const char *pattern, bool verbose, bool showSystem)
 
bool describeOperators (const char *oper_pattern, char **arg_patterns, int num_arg_patterns, bool verbose, bool showSystem)
 
bool listAllDbs (const char *pattern, bool verbose)
 
bool permissionsList (const char *pattern)
 
bool listDefaultACLs (const char *pattern)
 
bool objectDescription (const char *pattern, bool showSystem)
 
bool describeTableDetails (const char *pattern, bool verbose, bool showSystem)
 
bool describeRoles (const char *pattern, bool verbose, bool showSystem)
 
bool listDbRoleSettings (const char *pattern, const char *pattern2)
 
bool listTables (const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
 
bool listPartitionedTables (const char *reltypes, const char *pattern, bool verbose)
 
bool listLanguages (const char *pattern, bool verbose, bool showSystem)
 
bool listDomains (const char *pattern, bool verbose, bool showSystem)
 
bool listConversions (const char *pattern, bool verbose, bool showSystem)
 
bool describeConfigurationParameters (const char *pattern, bool verbose, bool showSystem)
 
bool listEventTriggers (const char *pattern, bool verbose)
 
bool listExtendedStats (const char *pattern)
 
bool listCasts (const char *pattern, bool verbose)
 
bool listCollations (const char *pattern, bool verbose, bool showSystem)
 
bool listSchemas (const char *pattern, bool verbose, bool showSystem)
 
bool listTSParsers (const char *pattern, bool verbose)
 
bool listTSDictionaries (const char *pattern, bool verbose)
 
bool listTSTemplates (const char *pattern, bool verbose)
 
bool listTSConfigs (const char *pattern, bool verbose)
 
bool listForeignDataWrappers (const char *pattern, bool verbose)
 
bool listForeignServers (const char *pattern, bool verbose)
 
bool listUserMappings (const char *pattern, bool verbose)
 
bool listForeignTables (const char *pattern, bool verbose)
 
bool listExtensions (const char *pattern)
 
bool listExtensionContents (const char *pattern)
 
bool listPublications (const char *pattern)
 
static bool addFooterToPublicationDesc (PQExpBuffer buf, const char *footermsg, bool as_schema, printTableContent *const cont)
 
bool describePublications (const char *pattern)
 
bool describeSubscriptions (const char *pattern, bool verbose)
 
bool listOperatorClasses (const char *access_method_pattern, const char *type_pattern, bool verbose)
 
bool listOperatorFamilies (const char *access_method_pattern, const char *type_pattern, bool verbose)
 
bool listOpFamilyOperators (const char *access_method_pattern, const char *family_pattern, bool verbose)
 
bool listOpFamilyFunctions (const char *access_method_pattern, const char *family_pattern, bool verbose)
 
bool listLargeObjects (bool verbose)
 

Function Documentation

◆ add_role_attribute()

static void add_role_attribute ( PQExpBuffer  buf,
const char *const  str 
)
static

Definition at line 3711 of file describe.c.

3712 {
3713  if (buf->len > 0)
3714  appendPQExpBufferStr(buf, ", ");
3715 
3717 }
static char * buf
Definition: pg_test_fsync.c:67
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369

References appendPQExpBufferStr(), buf, and generate_unaccent_rules::str.

Referenced by describeRoles().

◆ add_tablespace_footer()

static void add_tablespace_footer ( printTableContent *const  cont,
char  relkind,
Oid  tablespace,
const bool  newline 
)
static

Definition at line 3506 of file describe.c.

3508 {
3509  /* relkinds for which we support tablespaces */
3510  if (relkind == RELKIND_RELATION ||
3511  relkind == RELKIND_MATVIEW ||
3512  relkind == RELKIND_INDEX ||
3513  relkind == RELKIND_PARTITIONED_TABLE ||
3514  relkind == RELKIND_PARTITIONED_INDEX ||
3515  relkind == RELKIND_TOASTVALUE)
3516  {
3517  /*
3518  * We ignore the database default tablespace so that users not using
3519  * tablespaces don't need to know about them.
3520  */
3521  if (tablespace != 0)
3522  {
3523  PGresult *result = NULL;
3525 
3526  initPQExpBuffer(&buf);
3528  "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3529  "WHERE oid = '%u';", tablespace);
3530  result = PSQLexec(buf.data);
3531  if (!result)
3532  {
3533  termPQExpBuffer(&buf);
3534  return;
3535  }
3536  /* Should always be the case, but.... */
3537  if (PQntuples(result) > 0)
3538  {
3539  if (newline)
3540  {
3541  /* Add the tablespace as a new footer */
3542  printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3543  PQgetvalue(result, 0, 0));
3544  printTableAddFooter(cont, buf.data);
3545  }
3546  else
3547  {
3548  /* Append the tablespace to the latest footer */
3549  printfPQExpBuffer(&buf, "%s", cont->footer->data);
3550 
3551  /*-------
3552  translator: before this string there's an index description like
3553  '"foo_pkey" PRIMARY KEY, btree (a)' */
3554  appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3555  PQgetvalue(result, 0, 0));
3556  printTableSetFooter(cont, buf.data);
3557  }
3558  }
3559  PQclear(result);
3560  termPQExpBuffer(&buf);
3561  }
3562  }
3563 }
PGresult * PSQLexec(const char *query)
Definition: common.c:555
#define _(x)
Definition: elog.c:89
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3415
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3810
void printTableSetFooter(printTableContent *const content, const char *footer)
Definition: print.c:3277
void printTableAddFooter(printTableContent *const content, const char *footer)
Definition: print.c:3252
char * tablespace
Definition: pgbench.c:229
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:237
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
static chr newline(void)
Definition: regc_lex.c:1001
printTableFooter * footer
Definition: print.h:161
char * data
Definition: print.h:139

References _, appendPQExpBuffer(), buf, printTableFooter::data, printTableContent::footer, initPQExpBuffer(), newline(), PQclear(), PQgetvalue(), PQntuples(), printfPQExpBuffer(), printTableAddFooter(), printTableSetFooter(), PSQLexec(), tablespace, and termPQExpBuffer().

Referenced by describeOneTableDetails().

◆ addFooterToPublicationDesc()

static bool addFooterToPublicationDesc ( PQExpBuffer  buf,
const char *  footermsg,
bool  as_schema,
printTableContent *const  cont 
)
static

Definition at line 6119 of file describe.c.

6121 {
6122  PGresult *res;
6123  int count = 0;
6124  int i = 0;
6125 
6126  res = PSQLexec(buf->data);
6127  if (!res)
6128  return false;
6129  else
6130  count = PQntuples(res);
6131 
6132  if (count > 0)
6133  printTableAddFooter(cont, footermsg);
6134 
6135  for (i = 0; i < count; i++)
6136  {
6137  if (as_schema)
6138  printfPQExpBuffer(buf, " \"%s\"", PQgetvalue(res, i, 0));
6139  else
6140  {
6141  printfPQExpBuffer(buf, " \"%s.%s\"", PQgetvalue(res, i, 0),
6142  PQgetvalue(res, i, 1));
6143 
6144  if (!PQgetisnull(res, i, 3))
6145  appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
6146 
6147  if (!PQgetisnull(res, i, 2))
6148  appendPQExpBuffer(buf, " WHERE %s", PQgetvalue(res, i, 2));
6149  }
6150 
6151  printTableAddFooter(cont, buf->data);
6152  }
6153 
6154  PQclear(res);
6155  return true;
6156 }
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3835
int i
Definition: isn.c:73

References appendPQExpBuffer(), buf, i, PQclear(), PQgetisnull(), PQgetvalue(), PQntuples(), printfPQExpBuffer(), printTableAddFooter(), PSQLexec(), and res.

Referenced by describePublications().

◆ describeAccessMethods()

bool describeAccessMethods ( const char *  pattern,
bool  verbose 
)

Definition at line 139 of file describe.c.

140 {
142  PGresult *res;
143  printQueryOpt myopt = pset.popt;
144  static const bool translate_columns[] = {false, true, false, false};
145 
146  if (pset.sversion < 90600)
147  {
148  char sverbuf[32];
149 
150  pg_log_error("The server (version %s) does not support access methods.",
152  sverbuf, sizeof(sverbuf)));
153  return true;
154  }
155 
157 
159  "SELECT amname AS \"%s\",\n"
160  " CASE amtype"
161  " WHEN 'i' THEN '%s'"
162  " WHEN 't' THEN '%s'"
163  " END AS \"%s\"",
164  gettext_noop("Name"),
165  gettext_noop("Index"),
166  gettext_noop("Table"),
167  gettext_noop("Type"));
168 
169  if (verbose)
170  {
172  ",\n amhandler AS \"%s\",\n"
173  " pg_catalog.obj_description(oid, 'pg_am') AS \"%s\"",
174  gettext_noop("Handler"),
175  gettext_noop("Description"));
176  }
177 
179  "\nFROM pg_catalog.pg_am\n");
180 
181  if (!validateSQLNamePattern(&buf, pattern, false, false,
182  NULL, "amname", NULL,
183  NULL,
184  NULL, 1))
185  return false;
186 
187  appendPQExpBufferStr(&buf, "ORDER BY 1;");
188 
189  res = PSQLexec(buf.data);
191  if (!res)
192  return false;
193 
194  myopt.nullPrint = NULL;
195  myopt.title = _("List of access methods");
196  myopt.translate_header = true;
197  myopt.translate_columns = translate_columns;
198  myopt.n_translate_columns = lengthof(translate_columns);
199 
200  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
201 
202  PQclear(res);
203  return true;
204 }
#define gettext_noop(x)
Definition: c.h:1205
#define lengthof(array)
Definition: c.h:745
static bool validateSQLNamePattern(PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule, bool *added_clause, int maxparts)
Definition: describe.c:5996
void printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, bool is_pager, FILE *flog)
Definition: print.c:3490
#define pg_log_error(...)
Definition: logging.h:106
static int verbose
PsqlSettings pset
Definition: startup.c:32
char * formatPGVersionNumber(int version_number, bool include_minor, char *buf, size_t buflen)
Definition: string_utils.c:177
printQueryOpt popt
Definition: settings.h:91
FILE * logfile
Definition: settings.h:116
FILE * queryFout
Definition: settings.h:84
const bool * translate_columns
Definition: print.h:174
char * nullPrint
Definition: print.h:170
char * title
Definition: print.h:171
bool translate_header
Definition: print.h:173
int n_translate_columns
Definition: print.h:176

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, formatPGVersionNumber(), gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, pg_log_error, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_columns, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ describeAggregates()

bool describeAggregates ( const char *  pattern,
bool  verbose,
bool  showSystem 
)

Definition at line 71 of file describe.c.

72 {
74  PGresult *res;
75  printQueryOpt myopt = pset.popt;
76 
78 
80  "SELECT n.nspname as \"%s\",\n"
81  " p.proname AS \"%s\",\n"
82  " pg_catalog.format_type(p.prorettype, NULL) AS \"%s\",\n"
83  " CASE WHEN p.pronargs = 0\n"
84  " THEN CAST('*' AS pg_catalog.text)\n"
85  " ELSE pg_catalog.pg_get_function_arguments(p.oid)\n"
86  " END AS \"%s\",\n",
87  gettext_noop("Schema"),
88  gettext_noop("Name"),
89  gettext_noop("Result data type"),
90  gettext_noop("Argument data types"));
91 
92  if (pset.sversion >= 110000)
94  " pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
95  "FROM pg_catalog.pg_proc p\n"
96  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
97  "WHERE p.prokind = 'a'\n",
98  gettext_noop("Description"));
99  else
101  " pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
102  "FROM pg_catalog.pg_proc p\n"
103  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
104  "WHERE p.proisagg\n",
105  gettext_noop("Description"));
106 
107  if (!showSystem && !pattern)
108  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
109  " AND n.nspname <> 'information_schema'\n");
110 
111  if (!validateSQLNamePattern(&buf, pattern, true, false,
112  "n.nspname", "p.proname", NULL,
113  "pg_catalog.pg_function_is_visible(p.oid)",
114  NULL, 3))
115  return false;
116 
117  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
118 
119  res = PSQLexec(buf.data);
121  if (!res)
122  return false;
123 
124  myopt.nullPrint = NULL;
125  myopt.title = _("List of aggregate functions");
126  myopt.translate_header = true;
127 
128  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
129 
130  PQclear(res);
131  return true;
132 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, and validateSQLNamePattern().

Referenced by exec_command_d().

◆ describeConfigurationParameters()

bool describeConfigurationParameters ( const char *  pattern,
bool  verbose,
bool  showSystem 
)

Definition at line 4425 of file describe.c.

4427 {
4429  PGresult *res;
4430  printQueryOpt myopt = pset.popt;
4431 
4432  initPQExpBuffer(&buf);
4434  "SELECT s.name AS \"%s\", "
4435  "pg_catalog.current_setting(s.name) AS \"%s\"",
4436  gettext_noop("Parameter"),
4437  gettext_noop("Value"));
4438 
4439  if (verbose)
4440  {
4442  ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4443  gettext_noop("Type"),
4444  gettext_noop("Context"));
4445  if (pset.sversion >= 150000)
4446  printACLColumn(&buf, "p.paracl");
4447  else
4448  appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4449  gettext_noop("Access privileges"));
4450  }
4451 
4452  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4453 
4454  if (verbose && pset.sversion >= 150000)
4456  " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4457  " ON pg_catalog.lower(s.name) = p.parname\n");
4458 
4459  if (pattern)
4460  processSQLNamePattern(pset.db, &buf, pattern,
4461  false, false,
4462  NULL, "pg_catalog.lower(s.name)", NULL,
4463  NULL, NULL, NULL);
4464  else
4465  appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
4466  " s.setting IS DISTINCT FROM s.boot_val\n");
4467 
4468  appendPQExpBufferStr(&buf, "ORDER BY 1;");
4469 
4470  res = PSQLexec(buf.data);
4471  termPQExpBuffer(&buf);
4472  if (!res)
4473  return false;
4474 
4475  myopt.nullPrint = NULL;
4476  if (pattern)
4477  myopt.title = _("List of configuration parameters");
4478  else
4479  myopt.title = _("List of non-default configuration parameters");
4480  myopt.translate_header = true;
4481 
4482  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4483 
4484  PQclear(res);
4485  return true;
4486 }
static void printACLColumn(PQExpBuffer buf, const char *colname)
Definition: describe.c:6449
bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule, PQExpBuffer dbnamebuf, int *dotcnt)
Definition: string_utils.c:891
PGconn * db
Definition: settings.h:82

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, _psqlSettings::db, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printACLColumn(), printfPQExpBuffer(), printQuery(), processSQLNamePattern(), pset, PSQLexec(), _psqlSettings::queryFout, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, and verbose.

Referenced by exec_command_d().

◆ describeFunctions()

bool describeFunctions ( const char *  functypes,
const char *  func_pattern,
char **  arg_patterns,
int  num_arg_patterns,
bool  verbose,
bool  showSystem 
)

Definition at line 282 of file describe.c.

285 {
286  bool showAggregate = strchr(functypes, 'a') != NULL;
287  bool showNormal = strchr(functypes, 'n') != NULL;
288  bool showProcedure = strchr(functypes, 'p') != NULL;
289  bool showTrigger = strchr(functypes, 't') != NULL;
290  bool showWindow = strchr(functypes, 'w') != NULL;
291  bool have_where;
293  PGresult *res;
294  printQueryOpt myopt = pset.popt;
295  static const bool translate_columns[] = {false, false, false, false, true, true, true, false, true, false, false, false, false};
296 
297  /* No "Parallel" column before 9.6 */
298  static const bool translate_columns_pre_96[] = {false, false, false, false, true, true, false, true, false, false, false, false};
299 
300  if (strlen(functypes) != strspn(functypes, "anptwS+"))
301  {
302  pg_log_error("\\df only takes [anptwS+] as options");
303  return true;
304  }
305 
306  if (showProcedure && pset.sversion < 110000)
307  {
308  char sverbuf[32];
309 
310  pg_log_error("\\df does not take a \"%c\" option with server version %s",
311  'p',
313  sverbuf, sizeof(sverbuf)));
314  return true;
315  }
316 
317  if (!showAggregate && !showNormal && !showProcedure && !showTrigger && !showWindow)
318  {
319  showAggregate = showNormal = showTrigger = showWindow = true;
320  if (pset.sversion >= 110000)
321  showProcedure = true;
322  }
323 
325 
327  "SELECT n.nspname as \"%s\",\n"
328  " p.proname as \"%s\",\n",
329  gettext_noop("Schema"),
330  gettext_noop("Name"));
331 
332  if (pset.sversion >= 110000)
334  " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
335  " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
336  " CASE p.prokind\n"
337  " WHEN 'a' THEN '%s'\n"
338  " WHEN 'w' THEN '%s'\n"
339  " WHEN 'p' THEN '%s'\n"
340  " ELSE '%s'\n"
341  " END as \"%s\"",
342  gettext_noop("Result data type"),
343  gettext_noop("Argument data types"),
344  /* translator: "agg" is short for "aggregate" */
345  gettext_noop("agg"),
346  gettext_noop("window"),
347  gettext_noop("proc"),
348  gettext_noop("func"),
349  gettext_noop("Type"));
350  else
352  " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
353  " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
354  " CASE\n"
355  " WHEN p.proisagg THEN '%s'\n"
356  " WHEN p.proiswindow THEN '%s'\n"
357  " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
358  " ELSE '%s'\n"
359  " END as \"%s\"",
360  gettext_noop("Result data type"),
361  gettext_noop("Argument data types"),
362  /* translator: "agg" is short for "aggregate" */
363  gettext_noop("agg"),
364  gettext_noop("window"),
365  gettext_noop("trigger"),
366  gettext_noop("func"),
367  gettext_noop("Type"));
368 
369  if (verbose)
370  {
372  ",\n CASE\n"
373  " WHEN p.provolatile = 'i' THEN '%s'\n"
374  " WHEN p.provolatile = 's' THEN '%s'\n"
375  " WHEN p.provolatile = 'v' THEN '%s'\n"
376  " END as \"%s\"",
377  gettext_noop("immutable"),
378  gettext_noop("stable"),
379  gettext_noop("volatile"),
380  gettext_noop("Volatility"));
381  if (pset.sversion >= 90600)
383  ",\n CASE\n"
384  " WHEN p.proparallel = 'r' THEN '%s'\n"
385  " WHEN p.proparallel = 's' THEN '%s'\n"
386  " WHEN p.proparallel = 'u' THEN '%s'\n"
387  " END as \"%s\"",
388  gettext_noop("restricted"),
389  gettext_noop("safe"),
390  gettext_noop("unsafe"),
391  gettext_noop("Parallel"));
393  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
394  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\"",
395  gettext_noop("Owner"),
396  gettext_noop("definer"),
397  gettext_noop("invoker"),
398  gettext_noop("Security"));
399  appendPQExpBufferStr(&buf, ",\n ");
400  printACLColumn(&buf, "p.proacl");
402  ",\n l.lanname as \"%s\"",
403  gettext_noop("Language"));
404  if (pset.sversion >= 140000)
406  ",\n COALESCE(pg_catalog.pg_get_function_sqlbody(p.oid), p.prosrc) as \"%s\"",
407  gettext_noop("Source code"));
408  else
410  ",\n p.prosrc as \"%s\"",
411  gettext_noop("Source code"));
413  ",\n pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"",
414  gettext_noop("Description"));
415  }
416 
418  "\nFROM pg_catalog.pg_proc p"
419  "\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
420 
421  for (int i = 0; i < num_arg_patterns; i++)
422  {
424  " LEFT JOIN pg_catalog.pg_type t%d ON t%d.oid = p.proargtypes[%d]\n"
425  " LEFT JOIN pg_catalog.pg_namespace nt%d ON nt%d.oid = t%d.typnamespace\n",
426  i, i, i, i, i, i);
427  }
428 
429  if (verbose)
431  " LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n");
432 
433  have_where = false;
434 
435  /* filter by function type, if requested */
436  if (showNormal && showAggregate && showProcedure && showTrigger && showWindow)
437  /* Do nothing */ ;
438  else if (showNormal)
439  {
440  if (!showAggregate)
441  {
442  if (have_where)
443  appendPQExpBufferStr(&buf, " AND ");
444  else
445  {
446  appendPQExpBufferStr(&buf, "WHERE ");
447  have_where = true;
448  }
449  if (pset.sversion >= 110000)
450  appendPQExpBufferStr(&buf, "p.prokind <> 'a'\n");
451  else
452  appendPQExpBufferStr(&buf, "NOT p.proisagg\n");
453  }
454  if (!showProcedure && pset.sversion >= 110000)
455  {
456  if (have_where)
457  appendPQExpBufferStr(&buf, " AND ");
458  else
459  {
460  appendPQExpBufferStr(&buf, "WHERE ");
461  have_where = true;
462  }
463  appendPQExpBufferStr(&buf, "p.prokind <> 'p'\n");
464  }
465  if (!showTrigger)
466  {
467  if (have_where)
468  appendPQExpBufferStr(&buf, " AND ");
469  else
470  {
471  appendPQExpBufferStr(&buf, "WHERE ");
472  have_where = true;
473  }
474  appendPQExpBufferStr(&buf, "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n");
475  }
476  if (!showWindow)
477  {
478  if (have_where)
479  appendPQExpBufferStr(&buf, " AND ");
480  else
481  {
482  appendPQExpBufferStr(&buf, "WHERE ");
483  have_where = true;
484  }
485  if (pset.sversion >= 110000)
486  appendPQExpBufferStr(&buf, "p.prokind <> 'w'\n");
487  else
488  appendPQExpBufferStr(&buf, "NOT p.proiswindow\n");
489  }
490  }
491  else
492  {
493  bool needs_or = false;
494 
495  appendPQExpBufferStr(&buf, "WHERE (\n ");
496  have_where = true;
497  /* Note: at least one of these must be true ... */
498  if (showAggregate)
499  {
500  if (pset.sversion >= 110000)
501  appendPQExpBufferStr(&buf, "p.prokind = 'a'\n");
502  else
503  appendPQExpBufferStr(&buf, "p.proisagg\n");
504  needs_or = true;
505  }
506  if (showTrigger)
507  {
508  if (needs_or)
509  appendPQExpBufferStr(&buf, " OR ");
511  "p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype\n");
512  needs_or = true;
513  }
514  if (showProcedure)
515  {
516  if (needs_or)
517  appendPQExpBufferStr(&buf, " OR ");
518  appendPQExpBufferStr(&buf, "p.prokind = 'p'\n");
519  needs_or = true;
520  }
521  if (showWindow)
522  {
523  if (needs_or)
524  appendPQExpBufferStr(&buf, " OR ");
525  if (pset.sversion >= 110000)
526  appendPQExpBufferStr(&buf, "p.prokind = 'w'\n");
527  else
528  appendPQExpBufferStr(&buf, "p.proiswindow\n");
529  }
530  appendPQExpBufferStr(&buf, " )\n");
531  }
532 
533  if (!validateSQLNamePattern(&buf, func_pattern, have_where, false,
534  "n.nspname", "p.proname", NULL,
535  "pg_catalog.pg_function_is_visible(p.oid)",
536  NULL, 3))
537  return false;
538 
539  for (int i = 0; i < num_arg_patterns; i++)
540  {
541  if (strcmp(arg_patterns[i], "-") != 0)
542  {
543  /*
544  * Match type-name patterns against either internal or external
545  * name, like \dT. Unlike \dT, there seems no reason to
546  * discriminate against arrays or composite types.
547  */
548  char nspname[64];
549  char typname[64];
550  char ft[64];
551  char tiv[64];
552 
553  snprintf(nspname, sizeof(nspname), "nt%d.nspname", i);
554  snprintf(typname, sizeof(typname), "t%d.typname", i);
555  snprintf(ft, sizeof(ft),
556  "pg_catalog.format_type(t%d.oid, NULL)", i);
557  snprintf(tiv, sizeof(tiv),
558  "pg_catalog.pg_type_is_visible(t%d.oid)", i);
560  map_typename_pattern(arg_patterns[i]),
561  true, false,
562  nspname, typname, ft, tiv,
563  NULL, 3))
564  return false;
565  }
566  else
567  {
568  /* "-" pattern specifies no such parameter */
569  appendPQExpBuffer(&buf, " AND t%d.typname IS NULL\n", i);
570  }
571  }
572 
573  if (!showSystem && !func_pattern)
574  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
575  " AND n.nspname <> 'information_schema'\n");
576 
577  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;");
578 
579  res = PSQLexec(buf.data);
581  if (!res)
582  return false;
583 
584  myopt.nullPrint = NULL;
585  myopt.title = _("List of functions");
586  myopt.translate_header = true;
587  if (pset.sversion >= 90600)
588  {
589  myopt.translate_columns = translate_columns;
590  myopt.n_translate_columns = lengthof(translate_columns);
591  }
592  else
593  {
594  myopt.translate_columns = translate_columns_pre_96;
595  myopt.n_translate_columns = lengthof(translate_columns_pre_96);
596  }
597 
598  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
599 
600  PQclear(res);
601  return true;
602 }
static const char * map_typename_pattern(const char *pattern)
Definition: describe.c:714
NameData typname
Definition: pg_type.h:41
#define snprintf
Definition: port.h:225

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, formatPGVersionNumber(), gettext_noop, i, initPQExpBuffer(), lengthof, _psqlSettings::logfile, map_typename_pattern(), printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, pg_log_error, _psqlSettings::popt, PQclear(), printACLColumn(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, snprintf, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_columns, printQueryOpt::translate_header, typname, validateSQLNamePattern(), and verbose.

Referenced by exec_command_dfo().

◆ describeOneTableDetails()

static bool describeOneTableDetails ( const char *  schemaname,
const char *  relationname,
const char *  oid,
bool  verbose 
)
static

Definition at line 1488 of file describe.c.

1492 {
1493  bool retval = false;
1495  PGresult *res = NULL;
1496  printTableOpt myopt = pset.popt.topt;
1497  printTableContent cont;
1498  bool printTableInitialized = false;
1499  int i;
1500  char *view_def = NULL;
1501  char *headers[12];
1502  PQExpBufferData title;
1504  int cols;
1505  int attname_col = -1, /* column indexes in "res" */
1506  atttype_col = -1,
1507  attrdef_col = -1,
1508  attnotnull_col = -1,
1509  attcoll_col = -1,
1510  attidentity_col = -1,
1511  attgenerated_col = -1,
1512  isindexkey_col = -1,
1513  indexdef_col = -1,
1514  fdwopts_col = -1,
1515  attstorage_col = -1,
1516  attcompression_col = -1,
1517  attstattarget_col = -1,
1518  attdescr_col = -1;
1519  int numrows;
1520  struct
1521  {
1522  int16 checks;
1523  char relkind;
1524  bool hasindex;
1525  bool hasrules;
1526  bool hastriggers;
1527  bool rowsecurity;
1528  bool forcerowsecurity;
1529  bool hasoids;
1530  bool ispartition;
1531  Oid tablespace;
1532  char *reloptions;
1533  char *reloftype;
1534  char relpersistence;
1535  char relreplident;
1536  char *relam;
1537  } tableinfo;
1538  bool show_column_details = false;
1539 
1540  myopt.default_footer = false;
1541  /* This output looks confusing in expanded mode. */
1542  myopt.expanded = false;
1543 
1544  initPQExpBuffer(&buf);
1545  initPQExpBuffer(&title);
1547 
1548  /* Get general table info */
1549  if (pset.sversion >= 120000)
1550  {
1552  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1553  "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1554  "false AS relhasoids, c.relispartition, %s, c.reltablespace, "
1555  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1556  "c.relpersistence, c.relreplident, am.amname\n"
1557  "FROM pg_catalog.pg_class c\n "
1558  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1559  "LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid)\n"
1560  "WHERE c.oid = '%s';",
1561  (verbose ?
1562  "pg_catalog.array_to_string(c.reloptions || "
1563  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1564  : "''"),
1565  oid);
1566  }
1567  else if (pset.sversion >= 100000)
1568  {
1570  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1571  "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1572  "c.relhasoids, c.relispartition, %s, c.reltablespace, "
1573  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1574  "c.relpersistence, c.relreplident\n"
1575  "FROM pg_catalog.pg_class c\n "
1576  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1577  "WHERE c.oid = '%s';",
1578  (verbose ?
1579  "pg_catalog.array_to_string(c.reloptions || "
1580  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1581  : "''"),
1582  oid);
1583  }
1584  else if (pset.sversion >= 90500)
1585  {
1587  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1588  "c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
1589  "c.relhasoids, false as relispartition, %s, c.reltablespace, "
1590  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1591  "c.relpersistence, c.relreplident\n"
1592  "FROM pg_catalog.pg_class c\n "
1593  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1594  "WHERE c.oid = '%s';",
1595  (verbose ?
1596  "pg_catalog.array_to_string(c.reloptions || "
1597  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1598  : "''"),
1599  oid);
1600  }
1601  else if (pset.sversion >= 90400)
1602  {
1604  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1605  "c.relhastriggers, false, false, c.relhasoids, "
1606  "false as relispartition, %s, c.reltablespace, "
1607  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1608  "c.relpersistence, c.relreplident\n"
1609  "FROM pg_catalog.pg_class c\n "
1610  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1611  "WHERE c.oid = '%s';",
1612  (verbose ?
1613  "pg_catalog.array_to_string(c.reloptions || "
1614  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1615  : "''"),
1616  oid);
1617  }
1618  else
1619  {
1621  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
1622  "c.relhastriggers, false, false, c.relhasoids, "
1623  "false as relispartition, %s, c.reltablespace, "
1624  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
1625  "c.relpersistence\n"
1626  "FROM pg_catalog.pg_class c\n "
1627  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
1628  "WHERE c.oid = '%s';",
1629  (verbose ?
1630  "pg_catalog.array_to_string(c.reloptions || "
1631  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
1632  : "''"),
1633  oid);
1634  }
1635 
1636  res = PSQLexec(buf.data);
1637  if (!res)
1638  goto error_return;
1639 
1640  /* Did we get anything? */
1641  if (PQntuples(res) == 0)
1642  {
1643  if (!pset.quiet)
1644  pg_log_error("Did not find any relation with OID %s.", oid);
1645  goto error_return;
1646  }
1647 
1648  tableinfo.checks = atoi(PQgetvalue(res, 0, 0));
1649  tableinfo.relkind = *(PQgetvalue(res, 0, 1));
1650  tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
1651  tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
1652  tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
1653  tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
1654  tableinfo.forcerowsecurity = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
1655  tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 7), "t") == 0;
1656  tableinfo.ispartition = strcmp(PQgetvalue(res, 0, 8), "t") == 0;
1657  tableinfo.reloptions = pg_strdup(PQgetvalue(res, 0, 9));
1658  tableinfo.tablespace = atooid(PQgetvalue(res, 0, 10));
1659  tableinfo.reloftype = (strcmp(PQgetvalue(res, 0, 11), "") != 0) ?
1660  pg_strdup(PQgetvalue(res, 0, 11)) : NULL;
1661  tableinfo.relpersistence = *(PQgetvalue(res, 0, 12));
1662  tableinfo.relreplident = (pset.sversion >= 90400) ?
1663  *(PQgetvalue(res, 0, 13)) : 'd';
1664  if (pset.sversion >= 120000)
1665  tableinfo.relam = PQgetisnull(res, 0, 14) ?
1666  (char *) NULL : pg_strdup(PQgetvalue(res, 0, 14));
1667  else
1668  tableinfo.relam = NULL;
1669  PQclear(res);
1670  res = NULL;
1671 
1672  /*
1673  * If it's a sequence, deal with it here separately.
1674  */
1675  if (tableinfo.relkind == RELKIND_SEQUENCE)
1676  {
1677  PGresult *result = NULL;
1678  printQueryOpt myopt = pset.popt;
1679  char *footers[2] = {NULL, NULL};
1680 
1681  if (pset.sversion >= 100000)
1682  {
1684  "SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
1685  " seqstart AS \"%s\",\n"
1686  " seqmin AS \"%s\",\n"
1687  " seqmax AS \"%s\",\n"
1688  " seqincrement AS \"%s\",\n"
1689  " CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
1690  " seqcache AS \"%s\"\n",
1691  gettext_noop("Type"),
1692  gettext_noop("Start"),
1693  gettext_noop("Minimum"),
1694  gettext_noop("Maximum"),
1695  gettext_noop("Increment"),
1696  gettext_noop("yes"),
1697  gettext_noop("no"),
1698  gettext_noop("Cycles?"),
1699  gettext_noop("Cache"));
1701  "FROM pg_catalog.pg_sequence\n"
1702  "WHERE seqrelid = '%s';",
1703  oid);
1704  }
1705  else
1706  {
1708  "SELECT 'bigint' AS \"%s\",\n"
1709  " start_value AS \"%s\",\n"
1710  " min_value AS \"%s\",\n"
1711  " max_value AS \"%s\",\n"
1712  " increment_by AS \"%s\",\n"
1713  " CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n"
1714  " cache_value AS \"%s\"\n",
1715  gettext_noop("Type"),
1716  gettext_noop("Start"),
1717  gettext_noop("Minimum"),
1718  gettext_noop("Maximum"),
1719  gettext_noop("Increment"),
1720  gettext_noop("yes"),
1721  gettext_noop("no"),
1722  gettext_noop("Cycles?"),
1723  gettext_noop("Cache"));
1724  appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
1725  /* must be separate because fmtId isn't reentrant */
1726  appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
1727  }
1728 
1729  res = PSQLexec(buf.data);
1730  if (!res)
1731  goto error_return;
1732 
1733  /* Get the column that owns this sequence */
1734  printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
1735  "\n pg_catalog.quote_ident(relname) || '.' ||"
1736  "\n pg_catalog.quote_ident(attname),"
1737  "\n d.deptype"
1738  "\nFROM pg_catalog.pg_class c"
1739  "\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
1740  "\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
1741  "\nINNER JOIN pg_catalog.pg_attribute a ON ("
1742  "\n a.attrelid=c.oid AND"
1743  "\n a.attnum=d.refobjsubid)"
1744  "\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
1745  "\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
1746  "\n AND d.objid='%s'"
1747  "\n AND d.deptype IN ('a', 'i')",
1748  oid);
1749 
1750  result = PSQLexec(buf.data);
1751 
1752  /*
1753  * If we get no rows back, don't show anything (obviously). We should
1754  * never get more than one row back, but if we do, just ignore it and
1755  * don't print anything.
1756  */
1757  if (!result)
1758  goto error_return;
1759  else if (PQntuples(result) == 1)
1760  {
1761  switch (PQgetvalue(result, 0, 1)[0])
1762  {
1763  case 'a':
1764  footers[0] = psprintf(_("Owned by: %s"),
1765  PQgetvalue(result, 0, 0));
1766  break;
1767  case 'i':
1768  footers[0] = psprintf(_("Sequence for identity column: %s"),
1769  PQgetvalue(result, 0, 0));
1770  break;
1771  }
1772  }
1773  PQclear(result);
1774 
1775  if (tableinfo.relpersistence == 'u')
1776  printfPQExpBuffer(&title, _("Unlogged sequence \"%s.%s\""),
1777  schemaname, relationname);
1778  else
1779  printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
1780  schemaname, relationname);
1781 
1782  myopt.footers = footers;
1783  myopt.topt.default_footer = false;
1784  myopt.title = title.data;
1785  myopt.translate_header = true;
1786 
1787  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1788 
1789  free(footers[0]);
1790 
1791  retval = true;
1792  goto error_return; /* not an error, just return early */
1793  }
1794 
1795  /* Identify whether we should print collation, nullable, default vals */
1796  if (tableinfo.relkind == RELKIND_RELATION ||
1797  tableinfo.relkind == RELKIND_VIEW ||
1798  tableinfo.relkind == RELKIND_MATVIEW ||
1799  tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1800  tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
1801  tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
1802  show_column_details = true;
1803 
1804  /*
1805  * Get per-column info
1806  *
1807  * Since the set of query columns we need varies depending on relkind and
1808  * server version, we compute all the column numbers on-the-fly. Column
1809  * number variables for columns not fetched are left as -1; this avoids
1810  * duplicative test logic below.
1811  */
1812  cols = 0;
1813  printfPQExpBuffer(&buf, "SELECT a.attname");
1814  attname_col = cols++;
1815  appendPQExpBufferStr(&buf, ",\n pg_catalog.format_type(a.atttypid, a.atttypmod)");
1816  atttype_col = cols++;
1817 
1818  if (show_column_details)
1819  {
1820  /* use "pretty" mode for expression to avoid excessive parentheses */
1822  ",\n (SELECT pg_catalog.pg_get_expr(d.adbin, d.adrelid, true)"
1823  "\n FROM pg_catalog.pg_attrdef d"
1824  "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)"
1825  ",\n a.attnotnull");
1826  attrdef_col = cols++;
1827  attnotnull_col = cols++;
1828  appendPQExpBufferStr(&buf, ",\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
1829  " WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
1830  attcoll_col = cols++;
1831  if (pset.sversion >= 100000)
1832  appendPQExpBufferStr(&buf, ",\n a.attidentity");
1833  else
1834  appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attidentity");
1835  attidentity_col = cols++;
1836  if (pset.sversion >= 120000)
1837  appendPQExpBufferStr(&buf, ",\n a.attgenerated");
1838  else
1839  appendPQExpBufferStr(&buf, ",\n ''::pg_catalog.char AS attgenerated");
1840  attgenerated_col = cols++;
1841  }
1842  if (tableinfo.relkind == RELKIND_INDEX ||
1843  tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
1844  {
1845  if (pset.sversion >= 110000)
1846  {
1847  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",
1848  oid,
1849  gettext_noop("yes"),
1850  gettext_noop("no"));
1851  isindexkey_col = cols++;
1852  }
1853  appendPQExpBufferStr(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
1854  indexdef_col = cols++;
1855  }
1856  /* FDW options for foreign table column */
1857  if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
1858  {
1859  appendPQExpBufferStr(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE "
1860  " '(' || pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(option_name) || ' ' || pg_catalog.quote_literal(option_value) FROM "
1861  " pg_catalog.pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions");
1862  fdwopts_col = cols++;
1863  }
1864  if (verbose)
1865  {
1866  appendPQExpBufferStr(&buf, ",\n a.attstorage");
1867  attstorage_col = cols++;
1868 
1869  /* compression info, if relevant to relkind */
1870  if (pset.sversion >= 140000 &&
1872  (tableinfo.relkind == RELKIND_RELATION ||
1873  tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
1874  tableinfo.relkind == RELKIND_MATVIEW))
1875  {
1876  appendPQExpBufferStr(&buf, ",\n a.attcompression AS attcompression");
1877  attcompression_col = cols++;
1878  }
1879 
1880  /* stats target, if relevant to relkind */
1881  if (tableinfo.relkind == RELKIND_RELATION ||
1882  tableinfo.relkind == RELKIND_INDEX ||
1883  tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
1884  tableinfo.relkind == RELKIND_MATVIEW ||
1885  tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1886  tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
1887  {
1888  appendPQExpBufferStr(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
1889  attstattarget_col = cols++;
1890  }
1891 
1892  /*
1893  * In 9.0+, we have column comments for: relations, views, composite
1894  * types, and foreign tables (cf. CommentObject() in comment.c).
1895  */
1896  if (tableinfo.relkind == RELKIND_RELATION ||
1897  tableinfo.relkind == RELKIND_VIEW ||
1898  tableinfo.relkind == RELKIND_MATVIEW ||
1899  tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
1900  tableinfo.relkind == RELKIND_COMPOSITE_TYPE ||
1901  tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
1902  {
1903  appendPQExpBufferStr(&buf, ",\n pg_catalog.col_description(a.attrelid, a.attnum)");
1904  attdescr_col = cols++;
1905  }
1906  }
1907 
1908  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_attribute a");
1909  appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
1910  appendPQExpBufferStr(&buf, "\nORDER BY a.attnum;");
1911 
1912  res = PSQLexec(buf.data);
1913  if (!res)
1914  goto error_return;
1915  numrows = PQntuples(res);
1916 
1917  /* Make title */
1918  switch (tableinfo.relkind)
1919  {
1920  case RELKIND_RELATION:
1921  if (tableinfo.relpersistence == 'u')
1922  printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
1923  schemaname, relationname);
1924  else
1925  printfPQExpBuffer(&title, _("Table \"%s.%s\""),
1926  schemaname, relationname);
1927  break;
1928  case RELKIND_VIEW:
1929  printfPQExpBuffer(&title, _("View \"%s.%s\""),
1930  schemaname, relationname);
1931  break;
1932  case RELKIND_MATVIEW:
1933  if (tableinfo.relpersistence == 'u')
1934  printfPQExpBuffer(&title, _("Unlogged materialized view \"%s.%s\""),
1935  schemaname, relationname);
1936  else
1937  printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""),
1938  schemaname, relationname);
1939  break;
1940  case RELKIND_INDEX:
1941  if (tableinfo.relpersistence == 'u')
1942  printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
1943  schemaname, relationname);
1944  else
1945  printfPQExpBuffer(&title, _("Index \"%s.%s\""),
1946  schemaname, relationname);
1947  break;
1948  case RELKIND_PARTITIONED_INDEX:
1949  if (tableinfo.relpersistence == 'u')
1950  printfPQExpBuffer(&title, _("Unlogged partitioned index \"%s.%s\""),
1951  schemaname, relationname);
1952  else
1953  printfPQExpBuffer(&title, _("Partitioned index \"%s.%s\""),
1954  schemaname, relationname);
1955  break;
1956  case RELKIND_TOASTVALUE:
1957  printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
1958  schemaname, relationname);
1959  break;
1960  case RELKIND_COMPOSITE_TYPE:
1961  printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
1962  schemaname, relationname);
1963  break;
1964  case RELKIND_FOREIGN_TABLE:
1965  printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
1966  schemaname, relationname);
1967  break;
1968  case RELKIND_PARTITIONED_TABLE:
1969  if (tableinfo.relpersistence == 'u')
1970  printfPQExpBuffer(&title, _("Unlogged partitioned table \"%s.%s\""),
1971  schemaname, relationname);
1972  else
1973  printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""),
1974  schemaname, relationname);
1975  break;
1976  default:
1977  /* untranslated unknown relkind */
1978  printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
1979  tableinfo.relkind, schemaname, relationname);
1980  break;
1981  }
1982 
1983  /* Fill headers[] with the names of the columns we will output */
1984  cols = 0;
1985  headers[cols++] = gettext_noop("Column");
1986  headers[cols++] = gettext_noop("Type");
1987  if (show_column_details)
1988  {
1989  headers[cols++] = gettext_noop("Collation");
1990  headers[cols++] = gettext_noop("Nullable");
1991  headers[cols++] = gettext_noop("Default");
1992  }
1993  if (isindexkey_col >= 0)
1994  headers[cols++] = gettext_noop("Key?");
1995  if (indexdef_col >= 0)
1996  headers[cols++] = gettext_noop("Definition");
1997  if (fdwopts_col >= 0)
1998  headers[cols++] = gettext_noop("FDW options");
1999  if (attstorage_col >= 0)
2000  headers[cols++] = gettext_noop("Storage");
2001  if (attcompression_col >= 0)
2002  headers[cols++] = gettext_noop("Compression");
2003  if (attstattarget_col >= 0)
2004  headers[cols++] = gettext_noop("Stats target");
2005  if (attdescr_col >= 0)
2006  headers[cols++] = gettext_noop("Description");
2007 
2008  Assert(cols <= lengthof(headers));
2009 
2010  printTableInit(&cont, &myopt, title.data, cols, numrows);
2011  printTableInitialized = true;
2012 
2013  for (i = 0; i < cols; i++)
2014  printTableAddHeader(&cont, headers[i], true, 'l');
2015 
2016  /* Generate table cells to be printed */
2017  for (i = 0; i < numrows; i++)
2018  {
2019  /* Column */
2020  printTableAddCell(&cont, PQgetvalue(res, i, attname_col), false, false);
2021 
2022  /* Type */
2023  printTableAddCell(&cont, PQgetvalue(res, i, atttype_col), false, false);
2024 
2025  /* Collation, Nullable, Default */
2026  if (show_column_details)
2027  {
2028  char *identity;
2029  char *generated;
2030  char *default_str;
2031  bool mustfree = false;
2032 
2033  printTableAddCell(&cont, PQgetvalue(res, i, attcoll_col), false, false);
2034 
2035  printTableAddCell(&cont,
2036  strcmp(PQgetvalue(res, i, attnotnull_col), "t") == 0 ? "not null" : "",
2037  false, false);
2038 
2039  identity = PQgetvalue(res, i, attidentity_col);
2040  generated = PQgetvalue(res, i, attgenerated_col);
2041 
2042  if (identity[0] == ATTRIBUTE_IDENTITY_ALWAYS)
2043  default_str = "generated always as identity";
2044  else if (identity[0] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
2045  default_str = "generated by default as identity";
2046  else if (generated[0] == ATTRIBUTE_GENERATED_STORED)
2047  {
2048  default_str = psprintf("generated always as (%s) stored",
2049  PQgetvalue(res, i, attrdef_col));
2050  mustfree = true;
2051  }
2052  else
2053  default_str = PQgetvalue(res, i, attrdef_col);
2054 
2055  printTableAddCell(&cont, default_str, false, mustfree);
2056  }
2057 
2058  /* Info for index columns */
2059  if (isindexkey_col >= 0)
2060  printTableAddCell(&cont, PQgetvalue(res, i, isindexkey_col), true, false);
2061  if (indexdef_col >= 0)
2062  printTableAddCell(&cont, PQgetvalue(res, i, indexdef_col), false, false);
2063 
2064  /* FDW options for foreign table columns */
2065  if (fdwopts_col >= 0)
2066  printTableAddCell(&cont, PQgetvalue(res, i, fdwopts_col), false, false);
2067 
2068  /* Storage mode, if relevant */
2069  if (attstorage_col >= 0)
2070  {
2071  char *storage = PQgetvalue(res, i, attstorage_col);
2072 
2073  /* these strings are literal in our syntax, so not translated. */
2074  printTableAddCell(&cont, (storage[0] == 'p' ? "plain" :
2075  (storage[0] == 'm' ? "main" :
2076  (storage[0] == 'x' ? "extended" :
2077  (storage[0] == 'e' ? "external" :
2078  "???")))),
2079  false, false);
2080  }
2081 
2082  /* Column compression, if relevant */
2083  if (attcompression_col >= 0)
2084  {
2085  char *compression = PQgetvalue(res, i, attcompression_col);
2086 
2087  /* these strings are literal in our syntax, so not translated. */
2088  printTableAddCell(&cont, (compression[0] == 'p' ? "pglz" :
2089  (compression[0] == 'l' ? "lz4" :
2090  (compression[0] == '\0' ? "" :
2091  "???"))),
2092  false, false);
2093  }
2094 
2095  /* Statistics target, if the relkind supports this feature */
2096  if (attstattarget_col >= 0)
2097  printTableAddCell(&cont, PQgetvalue(res, i, attstattarget_col),
2098  false, false);
2099 
2100  /* Column comments, if the relkind supports this feature */
2101  if (attdescr_col >= 0)
2102  printTableAddCell(&cont, PQgetvalue(res, i, attdescr_col),
2103  false, false);
2104  }
2105 
2106  /* Make footers */
2107 
2108  if (tableinfo.ispartition)
2109  {
2110  /* Footer information for a partition child table */
2111  PGresult *result;
2112 
2114  "SELECT inhparent::pg_catalog.regclass,\n"
2115  " pg_catalog.pg_get_expr(c.relpartbound, c.oid),\n ");
2116 
2118  pset.sversion >= 140000 ? "inhdetachpending" :
2119  "false as inhdetachpending");
2120 
2121  /* If verbose, also request the partition constraint definition */
2122  if (verbose)
2124  ",\n pg_catalog.pg_get_partition_constraintdef(c.oid)");
2126  "\nFROM pg_catalog.pg_class c"
2127  " JOIN pg_catalog.pg_inherits i"
2128  " ON c.oid = inhrelid"
2129  "\nWHERE c.oid = '%s';", oid);
2130  result = PSQLexec(buf.data);
2131  if (!result)
2132  goto error_return;
2133 
2134  if (PQntuples(result) > 0)
2135  {
2136  char *parent_name = PQgetvalue(result, 0, 0);
2137  char *partdef = PQgetvalue(result, 0, 1);
2138  char *detached = PQgetvalue(result, 0, 2);
2139 
2140  printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s%s"), parent_name,
2141  partdef,
2142  strcmp(detached, "t") == 0 ? " DETACH PENDING" : "");
2144 
2145  if (verbose)
2146  {
2147  char *partconstraintdef = NULL;
2148 
2149  if (!PQgetisnull(result, 0, 3))
2150  partconstraintdef = PQgetvalue(result, 0, 3);
2151  /* If there isn't any constraint, show that explicitly */
2152  if (partconstraintdef == NULL || partconstraintdef[0] == '\0')
2153  printfPQExpBuffer(&tmpbuf, _("No partition constraint"));
2154  else
2155  printfPQExpBuffer(&tmpbuf, _("Partition constraint: %s"),
2156  partconstraintdef);
2158  }
2159  }
2160  PQclear(result);
2161  }
2162 
2163  if (tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2164  {
2165  /* Footer information for a partitioned table (partitioning parent) */
2166  PGresult *result;
2167 
2169  "SELECT pg_catalog.pg_get_partkeydef('%s'::pg_catalog.oid);",
2170  oid);
2171  result = PSQLexec(buf.data);
2172  if (!result)
2173  goto error_return;
2174 
2175  if (PQntuples(result) == 1)
2176  {
2177  char *partkeydef = PQgetvalue(result, 0, 0);
2178 
2179  printfPQExpBuffer(&tmpbuf, _("Partition key: %s"), partkeydef);
2181  }
2182  PQclear(result);
2183  }
2184 
2185  if (tableinfo.relkind == RELKIND_TOASTVALUE)
2186  {
2187  /* For a TOAST table, print name of owning table */
2188  PGresult *result;
2189 
2191  "SELECT n.nspname, c.relname\n"
2192  "FROM pg_catalog.pg_class c"
2193  " JOIN pg_catalog.pg_namespace n"
2194  " ON n.oid = c.relnamespace\n"
2195  "WHERE reltoastrelid = '%s';", oid);
2196  result = PSQLexec(buf.data);
2197  if (!result)
2198  goto error_return;
2199 
2200  if (PQntuples(result) == 1)
2201  {
2202  char *schemaname = PQgetvalue(result, 0, 0);
2203  char *relname = PQgetvalue(result, 0, 1);
2204 
2205  printfPQExpBuffer(&tmpbuf, _("Owning table: \"%s.%s\""),
2206  schemaname, relname);
2208  }
2209  PQclear(result);
2210  }
2211 
2212  if (tableinfo.relkind == RELKIND_INDEX ||
2213  tableinfo.relkind == RELKIND_PARTITIONED_INDEX)
2214  {
2215  /* Footer information about an index */
2216  PGresult *result;
2217 
2219  "SELECT i.indisunique, i.indisprimary, i.indisclustered, "
2220  "i.indisvalid,\n"
2221  " (NOT i.indimmediate) AND "
2222  "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2223  "WHERE conrelid = i.indrelid AND "
2224  "conindid = i.indexrelid AND "
2225  "contype IN ('p','u','x') AND "
2226  "condeferrable) AS condeferrable,\n"
2227  " (NOT i.indimmediate) AND "
2228  "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
2229  "WHERE conrelid = i.indrelid AND "
2230  "conindid = i.indexrelid AND "
2231  "contype IN ('p','u','x') AND "
2232  "condeferred) AS condeferred,\n");
2233 
2234  if (pset.sversion >= 90400)
2235  appendPQExpBufferStr(&buf, "i.indisreplident,\n");
2236  else
2237  appendPQExpBufferStr(&buf, "false AS indisreplident,\n");
2238 
2239  if (pset.sversion >= 150000)
2240  appendPQExpBufferStr(&buf, "i.indnullsnotdistinct,\n");
2241  else
2242  appendPQExpBufferStr(&buf, "false AS indnullsnotdistinct,\n");
2243 
2244  appendPQExpBuffer(&buf, " a.amname, c2.relname, "
2245  "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
2246  "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
2247  "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
2248  "AND i.indrelid = c2.oid;",
2249  oid);
2250 
2251  result = PSQLexec(buf.data);
2252  if (!result)
2253  goto error_return;
2254  else if (PQntuples(result) != 1)
2255  {
2256  PQclear(result);
2257  goto error_return;
2258  }
2259  else
2260  {
2261  char *indisunique = PQgetvalue(result, 0, 0);
2262  char *indisprimary = PQgetvalue(result, 0, 1);
2263  char *indisclustered = PQgetvalue(result, 0, 2);
2264  char *indisvalid = PQgetvalue(result, 0, 3);
2265  char *deferrable = PQgetvalue(result, 0, 4);
2266  char *deferred = PQgetvalue(result, 0, 5);
2267  char *indisreplident = PQgetvalue(result, 0, 6);
2268  char *indnullsnotdistinct = PQgetvalue(result, 0, 7);
2269  char *indamname = PQgetvalue(result, 0, 8);
2270  char *indtable = PQgetvalue(result, 0, 9);
2271  char *indpred = PQgetvalue(result, 0, 10);
2272 
2273  if (strcmp(indisprimary, "t") == 0)
2274  printfPQExpBuffer(&tmpbuf, _("primary key, "));
2275  else if (strcmp(indisunique, "t") == 0)
2276  {
2277  printfPQExpBuffer(&tmpbuf, _("unique"));
2278  if (strcmp(indnullsnotdistinct, "t") == 0)
2279  appendPQExpBufferStr(&tmpbuf, _(" nulls not distinct"));
2280  appendPQExpBuffer(&tmpbuf, _(", "));
2281  }
2282  else
2284  appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
2285 
2286  /* we assume here that index and table are in same schema */
2287  appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
2288  schemaname, indtable);
2289 
2290  if (strlen(indpred))
2291  appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
2292 
2293  if (strcmp(indisclustered, "t") == 0)
2294  appendPQExpBufferStr(&tmpbuf, _(", clustered"));
2295 
2296  if (strcmp(indisvalid, "t") != 0)
2297  appendPQExpBufferStr(&tmpbuf, _(", invalid"));
2298 
2299  if (strcmp(deferrable, "t") == 0)
2300  appendPQExpBufferStr(&tmpbuf, _(", deferrable"));
2301 
2302  if (strcmp(deferred, "t") == 0)
2303  appendPQExpBufferStr(&tmpbuf, _(", initially deferred"));
2304 
2305  if (strcmp(indisreplident, "t") == 0)
2306  appendPQExpBufferStr(&tmpbuf, _(", replica identity"));
2307 
2309 
2310  /*
2311  * If it's a partitioned index, we'll print the tablespace below
2312  */
2313  if (tableinfo.relkind == RELKIND_INDEX)
2314  add_tablespace_footer(&cont, tableinfo.relkind,
2315  tableinfo.tablespace, true);
2316  }
2317 
2318  PQclear(result);
2319  }
2320  /* If you add relkinds here, see also "Finish printing..." stanza below */
2321  else if (tableinfo.relkind == RELKIND_RELATION ||
2322  tableinfo.relkind == RELKIND_MATVIEW ||
2323  tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
2324  tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
2325  tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
2326  tableinfo.relkind == RELKIND_TOASTVALUE)
2327  {
2328  /* Footer information about a table */
2329  PGresult *result = NULL;
2330  int tuples = 0;
2331 
2332  /* print indexes */
2333  if (tableinfo.hasindex)
2334  {
2336  "SELECT c2.relname, i.indisprimary, i.indisunique, "
2337  "i.indisclustered, i.indisvalid, "
2338  "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n "
2339  "pg_catalog.pg_get_constraintdef(con.oid, true), "
2340  "contype, condeferrable, condeferred");
2341  if (pset.sversion >= 90400)
2342  appendPQExpBufferStr(&buf, ", i.indisreplident");
2343  else
2344  appendPQExpBufferStr(&buf, ", false AS indisreplident");
2345  appendPQExpBufferStr(&buf, ", c2.reltablespace");
2347  "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
2348  " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n"
2349  "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
2350  "ORDER BY i.indisprimary DESC, c2.relname;",
2351  oid);
2352  result = PSQLexec(buf.data);
2353  if (!result)
2354  goto error_return;
2355  else
2356  tuples = PQntuples(result);
2357 
2358  if (tuples > 0)
2359  {
2360  printTableAddFooter(&cont, _("Indexes:"));
2361  for (i = 0; i < tuples; i++)
2362  {
2363  /* untranslated index name */
2364  printfPQExpBuffer(&buf, " \"%s\"",
2365  PQgetvalue(result, i, 0));
2366 
2367  /* If exclusion constraint, print the constraintdef */
2368  if (strcmp(PQgetvalue(result, i, 7), "x") == 0)
2369  {
2370  appendPQExpBuffer(&buf, " %s",
2371  PQgetvalue(result, i, 6));
2372  }
2373  else
2374  {
2375  const char *indexdef;
2376  const char *usingpos;
2377 
2378  /* Label as primary key or unique (but not both) */
2379  if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
2380  appendPQExpBufferStr(&buf, " PRIMARY KEY,");
2381  else if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
2382  {
2383  if (strcmp(PQgetvalue(result, i, 7), "u") == 0)
2384  appendPQExpBufferStr(&buf, " UNIQUE CONSTRAINT,");
2385  else
2386  appendPQExpBufferStr(&buf, " UNIQUE,");
2387  }
2388 
2389  /* Everything after "USING" is echoed verbatim */
2390  indexdef = PQgetvalue(result, i, 5);
2391  usingpos = strstr(indexdef, " USING ");
2392  if (usingpos)
2393  indexdef = usingpos + 7;
2394  appendPQExpBuffer(&buf, " %s", indexdef);
2395 
2396  /* Need these for deferrable PK/UNIQUE indexes */
2397  if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
2398  appendPQExpBufferStr(&buf, " DEFERRABLE");
2399 
2400  if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
2401  appendPQExpBufferStr(&buf, " INITIALLY DEFERRED");
2402  }
2403 
2404  /* Add these for all cases */
2405  if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
2406  appendPQExpBufferStr(&buf, " CLUSTER");
2407 
2408  if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
2409  appendPQExpBufferStr(&buf, " INVALID");
2410 
2411  if (strcmp(PQgetvalue(result, i, 10), "t") == 0)
2412  appendPQExpBufferStr(&buf, " REPLICA IDENTITY");
2413 
2414  printTableAddFooter(&cont, buf.data);
2415 
2416  /* Print tablespace of the index on the same line */
2417  add_tablespace_footer(&cont, RELKIND_INDEX,
2418  atooid(PQgetvalue(result, i, 11)),
2419  false);
2420  }
2421  }
2422  PQclear(result);
2423  }
2424 
2425  /* print table (and column) check constraints */
2426  if (tableinfo.checks)
2427  {
2429  "SELECT r.conname, "
2430  "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
2431  "FROM pg_catalog.pg_constraint r\n"
2432  "WHERE r.conrelid = '%s' AND r.contype = 'c'\n"
2433  "ORDER BY 1;",
2434  oid);
2435  result = PSQLexec(buf.data);
2436  if (!result)
2437  goto error_return;
2438  else
2439  tuples = PQntuples(result);
2440 
2441  if (tuples > 0)
2442  {
2443  printTableAddFooter(&cont, _("Check constraints:"));
2444  for (i = 0; i < tuples; i++)
2445  {
2446  /* untranslated constraint name and def */
2447  printfPQExpBuffer(&buf, " \"%s\" %s",
2448  PQgetvalue(result, i, 0),
2449  PQgetvalue(result, i, 1));
2450 
2451  printTableAddFooter(&cont, buf.data);
2452  }
2453  }
2454  PQclear(result);
2455  }
2456 
2457  /*
2458  * Print foreign-key constraints (there are none if no triggers,
2459  * except if the table is partitioned, in which case the triggers
2460  * appear in the partitions)
2461  */
2462  if (tableinfo.hastriggers ||
2463  tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2464  {
2465  if (pset.sversion >= 120000 &&
2466  (tableinfo.ispartition || tableinfo.relkind == RELKIND_PARTITIONED_TABLE))
2467  {
2468  /*
2469  * Put the constraints defined in this table first, followed
2470  * by the constraints defined in ancestor partitioned tables.
2471  */
2473  "SELECT conrelid = '%s'::pg_catalog.regclass AS sametable,\n"
2474  " conname,\n"
2475  " pg_catalog.pg_get_constraintdef(oid, true) AS condef,\n"
2476  " conrelid::pg_catalog.regclass AS ontable\n"
2477  " FROM pg_catalog.pg_constraint,\n"
2478  " pg_catalog.pg_partition_ancestors('%s')\n"
2479  " WHERE conrelid = relid AND contype = 'f' AND conparentid = 0\n"
2480  "ORDER BY sametable DESC, conname;",
2481  oid, oid);
2482  }
2483  else
2484  {
2486  "SELECT true as sametable, conname,\n"
2487  " pg_catalog.pg_get_constraintdef(r.oid, true) as condef,\n"
2488  " conrelid::pg_catalog.regclass AS ontable\n"
2489  "FROM pg_catalog.pg_constraint r\n"
2490  "WHERE r.conrelid = '%s' AND r.contype = 'f'\n",
2491  oid);
2492 
2493  if (pset.sversion >= 120000)
2494  appendPQExpBufferStr(&buf, " AND conparentid = 0\n");
2495  appendPQExpBufferStr(&buf, "ORDER BY conname");
2496  }
2497 
2498  result = PSQLexec(buf.data);
2499  if (!result)
2500  goto error_return;
2501  else
2502  tuples = PQntuples(result);
2503 
2504  if (tuples > 0)
2505  {
2506  int i_sametable = PQfnumber(result, "sametable"),
2507  i_conname = PQfnumber(result, "conname"),
2508  i_condef = PQfnumber(result, "condef"),
2509  i_ontable = PQfnumber(result, "ontable");
2510 
2511  printTableAddFooter(&cont, _("Foreign-key constraints:"));
2512  for (i = 0; i < tuples; i++)
2513  {
2514  /*
2515  * Print untranslated constraint name and definition. Use
2516  * a "TABLE tab" prefix when the constraint is defined in
2517  * a parent partitioned table.
2518  */
2519  if (strcmp(PQgetvalue(result, i, i_sametable), "f") == 0)
2520  printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2521  PQgetvalue(result, i, i_ontable),
2522  PQgetvalue(result, i, i_conname),
2523  PQgetvalue(result, i, i_condef));
2524  else
2525  printfPQExpBuffer(&buf, " \"%s\" %s",
2526  PQgetvalue(result, i, i_conname),
2527  PQgetvalue(result, i, i_condef));
2528 
2529  printTableAddFooter(&cont, buf.data);
2530  }
2531  }
2532  PQclear(result);
2533  }
2534 
2535  /* print incoming foreign-key references */
2536  if (tableinfo.hastriggers ||
2537  tableinfo.relkind == RELKIND_PARTITIONED_TABLE)
2538  {
2539  if (pset.sversion >= 120000)
2540  {
2542  "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2543  " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2544  " FROM pg_catalog.pg_constraint c\n"
2545  " WHERE confrelid IN (SELECT pg_catalog.pg_partition_ancestors('%s')\n"
2546  " UNION ALL VALUES ('%s'::pg_catalog.regclass))\n"
2547  " AND contype = 'f' AND conparentid = 0\n"
2548  "ORDER BY conname;",
2549  oid, oid);
2550  }
2551  else
2552  {
2554  "SELECT conname, conrelid::pg_catalog.regclass AS ontable,\n"
2555  " pg_catalog.pg_get_constraintdef(oid, true) AS condef\n"
2556  " FROM pg_catalog.pg_constraint\n"
2557  " WHERE confrelid = %s AND contype = 'f'\n"
2558  "ORDER BY conname;",
2559  oid);
2560  }
2561 
2562  result = PSQLexec(buf.data);
2563  if (!result)
2564  goto error_return;
2565  else
2566  tuples = PQntuples(result);
2567 
2568  if (tuples > 0)
2569  {
2570  int i_conname = PQfnumber(result, "conname"),
2571  i_ontable = PQfnumber(result, "ontable"),
2572  i_condef = PQfnumber(result, "condef");
2573 
2574  printTableAddFooter(&cont, _("Referenced by:"));
2575  for (i = 0; i < tuples; i++)
2576  {
2577  printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
2578  PQgetvalue(result, i, i_ontable),
2579  PQgetvalue(result, i, i_conname),
2580  PQgetvalue(result, i, i_condef));
2581 
2582  printTableAddFooter(&cont, buf.data);
2583  }
2584  }
2585  PQclear(result);
2586  }
2587 
2588  /* print any row-level policies */
2589  if (pset.sversion >= 90500)
2590  {
2591  printfPQExpBuffer(&buf, "SELECT pol.polname,");
2592  if (pset.sversion >= 100000)
2594  " pol.polpermissive,\n");
2595  else
2597  " 't' as polpermissive,\n");
2599  " 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"
2600  " pg_catalog.pg_get_expr(pol.polqual, pol.polrelid),\n"
2601  " pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid),\n"
2602  " CASE pol.polcmd\n"
2603  " WHEN 'r' THEN 'SELECT'\n"
2604  " WHEN 'a' THEN 'INSERT'\n"
2605  " WHEN 'w' THEN 'UPDATE'\n"
2606  " WHEN 'd' THEN 'DELETE'\n"
2607  " END AS cmd\n"
2608  "FROM pg_catalog.pg_policy pol\n"
2609  "WHERE pol.polrelid = '%s' ORDER BY 1;",
2610  oid);
2611 
2612  result = PSQLexec(buf.data);
2613  if (!result)
2614  goto error_return;
2615  else
2616  tuples = PQntuples(result);
2617 
2618  /*
2619  * Handle cases where RLS is enabled and there are policies, or
2620  * there aren't policies, or RLS isn't enabled but there are
2621  * policies
2622  */
2623  if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
2624  printTableAddFooter(&cont, _("Policies:"));
2625 
2626  if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
2627  printTableAddFooter(&cont, _("Policies (forced row security enabled):"));
2628 
2629  if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
2630  printTableAddFooter(&cont, _("Policies (row security enabled): (none)"));
2631 
2632  if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
2633  printTableAddFooter(&cont, _("Policies (forced row security enabled): (none)"));
2634 
2635  if (!tableinfo.rowsecurity && tuples > 0)
2636  printTableAddFooter(&cont, _("Policies (row security disabled):"));
2637 
2638  /* Might be an empty set - that's ok */
2639  for (i = 0; i < tuples; i++)
2640  {
2641  printfPQExpBuffer(&buf, " POLICY \"%s\"",
2642  PQgetvalue(result, i, 0));
2643 
2644  if (*(PQgetvalue(result, i, 1)) == 'f')
2645  appendPQExpBufferStr(&buf, " AS RESTRICTIVE");
2646 
2647  if (!PQgetisnull(result, i, 5))
2648  appendPQExpBuffer(&buf, " FOR %s",
2649  PQgetvalue(result, i, 5));
2650 
2651  if (!PQgetisnull(result, i, 2))
2652  {
2653  appendPQExpBuffer(&buf, "\n TO %s",
2654  PQgetvalue(result, i, 2));
2655  }
2656 
2657  if (!PQgetisnull(result, i, 3))
2658  appendPQExpBuffer(&buf, "\n USING (%s)",
2659  PQgetvalue(result, i, 3));
2660 
2661  if (!PQgetisnull(result, i, 4))
2662  appendPQExpBuffer(&buf, "\n WITH CHECK (%s)",
2663  PQgetvalue(result, i, 4));
2664 
2665  printTableAddFooter(&cont, buf.data);
2666  }
2667  PQclear(result);
2668  }
2669 
2670  /* print any extended statistics */
2671  if (pset.sversion >= 140000)
2672  {
2674  "SELECT oid, "
2675  "stxrelid::pg_catalog.regclass, "
2676  "stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS nsp, "
2677  "stxname,\n"
2678  "pg_catalog.pg_get_statisticsobjdef_columns(oid) AS columns,\n"
2679  " 'd' = any(stxkind) AS ndist_enabled,\n"
2680  " 'f' = any(stxkind) AS deps_enabled,\n"
2681  " 'm' = any(stxkind) AS mcv_enabled,\n"
2682  "stxstattarget\n"
2683  "FROM pg_catalog.pg_statistic_ext\n"
2684  "WHERE stxrelid = '%s'\n"
2685  "ORDER BY nsp, stxname;",
2686  oid);
2687 
2688  result = PSQLexec(buf.data);
2689  if (!result)
2690  goto error_return;
2691  else
2692  tuples = PQntuples(result);
2693 
2694  if (tuples > 0)
2695  {
2696  printTableAddFooter(&cont, _("Statistics objects:"));
2697 
2698  for (i = 0; i < tuples; i++)
2699  {
2700  bool gotone = false;
2701  bool has_ndistinct;
2702  bool has_dependencies;
2703  bool has_mcv;
2704  bool has_all;
2705  bool has_some;
2706 
2707  has_ndistinct = (strcmp(PQgetvalue(result, i, 5), "t") == 0);
2708  has_dependencies = (strcmp(PQgetvalue(result, i, 6), "t") == 0);
2709  has_mcv = (strcmp(PQgetvalue(result, i, 7), "t") == 0);
2710 
2711  printfPQExpBuffer(&buf, " ");
2712 
2713  /* statistics object name (qualified with namespace) */
2714  appendPQExpBuffer(&buf, "\"%s.%s\"",
2715  PQgetvalue(result, i, 2),
2716  PQgetvalue(result, i, 3));
2717 
2718  /*
2719  * When printing kinds we ignore expression statistics,
2720  * which are used only internally and can't be specified
2721  * by user. We don't print the kinds when none are
2722  * specified (in which case it has to be statistics on a
2723  * single expr) or when all are specified (in which case
2724  * we assume it's expanded by CREATE STATISTICS).
2725  */
2726  has_all = (has_ndistinct && has_dependencies && has_mcv);
2727  has_some = (has_ndistinct || has_dependencies || has_mcv);
2728 
2729  if (has_some && !has_all)
2730  {
2731  appendPQExpBufferStr(&buf, " (");
2732 
2733  /* options */
2734  if (has_ndistinct)
2735  {
2736  appendPQExpBufferStr(&buf, "ndistinct");
2737  gotone = true;
2738  }
2739 
2740  if (has_dependencies)
2741  {
2742  appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2743  gotone = true;
2744  }
2745 
2746  if (has_mcv)
2747  {
2748  appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2749  }
2750 
2751  appendPQExpBufferChar(&buf, ')');
2752  }
2753 
2754  appendPQExpBuffer(&buf, " ON %s FROM %s",
2755  PQgetvalue(result, i, 4),
2756  PQgetvalue(result, i, 1));
2757 
2758  /* Show the stats target if it's not default */
2759  if (strcmp(PQgetvalue(result, i, 8), "-1") != 0)
2760  appendPQExpBuffer(&buf, "; STATISTICS %s",
2761  PQgetvalue(result, i, 8));
2762 
2763  printTableAddFooter(&cont, buf.data);
2764  }
2765  }
2766  PQclear(result);
2767  }
2768  else if (pset.sversion >= 100000)
2769  {
2771  "SELECT oid, "
2772  "stxrelid::pg_catalog.regclass, "
2773  "stxnamespace::pg_catalog.regnamespace AS nsp, "
2774  "stxname,\n"
2775  " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')\n"
2776  " FROM pg_catalog.unnest(stxkeys) s(attnum)\n"
2777  " JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND\n"
2778  " a.attnum = s.attnum AND NOT attisdropped)) AS columns,\n"
2779  " 'd' = any(stxkind) AS ndist_enabled,\n"
2780  " 'f' = any(stxkind) AS deps_enabled,\n"
2781  " 'm' = any(stxkind) AS mcv_enabled,\n");
2782 
2783  if (pset.sversion >= 130000)
2784  appendPQExpBufferStr(&buf, " stxstattarget\n");
2785  else
2786  appendPQExpBufferStr(&buf, " -1 AS stxstattarget\n");
2787  appendPQExpBuffer(&buf, "FROM pg_catalog.pg_statistic_ext\n"
2788  "WHERE stxrelid = '%s'\n"
2789  "ORDER BY 1;",
2790  oid);
2791 
2792  result = PSQLexec(buf.data);
2793  if (!result)
2794  goto error_return;
2795  else
2796  tuples = PQntuples(result);
2797 
2798  if (tuples > 0)
2799  {
2800  printTableAddFooter(&cont, _("Statistics objects:"));
2801 
2802  for (i = 0; i < tuples; i++)
2803  {
2804  bool gotone = false;
2805 
2806  printfPQExpBuffer(&buf, " ");
2807 
2808  /* statistics object name (qualified with namespace) */
2809  appendPQExpBuffer(&buf, "\"%s.%s\" (",
2810  PQgetvalue(result, i, 2),
2811  PQgetvalue(result, i, 3));
2812 
2813  /* options */
2814  if (strcmp(PQgetvalue(result, i, 5), "t") == 0)
2815  {
2816  appendPQExpBufferStr(&buf, "ndistinct");
2817  gotone = true;
2818  }
2819 
2820  if (strcmp(PQgetvalue(result, i, 6), "t") == 0)
2821  {
2822  appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
2823  gotone = true;
2824  }
2825 
2826  if (strcmp(PQgetvalue(result, i, 7), "t") == 0)
2827  {
2828  appendPQExpBuffer(&buf, "%smcv", gotone ? ", " : "");
2829  }
2830 
2831  appendPQExpBuffer(&buf, ") ON %s FROM %s",
2832  PQgetvalue(result, i, 4),
2833  PQgetvalue(result, i, 1));
2834 
2835  /* Show the stats target if it's not default */
2836  if (strcmp(PQgetvalue(result, i, 8), "-1") != 0)
2837  appendPQExpBuffer(&buf, "; STATISTICS %s",
2838  PQgetvalue(result, i, 8));
2839 
2840  printTableAddFooter(&cont, buf.data);
2841  }
2842  }
2843  PQclear(result);
2844  }
2845 
2846  /* print rules */
2847  if (tableinfo.hasrules && tableinfo.relkind != RELKIND_MATVIEW)
2848  {
2850  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
2851  "ev_enabled\n"
2852  "FROM pg_catalog.pg_rewrite r\n"
2853  "WHERE r.ev_class = '%s' ORDER BY 1;",
2854  oid);
2855  result = PSQLexec(buf.data);
2856  if (!result)
2857  goto error_return;
2858  else
2859  tuples = PQntuples(result);
2860 
2861  if (tuples > 0)
2862  {
2863  bool have_heading;
2864  int category;
2865 
2866  for (category = 0; category < 4; category++)
2867  {
2868  have_heading = false;
2869 
2870  for (i = 0; i < tuples; i++)
2871  {
2872  const char *ruledef;
2873  bool list_rule = false;
2874 
2875  switch (category)
2876  {
2877  case 0:
2878  if (*PQgetvalue(result, i, 2) == 'O')
2879  list_rule = true;
2880  break;
2881  case 1:
2882  if (*PQgetvalue(result, i, 2) == 'D')
2883  list_rule = true;
2884  break;
2885  case 2:
2886  if (*PQgetvalue(result, i, 2) == 'A')
2887  list_rule = true;
2888  break;
2889  case 3:
2890  if (*PQgetvalue(result, i, 2) == 'R')
2891  list_rule = true;
2892  break;
2893  }
2894  if (!list_rule)
2895  continue;
2896 
2897  if (!have_heading)
2898  {
2899  switch (category)
2900  {
2901  case 0:
2902  printfPQExpBuffer(&buf, _("Rules:"));
2903  break;
2904  case 1:
2905  printfPQExpBuffer(&buf, _("Disabled rules:"));
2906  break;
2907  case 2:
2908  printfPQExpBuffer(&buf, _("Rules firing always:"));
2909  break;
2910  case 3:
2911  printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
2912  break;
2913  }
2914  printTableAddFooter(&cont, buf.data);
2915  have_heading = true;
2916  }
2917 
2918  /* Everything after "CREATE RULE" is echoed verbatim */
2919  ruledef = PQgetvalue(result, i, 1);
2920  ruledef += 12;
2921  printfPQExpBuffer(&buf, " %s", ruledef);
2922  printTableAddFooter(&cont, buf.data);
2923  }
2924  }
2925  }
2926  PQclear(result);
2927  }
2928 
2929  /* print any publications */
2930  if (pset.sversion >= 100000)
2931  {
2932  if (pset.sversion >= 150000)
2933  {
2935  "SELECT pubname\n"
2936  " , NULL\n"
2937  " , NULL\n"
2938  "FROM pg_catalog.pg_publication p\n"
2939  " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
2940  " JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
2941  "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
2942  "UNION\n"
2943  "SELECT pubname\n"
2944  " , pg_get_expr(pr.prqual, c.oid)\n"
2945  " , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
2946  " (SELECT string_agg(attname, ', ')\n"
2947  " FROM pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
2948  " pg_catalog.pg_attribute\n"
2949  " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
2950  " ELSE NULL END) "
2951  "FROM pg_catalog.pg_publication p\n"
2952  " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
2953  " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
2954  "WHERE pr.prrelid = '%s'\n"
2955  "UNION\n"
2956  "SELECT pubname\n"
2957  " , NULL\n"
2958  " , NULL\n"
2959  "FROM pg_catalog.pg_publication p\n"
2960  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
2961  "ORDER BY 1;",
2962  oid, oid, oid, oid);
2963  }
2964  else
2965  {
2967  "SELECT pubname\n"
2968  " , NULL\n"
2969  " , NULL\n"
2970  "FROM pg_catalog.pg_publication p\n"
2971  "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
2972  "WHERE pr.prrelid = '%s'\n"
2973  "UNION ALL\n"
2974  "SELECT pubname\n"
2975  " , NULL\n"
2976  " , NULL\n"
2977  "FROM pg_catalog.pg_publication p\n"
2978  "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
2979  "ORDER BY 1;",
2980  oid, oid);
2981  }
2982 
2983  result = PSQLexec(buf.data);
2984  if (!result)
2985  goto error_return;
2986  else
2987  tuples = PQntuples(result);
2988 
2989  if (tuples > 0)
2990  printTableAddFooter(&cont, _("Publications:"));
2991 
2992  /* Might be an empty set - that's ok */
2993  for (i = 0; i < tuples; i++)
2994  {
2995  printfPQExpBuffer(&buf, " \"%s\"",
2996  PQgetvalue(result, i, 0));
2997 
2998  /* column list (if any) */
2999  if (!PQgetisnull(result, i, 2))
3000  appendPQExpBuffer(&buf, " (%s)",
3001  PQgetvalue(result, i, 2));
3002 
3003  /* row filter (if any) */
3004  if (!PQgetisnull(result, i, 1))
3005  appendPQExpBuffer(&buf, " WHERE %s",
3006  PQgetvalue(result, i, 1));
3007 
3008  printTableAddFooter(&cont, buf.data);
3009  }
3010  PQclear(result);
3011  }
3012  }
3013 
3014  /* Get view_def if table is a view or materialized view */
3015  if ((tableinfo.relkind == RELKIND_VIEW ||
3016  tableinfo.relkind == RELKIND_MATVIEW) && verbose)
3017  {
3018  PGresult *result;
3019 
3021  "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
3022  oid);
3023  result = PSQLexec(buf.data);
3024  if (!result)
3025  goto error_return;
3026 
3027  if (PQntuples(result) > 0)
3028  view_def = pg_strdup(PQgetvalue(result, 0, 0));
3029 
3030  PQclear(result);
3031  }
3032 
3033  if (view_def)
3034  {
3035  PGresult *result = NULL;
3036 
3037  /* Footer information about a view */
3038  printTableAddFooter(&cont, _("View definition:"));
3039  printTableAddFooter(&cont, view_def);
3040 
3041  /* print rules */
3042  if (tableinfo.hasrules)
3043  {
3045  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
3046  "FROM pg_catalog.pg_rewrite r\n"
3047  "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
3048  oid);
3049  result = PSQLexec(buf.data);
3050  if (!result)
3051  goto error_return;
3052 
3053  if (PQntuples(result) > 0)
3054  {
3055  printTableAddFooter(&cont, _("Rules:"));
3056  for (i = 0; i < PQntuples(result); i++)
3057  {
3058  const char *ruledef;
3059 
3060  /* Everything after "CREATE RULE" is echoed verbatim */
3061  ruledef = PQgetvalue(result, i, 1);
3062  ruledef += 12;
3063 
3064  printfPQExpBuffer(&buf, " %s", ruledef);
3065  printTableAddFooter(&cont, buf.data);
3066  }
3067  }
3068  PQclear(result);
3069  }
3070  }
3071 
3072  /*
3073  * Print triggers next, if any (but only user-defined triggers). This
3074  * could apply to either a table or a view.
3075  */
3076  if (tableinfo.hastriggers)
3077  {
3078  PGresult *result;
3079  int tuples;
3080 
3082  "SELECT t.tgname, "
3083  "pg_catalog.pg_get_triggerdef(t.oid, true), "
3084  "t.tgenabled, t.tgisinternal,\n");
3085 
3086  /*
3087  * Detect whether each trigger is inherited, and if so, get the name
3088  * of the topmost table it's inherited from. We have no easy way to
3089  * do that pre-v13, for lack of the tgparentid column. Even with
3090  * tgparentid, a straightforward search for the topmost parent would
3091  * require a recursive CTE, which seems unduly expensive. We cheat a
3092  * bit by assuming parent triggers will match by tgname; then, joining
3093  * with pg_partition_ancestors() allows the planner to make use of
3094  * pg_trigger_tgrelid_tgname_index if it wishes. We ensure we find
3095  * the correct topmost parent by stopping at the first-in-partition-
3096  * ancestry-order trigger that has tgparentid = 0. (There might be
3097  * unrelated, non-inherited triggers with the same name further up the
3098  * stack, so this is important.)
3099  */
3100  if (pset.sversion >= 130000)
3102  " CASE WHEN t.tgparentid != 0 THEN\n"
3103  " (SELECT u.tgrelid::pg_catalog.regclass\n"
3104  " FROM pg_catalog.pg_trigger AS u,\n"
3105  " pg_catalog.pg_partition_ancestors(t.tgrelid) WITH ORDINALITY AS a(relid, depth)\n"
3106  " WHERE u.tgname = t.tgname AND u.tgrelid = a.relid\n"
3107  " AND u.tgparentid = 0\n"
3108  " ORDER BY a.depth LIMIT 1)\n"
3109  " END AS parent\n");
3110  else
3111  appendPQExpBufferStr(&buf, " NULL AS parent\n");
3112 
3114  "FROM pg_catalog.pg_trigger t\n"
3115  "WHERE t.tgrelid = '%s' AND ",
3116  oid);
3117 
3118  /*
3119  * tgisinternal is set true for inherited triggers of partitions in
3120  * servers between v11 and v14, though these must still be shown to
3121  * the user. So we use another property that is true for such
3122  * inherited triggers to avoid them being hidden, which is their
3123  * dependence on another trigger.
3124  */
3125  if (pset.sversion >= 110000 && pset.sversion < 150000)
3126  appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n"
3127  " OR EXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n"
3128  " AND refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass))");
3129  else
3130  /* display/warn about disabled internal triggers */
3131  appendPQExpBufferStr(&buf, "(NOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D'))");
3132  appendPQExpBufferStr(&buf, "\nORDER BY 1;");
3133 
3134  result = PSQLexec(buf.data);
3135  if (!result)
3136  goto error_return;
3137  else
3138  tuples = PQntuples(result);
3139 
3140  if (tuples > 0)
3141  {
3142  bool have_heading;
3143  int category;
3144 
3145  /*
3146  * split the output into 4 different categories. Enabled triggers,
3147  * disabled triggers and the two special ALWAYS and REPLICA
3148  * configurations.
3149  */
3150  for (category = 0; category <= 4; category++)
3151  {
3152  have_heading = false;
3153  for (i = 0; i < tuples; i++)
3154  {
3155  bool list_trigger;
3156  const char *tgdef;
3157  const char *usingpos;
3158  const char *tgenabled;
3159  const char *tgisinternal;
3160 
3161  /*
3162  * Check if this trigger falls into the current category
3163  */
3164  tgenabled = PQgetvalue(result, i, 2);
3165  tgisinternal = PQgetvalue(result, i, 3);
3166  list_trigger = false;
3167  switch (category)
3168  {
3169  case 0:
3170  if (*tgenabled == 'O' || *tgenabled == 't')
3171  list_trigger = true;
3172  break;
3173  case 1:
3174  if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3175  *tgisinternal == 'f')
3176  list_trigger = true;
3177  break;
3178  case 2:
3179  if ((*tgenabled == 'D' || *tgenabled == 'f') &&
3180  *tgisinternal == 't')
3181  list_trigger = true;
3182  break;
3183  case 3:
3184  if (*tgenabled == 'A')
3185  list_trigger = true;
3186  break;
3187  case 4:
3188  if (*tgenabled == 'R')
3189  list_trigger = true;
3190  break;
3191  }
3192  if (list_trigger == false)
3193  continue;
3194 
3195  /* Print the category heading once */
3196  if (have_heading == false)
3197  {
3198  switch (category)
3199  {
3200  case 0:
3201  printfPQExpBuffer(&buf, _("Triggers:"));
3202  break;
3203  case 1:
3204  printfPQExpBuffer(&buf, _("Disabled user triggers:"));
3205  break;
3206  case 2:
3207  printfPQExpBuffer(&buf, _("Disabled internal triggers:"));
3208  break;
3209  case 3:
3210  printfPQExpBuffer(&buf, _("Triggers firing always:"));
3211  break;
3212  case 4:
3213  printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
3214  break;
3215  }
3216  printTableAddFooter(&cont, buf.data);
3217  have_heading = true;
3218  }
3219 
3220  /* Everything after "TRIGGER" is echoed verbatim */
3221  tgdef = PQgetvalue(result, i, 1);
3222  usingpos = strstr(tgdef, " TRIGGER ");
3223  if (usingpos)
3224  tgdef = usingpos + 9;
3225 
3226  printfPQExpBuffer(&buf, " %s", tgdef);
3227 
3228  /* Visually distinguish inherited triggers */
3229  if (!PQgetisnull(result, i, 4))
3230  appendPQExpBuffer(&buf, ", ON TABLE %s",
3231  PQgetvalue(result, i, 4));
3232 
3233  printTableAddFooter(&cont, buf.data);
3234  }
3235  }
3236  }
3237  PQclear(result);
3238  }
3239 
3240  /*
3241  * Finish printing the footer information about a table.
3242  */
3243  if (tableinfo.relkind == RELKIND_RELATION ||
3244  tableinfo.relkind == RELKIND_MATVIEW ||
3245  tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
3246  tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3247  tableinfo.relkind == RELKIND_PARTITIONED_INDEX ||
3248  tableinfo.relkind == RELKIND_TOASTVALUE)
3249  {
3250  bool is_partitioned;
3251  PGresult *result;
3252  int tuples;
3253 
3254  /* simplify some repeated tests below */
3255  is_partitioned = (tableinfo.relkind == RELKIND_PARTITIONED_TABLE ||
3256  tableinfo.relkind == RELKIND_PARTITIONED_INDEX);
3257 
3258  /* print foreign server name */
3259  if (tableinfo.relkind == RELKIND_FOREIGN_TABLE)
3260  {
3261  char *ftoptions;
3262 
3263  /* Footer information about foreign table */
3265  "SELECT s.srvname,\n"
3266  " pg_catalog.array_to_string(ARRAY(\n"
3267  " SELECT pg_catalog.quote_ident(option_name)"
3268  " || ' ' || pg_catalog.quote_literal(option_value)\n"
3269  " FROM pg_catalog.pg_options_to_table(ftoptions)), ', ')\n"
3270  "FROM pg_catalog.pg_foreign_table f,\n"
3271  " pg_catalog.pg_foreign_server s\n"
3272  "WHERE f.ftrelid = '%s' AND s.oid = f.ftserver;",
3273  oid);
3274  result = PSQLexec(buf.data);
3275  if (!result)
3276  goto error_return;
3277  else if (PQntuples(result) != 1)
3278  {
3279  PQclear(result);
3280  goto error_return;
3281  }
3282 
3283  /* Print server name */
3284  printfPQExpBuffer(&buf, _("Server: %s"),
3285  PQgetvalue(result, 0, 0));
3286  printTableAddFooter(&cont, buf.data);
3287 
3288  /* Print per-table FDW options, if any */
3289  ftoptions = PQgetvalue(result, 0, 1);
3290  if (ftoptions && ftoptions[0] != '\0')
3291  {
3292  printfPQExpBuffer(&buf, _("FDW options: (%s)"), ftoptions);
3293  printTableAddFooter(&cont, buf.data);
3294  }
3295  PQclear(result);
3296  }
3297 
3298  /* print tables inherited from (exclude partitioned parents) */
3300  "SELECT c.oid::pg_catalog.regclass\n"
3301  "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3302  "WHERE c.oid = i.inhparent AND i.inhrelid = '%s'\n"
3303  " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_TABLE)
3304  " AND c.relkind != " CppAsString2(RELKIND_PARTITIONED_INDEX)
3305  "\nORDER BY inhseqno;",
3306  oid);
3307 
3308  result = PSQLexec(buf.data);
3309  if (!result)
3310  goto error_return;
3311  else
3312  {
3313  const char *s = _("Inherits");
3314  int sw = pg_wcswidth(s, strlen(s), pset.encoding);
3315 
3316  tuples = PQntuples(result);
3317 
3318  for (i = 0; i < tuples; i++)
3319  {
3320  if (i == 0)
3321  printfPQExpBuffer(&buf, "%s: %s",
3322  s, PQgetvalue(result, i, 0));
3323  else
3324  printfPQExpBuffer(&buf, "%*s %s",
3325  sw, "", PQgetvalue(result, i, 0));
3326  if (i < tuples - 1)
3327  appendPQExpBufferChar(&buf, ',');
3328 
3329  printTableAddFooter(&cont, buf.data);
3330  }
3331 
3332  PQclear(result);
3333  }
3334 
3335  /* print child tables (with additional info if partitions) */
3336  if (pset.sversion >= 140000)
3338  "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3339  " inhdetachpending,"
3340  " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3341  "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3342  "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3343  "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3344  " c.oid::pg_catalog.regclass::pg_catalog.text;",
3345  oid);
3346  else if (pset.sversion >= 100000)
3348  "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3349  " false AS inhdetachpending,"
3350  " pg_catalog.pg_get_expr(c.relpartbound, c.oid)\n"
3351  "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3352  "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3353  "ORDER BY pg_catalog.pg_get_expr(c.relpartbound, c.oid) = 'DEFAULT',"
3354  " c.oid::pg_catalog.regclass::pg_catalog.text;",
3355  oid);
3356  else
3358  "SELECT c.oid::pg_catalog.regclass, c.relkind,"
3359  " false AS inhdetachpending, NULL\n"
3360  "FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i\n"
3361  "WHERE c.oid = i.inhrelid AND i.inhparent = '%s'\n"
3362  "ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;",
3363  oid);
3364 
3365  result = PSQLexec(buf.data);
3366  if (!result)
3367  goto error_return;
3368  tuples = PQntuples(result);
3369 
3370  /*
3371  * For a partitioned table with no partitions, always print the number
3372  * of partitions as zero, even when verbose output is expected.
3373  * Otherwise, we will not print "Partitions" section for a partitioned
3374  * table without any partitions.
3375  */
3376  if (is_partitioned && tuples == 0)
3377  {
3378  printfPQExpBuffer(&buf, _("Number of partitions: %d"), tuples);
3379  printTableAddFooter(&cont, buf.data);
3380  }
3381  else if (!verbose)
3382  {
3383  /* print the number of child tables, if any */
3384  if (tuples > 0)
3385  {
3386  if (is_partitioned)
3387  printfPQExpBuffer(&buf, _("Number of partitions: %d (Use \\d+ to list them.)"), tuples);
3388  else
3389  printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
3390  printTableAddFooter(&cont, buf.data);
3391  }
3392  }
3393  else
3394  {
3395  /* display the list of child tables */
3396  const char *ct = is_partitioned ? _("Partitions") : _("Child tables");
3397  int ctw = pg_wcswidth(ct, strlen(ct), pset.encoding);
3398 
3399  for (i = 0; i < tuples; i++)
3400  {
3401  char child_relkind = *PQgetvalue(result, i, 1);
3402 
3403  if (i == 0)
3404  printfPQExpBuffer(&buf, "%s: %s",
3405  ct, PQgetvalue(result, i, 0));
3406  else
3407  printfPQExpBuffer(&buf, "%*s %s",
3408  ctw, "", PQgetvalue(result, i, 0));
3409  if (!PQgetisnull(result, i, 3))
3410  appendPQExpBuffer(&buf, " %s", PQgetvalue(result, i, 3));
3411  if (child_relkind == RELKIND_PARTITIONED_TABLE ||
3412  child_relkind == RELKIND_PARTITIONED_INDEX)
3413  appendPQExpBufferStr(&buf, ", PARTITIONED");
3414  if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
3415  appendPQExpBufferStr(&buf, " (DETACH PENDING)");
3416  if (i < tuples - 1)
3417  appendPQExpBufferChar(&buf, ',');
3418 
3419  printTableAddFooter(&cont, buf.data);
3420  }
3421  }
3422  PQclear(result);
3423 
3424  /* Table type */
3425  if (tableinfo.reloftype)
3426  {
3427  printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
3428  printTableAddFooter(&cont, buf.data);
3429  }
3430 
3431  if (verbose &&
3432  (tableinfo.relkind == RELKIND_RELATION ||
3433  tableinfo.relkind == RELKIND_MATVIEW) &&
3434 
3435  /*
3436  * No need to display default values; we already display a REPLICA
3437  * IDENTITY marker on indexes.
3438  */
3439  tableinfo.relreplident != 'i' &&
3440  ((strcmp(schemaname, "pg_catalog") != 0 && tableinfo.relreplident != 'd') ||
3441  (strcmp(schemaname, "pg_catalog") == 0 && tableinfo.relreplident != 'n')))
3442  {
3443  const char *s = _("Replica Identity");
3444 
3445  printfPQExpBuffer(&buf, "%s: %s",
3446  s,
3447  tableinfo.relreplident == 'f' ? "FULL" :
3448  tableinfo.relreplident == 'n' ? "NOTHING" :
3449  "???");
3450 
3451  printTableAddFooter(&cont, buf.data);
3452  }
3453 
3454  /* OIDs, if verbose and not a materialized view */
3455  if (verbose && tableinfo.relkind != RELKIND_MATVIEW && tableinfo.hasoids)
3456  printTableAddFooter(&cont, _("Has OIDs: yes"));
3457 
3458  /* Tablespace info */
3459  add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
3460  true);
3461 
3462  /* Access method info */
3463  if (verbose && tableinfo.relam != NULL && !pset.hide_tableam)
3464  {
3465  printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam);
3466  printTableAddFooter(&cont, buf.data);
3467  }
3468  }
3469 
3470  /* reloptions, if verbose */
3471  if (verbose &&
3472  tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
3473  {
3474  const char *t = _("Options");
3475 
3476  printfPQExpBuffer(&buf, "%s: %s", t, tableinfo.reloptions);
3477  printTableAddFooter(&cont, buf.data);
3478  }
3479 
3480  printTable(&cont, pset.queryFout, false, pset.logfile);
3481 
3482  retval = true;
3483 
3484 error_return:
3485 
3486  /* clean up */
3487  if (printTableInitialized)
3488  printTableCleanup(&cont);
3489  termPQExpBuffer(&buf);
3490  termPQExpBuffer(&title);
3492 
3493  free(view_def);
3494 
3495  PQclear(res);
3496 
3497  return retval;
3498 }
signed short int16
Definition: c.h:439
#define CppAsString2(x)
Definition: c.h:300
static void add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline)
Definition: describe.c:3506
int PQfnumber(const PGresult *res, const char *field_name)
Definition: fe-exec.c:3523
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void printTableInit(printTableContent *const content, const printTableOpt *opt, const char *title, const int ncolumns, const int nrows)
Definition: print.c:3127
void printTableCleanup(printTableContent *const content)
Definition: print.c:3295
void printTableAddCell(printTableContent *const content, char *cell, const bool translate, const bool mustfree)
Definition: print.c:3204
void printTable(const printTableContent *cont, FILE *fout, bool is_pager, FILE *flog)
Definition: print.c:3384
void printTableAddHeader(printTableContent *const content, char *header, const bool translate, const char align)
Definition: print.c:3164
#define free(a)
Definition: header.h:65
Assert(fmt[strlen(fmt) - 1] !='\n')
int pg_wcswidth(const char *pwcs, size_t len, int encoding)
Definition: mbprint.c:177
NameData relname
Definition: pg_class.h:38
unsigned int Oid
Definition: postgres_ext.h:31
#define atooid(x)
Definition: postgres_ext.h:42
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:148
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:380
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
const char * fmtId(const char *rawid)
Definition: string_utils.c:64
bool hide_tableam
Definition: settings.h:138
int encoding
Definition: settings.h:83
bool hide_compression
Definition: settings.h:137
printTableOpt topt
Definition: print.h:169
char ** footers
Definition: print.h:172
unsigned short int expanded
Definition: print.h:102
bool default_footer
Definition: print.h:113
static StringInfoData tmpbuf
Definition: walsender.c:160

References _, add_tablespace_footer(), appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), Assert(), atooid, buf, CppAsString2, StringInfoData::data, PQExpBufferData::data, printTableOpt::default_footer, _psqlSettings::encoding, printTableOpt::expanded, fmtId(), printQueryOpt::footers, free, gettext_noop, _psqlSettings::hide_compression, _psqlSettings::hide_tableam, i, initPQExpBuffer(), lengthof, _psqlSettings::logfile, pg_log_error, pg_strdup(), pg_wcswidth(), _psqlSettings::popt, PQclear(), PQfnumber(), PQgetisnull(), PQgetvalue(), PQntuples(), printfPQExpBuffer(), printQuery(), printTable(), printTableAddCell(), printTableAddFooter(), printTableAddHeader(), printTableCleanup(), printTableInit(), pset, psprintf(), PSQLexec(), _psqlSettings::queryFout, _psqlSettings::quiet, relname, res, resetPQExpBuffer(), _psqlSettings::sversion, tablespace, termPQExpBuffer(), printQueryOpt::title, tmpbuf, printQueryOpt::topt, printQueryOpt::translate_header, and verbose.

Referenced by describeTableDetails().

◆ describeOneTSConfig()

static bool describeOneTSConfig ( const char *  oid,
const char *  nspname,
const char *  cfgname,
const char *  pnspname,
const char *  prsname 
)
static

Definition at line 5500 of file describe.c.

5502 {
5504  title;
5505  PGresult *res;
5506  printQueryOpt myopt = pset.popt;
5507 
5508  initPQExpBuffer(&buf);
5509 
5511  "SELECT\n"
5512  " ( SELECT t.alias FROM\n"
5513  " pg_catalog.ts_token_type(c.cfgparser) AS t\n"
5514  " WHERE t.tokid = m.maptokentype ) AS \"%s\",\n"
5515  " pg_catalog.btrim(\n"
5516  " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary\n"
5517  " FROM pg_catalog.pg_ts_config_map AS mm\n"
5518  " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype\n"
5519  " ORDER BY mapcfg, maptokentype, mapseqno\n"
5520  " ) :: pg_catalog.text,\n"
5521  " '{}') AS \"%s\"\n"
5522  "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m\n"
5523  "WHERE c.oid = '%s' AND m.mapcfg = c.oid\n"
5524  "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser\n"
5525  "ORDER BY 1;",
5526  gettext_noop("Token"),
5527  gettext_noop("Dictionaries"),
5528  oid);
5529 
5530  res = PSQLexec(buf.data);
5531  termPQExpBuffer(&buf);
5532  if (!res)
5533  return false;
5534 
5535  initPQExpBuffer(&title);
5536 
5537  if (nspname)
5538  appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
5539  nspname, cfgname);
5540  else
5541  appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
5542  cfgname);
5543 
5544  if (pnspname)
5545  appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
5546  pnspname, prsname);
5547  else
5548  appendPQExpBuffer(&title, _("\nParser: \"%s\""),
5549  prsname);
5550 
5551  myopt.nullPrint = NULL;
5552  myopt.title = title.data;
5553  myopt.footers = NULL;
5554  myopt.topt.default_footer = false;
5555  myopt.translate_header = true;
5556 
5557  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5558 
5559  termPQExpBuffer(&title);
5560 
5561  PQclear(res);
5562  return true;
5563 }

References _, appendPQExpBuffer(), buf, PQExpBufferData::data, printTableOpt::default_footer, printQueryOpt::footers, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::topt, and printQueryOpt::translate_header.

Referenced by listTSConfigsVerbose().

◆ describeOneTSParser()

static bool describeOneTSParser ( const char *  oid,
const char *  nspname,
const char *  prsname 
)
static

Definition at line 5127 of file describe.c.

5128 {
5130  PGresult *res;
5131  PQExpBufferData title;
5132  printQueryOpt myopt = pset.popt;
5133  static const bool translate_columns[] = {true, false, false};
5134 
5135  initPQExpBuffer(&buf);
5136 
5138  "SELECT '%s' AS \"%s\",\n"
5139  " p.prsstart::pg_catalog.regproc AS \"%s\",\n"
5140  " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\"\n"
5141  " FROM pg_catalog.pg_ts_parser p\n"
5142  " WHERE p.oid = '%s'\n"
5143  "UNION ALL\n"
5144  "SELECT '%s',\n"
5145  " p.prstoken::pg_catalog.regproc,\n"
5146  " pg_catalog.obj_description(p.prstoken, 'pg_proc')\n"
5147  " FROM pg_catalog.pg_ts_parser p\n"
5148  " WHERE p.oid = '%s'\n"
5149  "UNION ALL\n"
5150  "SELECT '%s',\n"
5151  " p.prsend::pg_catalog.regproc,\n"
5152  " pg_catalog.obj_description(p.prsend, 'pg_proc')\n"
5153  " FROM pg_catalog.pg_ts_parser p\n"
5154  " WHERE p.oid = '%s'\n"
5155  "UNION ALL\n"
5156  "SELECT '%s',\n"
5157  " p.prsheadline::pg_catalog.regproc,\n"
5158  " pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n"
5159  " FROM pg_catalog.pg_ts_parser p\n"
5160  " WHERE p.oid = '%s'\n"
5161  "UNION ALL\n"
5162  "SELECT '%s',\n"
5163  " p.prslextype::pg_catalog.regproc,\n"
5164  " pg_catalog.obj_description(p.prslextype, 'pg_proc')\n"
5165  " FROM pg_catalog.pg_ts_parser p\n"
5166  " WHERE p.oid = '%s';",
5167  gettext_noop("Start parse"),
5168  gettext_noop("Method"),
5169  gettext_noop("Function"),
5170  gettext_noop("Description"),
5171  oid,
5172  gettext_noop("Get next token"),
5173  oid,
5174  gettext_noop("End parse"),
5175  oid,
5176  gettext_noop("Get headline"),
5177  oid,
5178  gettext_noop("Get token types"),
5179  oid);
5180 
5181  res = PSQLexec(buf.data);
5182  termPQExpBuffer(&buf);
5183  if (!res)
5184  return false;
5185 
5186  myopt.nullPrint = NULL;
5187  initPQExpBuffer(&title);
5188  if (nspname)
5189  printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""),
5190  nspname, prsname);
5191  else
5192  printfPQExpBuffer(&title, _("Text search parser \"%s\""), prsname);
5193  myopt.title = title.data;
5194  myopt.footers = NULL;
5195  myopt.topt.default_footer = false;
5196  myopt.translate_header = true;
5197  myopt.translate_columns = translate_columns;
5198  myopt.n_translate_columns = lengthof(translate_columns);
5199 
5200  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5201 
5202  PQclear(res);
5203 
5204  initPQExpBuffer(&buf);
5205 
5207  "SELECT t.alias as \"%s\",\n"
5208  " t.description as \"%s\"\n"
5209  "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t\n"
5210  "ORDER BY 1;",
5211  gettext_noop("Token name"),
5212  gettext_noop("Description"),
5213  oid);
5214 
5215  res = PSQLexec(buf.data);
5216  termPQExpBuffer(&buf);
5217  if (!res)
5218  return false;
5219 
5220  myopt.nullPrint = NULL;
5221  if (nspname)
5222  printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""),
5223  nspname, prsname);
5224  else
5225  printfPQExpBuffer(&title, _("Token types for parser \"%s\""), prsname);
5226  myopt.title = title.data;
5227  myopt.footers = NULL;
5228  myopt.topt.default_footer = true;
5229  myopt.translate_header = true;
5230  myopt.translate_columns = NULL;
5231  myopt.n_translate_columns = 0;
5232 
5233  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5234 
5235  termPQExpBuffer(&title);
5236  PQclear(res);
5237  return true;
5238 }

References _, buf, PQExpBufferData::data, printTableOpt::default_footer, printQueryOpt::footers, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::topt, printQueryOpt::translate_columns, and printQueryOpt::translate_header.

Referenced by listTSParsersVerbose().

◆ describeOperators()

bool describeOperators ( const char *  oper_pattern,
char **  arg_patterns,
int  num_arg_patterns,
bool  verbose,
bool  showSystem 
)

Definition at line 764 of file describe.c.

767 {
769  PGresult *res;
770  printQueryOpt myopt = pset.popt;
771 
773 
774  /*
775  * Note: before Postgres 9.1, we did not assign comments to any built-in
776  * operators, preferring to let the comment on the underlying function
777  * suffice. The coalesce() on the obj_description() calls below supports
778  * this convention by providing a fallback lookup of a comment on the
779  * operator's function. Since 9.1 there is a policy that every built-in
780  * operator should have a comment; so the coalesce() is no longer
781  * necessary so far as built-in operators are concerned. We keep it
782  * anyway, for now, because third-party modules may still be following the
783  * old convention.
784  *
785  * The support for postfix operators in this query is dead code as of
786  * Postgres 14, but we need to keep it for as long as we support talking
787  * to pre-v14 servers.
788  */
789 
791  "SELECT n.nspname as \"%s\",\n"
792  " o.oprname AS \"%s\",\n"
793  " CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n"
794  " CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n"
795  " pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n",
796  gettext_noop("Schema"),
797  gettext_noop("Name"),
798  gettext_noop("Left arg type"),
799  gettext_noop("Right arg type"),
800  gettext_noop("Result type"));
801 
802  if (verbose)
804  " o.oprcode AS \"%s\",\n",
805  gettext_noop("Function"));
806 
808  " coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n"
809  " pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
810  "FROM pg_catalog.pg_operator o\n"
811  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
812  gettext_noop("Description"));
813 
814  if (num_arg_patterns >= 2)
815  {
816  num_arg_patterns = 2; /* ignore any additional arguments */
818  " LEFT JOIN pg_catalog.pg_type t0 ON t0.oid = o.oprleft\n"
819  " LEFT JOIN pg_catalog.pg_namespace nt0 ON nt0.oid = t0.typnamespace\n"
820  " LEFT JOIN pg_catalog.pg_type t1 ON t1.oid = o.oprright\n"
821  " LEFT JOIN pg_catalog.pg_namespace nt1 ON nt1.oid = t1.typnamespace\n");
822  }
823  else if (num_arg_patterns == 1)
824  {
826  " LEFT JOIN pg_catalog.pg_type t0 ON t0.oid = o.oprright\n"
827  " LEFT JOIN pg_catalog.pg_namespace nt0 ON nt0.oid = t0.typnamespace\n");
828  }
829 
830  if (!showSystem && !oper_pattern)
831  appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
832  " AND n.nspname <> 'information_schema'\n");
833 
834  if (!validateSQLNamePattern(&buf, oper_pattern,
835  !showSystem && !oper_pattern, true,
836  "n.nspname", "o.oprname", NULL,
837  "pg_catalog.pg_operator_is_visible(o.oid)",
838  NULL, 3))
839  return false;
840 
841  if (num_arg_patterns == 1)
842  appendPQExpBufferStr(&buf, " AND o.oprleft = 0\n");
843 
844  for (int i = 0; i < num_arg_patterns; i++)
845  {
846  if (strcmp(arg_patterns[i], "-") != 0)
847  {
848  /*
849  * Match type-name patterns against either internal or external
850  * name, like \dT. Unlike \dT, there seems no reason to
851  * discriminate against arrays or composite types.
852  */
853  char nspname[64];
854  char typname[64];
855  char ft[64];
856  char tiv[64];
857 
858  snprintf(nspname, sizeof(nspname), "nt%d.nspname", i);
859  snprintf(typname, sizeof(typname), "t%d.typname", i);
860  snprintf(ft, sizeof(ft),
861  "pg_catalog.format_type(t%d.oid, NULL)", i);
862  snprintf(tiv, sizeof(tiv),
863  "pg_catalog.pg_type_is_visible(t%d.oid)", i);
865  map_typename_pattern(arg_patterns[i]),
866  true, false,
867  nspname, typname, ft, tiv,
868  NULL, 3))
869  return false;
870  }
871  else
872  {
873  /* "-" pattern specifies no such parameter */
874  appendPQExpBuffer(&buf, " AND t%d.typname IS NULL\n", i);
875  }
876  }
877 
878  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3, 4;");
879 
880  res = PSQLexec(buf.data);
882  if (!res)
883  return false;
884 
885  myopt.nullPrint = NULL;
886  myopt.title = _("List of operators");
887  myopt.translate_header = true;
888 
889  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
890 
891  PQclear(res);
892  return true;
893 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, i, initPQExpBuffer(), _psqlSettings::logfile, map_typename_pattern(), printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, snprintf, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, typname, validateSQLNamePattern(), and verbose.

Referenced by exec_command_dfo().

◆ describePublications()

bool describePublications ( const char *  pattern)

Definition at line 6165 of file describe.c.

6166 {
6168  int i;
6169  PGresult *res;
6170  bool has_pubtruncate;
6171  bool has_pubviaroot;
6172 
6173  PQExpBufferData title;
6174  printTableContent cont;
6175 
6176  if (pset.sversion < 100000)
6177  {
6178  char sverbuf[32];
6179 
6180  pg_log_error("The server (version %s) does not support publications.",
6182  sverbuf, sizeof(sverbuf)));
6183  return true;
6184  }
6185 
6186  has_pubtruncate = (pset.sversion >= 110000);
6187  has_pubviaroot = (pset.sversion >= 130000);
6188 
6189  initPQExpBuffer(&buf);
6190 
6192  "SELECT oid, pubname,\n"
6193  " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
6194  " puballtables, pubinsert, pubupdate, pubdelete");
6195  if (has_pubtruncate)
6197  ", pubtruncate");
6198  if (has_pubviaroot)
6200  ", pubviaroot");
6202  "\nFROM pg_catalog.pg_publication\n");
6203 
6204  if (!validateSQLNamePattern(&buf, pattern, false, false,
6205  NULL, "pubname", NULL,
6206  NULL,
6207  NULL, 1))
6208  return false;
6209 
6210  appendPQExpBufferStr(&buf, "ORDER BY 2;");
6211 
6212  res = PSQLexec(buf.data);
6213  if (!res)
6214  {
6215  termPQExpBuffer(&buf);
6216  return false;
6217  }
6218 
6219  if (PQntuples(res) == 0)
6220  {
6221  if (!pset.quiet)
6222  {
6223  if (pattern)
6224  pg_log_error("Did not find any publication named \"%s\".",
6225  pattern);
6226  else
6227  pg_log_error("Did not find any publications.");
6228  }
6229 
6230  termPQExpBuffer(&buf);
6231  PQclear(res);
6232  return false;
6233  }
6234 
6235  for (i = 0; i < PQntuples(res); i++)
6236  {
6237  const char align = 'l';
6238  int ncols = 5;
6239  int nrows = 1;
6240  char *pubid = PQgetvalue(res, i, 0);
6241  char *pubname = PQgetvalue(res, i, 1);
6242  bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
6243  printTableOpt myopt = pset.popt.topt;
6244 
6245  if (has_pubtruncate)
6246  ncols++;
6247  if (has_pubviaroot)
6248  ncols++;
6249 
6250  initPQExpBuffer(&title);
6251  printfPQExpBuffer(&title, _("Publication %s"), pubname);
6252  printTableInit(&cont, &myopt, title.data, ncols, nrows);
6253 
6254  printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
6255  printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
6256  printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
6257  printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
6258  printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
6259  if (has_pubtruncate)
6260  printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
6261  if (has_pubviaroot)
6262  printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
6263 
6264  printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
6265  printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
6266  printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
6267  printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
6268  printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
6269  if (has_pubtruncate)
6270  printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
6271  if (has_pubviaroot)
6272  printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
6273 
6274  if (!puballtables)
6275  {
6276  /* Get the tables for the specified publication */
6278  "SELECT n.nspname, c.relname");
6279  if (pset.sversion >= 150000)
6280  {
6282  ", pg_get_expr(pr.prqual, c.oid)");
6284  ", (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
6285  " pg_catalog.array_to_string("
6286  " ARRAY(SELECT attname\n"
6287  " FROM\n"
6288  " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
6289  " pg_catalog.pg_attribute\n"
6290  " WHERE attrelid = c.oid AND attnum = prattrs[s]), ', ')\n"
6291  " ELSE NULL END)");
6292  }
6293  else
6295  ", NULL, NULL");
6297  "\nFROM pg_catalog.pg_class c,\n"
6298  " pg_catalog.pg_namespace n,\n"
6299  " pg_catalog.pg_publication_rel pr\n"
6300  "WHERE c.relnamespace = n.oid\n"
6301  " AND c.oid = pr.prrelid\n"
6302  " AND pr.prpubid = '%s'\n"
6303  "ORDER BY 1,2", pubid);
6304  if (!addFooterToPublicationDesc(&buf, _("Tables:"), false, &cont))
6305  goto error_return;
6306 
6307  if (pset.sversion >= 150000)
6308  {
6309  /* Get the schemas for the specified publication */
6311  "SELECT n.nspname\n"
6312  "FROM pg_catalog.pg_namespace n\n"
6313  " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
6314  "WHERE pn.pnpubid = '%s'\n"
6315  "ORDER BY 1", pubid);
6316  if (!addFooterToPublicationDesc(&buf, _("Tables from schemas:"),
6317  true, &cont))
6318  goto error_return;
6319  }
6320  }
6321 
6322  printTable(&cont, pset.queryFout, false, pset.logfile);
6323  printTableCleanup(&cont);
6324 
6325  termPQExpBuffer(&title);
6326  }
6327 
6328  termPQExpBuffer(&buf);
6329  PQclear(res);
6330 
6331  return true;
6332 
6333 error_return:
6334  printTableCleanup(&cont);
6335  PQclear(res);
6336  termPQExpBuffer(&buf);
6337  termPQExpBuffer(&title);
6338  return false;
6339 }
static bool addFooterToPublicationDesc(PQExpBuffer buf, const char *footermsg, bool as_schema, printTableContent *const cont)
Definition: describe.c:6119

References _, addFooterToPublicationDesc(), appendPQExpBuffer(), appendPQExpBufferStr(), buf, PQExpBufferData::data, formatPGVersionNumber(), gettext_noop, i, initPQExpBuffer(), _psqlSettings::logfile, pg_log_error, _psqlSettings::popt, PQclear(), PQgetvalue(), PQntuples(), printfPQExpBuffer(), printTable(), printTableAddCell(), printTableAddHeader(), printTableCleanup(), printTableInit(), pset, PSQLexec(), _psqlSettings::queryFout, _psqlSettings::quiet, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::topt, and validateSQLNamePattern().

Referenced by exec_command_d().

◆ describeRoles()

bool describeRoles ( const char *  pattern,
bool  verbose,
bool  showSystem 
)

Definition at line 3571 of file describe.c.

3572 {
3574  PGresult *res;
3575  printTableContent cont;
3576  printTableOpt myopt = pset.popt.topt;
3577  int ncols = 3;
3578  int nrows = 0;
3579  int i;
3580  int conns;
3581  const char align = 'l';
3582  char **attr;
3583 
3584  myopt.default_footer = false;
3585 
3586  initPQExpBuffer(&buf);
3587 
3589  "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
3590  " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
3591  " r.rolconnlimit, r.rolvaliduntil,\n"
3592  " ARRAY(SELECT b.rolname\n"
3593  " FROM pg_catalog.pg_auth_members m\n"
3594  " JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)\n"
3595  " WHERE m.member = r.oid) as memberof");
3596 
3597  if (verbose)
3598  {
3599  appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
3600  ncols++;
3601  }
3602  appendPQExpBufferStr(&buf, "\n, r.rolreplication");
3603 
3604  if (pset.sversion >= 90500)
3605  {
3606  appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
3607  }
3608 
3609  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
3610 
3611  if (!showSystem && !pattern)
3612  appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
3613 
3614  if (!validateSQLNamePattern(&buf, pattern, false, false,
3615  NULL, "r.rolname", NULL, NULL,
3616  NULL, 1))
3617  return false;
3618 
3619  appendPQExpBufferStr(&buf, "ORDER BY 1;");
3620 
3621  res = PSQLexec(buf.data);
3622  if (!res)
3623  return false;
3624 
3625  nrows = PQntuples(res);
3626  attr = pg_malloc0((nrows + 1) * sizeof(*attr));
3627 
3628  printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
3629 
3630  printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
3631  printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
3632  /* ignores implicit memberships from superuser & pg_database_owner */
3633  printTableAddHeader(&cont, gettext_noop("Member of"), true, align);
3634 
3635  if (verbose)
3636  printTableAddHeader(&cont, gettext_noop("Description"), true, align);
3637 
3638  for (i = 0; i < nrows; i++)
3639  {
3640  printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
3641 
3643  if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
3644  add_role_attribute(&buf, _("Superuser"));
3645 
3646  if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
3647  add_role_attribute(&buf, _("No inheritance"));
3648 
3649  if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
3650  add_role_attribute(&buf, _("Create role"));
3651 
3652  if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
3653  add_role_attribute(&buf, _("Create DB"));
3654 
3655  if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
3656  add_role_attribute(&buf, _("Cannot login"));
3657 
3658  if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
3659  add_role_attribute(&buf, _("Replication"));
3660 
3661  if (pset.sversion >= 90500)
3662  if (strcmp(PQgetvalue(res, i, (verbose ? 11 : 10)), "t") == 0)
3663  add_role_attribute(&buf, _("Bypass RLS"));
3664 
3665  conns = atoi(PQgetvalue(res, i, 6));
3666  if (conns >= 0)
3667  {
3668  if (buf.len > 0)
3669  appendPQExpBufferChar(&buf, '\n');
3670 
3671  if (conns == 0)
3672  appendPQExpBufferStr(&buf, _("No connections"));
3673  else
3674  appendPQExpBuffer(&buf, ngettext("%d connection",
3675  "%d connections",
3676  conns),
3677  conns);
3678  }
3679 
3680  if (strcmp(PQgetvalue(res, i, 7), "") != 0)
3681  {
3682  if (buf.len > 0)
3683  appendPQExpBufferChar(&buf, '\n');
3684  appendPQExpBufferStr(&buf, _("Password valid until "));
3686  }
3687 
3688  attr[i] = pg_strdup(buf.data);
3689 
3690  printTableAddCell(&cont, attr[i], false, false);
3691 
3692  printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
3693 
3694  if (verbose)
3695  printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
3696  }
3697  termPQExpBuffer(&buf);
3698 
3699  printTable(&cont, pset.queryFout, false, pset.logfile);
3700  printTableCleanup(&cont);
3701 
3702  for (i = 0; i < nrows; i++)
3703  free(attr[i]);
3704  free(attr);
3705 
3706  PQclear(res);
3707  return true;
3708 }
#define ngettext(s, p, n)
Definition: c.h:1190
static void add_role_attribute(PQExpBuffer buf, const char *const str)
Definition: describe.c:3711
void * pg_malloc0(size_t size)
Definition: fe_memutils.c:53
static IsoConnInfo * conns

References _, add_role_attribute(), appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), buf, conns, printTableOpt::default_footer, free, gettext_noop, i, initPQExpBuffer(), _psqlSettings::logfile, ngettext, pg_malloc0(), pg_strdup(), _psqlSettings::popt, PQclear(), PQgetvalue(), PQntuples(), printfPQExpBuffer(), printTable(), printTableAddCell(), printTableAddHeader(), printTableCleanup(), printTableInit(), pset, PSQLexec(), _psqlSettings::queryFout, res, resetPQExpBuffer(), _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::topt, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ describeSubscriptions()

bool describeSubscriptions ( const char *  pattern,
bool  verbose 
)

Definition at line 6348 of file describe.c.

6349 {
6351  PGresult *res;
6352  printQueryOpt myopt = pset.popt;
6353  static const bool translate_columns[] = {false, false, false, false,
6354  false, false, false, false, false, false, false};
6355 
6356  if (pset.sversion < 100000)
6357  {
6358  char sverbuf[32];
6359 
6360  pg_log_error("The server (version %s) does not support subscriptions.",
6362  sverbuf, sizeof(sverbuf)));
6363  return true;
6364  }
6365 
6366  initPQExpBuffer(&buf);
6367 
6369  "SELECT subname AS \"%s\"\n"
6370  ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
6371  ", subenabled AS \"%s\"\n"
6372  ", subpublications AS \"%s\"\n",
6373  gettext_noop("Name"),
6374  gettext_noop("Owner"),
6375  gettext_noop("Enabled"),
6376  gettext_noop("Publication"));
6377 
6378  if (verbose)
6379  {
6380  /* Binary mode and streaming are only supported in v14 and higher */
6381  if (pset.sversion >= 140000)
6383  ", subbinary AS \"%s\"\n"
6384  ", substream AS \"%s\"\n",
6385  gettext_noop("Binary"),
6386  gettext_noop("Streaming"));
6387 
6388  /* Two_phase and disable_on_error are only supported in v15 and higher */
6389  if (pset.sversion >= 150000)
6391  ", subtwophasestate AS \"%s\"\n"
6392  ", subdisableonerr AS \"%s\"\n",
6393  gettext_noop("Two-phase commit"),
6394  gettext_noop("Disable on error"));
6395 
6397  ", subsynccommit AS \"%s\"\n"
6398  ", subconninfo AS \"%s\"\n",
6399  gettext_noop("Synchronous commit"),
6400  gettext_noop("Conninfo"));
6401 
6402  /* Skip LSN is only supported in v15 and higher */
6403  if (pset.sversion >= 150000)
6405  ", subskiplsn AS \"%s\"\n",
6406  gettext_noop("Skip LSN"));
6407  }
6408 
6409  /* Only display subscriptions in current database. */
6411  "FROM pg_catalog.pg_subscription\n"
6412  "WHERE subdbid = (SELECT oid\n"
6413  " FROM pg_catalog.pg_database\n"
6414  " WHERE datname = pg_catalog.current_database())");
6415 
6416  if (!validateSQLNamePattern(&buf, pattern, true, false,
6417  NULL, "subname", NULL,
6418  NULL,
6419  NULL, 1))
6420  return false;
6421 
6422  appendPQExpBufferStr(&buf, "ORDER BY 1;");
6423 
6424  res = PSQLexec(buf.data);
6425  termPQExpBuffer(&buf);
6426  if (!res)
6427  return false;
6428 
6429  myopt.nullPrint = NULL;
6430  myopt.title = _("List of subscriptions");
6431  myopt.translate_header = true;
6432  myopt.translate_columns = translate_columns;
6433  myopt.n_translate_columns = lengthof(translate_columns);
6434 
6435  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6436 
6437  PQclear(res);
6438  return true;
6439 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, formatPGVersionNumber(), gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, pg_log_error, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_columns, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ describeTableDetails()

bool describeTableDetails ( const char *  pattern,
bool  verbose,
bool  showSystem 
)

Definition at line 1408 of file describe.c.

1409 {
1411  PGresult *res;
1412  int i;
1413 
1414  initPQExpBuffer(&buf);
1415 
1417  "SELECT c.oid,\n"
1418  " n.nspname,\n"
1419  " c.relname\n"
1420  "FROM pg_catalog.pg_class c\n"
1421  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
1422 
1423  if (!showSystem && !pattern)
1424  appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1425  " AND n.nspname <> 'information_schema'\n");
1426 
1427  if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern, false,
1428  "n.nspname", "c.relname", NULL,
1429  "pg_catalog.pg_table_is_visible(c.oid)",
1430  NULL, 3))
1431  return false;
1432 
1433  appendPQExpBufferStr(&buf, "ORDER BY 2, 3;");
1434 
1435  res = PSQLexec(buf.data);
1436  termPQExpBuffer(&buf);
1437  if (!res)
1438  return false;
1439 
1440  if (PQntuples(res) == 0)
1441  {
1442  if (!pset.quiet)
1443  {
1444  if (pattern)
1445  pg_log_error("Did not find any relation named \"%s\".",
1446  pattern);
1447  else
1448  pg_log_error("Did not find any relations.");
1449  }
1450  PQclear(res);
1451  return false;
1452  }
1453 
1454  for (i = 0; i < PQntuples(res); i++)
1455  {
1456  const char *oid;
1457  const char *nspname;
1458  const char *relname;
1459 
1460  oid = PQgetvalue(res, i, 0);
1461  nspname = PQgetvalue(res, i, 1);
1462  relname = PQgetvalue(res, i, 2);
1463 
1464  if (!describeOneTableDetails(nspname, relname, oid, verbose))
1465  {
1466  PQclear(res);
1467  return false;
1468  }
1469  if (cancel_pressed)
1470  {
1471  PQclear(res);
1472  return false;
1473  }
1474  }
1475 
1476  PQclear(res);
1477  return true;
1478 }
static bool describeOneTableDetails(const char *schemaname, const char *relationname, const char *oid, bool verbose)
Definition: describe.c:1488
volatile sig_atomic_t cancel_pressed
Definition: print.c:43

References appendPQExpBufferStr(), buf, cancel_pressed, describeOneTableDetails(), i, initPQExpBuffer(), pg_log_error, PQclear(), PQgetvalue(), PQntuples(), printfPQExpBuffer(), pset, PSQLexec(), _psqlSettings::quiet, relname, res, termPQExpBuffer(), validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ describeTablespaces()

bool describeTablespaces ( const char *  pattern,
bool  verbose 
)

Definition at line 211 of file describe.c.

212 {
214  PGresult *res;
215  printQueryOpt myopt = pset.popt;
216 
218 
220  "SELECT spcname AS \"%s\",\n"
221  " pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n"
222  " pg_catalog.pg_tablespace_location(oid) AS \"%s\"",
223  gettext_noop("Name"),
224  gettext_noop("Owner"),
225  gettext_noop("Location"));
226 
227  if (verbose)
228  {
229  appendPQExpBufferStr(&buf, ",\n ");
230  printACLColumn(&buf, "spcacl");
232  ",\n spcoptions AS \"%s\""
233  ",\n pg_catalog.pg_size_pretty(pg_catalog.pg_tablespace_size(oid)) AS \"%s\""
234  ",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
235  gettext_noop("Options"),
236  gettext_noop("Size"),
237  gettext_noop("Description"));
238  }
239 
241  "\nFROM pg_catalog.pg_tablespace\n");
242 
243  if (!validateSQLNamePattern(&buf, pattern, false, false,
244  NULL, "spcname", NULL,
245  NULL,
246  NULL, 1))
247  return false;
248 
249  appendPQExpBufferStr(&buf, "ORDER BY 1;");
250 
251  res = PSQLexec(buf.data);
253  if (!res)
254  return false;
255 
256  myopt.nullPrint = NULL;
257  myopt.title = _("List of tablespaces");
258  myopt.translate_header = true;
259 
260  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
261 
262  PQclear(res);
263  return true;
264 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printACLColumn(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ describeTypes()

bool describeTypes ( const char *  pattern,
bool  verbose,
bool  showSystem 
)

Definition at line 611 of file describe.c.

612 {
614  PGresult *res;
615  printQueryOpt myopt = pset.popt;
616 
618 
620  "SELECT n.nspname as \"%s\",\n"
621  " pg_catalog.format_type(t.oid, NULL) AS \"%s\",\n",
622  gettext_noop("Schema"),
623  gettext_noop("Name"));
624  if (verbose)
625  {
627  " t.typname AS \"%s\",\n"
628  " CASE WHEN t.typrelid != 0\n"
629  " THEN CAST('tuple' AS pg_catalog.text)\n"
630  " WHEN t.typlen < 0\n"
631  " THEN CAST('var' AS pg_catalog.text)\n"
632  " ELSE CAST(t.typlen AS pg_catalog.text)\n"
633  " END AS \"%s\",\n"
634  " pg_catalog.array_to_string(\n"
635  " ARRAY(\n"
636  " SELECT e.enumlabel\n"
637  " FROM pg_catalog.pg_enum e\n"
638  " WHERE e.enumtypid = t.oid\n"
639  " ORDER BY e.enumsortorder\n"
640  " ),\n"
641  " E'\\n'\n"
642  " ) AS \"%s\",\n"
643  " pg_catalog.pg_get_userbyid(t.typowner) AS \"%s\",\n",
644  gettext_noop("Internal name"),
645  gettext_noop("Size"),
646  gettext_noop("Elements"),
647  gettext_noop("Owner"));
648  printACLColumn(&buf, "t.typacl");
649  appendPQExpBufferStr(&buf, ",\n ");
650  }
651 
653  " pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
654  gettext_noop("Description"));
655 
656  appendPQExpBufferStr(&buf, "FROM pg_catalog.pg_type t\n"
657  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
658 
659  /*
660  * do not include complex types (typrelid!=0) unless they are standalone
661  * composite types
662  */
663  appendPQExpBufferStr(&buf, "WHERE (t.typrelid = 0 ");
664  appendPQExpBufferStr(&buf, "OR (SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE)
665  " FROM pg_catalog.pg_class c "
666  "WHERE c.oid = t.typrelid))\n");
667 
668  /*
669  * do not include array types unless the pattern contains []
670  */
671  if (pattern == NULL || strstr(pattern, "[]") == NULL)
672  appendPQExpBufferStr(&buf, " AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)\n");
673 
674  if (!showSystem && !pattern)
675  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
676  " AND n.nspname <> 'information_schema'\n");
677 
678  /* Match name pattern against either internal or external name */
680  true, false,
681  "n.nspname", "t.typname",
682  "pg_catalog.format_type(t.oid, NULL)",
683  "pg_catalog.pg_type_is_visible(t.oid)",
684  NULL, 3))
685  return false;
686 
687  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
688 
689  res = PSQLexec(buf.data);
691  if (!res)
692  return false;
693 
694  myopt.nullPrint = NULL;
695  myopt.title = _("List of data types");
696  myopt.translate_header = true;
697 
698  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
699 
700  PQclear(res);
701  return true;
702 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, CppAsString2, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, map_typename_pattern(), printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printACLColumn(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ listAllDbs()

bool listAllDbs ( const char *  pattern,
bool  verbose 
)

Definition at line 902 of file describe.c.

903 {
904  PGresult *res;
906  printQueryOpt myopt = pset.popt;
907 
909 
911  "SELECT d.datname as \"%s\",\n"
912  " pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n"
913  " pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n"
914  " d.datcollate as \"%s\",\n"
915  " d.datctype as \"%s\",\n",
916  gettext_noop("Name"),
917  gettext_noop("Owner"),
918  gettext_noop("Encoding"),
919  gettext_noop("Collate"),
920  gettext_noop("Ctype"));
921  if (pset.sversion >= 150000)
923  " d.daticulocale as \"%s\",\n"
924  " CASE d.datlocprovider WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS \"%s\",\n",
925  gettext_noop("ICU Locale"),
926  gettext_noop("Locale Provider"));
927  else
929  " NULL as \"%s\",\n"
930  " 'libc' AS \"%s\",\n",
931  gettext_noop("ICU Locale"),
932  gettext_noop("Locale Provider"));
933  appendPQExpBufferStr(&buf, " ");
934  printACLColumn(&buf, "d.datacl");
935  if (verbose)
937  ",\n CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n"
938  " THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n"
939  " ELSE 'No Access'\n"
940  " END as \"%s\""
941  ",\n t.spcname as \"%s\""
942  ",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"",
943  gettext_noop("Size"),
944  gettext_noop("Tablespace"),
945  gettext_noop("Description"));
947  "\nFROM pg_catalog.pg_database d\n");
948  if (verbose)
950  " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n");
951 
952  if (pattern)
953  if (!validateSQLNamePattern(&buf, pattern, false, false,
954  NULL, "d.datname", NULL, NULL,
955  NULL, 1))
956  return false;
957 
958  appendPQExpBufferStr(&buf, "ORDER BY 1;");
959  res = PSQLexec(buf.data);
961  if (!res)
962  return false;
963 
964  myopt.nullPrint = NULL;
965  myopt.title = _("List of databases");
966  myopt.translate_header = true;
967 
968  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
969 
970  PQclear(res);
971  return true;
972 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printACLColumn(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_list(), and main().

◆ listCasts()

bool listCasts ( const char *  pattern,
bool  verbose 
)

Definition at line 4666 of file describe.c.

4667 {
4669  PGresult *res;
4670  printQueryOpt myopt = pset.popt;
4671  static const bool translate_columns[] = {false, false, false, true, false};
4672 
4673  initPQExpBuffer(&buf);
4674 
4676  "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
4677  " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n",
4678  gettext_noop("Source type"),
4679  gettext_noop("Target type"));
4680 
4681  /*
4682  * We don't attempt to localize '(binary coercible)' or '(with inout)',
4683  * because there's too much risk of gettext translating a function name
4684  * that happens to match some string in the PO database.
4685  */
4687  " CASE WHEN c.castmethod = '%c' THEN '(binary coercible)'\n"
4688  " WHEN c.castmethod = '%c' THEN '(with inout)'\n"
4689  " ELSE p.proname\n"
4690  " END AS \"%s\",\n",
4691  COERCION_METHOD_BINARY,
4692  COERCION_METHOD_INOUT,
4693  gettext_noop("Function"));
4694 
4696  " CASE WHEN c.castcontext = '%c' THEN '%s'\n"
4697  " WHEN c.castcontext = '%c' THEN '%s'\n"
4698  " ELSE '%s'\n"
4699  " END AS \"%s\"",
4700  COERCION_CODE_EXPLICIT,
4701  gettext_noop("no"),
4702  COERCION_CODE_ASSIGNMENT,
4703  gettext_noop("in assignment"),
4704  gettext_noop("yes"),
4705  gettext_noop("Implicit?"));
4706 
4707  if (verbose)
4709  ",\n d.description AS \"%s\"",
4710  gettext_noop("Description"));
4711 
4712  /*
4713  * We need a left join to pg_proc for binary casts; the others are just
4714  * paranoia.
4715  */
4717  "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
4718  " ON c.castfunc = p.oid\n"
4719  " LEFT JOIN pg_catalog.pg_type ts\n"
4720  " ON c.castsource = ts.oid\n"
4721  " LEFT JOIN pg_catalog.pg_namespace ns\n"
4722  " ON ns.oid = ts.typnamespace\n"
4723  " LEFT JOIN pg_catalog.pg_type tt\n"
4724  " ON c.casttarget = tt.oid\n"
4725  " LEFT JOIN pg_catalog.pg_namespace nt\n"
4726  " ON nt.oid = tt.typnamespace\n");
4727 
4728  if (verbose)
4730  " LEFT JOIN pg_catalog.pg_description d\n"
4731  " ON d.classoid = c.tableoid AND d.objoid = "
4732  "c.oid AND d.objsubid = 0\n");
4733 
4734  appendPQExpBufferStr(&buf, "WHERE ( (true");
4735 
4736  /*
4737  * Match name pattern against either internal or external name of either
4738  * castsource or casttarget
4739  */
4740  if (!validateSQLNamePattern(&buf, pattern, true, false,
4741  "ns.nspname", "ts.typname",
4742  "pg_catalog.format_type(ts.oid, NULL)",
4743  "pg_catalog.pg_type_is_visible(ts.oid)",
4744  NULL, 3))
4745  return false;
4746 
4747  appendPQExpBufferStr(&buf, ") OR (true");
4748 
4749  if (!validateSQLNamePattern(&buf, pattern, true, false,
4750  "nt.nspname", "tt.typname",
4751  "pg_catalog.format_type(tt.oid, NULL)",
4752  "pg_catalog.pg_type_is_visible(tt.oid)",
4753  NULL, 3))
4754  return false;
4755 
4756  appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
4757 
4758  res = PSQLexec(buf.data);
4759  termPQExpBuffer(&buf);
4760  if (!res)
4761  return false;
4762 
4763  myopt.nullPrint = NULL;
4764  myopt.title = _("List of casts");
4765  myopt.translate_header = true;
4766  myopt.translate_columns = translate_columns;
4767  myopt.n_translate_columns = lengthof(translate_columns);
4768 
4769  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4770 
4771  PQclear(res);
4772  return true;
4773 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_columns, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ listCollations()

bool listCollations ( const char *  pattern,
bool  verbose,
bool  showSystem 
)

Definition at line 4781 of file describe.c.

4782 {
4784  PGresult *res;
4785  printQueryOpt myopt = pset.popt;
4786  static const bool translate_columns[] = {false, false, false, false, false, false, true, false};
4787 
4788  initPQExpBuffer(&buf);
4789 
4791  "SELECT n.nspname AS \"%s\",\n"
4792  " c.collname AS \"%s\",\n"
4793  " c.collcollate AS \"%s\",\n"
4794  " c.collctype AS \"%s\"",
4795  gettext_noop("Schema"),
4796  gettext_noop("Name"),
4797  gettext_noop("Collate"),
4798  gettext_noop("Ctype"));
4799 
4800  if (pset.sversion >= 150000)
4802  ",\n c.colliculocale AS \"%s\"",
4803  gettext_noop("ICU Locale"));
4804  else
4806  ",\n c.collcollate AS \"%s\"",
4807  gettext_noop("ICU Locale"));
4808 
4809  if (pset.sversion >= 100000)
4811  ",\n CASE c.collprovider WHEN 'd' THEN 'default' WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS \"%s\"",
4812  gettext_noop("Provider"));
4813  else
4815  ",\n 'libc' AS \"%s\"",
4816  gettext_noop("Provider"));
4817 
4818  if (pset.sversion >= 120000)
4820  ",\n CASE WHEN c.collisdeterministic THEN '%s' ELSE '%s' END AS \"%s\"",
4821  gettext_noop("yes"), gettext_noop("no"),
4822  gettext_noop("Deterministic?"));
4823  else
4825  ",\n '%s' AS \"%s\"",
4826  gettext_noop("yes"),
4827  gettext_noop("Deterministic?"));
4828 
4829  if (verbose)
4831  ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
4832  gettext_noop("Description"));
4833 
4835  "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
4836  "WHERE n.oid = c.collnamespace\n");
4837 
4838  if (!showSystem && !pattern)
4839  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4840  " AND n.nspname <> 'information_schema'\n");
4841 
4842  /*
4843  * Hide collations that aren't usable in the current database's encoding.
4844  * If you think to change this, note that pg_collation_is_visible rejects
4845  * unusable collations, so you will need to hack name pattern processing
4846  * somehow to avoid inconsistent behavior.
4847  */
4848  appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
4849 
4850  if (!validateSQLNamePattern(&buf, pattern, true, false,
4851  "n.nspname", "c.collname", NULL,
4852  "pg_catalog.pg_collation_is_visible(c.oid)",
4853  NULL, 3))
4854  return false;
4855 
4856  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4857 
4858  res = PSQLexec(buf.data);
4859  termPQExpBuffer(&buf);
4860  if (!res)
4861  return false;
4862 
4863  myopt.nullPrint = NULL;
4864  myopt.title = _("List of collations");
4865  myopt.translate_header = true;
4866  myopt.translate_columns = translate_columns;
4867  myopt.n_translate_columns = lengthof(translate_columns);
4868 
4869  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4870 
4871  PQclear(res);
4872  return true;
4873 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_columns, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ listConversions()

bool listConversions ( const char *  pattern,
bool  verbose,
bool  showSystem 
)

Definition at line 4347 of file describe.c.

4348 {
4350  PGresult *res;
4351  printQueryOpt myopt = pset.popt;
4352  static const bool translate_columns[] =
4353  {false, false, false, false, true, false};
4354 
4355  initPQExpBuffer(&buf);
4356 
4358  "SELECT n.nspname AS \"%s\",\n"
4359  " c.conname AS \"%s\",\n"
4360  " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
4361  " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
4362  " CASE WHEN c.condefault THEN '%s'\n"
4363  " ELSE '%s' END AS \"%s\"",
4364  gettext_noop("Schema"),
4365  gettext_noop("Name"),
4366  gettext_noop("Source"),
4367  gettext_noop("Destination"),
4368  gettext_noop("yes"), gettext_noop("no"),
4369  gettext_noop("Default?"));
4370 
4371  if (verbose)
4373  ",\n d.description AS \"%s\"",
4374  gettext_noop("Description"));
4375 
4377  "\nFROM pg_catalog.pg_conversion c\n"
4378  " JOIN pg_catalog.pg_namespace n "
4379  "ON n.oid = c.connamespace\n");
4380 
4381  if (verbose)
4383  "LEFT JOIN pg_catalog.pg_description d "
4384  "ON d.classoid = c.tableoid\n"
4385  " AND d.objoid = c.oid "
4386  "AND d.objsubid = 0\n");
4387 
4388  appendPQExpBufferStr(&buf, "WHERE true\n");
4389 
4390  if (!showSystem && !pattern)
4391  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4392  " AND n.nspname <> 'information_schema'\n");
4393 
4394  if (!validateSQLNamePattern(&buf, pattern, true, false,
4395  "n.nspname", "c.conname", NULL,
4396  "pg_catalog.pg_conversion_is_visible(c.oid)",
4397  NULL, 3))
4398  return false;
4399 
4400  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4401 
4402  res = PSQLexec(buf.data);
4403  termPQExpBuffer(&buf);
4404  if (!res)
4405  return false;
4406 
4407  myopt.nullPrint = NULL;
4408  myopt.title = _("List of conversions");
4409  myopt.translate_header = true;
4410  myopt.translate_columns = translate_columns;
4411  myopt.n_translate_columns = lengthof(translate_columns);
4412 
4413  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4414 
4415  PQclear(res);
4416  return true;
4417 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_columns, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ listDbRoleSettings()

bool listDbRoleSettings ( const char *  pattern,
const char *  pattern2 
)

Definition at line 3723 of file describe.c.

3724 {
3726  PGresult *res;
3727  printQueryOpt myopt = pset.popt;
3728  bool havewhere;
3729 
3730  initPQExpBuffer(&buf);
3731 
3732  printfPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
3733  "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
3734  "FROM pg_catalog.pg_db_role_setting s\n"
3735  "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n"
3736  "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n",
3737  gettext_noop("Role"),
3738  gettext_noop("Database"),
3739  gettext_noop("Settings"));
3740  if (!validateSQLNamePattern(&buf, pattern, false, false,
3741  NULL, "r.rolname", NULL, NULL, &havewhere, 1))
3742  return false;
3743  if (!validateSQLNamePattern(&buf, pattern2, havewhere, false,
3744  NULL, "d.datname", NULL, NULL,
3745  NULL, 1))
3746  return false;
3747  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3748 
3749  res = PSQLexec(buf.data);
3750  termPQExpBuffer(&buf);
3751  if (!res)
3752  return false;
3753 
3754  /*
3755  * Most functions in this file are content to print an empty table when
3756  * there are no matching objects. We intentionally deviate from that
3757  * here, but only in !quiet mode, because of the possibility that the user
3758  * is confused about what the two pattern arguments mean.
3759  */
3760  if (PQntuples(res) == 0 && !pset.quiet)
3761  {
3762  if (pattern && pattern2)
3763  pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".",
3764  pattern, pattern2);
3765  else if (pattern)
3766  pg_log_error("Did not find any settings for role \"%s\".",
3767  pattern);
3768  else
3769  pg_log_error("Did not find any settings.");
3770  }
3771  else
3772  {
3773  myopt.nullPrint = NULL;
3774  myopt.title = _("List of settings");
3775  myopt.translate_header = true;
3776 
3777  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3778  }
3779 
3780  PQclear(res);
3781  return true;
3782 }

References _, appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, printQueryOpt::nullPrint, pg_log_error, _psqlSettings::popt, PQclear(), PQntuples(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, _psqlSettings::quiet, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, and validateSQLNamePattern().

Referenced by exec_command_d().

◆ listDefaultACLs()

bool listDefaultACLs ( const char *  pattern)

Definition at line 1141 of file describe.c.

1142 {
1144  PGresult *res;
1145  printQueryOpt myopt = pset.popt;
1146  static const bool translate_columns[] = {false, false, true, false};
1147 
1148  initPQExpBuffer(&buf);
1149 
1151  "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
1152  " n.nspname AS \"%s\",\n"
1153  " CASE d.defaclobjtype WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' WHEN '%c' THEN '%s' END AS \"%s\",\n"
1154  " ",
1155  gettext_noop("Owner"),
1156  gettext_noop("Schema"),
1157  DEFACLOBJ_RELATION,
1158  gettext_noop("table"),
1159  DEFACLOBJ_SEQUENCE,
1160  gettext_noop("sequence"),
1161  DEFACLOBJ_FUNCTION,
1162  gettext_noop("function"),
1163  DEFACLOBJ_TYPE,
1164  gettext_noop("type"),
1165  DEFACLOBJ_NAMESPACE,
1166  gettext_noop("schema"),
1167  gettext_noop("Type"));
1168 
1169  printACLColumn(&buf, "d.defaclacl");
1170 
1171  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
1172  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
1173 
1174  if (!validateSQLNamePattern(&buf, pattern, false, false,
1175  NULL,
1176  "n.nspname",
1177  "pg_catalog.pg_get_userbyid(d.defaclrole)",
1178  NULL,
1179  NULL, 3))
1180  return false;
1181 
1182  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
1183 
1184  res = PSQLexec(buf.data);
1185  if (!res)
1186  {
1187  termPQExpBuffer(&buf);
1188  return false;
1189  }
1190 
1191  myopt.nullPrint = NULL;
1192  printfPQExpBuffer(&buf, _("Default access privileges"));
1193  myopt.title = buf.data;
1194  myopt.translate_header = true;
1195  myopt.translate_columns = translate_columns;
1196  myopt.n_translate_columns = lengthof(translate_columns);
1197 
1198  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1199 
1200  termPQExpBuffer(&buf);
1201  PQclear(res);
1202  return true;
1203 }

References _, appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printACLColumn(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_columns, printQueryOpt::translate_header, and validateSQLNamePattern().

Referenced by exec_command_d().

◆ listDomains()

bool listDomains ( const char *  pattern,
bool  verbose,
bool  showSystem 
)

Definition at line 4266 of file describe.c.

4267 {
4269  PGresult *res;
4270  printQueryOpt myopt = pset.popt;
4271 
4272  initPQExpBuffer(&buf);
4273 
4275  "SELECT n.nspname as \"%s\",\n"
4276  " t.typname as \"%s\",\n"
4277  " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
4278  " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
4279  " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4280  " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4281  " t.typdefault as \"%s\",\n"
4282  " pg_catalog.array_to_string(ARRAY(\n"
4283  " SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid\n"
4284  " ), ' ') as \"%s\"",
4285  gettext_noop("Schema"),
4286  gettext_noop("Name"),
4287  gettext_noop("Type"),
4288  gettext_noop("Collation"),
4289  gettext_noop("Nullable"),
4290  gettext_noop("Default"),
4291  gettext_noop("Check"));
4292 
4293  if (verbose)
4294  {
4295  appendPQExpBufferStr(&buf, ",\n ");
4296  printACLColumn(&buf, "t.typacl");
4298  ",\n d.description as \"%s\"",
4299  gettext_noop("Description"));
4300  }
4301 
4303  "\nFROM pg_catalog.pg_type t\n"
4304  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
4305 
4306  if (verbose)
4308  " LEFT JOIN pg_catalog.pg_description d "
4309  "ON d.classoid = t.tableoid AND d.objoid = t.oid "
4310  "AND d.objsubid = 0\n");
4311 
4312  appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
4313 
4314  if (!showSystem && !pattern)
4315  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4316  " AND n.nspname <> 'information_schema'\n");
4317 
4318  if (!validateSQLNamePattern(&buf, pattern, true, false,
4319  "n.nspname", "t.typname", NULL,
4320  "pg_catalog.pg_type_is_visible(t.oid)",
4321  NULL, 3))
4322  return false;
4323 
4324  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4325 
4326  res = PSQLexec(buf.data);
4327  termPQExpBuffer(&buf);
4328  if (!res)
4329  return false;
4330 
4331  myopt.nullPrint = NULL;
4332  myopt.title = _("List of domains");
4333  myopt.translate_header = true;
4334 
4335  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4336 
4337  PQclear(res);
4338  return true;
4339 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, printQueryOpt::nullPrint, _psqlSettings::popt, PQclear(), printACLColumn(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ listEventTriggers()

bool listEventTriggers ( const char *  pattern,
bool  verbose 
)

Definition at line 4494 of file describe.c.

4495 {
4497  PGresult *res;
4498  printQueryOpt myopt = pset.popt;
4499  static const bool translate_columns[] =
4500  {false, false, false, true, false, false, false};
4501 
4502  if (pset.sversion < 90300)
4503  {
4504  char sverbuf[32];
4505 
4506  pg_log_error("The server (version %s) does not support event triggers.",
4508  sverbuf, sizeof(sverbuf)));
4509  return true;
4510  }
4511 
4512  initPQExpBuffer(&buf);
4513 
4515  "SELECT evtname as \"%s\", "
4516  "evtevent as \"%s\", "
4517  "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
4518  " case evtenabled when 'O' then '%s'"
4519  " when 'R' then '%s'"
4520  " when 'A' then '%s'"
4521  " when 'D' then '%s' end as \"%s\",\n"
4522  " e.evtfoid::pg_catalog.regproc as \"%s\", "
4523  "pg_catalog.array_to_string(array(select x"
4524  " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
4525  gettext_noop("Name"),
4526  gettext_noop("Event"),
4527  gettext_noop("Owner"),
4528  gettext_noop("enabled"),
4529  gettext_noop("replica"),
4530  gettext_noop("always"),
4531  gettext_noop("disabled"),
4532  gettext_noop("Enabled"),
4533  gettext_noop("Function"),
4534  gettext_noop("Tags"));
4535  if (verbose)
4537  ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
4538  gettext_noop("Description"));
4540  "\nFROM pg_catalog.pg_event_trigger e ");
4541 
4542  if (!validateSQLNamePattern(&buf, pattern, false, false,
4543  NULL, "evtname", NULL, NULL,
4544  NULL, 1))
4545  return false;
4546 
4547  appendPQExpBufferStr(&buf, "ORDER BY 1");
4548 
4549  res = PSQLexec(buf.data);
4550  termPQExpBuffer(&buf);
4551  if (!res)
4552  return false;
4553 
4554  myopt.nullPrint = NULL;
4555  myopt.title = _("List of event triggers");
4556  myopt.translate_header = true;
4557  myopt.translate_columns = translate_columns;
4558  myopt.n_translate_columns = lengthof(translate_columns);
4559 
4560  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4561 
4562  PQclear(res);
4563  return true;
4564 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, formatPGVersionNumber(), gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, printQueryOpt::nullPrint, pg_log_error, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_columns, printQueryOpt::translate_header, validateSQLNamePattern(), and verbose.

Referenced by exec_command_d().

◆ listExtendedStats()

bool listExtendedStats ( const char *  pattern)

Definition at line 4572 of file describe.c.

4573 {
4575  PGresult *res;
4576  printQueryOpt myopt = pset.popt;
4577 
4578  if (pset.sversion < 100000)
4579  {
4580  char sverbuf[32];
4581 
4582  pg_log_error("The server (version %s) does not support extended statistics.",
4584  sverbuf, sizeof(sverbuf)));
4585  return true;
4586  }
4587 
4588  initPQExpBuffer(&buf);
4590  "SELECT \n"
4591  "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text AS \"%s\", \n"
4592  "es.stxname AS \"%s\", \n",
4593  gettext_noop("Schema"),
4594  gettext_noop("Name"));
4595 
4596  if (pset.sversion >= 140000)
4598  "pg_catalog.format('%%s FROM %%s', \n"
4599  " pg_catalog.pg_get_statisticsobjdef_columns(es.oid), \n"
4600  " es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4601  gettext_noop("Definition"));
4602  else
4604  "pg_catalog.format('%%s FROM %%s', \n"
4605  " (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(a.attname),', ') \n"
4606  " FROM pg_catalog.unnest(es.stxkeys) s(attnum) \n"
4607  " JOIN pg_catalog.pg_attribute a \n"
4608  " ON (es.stxrelid = a.attrelid \n"
4609  " AND a.attnum = s.attnum \n"
4610  " AND NOT a.attisdropped)), \n"
4611  "es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4612  gettext_noop("Definition"));
4613 
4615  ",\nCASE WHEN 'd' = any(es.stxkind) THEN 'defined' \n"
4616  "END AS \"%s\", \n"
4617  "CASE WHEN 'f' = any(es.stxkind) THEN 'defined' \n"
4618  "END AS \"%s\"",
4619  gettext_noop("Ndistinct"),
4620  gettext_noop("Dependencies"));
4621 
4622  /*
4623  * Include the MCV statistics kind.
4624  */
4625  if (pset.sversion >= 120000)
4626  {
4628  ",\nCASE WHEN 'm' = any(es.stxkind) THEN 'defined' \n"
4629  "END AS \"%s\" ",
4630  gettext_noop("MCV"));
4631  }
4632 
4634  " \nFROM pg_catalog.pg_statistic_ext es \n");
4635 
4636  if (!validateSQLNamePattern(&buf, pattern,
4637  false, false,
4638  "es.stxnamespace::pg_catalog.regnamespace::pg_catalog.text", "es.stxname",
4639  NULL, "pg_catalog.pg_statistics_obj_is_visible(es.oid)",
4640  NULL, 3))
4641  return false;
4642 
4643  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4644 
4645  res = PSQLexec(buf.data);
4646  termPQExpBuffer(&buf);
4647  if (!res)
4648  return false;
4649 
4650  myopt.nullPrint = NULL;
4651  myopt.title = _("List of extended statistics");
4652  myopt.translate_header = true;
4653 
4654  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4655 
4656  PQclear(res);
4657  return true;
4658 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, formatPGVersionNumber(), gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, printQueryOpt::nullPrint, pg_log_error, _psqlSettings::popt, PQclear(), printfPQExpBuffer(), printQuery(), pset, PSQLexec(), _psqlSettings::queryFout, res, _psqlSettings::sversion, termPQExpBuffer(), printQueryOpt::title, printQueryOpt::translate_header, and validateSQLNamePattern().

Referenced by exec_command_d().

◆ listExtensionContents()

bool listExtensionContents ( const char *  pattern)

Definition at line 5887 of file describe.c.

5888 {
5890  PGresult *res;
5891  int i;
5892 
5893  initPQExpBuffer(&buf);
5895  "SELECT e.extname, e.oid\n"
5896  "FROM pg_catalog.pg_extension e\n");
5897 
5898  if (!validateSQLNamePattern(&buf, pattern,
5899  false, false,
5900  NULL, "e.extname", NULL,
5901  NULL,
5902  NULL, 1))
5903  return false;
5904 
5905  appendPQExpBufferStr(&buf, "ORDER BY 1;");
5906 
5907  res = PSQLexec(buf.data);
5908  termPQExpBuffer(&buf);
5909  if (!res)
5910  return false;
5911 
5912  if (PQntuples(res) == 0)
5913  {
5914  if (!pset.quiet)
5915  {
5916  if (pattern)
5917  pg_log_error("Did not find any extension named \"%s\".",
5918  pattern);
5919  else
5920  pg_log_error("Did not find any extensions.");
5921  }
5922  PQclear(res);
5923  return false;
5924  }
5925 
5926  for (i = 0; i < PQntuples(res); i++)
5927  {
5928  const char *extname;
5929  const char *oid;
5930 
5931  extname = PQgetvalue(res, i, 0);
5932  oid = PQgetvalue(res, i, 1);
5933 
5934  if (!listOneExtensionContents(extname, oid))
5935  {
5936  PQclear(res);
5937  return false;
5938  }
5939  if (cancel_pressed)
5940  {
5941  PQclear(res);
5942  return false;
5943  }
5944  }
5945 
5946  PQclear(res);
5947  return true;
5948 }
static bool listOneExtensionContents(const char *extname, const char *oid)
Definition: describe.c:5951

References appendPQExpBufferStr(), buf, cancel_pressed, i, initPQExpBuffer(), listOneExtensionContents(), pg_log_error, PQclear(), PQgetvalue(), PQntuples(), printfPQExpBuffer(), pset, PSQLexec(), _psqlSettings::quiet, res, termPQExpBuffer(), and validateSQLNamePattern().

Referenced by exec_command_d().

◆ listExtensions()

bool listExtensions ( const char *  pattern)

Definition at line 5838 of file describe.c.

5839 {
5841  PGresult *res;
5842  printQueryOpt myopt = pset.popt;
5843 
5844  initPQExpBuffer(&buf);
5846  "SELECT e.extname AS \"%s\", "
5847  "e.extversion AS \"%s\", n.nspname AS \"%s\", c.description AS \"%s\"\n"
5848  "FROM pg_catalog.pg_extension e "
5849  "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
5850  "LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid "
5851  "AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n",
5852  gettext_noop("Name"),
5853  gettext_noop("Version"),
5854  gettext_noop("Schema"),