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