mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-07 19:47:50 +08:00
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:
parent
2ec993a7cb
commit
b48b9cb3a4
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user