]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
psql backslash commands are schema-aware. Pattern matching behavior
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 10 Aug 2002 03:56:24 +0000 (03:56 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 10 Aug 2002 03:56:24 +0000 (03:56 +0000)
follows recent pghackers discussion.  This commit includes all the
relevant fixes from Greg Mullane's patch of 24-June.

doc/src/sgml/ref/grant.sgml
doc/src/sgml/ref/psql-ref.sgml
src/bin/psql/command.c
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/large_obj.c
src/bin/psql/tab-complete.c

index a1384409cf3b521d00a4ecbb6b3384e95c8edf47..5a66973e32d0cea72c6144bd2c80cc7cf678fdca 100644 (file)
@@ -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
index b9f8554abfb6a7d6fd14267a6f2a67ba448ea516..48370d1e2146c720d22985989ee268a65939d8d6 100644 (file)
@@ -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>
 
index f99f909f285217e040d5b2110963f42ec8563782..9be47a90c91b39c190a34a22f5390f88b04d4efc 100644 (file)
@@ -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);
 
index 8deffacb5fae43ff2c1684bc513aa41631eb67cb..59369972d49b5c254250922252b8e8637ee8ee98 100644 (file)
@@ -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"
 #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
 
 
 /* \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
+}
index 4c95733912a389fec3b4365fcb6853281631d3f3..41edcd98fbb9804b438b38a7e73259b1da42c448 100644 (file)
@@ -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
 #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 */
index d134156477fb65aa9901ca04c2156f87e0877b7b..0e90fceb6c4711e5056706115e66a63e1e6fef1b 100644 (file)
@@ -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"));
 
index 3c0da27b759162c26fd1adcfd7bc66c7053f8bb7..e7fd3df45f95b1366f1473c4da3078ad4e7db97d 100644 (file)
@@ -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);