postgresql/src/bin/psql/describe.c

4174 lines
115 KiB
C
Raw Normal View History

2000-01-19 00:30:24 +01:00
/*
* psql - the PostgreSQL interactive terminal
*
* Support for the various \d ("describe") commands. Note that the current
* expectation is that all functions in this file will succeed when working
* with servers of versions 7.4 and up. It's okay to omit irrelevant
* information for an old server, but not to fail outright.
*
2011-01-01 19:18:15 +01:00
* Copyright (c) 2000-2011, PostgreSQL Global Development Group
2000-01-19 00:30:24 +01:00
*
2010-09-20 22:08:53 +02:00
* src/bin/psql/describe.c
2000-01-19 00:30:24 +01:00
*/
#include "postgres_fe.h"
#include <ctype.h>
#include "common.h"
#include "describe.h"
#include "dumputils.h"
#include "mbprint.h"
#include "print.h"
#include "settings.h"
#include "variables.h"
static bool describeOneTableDetails(const char *schemaname,
2002-09-04 22:31:48 +02:00
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,
2007-11-15 22:14:46 +01:00
const char *prsname);
static bool listTSConfigsVerbose(const char *pattern);
static bool describeOneTSConfig(const char *oid, const char *nspname,
2007-11-15 22:14:46 +01:00
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);
/*----------------
* Handlers for various slash commands displaying some sort of list
* of things in the database.
*
* Note: try to format the queries to look nice in -E output.
*----------------
*/
/* \da
* Takes an optional regexp to select particular aggregates
*/
bool
describeAggregates(const char *pattern, bool verbose, bool showSystem)
{
PQExpBufferData buf;
1999-11-05 00:14:30 +01:00
PGresult *res;
printQueryOpt myopt = pset.popt;
1999-11-05 00:14:30 +01:00
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT n.nspname as \"%s\",\n"
" p.proname AS \"%s\",\n"
" pg_catalog.format_type(p.prorettype, NULL) AS \"%s\",\n",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Result data type"));
if (pset.sversion >= 80200)
appendPQExpBuffer(&buf,
" CASE WHEN p.pronargs = 0\n"
" THEN CAST('*' AS pg_catalog.text)\n"
" ELSE\n"
" pg_catalog.array_to_string(ARRAY(\n"
" SELECT\n"
" pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
" FROM\n"
" pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
" ), ', ')\n"
" END AS \"%s\",\n",
gettext_noop("Argument data types"));
else
appendPQExpBuffer(&buf,
" pg_catalog.format_type(p.proargtypes[0], NULL) AS \"%s\",\n",
gettext_noop("Argument data types"));
appendPQExpBuffer(&buf,
2007-11-15 22:14:46 +01:00
" pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
2002-09-04 22:31:48 +02:00
"FROM pg_catalog.pg_proc p\n"
2007-11-15 22:14:46 +01:00
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
2002-09-04 22:31:48 +02:00
"WHERE p.proisagg\n",
gettext_noop("Description"));
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "p.proname", NULL,
"pg_catalog.pg_function_is_visible(p.oid)");
1999-11-05 00:14:30 +01:00
appendPQExpBuffer(&buf, "ORDER BY 1, 2, 4;");
1999-11-05 00:14:30 +01:00
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of aggregate functions");
myopt.translate_header = true;
1999-11-05 00:14:30 +01:00
printQuery(res, &myopt, pset.queryFout, pset.logfile);
1999-11-05 00:14:30 +01:00
PQclear(res);
return true;
}
/* \db
* Takes an optional regexp to select particular tablespaces
*/
bool
2004-07-15 05:56:06 +02:00
describeTablespaces(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 80000)
2004-08-29 07:07:03 +02:00
{
fprintf(stderr, _("The server (version %d.%d) does not support tablespaces.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
2004-08-29 07:07:03 +02:00
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT spcname AS \"%s\",\n"
2005-10-15 04:49:52 +02:00
" pg_catalog.pg_get_userbyid(spcowner) AS \"%s\",\n"
2004-07-15 05:56:06 +02:00
" spclocation AS \"%s\"",
gettext_noop("Name"),
gettext_noop("Owner"),
gettext_noop("Location"));
2004-07-15 05:56:06 +02:00
if (verbose)
{
appendPQExpBuffer(&buf, ",\n ");
printACLColumn(&buf, "spcacl");
}
if (verbose && pset.sversion >= 80200)
appendPQExpBuffer(&buf,
",\n pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
gettext_noop("Description"));
2004-08-29 07:07:03 +02:00
2004-07-15 05:56:06 +02:00
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_tablespace\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL, "spcname", NULL,
NULL);
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of tablespaces");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/* \df
* Takes an optional regexp to select particular functions.
*
* As with \d, you can specify the kinds of functions you want:
*
* a for aggregates
* n for normal
* t for trigger
* w for window
*
* and you can mix and match these in any order.
*/
bool
describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem)
{
bool showAggregate = strchr(functypes, 'a') != NULL;
bool showNormal = strchr(functypes, 'n') != NULL;
bool showTrigger = strchr(functypes, 't') != NULL;
bool showWindow = strchr(functypes, 'w') != NULL;
bool have_where;
PQExpBufferData buf;
1999-11-05 00:14:30 +01:00
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, false, false, true, true, false, false, false, false};
1999-11-05 00:14:30 +01:00
if (strlen(functypes) != strspn(functypes, "antwS+"))
{
fprintf(stderr, _("\\df only takes [antwS+] as options\n"));
return true;
}
if (showWindow && pset.sversion < 80400)
{
2009-06-10 23:51:56 +02:00
fprintf(stderr, _("\\df does not take a \"w\" option with server version %d.%d\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
if (!showAggregate && !showNormal && !showTrigger && !showWindow)
{
showAggregate = showNormal = showTrigger = true;
if (pset.sversion >= 80400)
showWindow = true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT n.nspname as \"%s\",\n"
" p.proname as \"%s\",\n",
gettext_noop("Schema"),
gettext_noop("Name"));
if (pset.sversion >= 80400)
appendPQExpBuffer(&buf,
" pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
" pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
" CASE\n"
" WHEN p.proisagg THEN '%s'\n"
" WHEN p.proiswindow THEN '%s'\n"
" WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
" ELSE '%s'\n"
"END as \"%s\"",
gettext_noop("Result data type"),
gettext_noop("Argument data types"),
/* translator: "agg" is short for "aggregate" */
gettext_noop("agg"),
gettext_noop("window"),
gettext_noop("trigger"),
gettext_noop("normal"),
gettext_noop("Type"));
else if (pset.sversion >= 80100)
appendPQExpBuffer(&buf,
" CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
" pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
" CASE WHEN proallargtypes IS NOT NULL THEN\n"
" pg_catalog.array_to_string(ARRAY(\n"
" SELECT\n"
" CASE\n"
" WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
" WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n"
" WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
" WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n"
" END ||\n"
" CASE\n"
2006-10-04 02:30:14 +02:00
" WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n"
" ELSE p.proargnames[s.i] || ' ' \n"
" END ||\n"
2006-10-04 02:30:14 +02:00
" pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n"
" FROM\n"
" pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n"
" ), ', ')\n"
" ELSE\n"
" pg_catalog.array_to_string(ARRAY(\n"
" SELECT\n"
" CASE\n"
2006-10-04 02:30:14 +02:00
" WHEN COALESCE(p.proargnames[s.i+1], '') = '' THEN ''\n"
" ELSE p.proargnames[s.i+1] || ' '\n"
" END ||\n"
2006-10-04 02:30:14 +02:00
" pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
" FROM\n"
" pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
" ), ', ')\n"
" END AS \"%s\",\n"
" CASE\n"
" WHEN p.proisagg THEN '%s'\n"
" WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
" ELSE '%s'\n"
" END AS \"%s\"",
gettext_noop("Result data type"),
gettext_noop("Argument data types"),
/* translator: "agg" is short for "aggregate" */
gettext_noop("agg"),
gettext_noop("trigger"),
gettext_noop("normal"),
gettext_noop("Type"));
else
appendPQExpBuffer(&buf,
" CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
" pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
" pg_catalog.oidvectortypes(p.proargtypes) as \"%s\",\n"
" CASE\n"
" WHEN p.proisagg THEN '%s'\n"
" WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
" ELSE '%s'\n"
" END AS \"%s\"",
gettext_noop("Result data type"),
gettext_noop("Argument data types"),
/* translator: "agg" is short for "aggregate" */
gettext_noop("agg"),
gettext_noop("trigger"),
gettext_noop("normal"),
gettext_noop("Type"));
if (verbose)
appendPQExpBuffer(&buf,
",\n CASE\n"
" WHEN p.provolatile = 'i' THEN '%s'\n"
" WHEN p.provolatile = 's' THEN '%s'\n"
" WHEN p.provolatile = 'v' THEN '%s'\n"
"END as \"%s\""
",\n pg_catalog.pg_get_userbyid(p.proowner) as \"%s\",\n"
2002-09-04 22:31:48 +02:00
" l.lanname as \"%s\",\n"
" p.prosrc as \"%s\",\n"
2005-10-15 04:49:52 +02:00
" pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"",
gettext_noop("immutable"),
gettext_noop("stable"),
gettext_noop("volatile"),
gettext_noop("Volatility"),
gettext_noop("Owner"),
gettext_noop("Language"),
gettext_noop("Source code"),
gettext_noop("Description"));
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_proc p"
"\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
if (verbose)
appendPQExpBuffer(&buf,
" LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n");
have_where = false;
/* filter by function type, if requested */
if (showNormal && showAggregate && showTrigger && showWindow)
/* Do nothing */ ;
else if (showNormal)
{
if (!showAggregate)
{
if (have_where)
appendPQExpBuffer(&buf, " AND ");
else
{
appendPQExpBuffer(&buf, "WHERE ");
have_where = true;
}
appendPQExpBuffer(&buf, "NOT p.proisagg\n");
}
if (!showTrigger)
{
if (have_where)
appendPQExpBuffer(&buf, " AND ");
else
{
appendPQExpBuffer(&buf, "WHERE ");
have_where = true;
}
appendPQExpBuffer(&buf, "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n");
}
if (!showWindow && pset.sversion >= 80400)
{
if (have_where)
appendPQExpBuffer(&buf, " AND ");
else
{
appendPQExpBuffer(&buf, "WHERE ");
have_where = true;
}
appendPQExpBuffer(&buf, "NOT p.proiswindow\n");
}
}
else
{
bool needs_or = false;
appendPQExpBuffer(&buf, "WHERE (\n ");
have_where = true;
/* Note: at least one of these must be true ... */
if (showAggregate)
{
appendPQExpBuffer(&buf, "p.proisagg\n");
needs_or = true;
}
if (showTrigger)
{
if (needs_or)
appendPQExpBuffer(&buf, " OR ");
appendPQExpBuffer(&buf,
"p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype\n");
needs_or = true;
}
if (showWindow)
{
if (needs_or)
appendPQExpBuffer(&buf, " OR ");
appendPQExpBuffer(&buf, "p.proiswindow\n");
needs_or = true;
}
appendPQExpBuffer(&buf, " )\n");
}
processSQLNamePattern(pset.db, &buf, pattern, have_where, false,
"n.nspname", "p.proname", NULL,
"pg_catalog.pg_function_is_visible(p.oid)");
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
appendPQExpBuffer(&buf, "ORDER BY 1, 2, 4;");
1999-11-05 00:14:30 +01:00
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of functions");
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
1999-11-05 00:14:30 +01:00
printQuery(res, &myopt, pset.queryFout, pset.logfile);
1999-11-05 00:14:30 +01:00
PQclear(res);
return true;
}
/*
* \dT
* describe types
*/
bool
describeTypes(const char *pattern, bool verbose, bool showSystem)
{
PQExpBufferData buf;
1999-11-05 00:14:30 +01:00
PGresult *res;
printQueryOpt myopt = pset.popt;
1999-11-05 00:14:30 +01:00
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT n.nspname as \"%s\",\n"
2005-10-15 04:49:52 +02:00
" pg_catalog.format_type(t.oid, NULL) AS \"%s\",\n",
gettext_noop("Schema"),
gettext_noop("Name"));
if (verbose)
appendPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
" t.typname AS \"%s\",\n"
" CASE WHEN t.typrelid != 0\n"
" THEN CAST('tuple' AS pg_catalog.text)\n"
" WHEN t.typlen < 0\n"
" THEN CAST('var' AS pg_catalog.text)\n"
" ELSE CAST(t.typlen AS pg_catalog.text)\n"
" END AS \"%s\",\n",
gettext_noop("Internal name"),
gettext_noop("Size"));
if (verbose && pset.sversion >= 80300)
{
appendPQExpBuffer(&buf,
" pg_catalog.array_to_string(\n"
" ARRAY(\n"
" SELECT e.enumlabel\n"
" FROM pg_catalog.pg_enum e\n"
" WHERE e.enumtypid = t.oid\n");
if (pset.sversion >= 90100)
appendPQExpBuffer(&buf,
" ORDER BY e.enumsortorder\n");
else
appendPQExpBuffer(&buf,
" ORDER BY e.oid\n");
appendPQExpBuffer(&buf,
" ),\n"
" E'\\n'\n"
" ) AS \"%s\",\n",
gettext_noop("Elements"));
}
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
gettext_noop("Description"));
appendPQExpBuffer(&buf, "FROM pg_catalog.pg_type t\n"
2005-10-15 04:49:52 +02:00
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
/*
* do not include complex types (typrelid!=0) unless they are standalone
* composite types
*/
appendPQExpBuffer(&buf, "WHERE (t.typrelid = 0 ");
appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c "
"WHERE c.oid = t.typrelid))\n");
/*
* do not include array types (before 8.3 we have to use the assumption
* that their names start with underscore)
*/
if (pset.sversion >= 80300)
appendPQExpBuffer(&buf, " AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)\n");
else
appendPQExpBuffer(&buf, " AND t.typname !~ '^_'\n");
1999-11-05 00:14:30 +01:00
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
/* Match name pattern against either internal or external name */
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "t.typname",
"pg_catalog.format_type(t.oid, NULL)",
"pg_catalog.pg_type_is_visible(t.oid)");
1999-11-05 00:14:30 +01:00
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of data types");
myopt.translate_header = true;
1999-11-05 00:14:30 +01:00
printQuery(res, &myopt, pset.queryFout, pset.logfile);
1999-11-05 00:14:30 +01:00
PQclear(res);
return true;
}
/* \do
*/
bool
describeOperators(const char *pattern, bool showSystem)
{
PQExpBufferData buf;
1999-11-05 00:14:30 +01:00
PGresult *res;
printQueryOpt myopt = pset.popt;
1999-11-05 00:14:30 +01:00
initPQExpBuffer(&buf);
/*
* Note: before Postgres 9.1, we did not assign comments to any built-in
* operators, preferring to let the comment on the underlying function
* suffice. The coalesce() on the obj_description() calls below supports
* this convention by providing a fallback lookup of a comment on the
* operator's function. As of 9.1 there is a policy that every built-in
* operator should have a comment; so the coalesce() is no longer
* necessary so far as built-in operators are concerned. We keep it
* anyway, for now, because (1) third-party modules may still be following
* the old convention, and (2) we'd need to do it anyway when talking to a
* pre-9.1 server.
*/
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT n.nspname as \"%s\",\n"
" o.oprname AS \"%s\",\n"
" CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n"
" CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n"
2005-10-15 04:49:52 +02:00
" pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n"
" coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n"
" pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
2002-09-04 22:31:48 +02:00
"FROM pg_catalog.pg_operator o\n"
2005-10-15 04:49:52 +02:00
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Left arg type"),
gettext_noop("Right arg type"),
gettext_noop("Result type"),
gettext_noop("Description"));
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, true,
"n.nspname", "o.oprname", NULL,
"pg_catalog.pg_operator_is_visible(o.oid)");
1999-11-05 00:14:30 +01:00
appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3, 4;");
1999-11-05 00:14:30 +01:00
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of operators");
myopt.translate_header = true;
1999-11-05 00:14:30 +01:00
printQuery(res, &myopt, pset.queryFout, pset.logfile);
1999-11-05 00:14:30 +01:00
PQclear(res);
return true;
}
/*
* listAllDbs
*
* for \l, \list, and -l switch
*/
bool
listAllDbs(bool verbose)
{
1999-11-05 00:14:30 +01:00
PGresult *res;
PQExpBufferData buf;
printQueryOpt myopt = pset.popt;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT d.datname as \"%s\",\n"
" pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n"
" pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n",
gettext_noop("Name"),
gettext_noop("Owner"),
gettext_noop("Encoding"));
if (pset.sversion >= 80400)
appendPQExpBuffer(&buf,
" d.datcollate as \"%s\",\n"
" d.datctype as \"%s\",\n",
gettext_noop("Collate"),
gettext_noop("Ctype"));
appendPQExpBuffer(&buf, " ");
printACLColumn(&buf, "d.datacl");
if (verbose && pset.sversion >= 80200)
appendPQExpBuffer(&buf,
",\n CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT')\n"
" THEN pg_catalog.pg_size_pretty(pg_catalog.pg_database_size(d.datname))\n"
" ELSE 'No Access'\n"
" END as \"%s\"",
gettext_noop("Size"));
if (verbose && pset.sversion >= 80000)
appendPQExpBuffer(&buf,
",\n t.spcname as \"%s\"",
gettext_noop("Tablespace"));
if (verbose && pset.sversion >= 80200)
appendPQExpBuffer(&buf,
",\n pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"",
gettext_noop("Description"));
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_database d\n");
if (verbose && pset.sversion >= 80000)
appendPQExpBuffer(&buf,
2006-10-04 02:30:14 +02:00
" JOIN pg_catalog.pg_tablespace t on d.dattablespace = t.oid\n");
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
if (!res)
return false;
1999-11-05 00:14:30 +01:00
myopt.nullPrint = NULL;
myopt.title = _("List of databases");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
1999-11-05 00:14:30 +01:00
PQclear(res);
return true;
}
/*
* List Tables' Grant/Revoke Permissions
* \z (now also \dp -- perhaps more mnemonic)
*/
bool
permissionsList(const char *pattern)
{
PQExpBufferData buf;
1999-11-05 00:14:30 +01:00
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, true, false, false};
1999-11-05 00:14:30 +01:00
initPQExpBuffer(&buf);
/*
2005-10-15 04:49:52 +02:00
* we ignore indexes and toast tables since they have no meaningful rights
*/
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT n.nspname as \"%s\",\n"
" c.relname as \"%s\",\n"
" CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
" ",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"),
2011-04-10 17:42:00 +02:00
gettext_noop("foreign table"),
gettext_noop("Type"));
printACLColumn(&buf, "c.relacl");
if (pset.sversion >= 80400)
appendPQExpBuffer(&buf,
",\n pg_catalog.array_to_string(ARRAY(\n"
" SELECT attname || E':\\n ' || pg_catalog.array_to_string(attacl, E'\\n ')\n"
" FROM pg_catalog.pg_attribute a\n"
" WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n"
" ), E'\\n') AS \"%s\"",
gettext_noop("Column access privileges"));
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
"WHERE c.relkind IN ('r', 'v', 'S', 'f')\n");
/*
* Unless a schema pattern is specified, we suppress system and temp
2005-10-15 04:49:52 +02:00
* tables, since they normally aren't very interesting from a permissions
* point of view. You can see 'em by explicit request though, eg with \z
* pg_catalog.*
*/
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "c.relname", NULL,
2005-10-15 04:49:52 +02:00
"n.nspname !~ '^pg_' AND pg_catalog.pg_table_is_visible(c.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
1999-11-05 00:14:30 +01:00
if (!res)
{
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
return false;
}
1999-11-05 00:14:30 +01:00
myopt.nullPrint = NULL;
printfPQExpBuffer(&buf, _("Access privileges"));
myopt.title = buf.data;
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
1999-11-05 00:14:30 +01:00
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
PQclear(res);
return true;
}
/*
* \ddp
*
* List DefaultACLs. The pattern can match either schema or role name.
*/
bool
listDefaultACLs(const char *pattern)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, true, false};
if (pset.sversion < 90000)
{
fprintf(stderr, _("The server (version %d.%d) does not support altering default privileges.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
2010-02-26 03:01:40 +01:00
"SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
" n.nspname AS \"%s\",\n"
" CASE d.defaclobjtype WHEN 'r' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END AS \"%s\",\n"
" ",
gettext_noop("Owner"),
gettext_noop("Schema"),
gettext_noop("table"),
gettext_noop("sequence"),
gettext_noop("function"),
gettext_noop("Type"));
printACLColumn(&buf, "d.defaclacl");
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
2010-02-26 03:01:40 +01:00
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL,
"n.nspname",
"pg_catalog.pg_get_userbyid(d.defaclrole)",
NULL);
appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
res = PSQLexec(buf.data, false);
if (!res)
{
termPQExpBuffer(&buf);
return false;
}
myopt.nullPrint = NULL;
printfPQExpBuffer(&buf, _("Default access privileges"));
myopt.title = buf.data;
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
termPQExpBuffer(&buf);
PQclear(res);
return true;
}
/*
* Get object comments
*
* \dd [foo]
*
* Note: This command only lists comments for object types which do not have
* their comments displayed by their own backslash commands. The following
* types of objects will be displayed: constraint, operator class,
* operator family, rule, and trigger.
*
*/
bool
objectDescription(const char *pattern, bool showSystem)
{
PQExpBufferData buf;
1999-11-05 00:14:30 +01:00
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, true, false};
1999-11-05 00:14:30 +01:00
initPQExpBuffer(&buf);
appendPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT DISTINCT tt.nspname AS \"%s\", tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n"
"FROM (\n",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Object"),
gettext_noop("Description"));
/* Constraint descriptions */
appendPQExpBuffer(&buf,
" SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
2002-09-04 22:31:48 +02:00
" n.nspname as nspname,\n"
" CAST(pgc.conname AS pg_catalog.text) as name,"
2002-09-04 22:31:48 +02:00
" CAST('%s' AS pg_catalog.text) as object\n"
" FROM pg_catalog.pg_constraint pgc\n"
" JOIN pg_catalog.pg_class c "
"ON c.oid = pgc.conrelid\n"
" LEFT JOIN pg_catalog.pg_namespace n "
" ON n.oid = c.relnamespace\n",
gettext_noop("constraint"));
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern,
false, "n.nspname", "pgc.conname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
/*
* pg_opclass.opcmethod only available in 8.3+
*/
if (pset.sversion >= 80300)
{
/* Operator class descriptions */
appendPQExpBuffer(&buf,
"UNION ALL\n"
" SELECT o.oid as oid, o.tableoid as tableoid,\n"
" n.nspname as nspname,\n"
" CAST(o.opcname AS pg_catalog.text) as name,\n"
" CAST('%s' AS pg_catalog.text) as object\n"
" FROM pg_catalog.pg_opclass o\n"
" JOIN pg_catalog.pg_am am ON "
"o.opcmethod = am.oid\n"
" JOIN pg_catalog.pg_namespace n ON "
"n.oid = o.opcnamespace\n",
gettext_noop("operator class"));
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "o.opcname", NULL,
"pg_catalog.pg_opclass_is_visible(o.oid)");
}
/*
* although operator family comments have been around since 8.3,
* pg_opfamily_is_visible is only available in 9.2+
*/
if (pset.sversion >= 90200)
{
/* Operator family descriptions */
appendPQExpBuffer(&buf,
"UNION ALL\n"
" SELECT opf.oid as oid, opf.tableoid as tableoid,\n"
" n.nspname as nspname,\n"
" CAST(opf.opfname AS pg_catalog.text) AS name,\n"
" CAST('%s' AS pg_catalog.text) as object\n"
" FROM pg_catalog.pg_opfamily opf\n"
" JOIN pg_catalog.pg_am am "
"ON opf.opfmethod = am.oid\n"
" JOIN pg_catalog.pg_namespace n "
"ON opf.opfnamespace = n.oid\n",
gettext_noop("operator family"));
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "opf.opfname", NULL,
"pg_catalog.pg_opfamily_is_visible(opf.oid)");
}
/* Rule descriptions (ignore rules for views) */
appendPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"UNION ALL\n"
" SELECT r.oid as oid, r.tableoid as tableoid,\n"
" n.nspname as nspname,\n"
" CAST(r.rulename AS pg_catalog.text) as name,"
" CAST('%s' AS pg_catalog.text) as object\n"
" FROM pg_catalog.pg_rewrite r\n"
2005-10-15 04:49:52 +02:00
" JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
2002-09-04 22:31:48 +02:00
" WHERE r.rulename != '_RETURN'\n",
gettext_noop("rule"));
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "r.rulename", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
/* Trigger descriptions */
appendPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"UNION ALL\n"
" SELECT t.oid as oid, t.tableoid as tableoid,\n"
" n.nspname as nspname,\n"
" CAST(t.tgname AS pg_catalog.text) as name,"
" CAST('%s' AS pg_catalog.text) as object\n"
" FROM pg_catalog.pg_trigger t\n"
2005-10-15 04:49:52 +02:00
" JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n",
gettext_noop("trigger"));
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
"n.nspname", "t.tgname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
appendPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
") AS tt\n"
2003-10-26 03:53:45 +01:00
" JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n");
appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
1999-11-05 00:14:30 +01:00
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("Object descriptions");
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
1999-11-05 00:14:30 +01:00
printQuery(res, &myopt, pset.queryFout, pset.logfile);
1999-11-05 00:14:30 +01:00
PQclear(res);
return true;
}
/*
* describeTableDetails (for \d)
*
* This routine finds the tables to be displayed, and calls
* describeOneTableDetails for each one.
*
* verbose: if true, this is \d+
*/
bool
describeTableDetails(const char *pattern, bool verbose, bool showSystem)
{
PQExpBufferData buf;
PGresult *res;
int i;
1999-11-05 00:14:30 +01:00
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT c.oid,\n"
" n.nspname,\n"
" c.relname\n"
"FROM pg_catalog.pg_class c\n"
2005-10-15 04:49:52 +02:00
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
"n.nspname", "c.relname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
appendPQExpBuffer(&buf, "ORDER BY 2, 3;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
if (PQntuples(res) == 0)
1999-11-05 00:14:30 +01:00
{
if (!pset.quiet)
fprintf(stderr, _("Did not find any relation named \"%s\".\n"),
pattern);
PQclear(res);
return false;
1999-11-05 00:14:30 +01:00
}
for (i = 0; i < PQntuples(res); i++)
{
const char *oid;
const char *nspname;
const char *relname;
oid = PQgetvalue(res, i, 0);
nspname = PQgetvalue(res, i, 1);
relname = PQgetvalue(res, i, 2);
if (!describeOneTableDetails(nspname, relname, oid, verbose))
{
PQclear(res);
return false;
}
if (cancel_pressed)
{
PQclear(res);
return false;
}
}
PQclear(res);
return true;
}
/*
* describeOneTableDetails (for \d)
*
* Unfortunately, the information presented here is so complicated that it
* cannot be done in a single query. So we have to assemble the printed table
* by hand and pass it to the underlying printTable() function.
*/
static bool
describeOneTableDetails(const char *schemaname,
const char *relationname,
const char *oid,
bool verbose)
{
PQExpBufferData buf;
PGresult *res = NULL;
printTableOpt myopt = pset.popt.topt;
printTableContent cont;
bool printTableInitialized = false;
1999-11-05 00:14:30 +01:00
int i;
2002-09-04 22:31:48 +02:00
char *view_def = NULL;
char *headers[6];
char **seq_values = NULL;
char **modifiers = NULL;
1999-11-05 00:14:30 +01:00
char **ptr;
PQExpBufferData title;
PQExpBufferData tmpbuf;
int cols;
int numrows = 0;
struct
{
int16 checks;
char relkind;
bool hasindex;
bool hasrules;
bool hastriggers;
2004-08-29 07:07:03 +02:00
bool hasoids;
Oid tablespace;
char *reloptions;
char *reloftype;
char relpersistence;
} tableinfo;
bool show_modifiers = false;
bool retval;
retval = false;
1999-11-05 00:14:30 +01:00
/* This output looks confusing in expanded mode. */
myopt.expanded = false;
initPQExpBuffer(&buf);
initPQExpBuffer(&title);
initPQExpBuffer(&tmpbuf);
1999-11-05 00:14:30 +01:00
/* Get general table info */
if (pset.sversion >= 90100)
{
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, c.relhasoids, "
"%s, c.reltablespace, "
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
"c.relpersistence\n"
"FROM pg_catalog.pg_class c\n "
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
"WHERE c.oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(c.reloptions || "
"array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
: "''"),
oid);
}
else if (pset.sversion >= 90000)
{
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, c.relhasoids, "
"%s, c.reltablespace, "
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n"
"FROM pg_catalog.pg_class c\n "
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
"WHERE c.oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(c.reloptions || "
"array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
: "''"),
oid);
}
else if (pset.sversion >= 80400)
{
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, c.relhasoids, "
"%s, c.reltablespace\n"
"FROM pg_catalog.pg_class c\n "
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
"WHERE c.oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(c.reloptions || "
"array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
: "''"),
oid);
}
else if (pset.sversion >= 80200)
{
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
"%s, reltablespace\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(reloptions, E', ')" : "''"),
oid);
}
else if (pset.sversion >= 80000)
{
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
"'', reltablespace\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
else
{
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
"'', ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
res = PSQLexec(buf.data, false);
if (!res)
goto error_return;
1999-11-05 00:14:30 +01:00
/* Did we get anything? */
if (PQntuples(res) == 0)
{
if (!pset.quiet)
fprintf(stderr, _("Did not find any relation with OID %s.\n"),
oid);
goto error_return;
1999-11-05 00:14:30 +01:00
}
tableinfo.checks = atoi(PQgetvalue(res, 0, 0));
tableinfo.relkind = *(PQgetvalue(res, 0, 1));
tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
tableinfo.reloptions = (pset.sversion >= 80200) ?
strdup(PQgetvalue(res, 0, 6)) : 0;
tableinfo.tablespace = (pset.sversion >= 80000) ?
atooid(PQgetvalue(res, 0, 7)) : 0;
tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 8), "") != 0) ?
strdup(PQgetvalue(res, 0, 8)) : 0;
tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
PQgetvalue(res, 0, 9)[0] : 0;
PQclear(res);
res = NULL;
/*
* If it's a sequence, fetch its values and store into an array that will
* be used later.
*/
if (tableinfo.relkind == 'S')
{
printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
/* must be separate because fmtId isn't reentrant */
appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
res = PSQLexec(buf.data, false);
if (!res)
goto error_return;
seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
for (i = 0; i < PQnfields(res); i++)
seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
seq_values[i] = NULL;
PQclear(res);
res = NULL;
}
1999-11-05 00:14:30 +01:00
/*
* Get column info
*
2011-11-04 15:57:43 +01:00
* You need to modify value of "firstvcol" which will be defined below if
* you are adding column(s) preceding to verbose-only columns.
*/
printfPQExpBuffer(&buf, "SELECT a.attname,");
appendPQExpBuffer(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod),"
"\n (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
"\n FROM pg_catalog.pg_attrdef d"
"\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),"
"\n a.attnotnull, a.attnum,");
if (pset.sversion >= 90100)
appendPQExpBuffer(&buf, "\n (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
" WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
else
appendPQExpBuffer(&buf, "\n NULL AS attcollation");
if (tableinfo.relkind == 'i')
appendPQExpBuffer(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
else
appendPQExpBuffer(&buf, ",\n NULL AS indexdef");
if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
appendPQExpBuffer(&buf, ",\n CASE WHEN attfdwoptions IS NULL THEN '' ELSE "
" '(' || array_to_string(ARRAY(SELECT quote_ident(option_name) || ' ' || quote_literal(option_value) FROM "
" pg_options_to_table(attfdwoptions)), ', ') || ')' END AS attfdwoptions");
else
appendPQExpBuffer(&buf, ",\n NULL AS attfdwoptions");
if (verbose)
{
appendPQExpBuffer(&buf, ",\n a.attstorage");
appendPQExpBuffer(&buf, ",\n CASE WHEN a.attstattarget=-1 THEN NULL ELSE a.attstattarget END AS attstattarget");
/*
* In 9.0+, we have column comments for: relations, views, composite
* types, and foreign tables (c.f. CommentObject() in comment.c).
*/
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
tableinfo.relkind == 'f' || tableinfo.relkind == 'c')
appendPQExpBuffer(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)");
}
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_attribute a");
appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
appendPQExpBuffer(&buf, "\nORDER BY a.attnum;");
res = PSQLexec(buf.data, false);
if (!res)
goto error_return;
numrows = PQntuples(res);
/* Make title */
switch (tableinfo.relkind)
{
case 'r':
if (tableinfo.relpersistence == 'u')
printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
schemaname, relationname);
else
printfPQExpBuffer(&title, _("Table \"%s.%s\""),
schemaname, relationname);
break;
case 'v':
printfPQExpBuffer(&title, _("View \"%s.%s\""),
schemaname, relationname);
break;
case 'S':
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
schemaname, relationname);
break;
case 'i':
if (tableinfo.relpersistence == 'u')
printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
schemaname, relationname);
else
printfPQExpBuffer(&title, _("Index \"%s.%s\""),
schemaname, relationname);
break;
case 's':
/* not used as of 8.2, but keep it for backwards compatibility */
printfPQExpBuffer(&title, _("Special relation \"%s.%s\""),
schemaname, relationname);
break;
case 't':
printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
schemaname, relationname);
break;
case 'c':
printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
schemaname, relationname);
break;
case 'f':
printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
schemaname, relationname);
break;
default:
/* untranslated unknown relkind */
printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
tableinfo.relkind, schemaname, relationname);
break;
}
/* Set the number of columns, and their names */
headers[0] = gettext_noop("Column");
headers[1] = gettext_noop("Type");
cols = 2;
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
tableinfo.relkind == 'f' || tableinfo.relkind == 'c')
{
show_modifiers = true;
headers[cols++] = gettext_noop("Modifiers");
modifiers = pg_malloc_zero((numrows + 1) * sizeof(*modifiers));
}
if (tableinfo.relkind == 'S')
headers[cols++] = gettext_noop("Value");
if (tableinfo.relkind == 'i')
headers[cols++] = gettext_noop("Definition");
if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
headers[cols++] = gettext_noop("FDW Options");
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
if (tableinfo.relkind == 'r')
headers[cols++] = gettext_noop("Stats target");
/* Column comments, if the relkind supports this feature. */
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
tableinfo.relkind == 'c' || tableinfo.relkind == 'f')
headers[cols++] = gettext_noop("Description");
}
printTableInit(&cont, &myopt, title.data, cols, numrows);
printTableInitialized = true;
for (i = 0; i < cols; i++)
printTableAddHeader(&cont, headers[i], true, 'l');
/* Check if table is a view */
if (tableinfo.relkind == 'v' && verbose)
{
PGresult *result;
printfPQExpBuffer(&buf,
"SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
if (PQntuples(result) > 0)
view_def = pg_strdup(PQgetvalue(result, 0, 0));
PQclear(result);
}
/* Generate table cells to be printed */
for (i = 0; i < numrows; i++)
1999-11-05 00:14:30 +01:00
{
/* Column */
printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
1999-11-05 00:14:30 +01:00
/* Type */
printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
/* Modifiers: collate, not null, default */
if (show_modifiers)
{
resetPQExpBuffer(&tmpbuf);
if (!PQgetisnull(res, i, 5))
{
if (tmpbuf.len > 0)
appendPQExpBufferStr(&tmpbuf, " ");
appendPQExpBuffer(&tmpbuf, _("collate %s"),
PQgetvalue(res, i, 5));
}
if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
{
if (tmpbuf.len > 0)
appendPQExpBufferStr(&tmpbuf, " ");
appendPQExpBufferStr(&tmpbuf, _("not null"));
}
/* handle "default" here */
/* (note: above we cut off the 'default' string at 128) */
if (strlen(PQgetvalue(res, i, 2)) != 0)
{
if (tmpbuf.len > 0)
appendPQExpBufferStr(&tmpbuf, " ");
/* translator: default values of column definitions */
appendPQExpBuffer(&tmpbuf, _("default %s"),
PQgetvalue(res, i, 2));
}
modifiers[i] = pg_strdup(tmpbuf.data);
printTableAddCell(&cont, modifiers[i], false, false);
}
/* Value: for sequences only */
if (tableinfo.relkind == 'S')
printTableAddCell(&cont, seq_values[i], false, false);
/* Expression for index column */
if (tableinfo.relkind == 'i')
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
/* FDW options for foreign table column, only for 9.2 or later */
if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
/* Storage and Description */
if (verbose)
{
int firstvcol = 8;
2010-02-26 03:01:40 +01:00
char *storage = PQgetvalue(res, i, firstvcol);
/* these strings are literal in our syntax, so not translated. */
printTableAddCell(&cont, (storage[0] == 'p' ? "plain" :
(storage[0] == 'm' ? "main" :
(storage[0] == 'x' ? "extended" :
(storage[0] == 'e' ? "external" :
"???")))),
false, false);
/* Statistics target, if the relkind supports this feature */
if (tableinfo.relkind == 'r')
{
printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
false, false);
}
/* Column comments, if the relkind supports this feature. */
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
tableinfo.relkind == 'c' || tableinfo.relkind == 'f')
printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 2),
false, false);
}
1999-11-05 00:14:30 +01:00
}
1999-11-05 00:14:30 +01:00
/* Make footers */
if (tableinfo.relkind == 'i')
{
/* Footer information about an index */
PGresult *result;
2002-09-04 22:31:48 +02:00
printfPQExpBuffer(&buf,
"SELECT i.indisunique, i.indisprimary, i.indisclustered, ");
if (pset.sversion >= 80200)
appendPQExpBuffer(&buf, "i.indisvalid,\n");
else
appendPQExpBuffer(&buf, "true AS indisvalid,\n");
if (pset.sversion >= 90000)
appendPQExpBuffer(&buf,
" (NOT i.indimmediate) AND "
2010-07-06 21:19:02 +02:00
"EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
"WHERE conrelid = i.indrelid AND "
"conindid = i.indexrelid AND "
"contype IN ('p','u','x') AND "
"condeferrable) AS condeferrable,\n"
" (NOT i.indimmediate) AND "
2010-07-06 21:19:02 +02:00
"EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
"WHERE conrelid = i.indrelid AND "
"conindid = i.indexrelid AND "
"contype IN ('p','u','x') AND "
"condeferred) AS condeferred,\n");
else
appendPQExpBuffer(&buf,
" false AS condeferrable, false AS condeferred,\n");
appendPQExpBuffer(&buf, " a.amname, c2.relname, "
2010-02-26 03:01:40 +01:00
"pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
"FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
2005-10-15 04:49:52 +02:00
"WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
"AND i.indrelid = c2.oid;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else if (PQntuples(result) != 1)
{
PQclear(result);
goto error_return;
}
else
{
char *indisunique = PQgetvalue(result, 0, 0);
char *indisprimary = PQgetvalue(result, 0, 1);
2004-04-06 06:05:17 +02:00
char *indisclustered = PQgetvalue(result, 0, 2);
char *indisvalid = PQgetvalue(result, 0, 3);
char *deferrable = PQgetvalue(result, 0, 4);
char *deferred = PQgetvalue(result, 0, 5);
char *indamname = PQgetvalue(result, 0, 6);
char *indtable = PQgetvalue(result, 0, 7);
char *indpred = PQgetvalue(result, 0, 8);
2002-04-24 08:17:04 +02:00
if (strcmp(indisprimary, "t") == 0)
2004-10-12 23:54:45 +02:00
printfPQExpBuffer(&tmpbuf, _("primary key, "));
2002-04-24 08:17:04 +02:00
else if (strcmp(indisunique, "t") == 0)
2004-10-12 23:54:45 +02:00
printfPQExpBuffer(&tmpbuf, _("unique, "));
2002-04-24 08:17:04 +02:00
else
resetPQExpBuffer(&tmpbuf);
appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
/* we assume here that index and table are in same schema */
appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
schemaname, indtable);
2002-04-24 08:17:04 +02:00
if (strlen(indpred))
2004-01-11 20:10:49 +01:00
appendPQExpBuffer(&tmpbuf, _(", predicate (%s)"), indpred);
2002-04-24 08:17:04 +02:00
2004-04-06 06:05:17 +02:00
if (strcmp(indisclustered, "t") == 0)
2004-10-12 23:54:45 +02:00
appendPQExpBuffer(&tmpbuf, _(", clustered"));
2004-04-06 06:05:17 +02:00
if (strcmp(indisvalid, "t") != 0)
appendPQExpBuffer(&tmpbuf, _(", invalid"));
if (strcmp(deferrable, "t") == 0)
appendPQExpBuffer(&tmpbuf, _(", deferrable"));
if (strcmp(deferred, "t") == 0)
appendPQExpBuffer(&tmpbuf, _(", initially deferred"));
printTableAddFooter(&cont, tmpbuf.data);
add_tablespace_footer(&cont, tableinfo.relkind,
tableinfo.tablespace, true);
}
PQclear(result);
}
else if (view_def)
1999-11-05 00:14:30 +01:00
{
PGresult *result = NULL;
/* Footer information about a view */
printTableAddFooter(&cont, _("View definition:"));
printTableAddFooter(&cont, view_def);
/* print rules */
if (tableinfo.hasrules)
{
printfPQExpBuffer(&buf,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
2002-09-04 22:31:48 +02:00
"FROM pg_catalog.pg_rewrite r\n"
"WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
2002-09-04 22:31:48 +02:00
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
if (PQntuples(result) > 0)
{
printTableAddFooter(&cont, _("Rules:"));
for (i = 0; i < PQntuples(result); i++)
{
const char *ruledef;
/* Everything after "CREATE RULE" is echoed verbatim */
ruledef = PQgetvalue(result, i, 1);
ruledef += 12;
printfPQExpBuffer(&buf, " %s", ruledef);
printTableAddFooter(&cont, buf.data);
}
}
PQclear(result);
}
1999-11-05 00:14:30 +01:00
}
else if (tableinfo.relkind == 'S')
{
/* Footer information about a sequence */
PGresult *result = NULL;
/* Get the column that owns this sequence */
printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
"\n pg_catalog.quote_ident(relname) || '.' ||"
"\n pg_catalog.quote_ident(attname)"
"\nFROM pg_catalog.pg_class c"
"\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
"\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
"\nINNER JOIN pg_catalog.pg_attribute a ON ("
"\n a.attrelid=c.oid AND"
"\n a.attnum=d.refobjsubid)"
"\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
"\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
"\n AND d.objid=%s"
"\n AND d.deptype='a'",
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else if (PQntuples(result) == 1)
{
printfPQExpBuffer(&buf, _("Owned by: %s"),
PQgetvalue(result, 0, 0));
printTableAddFooter(&cont, buf.data);
}
/*
* If we get no rows back, don't show anything (obviously).
* We should never get more than one row back, but if we do,
* just ignore it and don't print anything.
*/
PQclear(result);
}
else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
1999-11-05 00:14:30 +01:00
{
/* Footer information about a table */
PGresult *result = NULL;
int tuples = 0;
/* print indexes */
if (tableinfo.hasindex)
{
printfPQExpBuffer(&buf,
"SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, ");
if (pset.sversion >= 80200)
appendPQExpBuffer(&buf, "i.indisvalid, ");
else
appendPQExpBuffer(&buf, "true as indisvalid, ");
appendPQExpBuffer(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n ");
if (pset.sversion >= 90000)
appendPQExpBuffer(&buf,
2010-07-06 21:19:02 +02:00
"pg_catalog.pg_get_constraintdef(con.oid, true), "
"contype, condeferrable, condeferred");
else
appendPQExpBuffer(&buf,
"null AS constraintdef, null AS contype, "
2010-07-06 21:19:02 +02:00
"false AS condeferrable, false AS condeferred");
if (pset.sversion >= 80000)
appendPQExpBuffer(&buf, ", c2.reltablespace");
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n");
if (pset.sversion >= 90000)
appendPQExpBuffer(&buf,
" LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n");
appendPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
"ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname;",
2002-09-04 22:31:48 +02:00
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
if (tuples > 0)
{
printTableAddFooter(&cont, _("Indexes:"));
for (i = 0; i < tuples; i++)
{
/* untranslated index name */
printfPQExpBuffer(&buf, " \"%s\"",
PQgetvalue(result, i, 0));
/* If exclusion constraint, print the constraintdef */
if (strcmp(PQgetvalue(result, i, 7), "x") == 0)
{
appendPQExpBuffer(&buf, " %s",
PQgetvalue(result, i, 6));
}
else
{
const char *indexdef;
const char *usingpos;
/* Label as primary key or unique (but not both) */
if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
appendPQExpBuffer(&buf, " PRIMARY KEY,");
else if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
{
if (strcmp(PQgetvalue(result, i, 7), "u") == 0)
appendPQExpBuffer(&buf, " UNIQUE CONSTRAINT,");
else
appendPQExpBuffer(&buf, " UNIQUE,");
}
/* Everything after "USING" is echoed verbatim */
indexdef = PQgetvalue(result, i, 5);
usingpos = strstr(indexdef, " USING ");
if (usingpos)
indexdef = usingpos + 7;
appendPQExpBuffer(&buf, " %s", indexdef);
/* Need these for deferrable PK/UNIQUE indexes */
if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
appendPQExpBuffer(&buf, " DEFERRABLE");
if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
appendPQExpBuffer(&buf, " INITIALLY DEFERRED");
}
/* Add these for all cases */
if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
appendPQExpBuffer(&buf, " CLUSTER");
if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
appendPQExpBuffer(&buf, " INVALID");
printTableAddFooter(&cont, buf.data);
/* Print tablespace of the index on the same line */
if (pset.sversion >= 80000)
add_tablespace_footer(&cont, 'i',
2010-07-06 21:19:02 +02:00
atooid(PQgetvalue(result, i, 10)),
false);
}
}
PQclear(result);
}
/* print table (and column) check constraints */
if (tableinfo.checks)
{
printfPQExpBuffer(&buf,
"SELECT r.conname, "
"pg_catalog.pg_get_constraintdef(r.oid, true)\n"
2002-09-04 22:31:48 +02:00
"FROM pg_catalog.pg_constraint r\n"
"WHERE r.conrelid = '%s' AND r.contype = 'c'\nORDER BY 1;",
2002-09-04 22:31:48 +02:00
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
if (tuples > 0)
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
{
printTableAddFooter(&cont, _("Check constraints:"));
for (i = 0; i < tuples; i++)
{
/* untranslated contraint name and def */
printfPQExpBuffer(&buf, " \"%s\" %s",
PQgetvalue(result, i, 0),
PQgetvalue(result, i, 1));
printTableAddFooter(&cont, buf.data);
}
}
PQclear(result);
}
/* print foreign-key constraints (there are none if no triggers) */
if (tableinfo.hastriggers)
{
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT conname,\n"
2011-06-09 20:32:50 +02:00
" pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n"
"FROM pg_catalog.pg_constraint r\n"
"WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1;",
2002-09-04 22:31:48 +02:00
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
if (tuples > 0)
{
printTableAddFooter(&cont, _("Foreign-key constraints:"));
for (i = 0; i < tuples; i++)
{
/* untranslated constraint name and def */
printfPQExpBuffer(&buf, " \"%s\" %s",
PQgetvalue(result, i, 0),
PQgetvalue(result, i, 1));
printTableAddFooter(&cont, buf.data);
}
}
PQclear(result);
}
/* print incoming foreign-key references (none if no triggers) */
if (tableinfo.hastriggers)
{
printfPQExpBuffer(&buf,
"SELECT conname, conrelid::pg_catalog.regclass,\n"
" pg_catalog.pg_get_constraintdef(c.oid, true) as condef\n"
"FROM pg_catalog.pg_constraint c\n"
"WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
if (tuples > 0)
{
printTableAddFooter(&cont, _("Referenced by:"));
for (i = 0; i < tuples; i++)
{
printfPQExpBuffer(&buf, " TABLE \"%s\" CONSTRAINT \"%s\" %s",
PQgetvalue(result, i, 1),
PQgetvalue(result, i, 0),
PQgetvalue(result, i, 2));
printTableAddFooter(&cont, buf.data);
}
}
PQclear(result);
}
/* print rules */
if (tableinfo.hasrules)
2003-08-04 02:43:34 +02:00
{
if (pset.sversion >= 80300)
{
printfPQExpBuffer(&buf,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
"ev_enabled\n"
"FROM pg_catalog.pg_rewrite r\n"
"WHERE r.ev_class = '%s' ORDER BY 1;",
oid);
}
else
{
printfPQExpBuffer(&buf,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
"'O'::char AS ev_enabled\n"
"FROM pg_catalog.pg_rewrite r\n"
"WHERE r.ev_class = '%s' ORDER BY 1;",
oid);
}
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
if (tuples > 0)
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
{
bool have_heading;
int category;
for (category = 0; category < 4; category++)
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
{
have_heading = false;
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
for (i = 0; i < tuples; i++)
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
{
const char *ruledef;
bool list_rule = false;
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
switch (category)
{
case 0:
if (*PQgetvalue(result, i, 2) == 'O')
list_rule = true;
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
break;
case 1:
if (*PQgetvalue(result, i, 2) == 'D')
list_rule = true;
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
break;
case 2:
if (*PQgetvalue(result, i, 2) == 'A')
list_rule = true;
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
break;
case 3:
if (*PQgetvalue(result, i, 2) == 'R')
list_rule = true;
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
break;
}
if (!list_rule)
continue;
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
if (!have_heading)
{
switch (category)
{
case 0:
printfPQExpBuffer(&buf, _("Rules:"));
break;
case 1:
printfPQExpBuffer(&buf, _("Disabled rules:"));
break;
case 2:
printfPQExpBuffer(&buf, _("Rules firing always:"));
break;
case 3:
printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
break;
}
printTableAddFooter(&cont, buf.data);
have_heading = true;
}
/* Everything after "CREATE RULE" is echoed verbatim */
ruledef = PQgetvalue(result, i, 1);
ruledef += 12;
printfPQExpBuffer(&buf, " %s", ruledef);
printTableAddFooter(&cont, buf.data);
}
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
}
}
PQclear(result);
}
}
/*
* Print triggers next, if any (but only user-defined triggers). This
* could apply to either a table or a view.
*/
if (tableinfo.hastriggers)
{
2011-04-10 17:42:00 +02:00
PGresult *result;
int tuples;
2011-04-10 17:42:00 +02:00
printfPQExpBuffer(&buf,
"SELECT t.tgname, "
"pg_catalog.pg_get_triggerdef(t.oid%s), "
"t.tgenabled\n"
"FROM pg_catalog.pg_trigger t\n"
"WHERE t.tgrelid = '%s' AND ",
(pset.sversion >= 90000 ? ", true" : ""),
oid);
if (pset.sversion >= 90000)
appendPQExpBuffer(&buf, "NOT t.tgisinternal");
else if (pset.sversion >= 80300)
appendPQExpBuffer(&buf, "t.tgconstraint = 0");
else
appendPQExpBuffer(&buf,
"(NOT tgisconstraint "
" OR NOT EXISTS"
" (SELECT 1 FROM pg_catalog.pg_depend d "
" JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
" WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))");
appendPQExpBuffer(&buf, "\nORDER BY 1;");
2011-04-10 17:42:00 +02:00
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
2011-04-10 17:42:00 +02:00
if (tuples > 0)
{
bool have_heading;
int category;
/*
* split the output into 4 different categories. Enabled triggers,
* disabled triggers and the two special ALWAYS and REPLICA
* configurations.
*/
for (category = 0; category < 4; category++)
{
2011-04-10 17:42:00 +02:00
have_heading = false;
for (i = 0; i < tuples; i++)
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
{
2011-04-10 17:42:00 +02:00
bool list_trigger;
const char *tgdef;
const char *usingpos;
const char *tgenabled;
/*
* Check if this trigger falls into the current category
*/
tgenabled = PQgetvalue(result, i, 2);
list_trigger = false;
switch (category)
{
case 0:
if (*tgenabled == 'O' || *tgenabled == 't')
list_trigger = true;
break;
case 1:
if (*tgenabled == 'D' || *tgenabled == 'f')
list_trigger = true;
break;
case 2:
if (*tgenabled == 'A')
list_trigger = true;
break;
case 3:
if (*tgenabled == 'R')
list_trigger = true;
break;
}
if (list_trigger == false)
continue;
/* Print the category heading once */
if (have_heading == false)
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
{
switch (category)
{
case 0:
2011-04-10 17:42:00 +02:00
printfPQExpBuffer(&buf, _("Triggers:"));
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
break;
case 1:
2011-04-10 17:42:00 +02:00
printfPQExpBuffer(&buf, _("Disabled triggers:"));
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
break;
case 2:
2011-04-10 17:42:00 +02:00
printfPQExpBuffer(&buf, _("Triggers firing always:"));
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
break;
case 3:
2011-04-10 17:42:00 +02:00
printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
break;
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
}
printTableAddFooter(&cont, buf.data);
2011-04-10 17:42:00 +02:00
have_heading = true;
}
2011-04-10 17:42:00 +02:00
/* Everything after "TRIGGER" is echoed verbatim */
tgdef = PQgetvalue(result, i, 1);
usingpos = strstr(tgdef, " TRIGGER ");
if (usingpos)
tgdef = usingpos + 9;
printfPQExpBuffer(&buf, " %s", tgdef);
printTableAddFooter(&cont, buf.data);
Changes pg_trigger and extend pg_rewrite in order to allow triggers and rules to be defined with different, per session controllable, behaviors for replication purposes. This will allow replication systems like Slony-I and, as has been stated on pgsql-hackers, other products to control the firing mechanism of triggers and rewrite rules without modifying the system catalog directly. The firing mechanisms are controlled by a new superuser-only GUC variable, session_replication_role, together with a change to pg_trigger.tgenabled and a new column pg_rewrite.ev_enabled. Both columns are a single char data type now (tgenabled was a bool before). The possible values in these attributes are: 'O' - Trigger/Rule fires when session_replication_role is "origin" (default) or "local". This is the default behavior. 'D' - Trigger/Rule is disabled and fires never 'A' - Trigger/Rule fires always regardless of the setting of session_replication_role 'R' - Trigger/Rule fires when session_replication_role is "replica" The GUC variable can only be changed as long as the system does not have any cached query plans. This will prevent changing the session role and accidentally executing stored procedures or functions that have plans cached that expand to the wrong query set due to differences in the rule firing semantics. The SQL syntax for changing a triggers/rules firing semantics is ALTER TABLE <tabname> <when> TRIGGER|RULE <name>; <when> ::= ENABLE | ENABLE ALWAYS | ENABLE REPLICA | DISABLE psql's \d command as well as pg_dump are extended in a backward compatible fashion. Jan
2007-03-20 00:38:32 +01:00
}
}
2011-04-10 17:42:00 +02:00
}
PQclear(result);
}
/*
* Finish printing the footer information about a table.
*/
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
{
PGresult *result;
int tuples;
/* print foreign server name */
if (tableinfo.relkind == 'f')
{
char *ftoptions;
/* Footer information about foreign table */
printfPQExpBuffer(&buf,
"SELECT s.srvname,\n"
" array_to_string(ARRAY(SELECT "
" quote_ident(option_name) || ' ' || "
" quote_literal(option_value) FROM "
" pg_options_to_table(ftoptions)), ', ') "
"FROM pg_catalog.pg_foreign_table f,\n"
" pg_catalog.pg_foreign_server s\n"
"WHERE f.ftrelid = %s AND s.oid = f.ftserver;",
oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else if (PQntuples(result) != 1)
{
PQclear(result);
goto error_return;
}
/* Print server name */
printfPQExpBuffer(&buf, "Server: %s",
2011-04-10 17:42:00 +02:00
PQgetvalue(result, 0, 0));
printTableAddFooter(&cont, buf.data);
/* Print per-table FDW options, if any */
ftoptions = PQgetvalue(result, 0, 1);
if (ftoptions && ftoptions[0] != '\0')
{
printfPQExpBuffer(&buf, "FDW Options: (%s)", ftoptions);
printTableAddFooter(&cont, buf.data);
}
PQclear(result);
}
/* print inherited tables */
printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno;", oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
for (i = 0; i < tuples; i++)
{
2007-11-15 22:14:46 +01:00
const char *s = _("Inherits");
if (i == 0)
printfPQExpBuffer(&buf, "%s: %s", s, PQgetvalue(result, i, 0));
else
printfPQExpBuffer(&buf, "%*s %s", (int) strlen(s), "", PQgetvalue(result, i, 0));
if (i < tuples - 1)
appendPQExpBuffer(&buf, ",");
printTableAddFooter(&cont, buf.data);
}
PQclear(result);
/* print child tables */
if (pset.sversion >= 80300)
printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid);
else
printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.relname;", oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
if (!verbose)
{
/* print the number of child tables, if any */
if (tuples > 0)
{
printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
printTableAddFooter(&cont, buf.data);
}
}
else
{
/* display the list of child tables */
const char *ct = _("Child tables");
for (i = 0; i < tuples; i++)
{
if (i == 0)
printfPQExpBuffer(&buf, "%s: %s",
ct, PQgetvalue(result, i, 0));
else
printfPQExpBuffer(&buf, "%*s %s",
(int) strlen(ct), "",
PQgetvalue(result, i, 0));
if (i < tuples - 1)
appendPQExpBuffer(&buf, ",");
printTableAddFooter(&cont, buf.data);
}
}
PQclear(result);
/* Table type */
if (tableinfo.reloftype)
{
printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
printTableAddFooter(&cont, buf.data);
}
/* OIDs and options */
if (verbose)
{
2007-11-15 22:14:46 +01:00
const char *s = _("Has OIDs");
2004-08-29 07:07:03 +02:00
printfPQExpBuffer(&buf, "%s: %s", s,
(tableinfo.hasoids ? _("yes") : _("no")));
printTableAddFooter(&cont, buf.data);
/* print reloptions */
if (pset.sversion >= 80200)
{
if (tableinfo.reloptions && tableinfo.reloptions[0] != '\0')
{
const char *t = _("Options");
printfPQExpBuffer(&buf, "%s: %s", t,
tableinfo.reloptions);
printTableAddFooter(&cont, buf.data);
}
}
}
add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
true);
}
printTable(&cont, pset.queryFout, pset.logfile);
printTableCleanup(&cont);
retval = true;
error_return:
1999-11-05 00:14:30 +01:00
/* clean up */
if (printTableInitialized)
printTableCleanup(&cont);
termPQExpBuffer(&buf);
termPQExpBuffer(&title);
termPQExpBuffer(&tmpbuf);
if (seq_values)
{
for (ptr = seq_values; *ptr; ptr++)
free(*ptr);
free(seq_values);
}
if (modifiers)
{
for (ptr = modifiers; *ptr; ptr++)
free(*ptr);
free(modifiers);
}
if (view_def)
free(view_def);
if (res)
PQclear(res);
return retval;
}
2005-10-15 04:49:52 +02:00
/*
* Add a tablespace description to a footer. If 'newline' is true, it is added
* in a new line; otherwise it's appended to the current value of the last
* footer.
*/
static void
add_tablespace_footer(printTableContent *const cont, char relkind,
Oid tablespace, const bool newline)
{
/* relkinds for which we support tablespaces */
2004-08-29 07:07:03 +02:00
if (relkind == 'r' || relkind == 'i')
{
/*
2005-10-15 04:49:52 +02:00
* We ignore the database default tablespace so that users not using
* tablespaces don't need to know about them. This case also covers
* pre-8.0 servers, for which tablespace will always be 0.
*/
2004-08-29 07:07:03 +02:00
if (tablespace != 0)
{
PGresult *result = NULL;
PQExpBufferData buf;
2004-08-29 07:07:03 +02:00
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT spcname FROM pg_catalog.pg_tablespace\n"
"WHERE oid = '%u';", tablespace);
result = PSQLexec(buf.data, false);
if (!result)
return;
/* Should always be the case, but.... */
if (PQntuples(result) > 0)
{
if (newline)
{
/* Add the tablespace as a new footer */
printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
PQgetvalue(result, 0, 0));
printTableAddFooter(cont, buf.data);
}
else
{
/* Append the tablespace to the latest footer */
printfPQExpBuffer(&buf, "%s", cont->footer->data);
/*
* translator: before this string there's an index
* description like '"foo_pkey" PRIMARY KEY, btree (a)'
*/
appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
PQgetvalue(result, 0, 0));
printTableSetFooter(cont, buf.data);
}
}
PQclear(result);
termPQExpBuffer(&buf);
}
}
}
/*
* \du or \dg
*
* Describes roles. Any schema portion of the pattern is ignored.
*/
bool
describeRoles(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printTableContent cont;
printTableOpt myopt = pset.popt.topt;
int ncols = 3;
int nrows = 0;
int i;
int conns;
const char align = 'l';
char **attr;
initPQExpBuffer(&buf);
if (pset.sversion >= 80100)
{
printfPQExpBuffer(&buf,
"SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
" r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
" r.rolconnlimit,\n"
" ARRAY(SELECT b.rolname\n"
" FROM pg_catalog.pg_auth_members m\n"
" JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)\n"
" WHERE m.member = r.oid) as memberof");
if (verbose && pset.sversion >= 80200)
{
appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
ncols++;
}
if (pset.sversion >= 90100)
{
2011-04-10 17:42:00 +02:00
appendPQExpBufferStr(&buf, "\n, r.rolreplication");
}
appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL, "r.rolname", NULL, NULL);
}
else
{
printfPQExpBuffer(&buf,
"SELECT u.usename AS rolname,\n"
" u.usesuper AS rolsuper,\n"
" true AS rolinherit, false AS rolcreaterole,\n"
" u.usecreatedb AS rolcreatedb, true AS rolcanlogin,\n"
" -1 AS rolconnlimit,\n"
" ARRAY(SELECT g.groname FROM pg_catalog.pg_group g WHERE u.usesysid = ANY(g.grolist)) as memberof"
"\nFROM pg_catalog.pg_user u\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL, "u.usename", NULL, NULL);
}
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
if (!res)
return false;
nrows = PQntuples(res);
attr = pg_malloc_zero((nrows + 1) * sizeof(*attr));
printTableInit(&cont, &myopt, _("List of roles"), ncols, nrows);
printTableAddHeader(&cont, gettext_noop("Role name"), true, align);
printTableAddHeader(&cont, gettext_noop("Attributes"), true, align);
printTableAddHeader(&cont, gettext_noop("Member of"), true, align);
if (verbose && pset.sversion >= 80200)
printTableAddHeader(&cont, gettext_noop("Description"), true, align);
for (i = 0; i < nrows; i++)
{
printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
resetPQExpBuffer(&buf);
if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
add_role_attribute(&buf, _("Superuser"));
if (strcmp(PQgetvalue(res, i, 2), "t") != 0)
add_role_attribute(&buf, _("No inheritance"));
if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
add_role_attribute(&buf, _("Create role"));
if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
add_role_attribute(&buf, _("Create DB"));
if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
add_role_attribute(&buf, _("Cannot login"));
if (pset.sversion >= 90100)
if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
add_role_attribute(&buf, _("Replication"));
conns = atoi(PQgetvalue(res, i, 6));
if (conns >= 0)
{
if (buf.len > 0)
appendPQExpBufferStr(&buf, "\n");
if (conns == 0)
appendPQExpBuffer(&buf, _("No connections"));
else
appendPQExpBuffer(&buf, ngettext("%d connection",
"%d connections",
conns),
conns);
}
attr[i] = pg_strdup(buf.data);
printTableAddCell(&cont, attr[i], false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
if (verbose && pset.sversion >= 80200)
printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
}
termPQExpBuffer(&buf);
printTable(&cont, pset.queryFout, pset.logfile);
printTableCleanup(&cont);
for (i = 0; i < nrows; i++)
free(attr[i]);
free(attr);
PQclear(res);
return true;
}
static void
add_role_attribute(PQExpBuffer buf, const char *const str)
{
if (buf->len > 0)
appendPQExpBufferStr(buf, ", ");
appendPQExpBufferStr(buf, str);
}
/*
* \drds
*/
bool
listDbRoleSettings(const char *pattern, const char *pattern2)
{
2010-02-26 03:01:40 +01:00
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
initPQExpBuffer(&buf);
if (pset.sversion >= 90000)
{
2010-02-26 03:01:40 +01:00
bool havewhere;
printfPQExpBuffer(&buf, "SELECT rolname AS role, datname AS database,\n"
2010-02-26 03:01:40 +01:00
"pg_catalog.array_to_string(setconfig, E'\\n') AS settings\n"
"FROM pg_db_role_setting AS s\n"
2010-02-26 03:01:40 +01:00
"LEFT JOIN pg_database ON pg_database.oid = setdatabase\n"
"LEFT JOIN pg_roles ON pg_roles.oid = setrole\n");
havewhere = processSQLNamePattern(pset.db, &buf, pattern, false, false,
2010-02-26 03:01:40 +01:00
NULL, "pg_roles.rolname", NULL, NULL);
processSQLNamePattern(pset.db, &buf, pattern2, havewhere, false,
NULL, "pg_database.datname", NULL, NULL);
appendPQExpBufferStr(&buf, "ORDER BY role, database;");
}
else
{
fprintf(pset.queryFout,
2010-02-26 03:01:40 +01:00
_("No per-database role settings support in this server version.\n"));
return false;
}
res = PSQLexec(buf.data, false);
if (!res)
return false;
if (PQntuples(res) == 0 && !pset.quiet)
{
if (pattern)
fprintf(pset.queryFout, _("No matching settings found.\n"));
else
fprintf(pset.queryFout, _("No settings found.\n"));
}
else
{
myopt.nullPrint = NULL;
myopt.title = _("List of settings");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
}
PQclear(res);
resetPQExpBuffer(&buf);
return true;
}
/*
* listTables()
*
2009-04-04 02:44:30 +02:00
* handler for \dt, \di, etc.
*
* tabtypes is an array of characters, specifying what info is desired:
* t - tables
* i - indexes
* v - views
* s - sequences
* E - foreign table (Note: different from 'f', the relkind value)
* (any order of the above is fine)
* If tabtypes is empty, we default to \dtvsE.
*/
bool
listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
{
bool showTables = strchr(tabtypes, 't') != NULL;
bool showIndexes = strchr(tabtypes, 'i') != NULL;
bool showViews = strchr(tabtypes, 'v') != NULL;
bool showSeq = strchr(tabtypes, 's') != NULL;
bool showForeign = strchr(tabtypes, 'E') != NULL;
1999-11-05 00:14:30 +01:00
PQExpBufferData buf;
1999-11-05 00:14:30 +01:00
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, true, false, false, false, false};
1999-11-05 00:14:30 +01:00
if (!(showTables || showIndexes || showViews || showSeq || showForeign))
showTables = showViews = showSeq = showForeign = true;
initPQExpBuffer(&buf);
/*
* Note: as of Pg 8.2, we no longer use relkind 's', but we keep it here
* for backwards compatibility.
*/
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT n.nspname as \"%s\",\n"
" c.relname as \"%s\",\n"
" CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
" pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("table"),
gettext_noop("view"),
gettext_noop("index"),
gettext_noop("sequence"),
gettext_noop("special"),
gettext_noop("foreign table"),
gettext_noop("Type"),
gettext_noop("Owner"));
if (showIndexes)
appendPQExpBuffer(&buf,
",\n c2.relname as \"%s\"",
gettext_noop("Table"));
if (verbose)
{
/*
2011-04-10 17:42:00 +02:00
* As of PostgreSQL 9.0, use pg_table_size() to show a more acurate
* size of a table, including FSM, VM and TOAST tables.
*/
if (pset.sversion >= 90000)
appendPQExpBuffer(&buf,
",\n pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\"",
gettext_noop("Size"));
else if (pset.sversion >= 80100)
appendPQExpBuffer(&buf,
",\n pg_catalog.pg_size_pretty(pg_catalog.pg_relation_size(c.oid)) as \"%s\"",
gettext_noop("Size"));
appendPQExpBuffer(&buf,
2005-10-15 04:49:52 +02:00
",\n pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
gettext_noop("Description"));
}
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_class c"
2005-10-15 04:49:52 +02:00
"\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace");
2002-09-04 22:31:48 +02:00
if (showIndexes)
appendPQExpBuffer(&buf,
2005-10-15 04:49:52 +02:00
"\n LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
"\n LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid");
appendPQExpBuffer(&buf, "\nWHERE c.relkind IN (");
if (showTables)
appendPQExpBuffer(&buf, "'r',");
1999-11-05 00:14:30 +01:00
if (showViews)
appendPQExpBuffer(&buf, "'v',");
if (showIndexes)
appendPQExpBuffer(&buf, "'i',");
if (showSeq)
appendPQExpBuffer(&buf, "'S',");
if (showSystem || pattern)
appendPQExpBuffer(&buf, "'s',"); /* was RELKIND_SPECIAL in <=
* 8.1 */
if (showForeign)
appendPQExpBuffer(&buf, "'f',");
2002-09-04 22:31:48 +02:00
appendPQExpBuffer(&buf, "''"); /* dummy */
appendPQExpBuffer(&buf, ")\n");
1999-11-05 00:14:30 +01:00
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
/*
* TOAST objects are suppressed unconditionally. Since we don't provide
* any way to select relkind 't' above, we would never show toast tables
* in any case; it seems a bit confusing to allow their indexes to be
* shown. Use plain \d if you really need to look at a TOAST table/index.
*/
appendPQExpBuffer(&buf, " AND n.nspname !~ '^pg_toast'\n");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "c.relname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1,2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
1999-11-05 00:14:30 +01:00
if (!res)
return false;
if (PQntuples(res) == 0 && !pset.quiet)
{
if (pattern)
fprintf(pset.queryFout, _("No matching relations found.\n"));
else
fprintf(pset.queryFout, _("No relations found.\n"));
}
1999-11-05 00:14:30 +01:00
else
{
myopt.nullPrint = NULL;
myopt.title = _("List of relations");
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
1999-11-05 00:14:30 +01:00
}
1999-11-05 00:14:30 +01:00
PQclear(res);
return true;
}
/*
* \dL
*
* Describes languages.
*/
bool
listLanguages(const char *pattern, bool verbose, bool showSystem)
{
PQExpBufferData buf;
2011-04-10 17:42:00 +02:00
PGresult *res;
printQueryOpt myopt = pset.popt;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT l.lanname AS \"%s\",\n",
gettext_noop("Name"));
if (pset.sversion >= 80300)
2011-04-10 17:42:00 +02:00
appendPQExpBuffer(&buf,
" pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n",
gettext_noop("Owner"));
appendPQExpBuffer(&buf,
" l.lanpltrusted AS \"%s\"",
gettext_noop("Trusted"));
if (verbose)
{
2011-04-10 17:42:00 +02:00
appendPQExpBuffer(&buf,
",\n NOT l.lanispl AS \"%s\",\n"
" l.lanplcallfoid::regprocedure AS \"%s\",\n"
" l.lanvalidator::regprocedure AS \"%s\",\n ",
gettext_noop("Internal Language"),
gettext_noop("Call Handler"),
gettext_noop("Validator"));
if (pset.sversion >= 90000)
appendPQExpBuffer(&buf, "l.laninline::regprocedure AS \"%s\",\n ",
gettext_noop("Inline Handler"));
printACLColumn(&buf, "l.lanacl");
}
appendPQExpBuffer(&buf,
",\n d.description AS \"%s\""
"\nFROM pg_catalog.pg_language l\n"
"LEFT JOIN pg_catalog.pg_description d\n"
" ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
" AND d.objsubid = 0\n",
gettext_noop("Description"));
if (pattern)
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL, "l.lanname", NULL, NULL);
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, "WHERE l.lanplcallfoid != 0\n");
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of languages");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dD
*
* Describes domains.
*/
bool
listDomains(const char *pattern, bool verbose, bool showSystem)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
2002-09-04 22:31:48 +02:00
"SELECT n.nspname as \"%s\",\n"
" t.typname as \"%s\",\n"
2011-06-09 20:32:50 +02:00
" pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
" TRIM(LEADING\n",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Type"));
if (pset.sversion >= 90100)
appendPQExpBuffer(&buf,
" COALESCE((SELECT ' collate ' || c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
" WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation), '') ||\n");
appendPQExpBuffer(&buf,
2011-06-09 20:32:50 +02:00
" CASE WHEN t.typnotnull THEN ' not null' ELSE '' END ||\n"
" CASE WHEN t.typdefault IS NOT NULL THEN ' default ' || t.typdefault ELSE '' END\n"
" ) as \"%s\",\n"
" pg_catalog.array_to_string(ARRAY(\n"
" SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid\n"
" ), ' ') as \"%s\"",
gettext_noop("Modifier"),
gettext_noop("Check"));
if (verbose)
appendPQExpBuffer(&buf,
",\n d.description as \"%s\"",
gettext_noop("Description"));
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_type t\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
if (verbose)
appendPQExpBuffer(&buf,
" LEFT JOIN pg_catalog.pg_description d "
"ON d.classoid = t.tableoid AND d.objoid = t.oid "
"AND d.objsubid = 0\n");
appendPQExpBuffer(&buf, "WHERE t.typtype = 'd'\n");
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "t.typname", NULL,
"pg_catalog.pg_type_is_visible(t.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of domains");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dc
*
* Describes conversions.
*/
bool
listConversions(const char *pattern, bool verbose, bool showSystem)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, false, false, true};
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT n.nspname AS \"%s\",\n"
" c.conname AS \"%s\",\n"
2005-10-15 04:49:52 +02:00
" pg_catalog.pg_encoding_to_char(c.conforencoding) AS \"%s\",\n"
" pg_catalog.pg_encoding_to_char(c.contoencoding) AS \"%s\",\n"
" CASE WHEN c.condefault THEN '%s'\n"
" ELSE '%s' END AS \"%s\"",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Source"),
gettext_noop("Destination"),
gettext_noop("yes"), gettext_noop("no"),
gettext_noop("Default?"));
if (verbose)
appendPQExpBuffer(&buf,
",\n d.description AS \"%s\"",
gettext_noop("Description"));
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_conversion c\n"
" JOIN pg_catalog.pg_namespace n "
"ON n.oid = c.connamespace\n");
if (verbose)
appendPQExpBuffer(&buf,
"LEFT JOIN pg_catalog.pg_description d "
"ON d.classoid = c.tableoid\n"
" AND d.objoid = c.oid "
"AND d.objsubid = 0\n");
appendPQExpBuffer(&buf, "WHERE true\n");
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "c.conname", NULL,
"pg_catalog.pg_conversion_is_visible(c.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of conversions");
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dC
*
* Describes casts.
*/
bool
listCasts(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, false, true};
initPQExpBuffer(&buf);
/*
* We need a left join to pg_proc for binary casts; the others are just
* paranoia. Also note that we don't attempt to localize '(binary
* coercible)', because there's too much risk of gettext translating a
* function name that happens to match some string in the PO database.
*/
printfPQExpBuffer(&buf,
2005-10-15 04:49:52 +02:00
"SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
" pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n"
" CASE WHEN castfunc = 0 THEN '(binary coercible)'\n"
" ELSE p.proname\n"
" END as \"%s\",\n"
" CASE WHEN c.castcontext = 'e' THEN '%s'\n"
" WHEN c.castcontext = 'a' THEN '%s'\n"
" ELSE '%s'\n"
" END as \"%s\"",
gettext_noop("Source type"),
gettext_noop("Target type"),
gettext_noop("Function"),
gettext_noop("no"),
gettext_noop("in assignment"),
gettext_noop("yes"),
gettext_noop("Implicit?"));
if (verbose)
appendPQExpBuffer(&buf,
",\n d.description AS \"%s\"\n",
gettext_noop("Description"));
appendPQExpBuffer(&buf,
2005-10-15 04:49:52 +02:00
"FROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p\n"
" ON c.castfunc = p.oid\n"
" LEFT JOIN pg_catalog.pg_type ts\n"
" ON c.castsource = ts.oid\n"
" LEFT JOIN pg_catalog.pg_namespace ns\n"
" ON ns.oid = ts.typnamespace\n"
" LEFT JOIN pg_catalog.pg_type tt\n"
" ON c.casttarget = tt.oid\n"
" LEFT JOIN pg_catalog.pg_namespace nt\n"
" ON nt.oid = tt.typnamespace\n");
if (verbose)
appendPQExpBuffer(&buf,
" LEFT JOIN pg_catalog.pg_description d\n"
" ON d.classoid = c.tableoid AND d.objoid = "
"c.oid AND d.objsubid = 0\n");
appendPQExpBuffer(&buf, "WHERE ( (true");
/*
* Match name pattern against either internal or external name of either
* castsource or casttarget
*/
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"ns.nspname", "ts.typname",
"pg_catalog.format_type(ts.oid, NULL)",
"pg_catalog.pg_type_is_visible(ts.oid)");
appendPQExpBuffer(&buf, ") OR (true");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"nt.nspname", "tt.typname",
"pg_catalog.format_type(tt.oid, NULL)",
"pg_catalog.pg_type_is_visible(tt.oid)");
appendPQExpBuffer(&buf, ") )\nORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of casts");
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dO
*
* Describes collations.
*/
bool
listCollations(const char *pattern, bool verbose, bool showSystem)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {false, false, false, false, false};
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT n.nspname AS \"%s\",\n"
" c.collname AS \"%s\",\n"
" c.collcollate AS \"%s\",\n"
" c.collctype AS \"%s\"",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Collate"),
gettext_noop("Ctype"));
if (verbose)
appendPQExpBuffer(&buf,
2011-04-10 17:42:00 +02:00
",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
gettext_noop("Description"));
appendPQExpBuffer(&buf,
2011-04-10 17:42:00 +02:00
"\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
"WHERE n.oid = c.collnamespace\n");
if (!showSystem && !pattern)
appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n"
" AND n.nspname <> 'information_schema'\n");
/*
* Hide collations that aren't usable in the current database's encoding.
* If you think to change this, note that pg_collation_is_visible rejects
* unusable collations, so you will need to hack name pattern processing
* somehow to avoid inconsistent behavior.
*/
appendPQExpBuffer(&buf, " AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "c.collname", NULL,
"pg_catalog.pg_collation_is_visible(c.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of collations");
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dn
*
* Describes schemas (namespaces)
*/
bool
listSchemas(const char *pattern, bool verbose, bool showSystem)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
2004-08-29 07:07:03 +02:00
"SELECT n.nspname AS \"%s\",\n"
" pg_catalog.pg_get_userbyid(n.nspowner) AS \"%s\"",
gettext_noop("Name"),
gettext_noop("Owner"));
2004-08-29 07:07:03 +02:00
if (verbose)
{
appendPQExpBuffer(&buf, ",\n ");
printACLColumn(&buf, "n.nspacl");
appendPQExpBuffer(&buf,
",\n pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
gettext_noop("Description"));
}
2004-08-29 07:07:03 +02:00
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_namespace n\n");
if (!showSystem && !pattern)
appendPQExpBuffer(&buf,
2011-04-10 17:42:00 +02:00
"WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
processSQLNamePattern(pset.db, &buf, pattern,
!showSystem && !pattern, false,
NULL, "n.nspname", NULL,
NULL);
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of schemas");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dFp
* list text search parsers
*/
bool
listTSParsers(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 80300)
{
fprintf(stderr, _("The server (version %d.%d) does not support full text search.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
if (verbose)
return listTSParsersVerbose(pattern);
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT \n"
" n.nspname as \"%s\",\n"
" p.prsname as \"%s\",\n"
" pg_catalog.obj_description(p.oid, 'pg_ts_parser') as \"%s\"\n"
"FROM pg_catalog.pg_ts_parser p \n"
2007-11-15 22:14:46 +01:00
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Description")
);
processSQLNamePattern(pset.db, &buf, pattern, false, false,
"n.nspname", "p.prsname", NULL,
"pg_catalog.pg_ts_parser_is_visible(p.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of text search parsers");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* full description of parsers
*/
static bool
listTSParsersVerbose(const char *pattern)
{
PQExpBufferData buf;
PGresult *res;
int i;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT p.oid, \n"
" n.nspname, \n"
" p.prsname \n"
"FROM pg_catalog.pg_ts_parser p\n"
2007-11-15 22:14:46 +01:00
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace\n"
);
processSQLNamePattern(pset.db, &buf, pattern, false, false,
"n.nspname", "p.prsname", NULL,
"pg_catalog.pg_ts_parser_is_visible(p.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
if (PQntuples(res) == 0)
{
if (!pset.quiet)
fprintf(stderr, _("Did not find any text search parser named \"%s\".\n"),
pattern);
PQclear(res);
return false;
}
for (i = 0; i < PQntuples(res); i++)
{
const char *oid;
const char *nspname = NULL;
const char *prsname;
oid = PQgetvalue(res, i, 0);
if (!PQgetisnull(res, i, 1))
nspname = PQgetvalue(res, i, 1);
prsname = PQgetvalue(res, i, 2);
if (!describeOneTSParser(oid, nspname, prsname))
{
PQclear(res);
return false;
}
if (cancel_pressed)
{
PQclear(res);
return false;
}
}
PQclear(res);
return true;
}
static bool
describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
{
PQExpBufferData buf;
PGresult *res;
char title[1024];
printQueryOpt myopt = pset.popt;
static const bool translate_columns[] = {true, false, false};
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT '%s' AS \"%s\", \n"
" p.prsstart::pg_catalog.regproc AS \"%s\", \n"
2007-11-15 22:14:46 +01:00
" pg_catalog.obj_description(p.prsstart, 'pg_proc') as \"%s\" \n"
" FROM pg_catalog.pg_ts_parser p \n"
" WHERE p.oid = '%s' \n"
"UNION ALL \n"
"SELECT '%s', \n"
" p.prstoken::pg_catalog.regproc, \n"
2007-11-15 22:14:46 +01:00
" pg_catalog.obj_description(p.prstoken, 'pg_proc') \n"
" FROM pg_catalog.pg_ts_parser p \n"
" WHERE p.oid = '%s' \n"
"UNION ALL \n"
"SELECT '%s', \n"
" p.prsend::pg_catalog.regproc, \n"
" pg_catalog.obj_description(p.prsend, 'pg_proc') \n"
" FROM pg_catalog.pg_ts_parser p \n"
" WHERE p.oid = '%s' \n"
"UNION ALL \n"
"SELECT '%s', \n"
" p.prsheadline::pg_catalog.regproc, \n"
2007-11-15 22:14:46 +01:00
" pg_catalog.obj_description(p.prsheadline, 'pg_proc') \n"
" FROM pg_catalog.pg_ts_parser p \n"
" WHERE p.oid = '%s' \n"
"UNION ALL \n"
"SELECT '%s', \n"
" p.prslextype::pg_catalog.regproc, \n"
2007-11-15 22:14:46 +01:00
" pg_catalog.obj_description(p.prslextype, 'pg_proc') \n"
" FROM pg_catalog.pg_ts_parser p \n"
" WHERE p.oid = '%s';",
gettext_noop("Start parse"),
gettext_noop("Method"),
gettext_noop("Function"),
gettext_noop("Description"),
oid,
gettext_noop("Get next token"),
oid,
gettext_noop("End parse"),
oid,
gettext_noop("Get headline"),
oid,
gettext_noop("Get token types"),
oid);
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
if (nspname)
sprintf(title, _("Text search parser \"%s.%s\""), nspname, prsname);
else
sprintf(title, _("Text search parser \"%s\""), prsname);
myopt.title = title;
myopt.footers = NULL;
myopt.default_footer = false;
myopt.translate_header = true;
myopt.translate_columns = translate_columns;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT t.alias as \"%s\", \n"
" t.description as \"%s\" \n"
2007-11-15 22:14:46 +01:00
"FROM pg_catalog.ts_token_type( '%s'::pg_catalog.oid ) as t \n"
"ORDER BY 1;",
gettext_noop("Token name"),
gettext_noop("Description"),
oid);
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
if (nspname)
sprintf(title, _("Token types for parser \"%s.%s\""), nspname, prsname);
else
sprintf(title, _("Token types for parser \"%s\""), prsname);
myopt.title = title;
myopt.footers = NULL;
myopt.default_footer = true;
myopt.translate_header = true;
myopt.translate_columns = NULL;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dFd
* list text search dictionaries
*/
bool
listTSDictionaries(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 80300)
{
fprintf(stderr, _("The server (version %d.%d) does not support full text search.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT \n"
" n.nspname as \"%s\",\n"
" d.dictname as \"%s\",\n",
gettext_noop("Schema"),
gettext_noop("Name"));
if (verbose)
{
appendPQExpBuffer(&buf,
" ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM \n"
" pg_catalog.pg_ts_template t \n"
" LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace \n"
" WHERE d.dicttemplate = t.oid ) AS \"%s\", \n"
" d.dictinitoption as \"%s\", \n",
gettext_noop("Template"),
gettext_noop("Init options"));
}
appendPQExpBuffer(&buf,
" pg_catalog.obj_description(d.oid, 'pg_ts_dict') as \"%s\"\n",
gettext_noop("Description"));
appendPQExpBuffer(&buf, "FROM pg_catalog.pg_ts_dict d\n"
2007-11-15 22:14:46 +01:00
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
"n.nspname", "d.dictname", NULL,
"pg_catalog.pg_ts_dict_is_visible(d.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of text search dictionaries");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dFt
* list text search templates
*/
bool
listTSTemplates(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 80300)
{
fprintf(stderr, _("The server (version %d.%d) does not support full text search.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
if (verbose)
printfPQExpBuffer(&buf,
"SELECT \n"
" n.nspname AS \"%s\",\n"
" t.tmplname AS \"%s\",\n"
" t.tmplinit::pg_catalog.regproc AS \"%s\",\n"
" t.tmpllexize::pg_catalog.regproc AS \"%s\",\n"
2007-11-15 22:14:46 +01:00
" pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Init"),
gettext_noop("Lexize"),
gettext_noop("Description"));
else
printfPQExpBuffer(&buf,
"SELECT \n"
" n.nspname AS \"%s\",\n"
" t.tmplname AS \"%s\",\n"
2007-11-15 22:14:46 +01:00
" pg_catalog.obj_description(t.oid, 'pg_ts_template') AS \"%s\"\n",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Description"));
appendPQExpBuffer(&buf, "FROM pg_catalog.pg_ts_template t\n"
2007-11-15 22:14:46 +01:00
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
"n.nspname", "t.tmplname", NULL,
"pg_catalog.pg_ts_template_is_visible(t.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of text search templates");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dF
* list text search configurations
*/
bool
listTSConfigs(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 80300)
{
fprintf(stderr, _("The server (version %d.%d) does not support full text search.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
if (verbose)
return listTSConfigsVerbose(pattern);
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT \n"
" n.nspname as \"%s\",\n"
" c.cfgname as \"%s\",\n"
" pg_catalog.obj_description(c.oid, 'pg_ts_config') as \"%s\"\n"
"FROM pg_catalog.pg_ts_config c\n"
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace \n",
gettext_noop("Schema"),
gettext_noop("Name"),
gettext_noop("Description")
);
processSQLNamePattern(pset.db, &buf, pattern, false, false,
"n.nspname", "c.cfgname", NULL,
"pg_catalog.pg_ts_config_is_visible(c.oid)");
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of text search configurations");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
static bool
listTSConfigsVerbose(const char *pattern)
{
PQExpBufferData buf;
PGresult *res;
int i;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT c.oid, c.cfgname,\n"
" n.nspname, \n"
" p.prsname, \n"
" np.nspname as pnspname \n"
"FROM pg_catalog.pg_ts_config c \n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace, \n"
" pg_catalog.pg_ts_parser p \n"
2007-11-15 22:14:46 +01:00
" LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.prsnamespace \n"
"WHERE p.oid = c.cfgparser\n"
);
processSQLNamePattern(pset.db, &buf, pattern, true, false,
"n.nspname", "c.cfgname", NULL,
"pg_catalog.pg_ts_config_is_visible(c.oid)");
appendPQExpBuffer(&buf, "ORDER BY 3, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
if (PQntuples(res) == 0)
{
if (!pset.quiet)
fprintf(stderr, _("Did not find any text search configuration named \"%s\".\n"),
pattern);
PQclear(res);
return false;
}
for (i = 0; i < PQntuples(res); i++)
{
const char *oid;
const char *cfgname;
const char *nspname = NULL;
const char *prsname;
const char *pnspname = NULL;
oid = PQgetvalue(res, i, 0);
cfgname = PQgetvalue(res, i, 1);
if (!PQgetisnull(res, i, 2))
nspname = PQgetvalue(res, i, 2);
prsname = PQgetvalue(res, i, 3);
if (!PQgetisnull(res, i, 4))
pnspname = PQgetvalue(res, i, 4);
if (!describeOneTSConfig(oid, nspname, cfgname, pnspname, prsname))
{
PQclear(res);
return false;
}
if (cancel_pressed)
{
PQclear(res);
return false;
}
}
PQclear(res);
return true;
}
static bool
describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
const char *pnspname, const char *prsname)
{
PQExpBufferData buf,
title;
PGresult *res;
printQueryOpt myopt = pset.popt;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT \n"
" ( SELECT t.alias FROM \n"
" pg_catalog.ts_token_type(c.cfgparser) AS t \n"
" WHERE t.tokid = m.maptokentype ) AS \"%s\", \n"
" pg_catalog.btrim( \n"
2007-11-15 22:14:46 +01:00
" ARRAY( SELECT mm.mapdict::pg_catalog.regdictionary \n"
" FROM pg_catalog.pg_ts_config_map AS mm \n"
" WHERE mm.mapcfg = m.mapcfg AND mm.maptokentype = m.maptokentype \n"
" ORDER BY mapcfg, maptokentype, mapseqno \n"
" ) :: pg_catalog.text , \n"
" '{}') AS \"%s\" \n"
2007-11-15 22:14:46 +01:00
"FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m \n"
"WHERE c.oid = '%s' AND m.mapcfg = c.oid \n"
"GROUP BY m.mapcfg, m.maptokentype, c.cfgparser \n"
"ORDER BY 1;",
gettext_noop("Token"),
gettext_noop("Dictionaries"),
oid);
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
initPQExpBuffer(&title);
if (nspname)
appendPQExpBuffer(&title, _("Text search configuration \"%s.%s\""),
nspname, cfgname);
else
appendPQExpBuffer(&title, _("Text search configuration \"%s\""),
cfgname);
if (pnspname)
appendPQExpBuffer(&title, _("\nParser: \"%s.%s\""),
pnspname, prsname);
else
appendPQExpBuffer(&title, _("\nParser: \"%s\""),
prsname);
myopt.nullPrint = NULL;
myopt.title = title.data;
myopt.footers = NULL;
myopt.default_footer = false;
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
termPQExpBuffer(&title);
PQclear(res);
return true;
}
/*
* \dew
*
* Describes foreign-data wrappers
*/
bool
listForeignDataWrappers(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 80400)
{
fprintf(stderr, _("The server (version %d.%d) does not support foreign-data wrappers.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT fdw.fdwname AS \"%s\",\n"
" pg_catalog.pg_get_userbyid(fdw.fdwowner) AS \"%s\",\n",
gettext_noop("Name"),
gettext_noop("Owner"));
if (pset.sversion >= 90100)
appendPQExpBuffer(&buf,
" fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n",
gettext_noop("Handler"));
appendPQExpBuffer(&buf,
" fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
gettext_noop("Validator"));
if (verbose)
{
appendPQExpBuffer(&buf, ",\n ");
printACLColumn(&buf, "fdwacl");
appendPQExpBuffer(&buf,
",\n CASE WHEN fdwoptions IS NULL THEN '' ELSE "
" '(' || array_to_string(ARRAY(SELECT "
" quote_ident(option_name) || ' ' || "
" quote_literal(option_value) FROM "
" pg_options_to_table(fdwoptions)), ', ') || ')' "
" END AS \"%s\"",
gettext_noop("FDW Options"));
if (pset.sversion >= 90100)
appendPQExpBuffer(&buf,
",\n d.description AS \"%s\" ",
gettext_noop("Description"));
}
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
if (verbose && pset.sversion >= 90100)
appendPQExpBuffer(&buf,
"LEFT JOIN pg_catalog.pg_description d\n"
" ON d.classoid = fdw.tableoid "
"AND d.objoid = fdw.oid AND d.objsubid = 0\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL, "fdwname", NULL, NULL);
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of foreign-data wrappers");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \des
*
2009-03-25 14:11:43 +01:00
* Describes foreign servers.
*/
bool
listForeignServers(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 80400)
{
2009-03-25 14:11:43 +01:00
fprintf(stderr, _("The server (version %d.%d) does not support foreign servers.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT s.srvname AS \"%s\",\n"
" pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
" f.fdwname AS \"%s\"",
gettext_noop("Name"),
gettext_noop("Owner"),
gettext_noop("Foreign-data wrapper"));
if (verbose)
{
appendPQExpBuffer(&buf, ",\n ");
printACLColumn(&buf, "s.srvacl");
appendPQExpBuffer(&buf,
",\n"
" s.srvtype AS \"%s\",\n"
" s.srvversion AS \"%s\",\n"
" CASE WHEN srvoptions IS NULL THEN '' ELSE "
" '(' || array_to_string(ARRAY(SELECT "
" quote_ident(option_name) || ' ' || "
" quote_literal(option_value) FROM "
" pg_options_to_table(srvoptions)), ', ') || ')' "
" END AS \"%s\",\n"
" d.description AS \"%s\"",
gettext_noop("Type"),
gettext_noop("Version"),
gettext_noop("FDW Options"),
gettext_noop("Description"));
}
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_foreign_server s\n"
" JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
if (verbose)
appendPQExpBuffer(&buf,
"LEFT JOIN pg_description d\n "
"ON d.classoid = s.tableoid AND d.objoid = s.oid "
"AND d.objsubid = 0\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL, "s.srvname", NULL, NULL);
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of foreign servers");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \deu
*
* Describes user mappings.
*/
bool
listUserMappings(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 80400)
{
2009-03-25 14:11:43 +01:00
fprintf(stderr, _("The server (version %d.%d) does not support user mappings.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT um.srvname AS \"%s\",\n"
" um.usename AS \"%s\"",
gettext_noop("Server"),
2009-03-25 14:11:43 +01:00
gettext_noop("User name"));
if (verbose)
appendPQExpBuffer(&buf,
",\n CASE WHEN umoptions IS NULL THEN '' ELSE "
" '(' || array_to_string(ARRAY(SELECT "
" quote_ident(option_name) || ' ' || "
" quote_literal(option_value) FROM "
" pg_options_to_table(umoptions)), ', ') || ')' "
" END AS \"%s\"",
gettext_noop("FDW Options"));
appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_user_mappings um\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL, "um.srvname", "um.usename", NULL);
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of user mappings");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \det
*
* Describes foreign tables.
*/
bool
listForeignTables(const char *pattern, bool verbose)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 90100)
{
fprintf(stderr, _("The server (version %d.%d) does not support foreign tables.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT n.nspname AS \"%s\",\n"
" c.relname AS \"%s\",\n"
" s.srvname AS \"%s\"",
gettext_noop("Schema"),
gettext_noop("Table"),
gettext_noop("Server"));
if (verbose)
appendPQExpBuffer(&buf,
",\n CASE WHEN ftoptions IS NULL THEN '' ELSE "
" '(' || array_to_string(ARRAY(SELECT "
" quote_ident(option_name) || ' ' || "
" quote_literal(option_value) FROM "
" pg_options_to_table(ftoptions)), ', ') || ')' "
" END AS \"%s\",\n"
" d.description AS \"%s\"",
gettext_noop("FDW Options"),
gettext_noop("Description"));
appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_foreign_table ft\n"
" INNER JOIN pg_catalog.pg_class c"
" ON c.oid = ft.ftrelid\n"
" INNER JOIN pg_catalog.pg_namespace n"
" ON n.oid = c.relnamespace\n"
" INNER JOIN pg_catalog.pg_foreign_server s"
" ON s.oid = ft.ftserver\n");
if (verbose)
appendPQExpBuffer(&buf,
" LEFT JOIN pg_catalog.pg_description d\n"
" ON d.classoid = c.tableoid AND "
"d.objoid = c.oid AND d.objsubid = 0\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
NULL, "n.nspname", "c.relname", NULL);
appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of foreign tables");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dx
*
* Briefly describes installed extensions.
*/
bool
listExtensions(const char *pattern)
{
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
if (pset.sversion < 90100)
{
fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT e.extname AS \"%s\", "
2011-04-10 17:42:00 +02:00
"e.extversion AS \"%s\", n.nspname AS \"%s\", c.description AS \"%s\"\n"
"FROM pg_catalog.pg_extension e "
2011-04-10 17:42:00 +02:00
"LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
"LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid "
"AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n",
gettext_noop("Name"),
gettext_noop("Version"),
gettext_noop("Schema"),
gettext_noop("Description"));
processSQLNamePattern(pset.db, &buf, pattern,
false, false,
NULL, "e.extname", NULL,
NULL);
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
myopt.title = _("List of installed extensions");
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* \dx+
*
* List contents of installed extensions.
*/
bool
listExtensionContents(const char *pattern)
{
PQExpBufferData buf;
PGresult *res;
int i;
if (pset.sversion < 90100)
{
fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"),
pset.sversion / 10000, (pset.sversion / 100) % 100);
return true;
}
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
"SELECT e.extname, e.oid\n"
"FROM pg_catalog.pg_extension e\n");
processSQLNamePattern(pset.db, &buf, pattern,
false, false,
NULL, "e.extname", NULL,
NULL);
appendPQExpBuffer(&buf, "ORDER BY 1;");
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
if (PQntuples(res) == 0)
{
if (!pset.quiet)
{
if (pattern)
fprintf(stderr, _("Did not find any extension named \"%s\".\n"),
pattern);
else
fprintf(stderr, _("Did not find any extensions.\n"));
}
PQclear(res);
return false;
}
for (i = 0; i < PQntuples(res); i++)
{
const char *extname;
const char *oid;
extname = PQgetvalue(res, i, 0);
oid = PQgetvalue(res, i, 1);
if (!listOneExtensionContents(extname, oid))
{
PQclear(res);
return false;
}
if (cancel_pressed)
{
PQclear(res);
return false;
}
}
PQclear(res);
return true;
}
static bool
listOneExtensionContents(const char *extname, const char *oid)
{
PQExpBufferData buf;
PGresult *res;
char title[1024];
printQueryOpt myopt = pset.popt;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf,
2011-04-10 17:42:00 +02:00
"SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
"FROM pg_catalog.pg_depend\n"
"WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
"ORDER BY 1;",
gettext_noop("Object Description"),
oid);
res = PSQLexec(buf.data, false);
termPQExpBuffer(&buf);
if (!res)
return false;
myopt.nullPrint = NULL;
snprintf(title, sizeof(title), _("Objects in extension \"%s\""), extname);
myopt.title = title;
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, pset.logfile);
PQclear(res);
return true;
}
/*
* printACLColumn
*
* Helper function for consistently formatting ACL (privilege) columns.
* The proper targetlist entry is appended to buf. Note lack of any
* whitespace or comma decoration.
*/
static void
printACLColumn(PQExpBuffer buf, const char *colname)
{
if (pset.sversion >= 80100)
appendPQExpBuffer(buf,
"pg_catalog.array_to_string(%s, E'\\n') AS \"%s\"",
colname, gettext_noop("Access privileges"));
else
appendPQExpBuffer(buf,
"pg_catalog.array_to_string(%s, '\\n') AS \"%s\"",
colname, gettext_noop("Access privileges"));
}