]> granicus.if.org Git - postgresql/blobdiff - src/bin/psql/describe.c
Teach psql to display the comments on conversions and domains.
[postgresql] / src / bin / psql / describe.c
index 0cc52c8b71a21892b16e87edadee38a9314a2347..5587721ea8f0c4b040e5dfe833849f012a4896fb 100644 (file)
@@ -6,9 +6,9 @@
  * with servers of versions 7.4 and up.  It's okay to omit irrelevant
  * information for an old server, but not to fail outright.
  *
- * Copyright (c) 2000-2009, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2011, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.201 2009/02/24 10:06:34 petere Exp $
+ * src/bin/psql/describe.c
  */
 #include "postgres_fe.h"
 
@@ -28,7 +28,7 @@ static bool describeOneTableDetails(const char *schemaname,
                                                const char *oid,
                                                bool verbose);
 static void add_tablespace_footer(printTableContent *const cont, char relkind,
-                                               Oid tablespace, const bool newline);
+                                         Oid tablespace, const bool newline);
 static void add_role_attribute(PQExpBuffer buf, const char *const str);
 static bool listTSParsersVerbose(const char *pattern);
 static bool describeOneTSParser(const char *oid, const char *nspname,
@@ -38,6 +38,7 @@ static bool describeOneTSConfig(const char *oid, const char *nspname,
                                        const char *cfgname,
                                        const char *pnspname, const char *prsname);
 static void printACLColumn(PQExpBuffer buf, const char *colname);
+static bool listOneExtensionContents(const char *extname, const char *oid);
 
 
 /*----------------
@@ -64,28 +65,28 @@ describeAggregates(const char *pattern, bool verbose, bool showSystem)
        printfPQExpBuffer(&buf,
                                          "SELECT n.nspname as \"%s\",\n"
                                          "  p.proname AS \"%s\",\n"
-                                         "  pg_catalog.format_type(p.prorettype, NULL) AS \"%s\",\n",
+                                "  pg_catalog.format_type(p.prorettype, NULL) AS \"%s\",\n",
                                          gettext_noop("Schema"),
                                          gettext_noop("Name"),
                                          gettext_noop("Result data type"));
 
        if (pset.sversion >= 80200)
-           appendPQExpBuffer(&buf,
-                                         "  CASE WHEN p.pronargs = 0\n"
-                                         "    THEN CAST('*' AS pg_catalog.text)\n"
-                                         "    ELSE\n"
-                                         "    pg_catalog.array_to_string(ARRAY(\n"
-                                         "      SELECT\n"
-                                         "        pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
-                                         "      FROM\n"
-                                         "        pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
-                                         "    ), ', ')\n"
-                                         "  END AS \"%s\",\n",
-                                         gettext_noop("Argument data types"));
+               appendPQExpBuffer(&buf,
+                                                 "  CASE WHEN p.pronargs = 0\n"
+                                                 "    THEN CAST('*' AS pg_catalog.text)\n"
+                                                 "    ELSE\n"
+                                                 "    pg_catalog.array_to_string(ARRAY(\n"
+                                                 "      SELECT\n"
+                                "        pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
+                                                 "      FROM\n"
+                                                 "        pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
+                                                 "    ), ', ')\n"
+                                                 "  END AS \"%s\",\n",
+                                                 gettext_noop("Argument data types"));
        else
-           appendPQExpBuffer(&buf,
-                                         "  pg_catalog.format_type(p.proargtypes[0], NULL) AS \"%s\",\n",
-                                         gettext_noop("Argument data types"));
+               appendPQExpBuffer(&buf,
+                        "  pg_catalog.format_type(p.proargtypes[0], NULL) AS \"%s\",\n",
+                                                 gettext_noop("Argument data types"));
 
        appendPQExpBuffer(&buf,
                                 "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
@@ -94,8 +95,9 @@ describeAggregates(const char *pattern, bool verbose, bool showSystem)
                                          "WHERE p.proisagg\n",
                                          gettext_noop("Description"));
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\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", "p.proname", NULL,
@@ -153,7 +155,7 @@ describeTablespaces(const char *pattern, bool verbose)
 
        if (verbose && pset.sversion >= 80200)
                appendPQExpBuffer(&buf,
-                          ",\n  pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
+                ",\n  pg_catalog.shobj_description(oid, 'pg_tablespace') AS \"%s\"",
                                                  gettext_noop("Description"));
 
        appendPQExpBuffer(&buf,
@@ -182,14 +184,49 @@ describeTablespaces(const char *pattern, bool verbose)
 
 
 /* \df
- * Takes an optional regexp to select particular functions
+ * Takes an optional regexp to select particular functions.
+ *
+ * As with \d, you can specify the kinds of functions you want:
+ *
+ * a for aggregates
+ * n for normal
+ * t for trigger
+ * w for window
+ *
+ * and you can mix and match these in any order.
  */
 bool
-describeFunctions(const char *pattern, bool verbose, bool showSystem)
+describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem)
 {
+       bool            showAggregate = strchr(functypes, 'a') != NULL;
+       bool            showNormal = strchr(functypes, 'n') != NULL;
+       bool            showTrigger = strchr(functypes, 't') != NULL;
+       bool            showWindow = strchr(functypes, 'w') != NULL;
+       bool            have_where;
        PQExpBufferData buf;
        PGresult   *res;
        printQueryOpt myopt = pset.popt;
+       static const bool translate_columns[] = {false, false, false, false, true, true, false, false, false, false};
+
+       if (strlen(functypes) != strspn(functypes, "antwS+"))
+       {
+               fprintf(stderr, _("\\df only takes [antwS+] as options\n"));
+               return true;
+       }
+
+       if (showWindow && pset.sversion < 80400)
+       {
+               fprintf(stderr, _("\\df does not take a \"w\" option with server version %d.%d\n"),
+                               pset.sversion / 10000, (pset.sversion / 100) % 100);
+               return true;
+       }
+
+       if (!showAggregate && !showNormal && !showTrigger && !showWindow)
+       {
+               showAggregate = showNormal = showTrigger = true;
+               if (pset.sversion >= 80400)
+                       showWindow = true;
+       }
 
        initPQExpBuffer(&buf);
 
@@ -199,66 +236,101 @@ describeFunctions(const char *pattern, bool verbose, bool showSystem)
                                          gettext_noop("Schema"),
                                          gettext_noop("Name"));
 
-    if (pset.sversion >= 80400)
+       if (pset.sversion >= 80400)
                appendPQExpBuffer(&buf,
-                                                 "  pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
-                                                 "  pg_catalog.pg_get_function_arguments(p.oid) as \"%s\"",
+                                       "  pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
+                                "  pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
+                                                 " CASE\n"
+                                                 "  WHEN p.proisagg THEN '%s'\n"
+                                                 "  WHEN p.proiswindow THEN '%s'\n"
+                                                 "  WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
+                                                 "  ELSE '%s'\n"
+                                                 "END as \"%s\"",
                                                  gettext_noop("Result data type"),
-                                                 gettext_noop("Argument data types"));
-    else if (pset.sversion >= 80100)
+                                                 gettext_noop("Argument data types"),
+               /* translator: "agg" is short for "aggregate" */
+                                                 gettext_noop("agg"),
+                                                 gettext_noop("window"),
+                                                 gettext_noop("trigger"),
+                                                 gettext_noop("normal"),
+                                                 gettext_noop("Type"));
+       else if (pset.sversion >= 80100)
                appendPQExpBuffer(&buf,
-                                         "  CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
+                                        "  CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
                                  "  pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
-                                         "  CASE WHEN proallargtypes IS NOT NULL THEN\n"
-                                         "    pg_catalog.array_to_string(ARRAY(\n"
-                                         "      SELECT\n"
-                                         "        CASE\n"
-                                         "          WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
+                                                 "  CASE WHEN proallargtypes IS NOT NULL THEN\n"
+                                                 "    pg_catalog.array_to_string(ARRAY(\n"
+                                                 "      SELECT\n"
+                                                 "        CASE\n"
+                                                 "          WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
                                          "          WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n"
-                                         "          WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
-                                         "          WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n"
-                                         "        END ||\n"
-                                         "        CASE\n"
+                                       "          WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
+                                "          WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n"
+                                                 "        END ||\n"
+                                                 "        CASE\n"
                         "          WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n"
-                                         "          ELSE p.proargnames[s.i] || ' ' \n"
-                                         "        END ||\n"
+                                                 "          ELSE p.proargnames[s.i] || ' ' \n"
+                                                 "        END ||\n"
                          "        pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n"
-                                         "      FROM\n"
-                                         "        pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n"
-                                         "    ), ', ')\n"
-                                         "  ELSE\n"
-                                         "    pg_catalog.array_to_string(ARRAY(\n"
-                                         "      SELECT\n"
-                                         "        CASE\n"
+                                                 "      FROM\n"
+                                                 "        pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n"
+                                                 "    ), ', ')\n"
+                                                 "  ELSE\n"
+                                                 "    pg_catalog.array_to_string(ARRAY(\n"
+                                                 "      SELECT\n"
+                                                 "        CASE\n"
                   "          WHEN COALESCE(p.proargnames[s.i+1], '') = '' THEN ''\n"
-                                         "          ELSE p.proargnames[s.i+1] || ' '\n"
-                                         "          END ||\n"
+                                                 "          ELSE p.proargnames[s.i+1] || ' '\n"
+                                                 "          END ||\n"
                                 "        pg_catalog.format_type(p.proargtypes[s.i], NULL)\n"
-                                         "      FROM\n"
-                                         "        pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
-                                         "    ), ', ')\n"
-                                         "  END AS \"%s\"",
+                                                 "      FROM\n"
+                                                 "        pg_catalog.generate_series(0, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n"
+                                                 "    ), ', ')\n"
+                                                 "  END AS \"%s\",\n"
+                                                 "  CASE\n"
+                                                 "    WHEN p.proisagg THEN '%s'\n"
+                                                 "    WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
+                                                 "    ELSE '%s'\n"
+                                                 "  END AS \"%s\"",
                                                  gettext_noop("Result data type"),
-                                                 gettext_noop("Argument data types"));
+                                                 gettext_noop("Argument data types"),
+               /* translator: "agg" is short for "aggregate" */
+                                                 gettext_noop("agg"),
+                                                 gettext_noop("trigger"),
+                                                 gettext_noop("normal"),
+                                                 gettext_noop("Type"));
        else
                appendPQExpBuffer(&buf,
-                                         "  CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
+                                        "  CASE WHEN p.proretset THEN 'SETOF ' ELSE '' END ||\n"
                                  "  pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
-                                         "  pg_catalog.oidvectortypes(p.proargtypes) as \"%s\"",
+                                       "  pg_catalog.oidvectortypes(p.proargtypes) as \"%s\",\n"
+                                                 "  CASE\n"
+                                                 "    WHEN p.proisagg THEN '%s'\n"
+                                                 "    WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
+                                                 "    ELSE '%s'\n"
+                                                 "  END AS \"%s\"",
                                                  gettext_noop("Result data type"),
-                                                 gettext_noop("Argument data types"));
+                                                 gettext_noop("Argument data types"),
+               /* translator: "agg" is short for "aggregate" */
+                                                 gettext_noop("agg"),
+                                                 gettext_noop("trigger"),
+                                                 gettext_noop("normal"),
+                                                 gettext_noop("Type"));
 
        if (verbose)
                appendPQExpBuffer(&buf,
                                                  ",\n CASE\n"
-                                                 "  WHEN p.provolatile = 'i' THEN 'immutable'\n"
-                                                 "  WHEN p.provolatile = 's' THEN 'stable'\n"
-                                                 "  WHEN p.provolatile = 'v' THEN 'volatile'\n"
+                                                 "  WHEN p.provolatile = 'i' THEN '%s'\n"
+                                                 "  WHEN p.provolatile = 's' THEN '%s'\n"
+                                                 "  WHEN p.provolatile = 'v' THEN '%s'\n"
                                                  "END as \"%s\""
-                                                 ",\n  pg_catalog.pg_get_userbyid(p.proowner) as \"%s\",\n"
+                                  ",\n  pg_catalog.pg_get_userbyid(p.proowner) as \"%s\",\n"
                                                  "  l.lanname as \"%s\",\n"
                                                  "  p.prosrc as \"%s\",\n"
                                  "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"",
+                                                 gettext_noop("immutable"),
+                                                 gettext_noop("stable"),
+                                                 gettext_noop("volatile"),
                                                  gettext_noop("Volatility"),
                                                  gettext_noop("Owner"),
                                                  gettext_noop("Language"),
@@ -267,27 +339,91 @@ describeFunctions(const char *pattern, bool verbose, bool showSystem)
 
        appendPQExpBuffer(&buf,
                                          "\nFROM pg_catalog.pg_proc p"
-                                         "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
+       "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
 
        if (verbose)
                appendPQExpBuffer(&buf,
-                                                 "     LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n");
+                  "     LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang\n");
 
-       /*
-        * we skip in/out funcs by excluding functions that take or return cstring
-        */
-       appendPQExpBuffer(&buf,
-                  "WHERE p.prorettype <> 'pg_catalog.cstring'::pg_catalog.regtype\n"
-                                         "      AND p.proargtypes[0] IS DISTINCT FROM 'pg_catalog.cstring'::pg_catalog.regtype\n"
-                                         "      AND NOT p.proisagg\n");
+       have_where = false;
+
+       /* filter by function type, if requested */
+       if (showNormal && showAggregate && showTrigger && showWindow)
+                /* Do nothing */ ;
+       else if (showNormal)
+       {
+               if (!showAggregate)
+               {
+                       if (have_where)
+                               appendPQExpBuffer(&buf, "      AND ");
+                       else
+                       {
+                               appendPQExpBuffer(&buf, "WHERE ");
+                               have_where = true;
+                       }
+                       appendPQExpBuffer(&buf, "NOT p.proisagg\n");
+               }
+               if (!showTrigger)
+               {
+                       if (have_where)
+                               appendPQExpBuffer(&buf, "      AND ");
+                       else
+                       {
+                               appendPQExpBuffer(&buf, "WHERE ");
+                               have_where = true;
+                       }
+                       appendPQExpBuffer(&buf, "p.prorettype <> 'pg_catalog.trigger'::pg_catalog.regtype\n");
+               }
+               if (!showWindow && pset.sversion >= 80400)
+               {
+                       if (have_where)
+                               appendPQExpBuffer(&buf, "      AND ");
+                       else
+                       {
+                               appendPQExpBuffer(&buf, "WHERE ");
+                               have_where = true;
+                       }
+                       appendPQExpBuffer(&buf, "NOT p.proiswindow\n");
+               }
+       }
+       else
+       {
+               bool            needs_or = false;
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n");
+               appendPQExpBuffer(&buf, "WHERE (\n       ");
+               have_where = true;
+               /* Note: at least one of these must be true ... */
+               if (showAggregate)
+               {
+                       appendPQExpBuffer(&buf, "p.proisagg\n");
+                       needs_or = true;
+               }
+               if (showTrigger)
+               {
+                       if (needs_or)
+                               appendPQExpBuffer(&buf, "       OR ");
+                       appendPQExpBuffer(&buf,
+                               "p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype\n");
+                       needs_or = true;
+               }
+               if (showWindow)
+               {
+                       if (needs_or)
+                               appendPQExpBuffer(&buf, "       OR ");
+                       appendPQExpBuffer(&buf, "p.proiswindow\n");
+                       needs_or = true;
+               }
+               appendPQExpBuffer(&buf, "      )\n");
+       }
 
-       processSQLNamePattern(pset.db, &buf, pattern, true, false,
+       processSQLNamePattern(pset.db, &buf, pattern, have_where, false,
                                                  "n.nspname", "p.proname", NULL,
                                                  "pg_catalog.pg_function_is_visible(p.oid)");
 
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+                                                 "      AND n.nspname <> 'information_schema'\n");
+
        appendPQExpBuffer(&buf, "ORDER BY 1, 2, 4;");
 
        res = PSQLexec(buf.data, false);
@@ -298,6 +434,7 @@ describeFunctions(const char *pattern, bool verbose, bool showSystem)
        myopt.nullPrint = NULL;
        myopt.title = _("List of functions");
        myopt.translate_header = true;
+       myopt.translate_columns = translate_columns;
 
        printQuery(res, &myopt, pset.queryFout, pset.logfile);
 
@@ -337,20 +474,30 @@ describeTypes(const char *pattern, bool verbose, bool showSystem)
                                                  gettext_noop("Internal name"),
                                                  gettext_noop("Size"));
        if (verbose && pset.sversion >= 80300)
+       {
                appendPQExpBuffer(&buf,
                                                  "  pg_catalog.array_to_string(\n"
                                                  "      ARRAY(\n"
                                                  "                  SELECT e.enumlabel\n"
                                                  "          FROM pg_catalog.pg_enum e\n"
-                                                 "          WHERE e.enumtypid = t.oid\n"
-                                                 "          ORDER BY e.oid\n"
+                                                 "          WHERE e.enumtypid = t.oid\n");
+
+               if (pset.sversion >= 90100)
+                       appendPQExpBuffer(&buf,
+                                                         "          ORDER BY e.enumsortorder\n");
+               else
+                       appendPQExpBuffer(&buf,
+                                                         "          ORDER BY e.oid\n");
+
+               appendPQExpBuffer(&buf,
                                                  "      ),\n"
                                                  "      E'\\n'\n"
                                                  "  ) AS \"%s\",\n",
                                                  gettext_noop("Elements"));
+       }
 
        appendPQExpBuffer(&buf,
-                                         "  pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
+                               "  pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
                                          gettext_noop("Description"));
 
        appendPQExpBuffer(&buf, "FROM pg_catalog.pg_type t\n"
@@ -363,6 +510,7 @@ describeTypes(const char *pattern, bool verbose, bool showSystem)
        appendPQExpBuffer(&buf, "WHERE (t.typrelid = 0 ");
        appendPQExpBuffer(&buf, "OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c "
                                          "WHERE c.oid = t.typrelid))\n");
+
        /*
         * do not include array types (before 8.3 we have to use the assumption
         * that their names start with underscore)
@@ -372,8 +520,9 @@ describeTypes(const char *pattern, bool verbose, bool showSystem)
        else
                appendPQExpBuffer(&buf, "  AND t.typname !~ '^_'\n");
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n");
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+                                                 "      AND n.nspname <> 'information_schema'\n");
 
        /* Match name pattern against either internal or external name */
        processSQLNamePattern(pset.db, &buf, pattern, true, false,
@@ -410,6 +559,19 @@ describeOperators(const char *pattern, bool showSystem)
 
        initPQExpBuffer(&buf);
 
+       /*
+        * Note: before Postgres 9.1, we did not assign comments to any built-in
+        * operators, preferring to let the comment on the underlying function
+        * suffice.  The coalesce() on the obj_description() calls below supports
+        * this convention by providing a fallback lookup of a comment on the
+        * operator's function.  As of 9.1 there is a policy that every built-in
+        * operator should have a comment; so the coalesce() is no longer
+        * necessary so far as built-in operators are concerned.  We keep it
+        * anyway, for now, because (1) third-party modules may still be following
+        * the old convention, and (2) we'd need to do it anyway when talking to a
+        * pre-9.1 server.
+        */
+
        printfPQExpBuffer(&buf,
                                          "SELECT n.nspname as \"%s\",\n"
                                          "  o.oprname AS \"%s\",\n"
@@ -427,10 +589,11 @@ describeOperators(const char *pattern, bool showSystem)
                                          gettext_noop("Result type"),
                                          gettext_noop("Description"));
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      WHERE n.nspname <> 'pg_catalog'\n");
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
+                                                 "      AND n.nspname <> 'information_schema'\n");
 
-       processSQLNamePattern(pset.db, &buf, pattern, !showSystem, true,
+       processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, true,
                                                  "n.nspname", "o.oprname", NULL,
                                                  "pg_catalog.pg_operator_is_visible(o.oid)");
 
@@ -468,8 +631,8 @@ listAllDbs(bool verbose)
 
        printfPQExpBuffer(&buf,
                                          "SELECT d.datname as \"%s\",\n"
-                                         "       pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n"
-                                         "       pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n",
+                                  "       pg_catalog.pg_get_userbyid(d.datdba) as \"%s\",\n"
+                       "       pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\",\n",
                                          gettext_noop("Name"),
                                          gettext_noop("Owner"),
                                          gettext_noop("Encoding"));
@@ -477,7 +640,7 @@ listAllDbs(bool verbose)
                appendPQExpBuffer(&buf,
                                                  "       d.datcollate as \"%s\",\n"
                                                  "       d.datctype as \"%s\",\n",
-                                                 gettext_noop("Collation"),
+                                                 gettext_noop("Collate"),
                                                  gettext_noop("Ctype"));
        appendPQExpBuffer(&buf, "       ");
        printACLColumn(&buf, "d.datacl");
@@ -493,7 +656,7 @@ listAllDbs(bool verbose)
                                                  ",\n       t.spcname as \"%s\"",
                                                  gettext_noop("Tablespace"));
        if (verbose && pset.sversion >= 80200)
-                   appendPQExpBuffer(&buf,
+               appendPQExpBuffer(&buf,
                                                  ",\n       pg_catalog.shobj_description(d.oid, 'pg_database') as \"%s\"",
                                                  gettext_noop("Description"));
        appendPQExpBuffer(&buf,
@@ -538,11 +701,12 @@ permissionsList(const char *pattern)
        printfPQExpBuffer(&buf,
                                          "SELECT n.nspname as \"%s\",\n"
                                          "  c.relname as \"%s\",\n"
-                                         "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' END as \"%s\",\n"
+                                         "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
                                          "  ",
                                          gettext_noop("Schema"),
                                          gettext_noop("Name"),
-                                         gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"),
+          gettext_noop("table"), gettext_noop("view"), gettext_noop("sequence"),
+                                         gettext_noop("foreign table"),
                                          gettext_noop("Type"));
 
        printACLColumn(&buf, "c.relacl");
@@ -558,7 +722,7 @@ permissionsList(const char *pattern)
 
        appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_class c\n"
           "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
-                                         "WHERE c.relkind IN ('r', 'v', 'S')\n");
+                                         "WHERE c.relkind IN ('r', 'v', 'S', 'f')\n");
 
        /*
         * Unless a schema pattern is specified, we suppress system and temp
@@ -593,6 +757,73 @@ permissionsList(const char *pattern)
 }
 
 
+/*
+ * \ddp
+ *
+ * List DefaultACLs.  The pattern can match either schema or role name.
+ */
+bool
+listDefaultACLs(const char *pattern)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       printQueryOpt myopt = pset.popt;
+       static const bool translate_columns[] = {false, false, true, false};
+
+       if (pset.sversion < 90000)
+       {
+               fprintf(stderr, _("The server (version %d.%d) does not support altering default privileges.\n"),
+                               pset.sversion / 10000, (pset.sversion / 100) % 100);
+               return true;
+       }
+
+       initPQExpBuffer(&buf);
+
+       printfPQExpBuffer(&buf,
+                          "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
+                                         "  n.nspname AS \"%s\",\n"
+                                         "  CASE d.defaclobjtype WHEN 'r' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END AS \"%s\",\n"
+                                         "  ",
+                                         gettext_noop("Owner"),
+                                         gettext_noop("Schema"),
+                                         gettext_noop("table"),
+                                         gettext_noop("sequence"),
+                                         gettext_noop("function"),
+                                         gettext_noop("Type"));
+
+       printACLColumn(&buf, "d.defaclacl");
+
+       appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
+                                         "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
+
+       processSQLNamePattern(pset.db, &buf, pattern, false, false,
+                                                 NULL,
+                                                 "n.nspname",
+                                                 "pg_catalog.pg_get_userbyid(d.defaclrole)",
+                                                 NULL);
+
+       appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
+
+       res = PSQLexec(buf.data, false);
+       if (!res)
+       {
+               termPQExpBuffer(&buf);
+               return false;
+       }
+
+       myopt.nullPrint = NULL;
+       printfPQExpBuffer(&buf, _("Default access privileges"));
+       myopt.title = buf.data;
+       myopt.translate_header = true;
+       myopt.translate_columns = translate_columns;
+
+       printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+       termPQExpBuffer(&buf);
+       PQclear(res);
+       return true;
+}
+
 
 /*
  * Get object comments
@@ -631,14 +862,15 @@ objectDescription(const char *pattern, bool showSystem)
                                          "  WHERE p.proisagg\n",
                                          gettext_noop("aggregate"));
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\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", "p.proname", NULL,
                                                  "pg_catalog.pg_function_is_visible(p.oid)");
 
-       /* Function descriptions (except in/outs for datatypes) */
+       /* Function descriptions */
        appendPQExpBuffer(&buf,
                                          "UNION ALL\n"
                                          "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
@@ -647,21 +879,18 @@ objectDescription(const char *pattern, bool showSystem)
                                          "  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.prorettype <> 'pg_catalog.cstring'::pg_catalog.regtype\n"
-                                         "      AND (p.proargtypes[0] IS NULL\n"
-                                         "      OR   p.proargtypes[0] <> 'pg_catalog.cstring'::pg_catalog.regtype)\n"
-                                         "      AND NOT p.proisagg\n",
+                                         "  WHERE NOT p.proisagg\n",
                                          gettext_noop("function"));
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\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", "p.proname", NULL,
                                                  "pg_catalog.pg_function_is_visible(p.oid)");
 
-       /* Operator descriptions (only if operator has its own comment) */
+       /* Operator descriptions */
        appendPQExpBuffer(&buf,
                                          "UNION ALL\n"
                                          "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
@@ -672,14 +901,15 @@ objectDescription(const char *pattern, bool showSystem)
        "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
                                          gettext_noop("operator"));
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      WHERE n.nspname <> 'pg_catalog'\n");
-       processSQLNamePattern(pset.db, &buf, pattern, !showSystem, false,
+       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 description */
+       /* Type descriptions */
        appendPQExpBuffer(&buf,
                                          "UNION ALL\n"
                                          "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
@@ -690,10 +920,11 @@ objectDescription(const char *pattern, bool showSystem)
        "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
                                          gettext_noop("data type"));
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      WHERE n.nspname <> 'pg_catalog'\n");
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
+                                                 "      AND n.nspname <> 'information_schema'\n");
 
-       processSQLNamePattern(pset.db, &buf, pattern, !showSystem, false,
+       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)");
@@ -705,23 +936,26 @@ objectDescription(const char *pattern, bool showSystem)
                                          "  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' END"
+                                         "    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')\n",
+                                         "  WHERE c.relkind IN ('r', 'v', 'i', 'S', 'f')\n",
                                          gettext_noop("table"),
                                          gettext_noop("view"),
                                          gettext_noop("index"),
-                                         gettext_noop("sequence"));
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n");
+                                         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 description (ignore rules for views) */
+       /* Rule descriptions (ignore rules for views) */
        appendPQExpBuffer(&buf,
                                          "UNION ALL\n"
                                          "  SELECT r.oid as oid, r.tableoid as tableoid,\n"
@@ -734,15 +968,16 @@ objectDescription(const char *pattern, bool showSystem)
                                          "  WHERE r.rulename != '_RETURN'\n",
                                          gettext_noop("rule"));
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n");
+       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", "r.rulename", NULL,
                                                  "pg_catalog.pg_table_is_visible(c.oid)");
 
-       /* Trigger description */
+       /* Trigger descriptions */
        appendPQExpBuffer(&buf,
                                          "UNION ALL\n"
                                          "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
@@ -753,11 +988,13 @@ objectDescription(const char *pattern, bool showSystem)
                                   "       JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n"
        "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n",
                                          gettext_noop("trigger"));
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      WHERE n.nspname <> 'pg_catalog'\n");
+
+       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, false,
+       processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
                                                  "n.nspname", "t.tgname", NULL,
                                                  "pg_catalog.pg_table_is_visible(c.oid)");
 
@@ -808,10 +1045,11 @@ describeTableDetails(const char *pattern, bool verbose, bool showSystem)
                                          "FROM pg_catalog.pg_class c\n"
         "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      WHERE n.nspname <> 'pg_catalog'\n");
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
+                                                 "      AND n.nspname <> 'information_schema'\n");
 
-       processSQLNamePattern(pset.db, &buf, pattern, !showSystem, false,
+       processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern, false,
                                                  "n.nspname", "c.relname", NULL,
                                                  "pg_catalog.pg_table_is_visible(c.oid)");
 
@@ -874,7 +1112,7 @@ describeOneTableDetails(const char *schemaname,
        PGresult   *res = NULL;
        printTableOpt myopt = pset.popt.topt;
        printTableContent cont;
-       bool printTableInitialized = false;
+       bool            printTableInitialized = false;
        int                     i;
        char       *view_def = NULL;
        char       *headers[6];
@@ -883,7 +1121,7 @@ describeOneTableDetails(const char *schemaname,
        char      **ptr;
        PQExpBufferData title;
        PQExpBufferData tmpbuf;
-       int                     cols = 0;
+       int                     cols;
        int                     numrows = 0;
        struct
        {
@@ -895,6 +1133,8 @@ describeOneTableDetails(const char *schemaname,
                bool            hasoids;
                Oid                     tablespace;
                char       *reloptions;
+               char       *reloftype;
+               char            relpersistence;
        }                       tableinfo;
        bool            show_modifiers = false;
        bool            retval;
@@ -909,48 +1149,81 @@ describeOneTableDetails(const char *schemaname,
        initPQExpBuffer(&tmpbuf);
 
        /* Get general table info */
-       if (pset.sversion >= 80400)
+       if (pset.sversion >= 90100)
+       {
+               printfPQExpBuffer(&buf,
+                         "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
+                                                 "c.relhastriggers, c.relhasoids, "
+                                                 "%s, c.reltablespace, "
+                                                 "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
+                                                 "c.relpersistence\n"
+                                                 "FROM pg_catalog.pg_class c\n "
+                  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
+                                                 "WHERE c.oid = '%s';",
+                                                 (verbose ?
+                                                  "pg_catalog.array_to_string(c.reloptions || "
+                                                  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
+                                                  : "''"),
+                                                 oid);
+       }
+       else if (pset.sversion >= 90000)
+       {
+               printfPQExpBuffer(&buf,
+                         "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
+                                                 "c.relhastriggers, c.relhasoids, "
+                                                 "%s, c.reltablespace, "
+                                                 "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n"
+                                                 "FROM pg_catalog.pg_class c\n "
+                  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
+                                                 "WHERE c.oid = '%s';",
+                                                 (verbose ?
+                                                  "pg_catalog.array_to_string(c.reloptions || "
+                                                  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
+                                                  : "''"),
+                                                 oid);
+       }
+       else if (pset.sversion >= 80400)
        {
                printfPQExpBuffer(&buf,
-                                                 "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
+                         "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
                                                  "c.relhastriggers, c.relhasoids, "
                                                  "%s, c.reltablespace\n"
                                                  "FROM pg_catalog.pg_class c\n "
-                                                 "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
-                                                 "WHERE c.oid = '%s'\n",
+                  "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
+                                                 "WHERE c.oid = '%s';",
                                                  (verbose ?
-                                                 "pg_catalog.array_to_string(c.reloptions || "
-                                                 "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
-                                                 : "''"),
+                                                  "pg_catalog.array_to_string(c.reloptions || "
+                                                  "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
+                                                  : "''"),
                                                  oid);
        }
        else if (pset.sversion >= 80200)
        {
                printfPQExpBuffer(&buf,
-                                                 "SELECT relchecks, relkind, relhasindex, relhasrules, "
+                                         "SELECT relchecks, relkind, relhasindex, relhasrules, "
                                                  "reltriggers <> 0, relhasoids, "
                                                  "%s, reltablespace\n"
-                                                 "FROM pg_catalog.pg_class WHERE oid = '%s'",
+                                                 "FROM pg_catalog.pg_class WHERE oid = '%s';",
                                                  (verbose ?
-                                                  "pg_catalog.array_to_string(reloptions, E', ')" : ",''"),
+                                        "pg_catalog.array_to_string(reloptions, E', ')" : "''"),
                                                  oid);
        }
        else if (pset.sversion >= 80000)
        {
                printfPQExpBuffer(&buf,
-                                                 "SELECT relchecks, relkind, relhasindex, relhasrules, "
+                                         "SELECT relchecks, relkind, relhasindex, relhasrules, "
                                                  "reltriggers <> 0, relhasoids, "
                                                  "'', reltablespace\n"
-                                                 "FROM pg_catalog.pg_class WHERE oid = '%s'",
+                                                 "FROM pg_catalog.pg_class WHERE oid = '%s';",
                                                  oid);
        }
        else
        {
                printfPQExpBuffer(&buf,
-                                                 "SELECT relchecks, relkind, relhasindex, relhasrules, "
+                                         "SELECT relchecks, relkind, relhasindex, relhasrules, "
                                                  "reltriggers <> 0, relhasoids, "
                                                  "'', ''\n"
-                                                 "FROM pg_catalog.pg_class WHERE oid = '%s'",
+                                                 "FROM pg_catalog.pg_class WHERE oid = '%s';",
                                                  oid);
        }
 
@@ -973,60 +1246,81 @@ describeOneTableDetails(const char *schemaname,
        tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
        tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
        tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
-       tableinfo.reloptions = pset.sversion >= 80200 ?
-                                                       strdup(PQgetvalue(res, 0, 6)) : 0;
+       tableinfo.reloptions = (pset.sversion >= 80200) ?
+               strdup(PQgetvalue(res, 0, 6)) : 0;
        tableinfo.tablespace = (pset.sversion >= 80000) ?
-                                                               atooid(PQgetvalue(res, 0, 7)) : 0;
+               atooid(PQgetvalue(res, 0, 7)) : 0;
+       tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 8), "") != 0) ?
+               strdup(PQgetvalue(res, 0, 8)) : 0;
+       tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
+               PQgetvalue(res, 0, 9)[0] : 0;
        PQclear(res);
        res = NULL;
-       
+
        /*
-        * If it's a sequence, fetch its values and store into an
-        * array that will be used later.
+        * If it's a sequence, fetch its values and store into an array that will
+        * be used later.
         */
        if (tableinfo.relkind == 'S')
        {
-               PGresult   *result;
-               
-#define SEQ_NUM_COLS 10
-               printfPQExpBuffer(&buf,
-                                                 "SELECT sequence_name, last_value,\n"
-                                                 "       start_value, increment_by,\n"
-                                                 "       max_value, min_value, cache_value,\n"
-                                                 "       log_cnt, is_cycled, is_called\n"
-                                                 "FROM %s",
-                                                 fmtId(schemaname));
+               printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname));
                /* must be separate because fmtId isn't reentrant */
-               appendPQExpBuffer(&buf, ".%s", fmtId(relationname));
-               
-               result = PSQLexec(buf.data, false);
-               if (!result)
+               appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
+
+               res = PSQLexec(buf.data, false);
+               if (!res)
                        goto error_return;
-               
-               seq_values = pg_malloc_zero((SEQ_NUM_COLS+1) * sizeof(*seq_values));
-               
-               for (i = 0; i < SEQ_NUM_COLS; i++) 
-                       seq_values[i] = pg_strdup(PQgetvalue(result, 0, i));
-               
-               PQclear(result);
+
+               seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values));
+
+               for (i = 0; i < PQnfields(res); i++)
+                       seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
+               seq_values[i] = NULL;
+
+               PQclear(res);
+               res = NULL;
        }
 
-       /* Get column info (index requires additional checks) */
+       /*
+        * Get column info
+        *
+        * You need to modify value of "firstvcol" which willbe defined below if
+        * you are adding column(s) preceding to verbose-only columns.
+        */
        printfPQExpBuffer(&buf, "SELECT a.attname,");
        appendPQExpBuffer(&buf, "\n  pg_catalog.format_type(a.atttypid, a.atttypmod),"
                                          "\n  (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
                                          "\n   FROM pg_catalog.pg_attrdef d"
                                          "\n   WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),"
-                                         "\n  a.attnotnull, a.attnum");
+                                         "\n  a.attnotnull, a.attnum,");
+       if (pset.sversion >= 90100)
+               appendPQExpBuffer(&buf, "\n  (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t\n"
+                                                 "   WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) AS attcollation");
+       else
+               appendPQExpBuffer(&buf, "\n  NULL AS attcollation");
+       if (tableinfo.relkind == 'i')
+               appendPQExpBuffer(&buf, ",\n  pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
+       else
+               appendPQExpBuffer(&buf, ",\n  NULL AS indexdef");
+       if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
+               appendPQExpBuffer(&buf, ",\n  a.attfdwoptions");
+       else
+               appendPQExpBuffer(&buf, ",\n  NULL AS attfdwoptions");
        if (verbose)
-               appendPQExpBuffer(&buf, ", a.attstorage, pg_catalog.col_description(a.attrelid, a.attnum)");
+       {
+               appendPQExpBuffer(&buf, ",\n  a.attstorage");
+               /*
+                * In 9.0+, we have column comments for: relations, views, composite
+                * types, and foreign tables (c.f. CommentObject() in comment.c).
+                */
+               if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
+                       tableinfo.relkind == 'f' || tableinfo.relkind == 'c')
+                       appendPQExpBuffer(&buf, ", pg_catalog.col_description(a.attrelid, a.attnum)");
+       }
+
        appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_attribute a");
-       if (tableinfo.relkind == 'i')
-               appendPQExpBuffer(&buf, ", pg_catalog.pg_index i");
        appendPQExpBuffer(&buf, "\nWHERE a.attrelid = '%s' AND a.attnum > 0 AND NOT a.attisdropped", oid);
-       if (tableinfo.relkind == 'i')
-               appendPQExpBuffer(&buf, " AND a.attrelid = i.indexrelid");
-       appendPQExpBuffer(&buf, "\nORDER BY a.attnum");
+       appendPQExpBuffer(&buf, "\nORDER BY a.attnum;");
 
        res = PSQLexec(buf.data, false);
        if (!res)
@@ -1037,8 +1331,12 @@ describeOneTableDetails(const char *schemaname,
        switch (tableinfo.relkind)
        {
                case 'r':
-                       printfPQExpBuffer(&title, _("Table \"%s.%s\""),
-                                                         schemaname, relationname);
+                       if (tableinfo.relpersistence == 'u')
+                               printfPQExpBuffer(&title, _("Unlogged table \"%s.%s\""),
+                                                                 schemaname, relationname);
+                       else
+                               printfPQExpBuffer(&title, _("Table \"%s.%s\""),
+                                                                 schemaname, relationname);
                        break;
                case 'v':
                        printfPQExpBuffer(&title, _("View \"%s.%s\""),
@@ -1049,8 +1347,12 @@ describeOneTableDetails(const char *schemaname,
                                                          schemaname, relationname);
                        break;
                case 'i':
-                       printfPQExpBuffer(&title, _("Index \"%s.%s\""),
-                                                         schemaname, relationname);
+                       if (tableinfo.relpersistence == 'u')
+                               printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
+                                                                 schemaname, relationname);
+                       else
+                               printfPQExpBuffer(&title, _("Index \"%s.%s\""),
+                                                                 schemaname, relationname);
                        break;
                case 's':
                        /* not used as of 8.2, but keep it for backwards compatibility */
@@ -1065,6 +1367,10 @@ describeOneTableDetails(const char *schemaname,
                        printfPQExpBuffer(&title, _("Composite type \"%s.%s\""),
                                                          schemaname, relationname);
                        break;
+               case 'f':
+                       printfPQExpBuffer(&title, _("Foreign table \"%s.%s\""),
+                                                         schemaname, relationname);
+                       break;
                default:
                        /* untranslated unknown relkind */
                        printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
@@ -1073,11 +1379,12 @@ describeOneTableDetails(const char *schemaname,
        }
 
        /* Set the number of columns, and their names */
-       cols += 2;
        headers[0] = gettext_noop("Column");
        headers[1] = gettext_noop("Type");
+       cols = 2;
 
-       if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v')
+       if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
+               tableinfo.relkind == 'f' || tableinfo.relkind == 'c')
        {
                show_modifiers = true;
                headers[cols++] = gettext_noop("Modifiers");
@@ -1086,13 +1393,22 @@ describeOneTableDetails(const char *schemaname,
 
        if (tableinfo.relkind == 'S')
                headers[cols++] = gettext_noop("Value");
-               
+
+       if (tableinfo.relkind == 'i')
+               headers[cols++] = gettext_noop("Definition");
+
+       if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
+               headers[cols++] = gettext_noop("Options");
+
        if (verbose)
        {
                headers[cols++] = gettext_noop("Storage");
-               headers[cols++] = gettext_noop("Description");
+               /* Column comments, if the relkind supports this feature. */
+               if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
+                       tableinfo.relkind == 'c' || tableinfo.relkind == 'f')
+                       headers[cols++] = gettext_noop("Description");
        }
-       
+
        printTableInit(&cont, &myopt, title.data, cols, numrows);
        printTableInitialized = true;
 
@@ -1100,12 +1416,12 @@ describeOneTableDetails(const char *schemaname,
                printTableAddHeader(&cont, headers[i], true, 'l');
 
        /* Check if table is a view */
-       if (tableinfo.relkind == 'v')
+       if (tableinfo.relkind == 'v' && verbose)
        {
                PGresult   *result;
 
                printfPQExpBuffer(&buf,
-                                                 "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true)",
+                         "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid, true);",
                                                  oid);
                result = PSQLexec(buf.data, false);
                if (!result)
@@ -1121,17 +1437,30 @@ describeOneTableDetails(const char *schemaname,
        for (i = 0; i < numrows; i++)
        {
                /* Column */
-               printTableAddCell(&cont, PQgetvalue(res, i, 0), false);
+               printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
 
                /* Type */
-               printTableAddCell(&cont, PQgetvalue(res, i, 1), false);
+               printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
 
-               /* Modifiers: not null and default */
+               /* Modifiers: collate, not null, default */
                if (show_modifiers)
                {
                        resetPQExpBuffer(&tmpbuf);
+
+                       if (!PQgetisnull(res, i, 5))
+                       {
+                               if (tmpbuf.len > 0)
+                                       appendPQExpBufferStr(&tmpbuf, " ");
+                               appendPQExpBuffer(&tmpbuf, _("collate %s"),
+                                                                 PQgetvalue(res, i, 5));
+                       }
+
                        if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
+                       {
+                               if (tmpbuf.len > 0)
+                                       appendPQExpBufferStr(&tmpbuf, " ");
                                appendPQExpBufferStr(&tmpbuf, _("not null"));
+                       }
 
                        /* handle "default" here */
                        /* (note: above we cut off the 'default' string at 128) */
@@ -1145,26 +1474,39 @@ describeOneTableDetails(const char *schemaname,
                        }
 
                        modifiers[i] = pg_strdup(tmpbuf.data);
-                       printTableAddCell(&cont, modifiers[i], false);
+                       printTableAddCell(&cont, modifiers[i], false, false);
                }
 
                /* Value: for sequences only */
                if (tableinfo.relkind == 'S')
-                       printTableAddCell(&cont, seq_values[i], false);
+                       printTableAddCell(&cont, seq_values[i], false, false);
+
+               /* Expression for index column */
+               if (tableinfo.relkind == 'i')
+                       printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
+
+               /* FDW options for foreign table column, only for 9.2 or later */
+               if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
+                       printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
 
                /* Storage and Description */
                if (verbose)
                {
-                       char *storage = PQgetvalue(res, i, 5);
+                       int                     firstvcol = 8;
+                       char       *storage = PQgetvalue(res, i, firstvcol);
 
                        /* these strings are literal in our syntax, so not translated. */
-                       printTableAddCell(&cont, (storage[0]=='p' ? "plain" :
-                                                                         (storage[0]=='m' ? "main" :
-                                                                          (storage[0]=='x' ? "extended" :
-                                                                               (storage[0]=='e' ? "external" :
+                       printTableAddCell(&cont, (storage[0] == 'p' ? "plain" :
+                                                                         (storage[0] == 'm' ? "main" :
+                                                                          (storage[0] == 'x' ? "extended" :
+                                                                               (storage[0] == 'e' ? "external" :
                                                                                 "???")))),
-                                                         false);
-                       printTableAddCell(&cont, PQgetvalue(res, i, 6), false);
+                                                         false, false);
+                       /* Column comments, if the relkind supports this feature. */
+                       if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v' ||
+                               tableinfo.relkind == 'c' || tableinfo.relkind == 'f')
+                               printTableAddCell(&cont, PQgetvalue(res, i, firstvcol + 1),
+                                                                 false, false);
                }
        }
 
@@ -1175,16 +1517,33 @@ describeOneTableDetails(const char *schemaname,
                PGresult   *result;
 
                printfPQExpBuffer(&buf,
-                                                 "SELECT i.indisunique, i.indisprimary, i.indisclustered, ");
+                                "SELECT i.indisunique, i.indisprimary, i.indisclustered, ");
                if (pset.sversion >= 80200)
-                       appendPQExpBuffer(&buf, "i.indisvalid, ");
+                       appendPQExpBuffer(&buf, "i.indisvalid,\n");
+               else
+                       appendPQExpBuffer(&buf, "true AS indisvalid,\n");
+               if (pset.sversion >= 90000)
+                       appendPQExpBuffer(&buf,
+                                                         "  (NOT i.indimmediate) AND "
+                                                       "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
+                                                         "WHERE conrelid = i.indrelid AND "
+                                                         "conindid = i.indexrelid AND "
+                                                         "contype IN ('p','u','x') AND "
+                                                         "condeferrable) AS condeferrable,\n"
+                                                         "  (NOT i.indimmediate) AND "
+                                                       "EXISTS (SELECT 1 FROM pg_catalog.pg_constraint "
+                                                         "WHERE conrelid = i.indrelid AND "
+                                                         "conindid = i.indexrelid AND "
+                                                         "contype IN ('p','u','x') AND "
+                                                         "condeferred) AS condeferred,\n");
                else
-                       appendPQExpBuffer(&buf, "true as indisvalid, ");
-               appendPQExpBuffer(&buf, "a.amname, c2.relname,\n"
-                                       "  pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
+                       appendPQExpBuffer(&buf,
+                                               "  false AS condeferrable, false AS condeferred,\n");
+               appendPQExpBuffer(&buf, "  a.amname, c2.relname, "
+                                         "pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
                                                  "FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
                  "WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
-                                                 "AND i.indrelid = c2.oid",
+                                                 "AND i.indrelid = c2.oid;",
                                                  oid);
 
                result = PSQLexec(buf.data, false);
@@ -1201,9 +1560,11 @@ describeOneTableDetails(const char *schemaname,
                        char       *indisprimary = PQgetvalue(result, 0, 1);
                        char       *indisclustered = PQgetvalue(result, 0, 2);
                        char       *indisvalid = PQgetvalue(result, 0, 3);
-                       char       *indamname = PQgetvalue(result, 0, 4);
-                       char       *indtable = PQgetvalue(result, 0, 5);
-                       char       *indpred = PQgetvalue(result, 0, 6);
+                       char       *deferrable = PQgetvalue(result, 0, 4);
+                       char       *deferred = PQgetvalue(result, 0, 5);
+                       char       *indamname = PQgetvalue(result, 0, 6);
+                       char       *indtable = PQgetvalue(result, 0, 7);
+                       char       *indpred = PQgetvalue(result, 0, 8);
 
                        if (strcmp(indisprimary, "t") == 0)
                                printfPQExpBuffer(&tmpbuf, _("primary key, "));
@@ -1226,6 +1587,12 @@ describeOneTableDetails(const char *schemaname,
                        if (strcmp(indisvalid, "t") != 0)
                                appendPQExpBuffer(&tmpbuf, _(", invalid"));
 
+                       if (strcmp(deferrable, "t") == 0)
+                               appendPQExpBuffer(&tmpbuf, _(", deferrable"));
+
+                       if (strcmp(deferred, "t") == 0)
+                               appendPQExpBuffer(&tmpbuf, _(", initially deferred"));
+
                        printTableAddFooter(&cont, tmpbuf.data);
                        add_tablespace_footer(&cont, tableinfo.relkind,
                                                                  tableinfo.tablespace, true);
@@ -1247,7 +1614,7 @@ describeOneTableDetails(const char *schemaname,
                        printfPQExpBuffer(&buf,
                                                          "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true))\n"
                                                          "FROM pg_catalog.pg_rewrite r\n"
-                       "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1",
+                       "WHERE r.ev_class = '%s' AND r.rulename != '_RETURN' ORDER BY 1;",
                                                          oid);
                        result = PSQLexec(buf.data, false);
                        if (!result)
@@ -1271,7 +1638,7 @@ describeOneTableDetails(const char *schemaname,
                        PQclear(result);
                }
        }
-       else if (tableinfo.relkind == 'r')
+       else if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
        {
                /* Footer information about a table */
                PGresult   *result = NULL;
@@ -1282,17 +1649,29 @@ describeOneTableDetails(const char *schemaname,
                {
                        printfPQExpBuffer(&buf,
                                                          "SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, ");
-            if (pset.sversion >= 80200)
+                       if (pset.sversion >= 80200)
                                appendPQExpBuffer(&buf, "i.indisvalid, ");
-            else
+                       else
                                appendPQExpBuffer(&buf, "true as indisvalid, ");
-                       appendPQExpBuffer(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)");
-            if (pset.sversion >= 80000)
+                       appendPQExpBuffer(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true),\n  ");
+                       if (pset.sversion >= 90000)
+                               appendPQExpBuffer(&buf,
+                                                  "pg_catalog.pg_get_constraintdef(con.oid, true), "
+                                                                 "contype, condeferrable, condeferred");
+                       else
+                               appendPQExpBuffer(&buf,
+                                                                 "null AS constraintdef, null AS contype, "
+                                                        "false AS condeferrable, false AS condeferred");
+                       if (pset.sversion >= 80000)
                                appendPQExpBuffer(&buf, ", c2.reltablespace");
                        appendPQExpBuffer(&buf,
-                                                         "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
+                                                         "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n");
+                       if (pset.sversion >= 90000)
+                               appendPQExpBuffer(&buf,
+                                                                 "  LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n");
+                       appendPQExpBuffer(&buf,
                                                          "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
-                         "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname",
+                         "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname;",
                                                          oid);
                        result = PSQLexec(buf.data, false);
                        if (!result)
@@ -1305,28 +1684,48 @@ describeOneTableDetails(const char *schemaname,
                                printTableAddFooter(&cont, _("Indexes:"));
                                for (i = 0; i < tuples; i++)
                                {
-                                       const char *indexdef;
-                                       const char *usingpos;
-
                                        /* untranslated index name */
                                        printfPQExpBuffer(&buf, "    \"%s\"",
                                                                          PQgetvalue(result, i, 0));
 
-                                       /* Label as primary key or unique (but not both) */
-                                       appendPQExpBuffer(&buf,
-                                                                         strcmp(PQgetvalue(result, i, 1), "t") == 0
-                                                                         ? " PRIMARY KEY," :
-                                                                         (strcmp(PQgetvalue(result, i, 2), "t") == 0
-                                                                          ? " UNIQUE,"
-                                                                          : ""));
-                                       /* Everything after "USING" is echoed verbatim */
-                                       indexdef = PQgetvalue(result, i, 5);
-                                       usingpos = strstr(indexdef, " USING ");
-                                       if (usingpos)
-                                               indexdef = usingpos + 7;
+                                       /* If exclusion constraint, print the constraintdef */
+                                       if (strcmp(PQgetvalue(result, i, 7), "x") == 0)
+                                       {
+                                               appendPQExpBuffer(&buf, " %s",
+                                                                                 PQgetvalue(result, i, 6));
+                                       }
+                                       else
+                                       {
+                                               const char *indexdef;
+                                               const char *usingpos;
+
+                                               /* Label as primary key or unique (but not both) */
+                                               if (strcmp(PQgetvalue(result, i, 1), "t") == 0)
+                                                       appendPQExpBuffer(&buf, " PRIMARY KEY,");
+                                               else if (strcmp(PQgetvalue(result, i, 2), "t") == 0)
+                                               {
+                                                       if (strcmp(PQgetvalue(result, i, 7), "u") == 0)
+                                                               appendPQExpBuffer(&buf, " UNIQUE CONSTRAINT,");
+                                                       else
+                                                               appendPQExpBuffer(&buf, " UNIQUE,");
+                                               }
+
+                                               /* Everything after "USING" is echoed verbatim */
+                                               indexdef = PQgetvalue(result, i, 5);
+                                               usingpos = strstr(indexdef, " USING ");
+                                               if (usingpos)
+                                                       indexdef = usingpos + 7;
+                                               appendPQExpBuffer(&buf, " %s", indexdef);
+
+                                               /* Need these for deferrable PK/UNIQUE indexes */
+                                               if (strcmp(PQgetvalue(result, i, 8), "t") == 0)
+                                                       appendPQExpBuffer(&buf, " DEFERRABLE");
 
-                                       appendPQExpBuffer(&buf, " %s", indexdef);
+                                               if (strcmp(PQgetvalue(result, i, 9), "t") == 0)
+                                                       appendPQExpBuffer(&buf, " INITIALLY DEFERRED");
+                                       }
 
+                                       /* Add these for all cases */
                                        if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
                                                appendPQExpBuffer(&buf, " CLUSTER");
 
@@ -1337,8 +1736,8 @@ describeOneTableDetails(const char *schemaname,
 
                                        /* Print tablespace of the index on the same line */
                                        if (pset.sversion >= 80000)
-                                           add_tablespace_footer(&cont, 'i',
-                                                                                         atooid(PQgetvalue(result, i, 6)),
+                                               add_tablespace_footer(&cont, 'i',
+                                                                                  atooid(PQgetvalue(result, i, 10)),
                                                                                          false);
                                }
                        }
@@ -1352,7 +1751,7 @@ describeOneTableDetails(const char *schemaname,
                                                          "SELECT r.conname, "
                                                          "pg_catalog.pg_get_constraintdef(r.oid, true)\n"
                                                          "FROM pg_catalog.pg_constraint r\n"
-                                       "WHERE r.conrelid = '%s' AND r.contype = 'c'\nORDER BY 1",
+                                  "WHERE r.conrelid = '%s' AND r.contype = 'c'\nORDER BY 1;",
                                                          oid);
                        result = PSQLexec(buf.data, false);
                        if (!result)
@@ -1381,9 +1780,9 @@ describeOneTableDetails(const char *schemaname,
                {
                        printfPQExpBuffer(&buf,
                                                          "SELECT conname,\n"
-                                  "  pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n"
+                                "  pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n"
                                                          "FROM pg_catalog.pg_constraint r\n"
-                                       "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1",
+                                       "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1;",
                                                          oid);
                        result = PSQLexec(buf.data, false);
                        if (!result)
@@ -1411,10 +1810,10 @@ describeOneTableDetails(const char *schemaname,
                if (tableinfo.hastriggers)
                {
                        printfPQExpBuffer(&buf,
-                                                         "SELECT conname, conrelid::pg_catalog.regclass,\n"
-                                                         "  pg_catalog.pg_get_constraintdef(c.oid, true) as condef\n"
+                                                  "SELECT conname, conrelid::pg_catalog.regclass,\n"
+                                "  pg_catalog.pg_get_constraintdef(c.oid, true) as condef\n"
                                                          "FROM pg_catalog.pg_constraint c\n"
-                                                         "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1",
+                                  "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1;",
                                                          oid);
                        result = PSQLexec(buf.data, false);
                        if (!result)
@@ -1427,11 +1826,9 @@ describeOneTableDetails(const char *schemaname,
                                printTableAddFooter(&cont, _("Referenced by:"));
                                for (i = 0; i < tuples; i++)
                                {
-                                       /* translator: the first %s is a FK name, the following are
-                                        * a table name and the FK definition */
-                                       printfPQExpBuffer(&buf, _("  \"%s\" IN %s %s"),
-                                                                         PQgetvalue(result, i, 0),
+                                       printfPQExpBuffer(&buf, "    TABLE \"%s\" CONSTRAINT \"%s\" %s",
                                                                          PQgetvalue(result, i, 1),
+                                                                         PQgetvalue(result, i, 0),
                                                                          PQgetvalue(result, i, 2));
 
                                        printTableAddFooter(&cont, buf.data);
@@ -1449,7 +1846,7 @@ describeOneTableDetails(const char *schemaname,
                                                                  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
                                                                  "ev_enabled\n"
                                                                  "FROM pg_catalog.pg_rewrite r\n"
-                                                                 "WHERE r.ev_class = '%s' ORDER BY 1",
+                                                                 "WHERE r.ev_class = '%s' ORDER BY 1;",
                                                                  oid);
                        }
                        else
@@ -1458,7 +1855,7 @@ describeOneTableDetails(const char *schemaname,
                                                                  "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
                                                                  "'O'::char AS ev_enabled\n"
                                                                  "FROM pg_catalog.pg_rewrite r\n"
-                                                                 "WHERE r.ev_class = '%s' ORDER BY 1",
+                                                                 "WHERE r.ev_class = '%s' ORDER BY 1;",
                                                                  oid);
                        }
                        result = PSQLexec(buf.data, false);
@@ -1534,118 +1931,163 @@ describeOneTableDetails(const char *schemaname,
                        }
                        PQclear(result);
                }
+       }
 
-               /* print triggers (but ignore foreign-key triggers) */
-               if (tableinfo.hastriggers)
-               {
-                       printfPQExpBuffer(&buf,
-                                                         "SELECT t.tgname, "
-                                                         "pg_catalog.pg_get_triggerdef(t.oid), "
-                                                         "t.tgenabled\n"
-                                                         "FROM pg_catalog.pg_trigger t\n"
-                                                         "WHERE t.tgrelid = '%s' AND ",
-                                                         oid);
-                       if (pset.sversion >= 80300)
-                               appendPQExpBuffer(&buf, "t.tgconstraint = 0");
-                       else
-                               appendPQExpBuffer(&buf,
-                                                                 "(NOT tgisconstraint "
-                                                                 " OR NOT EXISTS"
-                                                                 "  (SELECT 1 FROM pg_catalog.pg_depend d "
-                                                                 "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
-                                                                 "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))");
-                       appendPQExpBuffer(&buf, "\nORDER BY 1");
+       /*
+        * Print triggers next, if any (but only user-defined triggers).  This
+        * could apply to either a table or a view.
+        */
+       if (tableinfo.hastriggers)
+       {
+               PGresult   *result;
+               int                     tuples;
 
-                       result = PSQLexec(buf.data, false);
-                       if (!result)
-                               goto error_return;
-                       else
-                               tuples = PQntuples(result);
+               printfPQExpBuffer(&buf,
+                                                 "SELECT t.tgname, "
+                                                 "pg_catalog.pg_get_triggerdef(t.oid%s), "
+                                                 "t.tgenabled\n"
+                                                 "FROM pg_catalog.pg_trigger t\n"
+                                                 "WHERE t.tgrelid = '%s' AND ",
+                                                 (pset.sversion >= 90000 ? ", true" : ""),
+                                                 oid);
+               if (pset.sversion >= 90000)
+                       appendPQExpBuffer(&buf, "NOT t.tgisinternal");
+               else if (pset.sversion >= 80300)
+                       appendPQExpBuffer(&buf, "t.tgconstraint = 0");
+               else
+                       appendPQExpBuffer(&buf,
+                                                         "(NOT tgisconstraint "
+                                                         " OR NOT EXISTS"
+                                                         "  (SELECT 1 FROM pg_catalog.pg_depend d "
+                                                         "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
+                                                         "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))");
+               appendPQExpBuffer(&buf, "\nORDER BY 1;");
 
-                       if (tuples > 0)
-                       {
-                               bool            have_heading;
-                               int                     category;
+               result = PSQLexec(buf.data, false);
+               if (!result)
+                       goto error_return;
+               else
+                       tuples = PQntuples(result);
 
-                               /*
-                                * split the output into 4 different categories. Enabled triggers,
-                                * disabled triggers and the two special ALWAYS and REPLICA
-                                * configurations.
-                                */
-                               for (category = 0; category < 4; category++)
+               if (tuples > 0)
+               {
+                       bool            have_heading;
+                       int                     category;
+
+                       /*
+                        * split the output into 4 different categories. Enabled triggers,
+                        * disabled triggers and the two special ALWAYS and REPLICA
+                        * configurations.
+                        */
+                       for (category = 0; category < 4; category++)
+                       {
+                               have_heading = false;
+                               for (i = 0; i < tuples; i++)
                                {
-                                       have_heading = false;
-                                       for (i = 0; i < tuples; i++)
+                                       bool            list_trigger;
+                                       const char *tgdef;
+                                       const char *usingpos;
+                                       const char *tgenabled;
+
+                                       /*
+                                        * Check if this trigger falls into the current category
+                                        */
+                                       tgenabled = PQgetvalue(result, i, 2);
+                                       list_trigger = false;
+                                       switch (category)
                                        {
-                                               bool            list_trigger;
-                                               const char *tgdef;
-                                               const char *usingpos;
-                                               const char *tgenabled;
+                                               case 0:
+                                                       if (*tgenabled == 'O' || *tgenabled == 't')
+                                                               list_trigger = true;
+                                                       break;
+                                               case 1:
+                                                       if (*tgenabled == 'D' || *tgenabled == 'f')
+                                                               list_trigger = true;
+                                                       break;
+                                               case 2:
+                                                       if (*tgenabled == 'A')
+                                                               list_trigger = true;
+                                                       break;
+                                               case 3:
+                                                       if (*tgenabled == 'R')
+                                                               list_trigger = true;
+                                                       break;
+                                       }
+                                       if (list_trigger == false)
+                                               continue;
 
-                                               /* Check if this trigger falls into the current category */
-                                               tgenabled = PQgetvalue(result, i, 2);
-                                               list_trigger = false;
+                                       /* Print the category heading once */
+                                       if (have_heading == false)
+                                       {
                                                switch (category)
                                                {
                                                        case 0:
-                                                               if (*tgenabled == 'O' || *tgenabled == 't')
-                                                                       list_trigger = true;
+                                                               printfPQExpBuffer(&buf, _("Triggers:"));
                                                                break;
                                                        case 1:
-                                                               if (*tgenabled == 'D' || *tgenabled == 'f')
-                                                                       list_trigger = true;
+                                                               printfPQExpBuffer(&buf, _("Disabled triggers:"));
                                                                break;
                                                        case 2:
-                                                               if (*tgenabled == 'A')
-                                                                       list_trigger = true;
+                                                               printfPQExpBuffer(&buf, _("Triggers firing always:"));
                                                                break;
                                                        case 3:
-                                                               if (*tgenabled == 'R')
-                                                                       list_trigger = true;
+                                                               printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
                                                                break;
-                                               }
-                                               if (list_trigger == false)
-                                                       continue;
 
-                                               /* Print the category heading once */
-                                               if (have_heading == false)
-                                               {
-                                                       switch (category)
-                                                       {
-                                                               case 0:
-                                                                       printfPQExpBuffer(&buf, _("Triggers:"));
-                                                                       break;
-                                                               case 1:
-                                                                       printfPQExpBuffer(&buf, _("Disabled triggers:"));
-                                                                       break;
-                                                               case 2:
-                                                                       printfPQExpBuffer(&buf, _("Triggers firing always:"));
-                                                                       break;
-                                                               case 3:
-                                                                       printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
-                                                                       break;
-
-                                                       }
-                                                       printTableAddFooter(&cont, buf.data);
-                                                       have_heading = true;
                                                }
-
-                                               /* Everything after "TRIGGER" is echoed verbatim */
-                                               tgdef = PQgetvalue(result, i, 1);
-                                               usingpos = strstr(tgdef, " TRIGGER ");
-                                               if (usingpos)
-                                                       tgdef = usingpos + 9;
-
-                                               printfPQExpBuffer(&buf, "    %s", tgdef);
                                                printTableAddFooter(&cont, buf.data);
+                                               have_heading = true;
                                        }
+
+                                       /* Everything after "TRIGGER" is echoed verbatim */
+                                       tgdef = PQgetvalue(result, i, 1);
+                                       usingpos = strstr(tgdef, " TRIGGER ");
+                                       if (usingpos)
+                                               tgdef = usingpos + 9;
+
+                                       printfPQExpBuffer(&buf, "    %s", tgdef);
+                                       printTableAddFooter(&cont, buf.data);
                                }
                        }
+               }
+               PQclear(result);
+       }
+
+       /*
+        * Finish printing the footer information about a table.
+        */
+       if (tableinfo.relkind == 'r' || tableinfo.relkind == 'f')
+       {
+               PGresult   *result;
+               int                     tuples;
+
+               /* print foreign server name */
+               if (tableinfo.relkind == 'f')
+               {
+                       /* Footer information about foreign table */
+                       printfPQExpBuffer(&buf,
+                                                         "SELECT s.srvname\n"
+                                                         "FROM pg_catalog.pg_foreign_table f,\n"
+                                                         "     pg_catalog.pg_foreign_server s\n"
+                                                         "WHERE f.ftrelid = %s AND s.oid = f.ftserver;",
+                                                         oid);
+                       result = PSQLexec(buf.data, false);
+                       if (!result)
+                               goto error_return;
+                       else if (PQntuples(result) != 1)
+                       {
+                               PQclear(result);
+                               goto error_return;
+                       }
+
+                       printfPQExpBuffer(&buf, "Server: %s",
+                                                         PQgetvalue(result, 0, 0));
+                       printTableAddFooter(&cont, buf.data);
                        PQclear(result);
                }
 
                /* print inherited tables */
-               printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
+               printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno;", oid);
 
                result = PSQLexec(buf.data, false);
                if (!result)
@@ -1668,6 +2110,57 @@ describeOneTableDetails(const char *schemaname,
                }
                PQclear(result);
 
+               /* print child tables */
+               if (pset.sversion >= 80300)
+                       printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.oid::pg_catalog.regclass::pg_catalog.text;", oid);
+               else
+                       printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhrelid AND i.inhparent = '%s' ORDER BY c.relname;", oid);
+
+               result = PSQLexec(buf.data, false);
+               if (!result)
+                       goto error_return;
+               else
+                       tuples = PQntuples(result);
+
+               if (!verbose)
+               {
+                       /* print the number of child tables, if any */
+                       if (tuples > 0)
+                       {
+                               printfPQExpBuffer(&buf, _("Number of child tables: %d (Use \\d+ to list them.)"), tuples);
+                               printTableAddFooter(&cont, buf.data);
+                       }
+               }
+               else
+               {
+                       /* display the list of child tables */
+                       const char *ct = _("Child tables");
+
+                       for (i = 0; i < tuples; i++)
+                       {
+                               if (i == 0)
+                                       printfPQExpBuffer(&buf, "%s: %s",
+                                                                         ct, PQgetvalue(result, i, 0));
+                               else
+                                       printfPQExpBuffer(&buf, "%*s  %s",
+                                                                         (int) strlen(ct), "",
+                                                                         PQgetvalue(result, i, 0));
+                               if (i < tuples - 1)
+                                       appendPQExpBuffer(&buf, ",");
+
+                               printTableAddFooter(&cont, buf.data);
+                       }
+               }
+               PQclear(result);
+
+               /* Table type */
+               if (tableinfo.reloftype)
+               {
+                       printfPQExpBuffer(&buf, _("Typed table of type: %s"), tableinfo.reloftype);
+                       printTableAddFooter(&cont, buf.data);
+               }
+
+               /* OIDs and options */
                if (verbose)
                {
                        const char *s = _("Has OIDs");
@@ -1707,14 +2200,14 @@ error_return:
        termPQExpBuffer(&buf);
        termPQExpBuffer(&title);
        termPQExpBuffer(&tmpbuf);
-       
+
        if (seq_values)
        {
                for (ptr = seq_values; *ptr; ptr++)
                        free(*ptr);
                free(seq_values);
        }
-    
+
        if (modifiers)
        {
                for (ptr = modifiers; *ptr; ptr++)
@@ -1756,7 +2249,7 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
                        initPQExpBuffer(&buf);
                        printfPQExpBuffer(&buf,
                                                          "SELECT spcname FROM pg_catalog.pg_tablespace\n"
-                                                         "WHERE oid = '%u'", tablespace);
+                                                         "WHERE oid = '%u';", tablespace);
                        result = PSQLexec(buf.data, false);
                        if (!result)
                                return;
@@ -1774,8 +2267,11 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
                                {
                                        /* Append the tablespace to the latest footer */
                                        printfPQExpBuffer(&buf, "%s", cont->footer->data);
-                                       /* translator: before this string there's an index
-                                        * description like '"foo_pkey" PRIMARY KEY, btree (a)' */
+
+                                       /*
+                                        * translator: before this string there's an index
+                                        * description like '"foo_pkey" PRIMARY KEY, btree (a)'
+                                        */
                                        appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
                                                                          PQgetvalue(result, 0, 0));
                                        printTableSetFooter(cont, buf.data);
@@ -1811,39 +2307,43 @@ describeRoles(const char *pattern, bool verbose)
        if (pset.sversion >= 80100)
        {
                printfPQExpBuffer(&buf,
-                                            "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
-                                                "  r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
-                                                "  r.rolconnlimit,\n"
-                                            "  ARRAY(SELECT b.rolname\n"
-                                            "        FROM pg_catalog.pg_auth_members m\n"
-                                            "        JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)\n"
-                                            "        WHERE m.member = r.oid) as memberof");
+                                                 "SELECT r.rolname, r.rolsuper, r.rolinherit,\n"
+                                                 "  r.rolcreaterole, r.rolcreatedb, r.rolcanlogin,\n"
+                                                 "  r.rolconnlimit,\n"
+                                                 "  ARRAY(SELECT b.rolname\n"
+                                                 "        FROM pg_catalog.pg_auth_members m\n"
+                                "        JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)\n"
+                                                 "        WHERE m.member = r.oid) as memberof");
 
                if (verbose && pset.sversion >= 80200)
                {
                        appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
                        ncols++;
                }
+               if (pset.sversion >= 90100)
+               {
+                       appendPQExpBufferStr(&buf, "\n, r.rolreplication");
+               }
 
                appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
 
                processSQLNamePattern(pset.db, &buf, pattern, false, false,
                                                          NULL, "r.rolname", NULL, NULL);
-    }
-    else
-    {
-           printfPQExpBuffer(&buf,
-                                         "SELECT u.usename AS rolname,\n"
-                                         "  u.usesuper AS rolsuper,\n"
-                                         "  true AS rolinherit, false AS rolcreaterole,\n"
-                                         "  u.usecreatedb AS rolcreatedb, true AS rolcanlogin,\n"
-                                         "  -1 AS rolconnlimit,\n"
-                                         "  ARRAY(SELECT g.groname FROM pg_catalog.pg_group g WHERE u.usesysid = ANY(g.grolist)) as memberof"
-                                         "\nFROM pg_catalog.pg_user u\n");
+       }
+       else
+       {
+               printfPQExpBuffer(&buf,
+                                                 "SELECT u.usename AS rolname,\n"
+                                                 "  u.usesuper AS rolsuper,\n"
+                                                 "  true AS rolinherit, false AS rolcreaterole,\n"
+                                        "  u.usecreatedb AS rolcreatedb, true AS rolcanlogin,\n"
+                                                 "  -1 AS rolconnlimit,\n"
+                                                 "  ARRAY(SELECT g.groname FROM pg_catalog.pg_group g WHERE u.usesysid = ANY(g.grolist)) as memberof"
+                                                 "\nFROM pg_catalog.pg_user u\n");
 
                processSQLNamePattern(pset.db, &buf, pattern, false, false,
                                                          NULL, "u.usename", NULL, NULL);
-    }
+       }
 
        appendPQExpBuffer(&buf, "ORDER BY 1;");
 
@@ -1865,7 +2365,7 @@ describeRoles(const char *pattern, bool verbose)
 
        for (i = 0; i < nrows; i++)
        {
-               printTableAddCell(&cont, PQgetvalue(res, i, 0), false);
+               printTableAddCell(&cont, PQgetvalue(res, i, 0), false, false);
 
                resetPQExpBuffer(&buf);
                if (strcmp(PQgetvalue(res, i, 1), "t") == 0)
@@ -1883,6 +2383,10 @@ describeRoles(const char *pattern, bool verbose)
                if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
                        add_role_attribute(&buf, _("Cannot login"));
 
+               if (pset.sversion >= 90100)
+                       if (strcmp(PQgetvalue(res, i, (verbose ? 9 : 8)), "t") == 0)
+                               add_role_attribute(&buf, _("Replication"));
+
                conns = atoi(PQgetvalue(res, i, 6));
                if (conns >= 0)
                {
@@ -1891,20 +2395,21 @@ describeRoles(const char *pattern, bool verbose)
 
                        if (conns == 0)
                                appendPQExpBuffer(&buf, _("No connections"));
-                       else if (conns == 1)
-                               appendPQExpBuffer(&buf, _("1 connection"));
                        else
-                               appendPQExpBuffer(&buf, _("%d connections"), conns);
+                               appendPQExpBuffer(&buf, ngettext("%d connection",
+                                                                                                "%d connections",
+                                                                                                conns),
+                                                                 conns);
                }
 
                attr[i] = pg_strdup(buf.data);
 
-               printTableAddCell(&cont, attr[i], false);
+               printTableAddCell(&cont, attr[i], false, false);
 
-               printTableAddCell(&cont, PQgetvalue(res, i, 7), false);
+               printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
 
                if (verbose && pset.sversion >= 80200)
-                       printTableAddCell(&cont, PQgetvalue(res, i, 8), false);
+                       printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
        }
        termPQExpBuffer(&buf);
 
@@ -1919,28 +2424,88 @@ describeRoles(const char *pattern, bool verbose)
        return true;
 }
 
-void
+static void
 add_role_attribute(PQExpBuffer buf, const char *const str)
 {
        if (buf->len > 0)
-               appendPQExpBufferStr(buf, "\n");
+               appendPQExpBufferStr(buf, "");
 
        appendPQExpBufferStr(buf, str);
 }
 
+/*
+ * \drds
+ */
+bool
+listDbRoleSettings(const char *pattern, const char *pattern2)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       printQueryOpt myopt = pset.popt;
+
+       initPQExpBuffer(&buf);
+
+       if (pset.sversion >= 90000)
+       {
+               bool            havewhere;
+
+               printfPQExpBuffer(&buf, "SELECT rolname AS role, datname AS database,\n"
+                               "pg_catalog.array_to_string(setconfig, E'\\n') AS settings\n"
+                                                 "FROM pg_db_role_setting AS s\n"
+                                  "LEFT JOIN pg_database ON pg_database.oid = setdatabase\n"
+                                                 "LEFT JOIN pg_roles ON pg_roles.oid = setrole\n");
+               havewhere = processSQLNamePattern(pset.db, &buf, pattern, false, false,
+                                                                          NULL, "pg_roles.rolname", NULL, NULL);
+               processSQLNamePattern(pset.db, &buf, pattern2, havewhere, false,
+                                                         NULL, "pg_database.datname", NULL, NULL);
+               appendPQExpBufferStr(&buf, "ORDER BY role, database;");
+       }
+       else
+       {
+               fprintf(pset.queryFout,
+               _("No per-database role settings support in this server version.\n"));
+               return false;
+       }
+
+       res = PSQLexec(buf.data, false);
+       if (!res)
+               return false;
+
+       if (PQntuples(res) == 0 && !pset.quiet)
+       {
+               if (pattern)
+                       fprintf(pset.queryFout, _("No matching settings found.\n"));
+               else
+                       fprintf(pset.queryFout, _("No settings found.\n"));
+       }
+       else
+       {
+               myopt.nullPrint = NULL;
+               myopt.title = _("List of settings");
+               myopt.translate_header = true;
+
+               printQuery(res, &myopt, pset.queryFout, pset.logfile);
+       }
+
+       PQclear(res);
+       resetPQExpBuffer(&buf);
+       return true;
+}
+
 
 /*
  * listTables()
  *
- * handler for \d, \dt, etc.
+ * handler for \dt, \di, etc.
  *
  * tabtypes is an array of characters, specifying what info is desired:
  * t - tables
  * i - indexes
  * v - views
  * s - sequences
- * S - system tables (pg_catalog)
+ * E - foreign table (Note: different from 'f', the relkind value)
  * (any order of the above is fine)
+ * If tabtypes is empty, we default to \dtvsE.
  */
 bool
 listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem)
@@ -1949,14 +2514,15 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
        bool            showIndexes = strchr(tabtypes, 'i') != NULL;
        bool            showViews = strchr(tabtypes, 'v') != NULL;
        bool            showSeq = strchr(tabtypes, 's') != NULL;
+       bool            showForeign = strchr(tabtypes, 'E') != NULL;
 
        PQExpBufferData buf;
        PGresult   *res;
        printQueryOpt myopt = pset.popt;
        static const bool translate_columns[] = {false, false, true, false, false, false, false};
 
-       if (!(showTables || showIndexes || showViews || showSeq))
-               showTables = showViews = showSeq = true;
+       if (!(showTables || showIndexes || showViews || showSeq || showForeign))
+               showTables = showViews = showSeq = showForeign = true;
 
        initPQExpBuffer(&buf);
 
@@ -1967,7 +2533,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
        printfPQExpBuffer(&buf,
                                          "SELECT n.nspname as \"%s\",\n"
                                          "  c.relname as \"%s\",\n"
-                                         "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' END as \"%s\",\n"
+                                         "  CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' WHEN 's' THEN '%s' WHEN 'f' THEN '%s' END as \"%s\",\n"
                                          "  pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
                                          gettext_noop("Schema"),
                                          gettext_noop("Name"),
@@ -1976,6 +2542,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
                                          gettext_noop("index"),
                                          gettext_noop("sequence"),
                                          gettext_noop("special"),
+                                         gettext_noop("foreign table"),
                                          gettext_noop("Type"),
                                          gettext_noop("Owner"));
 
@@ -1984,14 +2551,25 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
                                                  ",\n c2.relname as \"%s\"",
                                                  gettext_noop("Table"));
 
-       if (verbose && pset.sversion >= 80100)
-               appendPQExpBuffer(&buf,
-                                                 ",\n  pg_catalog.pg_size_pretty(pg_catalog.pg_relation_size(c.oid)) as \"%s\"",
-                                                 gettext_noop("Size"));
        if (verbose)
+       {
+               /*
+                * As of PostgreSQL 9.0, use pg_table_size() to show a more acurate
+                * size of a table, including FSM, VM and TOAST tables.
+                */
+               if (pset.sversion >= 90000)
+                       appendPQExpBuffer(&buf,
+                                                         ",\n  pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"%s\"",
+                                                         gettext_noop("Size"));
+               else if (pset.sversion >= 80100)
+                       appendPQExpBuffer(&buf,
+                                                         ",\n  pg_catalog.pg_size_pretty(pg_catalog.pg_relation_size(c.oid)) as \"%s\"",
+                                                         gettext_noop("Size"));
+
                appendPQExpBuffer(&buf,
                          ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
                                                  gettext_noop("Description"));
+       }
 
        appendPQExpBuffer(&buf,
                                          "\nFROM pg_catalog.pg_class c"
@@ -2010,16 +2588,26 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
                appendPQExpBuffer(&buf, "'i',");
        if (showSeq)
                appendPQExpBuffer(&buf, "'S',");
-       if (showSystem)
-               appendPQExpBuffer(&buf, "'s',");        /* was RELKIND_SPECIAL in <= 8.1 */
+       if (showSystem || pattern)
+               appendPQExpBuffer(&buf, "'s',");                /* was RELKIND_SPECIAL in <=
+                                                                                                * 8.1 */
+       if (showForeign)
+               appendPQExpBuffer(&buf, "'f',");
+
        appendPQExpBuffer(&buf, "''");          /* dummy */
        appendPQExpBuffer(&buf, ")\n");
 
-       if (!showSystem)
-               /* Exclude system and pg_toast objects, but show temp tables */
-               appendPQExpBuffer(&buf,
-                                                 "  AND n.nspname <> 'pg_catalog'\n"
-                                                 "  AND n.nspname !~ '^pg_toast'\n");
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+                                                 "      AND n.nspname <> 'information_schema'\n");
+
+       /*
+        * TOAST objects are suppressed unconditionally.  Since we don't provide
+        * any way to select relkind 't' above, we would never show toast tables
+        * in any case; it seems a bit confusing to allow their indexes to be
+        * shown. Use plain \d if you really need to look at a TOAST table/index.
+        */
+       appendPQExpBuffer(&buf, "      AND n.nspname !~ '^pg_toast'\n");
 
        processSQLNamePattern(pset.db, &buf, pattern, true, false,
                                                  "n.nspname", "c.relname", NULL,
@@ -2054,13 +2642,88 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
 }
 
 
+/*
+ * \dL
+ *
+ * Describes languages.
+ */
+bool
+listLanguages(const char *pattern, bool verbose, bool showSystem)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       printQueryOpt myopt = pset.popt;
+
+       initPQExpBuffer(&buf);
+
+       printfPQExpBuffer(&buf,
+                                         "SELECT l.lanname AS \"%s\",\n",
+                                         gettext_noop("Name"));
+       if (pset.sversion >= 80300)
+               appendPQExpBuffer(&buf,
+                               "       pg_catalog.pg_get_userbyid(l.lanowner) as \"%s\",\n",
+                                                 gettext_noop("Owner"));
+
+       appendPQExpBuffer(&buf,
+                                         "       l.lanpltrusted AS \"%s\"",
+                                         gettext_noop("Trusted"));
+
+       if (verbose)
+       {
+               appendPQExpBuffer(&buf,
+                                                 ",\n       NOT l.lanispl AS \"%s\",\n"
+                                                 "       l.lanplcallfoid::regprocedure AS \"%s\",\n"
+                                  "       l.lanvalidator::regprocedure AS \"%s\",\n       ",
+                                                 gettext_noop("Internal Language"),
+                                                 gettext_noop("Call Handler"),
+                                                 gettext_noop("Validator"));
+               if (pset.sversion >= 90000)
+                       appendPQExpBuffer(&buf, "l.laninline::regprocedure AS \"%s\",\n       ",
+                                                         gettext_noop("Inline Handler"));
+               printACLColumn(&buf, "l.lanacl");
+       }
+
+       appendPQExpBuffer(&buf,
+                                         ",\n       d.description AS \"%s\""
+                                         "\nFROM pg_catalog.pg_language l\n"
+                                         "LEFT JOIN pg_catalog.pg_description d\n"
+                                         "  ON d.classoid = l.tableoid AND d.objoid = l.oid\n"
+                                         "  AND d.objsubid = 0\n",
+                                         gettext_noop("Description"));
+
+       if (pattern)
+               processSQLNamePattern(pset.db, &buf, pattern, false, false,
+                                                         NULL, "l.lanname", NULL, NULL);
+
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "WHERE l.lanplcallfoid != 0\n");
+
+
+       appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+       res = PSQLexec(buf.data, false);
+       termPQExpBuffer(&buf);
+       if (!res)
+               return false;
+
+       myopt.nullPrint = NULL;
+       myopt.title = _("List of languages");
+       myopt.translate_header = true;
+
+       printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+       PQclear(res);
+       return true;
+}
+
+
 /*
  * \dD
  *
  * Describes domains.
  */
 bool
-listDomains(const char *pattern, bool showSystem)
+listDomains(const char *pattern, bool verbose, bool showSystem)
 {
        PQExpBufferData buf;
        PGresult   *res;
@@ -2072,25 +2735,44 @@ listDomains(const char *pattern, bool showSystem)
                                          "SELECT n.nspname as \"%s\",\n"
                                          "       t.typname as \"%s\",\n"
         "       pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
-                                         "       CASE WHEN t.typnotnull AND t.typdefault IS NOT NULL THEN 'not null default '||t.typdefault\n"
-       "            WHEN t.typnotnull AND t.typdefault IS NULL THEN 'not null'\n"
-                                         "            WHEN NOT t.typnotnull AND t.typdefault IS NOT NULL THEN 'default '||t.typdefault\n"
-                                         "            ELSE ''\n"
-                                         "       END 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\"\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",
+                                         "       TRIM(LEADING\n",
                                          gettext_noop("Schema"),
                                          gettext_noop("Name"),
-                                         gettext_noop("Type"),
+                                         gettext_noop("Type"));
+       if (pset.sversion >= 90100)
+               appendPQExpBuffer(&buf,
+                                                 "            COALESCE((SELECT ' collate ' || c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
+                                                 "                      WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation), '') ||\n");
+       appendPQExpBuffer(&buf,
+          "            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 (!showSystem)
-               appendPQExpBuffer(&buf, "  AND n.nspname <> 'pg_catalog'\n");
+       if (verbose)
+               appendPQExpBuffer(&buf,
+                                                 ",\n       d.description as \"%s\"",
+                                                 gettext_noop("Description"));
+
+       appendPQExpBuffer(&buf,
+                                         "\nFROM pg_catalog.pg_type t\n"
+          "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
+
+       if (verbose)
+               appendPQExpBuffer(&buf,
+                                                 "     LEFT JOIN pg_catalog.pg_description d "
+                                                 "ON d.classoid = t.tableoid AND d.objoid = t.oid "
+                                                 "AND d.objsubid = 0\n");
+
+       appendPQExpBuffer(&buf, "WHERE t.typtype = 'd'\n");
+
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+                                                 "      AND n.nspname <> 'information_schema'\n");
 
        processSQLNamePattern(pset.db, &buf, pattern, true, false,
                                                  "n.nspname", "t.typname", NULL,
@@ -2119,7 +2801,7 @@ listDomains(const char *pattern, bool showSystem)
  * Describes conversions.
  */
 bool
-listConversions(const char *pattern, bool showSystem)
+listConversions(const char *pattern, bool verbose, bool showSystem)
 {
        PQExpBufferData buf;
        PGresult   *res;
@@ -2134,9 +2816,7 @@ 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\"",
                                          gettext_noop("Schema"),
                                          gettext_noop("Name"),
                                          gettext_noop("Source"),
@@ -2144,8 +2824,28 @@ listConversions(const char *pattern, bool showSystem)
                                          gettext_noop("yes"), gettext_noop("no"),
                                          gettext_noop("Default?"));
 
-       if (!showSystem)
-               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n");
+       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,
@@ -2175,7 +2875,7 @@ listConversions(const char *pattern, bool showSystem)
  * Describes casts.
  */
 bool
-listCasts(const char *pattern)
+listCasts(const char *pattern, bool verbose)
 {
        PQExpBufferData buf;
        PGresult   *res;
@@ -2183,6 +2883,7 @@ listCasts(const char *pattern)
        static const bool translate_columns[] = {false, false, false, true};
 
        initPQExpBuffer(&buf);
+
        /*
         * We need a left join to pg_proc for binary casts; the others are just
         * paranoia.  Also note that we don't attempt to localize '(binary
@@ -2192,13 +2893,27 @@ listCasts(const char *pattern)
        printfPQExpBuffer(&buf,
                           "SELECT pg_catalog.format_type(castsource, NULL) AS \"%s\",\n"
                           "       pg_catalog.format_type(casttarget, NULL) AS \"%s\",\n"
-                                         "       CASE WHEN castfunc = 0 THEN '(binary coercible)'\n"
+                                 "       CASE WHEN castfunc = 0 THEN '(binary coercible)'\n"
                                          "            ELSE p.proname\n"
                                          "       END as \"%s\",\n"
                                          "       CASE WHEN c.castcontext = 'e' THEN '%s'\n"
                                          "            WHEN c.castcontext = 'a' THEN '%s'\n"
                                          "            ELSE '%s'\n"
-                                         "       END as \"%s\"\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"
@@ -2208,13 +2923,15 @@ listCasts(const char *pattern)
                                          "     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?"));
+                                         "     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
@@ -2232,7 +2949,7 @@ listCasts(const char *pattern)
                                                  "pg_catalog.format_type(tt.oid, NULL)",
                                                  "pg_catalog.pg_type_is_visible(tt.oid)");
 
-       appendPQExpBuffer(&buf, ")\nORDER BY 1, 2;");
+       appendPQExpBuffer(&buf, ") )\nORDER BY 1, 2;");
 
        res = PSQLexec(buf.data, false);
        termPQExpBuffer(&buf);
@@ -2250,13 +2967,81 @@ listCasts(const char *pattern)
        return true;
 }
 
+/*
+ * \dO
+ *
+ * Describes collations.
+ */
+bool
+listCollations(const char *pattern, bool verbose, bool showSystem)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       printQueryOpt myopt = pset.popt;
+       static const bool translate_columns[] = {false, false, false, false, false};
+
+       initPQExpBuffer(&buf);
+
+       printfPQExpBuffer(&buf,
+                                         "SELECT n.nspname AS \"%s\",\n"
+                                         "       c.collname AS \"%s\",\n"
+                                         "       c.collcollate AS \"%s\",\n"
+                                         "       c.collctype AS \"%s\"",
+                                         gettext_noop("Schema"),
+                                         gettext_noop("Name"),
+                                         gettext_noop("Collate"),
+                                         gettext_noop("Ctype"));
+
+       if (verbose)
+               appendPQExpBuffer(&buf,
+                                                 ",\n       pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"",
+                                                 gettext_noop("Description"));
+
+       appendPQExpBuffer(&buf,
+                         "\nFROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n"
+                                         "WHERE n.oid = c.collnamespace\n");
+
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf, "      AND n.nspname <> 'pg_catalog'\n"
+                                                 "      AND n.nspname <> 'information_schema'\n");
+
+       /*
+        * Hide collations that aren't usable in the current database's encoding.
+        * If you think to change this, note that pg_collation_is_visible rejects
+        * unusable collations, so you will need to hack name pattern processing
+        * somehow to avoid inconsistent behavior.
+        */
+       appendPQExpBuffer(&buf, "      AND c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))\n");
+
+       processSQLNamePattern(pset.db, &buf, pattern, true, false,
+                                                 "n.nspname", "c.collname", NULL,
+                                                 "pg_catalog.pg_collation_is_visible(c.oid)");
+
+       appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+
+       res = PSQLexec(buf.data, false);
+       termPQExpBuffer(&buf);
+       if (!res)
+               return false;
+
+       myopt.nullPrint = NULL;
+       myopt.title = _("List of collations");
+       myopt.translate_header = true;
+       myopt.translate_columns = translate_columns;
+
+       printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+       PQclear(res);
+       return true;
+}
+
 /*
  * \dn
  *
  * Describes schemas (namespaces)
  */
 bool
-listSchemas(const char *pattern, bool verbose)
+listSchemas(const char *pattern, bool verbose, bool showSystem)
 {
        PQExpBufferData buf;
        PGresult   *res;
@@ -2274,16 +3059,19 @@ listSchemas(const char *pattern, bool verbose)
                appendPQExpBuffer(&buf, ",\n  ");
                printACLColumn(&buf, "n.nspacl");
                appendPQExpBuffer(&buf,
-                       ",\n  pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
+                 ",\n  pg_catalog.obj_description(n.oid, 'pg_namespace') AS \"%s\"",
                                                  gettext_noop("Description"));
        }
 
        appendPQExpBuffer(&buf,
-                         "\nFROM pg_catalog.pg_namespace n\n"
-                                         "WHERE        (n.nspname !~ '^pg_temp_' OR\n"
-                  "             n.nspname = (pg_catalog.current_schemas(true))[1])\n");                /* temp schema is first */
+                                         "\nFROM pg_catalog.pg_namespace n\n");
 
-       processSQLNamePattern(pset.db, &buf, pattern, true, false,
+       if (!showSystem && !pattern)
+               appendPQExpBuffer(&buf,
+               "WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'\n");
+
+       processSQLNamePattern(pset.db, &buf, pattern,
+                                                 !showSystem && !pattern, false,
                                                  NULL, "n.nspname", NULL,
                                                  NULL);
 
@@ -2469,7 +3257,7 @@ describeOneTSParser(const char *oid, const char *nspname, const char *prsname)
                                          "   p.prslextype::pg_catalog.regproc, \n"
                                  "   pg_catalog.obj_description(p.prslextype, 'pg_proc') \n"
                                          " FROM pg_catalog.pg_ts_parser p \n"
-                                         " WHERE p.oid = '%s' \n",
+                                         " WHERE p.oid = '%s';",
                                          gettext_noop("Start parse"),
                                          gettext_noop("Method"),
                                          gettext_noop("Function"),
@@ -2830,7 +3618,7 @@ describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname,
         "FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m \n"
                                          "WHERE c.oid = '%s' AND m.mapcfg = c.oid \n"
                                          "GROUP BY m.mapcfg, m.maptokentype, c.cfgparser \n"
-                                         "ORDER BY 1",
+                                         "ORDER BY 1;",
                                          gettext_noop("Token"),
                                          gettext_noop("Dictionaries"),
                                          oid);
@@ -2893,10 +3681,15 @@ listForeignDataWrappers(const char *pattern, bool verbose)
        initPQExpBuffer(&buf);
        printfPQExpBuffer(&buf,
                                          "SELECT fdwname AS \"%s\",\n"
-                                         "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n"
-                                         "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
+                                         "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n",
                                          gettext_noop("Name"),
-                                         gettext_noop("Owner"),
+                                         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)
@@ -2933,7 +3726,7 @@ listForeignDataWrappers(const char *pattern, bool verbose)
 /*
  * \des
  *
- * Describes foreign-data servers.
+ * Describes foreign servers.
  */
 bool
 listForeignServers(const char *pattern, bool verbose)
@@ -2944,7 +3737,7 @@ listForeignServers(const char *pattern, bool verbose)
 
        if (pset.sversion < 80400)
        {
-               fprintf(stderr, _("The server (version %d.%d) does not support foreign-data servers.\n"),
+               fprintf(stderr, _("The server (version %d.%d) does not support foreign servers.\n"),
                                pset.sversion / 10000, (pset.sversion / 100) % 100);
                return true;
        }
@@ -2973,8 +3766,8 @@ 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");
+                                         "\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);
@@ -3010,7 +3803,7 @@ listUserMappings(const char *pattern, bool verbose)
 
        if (pset.sversion < 80400)
        {
-               fprintf(stderr, _("The server (version %d.%d) does not support foreign-data user mappings.\n"),
+               fprintf(stderr, _("The server (version %d.%d) does not support user mappings.\n"),
                                pset.sversion / 10000, (pset.sversion / 100) % 100);
                return true;
        }
@@ -3020,7 +3813,7 @@ listUserMappings(const char *pattern, bool verbose)
                                          "SELECT um.srvname AS \"%s\",\n"
                                          "  um.usename AS \"%s\"",
                                          gettext_noop("Server"),
-                                         gettext_noop("Username"));
+                                         gettext_noop("User name"));
 
        if (verbose)
                appendPQExpBuffer(&buf,
@@ -3049,11 +3842,233 @@ listUserMappings(const char *pattern, bool verbose)
        return true;
 }
 
+/*
+ * \det
+ *
+ * Describes foreign tables.
+ */
+bool
+listForeignTables(const char *pattern, bool verbose)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       printQueryOpt myopt = pset.popt;
+
+       if (pset.sversion < 90100)
+       {
+               fprintf(stderr, _("The server (version %d.%d) does not support foreign tables.\n"),
+                               pset.sversion / 10000, (pset.sversion / 100) % 100);
+               return true;
+       }
+
+       initPQExpBuffer(&buf);
+       printfPQExpBuffer(&buf,
+                                         "SELECT n.nspname AS \"%s\",\n"
+                                         "  c.relname AS \"%s\",\n"
+                                         "  s.srvname AS \"%s\"",
+                                         gettext_noop("Schema"),
+                                         gettext_noop("Table"),
+                                         gettext_noop("Server"));
+
+       if (verbose)
+               appendPQExpBuffer(&buf,
+                                                 ",\n  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;");
+
+       res = PSQLexec(buf.data, false);
+       termPQExpBuffer(&buf);
+       if (!res)
+               return false;
+
+       myopt.nullPrint = NULL;
+       myopt.title = _("List of foreign tables");
+       myopt.translate_header = true;
+
+       printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+       PQclear(res);
+       return true;
+}
+
+/*
+ * \dx
+ *
+ * Briefly describes installed extensions.
+ */
+bool
+listExtensions(const char *pattern)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       printQueryOpt myopt = pset.popt;
+
+       if (pset.sversion < 90100)
+       {
+               fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"),
+                               pset.sversion / 10000, (pset.sversion / 100) % 100);
+               return true;
+       }
+
+       initPQExpBuffer(&buf);
+       printfPQExpBuffer(&buf,
+                                         "SELECT e.extname AS \"%s\", "
+        "e.extversion AS \"%s\", n.nspname AS \"%s\", c.description AS \"%s\"\n"
+                                         "FROM pg_catalog.pg_extension e "
+                        "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace "
+                                "LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid "
+                "AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n",
+                                         gettext_noop("Name"),
+                                         gettext_noop("Version"),
+                                         gettext_noop("Schema"),
+                                         gettext_noop("Description"));
+
+       processSQLNamePattern(pset.db, &buf, pattern,
+                                                 false, false,
+                                                 NULL, "e.extname", NULL,
+                                                 NULL);
+
+       appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+       res = PSQLexec(buf.data, false);
+       termPQExpBuffer(&buf);
+       if (!res)
+               return false;
+
+       myopt.nullPrint = NULL;
+       myopt.title = _("List of installed extensions");
+       myopt.translate_header = true;
+
+       printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+       PQclear(res);
+       return true;
+}
+
+/*
+ * \dx+
+ *
+ * List contents of installed extensions.
+ */
+bool
+listExtensionContents(const char *pattern)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       int                     i;
+
+       if (pset.sversion < 90100)
+       {
+               fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"),
+                               pset.sversion / 10000, (pset.sversion / 100) % 100);
+               return true;
+       }
+
+       initPQExpBuffer(&buf);
+       printfPQExpBuffer(&buf,
+                                         "SELECT e.extname, e.oid\n"
+                                         "FROM pg_catalog.pg_extension e\n");
+
+       processSQLNamePattern(pset.db, &buf, pattern,
+                                                 false, false,
+                                                 NULL, "e.extname", NULL,
+                                                 NULL);
+
+       appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+       res = PSQLexec(buf.data, false);
+       termPQExpBuffer(&buf);
+       if (!res)
+               return false;
+
+       if (PQntuples(res) == 0)
+       {
+               if (!pset.quiet)
+               {
+                       if (pattern)
+                               fprintf(stderr, _("Did not find any extension named \"%s\".\n"),
+                                               pattern);
+                       else
+                               fprintf(stderr, _("Did not find any extensions.\n"));
+               }
+               PQclear(res);
+               return false;
+       }
+
+       for (i = 0; i < PQntuples(res); i++)
+       {
+               const char *extname;
+               const char *oid;
+
+               extname = PQgetvalue(res, i, 0);
+               oid = PQgetvalue(res, i, 1);
+
+               if (!listOneExtensionContents(extname, oid))
+               {
+                       PQclear(res);
+                       return false;
+               }
+               if (cancel_pressed)
+               {
+                       PQclear(res);
+                       return false;
+               }
+       }
+
+       PQclear(res);
+       return true;
+}
+
+static bool
+listOneExtensionContents(const char *extname, const char *oid)
+{
+       PQExpBufferData buf;
+       PGresult   *res;
+       char            title[1024];
+       printQueryOpt myopt = pset.popt;
+
+       initPQExpBuffer(&buf);
+       printfPQExpBuffer(&buf,
+               "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n"
+                                         "FROM pg_catalog.pg_depend\n"
+                                         "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n"
+                                         "ORDER BY 1;",
+                                         gettext_noop("Object Description"),
+                                         oid);
+
+       res = PSQLexec(buf.data, false);
+       termPQExpBuffer(&buf);
+       if (!res)
+               return false;
+
+       myopt.nullPrint = NULL;
+       snprintf(title, sizeof(title), _("Objects in extension \"%s\""), extname);
+       myopt.title = title;
+       myopt.translate_header = true;
+
+       printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+       PQclear(res);
+       return true;
+}
+
 /*
  * printACLColumn
  *
  * Helper function for consistently formatting ACL (privilege) columns.
- * The proper targetlist entry is appended to buf.  Note lack of any
+ * The proper targetlist entry is appended to buf.     Note lack of any
  * whitespace or comma decoration.
  */
 static void