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 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 showSystem)
 
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 describeRoleGrants (const char *pattern, bool showSystem)
 
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 3796 of file describe.c.

3797 {
3798  if (buf->len > 0)
3799  appendPQExpBufferStr(buf, ", ");
3800 
3802 }
const char * str
static char * buf
Definition: pg_test_fsync.c:72
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367

References appendPQExpBufferStr(), buf, and 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 3596 of file describe.c.

3598 {
3599  /* relkinds for which we support tablespaces */
3600  if (relkind == RELKIND_RELATION ||
3601  relkind == RELKIND_MATVIEW ||
3602  relkind == RELKIND_INDEX ||
3603  relkind == RELKIND_PARTITIONED_TABLE ||
3604  relkind == RELKIND_PARTITIONED_INDEX ||
3605  relkind == RELKIND_TOASTVALUE)
3606  {
3607  /*
3608  * We ignore the database default tablespace so that users not using
3609  * tablespaces don't need to know about them.
3610  */
3611  if (tablespace != 0)
3612  {
3613  PGresult *result = NULL;
3615 
3616  initPQExpBuffer(&buf);
3618  "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3619  "WHERE oid = '%u';", tablespace);
3620  result = PSQLexec(buf.data);
3621  if (!result)
3622  {
3623  termPQExpBuffer(&buf);
3624  return;
3625  }
3626  /* Should always be the case, but.... */
3627  if (PQntuples(result) > 0)
3628  {
3629  if (newline)
3630  {
3631  /* Add the tablespace as a new footer */
3632  printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3633  PQgetvalue(result, 0, 0));
3634  printTableAddFooter(cont, buf.data);
3635  }
3636  else
3637  {
3638  /* Append the tablespace to the latest footer */
3639  printfPQExpBuffer(&buf, "%s", cont->footer->data);
3640 
3641  /*-------
3642  translator: before this string there's an index description like
3643  '"foo_pkey" PRIMARY KEY, btree (a)' */
3644  appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3645  PQgetvalue(result, 0, 0));
3646  printTableSetFooter(cont, buf.data);
3647  }
3648  }
3649  PQclear(result);
3650  termPQExpBuffer(&buf);
3651  }
3652  }
3653 }
PGresult * PSQLexec(const char *query)
Definition: common.c:620
#define _(x)
Definition: elog.c:90
int PQntuples(const PGresult *res)
Definition: fe-exec.c:3481
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3876
void printTableSetFooter(printTableContent *const content, const char *footer)
Definition: print.c:3335
void printTableAddFooter(printTableContent *const content, const char *footer)
Definition: print.c:3310
#define newline
Definition: indent_codes.h:35
static char * tablespace
Definition: pgbench.c:216
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:235
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
printTableFooter * footer
Definition: print.h:177
char * data
Definition: print.h:155

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 6354 of file describe.c.

6356 {
6357  PGresult *res;
6358  int count = 0;
6359  int i = 0;
6360 
6361  res = PSQLexec(buf->data);
6362  if (!res)
6363  return false;
6364  else
6365  count = PQntuples(res);
6366 
6367  if (count > 0)
6368  printTableAddFooter(cont, footermsg);
6369 
6370  for (i = 0; i < count; i++)
6371  {
6372  if (as_schema)
6373  printfPQExpBuffer(buf, " \"%s\"", PQgetvalue(res, i, 0));
6374  else
6375  {
6376  printfPQExpBuffer(buf, " \"%s.%s\"", PQgetvalue(res, i, 0),
6377  PQgetvalue(res, i, 1));
6378 
6379  if (!PQgetisnull(res, i, 3))
6380  appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
6381 
6382  if (!PQgetisnull(res, i, 2))
6383  appendPQExpBuffer(buf, " WHERE %s", PQgetvalue(res, i, 2));
6384  }
6385 
6386  printTableAddFooter(cont, buf->data);
6387  }
6388 
6389  PQclear(res);
6390  return true;
6391 }
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3901
int i
Definition: isn.c:72

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 140 of file describe.c.

141 {
143  PGresult *res;
144  printQueryOpt myopt = pset.popt;
145  static const bool translate_columns[] = {false, true, false, false};
146 
147  if (pset.sversion < 90600)
148  {
149  char sverbuf[32];
150 
151  pg_log_error("The server (version %s) does not support access methods.",
153  sverbuf, sizeof(sverbuf)));
154  return true;
155  }
156 
158 
160  "SELECT amname AS \"%s\",\n"
161  " CASE amtype"
162  " WHEN 'i' THEN '%s'"
163  " WHEN 't' THEN '%s'"
164  " END AS \"%s\"",
165  gettext_noop("Name"),
166  gettext_noop("Index"),
167  gettext_noop("Table"),
168  gettext_noop("Type"));
169 
170  if (verbose)
171  {
173  ",\n amhandler AS \"%s\",\n"
174  " pg_catalog.obj_description(oid, 'pg_am') AS \"%s\"",
175  gettext_noop("Handler"),
176  gettext_noop("Description"));
177  }
178 
180  "\nFROM pg_catalog.pg_am\n");
181 
182  if (!validateSQLNamePattern(&buf, pattern, false, false,
183  NULL, "amname", NULL,
184  NULL,
185  NULL, 1))
186  {
188  return false;
189  }
190 
191  appendPQExpBufferStr(&buf, "ORDER BY 1;");
192 
193  res = PSQLexec(buf.data);
195  if (!res)
196  return false;
197 
198  myopt.title = _("List of access methods");
199  myopt.translate_header = true;
200  myopt.translate_columns = translate_columns;
201  myopt.n_translate_columns = lengthof(translate_columns);
202 
203  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
204 
205  PQclear(res);
206  return true;
207 }
#define gettext_noop(x)
Definition: c.h:1199
#define lengthof(array)
Definition: c.h:791
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:6221
void printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, bool is_pager, FILE *flog)
Definition: print.c:3549
int verbose
#define pg_log_error(...)
Definition: logging.h:106
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:100
FILE * logfile
Definition: settings.h:131
FILE * queryFout
Definition: settings.h:93
const bool * translate_columns
Definition: print.h:190
char * title
Definition: print.h:187
bool translate_header
Definition: print.h:189
int n_translate_columns
Definition: print.h:192

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, formatPGVersionNumber(), gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, 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 70 of file describe.c.

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

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, _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 4603 of file describe.c.

4605 {
4607  PGresult *res;
4608  printQueryOpt myopt = pset.popt;
4609 
4610  initPQExpBuffer(&buf);
4612  "SELECT s.name AS \"%s\", "
4613  "pg_catalog.current_setting(s.name) AS \"%s\"",
4614  gettext_noop("Parameter"),
4615  gettext_noop("Value"));
4616 
4617  if (verbose)
4618  {
4620  ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4621  gettext_noop("Type"),
4622  gettext_noop("Context"));
4623  if (pset.sversion >= 150000)
4624  printACLColumn(&buf, "p.paracl");
4625  else
4626  appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4627  gettext_noop("Access privileges"));
4628  }
4629 
4630  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4631 
4632  if (verbose && pset.sversion >= 150000)
4634  " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4635  " ON pg_catalog.lower(s.name) = p.parname\n");
4636 
4637  if (pattern)
4638  processSQLNamePattern(pset.db, &buf, pattern,
4639  false, false,
4640  NULL, "pg_catalog.lower(s.name)", NULL,
4641  NULL, NULL, NULL);
4642  else
4643  appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
4644  " s.setting IS DISTINCT FROM s.boot_val\n");
4645 
4646  appendPQExpBufferStr(&buf, "ORDER BY 1;");
4647 
4648  res = PSQLexec(buf.data);
4649  termPQExpBuffer(&buf);
4650  if (!res)
4651  return false;
4652 
4653  if (pattern)
4654  myopt.title = _("List of configuration parameters");
4655  else
4656  myopt.title = _("List of non-default configuration parameters");
4657  myopt.translate_header = true;
4658 
4659  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4660 
4661  PQclear(res);
4662  return true;
4663 }
static void printACLColumn(PQExpBuffer buf, const char *colname)
Definition: describe.c:6732
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:91

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, _psqlSettings::db, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, _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 287 of file describe.c.

290 {
291  bool showAggregate = strchr(functypes, 'a') != NULL;
292  bool showNormal = strchr(functypes, 'n') != NULL;
293  bool showProcedure = strchr(functypes, 'p') != NULL;
294  bool showTrigger = strchr(functypes, 't') != NULL;
295  bool showWindow = strchr(functypes, 'w') != NULL;
296  bool have_where;
298  PGresult *res;
299  printQueryOpt myopt = pset.popt;
300  static const bool translate_columns[] = {false, false, false, false, true, true, true, false, true, false, false, false, false};
301 
302  /* No "Parallel" column before 9.6 */
303  static const bool translate_columns_pre_96[] = {false, false, false, false, true, true, false, true, false, false, false, false};
304 
305  if (strlen(functypes) != strspn(functypes, "anptwS+"))
306  {
307  pg_log_error("\\df only takes [anptwS+] as options");
308  return true;
309  }
310 
311  if (showProcedure && pset.sversion < 110000)
312  {
313  char sverbuf[32];
314 
315  pg_log_error("\\df does not take a \"%c\" option with server version %s",
316  'p',
318  sverbuf, sizeof(sverbuf)));
319  return true;
320  }
321 
322  if (!showAggregate && !showNormal && !showProcedure && !showTrigger && !showWindow)
323  {
324  showAggregate = showNormal = showTrigger = showWindow = true;
325  if (pset.sversion >= 110000)
326  showProcedure = true;
327  }
328 
330 
332  "SELECT n.nspname as \"%s\",\n"
333  " p.proname as \"%s\",\n",
334  gettext_noop("Schema"),
335  gettext_noop("Name"));
336 
337  if (pset.sversion >= 110000)
339  " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
340  " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
341  " CASE p.prokind\n"
342  " WHEN 'a' THEN '%s'\n"
343  " WHEN 'w' THEN '%s'\n"
344  " WHEN 'p' THEN '%s'\n"
345  " ELSE '%s'\n"
346  " END as \"%s\"",
347  gettext_noop("Result data type"),
348  gettext_noop("Argument data types"),
349  /* translator: "agg" is short for "aggregate" */
350  gettext_noop("agg"),
351  gettext_noop("window"),
352  gettext_noop("proc"),
353  gettext_noop("func"),
354  gettext_noop("Type"));
355  else
357  " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
358  " pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
359  " CASE\n"
360  " WHEN p.proisagg THEN '%s'\n"
361  " WHEN p.proiswindow THEN '%s'\n"
362  " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
363  " ELSE '%s'\n"
364  " END as \"%s\"",
365  gettext_noop("Result data type"),
366  gettext_noop("Argument data types"),
367  /* translator: "agg" is short for "aggregate" */
368  gettext_noop("agg"),
369  gettext_noop("window"),
370  gettext_noop("trigger"),
371  gettext_noop("func"),
372  gettext_noop("Type"));
373 
374  if (verbose)
375  {
377  ",\n CASE\n"
378  " WHEN p.provolatile = 'i' THEN '%s'\n"
379  " WHEN p.provolatile = 's' THEN '%s'\n"
380  " WHEN p.provolatile = 'v' THEN '%s'\n"
381  " END as \"%s\"",
382  gettext_noop("immutable"),
383  gettext_noop("stable"),
384  gettext_noop("volatile"),
385  gettext_noop("Volatility"));
386  if (pset.sversion >= 90600)
388  ",\n CASE\n"
389  " WHEN p.proparallel = 'r' THEN '%s'\n"
390  " WHEN p.proparallel = 's' THEN '%s'\n"
391  " WHEN p.proparallel = 'u' THEN '%s'\n"
392  " END as \"%s\"",
393  gettext_noop("restricted"),
394  gettext_noop("safe"),
395  gettext_noop("unsafe"),
396  gettext_noop("Parallel"));
398  ",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\""
399  ",\n CASE WHEN prosecdef THEN '%s' ELSE '%s' END AS \"%s\"",
400  gettext_noop("Owner"),
401  gettext_noop("definer"),
402  gettext_noop("invoker"),
403  gettext_noop("Security"));
404  appendPQExpBufferStr(&buf, ",\n ");
405  printACLColumn(&buf, "p.proacl");
407  ",\n l.lanname as \"%s\"",
408  gettext_noop("Language"));
410  ",\n CASE WHEN l.lanname IN ('internal', 'c') THEN p.prosrc END as \"%s\"",
411  gettext_noop("Internal name"));
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  goto error_return;
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  goto error_return;
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.title = _("List of functions");
585  myopt.translate_header = true;
586  if (pset.sversion >= 90600)
587  {
588  myopt.translate_columns = translate_columns;
589  myopt.n_translate_columns = lengthof(translate_columns);
590  }
591  else
592  {
593  myopt.translate_columns = translate_columns_pre_96;
594  myopt.n_translate_columns = lengthof(translate_columns_pre_96);
595  }
596 
597  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
598 
599  PQclear(res);
600  return true;
601 
602 error_return:
604  return false;
605 }
static const char * map_typename_pattern(const char *pattern)
Definition: describe.c:719
NameData typname
Definition: pg_type.h:41
#define snprintf
Definition: port.h:238

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, formatPGVersionNumber(), gettext_noop, i, initPQExpBuffer(), lengthof, _psqlSettings::logfile, map_typename_pattern(), printQueryOpt::n_translate_columns, 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 1527 of file describe.c.

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

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(), storage, _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 5714 of file describe.c.

5716 {
5718  title;
5719  PGresult *res;
5720  printQueryOpt myopt = pset.popt;
5721 
5722  initPQExpBuffer(&buf);
5723 
5725  "SELECT\n"
5726  " ( SELECT t.alias FROM\n"
5727  " pg_catalog.ts_token_type(c.cfgparser) AS t\n"
5728  " WHERE t.tokid = m.maptokentype ) AS \"%s\",\n"
5729  " pg_catalog.btrim(\n"
5730  " ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary\n"
5731  " FROM pg_catalog.pg_ts_config_map AS mm\n"
5732  " WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype\n"
5733  " ORDER BY mapcfg, maptokentype, mapseqno\n"
5734  " ) :: pg_catalog.text,\n"
5735  " '{}') AS \"%s\"\n"
5736  "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m\n"
5737  "WHERE c.oid = '%s' AND m.mapcfg = c.oid\n"
5738  "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser\n"
5739  "ORDER BY 1;",
5740  gettext_noop("Token"),
5741  gettext_noop("Dictionaries"),
5742  oid);
5743 
5744  res = PSQLexec(buf.data);
5745  termPQExpBuffer(&buf);
5746  if (!res)
5747  return false;
5748 
5749  initPQExpBuffer(&title);
5750 
5751  if (nspname)
5752  appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
5753  nspname, cfgname);
5754  else
5755  appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
5756  cfgname);
5757 
5758  if (pnspname)
5759  appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
5760  pnspname, prsname);
5761  else
5762  appendPQExpBuffer(&title, _("\nParser: \"%s\""),
5763  prsname);
5764 
5765  myopt.title = title.data;
5766  myopt.footers = NULL;
5767  myopt.topt.default_footer = false;
5768  myopt.translate_header = true;
5769 
5770  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5771 
5772  termPQExpBuffer(&title);
5773 
5774  PQclear(res);
5775  return true;
5776 }

References _, appendPQExpBuffer(), buf, PQExpBufferData::data, printTableOpt::default_footer, printQueryOpt::footers, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, _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 5331 of file describe.c.

5332 {
5334  PGresult *res;
5335  PQExpBufferData title;
5336  printQueryOpt myopt = pset.popt;
5337  static const bool translate_columns[] = {true, false, false};
5338 
5339  initPQExpBuffer(&buf);
5340 
5342  "SELECT '%s' AS \"%s\",\n"
5343  " p.prsstart::pg_catalog.regproc AS \"%s\",\n"
5344  " pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\"\n"
5345  " FROM pg_catalog.pg_ts_parser p\n"
5346  " WHERE p.oid = '%s'\n"
5347  "UNION ALL\n"
5348  "SELECT '%s',\n"
5349  " p.prstoken::pg_catalog.regproc,\n"
5350  " pg_catalog.obj_description(p.prstoken, 'pg_proc')\n"
5351  " FROM pg_catalog.pg_ts_parser p\n"
5352  " WHERE p.oid = '%s'\n"
5353  "UNION ALL\n"
5354  "SELECT '%s',\n"
5355  " p.prsend::pg_catalog.regproc,\n"
5356  " pg_catalog.obj_description(p.prsend, 'pg_proc')\n"
5357  " FROM pg_catalog.pg_ts_parser p\n"
5358  " WHERE p.oid = '%s'\n"
5359  "UNION ALL\n"
5360  "SELECT '%s',\n"
5361  " p.prsheadline::pg_catalog.regproc,\n"
5362  " pg_catalog.obj_description(p.prsheadline, 'pg_proc')\n"
5363  " FROM pg_catalog.pg_ts_parser p\n"
5364  " WHERE p.oid = '%s'\n"
5365  "UNION ALL\n"
5366  "SELECT '%s',\n"
5367  " p.prslextype::pg_catalog.regproc,\n"
5368  " pg_catalog.obj_description(p.prslextype, 'pg_proc')\n"
5369  " FROM pg_catalog.pg_ts_parser p\n"
5370  " WHERE p.oid = '%s';",
5371  gettext_noop("Start parse"),
5372  gettext_noop("Method"),
5373  gettext_noop("Function"),
5374  gettext_noop("Description"),
5375  oid,
5376  gettext_noop("Get next token"),
5377  oid,
5378  gettext_noop("End parse"),
5379  oid,
5380  gettext_noop("Get headline"),
5381  oid,
5382  gettext_noop("Get token types"),
5383  oid);
5384 
5385  res = PSQLexec(buf.data);
5386  termPQExpBuffer(&buf);
5387  if (!res)
5388  return false;
5389 
5390  initPQExpBuffer(&title);
5391  if (nspname)
5392  printfPQExpBuffer(&title, _("Text search parser \"%s.%s\""),
5393  nspname, prsname);
5394  else
5395  printfPQExpBuffer(&title, _("Text search parser \"%s\""), prsname);
5396  myopt.title = title.data;
5397  myopt.footers = NULL;
5398  myopt.topt.default_footer = false;
5399  myopt.translate_header = true;
5400  myopt.translate_columns = translate_columns;
5401  myopt.n_translate_columns = lengthof(translate_columns);
5402 
5403  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5404 
5405  PQclear(res);
5406 
5407  initPQExpBuffer(&buf);
5408 
5410  "SELECT t.alias as \"%s\",\n"
5411  " t.description as \"%s\"\n"
5412  "FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t\n"
5413  "ORDER BY 1;",
5414  gettext_noop("Token name"),
5415  gettext_noop("Description"),
5416  oid);
5417 
5418  res = PSQLexec(buf.data);
5419  termPQExpBuffer(&buf);
5420  if (!res)
5421  {
5422  termPQExpBuffer(&title);
5423  return false;
5424  }
5425 
5426  if (nspname)
5427  printfPQExpBuffer(&title, _("Token types for parser \"%s.%s\""),
5428  nspname, prsname);
5429  else
5430  printfPQExpBuffer(&title, _("Token types for parser \"%s\""), prsname);
5431  myopt.title = title.data;
5432  myopt.footers = NULL;
5433  myopt.topt.default_footer = true;
5434  myopt.translate_header = true;
5435  myopt.translate_columns = NULL;
5436  myopt.n_translate_columns = 0;
5437 
5438  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5439 
5440  termPQExpBuffer(&title);
5441  PQclear(res);
5442  return true;
5443 }

References _, buf, PQExpBufferData::data, printTableOpt::default_footer, printQueryOpt::footers, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, _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 769 of file describe.c.

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

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, i, initPQExpBuffer(), _psqlSettings::logfile, map_typename_pattern(), _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 6400 of file describe.c.

6401 {
6403  int i;
6404  PGresult *res;
6405  bool has_pubtruncate;
6406  bool has_pubgencols;
6407  bool has_pubviaroot;
6408 
6409  PQExpBufferData title;
6410  printTableContent cont;
6411 
6412  if (pset.sversion < 100000)
6413  {
6414  char sverbuf[32];
6415 
6416  pg_log_error("The server (version %s) does not support publications.",
6418  sverbuf, sizeof(sverbuf)));
6419  return true;
6420  }
6421 
6422  has_pubtruncate = (pset.sversion >= 110000);
6423  has_pubgencols = (pset.sversion >= 180000);
6424  has_pubviaroot = (pset.sversion >= 130000);
6425 
6426  initPQExpBuffer(&buf);
6427 
6429  "SELECT oid, pubname,\n"
6430  " pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
6431  " puballtables, pubinsert, pubupdate, pubdelete");
6432  if (has_pubtruncate)
6434  ", pubtruncate");
6435  if (has_pubgencols)
6437  ", pubgencols");
6438  if (has_pubviaroot)
6440  ", pubviaroot");
6441 
6443  "\nFROM pg_catalog.pg_publication\n");
6444 
6445  if (!validateSQLNamePattern(&buf, pattern, false, false,
6446  NULL, "pubname", NULL,
6447  NULL,
6448  NULL, 1))
6449  {
6450  termPQExpBuffer(&buf);
6451  return false;
6452  }
6453 
6454  appendPQExpBufferStr(&buf, "ORDER BY 2;");
6455 
6456  res = PSQLexec(buf.data);
6457  if (!res)
6458  {
6459  termPQExpBuffer(&buf);
6460  return false;
6461  }
6462 
6463  if (PQntuples(res) == 0)
6464  {
6465  if (!pset.quiet)
6466  {
6467  if (pattern)
6468  pg_log_error("Did not find any publication named \"%s\".",
6469  pattern);
6470  else
6471  pg_log_error("Did not find any publications.");
6472  }
6473 
6474  termPQExpBuffer(&buf);
6475  PQclear(res);
6476  return false;
6477  }
6478 
6479  for (i = 0; i < PQntuples(res); i++)
6480  {
6481  const char align = 'l';
6482  int ncols = 5;
6483  int nrows = 1;
6484  char *pubid = PQgetvalue(res, i, 0);
6485  char *pubname = PQgetvalue(res, i, 1);
6486  bool puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
6487  printTableOpt myopt = pset.popt.topt;
6488 
6489  if (has_pubtruncate)
6490  ncols++;
6491  if (has_pubgencols)
6492  ncols++;
6493  if (has_pubviaroot)
6494  ncols++;
6495 
6496  initPQExpBuffer(&title);
6497  printfPQExpBuffer(&title, _("Publication %s"), pubname);
6498  printTableInit(&cont, &myopt, title.data, ncols, nrows);
6499 
6500  printTableAddHeader(&cont, gettext_noop("Owner"), true, align);
6501  printTableAddHeader(&cont, gettext_noop("All tables"), true, align);
6502  printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
6503  printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
6504  printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
6505  if (has_pubtruncate)
6506  printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
6507  if (has_pubgencols)
6508  printTableAddHeader(&cont, gettext_noop("Generated columns"), true, align);
6509  if (has_pubviaroot)
6510  printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
6511 
6512  printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
6513  printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
6514  printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
6515  printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
6516  printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
6517  if (has_pubtruncate)
6518  printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
6519  if (has_pubgencols)
6520  printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
6521  if (has_pubviaroot)
6522  printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
6523 
6524  if (!puballtables)
6525  {
6526  /* Get the tables for the specified publication */
6528  "SELECT n.nspname, c.relname");
6529  if (pset.sversion >= 150000)
6530  {
6532  ", pg_get_expr(pr.prqual, c.oid)");
6534  ", (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
6535  " pg_catalog.array_to_string("
6536  " ARRAY(SELECT attname\n"
6537  " FROM\n"
6538  " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
6539  " pg_catalog.pg_attribute\n"
6540  " WHERE attrelid = c.oid AND attnum = prattrs[s]), ', ')\n"
6541  " ELSE NULL END)");
6542  }
6543  else
6545  ", NULL, NULL");
6547  "\nFROM pg_catalog.pg_class c,\n"
6548  " pg_catalog.pg_namespace n,\n"
6549  " pg_catalog.pg_publication_rel pr\n"
6550  "WHERE c.relnamespace = n.oid\n"
6551  " AND c.oid = pr.prrelid\n"
6552  " AND pr.prpubid = '%s'\n"
6553  "ORDER BY 1,2", pubid);
6554  if (!addFooterToPublicationDesc(&buf, _("Tables:"), false, &cont))
6555  goto error_return;
6556 
6557  if (pset.sversion >= 150000)
6558  {
6559  /* Get the schemas for the specified publication */
6561  "SELECT n.nspname\n"
6562  "FROM pg_catalog.pg_namespace n\n"
6563  " JOIN pg_catalog.pg_publication_namespace pn ON n.oid = pn.pnnspid\n"
6564  "WHERE pn.pnpubid = '%s'\n"
6565  "ORDER BY 1", pubid);
6566  if (!addFooterToPublicationDesc(&buf, _("Tables from schemas:"),
6567  true, &cont))
6568  goto error_return;
6569  }
6570  }
6571 
6572  printTable(&cont, pset.queryFout, false, pset.logfile);
6573  printTableCleanup(&cont);
6574 
6575  termPQExpBuffer(&title);
6576  }
6577 
6578  termPQExpBuffer(&buf);
6579  PQclear(res);
6580 
6581  return true;
6582 
6583 error_return:
6584  printTableCleanup(&cont);
6585  PQclear(res);
6586  termPQExpBuffer(&buf);
6587  termPQExpBuffer(&title);
6588  return false;
6589 }
static bool addFooterToPublicationDesc(PQExpBuffer buf, const char *footermsg, bool as_schema, printTableContent *const cont)
Definition: describe.c:6354

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().

◆ describeRoleGrants()

bool describeRoleGrants ( const char *  pattern,
bool  showSystem 
)

Definition at line 3877 of file describe.c.

3878 {
3880  PGresult *res;
3881  printQueryOpt myopt = pset.popt;
3882 
3883  initPQExpBuffer(&buf);
3885  "SELECT m.rolname AS \"%s\", r.rolname AS \"%s\",\n"
3886  " pg_catalog.concat_ws(', ',\n",
3887  gettext_noop("Role name"),
3888  gettext_noop("Member of"));
3889 
3890  if (pset.sversion >= 160000)
3892  " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
3893  " CASE WHEN pam.inherit_option THEN 'INHERIT' END,\n"
3894  " CASE WHEN pam.set_option THEN 'SET' END\n");
3895  else
3897  " CASE WHEN pam.admin_option THEN 'ADMIN' END,\n"
3898  " CASE WHEN m.rolinherit THEN 'INHERIT' END,\n"
3899  " 'SET'\n");
3900 
3902  " ) AS \"%s\",\n"
3903  " g.rolname AS \"%s\"\n",
3904  gettext_noop("Options"),
3905  gettext_noop("Grantor"));
3906 
3908  "FROM pg_catalog.pg_roles m\n"
3909  " JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid)\n"
3910  " LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid)\n"
3911  " LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid)\n");
3912 
3913  if (!showSystem && !pattern)
3914  appendPQExpBufferStr(&buf, "WHERE m.rolname !~ '^pg_'\n");
3915 
3916  if (!validateSQLNamePattern(&buf, pattern, false, false,
3917  NULL, "m.rolname", NULL, NULL,
3918  NULL, 1))
3919  {
3920  termPQExpBuffer(&buf);
3921  return false;
3922  }
3923 
3924  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 4;\n");
3925 
3926  res = PSQLexec(buf.data);
3927  termPQExpBuffer(&buf);
3928  if (!res)
3929  return false;
3930 
3931  myopt.title = _("List of role grants");
3932  myopt.translate_header = true;
3933 
3934  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3935 
3936  PQclear(res);
3937  return true;
3938 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, _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().

◆ describeRoles()

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

Definition at line 3661 of file describe.c.

3662 {
3664  PGresult *res;
3665  printTableContent cont;
3666  printTableOpt myopt = pset.popt.topt;
3667  int ncols = 2;
3668  int nrows = 0;
3669  int i;
3670  int conns;
3671  const char align = 'l';
3672  char **attr;
3673 
3674  myopt.default_footer = false;
3675 
3676  initPQExpBuffer(&buf);
3677 
3679  "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
3680  " r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
3681  " r.rolconnlimit, r.rolvaliduntil");
3682 
3683  if (verbose)
3684  {
3685  appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
3686  ncols++;
3687  }
3688  appendPQExpBufferStr(&buf, "\n, r.rolreplication");
3689 
3690  if (pset.sversion >= 90500)
3691  {
3692  appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
3693  }
3694 
3695  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
3696 
3697  if (!showSystem && !pattern)
3698  appendPQExpBufferStr(&buf, "WHERE r.rolname !~ '^pg_'\n");
3699 
3700  if (!validateSQLNamePattern(&buf, pattern, false, false,
3701  NULL, "r.rolname", NULL, NULL,
3702  NULL, 1))
3703  {
3704  termPQExpBuffer(&buf);
3705  return false;
3706  }
3707 
3708  appendPQExpBufferStr(&buf, "ORDER BY 1;");
3709 
3710  res = PSQLexec(buf.data);
3711  if (!res)
3712  return false;
3713 
3714  nrows = PQntuples(res);
3715  attr = pg_malloc0((nrows + 1) * sizeof(*attr));
3716 
3717  printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
3718 
3719  printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
3720  printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
3721 
3722  if (verbose)
3723  printTableAddHeader(&cont, gettext_noop("Description"), true, align);
3724 
3725  for (i = 0; i < nrows; i++)
3726  {
3727  printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
3728 
3730  if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
3731  add_role_attribute(&buf, _("Superuser"));
3732 
3733  if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
3734  add_role_attribute(&buf, _("No inheritance"));
3735 
3736  if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
3737  add_role_attribute(&buf, _("Create role"));
3738 
3739  if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
3740  add_role_attribute(&buf, _("Create DB"));
3741 
3742  if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
3743  add_role_attribute(&buf, _("Cannot login"));
3744 
3745  if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
3746  add_role_attribute(&buf, _("Replication"));
3747 
3748  if (pset.sversion >= 90500)
3749  if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
3750  add_role_attribute(&buf, _("Bypass RLS"));
3751 
3752  conns = atoi(PQgetvalue(res, i, 6));
3753  if (conns >= 0)
3754  {
3755  if (buf.len > 0)
3756  appendPQExpBufferChar(&buf, '\n');
3757 
3758  if (conns == 0)
3759  appendPQExpBufferStr(&buf, _("No connections"));
3760  else
3761  appendPQExpBuffer(&buf, ngettext("%d connection",
3762  "%d connections",
3763  conns),
3764  conns);
3765  }
3766 
3767  if (strcmp(PQgetvalue(res, i, 7), "") != 0)
3768  {
3769  if (buf.len > 0)
3770  appendPQExpBufferChar(&buf, '\n');
3771  appendPQExpBufferStr(&buf, _("Password valid until "));
3773  }
3774 
3775  attr[i] = pg_strdup(buf.data);
3776 
3777  printTableAddCell(&cont, attr[i], false, false);
3778 
3779  if (verbose)
3780  printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
3781  }
3782  termPQExpBuffer(&buf);
3783 
3784  printTable(&cont, pset.queryFout, false, pset.logfile);
3785  printTableCleanup(&cont);
3786 
3787  for (i = 0; i < nrows; i++)
3788  free(attr[i]);
3789  free(attr);
3790 
3791  PQclear(res);
3792  return true;
3793 }
#define ngettext(s, p, n)
Definition: c.h:1184
static void add_role_attribute(PQExpBuffer buf, const char *const str)
Definition: describe.c:3796
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 6598 of file describe.c.

6599 {
6601  PGresult *res;
6602  printQueryOpt myopt = pset.popt;
6603  static const bool translate_columns[] = {false, false, false, false,
6604  false, false, false, false, false, false, false, false, false, false,
6605  false};
6606 
6607  if (pset.sversion < 100000)
6608  {
6609  char sverbuf[32];
6610 
6611  pg_log_error("The server (version %s) does not support subscriptions.",
6613  sverbuf, sizeof(sverbuf)));
6614  return true;
6615  }
6616 
6617  initPQExpBuffer(&buf);
6618 
6620  "SELECT subname AS \"%s\"\n"
6621  ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n"
6622  ", subenabled AS \"%s\"\n"
6623  ", subpublications AS \"%s\"\n",
6624  gettext_noop("Name"),
6625  gettext_noop("Owner"),
6626  gettext_noop("Enabled"),
6627  gettext_noop("Publication"));
6628 
6629  if (verbose)
6630  {
6631  /* Binary mode and streaming are only supported in v14 and higher */
6632  if (pset.sversion >= 140000)
6633  {
6635  ", subbinary AS \"%s\"\n",
6636  gettext_noop("Binary"));
6637 
6638  if (pset.sversion >= 160000)
6640  ", (CASE substream\n"
6641  " WHEN 'f' THEN 'off'\n"
6642  " WHEN 't' THEN 'on'\n"
6643  " WHEN 'p' THEN 'parallel'\n"
6644  " END) AS \"%s\"\n",
6645  gettext_noop("Streaming"));
6646  else
6648  ", substream AS \"%s\"\n",
6649  gettext_noop("Streaming"));
6650  }
6651 
6652  /* Two_phase and disable_on_error are only supported in v15 and higher */
6653  if (pset.sversion >= 150000)
6655  ", subtwophasestate AS \"%s\"\n"
6656  ", subdisableonerr AS \"%s\"\n",
6657  gettext_noop("Two-phase commit"),
6658  gettext_noop("Disable on error"));
6659 
6660  if (pset.sversion >= 160000)
6662  ", suborigin AS \"%s\"\n"
6663  ", subpasswordrequired AS \"%s\"\n"
6664  ", subrunasowner AS \"%s\"\n",
6665  gettext_noop("Origin"),
6666  gettext_noop("Password required"),
6667  gettext_noop("Run as owner?"));
6668 
6669  if (pset.sversion >= 170000)
6671  ", subfailover AS \"%s\"\n",
6672  gettext_noop("Failover"));
6673 
6675  ", subsynccommit AS \"%s\"\n"
6676  ", subconninfo AS \"%s\"\n",
6677  gettext_noop("Synchronous commit"),
6678  gettext_noop("Conninfo"));
6679 
6680  /* Skip LSN is only supported in v15 and higher */
6681  if (pset.sversion >= 150000)
6683  ", subskiplsn AS \"%s\"\n",
6684  gettext_noop("Skip LSN"));
6685  }
6686 
6687  /* Only display subscriptions in current database. */
6689  "FROM pg_catalog.pg_subscription\n"
6690  "WHERE subdbid = (SELECT oid\n"
6691  " FROM pg_catalog.pg_database\n"
6692  " WHERE datname = pg_catalog.current_database())");
6693 
6694  if (!validateSQLNamePattern(&buf, pattern, true, false,
6695  NULL, "subname", NULL,
6696  NULL,
6697  NULL, 1))
6698  {
6699  termPQExpBuffer(&buf);
6700  return false;
6701  }
6702 
6703  appendPQExpBufferStr(&buf, "ORDER BY 1;");
6704 
6705  res = PSQLexec(buf.data);
6706  termPQExpBuffer(&buf);
6707  if (!res)
6708  return false;
6709 
6710  myopt.title = _("List of subscriptions");
6711  myopt.translate_header = true;
6712  myopt.translate_columns = translate_columns;
6713  myopt.n_translate_columns = lengthof(translate_columns);
6714 
6715  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
6716 
6717  PQclear(res);
6718  return true;
6719 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, formatPGVersionNumber(), gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, 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 1444 of file describe.c.

1445 {
1447  PGresult *res;
1448  int i;
1449 
1450  initPQExpBuffer(&buf);
1451 
1453  "SELECT c.oid,\n"
1454  " n.nspname,\n"
1455  " c.relname\n"
1456  "FROM pg_catalog.pg_class c\n"
1457  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
1458 
1459  if (!showSystem && !pattern)
1460  appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
1461  " AND n.nspname <> 'information_schema'\n");
1462 
1463  if (!validateSQLNamePattern(&buf, pattern, !showSystem && !pattern, false,
1464  "n.nspname", "c.relname", NULL,
1465  "pg_catalog.pg_table_is_visible(c.oid)",
1466  NULL, 3))
1467  {
1468  termPQExpBuffer(&buf);
1469  return false;
1470  }
1471 
1472  appendPQExpBufferStr(&buf, "ORDER BY 2, 3;");
1473 
1474  res = PSQLexec(buf.data);
1475  termPQExpBuffer(&buf);
1476  if (!res)
1477  return false;
1478 
1479  if (PQntuples(res) == 0)
1480  {
1481  if (!pset.quiet)
1482  {
1483  if (pattern)
1484  pg_log_error("Did not find any relation named \"%s\".",
1485  pattern);
1486  else
1487  pg_log_error("Did not find any relations.");
1488  }
1489  PQclear(res);
1490  return false;
1491  }
1492 
1493  for (i = 0; i < PQntuples(res); i++)
1494  {
1495  const char *oid;
1496  const char *nspname;
1497  const char *relname;
1498 
1499  oid = PQgetvalue(res, i, 0);
1500  nspname = PQgetvalue(res, i, 1);
1501  relname = PQgetvalue(res, i, 2);
1502 
1503  if (!describeOneTableDetails(nspname, relname, oid, verbose))
1504  {
1505  PQclear(res);
1506  return false;
1507  }
1508  if (cancel_pressed)
1509  {
1510  PQclear(res);
1511  return false;
1512  }
1513  }
1514 
1515  PQclear(res);
1516  return true;
1517 }
static bool describeOneTableDetails(const char *schemaname, const char *relationname, const char *oid, bool verbose)
Definition: describe.c:1527
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 214 of file describe.c.

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

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, _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 614 of file describe.c.

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

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, CppAsString2, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, map_typename_pattern(), _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 910 of file describe.c.

911 {
912  PGresult *res;
914  printQueryOpt myopt = pset.popt;
915 
917 
919  "SELECT\n"
920  " d.datname as \"%s\",\n"
921  " pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n"
922  " pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n",
923  gettext_noop("Name"),
924  gettext_noop("Owner"),
925  gettext_noop("Encoding"));
926  if (pset.sversion >= 150000)
928  " CASE d.datlocprovider WHEN 'b' THEN 'builtin' WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS \"%s\",\n",
929  gettext_noop("Locale Provider"));
930  else
932  " 'libc' AS \"%s\",\n",
933  gettext_noop("Locale Provider"));
935  " d.datcollate as \"%s\",\n"
936  " d.datctype as \"%s\",\n",
937  gettext_noop("Collate"),
938  gettext_noop("Ctype"));
939  if (pset.sversion >= 170000)
941  " d.datlocale as \"%s\",\n",
942  gettext_noop("Locale"));
943  else if (pset.sversion >= 150000)
945  " d.daticulocale as \"%s\",\n",
946  gettext_noop("Locale"));
947  else
949  " NULL as \"%s\",\n",
950  gettext_noop("Locale"));
951  if (pset.sversion >= 160000)
953  " d.daticurules as \"%s\",\n",
954  gettext_noop("ICU Rules"));
955  else
957  " NULL as \"%s\",\n",
958  gettext_noop("ICU Rules"));
959  appendPQExpBufferStr(&buf, " ");
960  printACLColumn(&buf, "d.datacl");
961  if (verbose)
963  ",\n CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n"
964  " THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n"
965  " ELSE 'No Access'\n"
966  " END as \"%s\""
967  ",\n t.spcname as \"%s\""
968  ",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"",
969  gettext_noop("Size"),
970  gettext_noop("Tablespace"),
971  gettext_noop("Description"));
973  "\nFROM pg_catalog.pg_database d\n");
974  if (verbose)
976  " JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n");
977 
978  if (pattern)
979  {
980  if (!validateSQLNamePattern(&buf, pattern, false, false,
981  NULL, "d.datname", NULL, NULL,
982  NULL, 1))
983  {
985  return false;
986  }
987  }
988 
989  appendPQExpBufferStr(&buf, "ORDER BY 1;");
990  res = PSQLexec(buf.data);
992  if (!res)
993  return false;
994 
995  myopt.title = _("List of databases");
996  myopt.translate_header = true;
997 
998  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
999 
1000  PQclear(res);
1001  return true;
1002 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, _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 4847 of file describe.c.

4848 {
4850  PGresult *res;
4851  printQueryOpt myopt = pset.popt;
4852  static const bool translate_columns[] = {false, false, false, true, false};
4853 
4854  initPQExpBuffer(&buf);
4855 
4857  "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
4858  " pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n",
4859  gettext_noop("Source type"),
4860  gettext_noop("Target type"));
4861 
4862  /*
4863  * We don't attempt to localize '(binary coercible)' or '(with inout)',
4864  * because there's too much risk of gettext translating a function name
4865  * that happens to match some string in the PO database.
4866  */
4868  " CASE WHEN c.castmethod = '%c' THEN '(binary coercible)'\n"
4869  " WHEN c.castmethod = '%c' THEN '(with inout)'\n"
4870  " ELSE p.proname\n"
4871  " END AS \"%s\",\n",
4872  COERCION_METHOD_BINARY,
4873  COERCION_METHOD_INOUT,
4874  gettext_noop("Function"));
4875 
4877  " CASE WHEN c.castcontext = '%c' THEN '%s'\n"
4878  " WHEN c.castcontext = '%c' THEN '%s'\n"
4879  " ELSE '%s'\n"
4880  " END AS \"%s\"",
4881  COERCION_CODE_EXPLICIT,
4882  gettext_noop("no"),
4883  COERCION_CODE_ASSIGNMENT,
4884  gettext_noop("in assignment"),
4885  gettext_noop("yes"),
4886  gettext_noop("Implicit?"));
4887 
4888  if (verbose)
4890  ",\n d.description AS \"%s\"",
4891  gettext_noop("Description"));
4892 
4893  /*
4894  * We need a left join to pg_proc for binary casts; the others are just
4895  * paranoia.
4896  */
4898  "\nFROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
4899  " ON c.castfunc = p.oid\n"
4900  " LEFT JOIN pg_catalog.pg_type ts\n"
4901  " ON c.castsource = ts.oid\n"
4902  " LEFT JOIN pg_catalog.pg_namespace ns\n"
4903  " ON ns.oid = ts.typnamespace\n"
4904  " LEFT JOIN pg_catalog.pg_type tt\n"
4905  " ON c.casttarget = tt.oid\n"
4906  " LEFT JOIN pg_catalog.pg_namespace nt\n"
4907  " ON nt.oid = tt.typnamespace\n");
4908 
4909  if (verbose)
4911  " LEFT JOIN pg_catalog.pg_description d\n"
4912  " ON d.classoid = c.tableoid AND d.objoid = "
4913  "c.oid AND d.objsubid = 0\n");
4914 
4915  appendPQExpBufferStr(&buf, "WHERE ( (true");
4916 
4917  /*
4918  * Match name pattern against either internal or external name of either
4919  * castsource or casttarget
4920  */
4921  if (!validateSQLNamePattern(&buf, pattern, true, false,
4922  "ns.nspname", "ts.typname",
4923  "pg_catalog.format_type(ts.oid, NULL)",
4924  "pg_catalog.pg_type_is_visible(ts.oid)",
4925  NULL, 3))
4926  goto error_return;
4927 
4928  appendPQExpBufferStr(&buf, ") OR (true");
4929 
4930  if (!validateSQLNamePattern(&buf, pattern, true, false,
4931  "nt.nspname", "tt.typname",
4932  "pg_catalog.format_type(tt.oid, NULL)",
4933  "pg_catalog.pg_type_is_visible(tt.oid)",
4934  NULL, 3))
4935  goto error_return;
4936 
4937  appendPQExpBufferStr(&buf, ") )\nORDER BY 1, 2;");
4938 
4939  res = PSQLexec(buf.data);
4940  termPQExpBuffer(&buf);
4941  if (!res)
4942  return false;
4943 
4944  myopt.title = _("List of casts");
4945  myopt.translate_header = true;
4946  myopt.translate_columns = translate_columns;
4947  myopt.n_translate_columns = lengthof(translate_columns);
4948 
4949  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4950 
4951  PQclear(res);
4952  return true;
4953 
4954 error_return:
4955  termPQExpBuffer(&buf);
4956  return false;
4957 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, _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 4965 of file describe.c.

4966 {
4968  PGresult *res;
4969  printQueryOpt myopt = pset.popt;
4970  static const bool translate_columns[] = {false, false, false, false, false, false, false, true, false};
4971 
4972  initPQExpBuffer(&buf);
4973 
4975  "SELECT\n"
4976  " n.nspname AS \"%s\",\n"
4977  " c.collname AS \"%s\",\n",
4978  gettext_noop("Schema"),
4979  gettext_noop("Name"));
4980 
4981  if (pset.sversion >= 100000)
4983  " CASE c.collprovider WHEN 'd' THEN 'default' WHEN 'b' THEN 'builtin' WHEN 'c' THEN 'libc' WHEN 'i' THEN 'icu' END AS \"%s\",\n",
4984  gettext_noop("Provider"));
4985  else
4987  " 'libc' AS \"%s\",\n",
4988  gettext_noop("Provider"));
4989 
4991  " c.collcollate AS \"%s\",\n"
4992  " c.collctype AS \"%s\",\n",
4993  gettext_noop("Collate"),
4994  gettext_noop("Ctype"));
4995 
4996  if (pset.sversion >= 170000)
4998  " c.colllocale AS \"%s\",\n",
4999  gettext_noop("Locale"));
5000  else if (pset.sversion >= 150000)
5002  " c.colliculocale AS \"%s\",\n",
5003  gettext_noop("Locale"));
5004  else
5006  " c.collcollate AS \"%s\",\n",
5007  gettext_noop("Locale"));
5008 
5009  if (pset.sversion >= 160000)
5011  " c.collicurules AS \"%s\",\n",
5012  gettext_noop("ICU Rules"));
5013  else
5015  " NULL AS \"%s\",\n",
5016  gettext_noop("ICU Rules"));
5017 
5018  if (pset.sversion >= 120000)
5020  " CASE WHEN c.collisdeterministic THEN '%s' ELSE '%s' END AS \"%s\"",
5021  gettext_noop("yes"), gettext_noop("no"),
5022  gettext_noop("Deterministic?"));
5023  else
5025  " '%s' AS \"%s\"",
5026  gettext_noop("yes"),
5027  gettext_noop("Deterministic?"));
5028 
5029  if (verbose)
5031  ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
5032  gettext_noop("Description"));
5033 
5035  "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
5036  "WHERE n.oid = c.collnamespace\n");
5037 
5038  if (!showSystem && !pattern)
5039  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
5040  " AND n.nspname <> 'information_schema'\n");
5041 
5042  /*
5043  * Hide collations that aren't usable in the current database's encoding.
5044  * If you think to change this, note that pg_collation_is_visible rejects
5045  * unusable collations, so you will need to hack name pattern processing
5046  * somehow to avoid inconsistent behavior.
5047  */
5048  appendPQExpBufferStr(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
5049 
5050  if (!validateSQLNamePattern(&buf, pattern, true, false,
5051  "n.nspname", "c.collname", NULL,
5052  "pg_catalog.pg_collation_is_visible(c.oid)",
5053  NULL, 3))
5054  {
5055  termPQExpBuffer(&buf);
5056  return false;
5057  }
5058 
5059  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
5060 
5061  res = PSQLexec(buf.data);
5062  termPQExpBuffer(&buf);
5063  if (!res)
5064  return false;
5065 
5066  myopt.title = _("List of collations");
5067  myopt.translate_header = true;
5068  myopt.translate_columns = translate_columns;
5069  myopt.n_translate_columns = lengthof(translate_columns);
5070 
5071  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
5072 
5073  PQclear(res);
5074  return true;
5075 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, _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 4523 of file describe.c.

4524 {
4526  PGresult *res;
4527  printQueryOpt myopt = pset.popt;
4528  static const bool translate_columns[] =
4529  {false, false, false, false, true, false};
4530 
4531  initPQExpBuffer(&buf);
4532 
4534  "SELECT n.nspname AS \"%s\",\n"
4535  " c.conname AS \"%s\",\n"
4536  " pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
4537  " pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
4538  " CASE WHEN c.condefault THEN '%s'\n"
4539  " ELSE '%s' END AS \"%s\"",
4540  gettext_noop("Schema"),
4541  gettext_noop("Name"),
4542  gettext_noop("Source"),
4543  gettext_noop("Destination"),
4544  gettext_noop("yes"), gettext_noop("no"),
4545  gettext_noop("Default?"));
4546 
4547  if (verbose)
4549  ",\n d.description AS \"%s\"",
4550  gettext_noop("Description"));
4551 
4553  "\nFROM pg_catalog.pg_conversion c\n"
4554  " JOIN pg_catalog.pg_namespace n "
4555  "ON n.oid = c.connamespace\n");
4556 
4557  if (verbose)
4559  "LEFT JOIN pg_catalog.pg_description d "
4560  "ON d.classoid = c.tableoid\n"
4561  " AND d.objoid = c.oid "
4562  "AND d.objsubid = 0\n");
4563 
4564  appendPQExpBufferStr(&buf, "WHERE true\n");
4565 
4566  if (!showSystem && !pattern)
4567  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4568  " AND n.nspname <> 'information_schema'\n");
4569 
4570  if (!validateSQLNamePattern(&buf, pattern, true, false,
4571  "n.nspname", "c.conname", NULL,
4572  "pg_catalog.pg_conversion_is_visible(c.oid)",
4573  NULL, 3))
4574  {
4575  termPQExpBuffer(&buf);
4576  return false;
4577  }
4578 
4579  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4580 
4581  res = PSQLexec(buf.data);
4582  termPQExpBuffer(&buf);
4583  if (!res)
4584  return false;
4585 
4586  myopt.title = _("List of conversions");
4587  myopt.translate_header = true;
4588  myopt.translate_columns = translate_columns;
4589  myopt.n_translate_columns = lengthof(translate_columns);
4590 
4591  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4592 
4593  PQclear(res);
4594  return true;
4595 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, _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 3808 of file describe.c.

3809 {
3811  PGresult *res;
3812  printQueryOpt myopt = pset.popt;
3813  bool havewhere;
3814 
3815  initPQExpBuffer(&buf);
3816 
3817  printfPQExpBuffer(&buf, "SELECT rolname AS \"%s\", datname AS \"%s\",\n"
3818  "pg_catalog.array_to_string(setconfig, E'\\n') AS \"%s\"\n"
3819  "FROM pg_catalog.pg_db_role_setting s\n"
3820  "LEFT JOIN pg_catalog.pg_database d ON d.oid = setdatabase\n"
3821  "LEFT JOIN pg_catalog.pg_roles r ON r.oid = setrole\n",
3822  gettext_noop("Role"),
3823  gettext_noop("Database"),
3824  gettext_noop("Settings"));
3825  if (!validateSQLNamePattern(&buf, pattern, false, false,
3826  NULL, "r.rolname", NULL, NULL, &havewhere, 1))
3827  goto error_return;
3828  if (!validateSQLNamePattern(&buf, pattern2, havewhere, false,
3829  NULL, "d.datname", NULL, NULL,
3830  NULL, 1))
3831  goto error_return;
3832  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
3833 
3834  res = PSQLexec(buf.data);
3835  termPQExpBuffer(&buf);
3836  if (!res)
3837  return false;
3838 
3839  /*
3840  * Most functions in this file are content to print an empty table when
3841  * there are no matching objects. We intentionally deviate from that
3842  * here, but only in !quiet mode, because of the possibility that the user
3843  * is confused about what the two pattern arguments mean.
3844  */
3845  if (PQntuples(res) == 0 && !pset.quiet)
3846  {
3847  if (pattern && pattern2)
3848  pg_log_error("Did not find any settings for role \"%s\" and database \"%s\".",
3849  pattern, pattern2);
3850  else if (pattern)
3851  pg_log_error("Did not find any settings for role \"%s\".",
3852  pattern);
3853  else
3854  pg_log_error("Did not find any settings.");
3855  }
3856  else
3857  {
3858  myopt.title = _("List of settings");
3859  myopt.translate_header = true;
3860 
3861  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
3862  }
3863 
3864  PQclear(res);
3865  return true;
3866 
3867 error_return:
3868  termPQExpBuffer(&buf);
3869  return false;
3870 }

References _, appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, 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 1174 of file describe.c.

1175 {
1177  PGresult *res;
1178  printQueryOpt myopt = pset.popt;
1179  static const bool translate_columns[] = {false, false, true, false};
1180 
1181  initPQExpBuffer(&buf);
1182 
1184  "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
1185  " n.nspname AS \"%s\",\n"
1186  " 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"
1187  " ",
1188  gettext_noop("Owner"),
1189  gettext_noop("Schema"),
1190  DEFACLOBJ_RELATION,
1191  gettext_noop("table"),
1192  DEFACLOBJ_SEQUENCE,
1193  gettext_noop("sequence"),
1194  DEFACLOBJ_FUNCTION,
1195  gettext_noop("function"),
1196  DEFACLOBJ_TYPE,
1197  gettext_noop("type"),
1198  DEFACLOBJ_NAMESPACE,
1199  gettext_noop("schema"),
1200  gettext_noop("Type"));
1201 
1202  printACLColumn(&buf, "d.defaclacl");
1203 
1204  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
1205  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
1206 
1207  if (!validateSQLNamePattern(&buf, pattern, false, false,
1208  NULL,
1209  "n.nspname",
1210  "pg_catalog.pg_get_userbyid(d.defaclrole)",
1211  NULL,
1212  NULL, 3))
1213  goto error_return;
1214 
1215  appendPQExpBufferStr(&buf, "ORDER BY 1, 2, 3;");
1216 
1217  res = PSQLexec(buf.data);
1218  if (!res)
1219  goto error_return;
1220 
1221  printfPQExpBuffer(&buf, _("Default access privileges"));
1222  myopt.title = buf.data;
1223  myopt.translate_header = true;
1224  myopt.translate_columns = translate_columns;
1225  myopt.n_translate_columns = lengthof(translate_columns);
1226 
1227  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
1228 
1229  termPQExpBuffer(&buf);
1230  PQclear(res);
1231  return true;
1232 
1233 error_return:
1234  termPQExpBuffer(&buf);
1235  return false;
1236 }

References _, appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), lengthof, _psqlSettings::logfile, printQueryOpt::n_translate_columns, _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 4440 of file describe.c.

4441 {
4443  PGresult *res;
4444  printQueryOpt myopt = pset.popt;
4445 
4446  initPQExpBuffer(&buf);
4447 
4449  "SELECT n.nspname as \"%s\",\n"
4450  " t.typname as \"%s\",\n"
4451  " pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
4452  " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
4453  " WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4454  " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4455  " t.typdefault as \"%s\",\n"
4456  " pg_catalog.array_to_string(ARRAY(\n"
4457  " SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid AND r.contype = 'c' ORDER BY r.conname\n"
4458  " ), ' ') as \"%s\"",
4459  gettext_noop("Schema"),
4460  gettext_noop("Name"),
4461  gettext_noop("Type"),
4462  gettext_noop("Collation"),
4463  gettext_noop("Nullable"),
4464  gettext_noop("Default"),
4465  gettext_noop("Check"));
4466 
4467  if (verbose)
4468  {
4469  appendPQExpBufferStr(&buf, ",\n ");
4470  printACLColumn(&buf, "t.typacl");
4472  ",\n d.description as \"%s\"",
4473  gettext_noop("Description"));
4474  }
4475 
4477  "\nFROM pg_catalog.pg_type t\n"
4478  " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
4479 
4480  if (verbose)
4482  " LEFT JOIN pg_catalog.pg_description d "
4483  "ON d.classoid = t.tableoid AND d.objoid = t.oid "
4484  "AND d.objsubid = 0\n");
4485 
4486  appendPQExpBufferStr(&buf, "WHERE t.typtype = 'd'\n");
4487 
4488  if (!showSystem && !pattern)
4489  appendPQExpBufferStr(&buf, " AND n.nspname <> 'pg_catalog'\n"
4490  " AND n.nspname <> 'information_schema'\n");
4491 
4492  if (!validateSQLNamePattern(&buf, pattern, true, false,
4493  "n.nspname", "t.typname", NULL,
4494  "pg_catalog.pg_type_is_visible(t.oid)",
4495  NULL, 3))
4496  {
4497  termPQExpBuffer(&buf);
4498  return false;
4499  }
4500 
4501  appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
4502 
4503  res = PSQLexec(buf.data);
4504  termPQExpBuffer(&buf);
4505  if (!res)
4506  return false;
4507 
4508  myopt.title = _("List of domains");
4509  myopt.translate_header = true;
4510 
4511  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4512 
4513  PQclear(res);
4514  return true;
4515 }

References _, appendPQExpBuffer(), appendPQExpBufferStr(), buf, gettext_noop, initPQExpBuffer(), _psqlSettings::logfile, _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 4671 of file describe.c.

4672 {
4674  PGresult *res;
4675  printQueryOpt myopt = pset.popt;
4676  static const bool translate_columns[] =
4677  {false, false, false, true, false, false, false};
4678 
4679  if (pset.sversion < 90300)
4680  {
4681  char sverbuf[32];
4682 
4683  pg_log_error("The server (version %s) does not support event triggers.",
4685  sverbuf, sizeof(sverbuf)));
4686  return true;
4687  }
4688 
4689  initPQExpBuffer(&buf);
4690 
4692  "SELECT evtname as \"%s\", "
4693  "evtevent as \"%s\", "
4694  "pg_catalog.pg_get_userbyid(e.evtowner) as \"%s\",\n"
4695  " case evtenabled when 'O' then '%s'"
4696  " when 'R' then '%s'"
4697  " when 'A' then '%s'"
4698  " when 'D' then '%s' end as \"%s\",\n"
4699  " e.evtfoid::pg_catalog.regproc as \"%s\", "
4700  "pg_catalog.array_to_string(array(select x"
4701  " from pg_catalog.unnest(evttags) as t(x)), ', ') as \"%s\"",
4702  gettext_noop("Name"),
4703  gettext_noop("Event"),
4704  gettext_noop("Owner"),
4705  gettext_noop("enabled"),
4706  gettext_noop("replica"),
4707  gettext_noop("always"),
4708  gettext_noop("disabled"),
4709  gettext_noop("Enabled"),
4710  gettext_noop("Function"),
4711  gettext_noop("Tags"));
4712  if (verbose)
4714  ",\npg_catalog.obj_description(e.oid, 'pg_event_trigger') as \"%s\"",
4715  gettext_noop("Description"));
4717  "\nFROM pg_catalog.pg_event_trigger e ");
4718 
4719  if (!validateSQLNamePattern(&buf, pattern, false, false,
4720  NULL, "evtname", NULL, NULL,
4721  NULL, 1))
4722  {
4723  termPQExpBuffer(&buf);
4724  return false;
4725  }