mirror of
https://github.com/postgres/postgres.git
synced 2025-11-21 00:42:43 +03:00
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:
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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"));
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user