Teach psql to do tab completion for names of psql variables.

Completion is supported in the context of \set and when interpolating
a variable value using :foo etc.

In passing, fix some places in tab-complete.c that weren't following
project style for comment formatting.

Pavel Stehule, reviewed by Itagaki Takahiro
This commit is contained in:
Tom Lane 2010-10-10 18:42:35 -04:00
parent 2ec993a7cb
commit b48b9cb3a4
1 changed files with 134 additions and 63 deletions

View File

@ -576,20 +576,24 @@ static char *complete_from_query(const char *text, int state);
static char *complete_from_schema_query(const char *text, int state);
static char *_complete_from_query(int is_schema_query,
const char *text, int state);
static char *complete_from_const(const char *text, int state);
static char *complete_from_list(const char *text, int state);
static char *complete_from_const(const char *text, int state);
static char **complete_from_variables(char *text,
const char *prefix, const char *suffix);
static PGresult *exec_query(const char *query);
static char *previous_word(int point, int skip);
#if 0
#ifdef NOT_USED
static char *quote_file_name(char *text, int match_type, char *quote_pointer);
static char *dequote_file_name(char *text, char quote_char);
#endif
/* Initialize the readline library for our purposes. */
/*
* Initialize the readline library for our purposes.
*/
void
initialize_readline(void)
{
@ -607,11 +611,14 @@ initialize_readline(void)
}
/* The completion function. Acc. to readline spec this gets passed the text
entered to far and its start and end in the readline buffer. The return value
is some partially obscure list format that can be generated by the readline
libraries completion_matches() function, so we don't have to worry about it.
*/
/*
* The completion function.
*
* According to readline spec this gets passed the text entered so far and its
* start and end positions in the readline buffer. The return value is some
* partially obscure list format that can be generated by readline's
* completion_matches() function, so we don't have to worry about it.
*/
static char **
psql_completion(char *text, int start, int end)
{
@ -1943,7 +1950,7 @@ psql_completion(char *text, int start, int end)
pg_strcasecmp(prev_wd, "WRAPPER") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
/* GRANT && REVOKE*/
/* GRANT && REVOKE */
/* Complete GRANT/REVOKE with a list of privileges */
else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
pg_strcasecmp(prev_wd, "REVOKE") == 0)
@ -2512,7 +2519,6 @@ psql_completion(char *text, int start, int end)
pg_strcasecmp(prev3_wd, "\\copy") != 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
/* Backslash commands */
/* TODO: \dc \dd \dl */
else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
@ -2582,6 +2588,10 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_LIST(my_list);
}
else if (strcmp(prev_wd, "\\set") == 0)
{
matches = complete_from_variables(text, "", "");
}
else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
else if (strcmp(prev_wd, "\\cd") == 0 ||
@ -2594,6 +2604,16 @@ psql_completion(char *text, int start, int end)
)
matches = completion_matches(text, filename_completion_function);
/* Variable interpolation */
else if (text[0] == ':' && text[1] != ':')
{
if (text[1] == '\'')
matches = complete_from_variables(text, ":'", "'");
else if (text[1] == '"')
matches = complete_from_variables(text, ":\"", "\"");
else
matches = complete_from_variables(text, ":", "");
}
/*
* Finally, we look through the list of "things", such as TABLE, INDEX and
@ -2643,23 +2663,24 @@ psql_completion(char *text, int start, int end)
}
/*
* GENERATOR FUNCTIONS
*
* These functions do all the actual work of completing the input. They get
* passed the text so far and the count how many times they have been called
* so far with the same text.
* If you read the above carefully, you'll see that these don't get called
* directly but through the readline interface.
* The return value is expected to be the full completion of the text, going
* through a list each time, or NULL if there are no more matches. The string
* will be free()'d by readline, so you must run it through strdup() or
* something of that sort.
*/
/* GENERATOR FUNCTIONS
These functions do all the actual work of completing the input. They get
passed the text so far and the count how many times they have been called so
far with the same text.
If you read the above carefully, you'll see that these don't get called
directly but through the readline interface.
The return value is expected to be the full completion of the text, going
through a list each time, or NULL if there are no more matches. The string
will be free()'d by readline, so you must run it through strdup() or
something of that sort.
*/
/* This one gives you one from a list of things you can put after CREATE
as defined above.
*/
/*
* This one gives you one from a list of things you can put after CREATE
* as defined above.
*/
static char *
create_command_generator(const char *text, int state)
{
@ -2677,7 +2698,8 @@ create_command_generator(const char *text, int state)
/* find something that matches */
while ((name = words_after_create[list_index++].name))
{
if ((pg_strncasecmp(name, text, string_length) == 0) && !words_after_create[list_index - 1].noshow)
if ((pg_strncasecmp(name, text, string_length) == 0) &&
!words_after_create[list_index - 1].noshow)
return pg_strdup(name);
}
/* if nothing matches, return NULL */
@ -2745,26 +2767,27 @@ complete_from_schema_query(const char *text, int state)
}
/* This creates a list of matching things, according to a query pointed to
by completion_charp.
The query can be one of two kinds:
- A simple query which must contain a %d and a %s, which will be replaced
by the string length of the text and the text itself. The query may also
have up to four more %s in it; the first two such will be replaced by the
value of completion_info_charp, the next two by the value of
completion_info_charp2.
or:
- A schema query used for completion of both schema and relation names;
these are more complex and must contain in the following order:
%d %s %d %s %d %s %s %d %s
where %d is the string length of the text and %s the text itself.
It is assumed that strings should be escaped to become SQL literals
(that is, what is in the query is actually ... '%s' ...)
See top of file for examples of both kinds of query.
*/
/*
* This creates a list of matching things, according to a query pointed to
* by completion_charp.
* The query can be one of two kinds:
*
* 1. A simple query which must contain a %d and a %s, which will be replaced
* by the string length of the text and the text itself. The query may also
* have up to four more %s in it; the first two such will be replaced by the
* value of completion_info_charp, the next two by the value of
* completion_info_charp2.
*
* 2. A schema query used for completion of both schema and relation names.
* These are more complex and must contain in the following order:
* %d %s %d %s %d %s %s %d %s
* where %d is the string length of the text and %s the text itself.
*
* It is assumed that strings should be escaped to become SQL literals
* (that is, what is in the query is actually ... '%s' ...)
*
* See top of file for examples of both kinds of query.
*/
static char *
_complete_from_query(int is_schema_query, const char *text, int state)
{
@ -2950,10 +2973,11 @@ _complete_from_query(int is_schema_query, const char *text, int state)
}
/* This function returns in order one of a fixed, NULL pointer terminated list
of strings (if matching). This can be used if there are only a fixed number
SQL words that can appear at certain spot.
*/
/*
* This function returns in order one of a fixed, NULL pointer terminated list
* of strings (if matching). This can be used if there are only a fixed number
* SQL words that can appear at certain spot.
*/
static char *
complete_from_list(const char *text, int state)
{
@ -3006,12 +3030,13 @@ complete_from_list(const char *text, int state)
}
/* This function returns one fixed string the first time even if it doesn't
match what's there, and nothing the second time. This should be used if there
is only one possibility that can appear at a certain spot, so misspellings
will be overwritten.
The string to be passed must be in completion_charp.
*/
/*
* This function returns one fixed string the first time even if it doesn't
* match what's there, and nothing the second time. This should be used if
* there is only one possibility that can appear at a certain spot, so
* misspellings will be overwritten. The string to be passed must be in
* completion_charp.
*/
static char *
complete_from_const(const char *text, int state)
{
@ -3026,6 +3051,55 @@ complete_from_const(const char *text, int state)
}
/*
* This function supports completion with the name of a psql variable.
* The variable names can be prefixed and suffixed with additional text
* to support quoting usages.
*/
static char **
complete_from_variables(char *text, const char *prefix, const char *suffix)
{
char **matches;
int overhead = strlen(prefix) + strlen(suffix) + 1;
const char **varnames;
int nvars = 0;
int maxvars = 100;
int i;
struct _variable *ptr;
varnames = (const char **) pg_malloc((maxvars + 1) * sizeof(char *));
for (ptr = pset.vars->next; ptr; ptr = ptr->next)
{
char *buffer;
if (nvars >= maxvars)
{
maxvars *= 2;
varnames = (const char **) realloc(varnames,
(maxvars + 1) * sizeof(char *));
if (!varnames)
{
psql_error("out of memory\n");
exit(EXIT_FAILURE);
}
}
buffer = (char *) pg_malloc(strlen(ptr->name) + overhead);
sprintf(buffer, "%s%s%s", prefix, ptr->name, suffix);
varnames[nvars++] = buffer;
}
varnames[nvars] = NULL;
COMPLETE_WITH_LIST(varnames);
for (i = 0; i < nvars; i++)
free((void *) varnames[i]);
free(varnames);
return matches;
}
/* HELPER FUNCTIONS */
@ -3046,7 +3120,7 @@ exec_query(const char *query)
if (PQresultStatus(result) != PGRES_TUPLES_OK)
{
#if 0
#ifdef NOT_USED
psql_error("tab completion query failed: %s\nQuery was:\n%s\n",
PQerrorMessage(pset.db), query);
#endif
@ -3058,7 +3132,6 @@ exec_query(const char *query)
}
/*
* Return the word (space delimited) before point. Set skip > 0 to
* skip that many words; e.g. skip=1 finds the word before the
@ -3133,7 +3206,7 @@ previous_word(int point, int skip)
return s;
}
#if 0
#ifdef NOT_USED
/*
* Surround a string with single quotes. This works for both SQL and
@ -3158,8 +3231,6 @@ quote_file_name(char *text, int match_type, char *quote_pointer)
return s;
}
static char *
dequote_file_name(char *text, char quote_char)
{
@ -3175,6 +3246,6 @@ dequote_file_name(char *text, char quote_char)
return s;
}
#endif /* 0 */
#endif /* NOT_USED */
#endif /* USE_READLINE */