aboutsummaryrefslogtreecommitdiff
path: root/src/bin/psql/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/psql/command.c')
-rw-r--r--src/bin/psql/command.c310
1 files changed, 173 insertions, 137 deletions
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index f99f909f285..9be47a90c91 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1,9 +1,9 @@
/*
* psql - the PostgreSQL interactive terminal
*
- * Copyright 2000 by PostgreSQL Global Development Group
+ * Copyright 2000-2002 by PostgreSQL Global Development Group
*
- * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.74 2002/07/18 02:02:30 ishii Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.75 2002/08/10 03:56:23 tgl Exp $
*/
#include "postgres_fe.h"
#include "command.h"
@@ -54,7 +54,7 @@ enum option_type
OT_NORMAL, /* normal case */
OT_SQLID, /* treat as SQL identifier */
OT_SQLIDHACK, /* SQL identifier, but don't downcase */
- OT_FILEPIPE /* it's a file or pipe */
+ OT_FILEPIPE /* it's a filename or pipe */
};
static char *scan_option(char **string, enum option_type type,
@@ -328,10 +328,11 @@ exec_command(const char *cmd,
/* \d* commands */
else if (cmd[0] == 'd')
{
- char *name;
+ char *pattern;
bool show_verbose;
- name = scan_option(&string, OT_SQLID, NULL, true);
+ /* We don't do SQLID reduction on the pattern yet */
+ pattern = scan_option(&string, OT_NORMAL, NULL, true);
show_verbose = strchr(cmd, '+') ? true : false;
@@ -339,51 +340,53 @@ exec_command(const char *cmd,
{
case '\0':
case '+':
- if (name)
- success = describeTableDetails(name, show_verbose);
+ if (pattern)
+ success = describeTableDetails(pattern, show_verbose);
else
/* standard listing of interesting things */
success = listTables("tvs", NULL, show_verbose);
break;
case 'a':
- success = describeAggregates(name);
+ success = describeAggregates(pattern, show_verbose);
break;
case 'd':
- success = objectDescription(name);
+ success = objectDescription(pattern);
break;
case 'f':
- success = describeFunctions(name, show_verbose);
+ success = describeFunctions(pattern, show_verbose);
break;
case 'l':
success = do_lo_list();
break;
case 'o':
- success = describeOperators(name);
+ success = describeOperators(pattern);
break;
case 'p':
- success = permissionsList(name);
+ success = permissionsList(pattern);
break;
case 'T':
- success = describeTypes(name, show_verbose);
+ success = describeTypes(pattern, show_verbose);
break;
case 't':
case 'v':
case 'i':
case 's':
case 'S':
- success = listTables(&cmd[1], name, show_verbose);
+ success = listTables(&cmd[1], pattern, show_verbose);
break;
case 'u':
- success = describeUsers(name);
+ success = describeUsers(pattern);
break;
case 'D':
- success = listDomains(name);
+ success = listDomains(pattern);
break;
default:
status = CMD_UNKNOWN;
}
- free(name);
+
+ if (pattern)
+ free(pattern);
}
@@ -815,13 +818,14 @@ exec_command(const char *cmd,
success = do_pset("expanded", NULL, &pset.popt, quiet);
- /* \z -- list table rights (grant/revoke) */
+ /* \z -- list table rights (equivalent to \dp) */
else if (strcmp(cmd, "z") == 0)
{
- char *opt = scan_option(&string, OT_SQLID, NULL, true);
+ char *pattern = scan_option(&string, OT_NORMAL, NULL, true);
- success = permissionsList(opt);
- free(opt);
+ success = permissionsList(pattern);
+ if (pattern)
+ free(pattern);
}
/* \! -- shell escape */
@@ -881,11 +885,27 @@ exec_command(const char *cmd,
/*
* scan_option()
+ *
+ * *string points to possible option string on entry; on exit, it's updated
+ * to point past the option string (if any).
+ *
+ * type tells what processing, if any, to perform on the option string;
+ * for example, if it's a SQL identifier, we want to downcase any unquoted
+ * letters.
+ *
+ * if quote is not NULL, *quote is set to 0 if no quoting was found, else
+ * the quote symbol.
+ *
+ * if semicolon is true, trailing semicolon(s) that would otherwise be taken
+ * as part of the option string will be stripped.
+ *
+ * Return value is NULL if no option found, else a malloc'd copy of the
+ * processed option value.
*/
static char *
scan_option(char **string, enum option_type type, char *quote, bool semicolon)
{
- unsigned int pos = 0;
+ unsigned int pos;
char *options_string;
char *return_val;
@@ -897,82 +917,27 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
options_string = *string;
/* skip leading whitespace */
- pos += strspn(options_string + pos, " \t\n\r");
+ pos = strspn(options_string, " \t\n\r");
switch (options_string[pos])
{
/*
- * Double quoted string
+ * End of line: no option present
*/
- case '"':
- {
- unsigned int jj;
- unsigned short int bslash_count = 0;
-
- /* scan for end of quote */
- for (jj = pos + 1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding))
- {
- if (options_string[jj] == '"' && bslash_count % 2 == 0)
- break;
-
- if (options_string[jj] == '\\')
- bslash_count++;
- else
- bslash_count = 0;
- }
-
- if (options_string[jj] == 0)
- {
- psql_error("parse error at the end of line\n");
- *string = &options_string[jj];
- return NULL;
- }
-
- return_val = malloc(jj - pos + 2);
- if (!return_val)
- {
- psql_error("out of memory\n");
- exit(EXIT_FAILURE);
- }
-
- /*
- * If this is expected to be an SQL identifier like option
- * then we strip out the double quotes
- */
-
- if (type == OT_SQLID || type == OT_SQLIDHACK)
- {
- unsigned int k,
- cc;
-
- bslash_count = 0;
- cc = 0;
- for (k = pos + 1; options_string[k]; k += PQmblen(&options_string[k], pset.encoding))
- {
- if (options_string[k] == '"' && bslash_count % 2 == 0)
- break;
-
- if (options_string[jj] == '\\')
- bslash_count++;
- else
- bslash_count = 0;
-
- return_val[cc++] = options_string[k];
- }
- return_val[cc] = '\0';
- }
- else
- {
- strncpy(return_val, &options_string[pos], jj - pos + 1);
- return_val[jj - pos + 1] = '\0';
- }
-
- *string = options_string + jj + 1;
- if (quote)
- *quote = '"';
+ case '\0':
+ *string = &options_string[pos];
+ return NULL;
- return return_val;
- }
+ /*
+ * Next command: treat like end of line
+ *
+ * XXX this means we can't conveniently accept options that
+ * start with a backslash; therefore, option processing that
+ * encourages use of backslashes is rather broken.
+ */
+ case '\\':
+ *string = &options_string[pos];
+ return NULL;
/*
* A single quote has a psql internal meaning, such as for
@@ -1015,7 +980,7 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
case '`':
{
bool error = false;
- FILE *fd = NULL;
+ FILE *fd;
char *file;
PQExpBufferData output;
char buf[512];
@@ -1040,10 +1005,10 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
error = true;
}
+ initPQExpBuffer(&output);
+
if (!error)
{
- initPQExpBuffer(&output);
-
do
{
result = fread(buf, 1, 512, fd);
@@ -1056,27 +1021,26 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
appendBinaryPQExpBuffer(&output, buf, result);
} while (!feof(fd));
appendPQExpBufferChar(&output, '\0');
+ }
- if (pclose(fd) == -1)
- {
- psql_error("%s: %s\n", file, strerror(errno));
- error = true;
- }
+ if (fd && pclose(fd) == -1)
+ {
+ psql_error("%s: %s\n", file, strerror(errno));
+ error = true;
}
if (!error)
{
if (output.data[strlen(output.data) - 1] == '\n')
output.data[strlen(output.data) - 1] = '\0';
- }
-
- if (!error)
return_val = output.data;
+ }
else
{
return_val = xstrdup("");
termPQExpBuffer(&output);
}
+
options_string[pos + 1 + len] = '`';
*string = options_string + pos + len + 2;
if (quote)
@@ -1085,13 +1049,6 @@ scan_option(char **string, enum option_type type, char *quote, bool semicolon)
}
/*
- * end of line
- */
- case 0:
- *string = &options_string[pos];
- return NULL;
-
- /*
* Variable substitution
*/
case ':':
@@ -1109,67 +1066,146 @@ 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
*/
case '|':
if (type == OT_FILEPIPE)
{
- *string += strlen(options_string + pos);
+ *string += strlen(*string);
return xstrdup(options_string + pos);
- break;
}
/* fallthrough for other option types */
/*
- * A normal word
+ * Default case: token extends to next whitespace, except that
+ * whitespace within double quotes doesn't end the token.
+ *
+ * If we are processing the option as a SQL identifier, then
+ * downcase unquoted letters and remove double-quotes --- but
+ * doubled double-quotes become output double-quotes, per spec.
+ *
+ * Note that a string like FOO"BAR"BAZ will be converted to
+ * fooBARbaz; this is somewhat inconsistent with the SQL spec,
+ * which would have us parse it as several identifiers. But
+ * for psql's purposes, we want a string like "foo"."bar" to
+ * be treated as one option, so there's little choice.
*/
default:
{
- size_t token_end;
+ bool inquotes = false;
+ size_t token_len;
char *cp;
- token_end = strcspn(&options_string[pos], " \t\n\r");
- return_val = malloc(token_end + 1);
+ /* Find end of option */
+
+ cp = &options_string[pos];
+ for (;;)
+ {
+ /* Find next quote, whitespace, or end of string */
+ cp += strcspn(cp, "\" \t\n\r");
+ if (inquotes)
+ {
+ if (*cp == '\0')
+ {
+ psql_error("parse error at the end of line\n");
+ *string = cp;
+ return NULL;
+ }
+ if (*cp == '"')
+ inquotes = false;
+ cp++;
+ }
+ else
+ {
+ if (*cp != '"')
+ break; /* whitespace or end of string */
+ if (quote)
+ *quote = '"';
+ inquotes = true;
+ cp++;
+ }
+ }
+
+ *string = cp;
+
+ /* Copy the option */
+ token_len = cp - &options_string[pos];
+
+ return_val = malloc(token_len + 1);
if (!return_val)
{
psql_error("out of memory\n");
exit(EXIT_FAILURE);
}
- strncpy(return_val, &options_string[pos], token_end);
- return_val[token_end] = 0;
- /* Strip any trailing semi-colons for some types */
+ memcpy(return_val, &options_string[pos], token_len);
+ return_val[token_len] = '\0';
+
+ /* Strip any trailing semi-colons if requested */
if (semicolon)
{
- int i;
+ int i;
+
+ for (i = token_len - 1;
+ i >= 0 && return_val[i] == ';';
+ i--)
+ /* skip */;
+
+ if (i < 0)
+ {
+ /* nothing left after stripping the semicolon... */
+ free(return_val);
+ return NULL;
+ }
- for (i = strlen(return_val) - 1; i && return_val[i] == ';'; i--);
- if (i < strlen(return_val) - 1)
+ if (i < token_len - 1)
return_val[i + 1] = '\0';
}
- if (type == OT_SQLID)
- for (cp = return_val; *cp; cp += PQmblen(cp, pset.encoding))
- if (isupper((unsigned char) *cp))
- *cp = tolower((unsigned char) *cp);
+ /*
+ * If SQL identifier processing was requested,
+ * then we strip out excess double quotes and downcase
+ * unquoted letters.
+ */
+ if (type == OT_SQLID || type == OT_SQLIDHACK)
+ {
+ inquotes = false;
+ cp = return_val;
+
+ while (*cp)
+ {
+ if (*cp == '"')
+ {
+ if (inquotes && cp[1] == '"')
+ {
+ /* Keep the first quote, remove the second */
+ cp++;
+ }
+ inquotes = !inquotes;
+ /* Collapse out quote at *cp */
+ memmove(cp, cp+1, strlen(cp));
+ /* do not advance cp */
+ }
+ else
+ {
+ if (!inquotes && type == OT_SQLID)
+ {
+ if (isupper((unsigned char) *cp))
+ *cp = tolower((unsigned char) *cp);
+ }
+ cp += PQmblen(cp, pset.encoding);
+ }
+ }
+ }
- *string = &options_string[pos + token_end];
return return_val;
}
-
}
}
@@ -1429,7 +1465,7 @@ test_superuser(const char *username)
return false;
initPQExpBuffer(&buf);
- printfPQExpBuffer(&buf, "SELECT usesuper FROM pg_user WHERE usename = '%s'", username);
+ printfPQExpBuffer(&buf, "SELECT usesuper FROM pg_catalog.pg_user WHERE usename = '%s'", username);
res = PSQLexec(buf.data);
termPQExpBuffer(&buf);