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

Go to the source code of this file.

Functions

static const char * map_typename_pattern (const char *pattern)
 
static bool describeOneTableDetails (const char *schemaname, const char *relationname, const char *oid, bool verbose)
 
static void add_tablespace_footer (printTableContent *const cont, char relkind, Oid tablespace, const bool newline)
 
static void add_role_attribute (PQExpBuffer buf, const char *const str)
 
static bool listTSParsersVerbose (const char *pattern)
 
static bool describeOneTSParser (const char *oid, const char *nspname, const char *prsname)
 
static bool listTSConfigsVerbose (const char *pattern)
 
static bool describeOneTSConfig (const char *oid, const char *nspname, const char *cfgname, const char *pnspname, const char *prsname)
 
static void printACLColumn (PQExpBuffer buf, const char *colname)
 
static bool listOneExtensionContents (const char *extname, const char *oid)
 
static bool validateSQLNamePattern (PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule, bool *added_clause, int maxparts)
 
bool describeAggregates (const char *pattern, bool verbose, bool showSystem)
 
bool describeAccessMethods (const char *pattern, bool verbose)
 
bool describeTablespaces (const char *pattern, bool verbose)
 
bool describeFunctions (const char *functypes, const char *func_pattern, char **arg_patterns, int num_arg_patterns, bool verbose, bool showSystem)
 
bool describeTypes (const char *pattern, bool verbose, bool showSystem)
 
bool describeOperators (const char *oper_pattern, char **arg_patterns, int num_arg_patterns, bool verbose, bool showSystem)
 
bool listAllDbs (const char *pattern, bool verbose)
 
bool permissionsList (const char *pattern, bool 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 3801 of file describe.c.

3802 {
3803  if (buf->len > 0)
3804  appendPQExpBufferStr(buf, ", ");
3805 
3807 }
const char * str
static char * buf
Definition: pg_test_fsync.c:73
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 3601 of file describe.c.

3603 {
3604  /* relkinds for which we support tablespaces */
3605  if (relkind == RELKIND_RELATION ||
3606  relkind == RELKIND_MATVIEW ||
3607  relkind == RELKIND_INDEX ||
3608  relkind == RELKIND_PARTITIONED_TABLE ||
3609  relkind == RELKIND_PARTITIONED_INDEX ||
3610  relkind == RELKIND_TOASTVALUE)
3611  {
3612  /*
3613  * We ignore the database default tablespace so that users not using
3614  * tablespaces don't need to know about them.
3615  */
3616  if (tablespace != 0)
3617  {
3618  PGresult *result = NULL;
3620 
3621  initPQExpBuffer(&buf);
3623  "SELECT spcname FROM pg_catalog.pg_tablespace\n"
3624  "WHERE oid = '%u';", tablespace);
3625  result = PSQLexec(buf.data);
3626  if (!result)
3627  {
3628  termPQExpBuffer(&buf);
3629  return;
3630  }
3631  /* Should always be the case, but.... */
3632  if (PQntuples(result) > 0)
3633  {
3634  if (newline)
3635  {
3636  /* Add the tablespace as a new footer */
3637  printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
3638  PQgetvalue(result, 0, 0));
3639  printTableAddFooter(cont, buf.data);
3640  }
3641  else
3642  {
3643  /* Append the tablespace to the latest footer */
3644  printfPQExpBuffer(&buf, "%s", cont->footer->data);
3645 
3646  /*-------
3647  translator: before this string there's an index description like
3648  '"foo_pkey" PRIMARY KEY, btree (a)' */
3649  appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
3650  PQgetvalue(result, 0, 0));
3651  printTableSetFooter(cont, buf.data);
3652  }
3653  }
3654  PQclear(result);
3655  termPQExpBuffer(&buf);
3656  }
3657  }
3658 }
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
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 6345 of file describe.c.

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

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

Referenced by describePublications().

◆ describeAccessMethods()

bool describeAccessMethods ( const char *  pattern,
bool  verbose 
)

Definition at line 141 of file describe.c.

142 {
144  PGresult *res;
145  printQueryOpt myopt = pset.popt;
146  static const bool translate_columns[] = {false, true, false, false};
147 
148  if (pset.sversion < 90600)
149  {
150  char sverbuf[32];
151 
152  pg_log_error("The server (version %s) does not support access methods.",
154  sverbuf, sizeof(sverbuf)));
155  return true;
156  }
157 
159 
161  "SELECT amname AS \"%s\",\n"
162  " CASE amtype"
163  " WHEN 'i' THEN '%s'"
164  " WHEN 't' THEN '%s'"
165  " END AS \"%s\"",
166  gettext_noop("Name"),
167  gettext_noop("Index"),
168  gettext_noop("Table"),
169  gettext_noop("Type"));
170 
171  if (verbose)
172  {
174  ",\n amhandler AS \"%s\",\n"
175  " pg_catalog.obj_description(oid, 'pg_am') AS \"%s\"",
176  gettext_noop("Handler"),
177  gettext_noop("Description"));
178  }
179 
181  "\nFROM pg_catalog.pg_am\n");
182 
183  if (!validateSQLNamePattern(&buf, pattern, false, false,
184  NULL, "amname", NULL,
185  NULL,
186  NULL, 1))
187  {
189  return false;
190  }
191 
192  appendPQExpBufferStr(&buf, "ORDER BY 1;");
193 
194  res = PSQLexec(buf.data);
196  if (!res)
197  return false;
198 
199  myopt.title = _("List of access methods");
200  myopt.translate_header = true;
201  myopt.translate_columns = translate_columns;
202  myopt.n_translate_columns = lengthof(translate_columns);
203 
204  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
205 
206  PQclear(res);
207  return true;
208 }
#define gettext_noop(x)
Definition: c.h:1196
#define lengthof(array)
Definition: c.h:788
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:6216
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:91
FILE * logfile
Definition: settings.h:120
FILE * queryFout
Definition: settings.h:84
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 71 of file describe.c.

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

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

4600 {
4602  PGresult *res;
4603  printQueryOpt myopt = pset.popt;
4604 
4605  initPQExpBuffer(&buf);
4607  "SELECT s.name AS \"%s\", "
4608  "pg_catalog.current_setting(s.name) AS \"%s\"",
4609  gettext_noop("Parameter"),
4610  gettext_noop("Value"));
4611 
4612  if (verbose)
4613  {
4615  ", s.vartype AS \"%s\", s.context AS \"%s\", ",
4616  gettext_noop("Type"),
4617  gettext_noop("Context"));
4618  if (pset.sversion >= 150000)
4619  printACLColumn(&buf, "p.paracl");
4620  else
4621  appendPQExpBuffer(&buf, "NULL AS \"%s\"",
4622  gettext_noop("Access privileges"));
4623  }
4624 
4625  appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_settings s\n");
4626 
4627  if (verbose && pset.sversion >= 150000)
4629  " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
4630  " ON pg_catalog.lower(s.name) = p.parname\n");
4631 
4632  if (pattern)
4633  processSQLNamePattern(pset.db, &buf, pattern,
4634  false, false,
4635  NULL, "pg_catalog.lower(s.name)", NULL,
4636  NULL, NULL, NULL);
4637  else
4638  appendPQExpBufferStr(&buf, "WHERE s.source <> 'default' AND\n"
4639  " s.setting IS DISTINCT FROM s.boot_val\n");
4640 
4641  appendPQExpBufferStr(&buf, "ORDER BY 1;");
4642 
4643  res = PSQLexec(buf.data);
4644  termPQExpBuffer(&buf);
4645  if (!res)
4646  return false;
4647 
4648  if (pattern)
4649  myopt.title = _("List of configuration parameters");
4650  else
4651  myopt.title = _("List of non-default configuration parameters");
4652  myopt.translate_header = true;
4653 
4654  printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
4655 
4656  PQclear(res);
4657  return true;
4658 }
static void printACLColumn(PQExpBuffer buf, const char *colname)
Definition: describe.c:6711
bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern, bool have_where, bool force_escape, const char *schemavar, const char *namevar, const char *altnamevar, const char *visibilityrule, PQExpBuffer dbnamebuf, int *dotcnt)
Definition: string_utils.c:891
PGconn * db
Definition: settings.h:82

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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