diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 51010e1621..00c97d374d 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -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 */