pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
psql: display of object comments
14+ messages / 3 participants
[nested] [flat]

* psql: display of object comments
@ 2011-07-09 17:16  Josh Kupershmidt <schmiddy@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Josh Kupershmidt @ 2011-07-09 17:16 UTC (permalink / raw)
  To: pgsql-hackers

Hi all,

I would like to fix psql's incomplete and inconsistent handling of
object comments. I think this subject can be handled separately from
the pg_comments discussion, and we can focus on the
appearance/behavior of psql commands here.

First, As brought up recently [1], comments for the following object
types are not currently displayed by any psql backslash command:

  CAST
  CONSTRAINT
  CONVERSION
  FOREIGN DATA WRAPPER
  PROCEDURAL LANGUAGE
  SERVER

In the case of PROCEDURAL LANGUAGE, it was mere oversight that \dL did
not display comments, and I'm not aware of any reason why we're not
doing so for the other objects above. I'd like to propose adding an
additional column to the respective psql backslash commands for the
above object types which will display any comment associated with the
object.

Second, comments of the following object types would IMO be better
displayed in the object's individual backslash command, and not in \dd
as they are now:

  DOMAIN

Third, comments for the following objects are displayed by both \dd
and the object's individual backslash command:

  AGGREGATE
  FOREIGN TABLE (via \d+)
  INDEX
  FUNCTION
  OPERATOR
  SEQUENCE
  TABLE
  VIEW

Seems like there's no need to add more noise to \dd for these cases.

Fourth, there is a bug in at least the display of comments for indexes
using \d+ (the "Description" is empty when it shouldn't be).

As a summary, here is how I am proposing we display object comments [2]:

1.) Object comments displayed by \dd:

 CONSTRAINT
 OPERATOR CLASS
 OPERATOR FAMILY
 RULE
 TRIGGER

2.) Object comments displayed in the backslash commands for the object:

 AGGREGATE                     \da
 CAST                          \dC+
 COLLATION                     \dO
 COLUMN                        \d+ tablename
 CONVERSION                    \dc
 DATABASE                      \l+
 DOMAIN                        \dD and \dT+
 EXTENSION                     \dx
 FOREIGN DATA WRAPPER          \dew
 FOREIGN TABLE                 \det and \d+
 FUNCTION                      \df+
 INDEX                         \di+ and \d+
 LARGE OBJECT                  \dl
 OPERATOR                      \do
 PROCEDURAL LANGUAGE           \dL
 ROLE                          \dg+
 SCHEMA                        \dn+
 SEQUENCE                      \ds+ and \d+
 SERVER                        \des
 TABLE                         \dt+ and \d+
 TABLESPACE                    \db+
 TYPE                          \dT
 TEXT SEARCH CONFIGURATION     \dF
 TEXT SEARCH DICTIONARY        \dFd
 TEXT SEARCH PARSER            \dFp
 TEXT SEARCH TEMPLATE          \dFt
 VIEW                          \dv+

A few notes:
 a.) As you can see, there's not much consistency in whether we
display object comments only in verbose mode or not. I'm open to
suggestions on how to tweak/standardize this.
 b.) I'd rather not have any overlap between groups 1.) and 2.).
 c.) \dd doesn't yet display all the object types it should (operator
class & family are missing); that's slated to be fixed with the
pg_comments patch though.

Patch attached and comments welcome; will add to next CF barring
objections. I've also attached a test .sql file I was using to make up
a bunch of comments, it might save you some time if you're interested
in trying the patch out.

--
[1] http://archives.postgresql.org/pgsql-hackers/2011-05/msg00991.php
[2] The object types broken down here should cover all comment-able
types on http://www.postgresql.org/docs/9.1/static/sql-comment.html

Attachments:

  [application/octet-stream] describe_comments.v1.patch (68.1K, ../../CAK3UJRHQNqQZA9dWV7qgYTZJUWc-A2tJEOuTk9yHgm37oihKsg@mail.gmail.com/2-describe_comments.v1.patch)
  download | inline diff:
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
new file mode 100644
index 16ff9e9..ee4c4a2
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
*************** exec_command(const char *cmd,
*** 381,387 ****
  				success = listConversions(pattern, show_system);
  				break;
  			case 'C':
! 				success = listCasts(pattern);
  				break;
  			case 'd':
  				if (strncmp(cmd, "ddp", 3) == 0)
--- 381,387 ----
  				success = listConversions(pattern, show_system);
  				break;
  			case 'C':
! 				success = listCasts(pattern, show_verbose);
  				break;
  			case 'd':
  				if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
new file mode 100644
index 682cf8a..731ef8b
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
*************** objectDescription(const char *pattern, b
*** 851,963 ****
  					  gettext_noop("Object"),
  					  gettext_noop("Description"));
  
- 	/* Aggregate descriptions */
- 	appendPQExpBuffer(&buf,
- 					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
- 					  "  n.nspname as nspname,\n"
- 					  "  CAST(p.proname AS pg_catalog.text) as name,"
- 					  "  CAST('%s' AS pg_catalog.text) as object\n"
- 					  "  FROM pg_catalog.pg_proc p\n"
- 	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
- 					  "  WHERE p.proisagg\n",
- 					  gettext_noop("aggregate"));
- 
- 	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)");
- 
- 	/* Function descriptions */
- 	appendPQExpBuffer(&buf,
- 					  "UNION ALL\n"
- 					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
- 					  "  n.nspname as nspname,\n"
- 					  "  CAST(p.proname AS pg_catalog.text) as name,"
- 					  "  CAST('%s' AS pg_catalog.text) as object\n"
- 					  "  FROM pg_catalog.pg_proc p\n"
- 	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
- 					  "  WHERE NOT p.proisagg\n",
- 					  gettext_noop("function"));
- 
- 	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)");
- 
- 	/* Operator descriptions */
- 	appendPQExpBuffer(&buf,
- 					  "UNION ALL\n"
- 					  "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
- 					  "  n.nspname as nspname,\n"
- 					  "  CAST(o.oprname AS pg_catalog.text) as name,"
- 					  "  CAST('%s' AS pg_catalog.text) as object\n"
- 					  "  FROM pg_catalog.pg_operator o\n"
- 	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
- 					  gettext_noop("operator"));
- 
- 	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", "o.oprname", NULL,
- 						  "pg_catalog.pg_operator_is_visible(o.oid)");
- 
- 	/* Type descriptions */
- 	appendPQExpBuffer(&buf,
- 					  "UNION ALL\n"
- 					  "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
- 					  "  n.nspname as nspname,\n"
- 					  "  pg_catalog.format_type(t.oid, NULL) as name,"
- 					  "  CAST('%s' AS pg_catalog.text) as object\n"
- 					  "  FROM pg_catalog.pg_type t\n"
- 	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
- 					  gettext_noop("data type"));
- 
- 	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", "pg_catalog.format_type(t.oid, NULL)",
- 						  NULL,
- 						  "pg_catalog.pg_type_is_visible(t.oid)");
- 
- 	/* Relation (tables, views, indexes, sequences) descriptions */
- 	appendPQExpBuffer(&buf,
- 					  "UNION ALL\n"
- 					  "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
- 					  "  n.nspname as nspname,\n"
- 					  "  CAST(c.relname AS pg_catalog.text) as name,\n"
- 					  "  CAST(\n"
- 					  "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
- 					  "  AS pg_catalog.text) as object\n"
- 					  "  FROM 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', 'i', 'S', 'f')\n",
- 					  gettext_noop("table"),
- 					  gettext_noop("view"),
- 					  gettext_noop("index"),
- 					  gettext_noop("sequence"),
- 					  gettext_noop("foreign table"));
- 
- 	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.relname", NULL,
- 						  "pg_catalog.pg_table_is_visible(c.oid)");
- 
  	/* Rule descriptions (ignore rules for views) */
  	appendPQExpBuffer(&buf,
- 					  "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,"
--- 851,858 ----
*************** objectDescription(const char *pattern, b
*** 998,1003 ****
--- 893,921 ----
  						  "n.nspname", "t.tgname", NULL,
  						  "pg_catalog.pg_table_is_visible(c.oid)");
  
+ 	/* Constraint descriptions */
+ 	appendPQExpBuffer(&buf,
+ 					  "UNION ALL\n"
+ 					  "  SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
+ 					  "  n.nspname as nspname,\n"
+ 					  "  CAST(pgc.conname AS pg_catalog.text) as name,"
+ 					  "  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");
+  
+ 	/* XXX not sure what to do about visibility rule here? */
+ 	processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, 
+ 						  false, "n.nspname", "pgc.conname", NULL,
+ 						  "pg_catalog.pg_table_is_visible(c.oid)");
+ 
  	appendPQExpBuffer(&buf,
  					  ") AS tt\n"
  					  "  JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid AND tt.tableoid = d.classoid AND d.objsubid = 0)\n");
*************** listLanguages(const char *pattern, bool 
*** 2631,2638 ****
  						  gettext_noop("Owner"));
  
  	appendPQExpBuffer(&buf,
! 					  "       l.lanpltrusted AS \"%s\"",
! 					  gettext_noop("Trusted"));
  
  	if (verbose)
  	{
--- 2549,2558 ----
  						  gettext_noop("Owner"));
  
  	appendPQExpBuffer(&buf,
! 					  "       l.lanpltrusted AS \"%s\",\n"
! 					  "       d.description AS \"%s\"",
! 					  gettext_noop("Trusted"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  	{
*************** listLanguages(const char *pattern, bool 
*** 2650,2664 ****
  	}
  
  	appendPQExpBuffer(&buf,
! 					  "\nFROM pg_catalog.pg_language l\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "l.lanname", NULL, NULL);
  
  	if (!showSystem && !pattern)
! 		appendPQExpBuffer(&buf, "WHERE lanplcallfoid != 0\n");
  
! 	appendPQExpBuffer(&buf, "ORDER BY 1;");
  
  	res = PSQLexec(buf.data, false);
  	termPQExpBuffer(&buf);
--- 2570,2588 ----
  	}
  
  	appendPQExpBuffer(&buf,
! 					  "\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");
  
  	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "l.lanname", NULL, NULL);
  
+ 	appendPQExpBuffer(&buf, "WHERE (d.objsubid IS NULL OR d.objsubid = 0) ");
+ 
  	if (!showSystem && !pattern)
! 		appendPQExpBuffer(&buf, "AND lanplcallfoid != 0");
  
! 	appendPQExpBuffer(&buf, "\nORDER BY 1;");
  
  	res = PSQLexec(buf.data, false);
  	termPQExpBuffer(&buf);
*************** listDomains(const char *pattern, bool sh
*** 2710,2720 ****
  	appendPQExpBuffer(&buf,
  					  "       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\"\n"
  					  "FROM pg_catalog.pg_type t\n"
  	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
! 					  "WHERE t.typtype = 'd'\n",
! 					  gettext_noop("Check"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
--- 2634,2649 ----
  	appendPQExpBuffer(&buf,
  					  "       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\",\n"
! 					  "       d.description as \"%s\"\n"
  					  "FROM pg_catalog.pg_type t\n"
  	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
! 					  "     LEFT JOIN pg_catalog.pg_description d "
! 					  "ON d.classoid = t.tableoid AND d.objoid = t.oid\n"
! 					  "WHERE t.typtype = 'd' AND\n"
! 					  "(d.objsubid IS NULL OR d.objsubid = 0)\n",
! 					  gettext_noop("Check"),
! 					  gettext_noop("Description"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
*************** listConversions(const char *pattern, boo
*** 2762,2776 ****
  	   "       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\"\n"
! 			   "FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n\n"
! 					  "WHERE n.oid = c.connamespace\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("Source"),
  					  gettext_noop("Destination"),
  					  gettext_noop("yes"), gettext_noop("no"),
! 					  gettext_noop("Default?"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
--- 2691,2711 ----
  	   "       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\",\n"
! 					  "       d.description AS \"%s\"\n"
! 					  "FROM pg_catalog.pg_conversion c "
! 					  "JOIN pg_catalog.pg_namespace n "
! 					  "ON n.oid = c.connamespace\n"
! 					  "LEFT JOIN pg_catalog.pg_description d "
! 					  "ON d.classoid = c.tableoid AND d.objoid = c.oid\n"
! 					  "WHERE (d.objsubid = 0 OR d.objsubid IS NULL)\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("Source"),
  					  gettext_noop("Destination"),
  					  gettext_noop("yes"), gettext_noop("no"),
! 					  gettext_noop("Default?"),
! 					  gettext_noop("Description"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
*************** listConversions(const char *pattern, boo
*** 2804,2810 ****
   * Describes casts.
   */
  bool
! listCasts(const char *pattern)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
--- 2739,2745 ----
   * Describes casts.
   */
  bool
! listCasts(const char *pattern, bool verbose)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
*************** listCasts(const char *pattern)
*** 2828,2834 ****
  					  "       CASE WHEN c.castcontext = 'e' THEN '%s'\n"
  					  "            WHEN c.castcontext = 'a' THEN '%s'\n"
  					  "            ELSE '%s'\n"
! 					  "       END as \"%s\"\n"
  				 "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"
--- 2763,2783 ----
  					  "       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,
  				 "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"
*************** listCasts(const char *pattern)
*** 2838,2850 ****
  					  "     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"
! 					  "WHERE (true",
! 					  gettext_noop("Source type"),
! 					  gettext_noop("Target type"),
! 					  gettext_noop("Function"),
! 	  gettext_noop("no"), gettext_noop("in assignment"), gettext_noop("yes"),
! 					  gettext_noop("Implicit?"));
  
  	/*
  	 * Match name pattern against either internal or external name of either
--- 2787,2800 ----
  					  "     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\n");
! 
! 	appendPQExpBuffer(&buf, "WHERE ( (true");
  
  	/*
  	 * Match name pattern against either internal or external name of either
*************** listCasts(const char *pattern)
*** 2862,2868 ****
  						  "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);
--- 2812,2823 ----
  						  "pg_catalog.format_type(tt.oid, NULL)",
  						  "pg_catalog.pg_type_is_visible(tt.oid)");
  
! 	appendPQExpBuffer(&buf, ") )");
! 
! 	if (verbose)
! 		appendPQExpBuffer(&buf, "AND (d.objsubid IS NULL OR d.objsubid = 0)");
! 
! 	appendPQExpBuffer(&buf, "\nORDER BY 1, 2;");
  
  	res = PSQLexec(buf.data, false);
  	termPQExpBuffer(&buf);
*************** listForeignDataWrappers(const char *patt
*** 3593,3610 ****
  
  	initPQExpBuffer(&buf);
  	printfPQExpBuffer(&buf,
! 					  "SELECT fdwname AS \"%s\",\n"
! 					  "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n",
  					  gettext_noop("Name"),
  					  gettext_noop("Owner"));
  	if (pset.sversion >= 90100)
  		appendPQExpBuffer(&buf,
! 						  "  fdwhandler::pg_catalog.regproc AS \"%s\",\n",
  						  gettext_noop("Handler"));
  	appendPQExpBuffer(&buf,
! 					  "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
  					  gettext_noop("Validator"));
  
  	if (verbose)
  	{
  		appendPQExpBuffer(&buf, ",\n  ");
--- 3548,3570 ----
  
  	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 (pset.sversion >= 90100)
+ 		appendPQExpBuffer(&buf,
+ 						  ",\nd.description AS \"%s\" ",
+ 						  gettext_noop("Description"));
+ 
  	if (verbose)
  	{
  		appendPQExpBuffer(&buf, ",\n  ");
*************** listForeignDataWrappers(const char *patt
*** 3614,3622 ****
  						  gettext_noop("Options"));
  	}
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "fdwname", NULL, NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
--- 3574,3590 ----
  						  gettext_noop("Options"));
  	}
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
  
! 	if (pset.sversion >= 90100)
! 		appendPQExpBuffer(&buf,
! 						  "LEFT JOIN pg_catalog.pg_description d\n"
! 						  "ON d.classoid = fdw.tableoid "
! 						  "AND d.objoid = fdw.oid\n");
! 
! 	appendPQExpBuffer(&buf, "WHERE (d.objsubid IS NULL OR d.objsubid = 0)\n");
! 
! 	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "fdwname", NULL, NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
*************** listForeignServers(const char *pattern, 
*** 3659,3668 ****
  	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)
  	{
--- 3627,3638 ----
  	printfPQExpBuffer(&buf,
  					  "SELECT s.srvname AS \"%s\",\n"
  					  "  pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
! 					  "  f.fdwname AS \"%s\",\n"
! 					  "  d.description AS \"%s\"",
  					  gettext_noop("Name"),
  					  gettext_noop("Owner"),
! 					  gettext_noop("Foreign-data wrapper"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  	{
*************** listForeignServers(const char *pattern, 
*** 3680,3688 ****
  
  	appendPQExpBuffer(&buf,
  					  "\nFROM pg_catalog.pg_foreign_server s\n"
! 	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "s.srvname", NULL, NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
--- 3650,3661 ----
  
  	appendPQExpBuffer(&buf,
  					  "\nFROM pg_catalog.pg_foreign_server s\n"
! 	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n"
! 					  "LEFT JOIN pg_description d "
! 					  "ON d.classoid = s.tableoid AND d.objoid = s.oid\n"
! 					  "WHERE (d.objsubid IS NULL OR d.objsubid = 0)\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "s.srvname", NULL, NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
*************** listForeignTables(const char *pattern, b
*** 3778,3800 ****
  	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  ft.ftoptions AS \"%s\"",
  						  gettext_noop("Options"));
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
! 	appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
! 	appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
! 	appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "n.nspname", "c.relname", NULL);
--- 3751,3778 ----
  	printfPQExpBuffer(&buf,
  					  "SELECT n.nspname AS \"%s\",\n"
  					  "  c.relname AS \"%s\",\n"
! 					  "  s.srvname AS \"%s\",\n"
! 					  "  d.description AS \"%s\"\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Table"),
! 					  gettext_noop("Server"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  		appendPQExpBuffer(&buf,
  						  ",\n  ft.ftoptions AS \"%s\"",
  						  gettext_noop("Options"));
  
! 	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"
! 					  "  LEFT JOIN pg_catalog.pg_description d"
! 					  "\n    ON d.classoid = c.tableoid AND d.objoid = c.oid\n"
! 					  "WHERE (d.objsubid IS NULL OR d.objsubid = 0)\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "n.nspname", "c.relname", NULL);
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
new file mode 100644
index fb86d1e..7dc9a2c
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
*************** extern bool listDomains(const char *patt
*** 67,73 ****
  extern bool listConversions(const char *pattern, bool showSystem);
  
  /* \dC */
! extern bool listCasts(const char *pattern);
  
  /* \dO */
  extern bool listCollations(const char *pattern, bool verbose, bool showSystem);
--- 67,73 ----
  extern bool listConversions(const char *pattern, bool showSystem);
  
  /* \dC */
! extern bool listCasts(const char *pattern, bool verbose);
  
  /* \dO */
  extern bool listCollations(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
new file mode 100644
index 2b3eddf..8d1bc16
*** a/src/test/regress/expected/foreign_data.out
--- b/src/test/regress/expected/foreign_data.out
*************** CREATE FOREIGN DATA WRAPPER foo VALIDATO
*** 39,50 ****
  ERROR:  function bar(text[], oid) does not exist
  CREATE FOREIGN DATA WRAPPER foo;
  \dew
!                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         
! ------------+-------------------+---------+--------------------------
!  dummy      | foreign_data_user | -       | -
!  foo        | foreign_data_user | -       | -
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator
  (3 rows)
  
  CREATE FOREIGN DATA WRAPPER foo; -- duplicate
--- 39,50 ----
  ERROR:  function bar(text[], oid) does not exist
  CREATE FOREIGN DATA WRAPPER foo;
  \dew
!                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description 
! ------------+-------------------+---------+--------------------------+-------------
!  dummy      | foreign_data_user | -       | -                        | useless
!  foo        | foreign_data_user | -       | -                        | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator | 
  (3 rows)
  
  CREATE FOREIGN DATA WRAPPER foo; -- duplicate
*************** ERROR:  foreign-data wrapper "foo" alrea
*** 52,63 ****
  DROP FOREIGN DATA WRAPPER foo;
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
  \dew+
!                                      List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   
! ------------+-------------------+---------+--------------------------+-------------------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
--- 52,63 ----
  DROP FOREIGN DATA WRAPPER foo;
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
  \dew+
!                                             List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |   Options   
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {testing=1}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
*************** CREATE FOREIGN DATA WRAPPER foo OPTIONS 
*** 65,76 ****
  ERROR:  option "testing" provided more than once
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        
! ------------+-------------------+---------+--------------------------+-------------------+-----------------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
--- 65,76 ----
  ERROR:  option "testing" provided more than once
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
  \dew+
!                                                  List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |        Options        
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {testing=1,another=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
*************** HINT:  Must be superuser to create a for
*** 81,92 ****
  RESET ROLE;
  CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | postgresql_fdw_validator |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  -- ALTER FOREIGN DATA WRAPPER
--- 81,92 ----
  RESET ROLE;
  CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  -- ALTER FOREIGN DATA WRAPPER
*************** ALTER FOREIGN DATA WRAPPER foo VALIDATOR
*** 98,109 ****
  ERROR:  function bar(text[], oid) does not exist
  ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
--- 98,109 ----
  ERROR:  function bar(text[], oid) does not exist
  ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
*************** ALTER FOREIGN DATA WRAPPER foo OPTIONS (
*** 113,146 ****
  ERROR:  option "c" not found
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
  \dew+
!                                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
  \dew+
!                                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
  ERROR:  option "b" provided more than once
  \dew+
!                                       List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    
! ------------+-------------------+---------+--------------------------+-------------------+---------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  SET ROLE regress_test_role;
--- 113,146 ----
  ERROR:  option "c" not found
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
  \dew+
!                                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {a=1,b=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
  \dew+
!                                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
  ERROR:  option "b" provided more than once
  \dew+
!                                              List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |    Options    
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4,a=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  SET ROLE regress_test_role;
*************** HINT:  Must be superuser to alter a fore
*** 150,161 ****
  SET ROLE regress_test_role_super;
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
  \dew+
!                                         List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
--- 150,161 ----
  SET ROLE regress_test_role_super;
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
  \dew+
!                                                List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
*************** ERROR:  permission denied to alter forei
*** 169,180 ****
  HINT:  Must be superuser to alter a foreign-data wrapper.
  RESET ROLE;
  \dew+
!                                            List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        |                   | 
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  -- DROP FOREIGN DATA WRAPPER
--- 169,180 ----
  HINT:  Must be superuser to alter a foreign-data wrapper.
  RESET ROLE;
  \dew+
!                                                   List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        | useless     |                   | 
!  foo        | regress_test_role_super | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  -- DROP FOREIGN DATA WRAPPER
*************** ERROR:  foreign-data wrapper "nonexisten
*** 183,194 ****
  DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
  NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
  \dew+
!                                            List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        |                   | 
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP ROLE regress_test_role_super;                          -- ERROR
--- 183,194 ----
  DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
  NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
  \dew+
!                                                   List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        | useless     |                   | 
!  foo        | regress_test_role_super | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP ROLE regress_test_role_super;                          -- ERROR
*************** ALTER ROLE regress_test_role_super SUPER
*** 203,213 ****
  DROP FOREIGN DATA WRAPPER foo;
  DROP ROLE regress_test_role_super;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (2 rows)
  
  CREATE FOREIGN DATA WRAPPER foo;
--- 203,213 ----
  DROP FOREIGN DATA WRAPPER foo;
  DROP ROLE regress_test_role_super;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (2 rows)
  
  CREATE FOREIGN DATA WRAPPER foo;
*************** CREATE SERVER s1 FOREIGN DATA WRAPPER fo
*** 215,233 ****
  COMMENT ON SERVER s1 IS 'foreign server';
  CREATE USER MAPPING FOR current_user SERVER s1;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  \des+
!                                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges | Type | Version | Options 
! ------+-------------------+----------------------+-------------------+------+---------+---------
!  s1   | foreign_data_user | foo                  |                   |      |         | 
  (1 row)
  
  \deu+
--- 215,233 ----
  COMMENT ON SERVER s1 IS 'foreign server';
  CREATE USER MAPPING FOR current_user SERVER s1;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  \des+
!                                              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper |  Description   | Access privileges | Type | Version | Options 
! ------+-------------------+----------------------+----------------+-------------------+------+---------+---------
!  s1   | foreign_data_user | foo                  | foreign server |                   |      |         | 
  (1 row)
  
  \deu+
*************** NOTICE:  drop cascades to 2 other object
*** 252,268 ****
  DETAIL:  drop cascades to server s1
  drop cascades to user mapping for foreign_data_user
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (2 rows)
  
  \des+
!                               List of foreign servers
!  Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options 
! ------+-------+----------------------+-------------------+------+---------+---------
  (0 rows)
  
  \deu+
--- 252,268 ----
  DETAIL:  drop cascades to server s1
  drop cascades to user mapping for foreign_data_user
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (2 rows)
  
  \des+
!                                      List of foreign servers
!  Name | Owner | Foreign-data wrapper | Description | Access privileges | Type | Version | Options 
! ------+-------+----------------------+-------------+-------------------+------+---------+---------
  (0 rows)
  
  \deu+
*************** ERROR:  invalid option "foo"
*** 289,305 ****
  HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
  CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
  (8 rows)
  
  SET ROLE regress_test_role;
--- 289,305 ----
  HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
  CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
  (8 rows)
  
  SET ROLE regress_test_role;
*************** SET ROLE regress_test_role;
*** 311,328 ****
  CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
  RESET ROLE;
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                   |        |         | 
  (9 rows)
  
  REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
--- 311,328 ----
  CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
  RESET ROLE;
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                   |        |         | 
  (9 rows)
  
  REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
*************** GRANT regress_test_indirect TO regress_t
*** 335,353 ****
  SET ROLE regress_test_role;
  CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                   |        |         | 
!  t2   | regress_test_role | foo                  |                   |        |         | 
  (10 rows)
  
  RESET ROLE;
--- 335,353 ----
  SET ROLE regress_test_role;
  CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                   |        |         | 
!  t2   | regress_test_role | foo                  |             |                   |        |         | 
  (10 rows)
  
  RESET ROLE;
*************** ALTER SERVER s3 OPTIONS (tnsname 'orcl',
*** 365,385 ****
  GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
  GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
  \des+
!                                                            List of foreign servers
!  Name |       Owner       | Foreign-data wrapper |            Access privileges            |  Type  | Version |           Options            
! ------+-------------------+----------------------+-----------------------------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
!       |                   |                      | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user | foo                  |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                                         |        | 15.0    | 
!  s6   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                   |                      | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                                         |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                                         |        |         | 
!  t2   | regress_test_role | foo                  |                                         |        |         | 
  (10 rows)
  
  SET ROLE regress_test_role;
--- 365,385 ----
  GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
  GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
  \des+
!                                                                   List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description |            Access privileges            |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-----------------------------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
!       |                   |                      |             | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user | foo                  |             |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                                         |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                   |                      |             | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user | foo                  |             |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                                         |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                                         |        |         | 
!  t2   | regress_test_role | foo                  |             |                                         |        |         | 
  (10 rows)
  
  SET ROLE regress_test_role;
*************** ERROR:  role "regress_test_indirect" can
*** 416,436 ****
  DETAIL:  owner of server s1
  privileges for foreign-data wrapper foo
  \des+
!                                                               List of foreign servers
!  Name |         Owner         | Foreign-data wrapper |            Access privileges            |  Type  | Version |             Options             
! ------+-----------------------+----------------------+-----------------------------------------+--------+---------+---------------------------------
!  s1   | regress_test_indirect | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
!       |                       |                      | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user     | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user     | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user     | foo                  |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user     | foo                  |                                         |        | 15.0    | 
!  s6   | foreign_data_user     | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                       |                      | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user     | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user     | postgresql           |                                         |        |         | {dbname=db1,connect_timeout=30}
!  t1   | regress_test_role     | foo                  |                                         |        |         | 
!  t2   | regress_test_role     | foo                  |                                         |        |         | 
  (10 rows)
  
  -- DROP SERVER
--- 416,436 ----
  DETAIL:  owner of server s1
  privileges for foreign-data wrapper foo
  \des+
!                                                                      List of foreign servers
!  Name |         Owner         | Foreign-data wrapper | Description |            Access privileges            |  Type  | Version |             Options             
! ------+-----------------------+----------------------+-------------+-----------------------------------------+--------+---------+---------------------------------
!  s1   | regress_test_indirect | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
!       |                       |                      |             | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user     | foo                  |             |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user     | foo                  |             |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user     | foo                  |             |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user     | foo                  |             |                                         |        | 15.0    | 
!  s6   | foreign_data_user     | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                       |                      |             | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user     | foo                  |             |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user     | postgresql           |             |                                         |        |         | {dbname=db1,connect_timeout=30}
!  t1   | regress_test_role     | foo                  |             |                                         |        |         | 
!  t2   | regress_test_role     | foo                  |             |                                         |        |         | 
  (10 rows)
  
  -- DROP SERVER
*************** ERROR:  server "nonexistent" does not ex
*** 439,457 ****
  DROP SERVER IF EXISTS nonexistent;
  NOTICE:  server "nonexistent" does not exist, skipping
  \des
!                List of foreign servers
!  Name |         Owner         | Foreign-data wrapper 
! ------+-----------------------+----------------------
!  s1   | regress_test_indirect | foo
!  s2   | foreign_data_user     | foo
!  s3   | foreign_data_user     | foo
!  s4   | foreign_data_user     | foo
!  s5   | foreign_data_user     | foo
!  s6   | foreign_data_user     | foo
!  s7   | foreign_data_user     | foo
!  s8   | foreign_data_user     | postgresql
!  t1   | regress_test_role     | foo
!  t2   | regress_test_role     | foo
  (10 rows)
  
  SET ROLE regress_test_role;
--- 439,457 ----
  DROP SERVER IF EXISTS nonexistent;
  NOTICE:  server "nonexistent" does not exist, skipping
  \des
!                       List of foreign servers
!  Name |         Owner         | Foreign-data wrapper | Description 
! ------+-----------------------+----------------------+-------------
!  s1   | regress_test_indirect | foo                  | 
!  s2   | foreign_data_user     | foo                  | 
!  s3   | foreign_data_user     | foo                  | 
!  s4   | foreign_data_user     | foo                  | 
!  s5   | foreign_data_user     | foo                  | 
!  s6   | foreign_data_user     | foo                  | 
!  s7   | foreign_data_user     | foo                  | 
!  s8   | foreign_data_user     | postgresql           | 
!  t1   | regress_test_role     | foo                  | 
!  t2   | regress_test_role     | foo                  | 
  (10 rows)
  
  SET ROLE regress_test_role;
*************** ERROR:  must be owner of foreign server 
*** 460,477 ****
  DROP SERVER s1;
  RESET ROLE;
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s2   | foreign_data_user | foo
!  s3   | foreign_data_user | foo
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (9 rows)
  
  ALTER SERVER s2 OWNER TO regress_test_role;
--- 460,477 ----
  DROP SERVER s1;
  RESET ROLE;
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s2   | foreign_data_user | foo                  | 
!  s3   | foreign_data_user | foo                  | 
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (9 rows)
  
  ALTER SERVER s2 OWNER TO regress_test_role;
*************** SET ROLE regress_test_role;
*** 479,495 ****
  DROP SERVER s2;
  RESET ROLE;
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s3   | foreign_data_user | foo
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (8 rows)
  
  CREATE USER MAPPING FOR current_user SERVER s3;
--- 479,495 ----
  DROP SERVER s2;
  RESET ROLE;
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s3   | foreign_data_user | foo                  | 
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (8 rows)
  
  CREATE USER MAPPING FOR current_user SERVER s3;
*************** HINT:  Use DROP ... CASCADE to drop the 
*** 507,522 ****
  DROP SERVER s3 CASCADE;
  NOTICE:  drop cascades to user mapping for foreign_data_user
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (7 rows)
  
  \deu
--- 507,522 ----
  DROP SERVER s3 CASCADE;
  NOTICE:  drop cascades to user mapping for foreign_data_user
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (7 rows)
  
  \deu
*************** Server: sc
*** 663,672 ****
  Has OIDs: no
  
  \det+
!                 List of foreign tables
!  Schema | Table | Server |          Options           
! --------+-------+--------+----------------------------
!  public | ft1   | sc     | {"delimiter=,","quote=\""}
  (1 row)
  
  CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
--- 663,672 ----
  Has OIDs: no
  
  \det+
!                        List of foreign tables
!  Schema | Table | Server | Description |          Options           
! --------+-------+--------+-------------+----------------------------
!  public | ft1   | sc     | ft1         | {"delimiter=,","quote=\""}
  (1 row)
  
  CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR


  [application/octet-stream] create_comments.sql (2.5K, ../../CAK3UJRHQNqQZA9dWV7qgYTZJUWc-A2tJEOuTk9yHgm37oihKsg@mail.gmail.com/3-create_comments.sql)
  download

^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-07-23 02:44  Josh Kupershmidt <schmiddy@gmail.com>
  parent: Josh Kupershmidt <schmiddy@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Josh Kupershmidt @ 2011-07-23 02:44 UTC (permalink / raw)
  To: pgsql-hackers

On Sat, Jul 9, 2011 at 1:16 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:

Attached is an updated version of this patch, lifted out of the recent
pg_comments patch. With this v2 patch, \dd should properly show just
its five object types, and the psql documentation and help strings
should be fixed.

> Fourth, there is a bug in at least the display of comments for indexes
> using \d+ (the "Description" is empty when it shouldn't be).

BTW, this gripe is being dealt with separately in thread "psql: bogus
descriptions displayed by \d+".

Josh

Attachments:

  [application/octet-stream] describe_comments.v2.patch (74.6K, ../../CAK3UJRFswA1HXLT_5Od2kcJKfb-72f5fo3vNQY2Nu8WEf0taSg@mail.gmail.com/2-describe_comments.v2.patch)
  download | inline diff:
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
new file mode 100644
index 6385c78..4ff22e3
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
*************** testdb=&gt;
*** 966,972 ****
  
  
        <varlistentry>
!         <term><literal>\dC [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
          Lists type casts.
--- 966,972 ----
  
  
        <varlistentry>
!         <term><literal>\dC[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
          Lists type casts.
*************** testdb=&gt;
*** 982,1005 ****
          <term><literal>\dd[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
!         Shows the descriptions of objects matching the <replaceable
!         class="parameter">pattern</replaceable>, or of all visible objects if
!         no argument is given.  But in either case, only objects that have
!         a description are listed.
          By default, only user-created objects are shown;  supply a
          pattern or the <literal>S</literal> modifier to include system
          objects.
-         <quote>Object</quote> covers aggregates, functions, operators,
-         types, relations (tables, views, indexes, sequences), large
-         objects, rules, and triggers. For example:
- <programlisting>
- =&gt; <userinput>\dd version</userinput>
-                      Object descriptions
-    Schema   |  Name   |  Object  |        Description
- ------------+---------+----------+---------------------------
-  pg_catalog | version | function | PostgreSQL version string
- (1 row)
- </programlisting>
          </para>
  
          <para>
--- 982,1002 ----
          <term><literal>\dd[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
!         Shows the descriptions of objects of type <literal>constraint</>,
!         <literal>operator class</>, <literal>operator family</>,
!         <literal>rule</>, and <literal>trigger</>. All
!         other comments may be viewed by the respective backslash commands for
!         those object types.
!         </para>
! 
!         <para>
!         <literal>\dd</literal> displays descriptions for objects matching the
!         <replaceable class="parameter">pattern</replaceable>, or of visible
!         objects of the appropriate type if no argument is given.  But in either
!         case, only objects that have a description are listed.
          By default, only user-created objects are shown;  supply a
          pattern or the <literal>S</literal> modifier to include system
          objects.
          </para>
  
          <para>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
new file mode 100644
index 16ff9e9..ee4c4a2
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
*************** exec_command(const char *cmd,
*** 381,387 ****
  				success = listConversions(pattern, show_system);
  				break;
  			case 'C':
! 				success = listCasts(pattern);
  				break;
  			case 'd':
  				if (strncmp(cmd, "ddp", 3) == 0)
--- 381,387 ----
  				success = listConversions(pattern, show_system);
  				break;
  			case 'C':
! 				success = listCasts(pattern, show_verbose);
  				break;
  			case 'd':
  				if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
new file mode 100644
index 682cf8a..12477fd
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
*************** listDefaultACLs(const char *pattern)
*** 830,837 ****
   *
   * \dd [foo]
   *
!  * Note: This only lists things that actually have a description. For complete
!  * lists of things, there are other \d? commands.
   */
  bool
  objectDescription(const char *pattern, bool showSystem)
--- 830,840 ----
   *
   * \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)
*************** objectDescription(const char *pattern, b
*** 851,959 ****
  					  gettext_noop("Object"),
  					  gettext_noop("Description"));
  
! 	/* Aggregate descriptions */
  	appendPQExpBuffer(&buf,
! 					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
  					  "  n.nspname as nspname,\n"
! 					  "  CAST(p.proname AS pg_catalog.text) as name,"
  					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_proc p\n"
! 	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
! 					  "  WHERE p.proisagg\n",
! 					  gettext_noop("aggregate"));
  
  	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)");
  
! 	/* Function descriptions */
  	appendPQExpBuffer(&buf,
  					  "UNION ALL\n"
! 					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
  					  "  n.nspname as nspname,\n"
! 					  "  CAST(p.proname AS pg_catalog.text) as name,"
  					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_proc p\n"
! 	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
! 					  "  WHERE NOT p.proisagg\n",
! 					  gettext_noop("function"));
  
  	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)");
! 
! 	/* Operator descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  CAST(o.oprname AS pg_catalog.text) as name,"
! 					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_operator o\n"
! 	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
! 					  gettext_noop("operator"));
! 
! 	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", "o.oprname", NULL,
! 						  "pg_catalog.pg_operator_is_visible(o.oid)");
! 
! 	/* Type descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  pg_catalog.format_type(t.oid, NULL) as name,"
! 					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_type t\n"
! 	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
! 					  gettext_noop("data type"));
! 
! 	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", "pg_catalog.format_type(t.oid, NULL)",
! 						  NULL,
! 						  "pg_catalog.pg_type_is_visible(t.oid)");
  
! 	/* Relation (tables, views, indexes, sequences) descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  CAST(c.relname AS pg_catalog.text) as name,\n"
! 					  "  CAST(\n"
! 					  "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
! 					  "  AS pg_catalog.text) as object\n"
! 					  "  FROM 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', 'i', 'S', 'f')\n",
! 					  gettext_noop("table"),
! 					  gettext_noop("view"),
! 					  gettext_noop("index"),
! 					  gettext_noop("sequence"),
! 					  gettext_noop("foreign table"));
  
! 	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.relname", NULL,
- 						  "pg_catalog.pg_table_is_visible(c.oid)");
  
  	/* Rule descriptions (ignore rules for views) */
  	appendPQExpBuffer(&buf,
--- 854,930 ----
  					  gettext_noop("Object"),
  					  gettext_noop("Description"));
  
! 	/* Constraint descriptions */
  	appendPQExpBuffer(&buf,
! 					  "  SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
  					  "  n.nspname as nspname,\n"
! 					  "  CAST(pgc.conname AS pg_catalog.text) as name,"
  					  "  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");
  
! 	/* XXX not sure what to do about visibility rule here? */
! 	processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern,
! 						  false, "n.nspname", "pgc.conname", NULL,
! 						  "pg_catalog.pg_table_is_visible(c.oid)");
  
! 	/* 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");
  
+ 	/* XXX not sure what to do about visibility rule here? */
  	processSQLNamePattern(pset.db, &buf, pattern, true, false,
! 						  "n.nspname", "o.opcname", NULL,
! 						  "pg_catalog.pg_opclass_is_visible(o.oid)");
  
! 	/* comment on operator family only available in 8.3+ */
! 	if (pset.sversion >= 80300)
! 	{
! 		/* 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");
  
! 		/* XXX not sure what to do about visibility rule here? */
! 		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,
*************** listLanguages(const char *pattern, bool 
*** 2631,2638 ****
  						  gettext_noop("Owner"));
  
  	appendPQExpBuffer(&buf,
! 					  "       l.lanpltrusted AS \"%s\"",
! 					  gettext_noop("Trusted"));
  
  	if (verbose)
  	{
--- 2602,2611 ----
  						  gettext_noop("Owner"));
  
  	appendPQExpBuffer(&buf,
! 					  "       l.lanpltrusted AS \"%s\",\n"
! 					  "       d.description AS \"%s\"",
! 					  gettext_noop("Trusted"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  	{
*************** listLanguages(const char *pattern, bool 
*** 2650,2664 ****
  	}
  
  	appendPQExpBuffer(&buf,
! 					  "\nFROM pg_catalog.pg_language l\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "l.lanname", NULL, NULL);
  
  	if (!showSystem && !pattern)
! 		appendPQExpBuffer(&buf, "WHERE lanplcallfoid != 0\n");
  
! 	appendPQExpBuffer(&buf, "ORDER BY 1;");
  
  	res = PSQLexec(buf.data, false);
  	termPQExpBuffer(&buf);
--- 2623,2641 ----
  	}
  
  	appendPQExpBuffer(&buf,
! 					  "\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");
  
  	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "l.lanname", NULL, NULL);
  
+ 	appendPQExpBuffer(&buf, "WHERE (d.objsubid IS NULL OR d.objsubid = 0) ");
+ 
  	if (!showSystem && !pattern)
! 		appendPQExpBuffer(&buf, "AND lanplcallfoid != 0");
  
! 	appendPQExpBuffer(&buf, "\nORDER BY 1;");
  
  	res = PSQLexec(buf.data, false);
  	termPQExpBuffer(&buf);
*************** listDomains(const char *pattern, bool sh
*** 2710,2720 ****
  	appendPQExpBuffer(&buf,
  					  "       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\"\n"
  					  "FROM pg_catalog.pg_type t\n"
  	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
! 					  "WHERE t.typtype = 'd'\n",
! 					  gettext_noop("Check"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
--- 2687,2702 ----
  	appendPQExpBuffer(&buf,
  					  "       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\",\n"
! 					  "       d.description as \"%s\"\n"
  					  "FROM pg_catalog.pg_type t\n"
  	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
! 					  "     LEFT JOIN pg_catalog.pg_description d "
! 					  "ON d.classoid = t.tableoid AND d.objoid = t.oid\n"
! 					  "WHERE t.typtype = 'd' AND\n"
! 					  "(d.objsubid IS NULL OR d.objsubid = 0)\n",
! 					  gettext_noop("Check"),
! 					  gettext_noop("Description"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
*************** listConversions(const char *pattern, boo
*** 2762,2776 ****
  	   "       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\"\n"
! 			   "FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n\n"
! 					  "WHERE n.oid = c.connamespace\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("Source"),
  					  gettext_noop("Destination"),
  					  gettext_noop("yes"), gettext_noop("no"),
! 					  gettext_noop("Default?"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
--- 2744,2764 ----
  	   "       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\",\n"
! 					  "       d.description AS \"%s\"\n"
! 					  "FROM pg_catalog.pg_conversion c "
! 					  "JOIN pg_catalog.pg_namespace n "
! 					  "ON n.oid = c.connamespace\n"
! 					  "LEFT JOIN pg_catalog.pg_description d "
! 					  "ON d.classoid = c.tableoid AND d.objoid = c.oid\n"
! 					  "WHERE (d.objsubid = 0 OR d.objsubid IS NULL)\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("Source"),
  					  gettext_noop("Destination"),
  					  gettext_noop("yes"), gettext_noop("no"),
! 					  gettext_noop("Default?"),
! 					  gettext_noop("Description"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
*************** listConversions(const char *pattern, boo
*** 2804,2810 ****
   * Describes casts.
   */
  bool
! listCasts(const char *pattern)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
--- 2792,2798 ----
   * Describes casts.
   */
  bool
! listCasts(const char *pattern, bool verbose)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
*************** listCasts(const char *pattern)
*** 2828,2834 ****
  					  "       CASE WHEN c.castcontext = 'e' THEN '%s'\n"
  					  "            WHEN c.castcontext = 'a' THEN '%s'\n"
  					  "            ELSE '%s'\n"
! 					  "       END as \"%s\"\n"
  				 "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"
--- 2816,2836 ----
  					  "       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,
  				 "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"
*************** listCasts(const char *pattern)
*** 2838,2850 ****
  					  "     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"
! 					  "WHERE (true",
! 					  gettext_noop("Source type"),
! 					  gettext_noop("Target type"),
! 					  gettext_noop("Function"),
! 	  gettext_noop("no"), gettext_noop("in assignment"), gettext_noop("yes"),
! 					  gettext_noop("Implicit?"));
  
  	/*
  	 * Match name pattern against either internal or external name of either
--- 2840,2853 ----
  					  "     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\n");
! 
! 	appendPQExpBuffer(&buf, "WHERE ( (true");
  
  	/*
  	 * Match name pattern against either internal or external name of either
*************** listCasts(const char *pattern)
*** 2862,2868 ****
  						  "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);
--- 2865,2876 ----
  						  "pg_catalog.format_type(tt.oid, NULL)",
  						  "pg_catalog.pg_type_is_visible(tt.oid)");
  
! 	appendPQExpBuffer(&buf, ") )");
! 
! 	if (verbose)
! 		appendPQExpBuffer(&buf, "AND (d.objsubid IS NULL OR d.objsubid = 0)");
! 
! 	appendPQExpBuffer(&buf, "\nORDER BY 1, 2;");
  
  	res = PSQLexec(buf.data, false);
  	termPQExpBuffer(&buf);
*************** listForeignDataWrappers(const char *patt
*** 3593,3610 ****
  
  	initPQExpBuffer(&buf);
  	printfPQExpBuffer(&buf,
! 					  "SELECT fdwname AS \"%s\",\n"
! 					  "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n",
  					  gettext_noop("Name"),
  					  gettext_noop("Owner"));
  	if (pset.sversion >= 90100)
  		appendPQExpBuffer(&buf,
! 						  "  fdwhandler::pg_catalog.regproc AS \"%s\",\n",
  						  gettext_noop("Handler"));
  	appendPQExpBuffer(&buf,
! 					  "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
  					  gettext_noop("Validator"));
  
  	if (verbose)
  	{
  		appendPQExpBuffer(&buf, ",\n  ");
--- 3601,3623 ----
  
  	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 (pset.sversion >= 90100)
+ 		appendPQExpBuffer(&buf,
+ 						  ",\nd.description AS \"%s\" ",
+ 						  gettext_noop("Description"));
+ 
  	if (verbose)
  	{
  		appendPQExpBuffer(&buf, ",\n  ");
*************** listForeignDataWrappers(const char *patt
*** 3614,3622 ****
  						  gettext_noop("Options"));
  	}
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "fdwname", NULL, NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
--- 3627,3643 ----
  						  gettext_noop("Options"));
  	}
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
  
! 	if (pset.sversion >= 90100)
! 		appendPQExpBuffer(&buf,
! 						  "LEFT JOIN pg_catalog.pg_description d\n"
! 						  "ON d.classoid = fdw.tableoid "
! 						  "AND d.objoid = fdw.oid\n");
! 
! 	appendPQExpBuffer(&buf, "WHERE (d.objsubid IS NULL OR d.objsubid = 0)\n");
! 
! 	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "fdwname", NULL, NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
*************** listForeignServers(const char *pattern, 
*** 3659,3668 ****
  	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)
  	{
--- 3680,3691 ----
  	printfPQExpBuffer(&buf,
  					  "SELECT s.srvname AS \"%s\",\n"
  					  "  pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
! 					  "  f.fdwname AS \"%s\",\n"
! 					  "  d.description AS \"%s\"",
  					  gettext_noop("Name"),
  					  gettext_noop("Owner"),
! 					  gettext_noop("Foreign-data wrapper"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  	{
*************** listForeignServers(const char *pattern, 
*** 3680,3688 ****
  
  	appendPQExpBuffer(&buf,
  					  "\nFROM pg_catalog.pg_foreign_server s\n"
! 	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "s.srvname", NULL, NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
--- 3703,3714 ----
  
  	appendPQExpBuffer(&buf,
  					  "\nFROM pg_catalog.pg_foreign_server s\n"
! 	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n"
! 					  "LEFT JOIN pg_description d "
! 					  "ON d.classoid = s.tableoid AND d.objoid = s.oid\n"
! 					  "WHERE (d.objsubid IS NULL OR d.objsubid = 0)\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "s.srvname", NULL, NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
*************** listForeignTables(const char *pattern, b
*** 3778,3800 ****
  	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  ft.ftoptions AS \"%s\"",
  						  gettext_noop("Options"));
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
! 	appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
! 	appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
! 	appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "n.nspname", "c.relname", NULL);
--- 3804,3831 ----
  	printfPQExpBuffer(&buf,
  					  "SELECT n.nspname AS \"%s\",\n"
  					  "  c.relname AS \"%s\",\n"
! 					  "  s.srvname AS \"%s\",\n"
! 					  "  d.description AS \"%s\"\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Table"),
! 					  gettext_noop("Server"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  		appendPQExpBuffer(&buf,
  						  ",\n  ft.ftoptions AS \"%s\"",
  						  gettext_noop("Options"));
  
! 	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"
! 					  "  LEFT JOIN pg_catalog.pg_description d"
! 					  "\n    ON d.classoid = c.tableoid AND d.objoid = c.oid\n"
! 					  "WHERE (d.objsubid IS NULL OR d.objsubid = 0)\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "n.nspname", "c.relname", NULL);
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
new file mode 100644
index fb86d1e..7dc9a2c
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
*************** extern bool listDomains(const char *patt
*** 67,73 ****
  extern bool listConversions(const char *pattern, bool showSystem);
  
  /* \dC */
! extern bool listCasts(const char *pattern);
  
  /* \dO */
  extern bool listCollations(const char *pattern, bool verbose, bool showSystem);
--- 67,73 ----
  extern bool listConversions(const char *pattern, bool showSystem);
  
  /* \dC */
! extern bool listCasts(const char *pattern, bool verbose);
  
  /* \dO */
  extern bool listCollations(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
new file mode 100644
index e56ab61..5b294f7
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
*************** slashUsage(unsigned short int pager)
*** 196,203 ****
  	fprintf(output, _("  \\da[S]  [PATTERN]      list aggregates\n"));
  	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
  	fprintf(output, _("  \\dc[S]  [PATTERN]      list conversions\n"));
! 	fprintf(output, _("  \\dC     [PATTERN]      list casts\n"));
! 	fprintf(output, _("  \\dd[S]  [PATTERN]      show comments on objects\n"));
  	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
  	fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
  	fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
--- 196,203 ----
  	fprintf(output, _("  \\da[S]  [PATTERN]      list aggregates\n"));
  	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
  	fprintf(output, _("  \\dc[S]  [PATTERN]      list conversions\n"));
! 	fprintf(output, _("  \\dC[+]  [PATTERN]      list casts\n"));
! 	fprintf(output, _("  \\dd[S]  [PATTERN]      show object descriptions not displayed elsewhere\n"));
  	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
  	fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
  	fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
new file mode 100644
index 2b3eddf..8d1bc16
*** a/src/test/regress/expected/foreign_data.out
--- b/src/test/regress/expected/foreign_data.out
*************** CREATE FOREIGN DATA WRAPPER foo VALIDATO
*** 39,50 ****
  ERROR:  function bar(text[], oid) does not exist
  CREATE FOREIGN DATA WRAPPER foo;
  \dew
!                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         
! ------------+-------------------+---------+--------------------------
!  dummy      | foreign_data_user | -       | -
!  foo        | foreign_data_user | -       | -
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator
  (3 rows)
  
  CREATE FOREIGN DATA WRAPPER foo; -- duplicate
--- 39,50 ----
  ERROR:  function bar(text[], oid) does not exist
  CREATE FOREIGN DATA WRAPPER foo;
  \dew
!                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description 
! ------------+-------------------+---------+--------------------------+-------------
!  dummy      | foreign_data_user | -       | -                        | useless
!  foo        | foreign_data_user | -       | -                        | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator | 
  (3 rows)
  
  CREATE FOREIGN DATA WRAPPER foo; -- duplicate
*************** ERROR:  foreign-data wrapper "foo" alrea
*** 52,63 ****
  DROP FOREIGN DATA WRAPPER foo;
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
  \dew+
!                                      List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   
! ------------+-------------------+---------+--------------------------+-------------------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
--- 52,63 ----
  DROP FOREIGN DATA WRAPPER foo;
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
  \dew+
!                                             List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |   Options   
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {testing=1}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
*************** CREATE FOREIGN DATA WRAPPER foo OPTIONS 
*** 65,76 ****
  ERROR:  option "testing" provided more than once
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        
! ------------+-------------------+---------+--------------------------+-------------------+-----------------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
--- 65,76 ----
  ERROR:  option "testing" provided more than once
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
  \dew+
!                                                  List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |        Options        
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {testing=1,another=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
*************** HINT:  Must be superuser to create a for
*** 81,92 ****
  RESET ROLE;
  CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | postgresql_fdw_validator |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  -- ALTER FOREIGN DATA WRAPPER
--- 81,92 ----
  RESET ROLE;
  CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  -- ALTER FOREIGN DATA WRAPPER
*************** ALTER FOREIGN DATA WRAPPER foo VALIDATOR
*** 98,109 ****
  ERROR:  function bar(text[], oid) does not exist
  ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
--- 98,109 ----
  ERROR:  function bar(text[], oid) does not exist
  ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
*************** ALTER FOREIGN DATA WRAPPER foo OPTIONS (
*** 113,146 ****
  ERROR:  option "c" not found
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
  \dew+
!                                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
  \dew+
!                                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
  ERROR:  option "b" provided more than once
  \dew+
!                                       List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    
! ------------+-------------------+---------+--------------------------+-------------------+---------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  SET ROLE regress_test_role;
--- 113,146 ----
  ERROR:  option "c" not found
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
  \dew+
!                                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {a=1,b=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
  \dew+
!                                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
  ERROR:  option "b" provided more than once
  \dew+
!                                              List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |    Options    
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4,a=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  SET ROLE regress_test_role;
*************** HINT:  Must be superuser to alter a fore
*** 150,161 ****
  SET ROLE regress_test_role_super;
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
  \dew+
!                                         List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
--- 150,161 ----
  SET ROLE regress_test_role_super;
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
  \dew+
!                                                List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
*************** ERROR:  permission denied to alter forei
*** 169,180 ****
  HINT:  Must be superuser to alter a foreign-data wrapper.
  RESET ROLE;
  \dew+
!                                            List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        |                   | 
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  -- DROP FOREIGN DATA WRAPPER
--- 169,180 ----
  HINT:  Must be superuser to alter a foreign-data wrapper.
  RESET ROLE;
  \dew+
!                                                   List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        | useless     |                   | 
!  foo        | regress_test_role_super | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  -- DROP FOREIGN DATA WRAPPER
*************** ERROR:  foreign-data wrapper "nonexisten
*** 183,194 ****
  DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
  NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
  \dew+
!                                            List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        |                   | 
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP ROLE regress_test_role_super;                          -- ERROR
--- 183,194 ----
  DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
  NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
  \dew+
!                                                   List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        | useless     |                   | 
!  foo        | regress_test_role_super | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP ROLE regress_test_role_super;                          -- ERROR
*************** ALTER ROLE regress_test_role_super SUPER
*** 203,213 ****
  DROP FOREIGN DATA WRAPPER foo;
  DROP ROLE regress_test_role_super;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (2 rows)
  
  CREATE FOREIGN DATA WRAPPER foo;
--- 203,213 ----
  DROP FOREIGN DATA WRAPPER foo;
  DROP ROLE regress_test_role_super;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (2 rows)
  
  CREATE FOREIGN DATA WRAPPER foo;
*************** CREATE SERVER s1 FOREIGN DATA WRAPPER fo
*** 215,233 ****
  COMMENT ON SERVER s1 IS 'foreign server';
  CREATE USER MAPPING FOR current_user SERVER s1;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  \des+
!                                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges | Type | Version | Options 
! ------+-------------------+----------------------+-------------------+------+---------+---------
!  s1   | foreign_data_user | foo                  |                   |      |         | 
  (1 row)
  
  \deu+
--- 215,233 ----
  COMMENT ON SERVER s1 IS 'foreign server';
  CREATE USER MAPPING FOR current_user SERVER s1;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  \des+
!                                              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper |  Description   | Access privileges | Type | Version | Options 
! ------+-------------------+----------------------+----------------+-------------------+------+---------+---------
!  s1   | foreign_data_user | foo                  | foreign server |                   |      |         | 
  (1 row)
  
  \deu+
*************** NOTICE:  drop cascades to 2 other object
*** 252,268 ****
  DETAIL:  drop cascades to server s1
  drop cascades to user mapping for foreign_data_user
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (2 rows)
  
  \des+
!                               List of foreign servers
!  Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options 
! ------+-------+----------------------+-------------------+------+---------+---------
  (0 rows)
  
  \deu+
--- 252,268 ----
  DETAIL:  drop cascades to server s1
  drop cascades to user mapping for foreign_data_user
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (2 rows)
  
  \des+
!                                      List of foreign servers
!  Name | Owner | Foreign-data wrapper | Description | Access privileges | Type | Version | Options 
! ------+-------+----------------------+-------------+-------------------+------+---------+---------
  (0 rows)
  
  \deu+
*************** ERROR:  invalid option "foo"
*** 289,305 ****
  HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
  CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
  (8 rows)
  
  SET ROLE regress_test_role;
--- 289,305 ----
  HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
  CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
  (8 rows)
  
  SET ROLE regress_test_role;
*************** SET ROLE regress_test_role;
*** 311,328 ****
  CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
  RESET ROLE;
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                   |        |         | 
  (9 rows)
  
  REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
--- 311,328 ----
  CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
  RESET ROLE;
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                   |        |         | 
  (9 rows)
  
  REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
*************** GRANT regress_test_indirect TO regress_t
*** 335,353 ****
  SET ROLE regress_test_role;
  CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                   |        |         | 
!  t2   | regress_test_role | foo                  |                   |        |         | 
  (10 rows)
  
  RESET ROLE;
--- 335,353 ----
  SET ROLE regress_test_role;
  CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                   |        |         | 
!  t2   | regress_test_role | foo                  |             |                   |        |         | 
  (10 rows)
  
  RESET ROLE;
*************** ALTER SERVER s3 OPTIONS (tnsname 'orcl',
*** 365,385 ****
  GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
  GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
  \des+
!                                                            List of foreign servers
!  Name |       Owner       | Foreign-data wrapper |            Access privileges            |  Type  | Version |           Options            
! ------+-------------------+----------------------+-----------------------------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
!       |                   |                      | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user | foo                  |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                                         |        | 15.0    | 
!  s6   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                   |                      | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                                         |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                                         |        |         | 
!  t2   | regress_test_role | foo                  |                                         |        |         | 
  (10 rows)
  
  SET ROLE regress_test_role;
--- 365,385 ----
  GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
  GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
  \des+
!                                                                   List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description |            Access privileges            |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-----------------------------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
!       |                   |                      |             | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user | foo                  |             |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                                         |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                   |                      |             | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user | foo                  |             |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                                         |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                                         |        |         | 
!  t2   | regress_test_role | foo                  |             |                                         |        |         | 
  (10 rows)
  
  SET ROLE regress_test_role;
*************** ERROR:  role "regress_test_indirect" can
*** 416,436 ****
  DETAIL:  owner of server s1
  privileges for foreign-data wrapper foo
  \des+
!                                                               List of foreign servers
!  Name |         Owner         | Foreign-data wrapper |            Access privileges            |  Type  | Version |             Options             
! ------+-----------------------+----------------------+-----------------------------------------+--------+---------+---------------------------------
!  s1   | regress_test_indirect | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
!       |                       |                      | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user     | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user     | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user     | foo                  |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user     | foo                  |                                         |        | 15.0    | 
!  s6   | foreign_data_user     | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                       |                      | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user     | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user     | postgresql           |                                         |        |         | {dbname=db1,connect_timeout=30}
!  t1   | regress_test_role     | foo                  |                                         |        |         | 
!  t2   | regress_test_role     | foo                  |                                         |        |         | 
  (10 rows)
  
  -- DROP SERVER
--- 416,436 ----
  DETAIL:  owner of server s1
  privileges for foreign-data wrapper foo
  \des+
!                                                                      List of foreign servers
!  Name |         Owner         | Foreign-data wrapper | Description |            Access privileges            |  Type  | Version |             Options             
! ------+-----------------------+----------------------+-------------+-----------------------------------------+--------+---------+---------------------------------
!  s1   | regress_test_indirect | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
!       |                       |                      |             | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user     | foo                  |             |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user     | foo                  |             |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user     | foo                  |             |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user     | foo                  |             |                                         |        | 15.0    | 
!  s6   | foreign_data_user     | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                       |                      |             | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user     | foo                  |             |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user     | postgresql           |             |                                         |        |         | {dbname=db1,connect_timeout=30}
!  t1   | regress_test_role     | foo                  |             |                                         |        |         | 
!  t2   | regress_test_role     | foo                  |             |                                         |        |         | 
  (10 rows)
  
  -- DROP SERVER
*************** ERROR:  server "nonexistent" does not ex
*** 439,457 ****
  DROP SERVER IF EXISTS nonexistent;
  NOTICE:  server "nonexistent" does not exist, skipping
  \des
!                List of foreign servers
!  Name |         Owner         | Foreign-data wrapper 
! ------+-----------------------+----------------------
!  s1   | regress_test_indirect | foo
!  s2   | foreign_data_user     | foo
!  s3   | foreign_data_user     | foo
!  s4   | foreign_data_user     | foo
!  s5   | foreign_data_user     | foo
!  s6   | foreign_data_user     | foo
!  s7   | foreign_data_user     | foo
!  s8   | foreign_data_user     | postgresql
!  t1   | regress_test_role     | foo
!  t2   | regress_test_role     | foo
  (10 rows)
  
  SET ROLE regress_test_role;
--- 439,457 ----
  DROP SERVER IF EXISTS nonexistent;
  NOTICE:  server "nonexistent" does not exist, skipping
  \des
!                       List of foreign servers
!  Name |         Owner         | Foreign-data wrapper | Description 
! ------+-----------------------+----------------------+-------------
!  s1   | regress_test_indirect | foo                  | 
!  s2   | foreign_data_user     | foo                  | 
!  s3   | foreign_data_user     | foo                  | 
!  s4   | foreign_data_user     | foo                  | 
!  s5   | foreign_data_user     | foo                  | 
!  s6   | foreign_data_user     | foo                  | 
!  s7   | foreign_data_user     | foo                  | 
!  s8   | foreign_data_user     | postgresql           | 
!  t1   | regress_test_role     | foo                  | 
!  t2   | regress_test_role     | foo                  | 
  (10 rows)
  
  SET ROLE regress_test_role;
*************** ERROR:  must be owner of foreign server 
*** 460,477 ****
  DROP SERVER s1;
  RESET ROLE;
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s2   | foreign_data_user | foo
!  s3   | foreign_data_user | foo
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (9 rows)
  
  ALTER SERVER s2 OWNER TO regress_test_role;
--- 460,477 ----
  DROP SERVER s1;
  RESET ROLE;
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s2   | foreign_data_user | foo                  | 
!  s3   | foreign_data_user | foo                  | 
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (9 rows)
  
  ALTER SERVER s2 OWNER TO regress_test_role;
*************** SET ROLE regress_test_role;
*** 479,495 ****
  DROP SERVER s2;
  RESET ROLE;
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s3   | foreign_data_user | foo
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (8 rows)
  
  CREATE USER MAPPING FOR current_user SERVER s3;
--- 479,495 ----
  DROP SERVER s2;
  RESET ROLE;
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s3   | foreign_data_user | foo                  | 
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (8 rows)
  
  CREATE USER MAPPING FOR current_user SERVER s3;
*************** HINT:  Use DROP ... CASCADE to drop the 
*** 507,522 ****
  DROP SERVER s3 CASCADE;
  NOTICE:  drop cascades to user mapping for foreign_data_user
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (7 rows)
  
  \deu
--- 507,522 ----
  DROP SERVER s3 CASCADE;
  NOTICE:  drop cascades to user mapping for foreign_data_user
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (7 rows)
  
  \deu
*************** Server: sc
*** 663,672 ****
  Has OIDs: no
  
  \det+
!                 List of foreign tables
!  Schema | Table | Server |          Options           
! --------+-------+--------+----------------------------
!  public | ft1   | sc     | {"delimiter=,","quote=\""}
  (1 row)
  
  CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
--- 663,672 ----
  Has OIDs: no
  
  \det+
!                        List of foreign tables
!  Schema | Table | Server | Description |          Options           
! --------+-------+--------+-------------+----------------------------
!  public | ft1   | sc     | ft1         | {"delimiter=,","quote=\""}
  (1 row)
  
  CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR


^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-07-25 16:27  Robert Haas <robertmhaas@gmail.com>
  parent: Josh Kupershmidt <schmiddy@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Robert Haas @ 2011-07-25 16:27 UTC (permalink / raw)
  To: Josh Kupershmidt <schmiddy@gmail.com>; +Cc: pgsql-hackers

On Fri, Jul 22, 2011 at 10:44 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
> On Sat, Jul 9, 2011 at 1:16 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
>
> Attached is an updated version of this patch, lifted out of the recent
> pg_comments patch. With this v2 patch, \dd should properly show just
> its five object types, and the psql documentation and help strings
> should be fixed.

I took a look at this patch today and I think some of these queries
are not quite right.  When you do a left join against pg_description,
you have this sort of thing in the WHERE clause:

(d.objsubid IS NULL OR d.objsubid = 0)

I think what you actually want is "AND d.objsubid = 0" in the LEFT
JOIN's "ON" clause.  Then you are, in effect, only left joining
against the rows from pg_description where objsubid = 0, and
null-extending if none such is found.  I think that's what you want.

I think you can remove the XXX comments, too.  Unless I'm
misunderstanding something, using the table to test visibility for
constraints, rules, and triggers seems just right, and opclasses and
opfamilies you have a suitable function available, so those don't seem
problematic.  Or am I confused?

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-07-27 00:38  Josh Kupershmidt <schmiddy@gmail.com>
  parent: Robert Haas <robertmhaas@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Josh Kupershmidt @ 2011-07-27 00:38 UTC (permalink / raw)
  To: Robert Haas <robertmhaas@gmail.com>; +Cc: pgsql-hackers

On Mon, Jul 25, 2011 at 12:27 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Fri, Jul 22, 2011 at 10:44 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
>> On Sat, Jul 9, 2011 at 1:16 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
>>
>> Attached is an updated version of this patch, lifted out of the recent
>> pg_comments patch. With this v2 patch, \dd should properly show just
>> its five object types, and the psql documentation and help strings
>> should be fixed.
>
> I took a look at this patch today and I think some of these queries
> are not quite right.  When you do a left join against pg_description,
> you have this sort of thing in the WHERE clause:
>
> (d.objsubid IS NULL OR d.objsubid = 0)
>
> I think what you actually want is "AND d.objsubid = 0" in the LEFT
> JOIN's "ON" clause.  Then you are, in effect, only left joining
> against the rows from pg_description where objsubid = 0, and
> null-extending if none such is found.  I think that's what you want.

Thanks for taking a look at this.

Yeah, that is what I was going for. I believe my way worked, since
pg_description declares objsubid NOT NULL, so checking whether
d.objsubid IS NULL in the where clause should just match null-extended
rows. But anyway, your version is clearer, changed.

> I think you can remove the XXX comments, too.  Unless I'm
> misunderstanding something, using the table to test visibility for
> constraints, rules, and triggers seems just right, and opclasses and
> opfamilies you have a suitable function available, so those don't seem
> problematic.  Or am I confused?

I bet you're right; at least, it seems to work reasonably in the
testing I've done. I had just left those comments in there since they
were in all the sub-queries of objectDescription(), but now seems like
a good time to get rid of them.

Josh

Attachments:

  [text/x-patch] describe_comments.v3.patch (74.7K, ../../CAK3UJRG9m0=8AzJqkcZCGr7c-wy=wU7rfgfR5hm10rUTh+9nmw@mail.gmail.com/2-describe_comments.v3.patch)
  download | inline diff:
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 54fc3ee..c5c4cbd 100644
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
*************** testdb=&gt;
*** 966,972 ****
  
  
        <varlistentry>
!         <term><literal>\dC [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
          Lists type casts.
--- 966,972 ----
  
  
        <varlistentry>
!         <term><literal>\dC[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
          Lists type casts.
*************** testdb=&gt;
*** 982,1005 ****
          <term><literal>\dd[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
!         Shows the descriptions of objects matching the <replaceable
!         class="parameter">pattern</replaceable>, or of all visible objects if
!         no argument is given.  But in either case, only objects that have
!         a description are listed.
          By default, only user-created objects are shown;  supply a
          pattern or the <literal>S</literal> modifier to include system
          objects.
-         <quote>Object</quote> covers aggregates, functions, operators,
-         types, relations (tables, views, indexes, sequences), large
-         objects, rules, and triggers. For example:
- <programlisting>
- =&gt; <userinput>\dd version</userinput>
-                      Object descriptions
-    Schema   |  Name   |  Object  |        Description
- ------------+---------+----------+---------------------------
-  pg_catalog | version | function | PostgreSQL version string
- (1 row)
- </programlisting>
          </para>
  
          <para>
--- 982,1002 ----
          <term><literal>\dd[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
!         Shows the descriptions of objects of type <literal>constraint</>,
!         <literal>operator class</>, <literal>operator family</>,
!         <literal>rule</>, and <literal>trigger</>. All
!         other comments may be viewed by the respective backslash commands for
!         those object types.
!         </para>
! 
!         <para>
!         <literal>\dd</literal> displays descriptions for objects matching the
!         <replaceable class="parameter">pattern</replaceable>, or of visible
!         objects of the appropriate type if no argument is given.  But in either
!         case, only objects that have a description are listed.
          By default, only user-created objects are shown;  supply a
          pattern or the <literal>S</literal> modifier to include system
          objects.
          </para>
  
          <para>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index aeabe5b..0b20581 100644
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
*************** exec_command(const char *cmd,
*** 381,387 ****
  				success = listConversions(pattern, show_system);
  				break;
  			case 'C':
! 				success = listCasts(pattern);
  				break;
  			case 'd':
  				if (strncmp(cmd, "ddp", 3) == 0)
--- 381,387 ----
  				success = listConversions(pattern, show_system);
  				break;
  			case 'C':
! 				success = listCasts(pattern, show_verbose);
  				break;
  			case 'd':
  				if (strncmp(cmd, "ddp", 3) == 0)
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 682cf8a..dd75711 100644
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
*************** listDefaultACLs(const char *pattern)
*** 830,837 ****
   *
   * \dd [foo]
   *
!  * Note: This only lists things that actually have a description. For complete
!  * lists of things, there are other \d? commands.
   */
  bool
  objectDescription(const char *pattern, bool showSystem)
--- 830,840 ----
   *
   * \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)
*************** objectDescription(const char *pattern, b
*** 851,959 ****
  					  gettext_noop("Object"),
  					  gettext_noop("Description"));
  
! 	/* Aggregate descriptions */
  	appendPQExpBuffer(&buf,
! 					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
  					  "  n.nspname as nspname,\n"
! 					  "  CAST(p.proname AS pg_catalog.text) as name,"
  					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_proc p\n"
! 	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
! 					  "  WHERE p.proisagg\n",
! 					  gettext_noop("aggregate"));
  
  	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)");
  
! 	/* Function descriptions */
  	appendPQExpBuffer(&buf,
  					  "UNION ALL\n"
! 					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
  					  "  n.nspname as nspname,\n"
! 					  "  CAST(p.proname AS pg_catalog.text) as name,"
  					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_proc p\n"
! 	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
! 					  "  WHERE NOT p.proisagg\n",
! 					  gettext_noop("function"));
  
  	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)");
! 
! 	/* Operator descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  CAST(o.oprname AS pg_catalog.text) as name,"
! 					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_operator o\n"
! 	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
! 					  gettext_noop("operator"));
! 
! 	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", "o.oprname", NULL,
! 						  "pg_catalog.pg_operator_is_visible(o.oid)");
! 
! 	/* Type descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  pg_catalog.format_type(t.oid, NULL) as name,"
! 					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_type t\n"
! 	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
! 					  gettext_noop("data type"));
! 
! 	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", "pg_catalog.format_type(t.oid, NULL)",
! 						  NULL,
! 						  "pg_catalog.pg_type_is_visible(t.oid)");
  
! 	/* Relation (tables, views, indexes, sequences) descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  CAST(c.relname AS pg_catalog.text) as name,\n"
! 					  "  CAST(\n"
! 					  "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
! 					  "  AS pg_catalog.text) as object\n"
! 					  "  FROM 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', 'i', 'S', 'f')\n",
! 					  gettext_noop("table"),
! 					  gettext_noop("view"),
! 					  gettext_noop("index"),
! 					  gettext_noop("sequence"),
! 					  gettext_noop("foreign table"));
  
! 	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.relname", NULL,
! 						  "pg_catalog.pg_table_is_visible(c.oid)");
  
  	/* Rule descriptions (ignore rules for views) */
  	appendPQExpBuffer(&buf,
--- 854,926 ----
  					  gettext_noop("Object"),
  					  gettext_noop("Description"));
  
! 	/* Constraint descriptions */
  	appendPQExpBuffer(&buf,
! 					  "  SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
  					  "  n.nspname as nspname,\n"
! 					  "  CAST(pgc.conname AS pg_catalog.text) as name,"
  					  "  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)");
  
! 	/* 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)");
  
! 	/* comment on operator family only available in 8.3+ */
! 	if (pset.sversion >= 80300)
! 	{
! 		/* 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,
*************** objectDescription(const char *pattern, b
*** 972,978 ****
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
  						  "      AND n.nspname <> 'information_schema'\n");
  
- 	/* XXX not sure what to do about visibility rule here? */
  	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  "n.nspname", "r.rulename", NULL,
  						  "pg_catalog.pg_table_is_visible(c.oid)");
--- 939,944 ----
*************** objectDescription(const char *pattern, b
*** 993,999 ****
  		appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
  						  "      AND n.nspname <> 'information_schema'\n");
  
- 	/* XXX not sure what to do about visibility rule here? */
  	processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
  						  "n.nspname", "t.tgname", NULL,
  						  "pg_catalog.pg_table_is_visible(c.oid)");
--- 959,964 ----
*************** listLanguages(const char *pattern, bool
*** 2631,2638 ****
  						  gettext_noop("Owner"));
  
  	appendPQExpBuffer(&buf,
! 					  "       l.lanpltrusted AS \"%s\"",
! 					  gettext_noop("Trusted"));
  
  	if (verbose)
  	{
--- 2596,2605 ----
  						  gettext_noop("Owner"));
  
  	appendPQExpBuffer(&buf,
! 					  "       l.lanpltrusted AS \"%s\",\n"
! 					  "       d.description AS \"%s\"",
! 					  gettext_noop("Trusted"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  	{
*************** listLanguages(const char *pattern, bool
*** 2650,2662 ****
  	}
  
  	appendPQExpBuffer(&buf,
! 					  "\nFROM pg_catalog.pg_language l\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, false, false,
! 						  NULL, "l.lanname", NULL, NULL);
  
  	if (!showSystem && !pattern)
! 		appendPQExpBuffer(&buf, "WHERE lanplcallfoid != 0\n");
  
  	appendPQExpBuffer(&buf, "ORDER BY 1;");
  
--- 2617,2634 ----
  	}
  
  	appendPQExpBuffer(&buf,
! 					  "\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");
  
! 	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;");
  
*************** listDomains(const char *pattern, bool sh
*** 2710,2720 ****
  	appendPQExpBuffer(&buf,
  					  "       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\"\n"
  					  "FROM pg_catalog.pg_type t\n"
  	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
  					  "WHERE t.typtype = 'd'\n",
! 					  gettext_noop("Check"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
--- 2682,2697 ----
  	appendPQExpBuffer(&buf,
  					  "       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\",\n"
! 					  "       d.description as \"%s\"\n"
  					  "FROM pg_catalog.pg_type t\n"
  	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
+ 					  "     LEFT JOIN pg_catalog.pg_description d "
+ 					  "ON d.classoid = t.tableoid AND d.objoid = t.oid "
+ 					  "AND d.objsubid = 0\n"
  					  "WHERE t.typtype = 'd'\n",
! 					  gettext_noop("Check"),
! 					  gettext_noop("Description"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
*************** listConversions(const char *pattern, boo
*** 2762,2776 ****
  	   "       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\"\n"
! 			   "FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n\n"
! 					  "WHERE n.oid = c.connamespace\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("Source"),
  					  gettext_noop("Destination"),
  					  gettext_noop("yes"), gettext_noop("no"),
! 					  gettext_noop("Default?"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
--- 2739,2759 ----
  	   "       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\",\n"
! 					  "       d.description AS \"%s\"\n"
! 					  "FROM pg_catalog.pg_conversion c "
! 					  "JOIN pg_catalog.pg_namespace n "
! 					  "ON n.oid = c.connamespace\n"
! 					  "LEFT JOIN pg_catalog.pg_description d "
! 					  "ON d.classoid = c.tableoid AND d.objoid = c.oid "
! 					  "AND d.objsubid = 0\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("Source"),
  					  gettext_noop("Destination"),
  					  gettext_noop("yes"), gettext_noop("no"),
! 					  gettext_noop("Default?"),
! 					  gettext_noop("Description"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
*************** listConversions(const char *pattern, boo
*** 2804,2810 ****
   * Describes casts.
   */
  bool
! listCasts(const char *pattern)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
--- 2787,2793 ----
   * Describes casts.
   */
  bool
! listCasts(const char *pattern, bool verbose)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
*************** listCasts(const char *pattern)
*** 2828,2834 ****
  					  "       CASE WHEN c.castcontext = 'e' THEN '%s'\n"
  					  "            WHEN c.castcontext = 'a' THEN '%s'\n"
  					  "            ELSE '%s'\n"
! 					  "       END as \"%s\"\n"
  				 "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"
--- 2811,2831 ----
  					  "       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,
  				 "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"
*************** listCasts(const char *pattern)
*** 2838,2850 ****
  					  "     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"
! 					  "WHERE (true",
! 					  gettext_noop("Source type"),
! 					  gettext_noop("Target type"),
! 					  gettext_noop("Function"),
! 	  gettext_noop("no"), gettext_noop("in assignment"), gettext_noop("yes"),
! 					  gettext_noop("Implicit?"));
  
  	/*
  	 * Match name pattern against either internal or external name of either
--- 2835,2849 ----
  					  "     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
*************** listCasts(const char *pattern)
*** 2862,2868 ****
  						  "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);
--- 2861,2867 ----
  						  "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);
*************** listForeignDataWrappers(const char *patt
*** 3593,3610 ****
  
  	initPQExpBuffer(&buf);
  	printfPQExpBuffer(&buf,
! 					  "SELECT fdwname AS \"%s\",\n"
! 					  "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n",
  					  gettext_noop("Name"),
  					  gettext_noop("Owner"));
  	if (pset.sversion >= 90100)
  		appendPQExpBuffer(&buf,
! 						  "  fdwhandler::pg_catalog.regproc AS \"%s\",\n",
  						  gettext_noop("Handler"));
  	appendPQExpBuffer(&buf,
! 					  "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
  					  gettext_noop("Validator"));
  
  	if (verbose)
  	{
  		appendPQExpBuffer(&buf, ",\n  ");
--- 3592,3614 ----
  
  	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 (pset.sversion >= 90100)
+ 		appendPQExpBuffer(&buf,
+ 						  ",\nd.description AS \"%s\" ",
+ 						  gettext_noop("Description"));
+ 
  	if (verbose)
  	{
  		appendPQExpBuffer(&buf, ",\n  ");
*************** listForeignDataWrappers(const char *patt
*** 3614,3620 ****
  						  gettext_noop("Options"));
  	}
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "fdwname", NULL, NULL);
--- 3618,3630 ----
  						  gettext_noop("Options"));
  	}
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
! 
! 	if (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);
*************** listForeignServers(const char *pattern,
*** 3659,3668 ****
  	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)
  	{
--- 3669,3680 ----
  	printfPQExpBuffer(&buf,
  					  "SELECT s.srvname AS \"%s\",\n"
  					  "  pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
! 					  "  f.fdwname AS \"%s\",\n"
! 					  "  d.description AS \"%s\"",
  					  gettext_noop("Name"),
  					  gettext_noop("Owner"),
! 					  gettext_noop("Foreign-data wrapper"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  	{
*************** listForeignServers(const char *pattern,
*** 3680,3686 ****
  
  	appendPQExpBuffer(&buf,
  					  "\nFROM pg_catalog.pg_foreign_server s\n"
! 	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "s.srvname", NULL, NULL);
--- 3692,3701 ----
  
  	appendPQExpBuffer(&buf,
  					  "\nFROM pg_catalog.pg_foreign_server s\n"
! 	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n"
! 					  "LEFT JOIN pg_description d "
! 					  "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);
*************** listForeignTables(const char *pattern, b
*** 3778,3802 ****
  	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  ft.ftoptions AS \"%s\"",
  						  gettext_noop("Options"));
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
! 	appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
! 	appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
! 	appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "n.nspname", "c.relname", NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
--- 3793,3822 ----
  	printfPQExpBuffer(&buf,
  					  "SELECT n.nspname AS \"%s\",\n"
  					  "  c.relname AS \"%s\",\n"
! 					  "  s.srvname AS \"%s\",\n"
! 					  "  d.description AS \"%s\"\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Table"),
! 					  gettext_noop("Server"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  		appendPQExpBuffer(&buf,
  						  ",\n  ft.ftoptions AS \"%s\"",
  						  gettext_noop("Options"));
  
! 	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"
! 					  "  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;");
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index fb86d1e..7dc9a2c 100644
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
*************** extern bool listDomains(const char *patt
*** 67,73 ****
  extern bool listConversions(const char *pattern, bool showSystem);
  
  /* \dC */
! extern bool listCasts(const char *pattern);
  
  /* \dO */
  extern bool listCollations(const char *pattern, bool verbose, bool showSystem);
--- 67,73 ----
  extern bool listConversions(const char *pattern, bool showSystem);
  
  /* \dC */
! extern bool listCasts(const char *pattern, bool verbose);
  
  /* \dO */
  extern bool listCollations(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index e56ab61..5b294f7 100644
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
*************** slashUsage(unsigned short int pager)
*** 196,203 ****
  	fprintf(output, _("  \\da[S]  [PATTERN]      list aggregates\n"));
  	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
  	fprintf(output, _("  \\dc[S]  [PATTERN]      list conversions\n"));
! 	fprintf(output, _("  \\dC     [PATTERN]      list casts\n"));
! 	fprintf(output, _("  \\dd[S]  [PATTERN]      show comments on objects\n"));
  	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
  	fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
  	fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
--- 196,203 ----
  	fprintf(output, _("  \\da[S]  [PATTERN]      list aggregates\n"));
  	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
  	fprintf(output, _("  \\dc[S]  [PATTERN]      list conversions\n"));
! 	fprintf(output, _("  \\dC[+]  [PATTERN]      list casts\n"));
! 	fprintf(output, _("  \\dd[S]  [PATTERN]      show object descriptions not displayed elsewhere\n"));
  	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
  	fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
  	fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 2b3eddf..8d1bc16 100644
*** a/src/test/regress/expected/foreign_data.out
--- b/src/test/regress/expected/foreign_data.out
*************** CREATE FOREIGN DATA WRAPPER foo VALIDATO
*** 39,50 ****
  ERROR:  function bar(text[], oid) does not exist
  CREATE FOREIGN DATA WRAPPER foo;
  \dew
!                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         
! ------------+-------------------+---------+--------------------------
!  dummy      | foreign_data_user | -       | -
!  foo        | foreign_data_user | -       | -
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator
  (3 rows)
  
  CREATE FOREIGN DATA WRAPPER foo; -- duplicate
--- 39,50 ----
  ERROR:  function bar(text[], oid) does not exist
  CREATE FOREIGN DATA WRAPPER foo;
  \dew
!                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description 
! ------------+-------------------+---------+--------------------------+-------------
!  dummy      | foreign_data_user | -       | -                        | useless
!  foo        | foreign_data_user | -       | -                        | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator | 
  (3 rows)
  
  CREATE FOREIGN DATA WRAPPER foo; -- duplicate
*************** ERROR:  foreign-data wrapper "foo" alrea
*** 52,63 ****
  DROP FOREIGN DATA WRAPPER foo;
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
  \dew+
!                                      List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   
! ------------+-------------------+---------+--------------------------+-------------------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
--- 52,63 ----
  DROP FOREIGN DATA WRAPPER foo;
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
  \dew+
!                                             List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |   Options   
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {testing=1}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
*************** CREATE FOREIGN DATA WRAPPER foo OPTIONS
*** 65,76 ****
  ERROR:  option "testing" provided more than once
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        
! ------------+-------------------+---------+--------------------------+-------------------+-----------------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
--- 65,76 ----
  ERROR:  option "testing" provided more than once
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
  \dew+
!                                                  List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |        Options        
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {testing=1,another=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
*************** HINT:  Must be superuser to create a for
*** 81,92 ****
  RESET ROLE;
  CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | postgresql_fdw_validator |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  -- ALTER FOREIGN DATA WRAPPER
--- 81,92 ----
  RESET ROLE;
  CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  -- ALTER FOREIGN DATA WRAPPER
*************** ALTER FOREIGN DATA WRAPPER foo VALIDATOR
*** 98,109 ****
  ERROR:  function bar(text[], oid) does not exist
  ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
--- 98,109 ----
  ERROR:  function bar(text[], oid) does not exist
  ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
*************** ALTER FOREIGN DATA WRAPPER foo OPTIONS (
*** 113,146 ****
  ERROR:  option "c" not found
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
  \dew+
!                                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
  \dew+
!                                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
  ERROR:  option "b" provided more than once
  \dew+
!                                       List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    
! ------------+-------------------+---------+--------------------------+-------------------+---------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  SET ROLE regress_test_role;
--- 113,146 ----
  ERROR:  option "c" not found
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
  \dew+
!                                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {a=1,b=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
  \dew+
!                                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
  ERROR:  option "b" provided more than once
  \dew+
!                                              List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |    Options    
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4,a=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  SET ROLE regress_test_role;
*************** HINT:  Must be superuser to alter a fore
*** 150,161 ****
  SET ROLE regress_test_role_super;
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
  \dew+
!                                         List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
--- 150,161 ----
  SET ROLE regress_test_role_super;
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
  \dew+
!                                                List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
*************** ERROR:  permission denied to alter forei
*** 169,180 ****
  HINT:  Must be superuser to alter a foreign-data wrapper.
  RESET ROLE;
  \dew+
!                                            List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        |                   | 
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  -- DROP FOREIGN DATA WRAPPER
--- 169,180 ----
  HINT:  Must be superuser to alter a foreign-data wrapper.
  RESET ROLE;
  \dew+
!                                                   List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        | useless     |                   | 
!  foo        | regress_test_role_super | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  -- DROP FOREIGN DATA WRAPPER
*************** ERROR:  foreign-data wrapper "nonexisten
*** 183,194 ****
  DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
  NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
  \dew+
!                                            List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        |                   | 
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP ROLE regress_test_role_super;                          -- ERROR
--- 183,194 ----
  DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
  NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
  \dew+
!                                                   List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Description | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        | useless     |                   | 
!  foo        | regress_test_role_super | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  DROP ROLE regress_test_role_super;                          -- ERROR
*************** ALTER ROLE regress_test_role_super SUPER
*** 203,213 ****
  DROP FOREIGN DATA WRAPPER foo;
  DROP ROLE regress_test_role_super;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (2 rows)
  
  CREATE FOREIGN DATA WRAPPER foo;
--- 203,213 ----
  DROP FOREIGN DATA WRAPPER foo;
  DROP ROLE regress_test_role_super;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (2 rows)
  
  CREATE FOREIGN DATA WRAPPER foo;
*************** CREATE SERVER s1 FOREIGN DATA WRAPPER fo
*** 215,233 ****
  COMMENT ON SERVER s1 IS 'foreign server';
  CREATE USER MAPPING FOR current_user SERVER s1;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  \des+
!                                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges | Type | Version | Options 
! ------+-------------------+----------------------+-------------------+------+---------+---------
!  s1   | foreign_data_user | foo                  |                   |      |         | 
  (1 row)
  
  \deu+
--- 215,233 ----
  COMMENT ON SERVER s1 IS 'foreign server';
  CREATE USER MAPPING FOR current_user SERVER s1;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  foo        | foreign_data_user | -       | -                        |             |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (3 rows)
  
  \des+
!                                              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper |  Description   | Access privileges | Type | Version | Options 
! ------+-------------------+----------------------+----------------+-------------------+------+---------+---------
!  s1   | foreign_data_user | foo                  | foreign server |                   |      |         | 
  (1 row)
  
  \deu+
*************** NOTICE:  drop cascades to 2 other object
*** 252,268 ****
  DETAIL:  drop cascades to server s1
  drop cascades to user mapping for foreign_data_user
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (2 rows)
  
  \des+
!                               List of foreign servers
!  Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options 
! ------+-------+----------------------+-------------------+------+---------+---------
  (0 rows)
  
  \deu+
--- 252,268 ----
  DETAIL:  drop cascades to server s1
  drop cascades to user mapping for foreign_data_user
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        | useless     |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
  (2 rows)
  
  \des+
!                                      List of foreign servers
!  Name | Owner | Foreign-data wrapper | Description | Access privileges | Type | Version | Options 
! ------+-------+----------------------+-------------+-------------------+------+---------+---------
  (0 rows)
  
  \deu+
*************** ERROR:  invalid option "foo"
*** 289,305 ****
  HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
  CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
  (8 rows)
  
  SET ROLE regress_test_role;
--- 289,305 ----
  HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
  CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
  (8 rows)
  
  SET ROLE regress_test_role;
*************** SET ROLE regress_test_role;
*** 311,328 ****
  CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
  RESET ROLE;
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                   |        |         | 
  (9 rows)
  
  REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
--- 311,328 ----
  CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
  RESET ROLE;
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                   |        |         | 
  (9 rows)
  
  REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
*************** GRANT regress_test_indirect TO regress_t
*** 335,353 ****
  SET ROLE regress_test_role;
  CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                   |        |         | 
!  t2   | regress_test_role | foo                  |                   |        |         | 
  (10 rows)
  
  RESET ROLE;
--- 335,353 ----
  SET ROLE regress_test_role;
  CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             |                   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                   |        |         | 
!  t2   | regress_test_role | foo                  |             |                   |        |         | 
  (10 rows)
  
  RESET ROLE;
*************** ALTER SERVER s3 OPTIONS (tnsname 'orcl',
*** 365,385 ****
  GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
  GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
  \des+
!                                                            List of foreign servers
!  Name |       Owner       | Foreign-data wrapper |            Access privileges            |  Type  | Version |           Options            
! ------+-------------------+----------------------+-----------------------------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
!       |                   |                      | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user | foo                  |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                                         |        | 15.0    | 
!  s6   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                   |                      | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                                         |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                                         |        |         | 
!  t2   | regress_test_role | foo                  |                                         |        |         | 
  (10 rows)
  
  SET ROLE regress_test_role;
--- 365,385 ----
  GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
  GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
  \des+
!                                                                   List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description |            Access privileges            |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------+-----------------------------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
!       |                   |                      |             | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user | foo                  |             |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |             |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user | foo                  |             |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |             |                                         |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                   |                      |             | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user | foo                  |             |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |             |                                         |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |             |                                         |        |         | 
!  t2   | regress_test_role | foo                  |             |                                         |        |         | 
  (10 rows)
  
  SET ROLE regress_test_role;
*************** ERROR:  role "regress_test_indirect" can
*** 416,436 ****
  DETAIL:  owner of server s1
  privileges for foreign-data wrapper foo
  \des+
!                                                               List of foreign servers
!  Name |         Owner         | Foreign-data wrapper |            Access privileges            |  Type  | Version |             Options             
! ------+-----------------------+----------------------+-----------------------------------------+--------+---------+---------------------------------
!  s1   | regress_test_indirect | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
!       |                       |                      | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user     | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user     | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user     | foo                  |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user     | foo                  |                                         |        | 15.0    | 
!  s6   | foreign_data_user     | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                       |                      | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user     | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user     | postgresql           |                                         |        |         | {dbname=db1,connect_timeout=30}
!  t1   | regress_test_role     | foo                  |                                         |        |         | 
!  t2   | regress_test_role     | foo                  |                                         |        |         | 
  (10 rows)
  
  -- DROP SERVER
--- 416,436 ----
  DETAIL:  owner of server s1
  privileges for foreign-data wrapper foo
  \des+
!                                                                      List of foreign servers
!  Name |         Owner         | Foreign-data wrapper | Description |            Access privileges            |  Type  | Version |             Options             
! ------+-----------------------+----------------------+-------------+-----------------------------------------+--------+---------+---------------------------------
!  s1   | regress_test_indirect | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
!       |                       |                      |             | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user     | foo                  |             |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user     | foo                  |             |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user     | foo                  |             |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user     | foo                  |             |                                         |        | 15.0    | 
!  s6   | foreign_data_user     | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                       |                      |             | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user     | foo                  |             |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user     | postgresql           |             |                                         |        |         | {dbname=db1,connect_timeout=30}
!  t1   | regress_test_role     | foo                  |             |                                         |        |         | 
!  t2   | regress_test_role     | foo                  |             |                                         |        |         | 
  (10 rows)
  
  -- DROP SERVER
*************** ERROR:  server "nonexistent" does not ex
*** 439,457 ****
  DROP SERVER IF EXISTS nonexistent;
  NOTICE:  server "nonexistent" does not exist, skipping
  \des
!                List of foreign servers
!  Name |         Owner         | Foreign-data wrapper 
! ------+-----------------------+----------------------
!  s1   | regress_test_indirect | foo
!  s2   | foreign_data_user     | foo
!  s3   | foreign_data_user     | foo
!  s4   | foreign_data_user     | foo
!  s5   | foreign_data_user     | foo
!  s6   | foreign_data_user     | foo
!  s7   | foreign_data_user     | foo
!  s8   | foreign_data_user     | postgresql
!  t1   | regress_test_role     | foo
!  t2   | regress_test_role     | foo
  (10 rows)
  
  SET ROLE regress_test_role;
--- 439,457 ----
  DROP SERVER IF EXISTS nonexistent;
  NOTICE:  server "nonexistent" does not exist, skipping
  \des
!                       List of foreign servers
!  Name |         Owner         | Foreign-data wrapper | Description 
! ------+-----------------------+----------------------+-------------
!  s1   | regress_test_indirect | foo                  | 
!  s2   | foreign_data_user     | foo                  | 
!  s3   | foreign_data_user     | foo                  | 
!  s4   | foreign_data_user     | foo                  | 
!  s5   | foreign_data_user     | foo                  | 
!  s6   | foreign_data_user     | foo                  | 
!  s7   | foreign_data_user     | foo                  | 
!  s8   | foreign_data_user     | postgresql           | 
!  t1   | regress_test_role     | foo                  | 
!  t2   | regress_test_role     | foo                  | 
  (10 rows)
  
  SET ROLE regress_test_role;
*************** ERROR:  must be owner of foreign server
*** 460,477 ****
  DROP SERVER s1;
  RESET ROLE;
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s2   | foreign_data_user | foo
!  s3   | foreign_data_user | foo
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (9 rows)
  
  ALTER SERVER s2 OWNER TO regress_test_role;
--- 460,477 ----
  DROP SERVER s1;
  RESET ROLE;
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s2   | foreign_data_user | foo                  | 
!  s3   | foreign_data_user | foo                  | 
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (9 rows)
  
  ALTER SERVER s2 OWNER TO regress_test_role;
*************** SET ROLE regress_test_role;
*** 479,495 ****
  DROP SERVER s2;
  RESET ROLE;
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s3   | foreign_data_user | foo
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (8 rows)
  
  CREATE USER MAPPING FOR current_user SERVER s3;
--- 479,495 ----
  DROP SERVER s2;
  RESET ROLE;
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s3   | foreign_data_user | foo                  | 
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (8 rows)
  
  CREATE USER MAPPING FOR current_user SERVER s3;
*************** HINT:  Use DROP ... CASCADE to drop the
*** 507,522 ****
  DROP SERVER s3 CASCADE;
  NOTICE:  drop cascades to user mapping for foreign_data_user
  \des
!              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper 
! ------+-------------------+----------------------
!  s4   | foreign_data_user | foo
!  s5   | foreign_data_user | foo
!  s6   | foreign_data_user | foo
!  s7   | foreign_data_user | foo
!  s8   | foreign_data_user | postgresql
!  t1   | regress_test_role | foo
!  t2   | regress_test_role | foo
  (7 rows)
  
  \deu
--- 507,522 ----
  DROP SERVER s3 CASCADE;
  NOTICE:  drop cascades to user mapping for foreign_data_user
  \des
!                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Description 
! ------+-------------------+----------------------+-------------
!  s4   | foreign_data_user | foo                  | 
!  s5   | foreign_data_user | foo                  | 
!  s6   | foreign_data_user | foo                  | 
!  s7   | foreign_data_user | foo                  | 
!  s8   | foreign_data_user | postgresql           | 
!  t1   | regress_test_role | foo                  | 
!  t2   | regress_test_role | foo                  | 
  (7 rows)
  
  \deu
*************** Server: sc
*** 663,672 ****
  Has OIDs: no
  
  \det+
!                 List of foreign tables
!  Schema | Table | Server |          Options           
! --------+-------+--------+----------------------------
!  public | ft1   | sc     | {"delimiter=,","quote=\""}
  (1 row)
  
  CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
--- 663,672 ----
  Has OIDs: no
  
  \det+
!                        List of foreign tables
!  Schema | Table | Server | Description |          Options           
! --------+-------+--------+-------------+----------------------------
!  public | ft1   | sc     | ft1         | {"delimiter=,","quote=\""}
  (1 row)
  
  CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR


^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-04 16:26  Robert Haas <robertmhaas@gmail.com>
  parent: Josh Kupershmidt <schmiddy@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Robert Haas @ 2011-08-04 16:26 UTC (permalink / raw)
  To: Josh Kupershmidt <schmiddy@gmail.com>; +Cc: pgsql-hackers

On Tue, Jul 26, 2011 at 8:38 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
> [new patch]

I've committed the portion of this that displays comments on languages
and casts.

For domains and conversions, I am wondering if we should display the
comments only when + is specified, since the output is fairly wide
already.

For foreign data wrappers, foreign servers, and foreign tables, I am
wondering if there is any particular rule we should adhere to in terms
of where the description shows up in the output column list.  It
doesn't seem entirely consistent the way you've done it here, but
maybe you've put more thought into it than I have.

I haven't reviewed the part that changes the \dd output in detail yet (sorry).

Residual patch attached...

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

  [application/octet-stream] describe_comments.v3.residual.patch (59.4K, ../../CA+TgmobLH99kXRrZMoPsb-BnXuW8n2CxGxY_8EiiUHLxBXTFtg@mail.gmail.com/2-describe_comments.v3.residual.patch)
  download | inline diff:
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index ecfafad..c5c4cbd 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -982,24 +982,21 @@ testdb=&gt;
         <term><literal>\dd[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
-        Shows the descriptions of objects matching the <replaceable
-        class="parameter">pattern</replaceable>, or of all visible objects if
-        no argument is given.  But in either case, only objects that have
-        a description are listed.
+        Shows the descriptions of objects of type <literal>constraint</>,
+        <literal>operator class</>, <literal>operator family</>,
+        <literal>rule</>, and <literal>trigger</>. All
+        other comments may be viewed by the respective backslash commands for
+        those object types.
+        </para>
+
+        <para>
+        <literal>\dd</literal> displays descriptions for objects matching the
+        <replaceable class="parameter">pattern</replaceable>, or of visible
+        objects of the appropriate type if no argument is given.  But in either
+        case, only objects that have a description are listed.
         By default, only user-created objects are shown;  supply a
         pattern or the <literal>S</literal> modifier to include system
         objects.
-        <quote>Object</quote> covers aggregates, functions, operators,
-        types, relations (tables, views, indexes, sequences), large
-        objects, rules, and triggers. For example:
-<programlisting>
-=&gt; <userinput>\dd version</userinput>
-                     Object descriptions
-   Schema   |  Name   |  Object  |        Description
-------------+---------+----------+---------------------------
- pg_catalog | version | function | PostgreSQL version string
-(1 row)
-</programlisting>
         </para>
 
         <para>
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index e465686..714f826 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -830,8 +830,11 @@ listDefaultACLs(const char *pattern)
  *
  * \dd [foo]
  *
- * Note: This only lists things that actually have a description. For complete
- * lists of things, there are other \d? commands.
+ * 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)
@@ -851,109 +854,73 @@ objectDescription(const char *pattern, bool showSystem)
 					  gettext_noop("Object"),
 					  gettext_noop("Description"));
 
-	/* Aggregate descriptions */
-	appendPQExpBuffer(&buf,
-					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
-					  "  n.nspname as nspname,\n"
-					  "  CAST(p.proname AS pg_catalog.text) as name,"
-					  "  CAST('%s' AS pg_catalog.text) as object\n"
-					  "  FROM pg_catalog.pg_proc p\n"
-	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
-					  "  WHERE p.proisagg\n",
-					  gettext_noop("aggregate"));
-
-	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)");
-
-	/* Function descriptions */
-	appendPQExpBuffer(&buf,
-					  "UNION ALL\n"
-					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
-					  "  n.nspname as nspname,\n"
-					  "  CAST(p.proname AS pg_catalog.text) as name,"
-					  "  CAST('%s' AS pg_catalog.text) as object\n"
-					  "  FROM pg_catalog.pg_proc p\n"
-	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
-					  "  WHERE NOT p.proisagg\n",
-					  gettext_noop("function"));
-
-	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)");
-
-	/* Operator descriptions */
+	/* Constraint descriptions */
 	appendPQExpBuffer(&buf,
-					  "UNION ALL\n"
-					  "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
+					  "  SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
 					  "  n.nspname as nspname,\n"
-					  "  CAST(o.oprname AS pg_catalog.text) as name,"
+					  "  CAST(pgc.conname AS pg_catalog.text) as name,"
 					  "  CAST('%s' AS pg_catalog.text) as object\n"
-					  "  FROM pg_catalog.pg_operator o\n"
-	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
-					  gettext_noop("operator"));
+					  "  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", "o.oprname", NULL,
-						  "pg_catalog.pg_operator_is_visible(o.oid)");
+	processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern,
+						  false, "n.nspname", "pgc.conname", NULL,
+						  "pg_catalog.pg_table_is_visible(c.oid)");
 
-	/* Type descriptions */
+	/* Operator class descriptions */
 	appendPQExpBuffer(&buf,
 					  "UNION ALL\n"
-					  "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
+					  "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
 					  "  n.nspname as nspname,\n"
-					  "  pg_catalog.format_type(t.oid, NULL) as name,"
+					  "  CAST(o.opcname AS pg_catalog.text) as name,\n"
 					  "  CAST('%s' AS pg_catalog.text) as object\n"
-					  "  FROM pg_catalog.pg_type t\n"
-	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
-					  gettext_noop("data type"));
-
-	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", "pg_catalog.format_type(t.oid, NULL)",
-						  NULL,
-						  "pg_catalog.pg_type_is_visible(t.oid)");
-
-	/* Relation (tables, views, indexes, sequences) descriptions */
-	appendPQExpBuffer(&buf,
-					  "UNION ALL\n"
-					  "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
-					  "  n.nspname as nspname,\n"
-					  "  CAST(c.relname AS pg_catalog.text) as name,\n"
-					  "  CAST(\n"
-					  "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
-					  "  AS pg_catalog.text) as object\n"
-					  "  FROM 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', 'i', 'S', 'f')\n",
-					  gettext_noop("table"),
-					  gettext_noop("view"),
-					  gettext_noop("index"),
-					  gettext_noop("sequence"),
-					  gettext_noop("foreign table"));
+					  "  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", "c.relname", NULL,
-						  "pg_catalog.pg_table_is_visible(c.oid)");
+						  "n.nspname", "o.opcname", NULL,
+						  "pg_catalog.pg_opclass_is_visible(o.oid)");
+
+	/* comment on operator family only available in 8.3+ */
+	if (pset.sversion >= 80300)
+	{
+		/* 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,
@@ -972,7 +939,6 @@ objectDescription(const char *pattern, bool showSystem)
 		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
 						  "      AND n.nspname <> 'information_schema'\n");
 
-	/* XXX not sure what to do about visibility rule here? */
 	processSQLNamePattern(pset.db, &buf, pattern, true, false,
 						  "n.nspname", "r.rulename", NULL,
 						  "pg_catalog.pg_table_is_visible(c.oid)");
@@ -993,7 +959,6 @@ objectDescription(const char *pattern, bool showSystem)
 		appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
 						  "      AND n.nspname <> 'information_schema'\n");
 
-	/* XXX not sure what to do about visibility rule here? */
 	processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
 						  "n.nspname", "t.tgname", NULL,
 						  "pg_catalog.pg_table_is_visible(c.oid)");
@@ -2733,11 +2698,16 @@ listDomains(const char *pattern, bool showSystem)
 	appendPQExpBuffer(&buf,
 					  "       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\"\n"
+					  "       ), ' ') as \"%s\",\n"
+					  "       d.description as \"%s\"\n"
 					  "FROM pg_catalog.pg_type t\n"
 	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
+					  "     LEFT JOIN pg_catalog.pg_description d "
+					  "ON d.classoid = t.tableoid AND d.objoid = t.oid "
+					  "AND d.objsubid = 0\n"
 					  "WHERE t.typtype = 'd'\n",
-					  gettext_noop("Check"));
+					  gettext_noop("Check"),
+					  gettext_noop("Description"));
 
 	if (!showSystem && !pattern)
 		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
@@ -2785,15 +2755,21 @@ listConversions(const char *pattern, bool showSystem)
 	   "       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\"\n"
-			   "FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n\n"
-					  "WHERE n.oid = c.connamespace\n",
+					  "       ELSE '%s' END AS \"%s\",\n"
+					  "       d.description AS \"%s\"\n"
+					  "FROM pg_catalog.pg_conversion c "
+					  "JOIN pg_catalog.pg_namespace n "
+					  "ON n.oid = c.connamespace\n"
+					  "LEFT JOIN pg_catalog.pg_description d "
+					  "ON d.classoid = c.tableoid AND d.objoid = c.oid "
+					  "AND d.objsubid = 0\n",
 					  gettext_noop("Schema"),
 					  gettext_noop("Name"),
 					  gettext_noop("Source"),
 					  gettext_noop("Destination"),
 					  gettext_noop("yes"), gettext_noop("no"),
-					  gettext_noop("Default?"));
+					  gettext_noop("Default?"),
+					  gettext_noop("Description"));
 
 	if (!showSystem && !pattern)
 		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
@@ -3632,18 +3608,23 @@ listForeignDataWrappers(const char *pattern, bool verbose)
 
 	initPQExpBuffer(&buf);
 	printfPQExpBuffer(&buf,
-					  "SELECT fdwname AS \"%s\",\n"
-					  "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n",
+					  "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,
-						  "  fdwhandler::pg_catalog.regproc AS \"%s\",\n",
+						  "  fdw.fdwhandler::pg_catalog.regproc AS \"%s\",\n",
 						  gettext_noop("Handler"));
 	appendPQExpBuffer(&buf,
-					  "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
+					  "  fdw.fdwvalidator::pg_catalog.regproc AS \"%s\"",
 					  gettext_noop("Validator"));
 
+	if (pset.sversion >= 90100)
+		appendPQExpBuffer(&buf,
+						  ",\nd.description AS \"%s\" ",
+						  gettext_noop("Description"));
+
 	if (verbose)
 	{
 		appendPQExpBuffer(&buf, ",\n  ");
@@ -3653,7 +3634,13 @@ listForeignDataWrappers(const char *pattern, bool verbose)
 						  gettext_noop("Options"));
 	}
 
-	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper\n");
+	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper fdw\n");
+
+	if (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);
@@ -3698,10 +3685,12 @@ listForeignServers(const char *pattern, bool verbose)
 	printfPQExpBuffer(&buf,
 					  "SELECT s.srvname AS \"%s\",\n"
 					  "  pg_catalog.pg_get_userbyid(s.srvowner) AS \"%s\",\n"
-					  "  f.fdwname AS \"%s\"",
+					  "  f.fdwname AS \"%s\",\n"
+					  "  d.description AS \"%s\"",
 					  gettext_noop("Name"),
 					  gettext_noop("Owner"),
-					  gettext_noop("Foreign-data wrapper"));
+					  gettext_noop("Foreign-data wrapper"),
+					  gettext_noop("Description"));
 
 	if (verbose)
 	{
@@ -3719,7 +3708,10 @@ listForeignServers(const char *pattern, bool verbose)
 
 	appendPQExpBuffer(&buf,
 					  "\nFROM pg_catalog.pg_foreign_server s\n"
-	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
+	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n"
+					  "LEFT JOIN pg_description d "
+					  "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);
@@ -3817,25 +3809,30 @@ listForeignTables(const char *pattern, bool verbose)
 	printfPQExpBuffer(&buf,
 					  "SELECT n.nspname AS \"%s\",\n"
 					  "  c.relname AS \"%s\",\n"
-					  "  s.srvname AS \"%s\"",
+					  "  s.srvname AS \"%s\",\n"
+					  "  d.description AS \"%s\"\n",
 					  gettext_noop("Schema"),
 					  gettext_noop("Table"),
-					  gettext_noop("Server"));
+					  gettext_noop("Server"),
+					  gettext_noop("Description"));
 
 	if (verbose)
 		appendPQExpBuffer(&buf,
 						  ",\n  ft.ftoptions AS \"%s\"",
 						  gettext_noop("Options"));
 
-	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
-	appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
-	appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
-	appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
-	appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
-	appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
-	appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
+	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"
+					  "  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, true, false,
+	processSQLNamePattern(pset.db, &buf, pattern, false, false,
 						  NULL, "n.nspname", "c.relname", NULL);
 
 	appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index d3b8c17..5b294f7 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -197,7 +197,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
 	fprintf(output, _("  \\dc[S]  [PATTERN]      list conversions\n"));
 	fprintf(output, _("  \\dC[+]  [PATTERN]      list casts\n"));
-	fprintf(output, _("  \\dd[S]  [PATTERN]      show comments on objects\n"));
+	fprintf(output, _("  \\dd[S]  [PATTERN]      show object descriptions not displayed elsewhere\n"));
 	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
 	fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
 	fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 2b3eddf..8d1bc16 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -39,12 +39,12 @@ CREATE FOREIGN DATA WRAPPER foo VALIDATOR bar;            -- ERROR
 ERROR:  function bar(text[], oid) does not exist
 CREATE FOREIGN DATA WRAPPER foo;
 \dew
-                    List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         
-------------+-------------------+---------+--------------------------
- dummy      | foreign_data_user | -       | -
- foo        | foreign_data_user | -       | -
- postgresql | foreign_data_user | -       | postgresql_fdw_validator
+                           List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description 
+------------+-------------------+---------+--------------------------+-------------
+ dummy      | foreign_data_user | -       | -                        | useless
+ foo        | foreign_data_user | -       | -                        | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator | 
 (3 rows)
 
 CREATE FOREIGN DATA WRAPPER foo; -- duplicate
@@ -52,12 +52,12 @@ ERROR:  foreign-data wrapper "foo" already exists
 DROP FOREIGN DATA WRAPPER foo;
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
 \dew+
-                                     List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   
-------------+-------------------+---------+--------------------------+-------------------+-------------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | -                        |                   | {testing=1}
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                            List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges |   Options   
+------------+-------------------+---------+--------------------------+-------------+-------------------+-------------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | -                        |             |                   | {testing=1}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 DROP FOREIGN DATA WRAPPER foo;
@@ -65,12 +65,12 @@ CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', testing '2');   -- ERROR
 ERROR:  option "testing" provided more than once
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
 \dew+
-                                          List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        
-------------+-------------------+---------+--------------------------+-------------------+-----------------------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2}
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                                 List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges |        Options        
+------------+-------------------+---------+--------------------------+-------------+-------------------+-----------------------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | -                        |             |                   | {testing=1,another=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 DROP FOREIGN DATA WRAPPER foo;
@@ -81,12 +81,12 @@ HINT:  Must be superuser to create a foreign-data wrapper.
 RESET ROLE;
 CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
 \dew+
-                                   List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
-------------+-------------------+---------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | postgresql_fdw_validator |                   | 
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                          List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 -- ALTER FOREIGN DATA WRAPPER
@@ -98,12 +98,12 @@ ALTER FOREIGN DATA WRAPPER foo VALIDATOR bar;               -- ERROR
 ERROR:  function bar(text[], oid) does not exist
 ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
 \dew+
-                                   List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
-------------+-------------------+---------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | -                        |                   | 
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                          List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | -                        |             |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
@@ -113,34 +113,34 @@ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c);            -- ERROR
 ERROR:  option "c" not found
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
 \dew+
-                                    List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
-------------+-------------------+---------+--------------------------+-------------------+-----------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2}
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                           List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges |  Options  
+------------+-------------------+---------+--------------------------+-------------+-------------------+-----------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | -                        |             |                   | {a=1,b=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
 \dew+
-                                    List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
-------------+-------------------+---------+--------------------------+-------------------+-----------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4}
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                           List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges |  Options  
+------------+-------------------+---------+--------------------------+-------------+-------------------+-----------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
 ERROR:  option "b" provided more than once
 \dew+
-                                      List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    
-------------+-------------------+---------+--------------------------+-------------------+---------------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2}
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                             List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges |    Options    
+------------+-------------------+---------+--------------------------+-------------+-------------------+---------------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4,a=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 SET ROLE regress_test_role;
@@ -150,12 +150,12 @@ HINT:  Must be superuser to alter a foreign-data wrapper.
 SET ROLE regress_test_role_super;
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
 \dew+
-                                        List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      
-------------+-------------------+---------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                               List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges |      Options      
+------------+-------------------+---------+--------------------------+-------------+-------------------+-------------------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
@@ -169,12 +169,12 @@ ERROR:  permission denied to alter foreign-data wrapper "foo"
 HINT:  Must be superuser to alter a foreign-data wrapper.
 RESET ROLE;
 \dew+
-                                           List of foreign-data wrappers
-    Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
-------------+-------------------------+---------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user       | -       | -                        |                   | 
- foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
+                                                  List of foreign-data wrappers
+    Name    |          Owner          | Handler |        Validator         | Description | Access privileges |      Options      
+------------+-------------------------+---------+--------------------------+-------------+-------------------+-------------------
+ dummy      | foreign_data_user       | -       | -                        | useless     |                   | 
+ foo        | regress_test_role_super | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user       | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 -- DROP FOREIGN DATA WRAPPER
@@ -183,12 +183,12 @@ ERROR:  foreign-data wrapper "nonexistent" does not exist
 DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
 NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
 \dew+
-                                           List of foreign-data wrappers
-    Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
-------------+-------------------------+---------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user       | -       | -                        |                   | 
- foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
+                                                  List of foreign-data wrappers
+    Name    |          Owner          | Handler |        Validator         | Description | Access privileges |      Options      
+------------+-------------------------+---------+--------------------------+-------------+-------------------+-------------------
+ dummy      | foreign_data_user       | -       | -                        | useless     |                   | 
+ foo        | regress_test_role_super | -       | -                        |             |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user       | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 DROP ROLE regress_test_role_super;                          -- ERROR
@@ -203,11 +203,11 @@ ALTER ROLE regress_test_role_super SUPERUSER;
 DROP FOREIGN DATA WRAPPER foo;
 DROP ROLE regress_test_role_super;
 \dew+
-                                   List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
-------------+-------------------+---------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -       | -                        |                   | 
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                          List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (2 rows)
 
 CREATE FOREIGN DATA WRAPPER foo;
@@ -215,19 +215,19 @@ CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
 COMMENT ON SERVER s1 IS 'foreign server';
 CREATE USER MAPPING FOR current_user SERVER s1;
 \dew+
-                                   List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
-------------+-------------------+---------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -       | -                        |                   | 
- foo        | foreign_data_user | -       | -                        |                   | 
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                          List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ foo        | foreign_data_user | -       | -                        |             |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (3 rows)
 
 \des+
-                                    List of foreign servers
- Name |       Owner       | Foreign-data wrapper | Access privileges | Type | Version | Options 
-------+-------------------+----------------------+-------------------+------+---------+---------
- s1   | foreign_data_user | foo                  |                   |      |         | 
+                                             List of foreign servers
+ Name |       Owner       | Foreign-data wrapper |  Description   | Access privileges | Type | Version | Options 
+------+-------------------+----------------------+----------------+-------------------+------+---------+---------
+ s1   | foreign_data_user | foo                  | foreign server |                   |      |         | 
 (1 row)
 
 \deu+
@@ -252,17 +252,17 @@ NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to server s1
 drop cascades to user mapping for foreign_data_user
 \dew+
-                                   List of foreign-data wrappers
-    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
-------------+-------------------+---------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -       | -                        |                   | 
- postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+                                          List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Description | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        | useless     |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |             |                   | 
 (2 rows)
 
 \des+
-                              List of foreign servers
- Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options 
-------+-------+----------------------+-------------------+------+---------+---------
+                                     List of foreign servers
+ Name | Owner | Foreign-data wrapper | Description | Access privileges | Type | Version | Options 
+------+-------+----------------------+-------------+-------------------+------+---------+---------
 (0 rows)
 
 \deu+
@@ -289,17 +289,17 @@ ERROR:  invalid option "foo"
 HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
 CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
 \des+
-                                                List of foreign servers
- Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
-------+-------------------+----------------------+-------------------+--------+---------+------------------------------
- s1   | foreign_data_user | foo                  |                   |        |         | 
- s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
- s3   | foreign_data_user | foo                  |                   | oracle |         | 
- s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
- s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
- s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
- s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
- s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
+                                                       List of foreign servers
+ Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
+------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
+ s1   | foreign_data_user | foo                  |             |                   |        |         | 
+ s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
+ s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
+ s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
+ s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
+ s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
+ s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
+ s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
 (8 rows)
 
 SET ROLE regress_test_role;
@@ -311,18 +311,18 @@ SET ROLE regress_test_role;
 CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
 RESET ROLE;
 \des+
-                                                List of foreign servers
- Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
-------+-------------------+----------------------+-------------------+--------+---------+------------------------------
- s1   | foreign_data_user | foo                  |                   |        |         | 
- s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
- s3   | foreign_data_user | foo                  |                   | oracle |         | 
- s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
- s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
- s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
- s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
- s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
- t1   | regress_test_role | foo                  |                   |        |         | 
+                                                       List of foreign servers
+ Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
+------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
+ s1   | foreign_data_user | foo                  |             |                   |        |         | 
+ s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
+ s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
+ s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
+ s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
+ s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
+ s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
+ s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
+ t1   | regress_test_role | foo                  |             |                   |        |         | 
 (9 rows)
 
 REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
@@ -335,19 +335,19 @@ GRANT regress_test_indirect TO regress_test_role;
 SET ROLE regress_test_role;
 CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
 \des+
-                                                List of foreign servers
- Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
-------+-------------------+----------------------+-------------------+--------+---------+------------------------------
- s1   | foreign_data_user | foo                  |                   |        |         | 
- s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
- s3   | foreign_data_user | foo                  |                   | oracle |         | 
- s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
- s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
- s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
- s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
- s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
- t1   | regress_test_role | foo                  |                   |        |         | 
- t2   | regress_test_role | foo                  |                   |        |         | 
+                                                       List of foreign servers
+ Name |       Owner       | Foreign-data wrapper | Description | Access privileges |  Type  | Version |           Options            
+------+-------------------+----------------------+-------------+-------------------+--------+---------+------------------------------
+ s1   | foreign_data_user | foo                  |             |                   |        |         | 
+ s2   | foreign_data_user | foo                  |             |                   |        |         | {host=a,dbname=b}
+ s3   | foreign_data_user | foo                  |             |                   | oracle |         | 
+ s4   | foreign_data_user | foo                  |             |                   | oracle |         | {host=a,dbname=b}
+ s5   | foreign_data_user | foo                  |             |                   |        | 15.0    | 
+ s6   | foreign_data_user | foo                  |             |                   |        | 16.0    | {host=a,dbname=b}
+ s7   | foreign_data_user | foo                  |             |                   | oracle | 17.0    | {host=a,dbname=b}
+ s8   | foreign_data_user | postgresql           |             |                   |        |         | {host=localhost,dbname=s8db}
+ t1   | regress_test_role | foo                  |             |                   |        |         | 
+ t2   | regress_test_role | foo                  |             |                   |        |         | 
 (10 rows)
 
 RESET ROLE;
@@ -365,21 +365,21 @@ ALTER SERVER s3 OPTIONS (tnsname 'orcl', port '1521');
 GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
 GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
 \des+
-                                                           List of foreign servers
- Name |       Owner       | Foreign-data wrapper |            Access privileges            |  Type  | Version |           Options            
-------+-------------------+----------------------+-----------------------------------------+--------+---------+------------------------------
- s1   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
-      |                   |                      | regress_test_role=U/foreign_data_user   |        |         | 
- s2   | foreign_data_user | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
- s3   | foreign_data_user | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
- s4   | foreign_data_user | foo                  |                                         | oracle |         | {host=a,dbname=b}
- s5   | foreign_data_user | foo                  |                                         |        | 15.0    | 
- s6   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
-      |                   |                      | regress_test_role2=U*/foreign_data_user |        |         | 
- s7   | foreign_data_user | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
- s8   | foreign_data_user | postgresql           |                                         |        |         | {host=localhost,dbname=s8db}
- t1   | regress_test_role | foo                  |                                         |        |         | 
- t2   | regress_test_role | foo                  |                                         |        |         | 
+                                                                  List of foreign servers
+ Name |       Owner       | Foreign-data wrapper | Description |            Access privileges            |  Type  | Version |           Options            
+------+-------------------+----------------------+-------------+-----------------------------------------+--------+---------+------------------------------
+ s1   | foreign_data_user | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
+      |                   |                      |             | regress_test_role=U/foreign_data_user   |        |         | 
+ s2   | foreign_data_user | foo                  |             |                                         |        | 1.1     | {host=a,dbname=b}
+ s3   | foreign_data_user | foo                  |             |                                         | oracle |         | {tnsname=orcl,port=1521}
+ s4   | foreign_data_user | foo                  |             |                                         | oracle |         | {host=a,dbname=b}
+ s5   | foreign_data_user | foo                  |             |                                         |        | 15.0    | 
+ s6   | foreign_data_user | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
+      |                   |                      |             | regress_test_role2=U*/foreign_data_user |        |         | 
+ s7   | foreign_data_user | foo                  |             |                                         | oracle | 17.0    | {host=a,dbname=b}
+ s8   | foreign_data_user | postgresql           |             |                                         |        |         | {host=localhost,dbname=s8db}
+ t1   | regress_test_role | foo                  |             |                                         |        |         | 
+ t2   | regress_test_role | foo                  |             |                                         |        |         | 
 (10 rows)
 
 SET ROLE regress_test_role;
@@ -416,21 +416,21 @@ ERROR:  role "regress_test_indirect" cannot be dropped because some objects depe
 DETAIL:  owner of server s1
 privileges for foreign-data wrapper foo
 \des+
-                                                              List of foreign servers
- Name |         Owner         | Foreign-data wrapper |            Access privileges            |  Type  | Version |             Options             
-------+-----------------------+----------------------+-----------------------------------------+--------+---------+---------------------------------
- s1   | regress_test_indirect | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
-      |                       |                      | regress_test_role=U/foreign_data_user   |        |         | 
- s2   | foreign_data_user     | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
- s3   | foreign_data_user     | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
- s4   | foreign_data_user     | foo                  |                                         | oracle |         | {host=a,dbname=b}
- s5   | foreign_data_user     | foo                  |                                         |        | 15.0    | 
- s6   | foreign_data_user     | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
-      |                       |                      | regress_test_role2=U*/foreign_data_user |        |         | 
- s7   | foreign_data_user     | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
- s8   | foreign_data_user     | postgresql           |                                         |        |         | {dbname=db1,connect_timeout=30}
- t1   | regress_test_role     | foo                  |                                         |        |         | 
- t2   | regress_test_role     | foo                  |                                         |        |         | 
+                                                                     List of foreign servers
+ Name |         Owner         | Foreign-data wrapper | Description |            Access privileges            |  Type  | Version |             Options             
+------+-----------------------+----------------------+-------------+-----------------------------------------+--------+---------+---------------------------------
+ s1   | regress_test_indirect | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
+      |                       |                      |             | regress_test_role=U/foreign_data_user   |        |         | 
+ s2   | foreign_data_user     | foo                  |             |                                         |        | 1.1     | {host=a,dbname=b}
+ s3   | foreign_data_user     | foo                  |             |                                         | oracle |         | {tnsname=orcl,port=1521}
+ s4   | foreign_data_user     | foo                  |             |                                         | oracle |         | {host=a,dbname=b}
+ s5   | foreign_data_user     | foo                  |             |                                         |        | 15.0    | 
+ s6   | foreign_data_user     | foo                  |             | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
+      |                       |                      |             | regress_test_role2=U*/foreign_data_user |        |         | 
+ s7   | foreign_data_user     | foo                  |             |                                         | oracle | 17.0    | {host=a,dbname=b}
+ s8   | foreign_data_user     | postgresql           |             |                                         |        |         | {dbname=db1,connect_timeout=30}
+ t1   | regress_test_role     | foo                  |             |                                         |        |         | 
+ t2   | regress_test_role     | foo                  |             |                                         |        |         | 
 (10 rows)
 
 -- DROP SERVER
@@ -439,19 +439,19 @@ ERROR:  server "nonexistent" does not exist
 DROP SERVER IF EXISTS nonexistent;
 NOTICE:  server "nonexistent" does not exist, skipping
 \des
-               List of foreign servers
- Name |         Owner         | Foreign-data wrapper 
-------+-----------------------+----------------------
- s1   | regress_test_indirect | foo
- s2   | foreign_data_user     | foo
- s3   | foreign_data_user     | foo
- s4   | foreign_data_user     | foo
- s5   | foreign_data_user     | foo
- s6   | foreign_data_user     | foo
- s7   | foreign_data_user     | foo
- s8   | foreign_data_user     | postgresql
- t1   | regress_test_role     | foo
- t2   | regress_test_role     | foo
+                      List of foreign servers
+ Name |         Owner         | Foreign-data wrapper | Description 
+------+-----------------------+----------------------+-------------
+ s1   | regress_test_indirect | foo                  | 
+ s2   | foreign_data_user     | foo                  | 
+ s3   | foreign_data_user     | foo                  | 
+ s4   | foreign_data_user     | foo                  | 
+ s5   | foreign_data_user     | foo                  | 
+ s6   | foreign_data_user     | foo                  | 
+ s7   | foreign_data_user     | foo                  | 
+ s8   | foreign_data_user     | postgresql           | 
+ t1   | regress_test_role     | foo                  | 
+ t2   | regress_test_role     | foo                  | 
 (10 rows)
 
 SET ROLE regress_test_role;
@@ -460,18 +460,18 @@ ERROR:  must be owner of foreign server s2
 DROP SERVER s1;
 RESET ROLE;
 \des
-             List of foreign servers
- Name |       Owner       | Foreign-data wrapper 
-------+-------------------+----------------------
- s2   | foreign_data_user | foo
- s3   | foreign_data_user | foo
- s4   | foreign_data_user | foo
- s5   | foreign_data_user | foo
- s6   | foreign_data_user | foo
- s7   | foreign_data_user | foo
- s8   | foreign_data_user | postgresql
- t1   | regress_test_role | foo
- t2   | regress_test_role | foo
+                    List of foreign servers
+ Name |       Owner       | Foreign-data wrapper | Description 
+------+-------------------+----------------------+-------------
+ s2   | foreign_data_user | foo                  | 
+ s3   | foreign_data_user | foo                  | 
+ s4   | foreign_data_user | foo                  | 
+ s5   | foreign_data_user | foo                  | 
+ s6   | foreign_data_user | foo                  | 
+ s7   | foreign_data_user | foo                  | 
+ s8   | foreign_data_user | postgresql           | 
+ t1   | regress_test_role | foo                  | 
+ t2   | regress_test_role | foo                  | 
 (9 rows)
 
 ALTER SERVER s2 OWNER TO regress_test_role;
@@ -479,17 +479,17 @@ SET ROLE regress_test_role;
 DROP SERVER s2;
 RESET ROLE;
 \des
-             List of foreign servers
- Name |       Owner       | Foreign-data wrapper 
-------+-------------------+----------------------
- s3   | foreign_data_user | foo
- s4   | foreign_data_user | foo
- s5   | foreign_data_user | foo
- s6   | foreign_data_user | foo
- s7   | foreign_data_user | foo
- s8   | foreign_data_user | postgresql
- t1   | regress_test_role | foo
- t2   | regress_test_role | foo
+                    List of foreign servers
+ Name |       Owner       | Foreign-data wrapper | Description 
+------+-------------------+----------------------+-------------
+ s3   | foreign_data_user | foo                  | 
+ s4   | foreign_data_user | foo                  | 
+ s5   | foreign_data_user | foo                  | 
+ s6   | foreign_data_user | foo                  | 
+ s7   | foreign_data_user | foo                  | 
+ s8   | foreign_data_user | postgresql           | 
+ t1   | regress_test_role | foo                  | 
+ t2   | regress_test_role | foo                  | 
 (8 rows)
 
 CREATE USER MAPPING FOR current_user SERVER s3;
@@ -507,16 +507,16 @@ HINT:  Use DROP ... CASCADE to drop the dependent objects too.
 DROP SERVER s3 CASCADE;
 NOTICE:  drop cascades to user mapping for foreign_data_user
 \des
-             List of foreign servers
- Name |       Owner       | Foreign-data wrapper 
-------+-------------------+----------------------
- s4   | foreign_data_user | foo
- s5   | foreign_data_user | foo
- s6   | foreign_data_user | foo
- s7   | foreign_data_user | foo
- s8   | foreign_data_user | postgresql
- t1   | regress_test_role | foo
- t2   | regress_test_role | foo
+                    List of foreign servers
+ Name |       Owner       | Foreign-data wrapper | Description 
+------+-------------------+----------------------+-------------
+ s4   | foreign_data_user | foo                  | 
+ s5   | foreign_data_user | foo                  | 
+ s6   | foreign_data_user | foo                  | 
+ s7   | foreign_data_user | foo                  | 
+ s8   | foreign_data_user | postgresql           | 
+ t1   | regress_test_role | foo                  | 
+ t2   | regress_test_role | foo                  | 
 (7 rows)
 
 \deu
@@ -663,10 +663,10 @@ Server: sc
 Has OIDs: no
 
 \det+
-                List of foreign tables
- Schema | Table | Server |          Options           
---------+-------+--------+----------------------------
- public | ft1   | sc     | {"delimiter=,","quote=\""}
+                       List of foreign tables
+ Schema | Table | Server | Description |          Options           
+--------+-------+--------+-------------+----------------------------
+ public | ft1   | sc     | ft1         | {"delimiter=,","quote=\""}
 (1 row)
 
 CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR


^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-05 02:24  Josh Kupershmidt <schmiddy@gmail.com>
  parent: Robert Haas <robertmhaas@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Josh Kupershmidt @ 2011-08-05 02:24 UTC (permalink / raw)
  To: Robert Haas <robertmhaas@gmail.com>; +Cc: pgsql-hackers

On Thu, Aug 4, 2011 at 12:26 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> On Tue, Jul 26, 2011 at 8:38 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
>> [new patch]
>
> I've committed the portion of this that displays comments on languages
> and casts.

Thanks!

> For domains and conversions, I am wondering if we should display the
> comments only when + is specified, since the output is fairly wide
> already.

I wasn't sure whether there was some sort of precedent for whether
comments should be displayed only in verbose mode, but looking through
the existing backslash commands, it seems reasonable to make it
verbose-only if the output is already pushing 80 characters for
typical usage (object names and other column outputs of lengths
typically encountered).

A few existing backslash commands, such as \dn and maybe \db, don't
exactly follow this precedent. Not sure if we want to bother adjusting
the existing commands to be consistent in this regard. Defining
"typical usage" is pretty wishy-washy, so I'm not real inclined to try
messing with the existing commands.

> For foreign data wrappers, foreign servers, and foreign tables, I am
> wondering if there is any particular rule we should adhere to in terms
> of where the description shows up in the output column list.  It
> doesn't seem entirely consistent the way you've done it here, but
> maybe you've put more thought into it than I have.

Hrm, what wasn't consistent? I intended to just put the "Description"
column at the end of the outputs for \dew, \des, and \det, which seems
to be the way other commands handle this. Though now that I double
check, I notice that the verbose modes of these commands include extra
columns which come after "Description", and it might be better to
force "Description" to stay at the end in those cases, the way that
\dT[+] and \dFt[+] do. Though perhaps you're complaining about
something different -- \dL isn't forcing "Description" to the end in
verbose mode.

Josh



^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-05 12:32  Robert Haas <robertmhaas@gmail.com>
  parent: Josh Kupershmidt <schmiddy@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Robert Haas @ 2011-08-05 12:32 UTC (permalink / raw)
  To: Josh Kupershmidt <schmiddy@gmail.com>; +Cc: pgsql-hackers

On Thu, Aug 4, 2011 at 10:24 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
> A few existing backslash commands, such as \dn and maybe \db, don't
> exactly follow this precedent. Not sure if we want to bother adjusting
> the existing commands to be consistent in this regard. Defining
> "typical usage" is pretty wishy-washy, so I'm not real inclined to try
> messing with the existing commands.

Me neither.

>> For foreign data wrappers, foreign servers, and foreign tables, I am
>> wondering if there is any particular rule we should adhere to in terms
>> of where the description shows up in the output column list.  It
>> doesn't seem entirely consistent the way you've done it here, but
>> maybe you've put more thought into it than I have.
>
> Hrm, what wasn't consistent? I intended to just put the "Description"
> column at the end of the outputs for \dew, \des, and \det, which seems
> to be the way other commands handle this. Though now that I double
> check, I notice that the verbose modes of these commands include extra
> columns which come after "Description", and it might be better to
> force "Description" to stay at the end in those cases, the way that
> \dT[+] and \dFt[+] do. Though perhaps you're complaining about
> something different -- \dL isn't forcing "Description" to the end in
> verbose mode.

Oh, I see.  I was right - you have thought about this more than I have.  :-)

I guess my vote is to make the SQL/MED stuff show the description only
in verbose mode, and always at the end; and revise what we did with
\dL to put the description at the end even in verbose mode.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-05 23:25  Josh Kupershmidt <schmiddy@gmail.com>
  parent: Robert Haas <robertmhaas@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Josh Kupershmidt @ 2011-08-05 23:25 UTC (permalink / raw)
  To: Robert Haas <robertmhaas@gmail.com>; +Cc: pgsql-hackers

On Fri, Aug 5, 2011 at 8:32 AM, Robert Haas <robertmhaas@gmail.com> wrote:
> I guess my vote is to make the SQL/MED stuff show the description only
> in verbose mode, and always at the end; and revise what we did with
> \dL to put the description at the end even in verbose mode.

Yeah, that sounds fine to me. I've revised the residual patch to do
so, as well as incorporated your earlier suggestion about having \dD
and \dc only display descriptions in verbose-mode.

I did more testing, and found and fixed some brokenness related to
d.objsubid I had introduced into the listConversions() query, as well
as improved the version checking in objectDescription(). Updated patch
attached.

Josh

Attachments:

  [application/octet-stream] describe_comments.v4f.residual.patch (69.1K, ../../CAK3UJREJdHZ-6GCmSfJkKtbzk-JQ3iDcO1JBp3BG6-MA4bcZ6Q@mail.gmail.com/2-describe_comments.v4f.residual.patch)
  download | inline diff:
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
new file mode 100644
index 98cb6bd..d2ded98
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
*************** testdb=&gt;
*** 956,962 ****
  
  
        <varlistentry>
!         <term><literal>\dc[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
          Lists conversions between character-set encodings.
--- 956,962 ----
  
  
        <varlistentry>
!         <term><literal>\dc[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
          Lists conversions between character-set encodings.
*************** testdb=&gt;
*** 988,1011 ****
          <term><literal>\dd[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
!         Shows the descriptions of objects matching the <replaceable
!         class="parameter">pattern</replaceable>, or of all visible objects if
!         no argument is given.  But in either case, only objects that have
!         a description are listed.
          By default, only user-created objects are shown;  supply a
          pattern or the <literal>S</literal> modifier to include system
          objects.
-         <quote>Object</quote> covers aggregates, functions, operators,
-         types, relations (tables, views, indexes, sequences), large
-         objects, rules, and triggers. For example:
- <programlisting>
- =&gt; <userinput>\dd version</userinput>
-                      Object descriptions
-    Schema   |  Name   |  Object  |        Description
- ------------+---------+----------+---------------------------
-  pg_catalog | version | function | PostgreSQL version string
- (1 row)
- </programlisting>
          </para>
  
          <para>
--- 988,1008 ----
          <term><literal>\dd[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
!         Shows the descriptions of objects of type <literal>constraint</>,
!         <literal>operator class</>, <literal>operator family</>,
!         <literal>rule</>, and <literal>trigger</>. All
!         other comments may be viewed by the respective backslash commands for
!         those object types.
!         </para>
! 
!         <para>
!         <literal>\dd</literal> displays descriptions for objects matching the
!         <replaceable class="parameter">pattern</replaceable>, or of visible
!         objects of the appropriate type if no argument is given.  But in either
!         case, only objects that have a description are listed.
          By default, only user-created objects are shown;  supply a
          pattern or the <literal>S</literal> modifier to include system
          objects.
          </para>
  
          <para>
*************** testdb=&gt;
*** 1040,1046 ****
  
  
        <varlistentry>
!         <term><literal>\dD[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
          Lists domains. If <replaceable
--- 1037,1043 ----
  
  
        <varlistentry>
!         <term><literal>\dD[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
          <listitem>
          <para>
          Lists domains. If <replaceable
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
new file mode 100644
index 84e031d..d6a925e
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
*************** exec_command(const char *cmd,
*** 378,384 ****
  				success = describeTablespaces(pattern, show_verbose);
  				break;
  			case 'c':
! 				success = listConversions(pattern, show_system);
  				break;
  			case 'C':
  				success = listCasts(pattern, show_verbose);
--- 378,384 ----
  				success = describeTablespaces(pattern, show_verbose);
  				break;
  			case 'c':
! 				success = listConversions(pattern, show_verbose, show_system);
  				break;
  			case 'C':
  				success = listCasts(pattern, show_verbose);
*************** exec_command(const char *cmd,
*** 390,396 ****
  					success = objectDescription(pattern, show_system);
  				break;
  			case 'D':
! 				success = listDomains(pattern, show_system);
  				break;
  			case 'f':			/* function subsystem */
  				switch (cmd[2])
--- 390,396 ----
  					success = objectDescription(pattern, show_system);
  				break;
  			case 'D':
! 				success = listDomains(pattern, show_verbose, show_system);
  				break;
  			case 'f':			/* function subsystem */
  				switch (cmd[2])
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
new file mode 100644
index a8d5ddc..c7bea0d
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
*************** listDefaultACLs(const char *pattern)
*** 830,837 ****
   *
   * \dd [foo]
   *
!  * Note: This only lists things that actually have a description. For complete
!  * lists of things, there are other \d? commands.
   */
  bool
  objectDescription(const char *pattern, bool showSystem)
--- 830,840 ----
   *
   * \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)
*************** objectDescription(const char *pattern, b
*** 851,959 ****
  					  gettext_noop("Object"),
  					  gettext_noop("Description"));
  
! 	/* Aggregate descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  CAST(p.proname AS pg_catalog.text) as name,"
! 					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_proc p\n"
! 	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
! 					  "  WHERE p.proisagg\n",
! 					  gettext_noop("aggregate"));
! 
! 	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)");
! 
! 	/* Function descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  CAST(p.proname AS pg_catalog.text) as name,"
! 					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_proc p\n"
! 	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
! 					  "  WHERE NOT p.proisagg\n",
! 					  gettext_noop("function"));
! 
! 	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)");
! 
! 	/* Operator descriptions */
  	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
  					  "  n.nspname as nspname,\n"
! 					  "  CAST(o.oprname AS pg_catalog.text) as name,"
  					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_operator o\n"
! 	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
! 					  gettext_noop("operator"));
  
  	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", "o.oprname", NULL,
! 						  "pg_catalog.pg_operator_is_visible(o.oid)");
  
! 	/* Type descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  pg_catalog.format_type(t.oid, NULL) as name,"
! 					  "  CAST('%s' AS pg_catalog.text) as object\n"
! 					  "  FROM pg_catalog.pg_type t\n"
! 	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
! 					  gettext_noop("data type"));
  
! 	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", "pg_catalog.format_type(t.oid, NULL)",
! 						  NULL,
! 						  "pg_catalog.pg_type_is_visible(t.oid)");
  
! 	/* Relation (tables, views, indexes, sequences) descriptions */
! 	appendPQExpBuffer(&buf,
! 					  "UNION ALL\n"
! 					  "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
! 					  "  n.nspname as nspname,\n"
! 					  "  CAST(c.relname AS pg_catalog.text) as name,\n"
! 					  "  CAST(\n"
! 					  "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
! 					  "  AS pg_catalog.text) as object\n"
! 					  "  FROM 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', 'i', 'S', 'f')\n",
! 					  gettext_noop("table"),
! 					  gettext_noop("view"),
! 					  gettext_noop("index"),
! 					  gettext_noop("sequence"),
! 					  gettext_noop("foreign table"));
  
! 	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.relname", NULL,
! 						  "pg_catalog.pg_table_is_visible(c.oid)");
  
  	/* Rule descriptions (ignore rules for views) */
  	appendPQExpBuffer(&buf,
--- 854,928 ----
  					  gettext_noop("Object"),
  					  gettext_noop("Description"));
  
! 	/* Constraint descriptions */
  	appendPQExpBuffer(&buf,
! 					  "  SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
  					  "  n.nspname as nspname,\n"
! 					  "  CAST(pgc.conname AS pg_catalog.text) as name,"
  					  "  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+, and comment on operator
! 	 * family 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)");
  
! 		/* 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,
*************** objectDescription(const char *pattern, b
*** 972,978 ****
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
  						  "      AND n.nspname <> 'information_schema'\n");
  
- 	/* XXX not sure what to do about visibility rule here? */
  	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  "n.nspname", "r.rulename", NULL,
  						  "pg_catalog.pg_table_is_visible(c.oid)");
--- 941,946 ----
*************** objectDescription(const char *pattern, b
*** 993,999 ****
  		appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
  						  "      AND n.nspname <> 'information_schema'\n");
  
- 	/* XXX not sure what to do about visibility rule here? */
  	processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
  						  "n.nspname", "t.tgname", NULL,
  						  "pg_catalog.pg_table_is_visible(c.oid)");
--- 961,966 ----
*************** listLanguages(const char *pattern, bool 
*** 2665,2674 ****
  						  gettext_noop("Owner"));
  
  	appendPQExpBuffer(&buf,
! 					  "       l.lanpltrusted AS \"%s\",\n"
! 					  "       d.description AS \"%s\"",
! 					  gettext_noop("Trusted"),
! 					  gettext_noop("Description"));
  
  	if (verbose)
  	{
--- 2632,2639 ----
  						  gettext_noop("Owner"));
  
  	appendPQExpBuffer(&buf,
! 					  "       l.lanpltrusted AS \"%s\"",
! 					  gettext_noop("Trusted"));
  
  	if (verbose)
  	{
*************** listLanguages(const char *pattern, bool 
*** 2686,2695 ****
  	}
  
  	appendPQExpBuffer(&buf,
  					  "\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");
  
  	if (pattern)
  		processSQLNamePattern(pset.db, &buf, pattern, false, false,
--- 2651,2662 ----
  	}
  
  	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,
*************** listLanguages(const char *pattern, bool 
*** 2723,2729 ****
   * Describes domains.
   */
  bool
! listDomains(const char *pattern, bool showSystem)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
--- 2690,2696 ----
   * Describes domains.
   */
  bool
! listDomains(const char *pattern, bool verbose, bool showSystem)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
*************** listDomains(const char *pattern, bool sh
*** 2739,2744 ****
--- 2706,2712 ----
  					  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"
*************** listDomains(const char *pattern, bool sh
*** 2746,2762 ****
  	appendPQExpBuffer(&buf,
  	   "            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",
! 					  gettext_noop("Modifier"));
! 	appendPQExpBuffer(&buf,
  					  "       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\"\n"
! 					  "FROM pg_catalog.pg_type t\n"
! 	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n"
! 					  "WHERE t.typtype = 'd'\n",
  					  gettext_noop("Check"));
  
  	if (!showSystem && !pattern)
  		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
  						  "      AND n.nspname <> 'information_schema'\n");
--- 2714,2743 ----
  	appendPQExpBuffer(&buf,
  	   "            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");
*************** listDomains(const char *pattern, bool sh
*** 2788,2794 ****
   * Describes conversions.
   */
  bool
! listConversions(const char *pattern, bool showSystem)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
--- 2769,2775 ----
   * Describes conversions.
   */
  bool
! listConversions(const char *pattern, bool verbose, bool showSystem)
  {
  	PQExpBufferData buf;
  	PGresult   *res;
*************** listConversions(const char *pattern, boo
*** 2803,2811 ****
  	   "       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\"\n"
! 			   "FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n\n"
! 					  "WHERE n.oid = c.connamespace\n",
  					  gettext_noop("Schema"),
  					  gettext_noop("Name"),
  					  gettext_noop("Source"),
--- 2784,2790 ----
  	   "       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"),
*************** listConversions(const char *pattern, boo
*** 2813,2821 ****
  					  gettext_noop("yes"), gettext_noop("no"),
  					  gettext_noop("Default?"));
  
  	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,
--- 2792,2819 ----
  					  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,
*************** listForeignDataWrappers(const char *patt
*** 3650,3665 ****
  
  	initPQExpBuffer(&buf);
  	printfPQExpBuffer(&buf,
! 					  "SELECT fdwname AS \"%s\",\n"
! 					  "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n",
  					  gettext_noop("Name"),
  					  gettext_noop("Owner"));
  	if (pset.sversion >= 90100)
  		appendPQExpBuffer(&buf,
! 						  "  fdwhandler::pg_catalog.regproc AS \"%s\",\n",
  						  gettext_noop("Handler"));
  	appendPQExpBuffer(&buf,
! 					  "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
  					  gettext_noop("Validator"));
  
  	if (verbose)
--- 3648,3663 ----
  
  	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)
*************** listForeignDataWrappers(const char *patt
*** 3669,3677 ****
  		appendPQExpBuffer(&buf,
  						  ",\n  fdwoptions AS \"%s\"",
  						  gettext_noop("Options"));
  	}
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_data_wrapper\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "fdwname", NULL, NULL);
--- 3667,3686 ----
  		appendPQExpBuffer(&buf,
  						  ",\n  fdwoptions AS \"%s\"",
  						  gettext_noop("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);
*************** listForeignServers(const char *pattern, 
*** 3729,3744 ****
  						  ",\n"
  						  "  s.srvtype AS \"%s\",\n"
  						  "  s.srvversion AS \"%s\",\n"
! 						  "  s.srvoptions AS \"%s\"",
  						  gettext_noop("Type"),
  						  gettext_noop("Version"),
! 						  gettext_noop("Options"));
  	}
  
  	appendPQExpBuffer(&buf,
  					  "\nFROM pg_catalog.pg_foreign_server s\n"
  	   "     JOIN pg_catalog.pg_foreign_data_wrapper f ON f.oid=s.srvfdw\n");
  
  	processSQLNamePattern(pset.db, &buf, pattern, false, false,
  						  NULL, "s.srvname", NULL, NULL);
  
--- 3738,3761 ----
  						  ",\n"
  						  "  s.srvtype AS \"%s\",\n"
  						  "  s.srvversion AS \"%s\",\n"
! 						  "  s.srvoptions AS \"%s\",\n"
! 						  "  d.description AS \"%s\"",
  						  gettext_noop("Type"),
  						  gettext_noop("Version"),
! 						  gettext_noop("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);
  
*************** listForeignTables(const char *pattern, b
*** 3842,3859 ****
  
  	if (verbose)
  		appendPQExpBuffer(&buf,
! 						  ",\n  ft.ftoptions AS \"%s\"",
! 						  gettext_noop("Options"));
  
! 	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
! 	appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s\n");
! 	appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
! 	appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
! 	appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
  
! 	processSQLNamePattern(pset.db, &buf, pattern, true, false,
  						  NULL, "n.nspname", "c.relname", NULL);
  
  	appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
--- 3859,3884 ----
  
  	if (verbose)
  		appendPQExpBuffer(&buf,
! 						  ",\n  ft.ftoptions AS \"%s\",\n"
! 						  "  d.description AS \"%s\"",
! 						  gettext_noop("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;");
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
new file mode 100644
index 7dc9a2c..bf4c312
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
*************** extern bool listAllDbs(bool verbose);
*** 61,70 ****
  extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
  
  /* \dD */
! extern bool listDomains(const char *pattern, bool showSystem);
  
  /* \dc */
! extern bool listConversions(const char *pattern, bool showSystem);
  
  /* \dC */
  extern bool listCasts(const char *pattern, bool verbose);
--- 61,70 ----
  extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
  
  /* \dD */
! extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
  
  /* \dc */
! extern bool listConversions(const char *pattern, bool verbose, bool showSystem);
  
  /* \dC */
  extern bool listCasts(const char *pattern, bool verbose);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
new file mode 100644
index d3b8c17..53e4cd0
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
*************** slashUsage(unsigned short int pager)
*** 195,205 ****
  	fprintf(output, _("  \\d[S+]  NAME           describe table, view, sequence, or index\n"));
  	fprintf(output, _("  \\da[S]  [PATTERN]      list aggregates\n"));
  	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
! 	fprintf(output, _("  \\dc[S]  [PATTERN]      list conversions\n"));
  	fprintf(output, _("  \\dC[+]  [PATTERN]      list casts\n"));
! 	fprintf(output, _("  \\dd[S]  [PATTERN]      show comments on objects\n"));
  	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
! 	fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
  	fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
  	fprintf(output, _("  \\des[+] [PATTERN]      list foreign servers\n"));
  	fprintf(output, _("  \\deu[+] [PATTERN]      list user mappings\n"));
--- 195,205 ----
  	fprintf(output, _("  \\d[S+]  NAME           describe table, view, sequence, or index\n"));
  	fprintf(output, _("  \\da[S]  [PATTERN]      list aggregates\n"));
  	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
! 	fprintf(output, _("  \\dc[S+] [PATTERN]      list conversions\n"));
  	fprintf(output, _("  \\dC[+]  [PATTERN]      list casts\n"));
! 	fprintf(output, _("  \\dd[S]  [PATTERN]      show object descriptions not displayed elsewhere\n"));
  	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
! 	fprintf(output, _("  \\dD[S+] [PATTERN]      list domains\n"));
  	fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));
  	fprintf(output, _("  \\des[+] [PATTERN]      list foreign servers\n"));
  	fprintf(output, _("  \\deu[+] [PATTERN]      list user mappings\n"));
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
new file mode 100644
index 45292b5..ef13f27
*** a/src/test/regress/expected/foreign_data.out
--- b/src/test/regress/expected/foreign_data.out
*************** ERROR:  foreign-data wrapper "foo" alrea
*** 52,63 ****
  DROP FOREIGN DATA WRAPPER foo;
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
  \dew+
!                                      List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   
! ------------+-------------------+---------+--------------------------+-------------------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
--- 52,63 ----
  DROP FOREIGN DATA WRAPPER foo;
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
  \dew+
!                                             List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   | Description 
! ------------+-------------------+---------+--------------------------+-------------------+-------------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |             | useless
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1} | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |             | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
*************** CREATE FOREIGN DATA WRAPPER foo OPTIONS 
*** 65,76 ****
  ERROR:  option "testing" provided more than once
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        
! ------------+-------------------+---------+--------------------------+-------------------+-----------------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
--- 65,76 ----
  ERROR:  option "testing" provided more than once
  CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
  \dew+
!                                                  List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        | Description 
! ------------+-------------------+---------+--------------------------+-------------------+-----------------------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |                       | useless
!  foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2} | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |                       | 
  (3 rows)
  
  DROP FOREIGN DATA WRAPPER foo;
*************** HINT:  Must be superuser to create a for
*** 81,92 ****
  RESET ROLE;
  CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | postgresql_fdw_validator |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  -- ALTER FOREIGN DATA WRAPPER
--- 81,92 ----
  RESET ROLE;
  CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options | Description 
! ------------+-------------------+---------+--------------------------+-------------------+---------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |         | useless
!  foo        | foreign_data_user | -       | postgresql_fdw_validator |                   |         | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |         | 
  (3 rows)
  
  -- ALTER FOREIGN DATA WRAPPER
*************** ALTER FOREIGN DATA WRAPPER foo VALIDATOR
*** 98,109 ****
  ERROR:  function bar(text[], oid) does not exist
  ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
--- 98,109 ----
  ERROR:  function bar(text[], oid) does not exist
  ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options | Description 
! ------------+-------------------+---------+--------------------------+-------------------+---------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |         | useless
!  foo        | foreign_data_user | -       | -                        |                   |         | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |         | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
*************** ALTER FOREIGN DATA WRAPPER foo OPTIONS (
*** 113,146 ****
  ERROR:  option "c" not found
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
  \dew+
!                                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
  \dew+
!                                     List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
! ------------+-------------------+---------+--------------------------+-------------------+-----------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
  ERROR:  option "b" provided more than once
  \dew+
!                                       List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    
! ------------+-------------------+---------+--------------------------+-------------------+---------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  SET ROLE regress_test_role;
--- 113,146 ----
  ERROR:  option "c" not found
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
  \dew+
!                                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  | Description 
! ------------+-------------------+---------+--------------------------+-------------------+-----------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |           | useless
!  foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2} | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |           | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
  \dew+
!                                            List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  | Description 
! ------------+-------------------+---------+--------------------------+-------------------+-----------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |           | useless
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4} | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |           | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
  ERROR:  option "b" provided more than once
  \dew+
!                                              List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    | Description 
! ------------+-------------------+---------+--------------------------+-------------------+---------------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |               | useless
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2} | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |               | 
  (3 rows)
  
  SET ROLE regress_test_role;
*************** HINT:  Must be superuser to alter a fore
*** 150,161 ****
  SET ROLE regress_test_role_super;
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
  \dew+
!                                         List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
--- 150,161 ----
  SET ROLE regress_test_role_super;
  ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
  \dew+
!                                                List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      | Description 
! ------------+-------------------+---------+--------------------------+-------------------+-------------------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |                   | useless
!  foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5} | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |                   | 
  (3 rows)
  
  ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
*************** ERROR:  permission denied to alter forei
*** 169,180 ****
  HINT:  Must be superuser to alter a foreign-data wrapper.
  RESET ROLE;
  \dew+
!                                            List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        |                   | 
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  -- DROP FOREIGN DATA WRAPPER
--- 169,180 ----
  HINT:  Must be superuser to alter a foreign-data wrapper.
  RESET ROLE;
  \dew+
!                                                   List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      | Description 
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------+-------------
!  dummy      | foreign_data_user       | -       | -                        |                   |                   | useless
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5} | 
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   |                   | 
  (3 rows)
  
  -- DROP FOREIGN DATA WRAPPER
*************** ERROR:  foreign-data wrapper "nonexisten
*** 183,194 ****
  DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
  NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
  \dew+
!                                            List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------
!  dummy      | foreign_data_user       | -       | -                        |                   | 
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  DROP ROLE regress_test_role_super;                          -- ERROR
--- 183,194 ----
  DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
  NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
  \dew+
!                                                   List of foreign-data wrappers
!     Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      | Description 
! ------------+-------------------------+---------+--------------------------+-------------------+-------------------+-------------
!  dummy      | foreign_data_user       | -       | -                        |                   |                   | useless
!  foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5} | 
!  postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   |                   | 
  (3 rows)
  
  DROP ROLE regress_test_role_super;                          -- ERROR
*************** ALTER ROLE regress_test_role_super SUPER
*** 203,213 ****
  DROP FOREIGN DATA WRAPPER foo;
  DROP ROLE regress_test_role_super;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (2 rows)
  
  CREATE FOREIGN DATA WRAPPER foo;
--- 203,213 ----
  DROP FOREIGN DATA WRAPPER foo;
  DROP ROLE regress_test_role_super;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options | Description 
! ------------+-------------------+---------+--------------------------+-------------------+---------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |         | useless
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |         | 
  (2 rows)
  
  CREATE FOREIGN DATA WRAPPER foo;
*************** CREATE SERVER s1 FOREIGN DATA WRAPPER fo
*** 215,233 ****
  COMMENT ON SERVER s1 IS 'foreign server';
  CREATE USER MAPPING FOR current_user SERVER s1;
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  foo        | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (3 rows)
  
  \des+
!                                     List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges | Type | Version | Options 
! ------+-------------------+----------------------+-------------------+------+---------+---------
!  s1   | foreign_data_user | foo                  |                   |      |         | 
  (1 row)
  
  \deu+
--- 215,233 ----
  COMMENT ON SERVER s1 IS 'foreign server';
  CREATE USER MAPPING FOR current_user SERVER s1;
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options | Description 
! ------------+-------------------+---------+--------------------------+-------------------+---------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |         | useless
!  foo        | foreign_data_user | -       | -                        |                   |         | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |         | 
  (3 rows)
  
  \des+
!                                              List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges | Type | Version | Options |  Description   
! ------+-------------------+----------------------+-------------------+------+---------+---------+----------------
!  s1   | foreign_data_user | foo                  |                   |      |         |         | foreign server
  (1 row)
  
  \deu+
*************** NOTICE:  drop cascades to 2 other object
*** 252,268 ****
  DETAIL:  drop cascades to server s1
  drop cascades to user mapping for foreign_data_user
  \dew+
!                                    List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
! ------------+-------------------+---------+--------------------------+-------------------+---------
!  dummy      | foreign_data_user | -       | -                        |                   | 
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
  (2 rows)
  
  \des+
!                               List of foreign servers
!  Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options 
! ------+-------+----------------------+-------------------+------+---------+---------
  (0 rows)
  
  \deu+
--- 252,268 ----
  DETAIL:  drop cascades to server s1
  drop cascades to user mapping for foreign_data_user
  \dew+
!                                           List of foreign-data wrappers
!     Name    |       Owner       | Handler |        Validator         | Access privileges | Options | Description 
! ------------+-------------------+---------+--------------------------+-------------------+---------+-------------
!  dummy      | foreign_data_user | -       | -                        |                   |         | useless
!  postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   |         | 
  (2 rows)
  
  \des+
!                                      List of foreign servers
!  Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | Options | Description 
! ------+-------+----------------------+-------------------+------+---------+---------+-------------
  (0 rows)
  
  \deu+
*************** ERROR:  invalid option "foo"
*** 289,305 ****
  HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
  CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
  (8 rows)
  
  SET ROLE regress_test_role;
--- 289,305 ----
  HINT:  Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
  CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            | Description 
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------+-------------
!  s1   | foreign_data_user | foo                  |                   |        |         |                              | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}            | 
!  s3   | foreign_data_user | foo                  |                   | oracle |         |                              | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}            | 
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    |                              | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}            | 
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}            | 
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db} | 
  (8 rows)
  
  SET ROLE regress_test_role;
*************** SET ROLE regress_test_role;
*** 311,328 ****
  CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
  RESET ROLE;
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                   |        |         | 
  (9 rows)
  
  REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
--- 311,328 ----
  CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
  RESET ROLE;
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            | Description 
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------+-------------
!  s1   | foreign_data_user | foo                  |                   |        |         |                              | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}            | 
!  s3   | foreign_data_user | foo                  |                   | oracle |         |                              | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}            | 
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    |                              | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}            | 
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}            | 
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db} | 
!  t1   | regress_test_role | foo                  |                   |        |         |                              | 
  (9 rows)
  
  REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
*************** GRANT regress_test_indirect TO regress_t
*** 335,353 ****
  SET ROLE regress_test_role;
  CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
  \des+
!                                                 List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  |                   |        |         | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                   | oracle |         | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                   |        |         | 
!  t2   | regress_test_role | foo                  |                   |        |         | 
  (10 rows)
  
  RESET ROLE;
--- 335,353 ----
  SET ROLE regress_test_role;
  CREATE SERVER t2 FOREIGN DATA WRAPPER foo;
  \des+
!                                                        List of foreign servers
!  Name |       Owner       | Foreign-data wrapper | Access privileges |  Type  | Version |           Options            | Description 
! ------+-------------------+----------------------+-------------------+--------+---------+------------------------------+-------------
!  s1   | foreign_data_user | foo                  |                   |        |         |                              | 
!  s2   | foreign_data_user | foo                  |                   |        |         | {host=a,dbname=b}            | 
!  s3   | foreign_data_user | foo                  |                   | oracle |         |                              | 
!  s4   | foreign_data_user | foo                  |                   | oracle |         | {host=a,dbname=b}            | 
!  s5   | foreign_data_user | foo                  |                   |        | 15.0    |                              | 
!  s6   | foreign_data_user | foo                  |                   |        | 16.0    | {host=a,dbname=b}            | 
!  s7   | foreign_data_user | foo                  |                   | oracle | 17.0    | {host=a,dbname=b}            | 
!  s8   | foreign_data_user | postgresql           |                   |        |         | {host=localhost,dbname=s8db} | 
!  t1   | regress_test_role | foo                  |                   |        |         |                              | 
!  t2   | regress_test_role | foo                  |                   |        |         |                              | 
  (10 rows)
  
  RESET ROLE;
*************** ALTER SERVER s3 OPTIONS (tnsname 'orcl',
*** 365,385 ****
  GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
  GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
  \des+
!                                                            List of foreign servers
!  Name |       Owner       | Foreign-data wrapper |            Access privileges            |  Type  | Version |           Options            
! ------+-------------------+----------------------+-----------------------------------------+--------+---------+------------------------------
!  s1   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}
!       |                   |                      | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user | foo                  |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user | foo                  |                                         |        | 15.0    | 
!  s6   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                   |                      | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user | postgresql           |                                         |        |         | {host=localhost,dbname=s8db}
!  t1   | regress_test_role | foo                  |                                         |        |         | 
!  t2   | regress_test_role | foo                  |                                         |        |         | 
  (10 rows)
  
  SET ROLE regress_test_role;
--- 365,385 ----
  GRANT USAGE ON FOREIGN SERVER s1 TO regress_test_role;
  GRANT USAGE ON FOREIGN SERVER s6 TO regress_test_role2 WITH GRANT OPTION;
  \des+
!                                                                   List of foreign servers
!  Name |       Owner       | Foreign-data wrapper |            Access privileges            |  Type  | Version |           Options            | Description 
! ------+-------------------+----------------------+-----------------------------------------+--------+---------+------------------------------+-------------
!  s1   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.0     | {servername=s1}              | 
!       |                   |                      | regress_test_role=U/foreign_data_user   |        |         |                              | 
!  s2   | foreign_data_user | foo                  |                                         |        | 1.1     | {host=a,dbname=b}            | 
!  s3   | foreign_data_user | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}     | 
!  s4   | foreign_data_user | foo                  |                                         | oracle |         | {host=a,dbname=b}            | 
!  s5   | foreign_data_user | foo                  |                                         |        | 15.0    |                              | 
!  s6   | foreign_data_user | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}            | 
!       |                   |                      | regress_test_role2=U*/foreign_data_user |        |         |                              | 
!  s7   | foreign_data_user | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}            | 
!  s8   | foreign_data_user | postgresql           |                                         |        |         | {host=localhost,dbname=s8db} | 
!  t1   | regress_test_role | foo                  |                                         |        |         |                              | 
!  t2   | regress_test_role | foo                  |                                         |        |         |                              | 
  (10 rows)
  
  SET ROLE regress_test_role;
*************** ERROR:  role "regress_test_indirect" can
*** 416,436 ****
  DETAIL:  owner of server s1
  privileges for foreign-data wrapper foo
  \des+
!                                                               List of foreign servers
!  Name |         Owner         | Foreign-data wrapper |            Access privileges            |  Type  | Version |             Options             
! ------+-----------------------+----------------------+-----------------------------------------+--------+---------+---------------------------------
!  s1   | regress_test_indirect | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}
!       |                       |                      | regress_test_role=U/foreign_data_user   |        |         | 
!  s2   | foreign_data_user     | foo                  |                                         |        | 1.1     | {host=a,dbname=b}
!  s3   | foreign_data_user     | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}
!  s4   | foreign_data_user     | foo                  |                                         | oracle |         | {host=a,dbname=b}
!  s5   | foreign_data_user     | foo                  |                                         |        | 15.0    | 
!  s6   | foreign_data_user     | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}
!       |                       |                      | regress_test_role2=U*/foreign_data_user |        |         | 
!  s7   | foreign_data_user     | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}
!  s8   | foreign_data_user     | postgresql           |                                         |        |         | {dbname=db1,connect_timeout=30}
!  t1   | regress_test_role     | foo                  |                                         |        |         | 
!  t2   | regress_test_role     | foo                  |                                         |        |         | 
  (10 rows)
  
  -- DROP SERVER
--- 416,436 ----
  DETAIL:  owner of server s1
  privileges for foreign-data wrapper foo
  \des+
!                                                                      List of foreign servers
!  Name |         Owner         | Foreign-data wrapper |            Access privileges            |  Type  | Version |             Options             | Description 
! ------+-----------------------+----------------------+-----------------------------------------+--------+---------+---------------------------------+-------------
!  s1   | regress_test_indirect | foo                  | foreign_data_user=U/foreign_data_user  +|        | 1.1     | {servername=s1}                 | 
!       |                       |                      | regress_test_role=U/foreign_data_user   |        |         |                                 | 
!  s2   | foreign_data_user     | foo                  |                                         |        | 1.1     | {host=a,dbname=b}               | 
!  s3   | foreign_data_user     | foo                  |                                         | oracle |         | {tnsname=orcl,port=1521}        | 
!  s4   | foreign_data_user     | foo                  |                                         | oracle |         | {host=a,dbname=b}               | 
!  s5   | foreign_data_user     | foo                  |                                         |        | 15.0    |                                 | 
!  s6   | foreign_data_user     | foo                  | foreign_data_user=U/foreign_data_user  +|        | 16.0    | {host=a,dbname=b}               | 
!       |                       |                      | regress_test_role2=U*/foreign_data_user |        |         |                                 | 
!  s7   | foreign_data_user     | foo                  |                                         | oracle | 17.0    | {host=a,dbname=b}               | 
!  s8   | foreign_data_user     | postgresql           |                                         |        |         | {dbname=db1,connect_timeout=30} | 
!  t1   | regress_test_role     | foo                  |                                         |        |         |                                 | 
!  t2   | regress_test_role     | foo                  |                                         |        |         |                                 | 
  (10 rows)
  
  -- DROP SERVER
*************** Server: sc
*** 663,672 ****
  Has OIDs: no
  
  \det+
!                 List of foreign tables
!  Schema | Table | Server |          Options           
! --------+-------+--------+----------------------------
!  public | ft1   | sc     | {"delimiter=,","quote=\""}
  (1 row)
  
  CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
--- 663,672 ----
  Has OIDs: no
  
  \det+
!                        List of foreign tables
!  Schema | Table | Server |          Options           | Description 
! --------+-------+--------+----------------------------+-------------
!  public | ft1   | sc     | {"delimiter=,","quote=\""} | ft1
  (1 row)
  
  CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR


^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-08 20:34  Robert Haas <robertmhaas@gmail.com>
  parent: Josh Kupershmidt <schmiddy@gmail.com>
  0 siblings, 1 reply; 14+ messages in thread

From: Robert Haas @ 2011-08-08 20:34 UTC (permalink / raw)
  To: Josh Kupershmidt <schmiddy@gmail.com>; +Cc: pgsql-hackers; Shigeru Hanada <shigeru.hanada@gmail.com>

On Fri, Aug 5, 2011 at 7:25 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
> On Fri, Aug 5, 2011 at 8:32 AM, Robert Haas <robertmhaas@gmail.com> wrote:
>> I guess my vote is to make the SQL/MED stuff show the description only
>> in verbose mode, and always at the end; and revise what we did with
>> \dL to put the description at the end even in verbose mode.
>
> Yeah, that sounds fine to me. I've revised the residual patch to do
> so, as well as incorporated your earlier suggestion about having \dD
> and \dc only display descriptions in verbose-mode.
>
> I did more testing, and found and fixed some brokenness related to
> d.objsubid I had introduced into the listConversions() query, as well
> as improved the version checking in objectDescription(). Updated patch
> attached.

OK, I've now committed most of this, with some additions to the
documentation.  Remaining bits attached.

I am a bit confused as to why we have both \det and \dE.  They seem
redundant.  Shouldn't we rip one of those out?  IMHO, \det should be
the one to go, as it could be useful to do, e.g. \dtvE, which isn't
going to work with the \det syntax.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

  [application/octet-stream] describe_comments.v5.patch (11.4K, ../../CA+TgmoZw5kU8WAXjbe3OaZQOi-nYfzFgkxv9tx1NtNdM9vN2yQ@mail.gmail.com/2-describe_comments.v5.patch)
  download | inline diff:
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index db97df1..3a10e76 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -990,24 +990,21 @@ testdb=&gt;
         <term><literal>\dd[S] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
         <listitem>
         <para>
-        Shows the descriptions of objects matching the <replaceable
-        class="parameter">pattern</replaceable>, or of all visible objects if
-        no argument is given.  But in either case, only objects that have
-        a description are listed.
+        Shows the descriptions of objects of type <literal>constraint</>,
+        <literal>operator class</>, <literal>operator family</>,
+        <literal>rule</>, and <literal>trigger</>. All
+        other comments may be viewed by the respective backslash commands for
+        those object types.
+        </para>
+
+        <para>
+        <literal>\dd</literal> displays descriptions for objects matching the
+        <replaceable class="parameter">pattern</replaceable>, or of visible
+        objects of the appropriate type if no argument is given.  But in either
+        case, only objects that have a description are listed.
         By default, only user-created objects are shown;  supply a
         pattern or the <literal>S</literal> modifier to include system
         objects.
-        <quote>Object</quote> covers aggregates, functions, operators,
-        types, relations (tables, views, indexes, sequences), large
-        objects, rules, and triggers. For example:
-<programlisting>
-=&gt; <userinput>\dd version</userinput>
-                     Object descriptions
-   Schema   |  Name   |  Object  |        Description
-------------+---------+----------+---------------------------
- pg_catalog | version | function | PostgreSQL version string
-(1 row)
-</programlisting>
         </para>
 
         <para>
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index f08f917..c7bea0d 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -830,8 +830,11 @@ listDefaultACLs(const char *pattern)
  *
  * \dd [foo]
  *
- * Note: This only lists things that actually have a description. For complete
- * lists of things, there are other \d? commands.
+ * 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)
@@ -851,110 +854,76 @@ objectDescription(const char *pattern, bool showSystem)
 					  gettext_noop("Object"),
 					  gettext_noop("Description"));
 
-	/* Aggregate descriptions */
-	appendPQExpBuffer(&buf,
-					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
-					  "  n.nspname as nspname,\n"
-					  "  CAST(p.proname AS pg_catalog.text) as name,"
-					  "  CAST('%s' AS pg_catalog.text) as object\n"
-					  "  FROM pg_catalog.pg_proc p\n"
-	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
-					  "  WHERE p.proisagg\n",
-					  gettext_noop("aggregate"));
-
-	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)");
-
-	/* Function descriptions */
-	appendPQExpBuffer(&buf,
-					  "UNION ALL\n"
-					  "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
-					  "  n.nspname as nspname,\n"
-					  "  CAST(p.proname AS pg_catalog.text) as name,"
-					  "  CAST('%s' AS pg_catalog.text) as object\n"
-					  "  FROM pg_catalog.pg_proc p\n"
-	 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
-					  "  WHERE NOT p.proisagg\n",
-					  gettext_noop("function"));
-
-	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)");
-
-	/* Operator descriptions */
+	/* Constraint descriptions */
 	appendPQExpBuffer(&buf,
-					  "UNION ALL\n"
-					  "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
+					  "  SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
 					  "  n.nspname as nspname,\n"
-					  "  CAST(o.oprname AS pg_catalog.text) as name,"
+					  "  CAST(pgc.conname AS pg_catalog.text) as name,"
 					  "  CAST('%s' AS pg_catalog.text) as object\n"
-					  "  FROM pg_catalog.pg_operator o\n"
-	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
-					  gettext_noop("operator"));
+					  "  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", "o.oprname", NULL,
-						  "pg_catalog.pg_operator_is_visible(o.oid)");
-
-	/* Type descriptions */
-	appendPQExpBuffer(&buf,
-					  "UNION ALL\n"
-					  "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
-					  "  n.nspname as nspname,\n"
-					  "  pg_catalog.format_type(t.oid, NULL) as name,"
-					  "  CAST('%s' AS pg_catalog.text) as object\n"
-					  "  FROM pg_catalog.pg_type t\n"
-	"       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
-					  gettext_noop("data type"));
-
-	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", "pg_catalog.format_type(t.oid, NULL)",
-						  NULL,
-						  "pg_catalog.pg_type_is_visible(t.oid)");
-
-	/* Relation (tables, views, indexes, sequences) descriptions */
-	appendPQExpBuffer(&buf,
-					  "UNION ALL\n"
-					  "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
-					  "  n.nspname as nspname,\n"
-					  "  CAST(c.relname AS pg_catalog.text) as name,\n"
-					  "  CAST(\n"
-					  "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END"
-					  "  AS pg_catalog.text) as object\n"
-					  "  FROM 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', 'i', 'S', 'f')\n",
-					  gettext_noop("table"),
-					  gettext_noop("view"),
-					  gettext_noop("index"),
-					  gettext_noop("sequence"),
-					  gettext_noop("foreign table"));
-
-	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.relname", NULL,
+	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+, and comment on operator
+	 * family 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)");
+
+		/* 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,
 					  "UNION ALL\n"
@@ -972,7 +941,6 @@ objectDescription(const char *pattern, bool showSystem)
 		appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
 						  "      AND n.nspname <> 'information_schema'\n");
 
-	/* XXX not sure what to do about visibility rule here? */
 	processSQLNamePattern(pset.db, &buf, pattern, true, false,
 						  "n.nspname", "r.rulename", NULL,
 						  "pg_catalog.pg_table_is_visible(c.oid)");
@@ -993,7 +961,6 @@ objectDescription(const char *pattern, bool showSystem)
 		appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
 						  "      AND n.nspname <> 'information_schema'\n");
 
-	/* XXX not sure what to do about visibility rule here? */
 	processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
 						  "n.nspname", "t.tgname", NULL,
 						  "pg_catalog.pg_table_is_visible(c.oid)");
@@ -2739,6 +2706,7 @@ listDomains(const char *pattern, bool verbose, bool showSystem)
 					  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"
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index b99ba3a..53e4cd0 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -197,7 +197,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\db[+]  [PATTERN]      list tablespaces\n"));
 	fprintf(output, _("  \\dc[S+] [PATTERN]      list conversions\n"));
 	fprintf(output, _("  \\dC[+]  [PATTERN]      list casts\n"));
-	fprintf(output, _("  \\dd[S]  [PATTERN]      show comments on objects\n"));
+	fprintf(output, _("  \\dd[S]  [PATTERN]      show object descriptions not displayed elsewhere\n"));
 	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
 	fprintf(output, _("  \\dD[S+] [PATTERN]      list domains\n"));
 	fprintf(output, _("  \\det[+] [PATTERN]      list foreign tables\n"));


^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-08 22:01  Josh Kupershmidt <schmiddy@gmail.com>
  parent: Robert Haas <robertmhaas@gmail.com>
  0 siblings, 3 replies; 14+ messages in thread

From: Josh Kupershmidt @ 2011-08-08 22:01 UTC (permalink / raw)
  To: Robert Haas <robertmhaas@gmail.com>; +Cc: pgsql-hackers; Shigeru Hanada <shigeru.hanada@gmail.com>

On Mon, Aug 8, 2011 at 4:34 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> OK, I've now committed most of this, with some additions to the
> documentation.  Remaining bits attached.

Looks good, thanks.

> I am a bit confused as to why we have both \det and \dE.  They seem
> redundant.  Shouldn't we rip one of those out?  IMHO, \det should be
> the one to go, as it could be useful to do, e.g. \dtvE, which isn't
> going to work with the \det syntax.

Yeah, I was wondering that myself. At first I thought maybe someone
added in one without being aware of the other, but it looks like both
\dE and \det got added in by commit
0d692a0dc9f0e532c67c577187fe5d7d323cb95b. They are using different
queries internally (pg_class vs. pg_foreign_table), but I doubt end
users would care about that. Or perhaps the author just wanted a
command name similar to \dew and \des.

+1 for getting rid of one of them; I don't really care which one gets
the axe. Though we should make sure to be able to show all possible
columns in whichever command we keep (i.e. add "Options" column to
\dE+ if we keep that one). BTW, I haven't bothered setting up
functioning foreign tables yet, but is "Size" always going to be 0
bytes in \dE+?

Josh



^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-08 22:10  Josh Kupershmidt <schmiddy@gmail.com>
  parent: Josh Kupershmidt <schmiddy@gmail.com>
  2 siblings, 0 replies; 14+ messages in thread

From: Josh Kupershmidt @ 2011-08-08 22:10 UTC (permalink / raw)
  To: Robert Haas <robertmhaas@gmail.com>; +Cc: pgsql-hackers; Shigeru Hanada <shigeru.hanada@gmail.com>

On Mon, Aug 8, 2011 at 6:01 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
> (i.e. add "Options" column to \dE+ if we keep that one).

Oh nevermind, "Options" is displayed by \d+ foreign_table_name.

Josh



^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-09 06:41  Shigeru Hanada <shigeru.hanada@gmail.com>
  parent: Josh Kupershmidt <schmiddy@gmail.com>
  2 siblings, 1 reply; 14+ messages in thread

From: Shigeru Hanada @ 2011-08-09 06:41 UTC (permalink / raw)
  To: Josh Kupershmidt <schmiddy@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers

(2011/08/09 7:01), Josh Kupershmidt wrote:
>> I am a bit confused as to why we have both \det and \dE.  They seem
>> redundant.  Shouldn't we rip one of those out?  IMHO, \det should be
>> the one to go, as it could be useful to do, e.g. \dtvE, which isn't
>> going to work with the \det syntax.
> 
> Yeah, I was wondering that myself. At first I thought maybe someone
> added in one without being aware of the other, but it looks like both
> \dE and \det got added in by commit
> 0d692a0dc9f0e532c67c577187fe5d7d323cb95b. They are using different
> queries internally (pg_class vs. pg_foreign_table), but I doubt end
> users would care about that. Or perhaps the author just wanted a
> command name similar to \dew and \des.

I'm the author of that patch, sorry for confusion.  May I explain the
background of implementing those command? :)

Basically, during implementing foreign table support, I tried to follow
the existing design.

I found two backslash command groups in psql, \de[wsu] and \d[tvsT],
which are relevant to foreign table.  Former is a group for listing
relation (table, view, sequence and type), and they have common output
format.  Users would use them (sometimes with combination like \dtv) to
list only specified type of relations.  OTOH latter is a group for
listing SQL/MED objects (wrapper, server and user mapping), and commands
in that group have different output columns.  Users would use them to
see detail of FDW options, maybe mainly FDW options.

They had been implemented in different ways then, and I kept
implementation similar to existing codes for each.  But now, as you say,
they seem redundant and inconsistent each other.

> +1 for getting rid of one of them; I don't really care which one gets
> the axe. Though we should make sure to be able to show all possible
> columns in whichever command we keep (i.e. add "Options" column to
> \dE+ if we keep that one).

I'm not sure whether getting rid of one of them is better.
But maybe \dE is useless than \det, because former doesn't shows more
information than simple \d.

>                             BTW, I haven't bothered setting up
> functioning foreign tables yet, but is "Size" always going to be 0
> bytes in \dE+?

Yes, "Size" is total file size calculated with pg_table_size(), but
foreign table has no data file.

Regards,
-- 
Shigeru Hanada



^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-11 14:48  Robert Haas <robertmhaas@gmail.com>
  parent: Shigeru Hanada <shigeru.hanada@gmail.com>
  0 siblings, 0 replies; 14+ messages in thread

From: Robert Haas @ 2011-08-11 14:48 UTC (permalink / raw)
  To: Shigeru Hanada <shigeru.hanada@gmail.com>; +Cc: Josh Kupershmidt <schmiddy@gmail.com>; pgsql-hackers

2011/8/9 Shigeru Hanada <shigeru.hanada@gmail.com>:
> I'm the author of that patch, sorry for confusion.  May I explain the
> background of implementing those command? :)
>
> Basically, during implementing foreign table support, I tried to follow
> the existing design.
>
> I found two backslash command groups in psql, \de[wsu] and \d[tvsT],
> which are relevant to foreign table.  Former is a group for listing
> relation (table, view, sequence and type), and they have common output
> format.  Users would use them (sometimes with combination like \dtv) to
> list only specified type of relations.  OTOH latter is a group for
> listing SQL/MED objects (wrapper, server and user mapping), and commands
> in that group have different output columns.  Users would use them to
> see detail of FDW options, maybe mainly FDW options.
>
> They had been implemented in different ways then, and I kept
> implementation similar to existing codes for each.  But now, as you say,
> they seem redundant and inconsistent each other.
>
>> +1 for getting rid of one of them; I don't really care which one gets
>> the axe. Though we should make sure to be able to show all possible
>> columns in whichever command we keep (i.e. add "Options" column to
>> \dE+ if we keep that one).
>
> I'm not sure whether getting rid of one of them is better.
> But maybe \dE is useless than \det, because former doesn't shows more
> information than simple \d.

Looking at this again, I see that they each have certain advantages.
\det shows more information that is specific to foreign tables; but I
don't want to get rid of \dE because it's useful to be able to do
stuff like \dtE to see tables and foreign tables in a single listing.
So maybe we should just leave this well enough alone.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



^ permalink  raw  reply  [nested|flat] 14+ messages in thread

* Re: psql: display of object comments
@ 2011-08-11 15:17  Robert Haas <robertmhaas@gmail.com>
  parent: Josh Kupershmidt <schmiddy@gmail.com>
  2 siblings, 0 replies; 14+ messages in thread

From: Robert Haas @ 2011-08-11 15:17 UTC (permalink / raw)
  To: Josh Kupershmidt <schmiddy@gmail.com>; +Cc: pgsql-hackers; Shigeru Hanada <shigeru.hanada@gmail.com>

On Mon, Aug 8, 2011 at 6:01 PM, Josh Kupershmidt <schmiddy@gmail.com> wrote:
> On Mon, Aug 8, 2011 at 4:34 PM, Robert Haas <robertmhaas@gmail.com> wrote:
>> OK, I've now committed most of this, with some additions to the
>> documentation.  Remaining bits attached.
>
> Looks good, thanks.

And now I've committed (nearly) all of what remains.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



^ permalink  raw  reply  [nested|flat] 14+ messages in thread


end of thread, other threads:[~2011-08-11 15:17 UTC | newest]

Thread overview: 14+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2011-07-09 17:16 psql: display of object comments Josh Kupershmidt <schmiddy@gmail.com>
2011-07-23 02:44 ` Josh Kupershmidt <schmiddy@gmail.com>
2011-07-25 16:27   ` Robert Haas <robertmhaas@gmail.com>
2011-07-27 00:38     ` Josh Kupershmidt <schmiddy@gmail.com>
2011-08-04 16:26       ` Robert Haas <robertmhaas@gmail.com>
2011-08-05 02:24         ` Josh Kupershmidt <schmiddy@gmail.com>
2011-08-05 12:32           ` Robert Haas <robertmhaas@gmail.com>
2011-08-05 23:25             ` Josh Kupershmidt <schmiddy@gmail.com>
2011-08-08 20:34               ` Robert Haas <robertmhaas@gmail.com>
2011-08-08 22:01                 ` Josh Kupershmidt <schmiddy@gmail.com>
2011-08-08 22:10                   ` Josh Kupershmidt <schmiddy@gmail.com>
2011-08-09 06:41                   ` Shigeru Hanada <shigeru.hanada@gmail.com>
2011-08-11 14:48                     ` Robert Haas <robertmhaas@gmail.com>
2011-08-11 15:17                   ` Robert Haas <robertmhaas@gmail.com>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox