diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index a1384409cf3b521d00a4ecbb6b3384e95c8edf47..5a66973e32d0cea72c6144bd2c80cc7cf678fdca 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/grant.sgml,v 1.26 2002/05/14 18:47:58 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/grant.sgml,v 1.27 2002/08/10 03:56:23 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -249,16 +249,17 @@ GRANT { { CREATE | USAGE } [,...] | ALL [ PRIVILEGES ] }
    </para>
 
    <para>
-    Use <xref linkend="app-psql">'s <command>\z</command> command
+    Use <xref linkend="app-psql">'s <command>\dp</command> command
     to obtain information about existing privileges, for example:
 <programlisting>
-lusitania=> \z mytable
-    Access privileges for database "lusitania"
-  Table  |           Access privileges
----------+---------------------------------------
- mytable | {=r,miriam=arwdRxt,"group todos=arw"}
+lusitania=> \dp mytable
+        Access privileges for database "lusitania"
+ Schema |  Table  |           Access privileges
+--------+---------+---------------------------------------
+ public | mytable | {=r,miriam=arwdRxt,"group todos=arw"}
+(1 row)
 </programlisting>
-    The entries shown by <command>\z</command> are interpreted thus:
+    The entries shown by <command>\dp</command> are interpreted thus:
 <programlisting>
               =xxxx -- privileges granted to PUBLIC
          uname=xxxx -- privileges granted to a user
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index b9f8554abfb6a7d6fd14267a6f2a67ba448ea516..48370d1e2146c720d22985989ee268a65939d8d6 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.69 2002/07/28 15:22:21 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.70 2002/08/10 03:56:23 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -538,7 +538,7 @@ testdb=>
     </para>
 
     <para>
-    To include whitespace into an argument you must quote it with a
+    To include whitespace into an argument you may quote it with a
     single quote. To include a single quote into such an argument,
     precede it by a backslash. Anything contained in single quotes is
     furthermore subject to C-like substitutions for
@@ -551,25 +551,24 @@ testdb=>
 
     <para>
     If an unquoted argument begins with a colon (<literal>:</literal>),
-    it is taken as a variable and the value of the variable is taken as
-    the argument instead.
+    it is taken as a <application>psql</> variable and the value of the
+    variable is used as the argument instead.
     </para>
 
     <para>
     Arguments that are quoted in <quote>backticks</quote>
     (<literal>`</literal>) are taken as a command line that is passed to
-    the shell. The output of the command (with a trailing newline
+    the shell. The output of the command (with any trailing newline
     removed) is taken as the argument value. The above escape sequences
     also apply in backticks.
     </para>
 
     <para>
-    Some commands take the name of an <acronym>SQL</acronym> identifier
+    Some commands take an <acronym>SQL</acronym> identifier
     (such as a table name) as argument. These arguments follow the
     syntax rules of <acronym>SQL</acronym> regarding double quotes: an
-    identifier without double quotes is coerced to lower-case. For all
-    other commands double quotes are not special and will become part of
-    the argument.
+    identifier without double quotes is coerced to lower-case, while
+    whitespace within double quotes is included in the argument.
     </para>
 
     <para>
@@ -732,18 +731,17 @@ testdb=>
       </varlistentry>
 
       <varlistentry>
-        <term><literal>\d</literal> <replaceable class="parameter">relation</replaceable> </term>
+        <term><literal>\d</literal> [ <replaceable class="parameter">pattern</replaceable> ]</term>
 
         <listitem>
         <para>
-	Shows all columns of <replaceable
-	class="parameter">relation</replaceable> (which could be a
-	table, view, index, or sequence), their types, and any special
+	For each relation (table, view, index, or sequence) matching the
+	<replaceable class="parameter">pattern</replaceable>, show all
+	columns, their types, and any special
 	attributes such as <literal>NOT NULL</literal> or defaults, if
-	any. If the relation is, in fact, a table, any defined indices,
-	primary keys, unique constraints and check constraints are also
-	listed. If the relation is a view, the view definition is also
-	shown.
+	any. Associated indexes, constraints, rules, and triggers are
+	also shown, as is the view definition if the relation is a view.
+	(<quote>Matching the pattern</> is defined below.)
 	</para>
 
 	<para>
@@ -753,7 +751,8 @@ testdb=>
 
 	<note>
 	<para>
-	If <command>\d</command> is called without any arguments, it is
+	If <command>\d</command> is used without a
+	<replaceable class="parameter">pattern</replaceable> argument, it is
 	equivalent to <command>\dtvs</command> which will show a list of
 	all tables, views, and sequences. This is purely a convenience
 	measure.
@@ -776,34 +775,35 @@ testdb=>
       </varlistentry>
 
       <varlistentry>
-        <term><literal>\dd</literal> [ <replaceable class="parameter">object</replaceable> ]</term>
+        <term><literal>\dd</literal> [ <replaceable class="parameter">pattern</replaceable> ]</term>
         <listitem>
         <para>
-        Shows the descriptions of <replaceable
-        class="parameter">object</replaceable> (which can be a regular
-        expression), or of all objects if no argument is given.
+        Shows the descriptions of objects matching the <replaceable
+        class="parameter">pattern</replaceable>, or of all visible objects if
+	no argument is given.  But in either case, only objects that have
+	a description are listed.
         (<quote>Object</quote> covers aggregates, functions, operators,
         types, relations (tables, views, indexes, sequences, large
         objects), rules, and triggers.) For example:
 <programlisting>
 => <userinput>\dd version</userinput>
-              Object descriptions
-  Name   |   What   |        Description
----------+----------+---------------------------
- version | function | PostgreSQL version string
+                     Object descriptions
+   Schema   |  Name   |  Object  |        Description
+------------+---------+----------+---------------------------
+ pg_catalog | version | function | PostgreSQL version string
 (1 row)
 </programlisting>
         </para>
 
         <para>
-        Descriptions for objects can be generated with the
+        Descriptions for objects can be created with the
         <command>COMMENT ON</command> <acronym>SQL</acronym> command.
 	</para>
 
         <note>
         <para>
         <productname>PostgreSQL</productname> stores the object
-        descriptions in the pg_description system table.
+        descriptions in the <structname>pg_description</> system table.
         </para>
         </note>
 
@@ -816,7 +816,7 @@ testdb=>
         <listitem>
         <para>
         Lists all available domains (derived types). If <replaceable
-        class="parameter">pattern</replaceable> (a regular expression)
+        class="parameter">pattern</replaceable>
         is specified, only matching domains are shown.
         </para>
         </listitem>
@@ -830,7 +830,7 @@ testdb=>
         <para>
         Lists available functions, together with their argument and
         return types. If <replaceable
-        class="parameter">pattern</replaceable> (a regular expression)
+        class="parameter">pattern</replaceable>
         is specified, only matching functions are shown. If the form
         <literal>\df+</literal> is used, additional information about
         each function, including language and description, is shown.
@@ -844,18 +844,17 @@ testdb=>
 
 	<listitem>
 	<para>
-	This is not the actual command name: The letters i, s, t, v, S
+	This is not the actual command name: the letters i, s, t, v, S
 	stand for index, sequence, table, view, and system table,
-	respectively. You can specify any or all of them in any order to
-	obtain a listing of them, together with who the owner is.
+	respectively. You can specify any or all of these letters, in any
+	order, to obtain a listing of all the matching objects.
+	If <quote>+</quote> is appended to the command name, each object is
+	listed with its associated description, if any.
 	</para>
 
 	<para>
-	If <replaceable class="parameter">pattern</replaceable> is
-	specified, it is a regular expression that restricts the listing
-	to those objects whose name matches. If one appends a
-	<quote>+</quote> to the command name, each object is listed with
-	its associated description, if any.
+	If a <replaceable class="parameter">pattern</replaceable> is
+	specified, only objects whose name matches the pattern are listed.
 	</para>
 	</listitem>
       </varlistentry>
@@ -873,12 +872,12 @@ testdb=>
 
 
       <varlistentry>
-        <term><literal>\do [ <replaceable class="parameter">name</replaceable> ]</literal></term>
+        <term><literal>\do [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
         <listitem>
         <para>
         Lists available operators with their operand and return types.
-        If <replaceable class="parameter">name</replaceable> is
-        specified, only operators with that name will be shown.
+	If a <replaceable class="parameter">pattern</replaceable> is
+	specified, only operators whose name matches the pattern are listed.
         </para>
         </listitem>
       </varlistentry>
@@ -888,9 +887,17 @@ testdb=>
         <term><literal>\dp</literal> [ <replaceable class="parameter">pattern</replaceable> ]</term>
 	<listitem>
 	<para>
-	This is an alias for <command>\z</command> which was included
-	for its greater mnemonic value (<quote>display
-	permissions</quote>).
+        Produces a list of all available tables with their
+        associated access permissions.
+	If a <replaceable class="parameter">pattern</replaceable> is
+	specified, only tables whose name matches the pattern are listed.
+	</para>
+
+	<para>
+	The commands <xref linkend="SQL-GRANT"> and
+	<xref linkend="SQL-REVOKE">
+	are used to set access permissions.  See <xref linkend="SQL-GRANT">
+	for more information.
 	</para>
 	</listitem>
       </varlistentry>
@@ -912,7 +919,7 @@ testdb=>
         <term><literal>\du [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
         <listitem>
         <para>
-        Lists all configured users or only those that match <replaceable
+        Lists all database users, or only those that match <replaceable
         class="parameter">pattern</replaceable>.
         </para>
         </listitem>
@@ -1608,57 +1615,23 @@ lo_import 152801
         <term><literal>\z</literal> [ <replaceable class="parameter">pattern</replaceable> ]</term>
         <listitem>
         <para>
-        Produces a list of all tables in the database with their
-        appropriate access permissions listed. If an argument is given
-        it is taken as a regular expression which limits the listing to
-        those tables which match it.
-        </para>
-
-	<para>
-<programlisting>
-test=&gt; <userinput>\z</userinput>
-Access permissions for database "test"
- Relation |           Access permissions
-----------+-------------------------------------
- my_table | {"=r","joe=arwR", "group staff=ar"}
-(1 row )
-</programlisting>
-        Read this as follows:
-
-	<itemizedlist>
-          <listitem>
-          <para>
-          <literal>"=r"</literal>: <literal>PUBLIC</literal> has read
-          (<command>SELECT</command>) permission on the table.
-	  </para>
-	  </listitem>
-
-	  <listitem>
-	  <para>
-	  <literal>"joe=arwR"</literal>: User <literal>joe</literal> has
-	  read, write (<command>UPDATE</command>,
-	  <command>DELETE</command>), <quote>append</quote>
-	  (<command>INSERT</command>) permissions, and permission to
-	  create rules on the table.
-	  </para>
-	  </listitem>
-
-	  <listitem>
-	  <para>
-	  <literal>"group staff=ar"</literal>: Group
-	  <literal>staff</literal> has <command>SELECT</command> and
-	  <command>INSERT</command> permission.
-	  </para>
-	  </listitem>
-	</itemizedlist>
+        Produces a list of all available tables with their
+        associated access permissions.
+	If a <replaceable class="parameter">pattern</replaceable> is
+	specified, only tables whose name matches the pattern are listed.
 	</para>
 
 	<para>
 	The commands <xref linkend="SQL-GRANT"> and
 	<xref linkend="SQL-REVOKE">
-	are used to set access permissions.
+	are used to set access permissions.  See <xref linkend="SQL-GRANT">
+	for more information.
 	</para>
 
+	<para>
+	This is an alias for <command>\dp</command> (<quote>display
+	permissions</quote>).
+        </para>
         </listitem>
       </varlistentry>
 
@@ -1688,6 +1661,46 @@ Access permissions for database "test"
 
     </variablelist>
   </para>
+
+  <para>
+   The various <literal>\d</> commands accept a <replaceable
+   class="parameter">pattern</replaceable> parameter to specify the
+   object name(s) to be displayed.  Patterns are interpreted similarly
+   to SQL identifiers, in that unquoted letters are forced to lowercase,
+   while double quotes (<literal>"</>) protect letters from case conversion
+   and allow incorporation of whitespace into the identifier.  Within
+   double quotes, paired double quotes reduce to a single double quote in
+   the resulting name.  For example, <literal>FOO"BAR"BAZ</> is interpreted
+   as <literal>fooBARbaz</>, and <literal>"A weird"" name"</> becomes
+   <literal>A weird" name</>.
+  </para>
+
+  <para>
+   More interestingly, <literal>\d</> patterns allow the use of
+   <literal>*</> to mean <quote>any sequence of characters</>, and
+   <literal>?</> to mean <quote>any single character</>.  (This notation
+   is comparable to Unix shell filename patterns.)  Advanced users can
+   also use regular-expression notations such as character classes, for
+   example <literal>[0-9]</> to match <quote>any digit</>.  To make any of
+   these pattern-matching characters be interpreted literally, surround it
+   with double quotes.
+  </para>
+
+  <para>
+   A pattern that contains an (unquoted) dot is interpreted as a schema
+   name pattern followed by an object name pattern.  For example,
+   <literal> \dt foo*.bar*</> displays all tables in schemas whose name
+   starts with <literal>foo</> and whose table name 
+   starts with <literal>bar</>.  If no dot appears, then the pattern
+   matches only objects that are visible in the current schema search path.
+  </para>
+
+  <para>
+   Whenever the <replaceable class="parameter">pattern</replaceable> parameter
+   is omitted completely, the <literal>\d</> commands display all objects
+   that are visible in the current schema search path.  To see all objects
+   in the database, use the pattern <literal>*.*</>.
+  </para>
  </refsect2>
 
  <refsect2>
@@ -2402,11 +2415,12 @@ $ ./configure --with-includes=/opt/gnu/include --with-libs=/opt/gnu/lib  ...
     <itemizedlist>
       <listitem>
       <para>
-      In some earlier life <application>psql</application> allowed the
-      first argument to start directly after the (single-letter)
-      command. For compatibility this is still supported to some extent
+      In an earlier life <application>psql</application> allowed the
+      first argument of a single-letter backslash command to start
+      directly after the command, without intervening whitespace. For
+      compatibility this is still supported to some extent,
       but I am not going to explain the details here as this use is
-      discouraged. But if you get strange messages, keep this in mind.
+      discouraged.  If you get strange messages, keep this in mind.
       For example
 <programlisting>
 testdb=> <userinput>\foo</userinput>
@@ -2421,7 +2435,8 @@ Field separator is "oo",
       <application>psql</application> only works smoothly with servers
       of the same version. That does not mean other combinations will
       fail outright, but subtle and not-so-subtle problems might come
-      up.
+      up.  Backslash commands are particularly likely to fail if the
+      server is of a different version.
       </para>
       </listitem>
 
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index f99f909f285217e040d5b2110963f42ec8563782..9be47a90c91b39c190a34a22f5390f88b04d4efc 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1,9 +1,9 @@
 /*
  * psql - the PostgreSQL interactive terminal
  *
- * Copyright 2000 by PostgreSQL Global Development Group
+ * Copyright 2000-2002 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.74 2002/07/18 02:02:30 ishii Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.75 2002/08/10 03:56:23 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "command.h"
@@ -54,7 +54,7 @@ enum option_type
 	OT_NORMAL,					/* normal case */
 	OT_SQLID,					/* treat as SQL identifier */
 	OT_SQLIDHACK,				/* SQL identifier, but don't downcase */
-	OT_FILEPIPE					/* it's a file or pipe */
+	OT_FILEPIPE					/* it's a filename or pipe */
 };
 
 static char *scan_option(char **string, enum option_type type,
@@ -328,10 +328,11 @@ exec_command(const char *cmd,
 	/* \d* commands */
 	else if (cmd[0] == 'd')
 	{
-		char	   *name;
+		char	   *pattern;
 		bool		show_verbose;
 
-		name = scan_option(&string, OT_SQLID, NULL, true);
+		/* We don't do SQLID reduction on the pattern yet */
+		pattern = scan_option(&string, OT_NORMAL, NULL, true);
 
 		show_verbose = strchr(cmd, '+') ? true : false;
 
@@ -339,51 +340,53 @@ exec_command(const char *cmd,
 		{
 			case '\0':
 			case '+':
-				if (name)
-					success = describeTableDetails(name, show_verbose);
+				if (pattern)
+					success = describeTableDetails(pattern, show_verbose);
 				else
 					/* standard listing of interesting things */
 					success = listTables("tvs", NULL, show_verbose);
 				break;
 			case 'a':
-				success = describeAggregates(name);
+				success = describeAggregates(pattern, show_verbose);
 				break;
 			case 'd':
-				success = objectDescription(name);
+				success = objectDescription(pattern);
 				break;
 			case 'f':
-				success = describeFunctions(name, show_verbose);
+				success = describeFunctions(pattern, show_verbose);
 				break;
 			case 'l':
 				success = do_lo_list();
 				break;
 			case 'o':
-				success = describeOperators(name);
+				success = describeOperators(pattern);
 				break;
 			case 'p':
-				success = permissionsList(name);
+				success = permissionsList(pattern);
 				break;
 			case 'T':
-				success = describeTypes(name, show_verbose);
+				success = describeTypes(pattern, show_verbose);
 				break;
 			case 't':
 			case 'v':
 			case 'i':
 			case 's':
 			case 'S':
-				success = listTables(&cmd[1], name, show_verbose);
+				success = listTables(&cmd[1], pattern, show_verbose);
 				break;
 			case 'u':
-				success = describeUsers(name);
+				success = describeUsers(pattern);
 				break;
 			case 'D':
-				success = listDomains(name);
+				success = listDomains(pattern);
 				break;
 
 			default:
 				status = CMD_UNKNOWN;
 		}
-		free(name);
+
+		if (pattern)
+			free(pattern);
 	}
 
 
@@ -815,13 +818,14 @@ exec_command(const char *cmd,
 		success = do_pset("expanded", NULL, &pset.popt, quiet);
 
 
-	/* \z -- list table rights (grant/revoke) */
+	/* \z -- list table rights (equivalent to \dp) */
 	else if (strcmp(cmd, "z") == 0)
 	{
-		char	   *opt = scan_option(&string, OT_SQLID, NULL, true);
+		char	   *pattern = scan_option(&string, OT_NORMAL, NULL, true);
 
-		success = permissionsList(opt);
-		free(opt);
+		success = permissionsList(pattern);
+		if (pattern)
+			free(pattern);
 	}
 
 	/* \! -- shell escape */
@@ -881,11 +885,27 @@ exec_command(const char *cmd,
 
 /*
  * scan_option()
+ *
+ * *string points to possible option string on entry; on exit, it's updated
+ * to point past the option string (if any).
+ *
+ * type tells what processing, if any, to perform on the option string;
+ * for example, if it's a SQL identifier, we want to downcase any unquoted
+ * letters.
+ *
+ * if quote is not NULL, *quote is set to 0 if no quoting was found, else
+ * the quote symbol.
+ *
+ * if semicolon is true, trailing semicolon(s) that would otherwise be taken
+ * as part of the option string will be stripped.
+ *
+ * Return value is NULL if no option found, else a malloc'd copy of the
+ * processed option value.
  */
 static char *
 scan_option(char **string, enum option_type type, char *quote, bool semicolon)
 {
-	unsigned int pos = 0;
+	unsigned int pos;
 	char	   *options_string;
 	char	   *return_val;
 
@@ -897,82 +917,27 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
 
 	options_string = *string;
 	/* skip leading whitespace */
-	pos += strspn(options_string + pos, " \t\n\r");
+	pos = strspn(options_string, " \t\n\r");
 
 	switch (options_string[pos])
 	{
 			/*
-			 * Double quoted string
+			 * End of line: no option present
 			 */
-		case '"':
-			{
-				unsigned int jj;
-				unsigned short int bslash_count = 0;
-
-				/* scan for end of quote */
-				for (jj = pos + 1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding))
-				{
-					if (options_string[jj] == '"' && bslash_count % 2 == 0)
-						break;
-
-					if (options_string[jj] == '\\')
-						bslash_count++;
-					else
-						bslash_count = 0;
-				}
-
-				if (options_string[jj] == 0)
-				{
-					psql_error("parse error at the end of line\n");
-					*string = &options_string[jj];
-					return NULL;
-				}
-
-				return_val = malloc(jj - pos + 2);
-				if (!return_val)
-				{
-					psql_error("out of memory\n");
-					exit(EXIT_FAILURE);
-				}
-
-				/*
-				 * If this is expected to be an SQL identifier like option
-				 * then we strip out the double quotes
-				 */
-
-				if (type == OT_SQLID || type == OT_SQLIDHACK)
-				{
-					unsigned int k,
-								cc;
-
-					bslash_count = 0;
-					cc = 0;
-					for (k = pos + 1; options_string[k]; k += PQmblen(&options_string[k], pset.encoding))
-					{
-						if (options_string[k] == '"' && bslash_count % 2 == 0)
-							break;
-
-						if (options_string[jj] == '\\')
-							bslash_count++;
-						else
-							bslash_count = 0;
-
-						return_val[cc++] = options_string[k];
-					}
-					return_val[cc] = '\0';
-				}
-				else
-				{
-					strncpy(return_val, &options_string[pos], jj - pos + 1);
-					return_val[jj - pos + 1] = '\0';
-				}
-
-				*string = options_string + jj + 1;
-				if (quote)
-					*quote = '"';
+		case '\0':
+			*string = &options_string[pos];
+			return NULL;
 
-				return return_val;
-			}
+			/*
+			 * Next command: treat like end of line
+			 *
+			 * XXX this means we can't conveniently accept options that
+			 * start with a backslash; therefore, option processing that
+			 * encourages use of backslashes is rather broken.
+			 */
+		case '\\':
+			*string = &options_string[pos];
+			return NULL;
 
 			/*
 			 * A single quote has a psql internal meaning, such as for
@@ -1015,7 +980,7 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
 		case '`':
 			{
 				bool		error = false;
-				FILE	   *fd = NULL;
+				FILE	   *fd;
 				char	   *file;
 				PQExpBufferData output;
 				char		buf[512];
@@ -1040,10 +1005,10 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
 					error = true;
 				}
 
+				initPQExpBuffer(&output);
+
 				if (!error)
 				{
-					initPQExpBuffer(&output);
-
 					do
 					{
 						result = fread(buf, 1, 512, fd);
@@ -1056,27 +1021,26 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
 						appendBinaryPQExpBuffer(&output, buf, result);
 					} while (!feof(fd));
 					appendPQExpBufferChar(&output, '\0');
+				}
 
-					if (pclose(fd) == -1)
-					{
-						psql_error("%s: %s\n", file, strerror(errno));
-						error = true;
-					}
+				if (fd && pclose(fd) == -1)
+				{
+					psql_error("%s: %s\n", file, strerror(errno));
+					error = true;
 				}
 
 				if (!error)
 				{
 					if (output.data[strlen(output.data) - 1] == '\n')
 						output.data[strlen(output.data) - 1] = '\0';
-				}
-
-				if (!error)
 					return_val = output.data;
+				}
 				else
 				{
 					return_val = xstrdup("");
 					termPQExpBuffer(&output);
 				}
+
 				options_string[pos + 1 + len] = '`';
 				*string = options_string + pos + len + 2;
 				if (quote)
@@ -1084,13 +1048,6 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
 				return return_val;
 			}
 
-			/*
-			 * end of line
-			 */
-		case 0:
-			*string = &options_string[pos];
-			return NULL;
-
 			/*
 			 * Variable substitution
 			 */
@@ -1109,17 +1066,10 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
 				return_val = xstrdup(value);
 				options_string[pos + token_end + 1] = save_char;
 				*string = &options_string[pos + token_end + 1];
+				/* XXX should we set *quote to ':' here? */
 				return return_val;
 			}
 
-			/*
-			 * Next command
-			 */
-		case '\\':
-			*string = options_string + pos;
-			return NULL;
-			break;
-
 			/*
 			 * | could be the beginning of a pipe if so, take rest of line
 			 * as command
@@ -1127,49 +1077,135 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
 		case '|':
 			if (type == OT_FILEPIPE)
 			{
-				*string += strlen(options_string + pos);
+				*string += strlen(*string);
 				return xstrdup(options_string + pos);
-				break;
 			}
 			/* fallthrough for other option types */
 
 			/*
-			 * A normal word
+			 * Default case: token extends to next whitespace, except that
+			 * whitespace within double quotes doesn't end the token.
+			 *
+			 * If we are processing the option as a SQL identifier, then
+			 * downcase unquoted letters and remove double-quotes --- but
+			 * doubled double-quotes become output double-quotes, per spec.
+			 *
+			 * Note that a string like FOO"BAR"BAZ will be converted to
+			 * fooBARbaz; this is somewhat inconsistent with the SQL spec,
+			 * which would have us parse it as several identifiers.  But
+			 * for psql's purposes, we want a string like "foo"."bar" to
+			 * be treated as one option, so there's little choice.
 			 */
 		default:
 			{
-				size_t		token_end;
+				bool		inquotes = false;
+				size_t		token_len;
 				char	   *cp;
 
-				token_end = strcspn(&options_string[pos], " \t\n\r");
-				return_val = malloc(token_end + 1);
+				/* Find end of option */
+
+				cp = &options_string[pos];
+				for (;;)
+				{
+					/* Find next quote, whitespace, or end of string */
+					cp += strcspn(cp, "\" \t\n\r");
+					if (inquotes)
+					{
+						if (*cp == '\0')
+						{
+							psql_error("parse error at the end of line\n");
+							*string = cp;
+							return NULL;
+						}
+						if (*cp == '"')
+							inquotes = false;
+						cp++;
+					}
+					else
+					{
+						if (*cp != '"')
+							break; /* whitespace or end of string */
+						if (quote)
+							*quote = '"';
+						inquotes = true;
+						cp++;
+					}
+				}
+
+				*string = cp;
+
+				/* Copy the option */
+				token_len = cp - &options_string[pos];
+
+				return_val = malloc(token_len + 1);
 				if (!return_val)
 				{
 					psql_error("out of memory\n");
 					exit(EXIT_FAILURE);
 				}
-				strncpy(return_val, &options_string[pos], token_end);
-				return_val[token_end] = 0;
 
-				/* Strip any trailing semi-colons for some types */
+				memcpy(return_val, &options_string[pos], token_len);
+				return_val[token_len] = '\0';
+
+				/* Strip any trailing semi-colons if requested */
 				if (semicolon)
 				{
-					int			i;
+					int		i;
+
+					for (i = token_len - 1;
+						 i >= 0 && return_val[i] == ';';
+						 i--)
+						/* skip */;
+
+					if (i < 0)
+					{
+						/* nothing left after stripping the semicolon... */
+						free(return_val);
+						return NULL;
+					}
 
-					for (i = strlen(return_val) - 1; i && return_val[i] == ';'; i--);
-					if (i < strlen(return_val) - 1)
+					if (i < token_len - 1)
 						return_val[i + 1] = '\0';
 				}
 
-				if (type == OT_SQLID)
-					for (cp = return_val; *cp; cp += PQmblen(cp, pset.encoding))
-						if (isupper((unsigned char) *cp))
-							*cp = tolower((unsigned char) *cp);
+				/*
+				 * If SQL identifier processing was requested,
+				 * then we strip out excess double quotes and downcase
+				 * unquoted letters.
+				 */
+				if (type == OT_SQLID || type == OT_SQLIDHACK)
+				{
+					inquotes = false;
+					cp = return_val;
+
+					while (*cp)
+					{
+						if (*cp == '"')
+						{
+							if (inquotes && cp[1] == '"')
+							{
+								/* Keep the first quote, remove the second */
+								cp++;
+							}
+							inquotes = !inquotes;
+							/* Collapse out quote at *cp */
+							memmove(cp, cp+1, strlen(cp));
+							/* do not advance cp */
+						}
+						else
+						{
+							if (!inquotes && type == OT_SQLID)
+							{
+								if (isupper((unsigned char) *cp))
+									*cp = tolower((unsigned char) *cp);
+							}
+							cp += PQmblen(cp, pset.encoding);
+						}
+					}
+				}
 
-				*string = &options_string[pos + token_end];
 				return return_val;
 			}
-
 	}
 }
 
@@ -1429,7 +1465,7 @@ test_superuser(const char *username)
 		return false;
 
 	initPQExpBuffer(&buf);
-	printfPQExpBuffer(&buf, "SELECT usesuper FROM pg_user WHERE usename = '%s'", username);
+	printfPQExpBuffer(&buf, "SELECT usesuper FROM pg_catalog.pg_user WHERE usename = '%s'", username);
 	res = PSQLexec(buf.data);
 	termPQExpBuffer(&buf);
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 8deffacb5fae43ff2c1684bc513aa41631eb67cb..59369972d49b5c254250922252b8e8637ee8ee98 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1,9 +1,9 @@
 /*
  * psql - the PostgreSQL interactive terminal
  *
- * Copyright 2000 by PostgreSQL Global Development Group
+ * Copyright 2000-2002 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.58 2002/08/09 18:06:57 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.59 2002/08/10 03:56:24 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "describe.h"
@@ -16,8 +16,34 @@
 #include "print.h"
 #include "variables.h"
 
+#include <ctype.h>
+
 #define _(x) gettext((x))
 
+static bool describeOneTableDetails(const char *schemaname,
+									const char *relationname,
+									const char *oid,
+									bool verbose);
+static void processNamePattern(PQExpBuffer buf, const char *pattern,
+							   bool have_where, bool force_escape,
+							   const char *schemavar, const char *namevar,
+							   const char *altnamevar, const char *visibilityrule);
+
+
+static void *
+xmalloc(size_t size)
+{
+	void	   *tmp;
+
+	tmp = malloc(size);
+	if (!tmp)
+	{
+		psql_error("out of memory\n");
+		exit(EXIT_FAILURE);
+	}
+	return tmp;
+}
+
 
 /*----------------
  * Handlers for various slash commands displaying some sort of list
@@ -29,10 +55,10 @@
 
 
 /* \da
- * takes an optional regexp to match specific aggregates by name
+ * Takes an optional regexp to select particular aggregates
  */
 bool
-describeAggregates(const char *name)
+describeAggregates(const char *pattern, bool verbose)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -45,21 +71,24 @@ describeAggregates(const char *name)
 	 * types and ones that work on all (denoted by input type = 0)
 	 */
 	printfPQExpBuffer(&buf,
-			 "SELECT p.proname AS \"%s\",\n"
+			 "SELECT n.nspname as \"%s\",\n"
+			 "  p.proname AS \"%s\",\n"
 			 "  CASE p.proargtypes[0]\n"
-			 "    WHEN 0 THEN CAST('%s' AS text)\n"
-			 "    ELSE format_type(p.proargtypes[0], NULL)\n"
+			 "    WHEN 0 THEN CAST('%s' AS pg_catalog.text)\n"
+			 "    ELSE pg_catalog.format_type(p.proargtypes[0], NULL)\n"
 			 "  END AS \"%s\",\n"
-			 "  obj_description(p.oid, 'pg_proc') as \"%s\"\n"
-			 "FROM pg_proc p\n"
+			 "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"\n"
+			 "FROM pg_catalog.pg_proc p\n"
+			 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
 			 "WHERE p.proisagg\n",
-			 _("Name"), _("(all types)"),
+			 _("Schema"), _("Name"), _("(all types)"),
 			 _("Data type"), _("Description"));
 
-	if (name)
-		appendPQExpBuffer(&buf, "  AND p.proname ~ '^%s'\n", name);
+	processNamePattern(&buf, pattern, true, false,
+					   "n.nspname", "p.proname", NULL,
+					   "pg_catalog.pg_function_is_visible(p.oid)");
 
-	appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
+	appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
 
 	res = PSQLexec(buf.data);
 	termPQExpBuffer(&buf);
@@ -77,10 +106,10 @@ describeAggregates(const char *name)
 
 
 /* \df
- * Takes an optional regexp to narrow down the function name
+ * Takes an optional regexp to select particular functions
  */
 bool
-describeFunctions(const char *name, bool verbose)
+describeFunctions(const char *pattern, bool verbose)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -88,15 +117,12 @@ describeFunctions(const char *name, bool verbose)
 
 	initPQExpBuffer(&buf);
 
-	/*
-	 * we skip in/out funcs by excluding functions that take some
-	 * arguments, but have no types defined for those arguments
-	 */
 	printfPQExpBuffer(&buf,
-			 "SELECT format_type(p.prorettype, NULL) as \"%s\",\n"
+			 "SELECT pg_catalog.format_type(p.prorettype, NULL) as \"%s\",\n"
+			 "  n.nspname as \"%s\",\n"
 			 "  p.proname as \"%s\",\n"
-			 "  oidvectortypes(p.proargtypes) as \"%s\"",
-			 _("Result data type"), _("Name"),
+			 "  pg_catalog.oidvectortypes(p.proargtypes) as \"%s\"",
+			 _("Result data type"), _("Schema"), _("Name"),
 			 _("Argument data types"));
 
 	if (verbose)
@@ -104,23 +130,35 @@ describeFunctions(const char *name, bool verbose)
 				 ",\n  u.usename as \"%s\",\n"
 				 "  l.lanname as \"%s\",\n"
 				 "  p.prosrc as \"%s\",\n"
-				 "  obj_description(p.oid, 'pg_proc') as \"%s\"",
+				 "  pg_catalog.obj_description(p.oid, 'pg_proc') as \"%s\"",
 				 _("Owner"), _("Language"),
 				 _("Source code"), _("Description"));
 
 	if (!verbose)
 		appendPQExpBuffer(&buf,
-			   "\nFROM pg_proc p\n"
-			   "WHERE p.prorettype <> 0 AND (pronargs = 0 OR oidvectortypes(p.proargtypes) <> '') AND NOT p.proisagg\n");
+			   "\nFROM pg_catalog.pg_proc p"
+			   "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n");
 	else
 		appendPQExpBuffer(&buf,
-			   "\nFROM pg_proc p,  pg_language l, pg_user u\n"
-			   "WHERE p.prolang = l.oid AND p.proowner = u.usesysid\n"
-			   "  AND p.prorettype <> 0 AND (pronargs = 0 OR oidvectortypes(p.proargtypes) <> '') AND NOT p.proisagg\n");
+			   "\nFROM pg_catalog.pg_proc p"
+			   "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace"
+			   "\n     LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang"
+			   "\n     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner\n");
 
-	if (name)
-		appendPQExpBuffer(&buf, "  AND p.proname ~ '^%s'\n", name);
-	appendPQExpBuffer(&buf, "ORDER BY 2, 1, 3;");
+	/*
+	 * we skip in/out funcs by excluding functions that take some
+	 * arguments, but have no types defined for those arguments
+	 */
+	appendPQExpBuffer(&buf,
+					  "WHERE p.prorettype <> 0\n"
+					  "      AND (p.pronargs = 0 OR pg_catalog.oidvectortypes(p.proargtypes) <> '')\n"
+					  "      AND NOT p.proisagg\n");
+
+	processNamePattern(&buf, pattern, true, false,
+					   "n.nspname", "p.proname", NULL,
+					   "pg_catalog.pg_function_is_visible(p.oid)");
+
+	appendPQExpBuffer(&buf, "ORDER BY 2, 3, 1, 4;");
 
 	res = PSQLexec(buf.data);
 	termPQExpBuffer(&buf);
@@ -143,7 +181,7 @@ describeFunctions(const char *name, bool verbose)
  * describe types
  */
 bool
-describeTypes(const char *name, bool verbose)
+describeTypes(const char *pattern, bool verbose)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -152,31 +190,37 @@ describeTypes(const char *name, bool verbose)
 	initPQExpBuffer(&buf);
 
 	printfPQExpBuffer(&buf,
-			 "SELECT format_type(t.oid, NULL) AS \"%s\",\n",
-			 _("Name"));
+			 "SELECT n.nspname as \"%s\",\n"
+			 "  pg_catalog.format_type(t.oid, NULL) AS \"%s\",\n",
+			 _("Schema"), _("Name"));
 	if (verbose)
 		appendPQExpBuffer(&buf,
 				 "  t.typname AS \"%s\",\n"
 				 "  CASE WHEN t.typlen = -1\n"
-				 "    THEN CAST('var' AS text)\n"
-				 "    ELSE CAST(t.typlen AS text)\n"
+				 "    THEN CAST('var' AS pg_catalog.text)\n"
+				 "    ELSE CAST(t.typlen AS pg_catalog.text)\n"
 				 "  END AS \"%s\",\n",
 				 _("Internal name"), _("Size"));
 	appendPQExpBuffer(&buf,
-			 "  obj_description(t.oid, 'pg_type') as \"%s\"\n",
+			 "  pg_catalog.obj_description(t.oid, 'pg_type') as \"%s\"\n",
 			 _("Description"));
 
+	appendPQExpBuffer(&buf, "FROM pg_catalog.pg_type t\n"
+			 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n");
+
 	/*
 	 * do not include array types (start with underscore), do not include
 	 * user relations (typrelid!=0)
 	 */
-	appendPQExpBuffer(&buf, "FROM pg_type t\nWHERE t.typrelid = 0 AND t.typname !~ '^_.*'\n");
+	appendPQExpBuffer(&buf, "WHERE t.typrelid = 0 AND t.typname !~ '^_'\n");
 
-	if (name)
-		/* accept either internal or external type name */
-		appendPQExpBuffer(&buf, "  AND (format_type(t.oid, NULL) ~ '^%s' OR t.typname ~ '^%s')\n", name, name);
+	/* Match name pattern against either internal or external name */
+	processNamePattern(&buf, pattern, true, false,
+					   "n.nspname", "t.typname",
+					   "pg_catalog.format_type(t.oid, NULL)",
+					   "pg_catalog.pg_type_is_visible(t.oid)");
 
-	appendPQExpBuffer(&buf, "ORDER BY 1;");
+	appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
 
 	res = PSQLexec(buf.data);
 	termPQExpBuffer(&buf);
@@ -197,7 +241,7 @@ describeTypes(const char *name, bool verbose)
 /* \do
  */
 bool
-describeOperators(const char *name)
+describeOperators(const char *pattern)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -206,17 +250,22 @@ describeOperators(const char *name)
 	initPQExpBuffer(&buf);
 
 	printfPQExpBuffer(&buf,
-			 "SELECT o.oprname AS \"%s\",\n"
-			 "  CASE WHEN o.oprkind='l' THEN NULL ELSE format_type(o.oprleft, NULL) END AS \"%s\",\n"
-			 "  CASE WHEN o.oprkind='r' THEN NULL ELSE format_type(o.oprright, NULL) END AS \"%s\",\n"
-			 "  format_type(o.oprresult, NULL) AS \"%s\",\n"
-			 "  coalesce(obj_description(o.oid, 'pg_operator'),"
-			 "           obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
-			 "FROM pg_operator o\n",
-			 _("Name"), _("Left arg type"), _("Right arg type"),
+			 "SELECT n.nspname as \"%s\",\n"
+			 "  o.oprname AS \"%s\",\n"
+			 "  CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS \"%s\",\n"
+			 "  CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS \"%s\",\n"
+			 "  pg_catalog.format_type(o.oprresult, NULL) AS \"%s\",\n"
+			 "  coalesce(pg_catalog.obj_description(o.oid, 'pg_operator'),\n"
+			 "           pg_catalog.obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
+			 "FROM pg_catalog.pg_operator o\n"
+			 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
+			 _("Schema"), _("Name"),
+			 _("Left arg type"), _("Right arg type"),
 			 _("Result type"), _("Description"));
-	if (name)
-		appendPQExpBuffer(&buf, "WHERE o.oprname = '%s'\n", name);
+
+	processNamePattern(&buf, pattern, false, true,
+					   "n.nspname", "o.oprname", NULL,
+					   "pg_catalog.pg_operator_is_visible(o.oid)");
 
 	appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3, 4;");
 
@@ -255,15 +304,16 @@ listAllDbs(bool desc)
 			 _("Name"), _("Owner"));
 #ifdef MULTIBYTE
 	appendPQExpBuffer(&buf,
-			 ",\n       pg_encoding_to_char(d.encoding) as \"%s\"",
+			 ",\n       pg_catalog.pg_encoding_to_char(d.encoding) as \"%s\"",
 			 _("Encoding"));
 #endif
 	if (desc)
 		appendPQExpBuffer(&buf,
-			 ",\n       obj_description(d.oid, 'pg_database') as \"%s\"",
+			 ",\n       pg_catalog.obj_description(d.oid, 'pg_database') as \"%s\"",
 				 _("Description"));
 	appendPQExpBuffer(&buf,
-	"\nFROM pg_database d LEFT JOIN pg_user u ON d.datdba = u.usesysid\n"
+					  "\nFROM pg_catalog.pg_database d"
+					  "\n  LEFT JOIN pg_catalog.pg_user u ON d.datdba = u.usesysid\n"
 		   "ORDER BY 1;");
 
 	res = PSQLexec(buf.data);
@@ -286,7 +336,7 @@ listAllDbs(bool desc)
  * \z (now also \dp -- perhaps more mnemonic)
  */
 bool
-permissionsList(const char *name)
+permissionsList(const char *pattern)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -294,17 +344,29 @@ permissionsList(const char *name)
 
 	initPQExpBuffer(&buf);
 
-	/* Currently, we ignore indexes since they have no meaningful rights */
+	/*
+	 * we ignore indexes and toast tables since they have no meaningful rights
+	 */
 	printfPQExpBuffer(&buf,
-			 "SELECT relname as \"%s\",\n"
-			 "       relacl as \"%s\"\n"
-			 "FROM   pg_class\n"
-			 "WHERE  relkind in ('r', 'v', 'S') AND\n"
-			 "       relname NOT LIKE 'pg$_%%' ESCAPE '$'\n",
-			 _("Table"), _("Access privileges"));
-	if (name)
-		appendPQExpBuffer(&buf, "  AND relname ~ '^%s'\n", name);
-	appendPQExpBuffer(&buf, "ORDER BY 1;");
+			 "SELECT n.nspname as \"%s\",\n"
+			 "  c.relname as \"%s\",\n"
+			 "  c.relacl as \"%s\"\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', 'S')\n",
+			 _("Schema"), _("Table"), _("Access privileges"));
+
+	/*
+	 * Unless a schema pattern is specified, we suppress system and temp
+	 * tables, since they normally aren't very interesting from a permissions
+	 * point of view.  You can see 'em by explicit request though,
+	 * eg with \z pg_catalog.*
+	 */
+	processNamePattern(&buf, pattern, true, false,
+					   "n.nspname", "c.relname", NULL,
+					   "pg_catalog.pg_table_is_visible(c.oid) AND n.nspname !~ '^pg_'");
+
+	appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
 
 	res = PSQLexec(buf.data);
 	if (!res)
@@ -335,7 +397,7 @@ permissionsList(const char *name)
  * lists of things, there are other \d? commands.
  */
 bool
-objectDescription(const char *object)
+objectDescription(const char *pattern)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -343,71 +405,123 @@ objectDescription(const char *object)
 
 	initPQExpBuffer(&buf);
 
-	printfPQExpBuffer(&buf,
-			 "SELECT DISTINCT tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n"
-			 "FROM (\n"
+	appendPQExpBuffer(&buf,
+			 "SELECT DISTINCT tt.nspname AS \"%s\", tt.name AS \"%s\", tt.object AS \"%s\", d.description AS \"%s\"\n"
+			 "FROM (\n",
+			  _("Schema"), _("Name"), _("Object"), _("Description"));
 
 	/* Aggregate descriptions */
+	appendPQExpBuffer(&buf,
 			 "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
-	  "  CAST(p.proname AS text) as name, CAST('%s' AS text) as object\n"
-			 "  FROM pg_proc p\n"
-		"  WHERE p.proisagg\n"
+			 "  n.nspname as nspname,\n"
+			 "  CAST(p.proname AS pg_catalog.text) as name,"
+			 "  CAST('%s' AS pg_catalog.text) as object\n"
+			 "  FROM pg_catalog.pg_proc p\n"
+			 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
+			 "  WHERE p.proisagg\n",
+			 _("aggregate"));
+	processNamePattern(&buf, pattern, true, false,
+					   "n.nspname", "p.proname", NULL,
+					   "pg_catalog.pg_function_is_visible(p.oid)");
 
 	/* Function descriptions (except in/outs for datatypes) */
+	appendPQExpBuffer(&buf,
 			 "UNION ALL\n"
 			 "  SELECT p.oid as oid, p.tableoid as tableoid,\n"
-	  "  CAST(p.proname AS text) as name, CAST('%s' AS text) as object\n"
-			 "  FROM pg_proc p\n"
-		"  WHERE (p.pronargs = 0 or oidvectortypes(p.proargtypes) <> '') AND NOT p.proisagg\n"
+			 "  n.nspname as nspname,\n"
+			 "  CAST(p.proname AS pg_catalog.text) as name,"
+			 "  CAST('%s' AS pg_catalog.text) as object\n"
+			 "  FROM pg_catalog.pg_proc p\n"
+			 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace\n"
+		"  WHERE (p.pronargs = 0 or pg_catalog.oidvectortypes(p.proargtypes) <> '') AND NOT p.proisagg\n",
+			 _("function"));
+	processNamePattern(&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) */
+	appendPQExpBuffer(&buf,
 			 "UNION ALL\n"
 			 "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
-	  "  CAST(o.oprname AS text) as name, CAST('%s' AS text) as object\n"
-			 "  FROM pg_operator o\n"
+			 "  n.nspname as nspname,\n"
+			 "  CAST(o.oprname AS pg_catalog.text) as name,"
+			 "  CAST('%s' AS pg_catalog.text) as object\n"
+			 "  FROM pg_catalog.pg_operator o\n"
+			 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace\n",
+			 _("operator"));
+	processNamePattern(&buf, pattern, false, false,
+					   "n.nspname", "o.oprname", NULL,
+					   "pg_catalog.pg_operator_is_visible(o.oid)");
 
 	/* Type description */
+	appendPQExpBuffer(&buf,
 			 "UNION ALL\n"
 			 "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
-	 "  format_type(t.oid, NULL) as name, CAST('%s' AS text) as object\n"
-			 "  FROM pg_type t\n"
+			 "  n.nspname as nspname,\n"
+			 "  pg_catalog.format_type(t.oid, NULL) as name,"
+			 "  CAST('%s' AS pg_catalog.text) as object\n"
+			 "  FROM pg_catalog.pg_type t\n"
+			 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace\n",
+			 _("data type"));
+	processNamePattern(&buf, pattern, false, false,
+					   "n.nspname", "pg_catalog.format_type(t.oid, NULL)", NULL,
+					   "pg_catalog.pg_type_is_visible(t.oid)");
 
 	/* Relation (tables, views, indexes, sequences) descriptions */
+	appendPQExpBuffer(&buf,
 			 "UNION ALL\n"
 			 "  SELECT c.oid as oid, c.tableoid as tableoid,\n"
-			 "  CAST(c.relname AS text) as name,\n"
+			 "  n.nspname as nspname,\n"
+			 "  CAST(c.relname AS pg_catalog.text) as name,\n"
 			 "  CAST(\n"
 			 "    CASE c.relkind WHEN 'r' THEN '%s' WHEN 'v' THEN '%s' WHEN 'i' THEN '%s' WHEN 'S' THEN '%s' END"
-			 "  AS text) as object\n"
-			 "  FROM pg_class c\n"
+			 "  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",
+			 _("table"), _("view"), _("index"), _("sequence"));
+	processNamePattern(&buf, pattern, true, false,
+					   "n.nspname", "c.relname", NULL,
+					   "pg_catalog.pg_table_is_visible(c.oid)");
 
 	/* Rule description (ignore rules for views) */
+	appendPQExpBuffer(&buf,
 			 "UNION ALL\n"
 			 "  SELECT r.oid as oid, r.tableoid as tableoid,\n"
-			 "  CAST(r.rulename AS text) as name, CAST('%s' AS text) as object\n"
-			 "  FROM pg_rewrite r\n"
-			 "  WHERE r.rulename != '_RETURN'\n"
+			 "  n.nspname as nspname,\n"
+			 "  CAST(r.rulename AS pg_catalog.text) as name,"
+			 "  CAST('%s' AS pg_catalog.text) as object\n"
+			 "  FROM pg_catalog.pg_rewrite r\n"
+			 "       JOIN pg_catalog.pg_class c ON c.oid = r.ev_class\n"
+			 "       LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
+			 "  WHERE r.rulename != '_RETURN'\n",
+			 _("rule"));
+	/* XXX not sure what to do about visibility rule here? */
+	processNamePattern(&buf, pattern, true, false,
+					   "n.nspname", "r.rulename", NULL,
+					   "pg_catalog.pg_table_is_visible(c.oid)");
 
 	/* Trigger description */
+	appendPQExpBuffer(&buf,
 			 "UNION ALL\n"
 			 "  SELECT t.oid as oid, t.tableoid as tableoid,\n"
-	   "  CAST(t.tgname AS text) as name, CAST('%s' AS text) as object\n"
-			 "  FROM pg_trigger t\n"
-
-			 ") AS tt,\n"
-			 "pg_description d\n"
-			 "WHERE tt.oid = d.objoid and tt.tableoid = d.classoid and d.objsubid = 0\n",
-
-			 _("Name"), _("Object"), _("Description"),
-			 _("aggregate"), _("function"), _("operator"),
-			 _("data type"), _("table"), _("view"),
-			 _("index"), _("sequence"), _("rule"),
-			 _("trigger")
-		);
-
-	if (object)
-		appendPQExpBuffer(&buf, "  AND tt.name ~ '^%s'\n", object);
-	appendPQExpBuffer(&buf, "ORDER BY 1;");
+			 "  n.nspname as nspname,\n"
+			 "  CAST(t.tgname AS pg_catalog.text) as name,"
+			 "  CAST('%s' AS pg_catalog.text) as object\n"
+			 "  FROM pg_catalog.pg_trigger t\n"
+			 "       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",
+			 _("trigger"));
+	/* XXX not sure what to do about visibility rule here? */
+	processNamePattern(&buf, pattern, false, false,
+					   "n.nspname", "t.tgname", NULL,
+					   "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBuffer(&buf,
+			 ") AS tt\n"
+			 "  JOIN pg_catalog.pg_description d ON (tt.oid = d.objoid and tt.tableoid = d.classoid and d.objsubid = 0)\n");
+
+	appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
 
 	res = PSQLexec(buf.data);
 	termPQExpBuffer(&buf);
@@ -428,35 +542,84 @@ objectDescription(const char *object)
 /*
  * describeTableDetails (for \d)
  *
- * Unfortunately, the information presented here is so complicated that it cannot
- * be done in a single query. So we have to assemble the printed table by hand
- * and pass it to the underlying printTable() function.
- *
+ * This routine finds the tables to be displayed, and calls
+ * describeOneTableDetails for each one.
  */
-
-static void *
-xmalloc(size_t size)
+bool
+describeTableDetails(const char *pattern, bool verbose)
 {
-	void	   *tmp;
+	PQExpBufferData buf;
+	PGresult   *res;
+	int			i;
 
-	tmp = malloc(size);
-	if (!tmp)
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+			 "SELECT c.oid,\n"
+			 "  n.nspname,\n"
+			 "  c.relname\n"
+			 "FROM pg_catalog.pg_class c\n"
+			 "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
+
+	processNamePattern(&buf, pattern, false, false,
+					   "n.nspname", "c.relname", NULL,
+					   "pg_catalog.pg_table_is_visible(c.oid)");
+
+	appendPQExpBuffer(&buf, "ORDER BY 2, 3;");
+
+	res = PSQLexec(buf.data);
+	termPQExpBuffer(&buf);
+	if (!res)
+		return false;
+
+	if (PQntuples(res) == 0)
 	{
-		psql_error("out of memory\n");
-		exit(EXIT_FAILURE);
+		if (!QUIET())
+			fprintf(stderr, _("Did not find any relation named \"%s\".\n"),
+					pattern);
+		PQclear(res);
+		return false;
 	}
-	return tmp;
-}
 
+	for (i = 0; i < PQntuples(res); i++)
+	{
+		const char *oid;
+		const char *nspname;
+		const char *relname;
+
+		oid = PQgetvalue(res, i, 0);
+		nspname = PQgetvalue(res, i, 1);
+		relname = PQgetvalue(res, i, 2);
 
-bool
-describeTableDetails(const char *name, bool desc)
+		if (!describeOneTableDetails(nspname, relname, oid, verbose))
+		{
+			PQclear(res);
+			return false;
+		}
+	}
+
+	PQclear(res);
+	return true;
+}
+
+/*
+ * describeOneTableDetails (for \d)
+ *
+ * Unfortunately, the information presented here is so complicated that it
+ * cannot be done in a single query. So we have to assemble the printed table
+ * by hand and pass it to the underlying printTable() function.
+ */
+static bool
+describeOneTableDetails(const char *schemaname,
+						const char *relationname,
+						const char *oid,
+						bool verbose)
 {
 	PQExpBufferData buf;
 	PGresult   *res = NULL;
 	printTableOpt myopt = pset.popt.topt;
 	int			i;
-	const char *view_def = NULL;
+	char *view_def = NULL;
 	const char *headers[5];
 	char	  **cells = NULL;
 	char	  **footers = NULL;
@@ -481,8 +644,8 @@ describeTableDetails(const char *name, bool desc)
 	/* Get general table info */
 	printfPQExpBuffer(&buf,
 					  "SELECT relhasindex, relkind, relchecks, reltriggers, relhasrules\n"
-					  "FROM pg_class WHERE relname='%s'",
-					  name);
+					  "FROM pg_catalog.pg_class WHERE oid = '%s'",
+					  oid);
 	res = PSQLexec(buf.data);
 	if (!res)
 		goto error_return;
@@ -491,9 +654,8 @@ describeTableDetails(const char *name, bool desc)
 	if (PQntuples(res) == 0)
 	{
 		if (!QUIET())
-			fprintf(stderr, _("Did not find any relation named \"%s\".\n"), name);
-		PQclear(res);
-		res = NULL;
+			fprintf(stderr, _("Did not find any relation with oid %s.\n"),
+					oid);
 		goto error_return;
 	}
 
@@ -505,7 +667,6 @@ describeTableDetails(const char *name, bool desc)
 	tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
 	PQclear(res);
 
-
 	headers[0] = _("Column");
 	headers[1] = _("Type");
 	cols = 2;
@@ -516,7 +677,7 @@ describeTableDetails(const char *name, bool desc)
 		headers[cols - 1] = _("Modifiers");
 	}
 
-	if (desc)
+	if (verbose)
 	{
 		cols++;
 		headers[cols - 1] = _("Description");
@@ -524,19 +685,19 @@ describeTableDetails(const char *name, bool desc)
 
 	headers[cols] = NULL;
 
-
 	/* Get column info (index requires additional checks) */
 	if (tableinfo.relkind == 'i')
-		printfPQExpBuffer(&buf, "SELECT\n  CASE i.indproc WHEN ('-'::regproc) THEN a.attname\n  ELSE SUBSTR(pg_get_indexdef(attrelid),\n  POSITION('(' in pg_get_indexdef(attrelid)))\n  END, ");
+		printfPQExpBuffer(&buf, "SELECT\n  CASE i.indproc WHEN ('-'::pg_catalog.regproc) THEN a.attname\n  ELSE SUBSTR(pg_catalog.pg_get_indexdef(attrelid),\n  POSITION('(' in pg_catalog.pg_get_indexdef(attrelid)))\n  END,");
 	else
-		printfPQExpBuffer(&buf, "SELECT a.attname, ");
-	appendPQExpBuffer(&buf, "format_type(a.atttypid, a.atttypmod), a.attnotnull, a.atthasdef, a.attnum");
-	if (desc)
-		appendPQExpBuffer(&buf, ", col_description(a.attrelid, a.attnum)");
-	appendPQExpBuffer(&buf, "\nFROM pg_class c, pg_attribute a");
+		printfPQExpBuffer(&buf, "SELECT a.attname,");
+	appendPQExpBuffer(&buf, "\n  pg_catalog.format_type(a.atttypid, a.atttypmod),\n"
+					  "  a.attnotnull, a.atthasdef, a.attnum");
+	if (verbose)
+		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_index i");
-	appendPQExpBuffer(&buf, "\nWHERE c.relname = '%s'\n  AND a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = c.oid", name);
+		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");
@@ -546,11 +707,11 @@ describeTableDetails(const char *name, bool desc)
 		goto error_return;
 
 	/* Check if table is a view */
-	if (tableinfo.hasrules)
+	if (tableinfo.relkind == 'v')
 	{
 		PGresult   *result;
 
-		printfPQExpBuffer(&buf, "SELECT definition FROM pg_views WHERE viewname = '%s'", name);
+		printfPQExpBuffer(&buf, "SELECT pg_catalog.pg_get_viewdef('%s'::pg_catalog.oid)", oid);
 		result = PSQLexec(buf.data);
 		if (!result)
 		{
@@ -561,10 +722,10 @@ describeTableDetails(const char *name, bool desc)
 
 		if (PQntuples(result) > 0)
 			view_def = xstrdup(PQgetvalue(result, 0, 0));
+
 		PQclear(result);
 	}
 
-
 	/* Generate table cells to be printed */
 	cells = xmalloc((PQntuples(res) * cols + 1) * sizeof(*cells));
 	cells[PQntuples(res) * cols] = NULL;		/* end of list */
@@ -593,9 +754,9 @@ describeTableDetails(const char *name, bool desc)
 				PGresult   *result;
 
 				printfPQExpBuffer(&buf,
-								  "SELECT substring(d.adsrc for 128) FROM pg_attrdef d, pg_class c\n"
-								  "WHERE c.relname = '%s' AND c.oid = d.adrelid AND d.adnum = %s",
-								  name, PQgetvalue(res, i, 4));
+								  "SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d\n"
+								  "WHERE d.adrelid = '%s' AND d.adnum = %s",
+								  oid, PQgetvalue(res, i, 4));
 
 				result = PSQLexec(buf.data);
 
@@ -609,7 +770,7 @@ describeTableDetails(const char *name, bool desc)
 		}
 
 		/* Description */
-		if (desc)
+		if (verbose)
 			cells[i * cols + cols - 1] = PQgetvalue(res, i, 5);
 	}
 
@@ -617,25 +778,32 @@ describeTableDetails(const char *name, bool desc)
 	switch (tableinfo.relkind)
 	{
 		case 'r':
-			printfPQExpBuffer(&title, _("Table \"%s\""), name);
+			printfPQExpBuffer(&title, _("Table \"%s.%s\""),
+							  schemaname, relationname);
 			break;
 		case 'v':
-			printfPQExpBuffer(&title, _("View \"%s\""), name);
+			printfPQExpBuffer(&title, _("View \"%s.%s\""),
+							  schemaname, relationname);
 			break;
 		case 'S':
-			printfPQExpBuffer(&title, _("Sequence \"%s\""), name);
+			printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
+							  schemaname, relationname);
 			break;
 		case 'i':
-			printfPQExpBuffer(&title, _("Index \"%s\""), name);
+			printfPQExpBuffer(&title, _("Index \"%s.%s\""),
+							  schemaname, relationname);
 			break;
 		case 's':
-			printfPQExpBuffer(&title, _("Special relation \"%s\""), name);
+			printfPQExpBuffer(&title, _("Special relation \"%s.%s\""),
+							  schemaname, relationname);
 			break;
 		case 't':
-			printfPQExpBuffer(&title, _("TOAST table \"%s\""), name);
+			printfPQExpBuffer(&title, _("TOAST table \"%s.%s\""),
+							  schemaname, relationname);
 			break;
 		default:
-			printfPQExpBuffer(&title, _("?%c? \"%s\""), tableinfo.relkind, name);
+			printfPQExpBuffer(&title, _("?%c? \"%s.%s\""),
+							  tableinfo.relkind, schemaname, relationname);
 			break;
 	}
 
@@ -646,11 +814,11 @@ describeTableDetails(const char *name, bool desc)
 		PGresult   *result;
 		printfPQExpBuffer(&buf,
 						  "SELECT i.indisunique, i.indisprimary, a.amname, c2.relname,\n"
-						  "pg_get_expr(i.indpred,i.indrelid)\n"
-						  "FROM pg_index i, pg_class c, pg_class c2, pg_am a\n"
-						  "WHERE i.indexrelid = c.oid AND c.relname = '%s' AND c.relam = a.oid\n"
+						  "  pg_catalog.pg_get_expr(i.indpred, i.indrelid)\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",
-						  name);
+						  oid);
 
 		result = PSQLexec(buf.data);
 		if (!result)
@@ -679,7 +847,10 @@ describeTableDetails(const char *name, bool desc)
 				resetPQExpBuffer(&tmpbuf);
 			appendPQExpBuffer(&tmpbuf, "%s, ", indamname);
 
-			appendPQExpBuffer(&tmpbuf, _("for table \"%s\""), indtable);
+			/* we assume here that index and table are in same schema */
+			appendPQExpBuffer(&tmpbuf, _("for table \"%s.%s\""),
+							  schemaname, indtable);
+
 			if (strlen(indpred))
 				appendPQExpBuffer(&tmpbuf, ", predicate %s", indpred);
 
@@ -697,15 +868,14 @@ describeTableDetails(const char *name, bool desc)
 		int			rule_count = 0;
 		int			count_footers = 0;
 
-		/* count rules */
+		/* count rules other than the view rule */
 		if (tableinfo.hasrules)
 		{
 			printfPQExpBuffer(&buf,
 					"SELECT r.rulename\n"
-					"FROM pg_rewrite r, pg_class c\n"
-					"WHERE c.relname = '%s' AND c.oid = r.ev_class\n"
-					"AND r.rulename != '_RETURN'",
-					name);
+					"FROM pg_catalog.pg_rewrite r\n"
+					"WHERE r.ev_class = '%s' AND r.rulename != '_RETURN'",
+					 oid);
 			result = PSQLexec(buf.data);
 			if (!result)
 				goto error_return;
@@ -756,13 +926,12 @@ describeTableDetails(const char *name, bool desc)
 		if (tableinfo.hasindex)
 		{
 			printfPQExpBuffer(&buf,
-					"SELECT c2.relname, i.indisprimary, i.indisunique,\n"
-					"SUBSTR(pg_get_indexdef(i.indexrelid),\n"
-					"POSITION('USING ' IN pg_get_indexdef(i.indexrelid))+5)\n"
-					"FROM pg_class c, pg_class c2, pg_index i\n"
-					"WHERE c.relname = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
+					"SELECT c2.relname, i.indisprimary, i.indisunique, "
+					"pg_catalog.pg_get_indexdef(i.indexrelid)\n"
+					"FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
+					"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",
-					name);
+					oid);
 			result1 = PSQLexec(buf.data);
 			if (!result1)
 				goto error_return;
@@ -775,10 +944,9 @@ describeTableDetails(const char *name, bool desc)
 		{
 			printfPQExpBuffer(&buf,
 					"SELECT consrc, conname\n"
-					"FROM pg_constraint r, pg_class c\n"
-					"WHERE c.relname='%s' AND c.oid = r.conrelid\n"
-					"AND r.contype = 'c'",
-					name);
+					"FROM pg_catalog.pg_constraint r\n"
+					"WHERE r.conrelid = '%s' AND r.contype = 'c'",
+					oid);
 			result2 = PSQLexec(buf.data);
 			if (!result2)
 				goto error_return;
@@ -791,9 +959,9 @@ describeTableDetails(const char *name, bool desc)
 		{
 			printfPQExpBuffer(&buf,
 					"SELECT r.rulename\n"
-					"FROM pg_rewrite r, pg_class c\n"
-					"WHERE c.relname='%s' AND c.oid = r.ev_class",
-					name);
+					"FROM pg_catalog.pg_rewrite r\n"
+					"WHERE r.ev_class = '%s'",
+					oid);
 			result3 = PSQLexec(buf.data);
 			if (!result3)
 				goto error_return;
@@ -806,9 +974,9 @@ describeTableDetails(const char *name, bool desc)
 		{
 			printfPQExpBuffer(&buf,
 					"SELECT t.tgname\n"
-					"FROM pg_trigger t, pg_class c\n"
-					"WHERE c.relname='%s' AND c.oid = t.tgrelid",
-					name);
+					"FROM pg_catalog.pg_trigger t\n"
+					"WHERE t.tgrelid = '%s'",
+					oid);
 			result4 = PSQLexec(buf.data);
 			if (!result4)
 				goto error_return;
@@ -823,11 +991,15 @@ describeTableDetails(const char *name, bool desc)
 		for (i = 0; i < index_count; i++)
 		{
 			char	   *s = _("Indexes");
+			const char *indexdef;
+			const char *usingpos;
 	
 			if (i == 0)
-				printfPQExpBuffer(&buf, "%s: %s", s, PQgetvalue(result1, i, 0));
+				printfPQExpBuffer(&buf, "%s: %s", s,
+								  PQgetvalue(result1, i, 0));
 			else
-				printfPQExpBuffer(&buf, "%*s  %s", (int) strlen(s), "", PQgetvalue(result1, i, 0));
+				printfPQExpBuffer(&buf, "%*s  %s", (int) strlen(s), "",
+								  PQgetvalue(result1, i, 0));
 
 			/* Label as primary key or unique (but not both) */
 			appendPQExpBuffer(&buf,
@@ -838,7 +1010,12 @@ describeTableDetails(const char *name, bool desc)
 							   : ""));
 
 			/* Everything after "USING" is echoed verbatim */
-			appendPQExpBuffer(&buf, "%s", PQgetvalue(result1,i,3));
+			indexdef = PQgetvalue(result1, i, 3);
+			usingpos = strstr(indexdef, " USING ");
+			if (usingpos)
+				indexdef = usingpos + 7;
+
+			appendPQExpBuffer(&buf, " %s", indexdef);
 
 			if (i < index_count - 1)
 				appendPQExpBuffer(&buf, ",");
@@ -931,6 +1108,9 @@ error_return:
 		free(footers);
 	}
 
+	if (view_def)
+		free(view_def);
+
 	if (res)
 		PQclear(res);
 
@@ -939,13 +1119,12 @@ error_return:
 
 
 /*
- * \du [user]
+ * \du
  *
- * Describes users, possibly based on a simplistic prefix search on the
- * argument.
+ * Describes users.  Any schema portion of the pattern is ignored.
  */
 bool
-describeUsers(const char *name)
+describeUsers(const char *pattern)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -956,18 +1135,20 @@ describeUsers(const char *name)
 	printfPQExpBuffer(&buf,
 			 "SELECT u.usename AS \"%s\",\n"
 			 "  u.usesysid AS \"%s\",\n"
-			 "  CASE WHEN u.usesuper AND u.usecreatedb THEN CAST('%s' AS text)\n"
-			 "       WHEN u.usesuper THEN CAST('%s' AS text)\n"
-			 "       WHEN u.usecreatedb THEN CAST('%s' AS text)\n"
-			 "       ELSE CAST('' AS text)\n"
+			 "  CASE WHEN u.usesuper AND u.usecreatedb THEN CAST('%s' AS pg_catalog.text)\n"
+			 "       WHEN u.usesuper THEN CAST('%s' AS pg_catalog.text)\n"
+			 "       WHEN u.usecreatedb THEN CAST('%s' AS pg_catalog.text)\n"
+			 "       ELSE CAST('' AS pg_catalog.text)\n"
 			 "  END AS \"%s\"\n"
-			 "FROM pg_user u\n",
+			 "FROM pg_catalog.pg_user u\n",
 			 _("User name"), _("User ID"),
 			 _("superuser, create database"),
 			 _("superuser"), _("create database"),
 			 _("Attributes"));
-	if (name)
-		appendPQExpBuffer(&buf, "WHERE u.usename ~ '^%s'\n", name);
+
+	processNamePattern(&buf, pattern, false, false,
+					   NULL, "u.usename", NULL, NULL);
+
 	appendPQExpBuffer(&buf, "ORDER BY 1;");
 
 	res = PSQLexec(buf.data);
@@ -990,26 +1171,22 @@ describeUsers(const char *name)
  *
  * handler for \d, \dt, etc.
  *
- * The infotype is an array of characters, specifying what info is desired:
+ * tabtypes is an array of characters, specifying what info is desired:
  * t - tables
  * i - indexes
  * v - views
  * s - sequences
- * S - systems tables (~ '^pg_')
+ * S - system tables (~ '^pg_')
  * (any order of the above is fine)
- *
- * Note: For some reason it always happens to people that their tables have owners
- * that are no longer in pg_user; consequently they wouldn't show up here. The code
- * tries to fix this the painful way, hopefully outer joins will be done sometime.
  */
 bool
-listTables(const char *infotype, const char *name, bool desc)
+listTables(const char *tabtypes, const char *pattern, bool verbose)
 {
-	bool		showTables = strchr(infotype, 't') != NULL;
-	bool		showIndexes = strchr(infotype, 'i') != NULL;
-	bool		showViews = strchr(infotype, 'v') != NULL;
-	bool		showSeq = strchr(infotype, 's') != NULL;
-	bool		showSystem = strchr(infotype, 'S') != NULL;
+	bool		showTables = strchr(tabtypes, 't') != NULL;
+	bool		showIndexes = strchr(tabtypes, 'i') != NULL;
+	bool		showViews = strchr(tabtypes, 'v') != NULL;
+	bool		showSeq = strchr(tabtypes, 's') != NULL;
+	bool		showSystem = strchr(tabtypes, 'S') != NULL;
 
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -1025,28 +1202,31 @@ listTables(const char *infotype, const char *name, bool desc)
 			 "  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"
 			 "  u.usename as \"%s\"",
-			 _("Schema"), _("Name"), _("table"), _("view"), _("index"), _("sequence"),
+			 _("Schema"), _("Name"),
+			 _("table"), _("view"), _("index"), _("sequence"),
 			 _("special"), _("Type"), _("Owner"));
 
-	if (desc)
+	if (verbose)
 		appendPQExpBuffer(&buf,
-				 ",\n  obj_description(c.oid, 'pg_class') as \"%s\"",
+				 ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
 				 _("Description"));
+
     if (showIndexes)
 		appendPQExpBuffer(&buf,
 						  ",\n c2.relname as \"%s\""
-						  "\nFROM pg_class c, pg_class c2, pg_index i, pg_user u, pg_namespace n\n"
-						  "WHERE c.relowner = u.usesysid\n"
-						  "AND c.relnamespace = n.oid\n"
-						  "AND i.indrelid = c2.oid AND i.indexrelid = c.oid\n",
+						  "\nFROM pg_catalog.pg_class c"
+						  "\n     JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid"
+						  "\n     JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid"
+						  "\n     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner"
+						  "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n",
 						  _("Table"));
 	else
 		appendPQExpBuffer(&buf,
-						  "\nFROM pg_class c, pg_user u, pg_namespace n\n"
-						  "WHERE c.relowner = u.usesysid\n"
-						  "AND c.relnamespace = n.oid\n");
+						  "\nFROM pg_catalog.pg_class c"
+						  "\n     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner"
+						  "\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n");
 
-	appendPQExpBuffer(&buf, "AND c.relkind IN (");
+	appendPQExpBuffer(&buf, "WHERE c.relkind IN (");
 	if (showTables)
 		appendPQExpBuffer(&buf, "'r',");
 	if (showViews)
@@ -1060,13 +1240,18 @@ listTables(const char *infotype, const char *name, bool desc)
 	appendPQExpBuffer(&buf, "''");			/* dummy */
 	appendPQExpBuffer(&buf, ")\n");
 
+	/*
+	 * Unless showSystem is specified, we suppress system tables, ie, those
+	 * in pg_catalog and pg_toast.  (We don't want to hide temp tables though.)
+	 */
 	if (showSystem)
-		appendPQExpBuffer(&buf, "  AND n.nspname ~ '^pg_'\n");
+		processNamePattern(&buf, pattern, true, false,
+						   "n.nspname", "c.relname", NULL,
+						   "pg_catalog.pg_table_is_visible(c.oid)");
 	else
-		appendPQExpBuffer(&buf, "  AND n.nspname !~ '^pg_'\n");
-
-	if (name)
-		appendPQExpBuffer(&buf, "  AND c.relname ~ '^%s'\n", name);
+		processNamePattern(&buf, pattern, true, false,
+						   "n.nspname", "c.relname", NULL,
+						   "pg_catalog.pg_table_is_visible(c.oid) AND n.nspname <> 'pg_catalog' AND n.nspname <> 'pg_toast'");
 
 	appendPQExpBuffer(&buf, "ORDER BY 1,2;");
 
@@ -1077,7 +1262,7 @@ listTables(const char *infotype, const char *name, bool desc)
 
 	if (PQntuples(res) == 0 && !QUIET())
 	{
-		if (name)
+		if (pattern)
 			fprintf(pset.queryFout, _("No matching relations found.\n"));
 		else
 			fprintf(pset.queryFout, _("No relations found.\n"));
@@ -1096,13 +1281,12 @@ listTables(const char *infotype, const char *name, bool desc)
 
 
 /*
- * \dD [domain]
+ * \dD
  *
- * Describes domains, possibly based on a simplistic prefix search on the
- * argument.
+ * Describes domains.
  */
 bool
-listDomains(const char *name)
+listDomains(const char *pattern)
 {
 	PQExpBufferData buf;
 	PGresult   *res;
@@ -1111,21 +1295,27 @@ listDomains(const char *name)
 	initPQExpBuffer(&buf);
 
 	printfPQExpBuffer(&buf,
-		 "SELECT t.typname as \"%s\",\n"
-		 "       format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
+		 "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"
-		 "FROM pg_type t\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",
+		 _("Schema"),
 		 _("Name"),
 		 _("Type"),
 		 _("Modifier"));
-	if (name)
-		appendPQExpBuffer(&buf, "AND t.typname ~ '^%s'\n", name);
-	appendPQExpBuffer(&buf, "ORDER BY 1;");
+
+	processNamePattern(&buf, pattern, true, false,
+					   "n.nspname", "t.typname", NULL,
+					   "pg_catalog.pg_type_is_visible(t.oid)");
+
+	appendPQExpBuffer(&buf, "ORDER BY 1, 2;");
 
 	res = PSQLexec(buf.data);
 	termPQExpBuffer(&buf);
@@ -1140,3 +1330,184 @@ listDomains(const char *name)
 	PQclear(res);
 	return true;
 }
+
+/*
+ * processNamePattern
+ *
+ * Scan a wildcard-pattern option and generate appropriate WHERE clauses
+ * to limit the set of objects returned.  The WHERE clauses are appended
+ * to buf.
+ *
+ * pattern: user-specified pattern option to a \d command, or NULL if none.
+ * have_where: true if caller already emitted WHERE.
+ * force_escape: always quote regexp special characters, even outside quotes.
+ * schemavar: name of WHERE variable to match against a schema-name pattern.
+ * Can be NULL if no schema.
+ * namevar: name of WHERE variable to match against an object-name pattern.
+ * altnamevar: NULL, or name of an alternate variable to match against name.
+ * visibilityrule: clause to use if we want to restrict to visible objects
+ * (for example, "pg_catalog.pg_table_is_visible(p.oid)").  Can be NULL.
+ */
+static void
+processNamePattern(PQExpBuffer buf, const char *pattern,
+				   bool have_where, bool force_escape,
+				   const char *schemavar, const char *namevar,
+				   const char *altnamevar, const char *visibilityrule)
+{
+	PQExpBufferData schemabuf;
+	PQExpBufferData namebuf;
+	bool		inquotes;
+	const char *cp;
+	int			i;
+
+#define WHEREAND() \
+	(appendPQExpBuffer(buf, have_where ? "      AND " : "WHERE "), have_where = true)
+
+	if (pattern == NULL)
+	{
+		/* Default: select all visible objects */
+		if (visibilityrule)
+		{
+			WHEREAND();
+			appendPQExpBuffer(buf, "%s\n", visibilityrule);
+		}
+		return;
+	}
+
+	initPQExpBuffer(&schemabuf);
+	initPQExpBuffer(&namebuf);
+
+	/*
+	 * Parse the pattern, converting quotes and lower-casing unquoted
+	 * letters; we assume this was NOT done by scan_option.  Also, adjust
+	 * shell-style wildcard characters into regexp notation.
+	 */
+	inquotes = false;
+	cp = pattern;
+
+	while (*cp)
+	{
+		if (*cp == '"')
+		{
+			if (inquotes && cp[1] == '"')
+			{
+				/* emit one quote */
+				appendPQExpBufferChar(&namebuf, '"');
+				cp++;
+			}
+			inquotes = !inquotes;
+			cp++;
+		}
+		else if (!inquotes && isupper((unsigned char) *cp))
+		{
+			appendPQExpBufferChar(&namebuf,
+								  tolower((unsigned char) *cp));
+			cp++;
+		}
+		else if (!inquotes && *cp == '*')
+		{
+			appendPQExpBuffer(&namebuf, ".*");
+			cp++;
+		}
+		else if (!inquotes && *cp == '?')
+		{
+			appendPQExpBufferChar(&namebuf, '.');
+			cp++;
+		}
+		else if (!inquotes && *cp == '.')
+		{
+			/* Found schema/name separator, move current pattern to schema */
+			resetPQExpBuffer(&schemabuf);
+			appendPQExpBufferStr(&schemabuf, namebuf.data);
+			resetPQExpBuffer(&namebuf);
+			cp++;
+		}
+		else
+		{
+			/*
+			 * Ordinary data character, transfer to pattern
+			 *
+			 * Inside double quotes, or at all times if parsing an operator
+			 * name, quote regexp special characters with a backslash to avoid
+			 * regexp errors.  Outside quotes, however, let them pass through
+			 * as-is; this lets knowledgeable users build regexp expressions
+			 * that are more powerful than shell-style patterns.
+			 */
+			if ((inquotes || force_escape) &&
+				strchr("|*+?()[]{}.^$\\", *cp))
+				appendPQExpBuffer(&namebuf, "\\\\");
+
+			/* Ensure chars special to string literals are passed properly */
+			if (*cp == '\'' || *cp == '\\')
+				appendPQExpBufferChar(&namebuf, *cp);
+
+			i = PQmblen(cp, pset.encoding);
+			while (i--)
+			{
+				appendPQExpBufferChar(&namebuf, *cp);
+				cp++;
+			}
+		}
+	}
+
+	/*
+	 * Now decide what we need to emit.
+	 */
+	if (schemabuf.len > 0)
+	{
+		/* We have a schema pattern, so constrain the schemavar */
+
+		appendPQExpBufferChar(&schemabuf, '$');
+		/* Optimize away ".*$", and possibly the whole pattern */
+		if (schemabuf.len >= 3 &&
+			strcmp(schemabuf.data + (schemabuf.len-3), ".*$") == 0)
+			schemabuf.data[schemabuf.len-3] = '\0';
+
+		if (schemabuf.data[0] && schemavar)
+		{
+			WHEREAND();
+			appendPQExpBuffer(buf, "%s ~ '^%s'\n",
+							  schemavar, schemabuf.data);
+		}
+	}
+	else
+	{
+		/* No schema pattern given, so select only visible objects */
+		if (visibilityrule)
+		{
+			WHEREAND();
+			appendPQExpBuffer(buf, "%s\n", visibilityrule);
+		}
+	}
+
+	if (namebuf.len > 0)
+	{
+		/* We have a name pattern, so constrain the namevar(s) */
+
+		appendPQExpBufferChar(&namebuf, '$');
+		/* Optimize away ".*$", and possibly the whole pattern */
+		if (namebuf.len >= 3 &&
+			strcmp(namebuf.data + (namebuf.len-3), ".*$") == 0)
+			namebuf.data[namebuf.len-3] = '\0';
+
+		if (namebuf.data[0])
+		{
+			WHEREAND();
+			if (altnamevar)
+				appendPQExpBuffer(buf,
+								  "(%s ~ '^%s'\n"
+								  "        OR %s ~ '^%s')\n",
+								  namevar, namebuf.data,
+								  altnamevar, namebuf.data);
+			else
+				appendPQExpBuffer(buf,
+								  "%s ~ '^%s'\n",
+								  namevar, namebuf.data);
+		}
+	}
+
+	termPQExpBuffer(&schemabuf);
+	termPQExpBuffer(&namebuf);
+
+#undef WHEREAND
+}
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 4c95733912a389fec3b4365fcb6853281631d3f3..41edcd98fbb9804b438b38a7e73259b1da42c448 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -1,9 +1,9 @@
 /*
  * psql - the PostgreSQL interactive terminal
  *
- * Copyright 2000 by PostgreSQL Global Development Group
+ * Copyright 2000-2002 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/describe.h,v 1.16 2002/03/19 02:32:21 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/describe.h,v 1.17 2002/08/10 03:56:24 tgl Exp $
  */
 #ifndef DESCRIBE_H
 #define DESCRIBE_H
@@ -11,36 +11,36 @@
 #include "settings.h"
 
 /* \da */
-bool		describeAggregates(const char *name);
+bool		describeAggregates(const char *pattern, bool verbose);
 
 /* \df */
-bool		describeFunctions(const char *name, bool verbose);
+bool		describeFunctions(const char *pattern, bool verbose);
 
 /* \dT */
-bool		describeTypes(const char *name, bool verbose);
+bool		describeTypes(const char *pattern, bool verbose);
 
 /* \do */
-bool		describeOperators(const char *name);
+bool		describeOperators(const char *pattern);
 
 /* \du */
-bool		describeUsers(const char *name);
+bool		describeUsers(const char *pattern);
 
 /* \z (or \dp) */
-bool		permissionsList(const char *name);
+bool		permissionsList(const char *pattern);
 
 /* \dd */
-bool		objectDescription(const char *object);
+bool		objectDescription(const char *pattern);
 
 /* \d foo */
-bool		describeTableDetails(const char *name, bool desc);
+bool		describeTableDetails(const char *pattern, bool verbose);
 
 /* \l */
 bool		listAllDbs(bool desc);
 
 /* \dt, \di, \ds, \dS, etc. */
-bool		listTables(const char *infotype, const char *name, bool desc);
+bool		listTables(const char *tabtypes, const char *pattern, bool verbose);
 
 /* \dD */
-bool		listDomains(const char *name);
+bool		listDomains(const char *pattern);
 
 #endif   /* DESCRIBE_H */
diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c
index d134156477fb65aa9901ca04c2156f87e0877b7b..0e90fceb6c4711e5056706115e66a63e1e6fef1b 100644
--- a/src/bin/psql/large_obj.c
+++ b/src/bin/psql/large_obj.c
@@ -1,9 +1,9 @@
 /*
  * psql - the PostgreSQL interactive terminal
  *
- * Copyright 2000 by PostgreSQL Global Development Group
+ * Copyright 2000-2002 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.19 2002/03/06 06:10:31 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.20 2002/08/10 03:56:24 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "large_obj.h"
@@ -209,9 +209,10 @@ do_lo_import(const char *filename_arg, const char *comment_arg)
 			return false;
 		}
 		sprintf(cmdbuf,
-				"INSERT INTO pg_description VALUES ('%u', "
-		   "(SELECT oid FROM pg_class WHERE relname = 'pg_largeobject'),"
-				" 0, '", loid);
+				"INSERT INTO pg_catalog.pg_description VALUES ('%u', "
+				"'pg_catalog.pg_largeobject'::regclass, "
+				"0, '",
+				loid);
 		bufptr = cmdbuf + strlen(cmdbuf);
 		for (i = 0; i < slen; i++)
 		{
@@ -310,8 +311,8 @@ do_lo_unlink(const char *loid_arg)
 	/* XXX ought to replace this with some kind of COMMENT command */
 	if (pset.issuper)
 	{
-		sprintf(buf, "DELETE FROM pg_description WHERE objoid = '%u' "
-				"AND classoid = (SELECT oid FROM pg_class WHERE relname = 'pg_largeobject')",
+		sprintf(buf, "DELETE FROM pg_catalog.pg_description WHERE objoid = '%u' "
+				"AND classoid = 'pg_catalog.pg_largeobject'::regclass",
 				loid);
 		if (!(res = PSQLexec(buf)))
 		{
@@ -356,8 +357,8 @@ do_lo_list(void)
 	printQueryOpt myopt = pset.popt;
 
 	snprintf(buf, sizeof(buf),
-			 "SELECT loid as \"ID\", obj_description(loid, 'pg_largeobject') as \"%s\"\n"
-			 "FROM (SELECT DISTINCT loid FROM pg_largeobject) x\n"
+			 "SELECT loid as \"ID\", pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
+			 "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
 			 "ORDER BY \"ID\"",
 			 gettext("Description"));
 
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 3c0da27b759162c26fd1adcfd7bc66c7053f8bb7..e7fd3df45f95b1366f1473c4da3078ad4e7db97d 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1,9 +1,9 @@
 /*
  * psql - the PostgreSQL interactive terminal
  *
- * Copyright 2000 by PostgreSQL Global Development Group
+ * Copyright 2000-2002 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.55 2002/08/04 05:01:57 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.56 2002/08/10 03:56:24 tgl Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -118,11 +118,20 @@ initialize_readline(void)
 }
 
 
+/*
+ * Queries to get lists of names of various kinds of things, possibly
+ * restricted to names matching a partially entered name.  In these queries,
+ * the %s will be replaced by the text entered so far, the %d by its length.
+ */
+
+#define Query_for_list_of_tables "SELECT relname FROM pg_catalog.pg_class WHERE (relkind='r' or relkind='v') and substr(relname,1,%d)='%s' and pg_catalog.pg_table_is_visible(oid)"
+#define Query_for_list_of_indexes "SELECT relname FROM pg_catalog.pg_class WHERE relkind='i' and substr(relname,1,%d)='%s' and pg_catalog.pg_table_is_visible(oid)"
+#define Query_for_list_of_databases "SELECT datname FROM pg_catalog.pg_database WHERE substr(datname,1,%d)='%s'"
+#define Query_for_list_of_attributes "SELECT a.attname FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c WHERE c.oid = a.attrelid and a.attnum>0 and not a.attisdropped and substr(a.attname,1,%d)='%s' and c.relname='%s' and pg_catalog.pg_table_is_visible(c.oid)"
+#define Query_for_list_of_users "SELECT usename FROM pg_catalog.pg_user WHERE substr(usename,1,%d)='%s'"
+
 /* This is a list of all "things" in Pgsql, which can show up after CREATE or
    DROP; and there is also a query to get a list of them.
-   The %s will be replaced by the text entered so far, the %d by its length.
-   If you change the order here or insert things, make sure to also adjust the
-   referencing macros below.
 */
 typedef struct
 {
@@ -131,37 +140,29 @@ typedef struct
 } pgsql_thing_t;
 
 pgsql_thing_t words_after_create[] = {
-	{"AGGREGATE", "SELECT distinct proname FROM pg_catalog.pg_proc WHERE proisagg AND substr(proname,1,%d)='%s'"},
-	{"DATABASE", "SELECT datname FROM pg_catalog.pg_database WHERE substr(datname,1,%d)='%s'"},
-	{"FUNCTION", "SELECT distinct proname FROM pg_catalog.pg_proc WHERE substr(proname,1,%d)='%s'"},
+	{"AGGREGATE", "SELECT DISTINCT proname FROM pg_catalog.pg_proc WHERE proisagg AND substr(proname,1,%d)='%s'"},
+	{"DATABASE", Query_for_list_of_databases},
+	{"FUNCTION", "SELECT DISTINCT proname FROM pg_catalog.pg_proc WHERE substr(proname,1,%d)='%s'"},
 	{"GROUP", "SELECT groname FROM pg_catalog.pg_group WHERE substr(groname,1,%d)='%s'"},
-	{"INDEX", "SELECT relname FROM pg_catalog.pg_class WHERE relkind='i' and substr(relname,1,%d)='%s'"},
+	{"INDEX", Query_for_list_of_indexes},
 	{"OPERATOR", NULL},			/* Querying for this is probably not such a good idea. */
 	{"RULE", "SELECT rulename FROM pg_catalog.pg_rules WHERE substr(rulename,1,%d)='%s'"},
 	{"SCHEMA", "SELECT nspname FROM pg_catalog.pg_namespace WHERE substr(nspname,1,%d)='%s'"},
 	{"SEQUENCE", "SELECT relname FROM pg_catalog.pg_class WHERE relkind='S' and substr(relname,1,%d)='%s'"},
-	{"TABLE", "SELECT relname FROM pg_catalog.pg_class WHERE (relkind='r' or relkind='v') and substr(relname,1,%d)='%s'"},
+	{"TABLE", Query_for_list_of_tables},
 	{"TEMP", NULL},				/* for CREATE TEMP TABLE ... */
 	{"TRIGGER", "SELECT tgname FROM pg_catalog.pg_trigger WHERE substr(tgname,1,%d)='%s'"},
 	{"TYPE", "SELECT typname FROM pg_catalog.pg_type WHERE substr(typname,1,%d)='%s'"},
 	{"UNIQUE", NULL},			/* for CREATE UNIQUE INDEX ... */
-	{"USER", "SELECT usename FROM pg_catalog.pg_user WHERE substr(usename,1,%d)='%s'"},
+	{"USER", Query_for_list_of_users},
 	{"VIEW", "SELECT viewname FROM pg_catalog.pg_views WHERE substr(viewname,1,%d)='%s'"},
 	{NULL, NULL}				/* end of list */
 };
 
 
-/* The query to get a list of tables and a list of indexes, which are used at
-   various places. */
-#define Query_for_list_of_tables words_after_create[9].query
-#define Query_for_list_of_indexes words_after_create[4].query
-#define Query_for_list_of_databases words_after_create[1].query
-#define Query_for_list_of_attributes "SELECT a.attname FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c WHERE c.oid = a.attrelid and a.attnum>0 and not a.attisdropped and substr(a.attname,1,%d)='%s' and c.relname='%s'"
-#define Query_for_list_of_users words_after_create[14].query
-
 /* A couple of macros to ease typing. You can use these to complete the given
    string with
-   1) The results from a query you pass it. (Perhaps one of those right above?)
+   1) The results from a query you pass it. (Perhaps one of those above?)
    2) The items from a null-pointer-terminated list.
    3) A string constant
    4) The list of attributes to the given table.
@@ -375,7 +376,7 @@ psql_completion(char *text, int start, int end)
 												 * queries. */
 
 		if (snprintf(query_buffer, BUF_SIZE,
-					 "SELECT c1.relname FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid and c2.relname='%s'",
+					 "SELECT c1.relname FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid and c2.relname='%s' and pg_catalog.pg_table_is_visible(c2.oid)",
 					 prev2_wd) == -1)
 			ERROR_QUERY_TOO_LONG;
 		else
@@ -389,7 +390,8 @@ psql_completion(char *text, int start, int end)
 	{
 		char	   *list_COMMENT[] =
 		{"DATABASE", "INDEX", "RULE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW",
-		"COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", NULL};
+		 "COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", "CONSTRAINT",
+		 "DOMAIN", NULL};
 
 		COMPLETE_WITH_LIST(list_COMMENT);
 	}
@@ -440,7 +442,7 @@ psql_completion(char *text, int start, int end)
 	/* Complete USING with an index method */
 	else if (strcasecmp(prev_wd, "USING") == 0)
 	{
-		char	   *index_mth[] = {"BTREE", "RTREE", "HASH", NULL};
+		char	   *index_mth[] = {"BTREE", "RTREE", "HASH", "GIST", NULL};
 
 		COMPLETE_WITH_LIST(index_mth);
 	}
@@ -553,7 +555,7 @@ psql_completion(char *text, int start, int end)
 	/* Complete GRANT/REVOKE with a list of privileges */
 	else if (strcasecmp(prev_wd, "GRANT") == 0 || strcasecmp(prev_wd, "REVOKE") == 0)
 	{
-		char	   *list_privileg[] = {"SELECT", "INSERT", "UPDATE", "DELETE", "RULE", "ALL", NULL};
+		char	   *list_privileg[] = {"SELECT", "INSERT", "UPDATE", "DELETE", "RULE", "REFERENCES", "TRIGGER", "CREATE", "TEMPORARY", "EXECUTE", "USAGE", "ALL", NULL};
 
 		COMPLETE_WITH_LIST(list_privileg);
 	}
@@ -563,14 +565,15 @@ psql_completion(char *text, int start, int end)
 
 	/*
 	 * Complete GRANT/REVOKE <sth> ON with a list of tables, views,
-	 * schema, sequences, and indexes
+	 * sequences, and indexes
+	 *
+	 * XXX should also offer DATABASE, FUNCTION, LANGUAGE, SCHEMA here
 	 */
 	else if ((strcasecmp(prev3_wd, "GRANT") == 0 || strcasecmp(prev3_wd, "REVOKE") == 0) &&
 			 strcasecmp(prev_wd, "ON") == 0)
 		COMPLETE_WITH_QUERY("SELECT relname FROM pg_catalog.pg_class "
 							"WHERE relkind in ('r','i','S','v') AND "
-							"substr(relname,1,%d)='%s' UNION "
-							"SELECT nspname FROM pg_catalog.pg_namespace;");
+							"substr(relname,1,%d)='%s' AND pg_catalog.pg_table_is_visible(oid)");
 	/* Complete "GRANT * ON * " with "TO" */
 	else if (strcasecmp(prev4_wd, "GRANT") == 0 && strcasecmp(prev2_wd, "ON") == 0)
 		COMPLETE_WITH_CONST("TO");
@@ -745,7 +748,7 @@ psql_completion(char *text, int start, int end)
 
 /* UNLISTEN */
 	else if (strcasecmp(prev_wd, "UNLISTEN") == 0)
-		COMPLETE_WITH_QUERY("SELECT relname FROM pg_catalog.pg_listener WHERE substr(relname,1,%d)='%s' UNION SELECT '*'::text");
+		COMPLETE_WITH_QUERY("SELECT relname FROM pg_catalog.pg_listener WHERE substr(relname,1,%d)='%s' UNION SELECT '*'::name");
 
 /* UPDATE */
 	/* If prev. word is UPDATE suggest a list of tables */
@@ -765,7 +768,7 @@ psql_completion(char *text, int start, int end)
 
 /* VACUUM */
 	else if (strcasecmp(prev_wd, "VACUUM") == 0)
-		COMPLETE_WITH_QUERY("SELECT relname FROM pg_catalog.pg_class WHERE relkind='r' and substr(relname,1,%d)='%s' UNION SELECT 'FULL'::text UNION SELECT 'ANALYZE'::text");
+		COMPLETE_WITH_QUERY("SELECT relname FROM pg_catalog.pg_class WHERE relkind='r' and substr(relname,1,%d)='%s' and pg_catalog.pg_table_is_visible(oid) UNION SELECT 'FULL'::name UNION SELECT 'ANALYZE'::name");
 	else if (strcasecmp(prev2_wd, "VACUUM") == 0 && (strcasecmp(prev_wd, "FULL") == 0 || strcasecmp(prev_wd, "ANALYZE") == 0))
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables);