psql backslash commands are schema-aware. Pattern matching behavior

follows recent pghackers discussion.  This commit includes all the
relevant fixes from Greg Mullane's patch of 24-June.
This commit is contained in:
Tom Lane 2002-08-10 03:56:24 +00:00
parent 6ce4a4e3e1
commit 039cb47988
7 changed files with 944 additions and 517 deletions

View 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

View 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>

View 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;
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);

File diff suppressed because it is too large Load Diff

View 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
@ -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 */

View 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"));

View 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);