diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 90516a1..58e139b 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1473,6 +1473,35 @@ ends_with(const char *s, char c) } /* + * Get the last keyword matching a pattern. + */ +static const char * +last_keyword(char **previous_words, int previous_words_count, const char *keyword_pattern) { + int i; + + for (i = 0; i < previous_words_count; i++) { + if (word_matches(keyword_pattern, previous_words[i])) { + return previous_words[i]; + } + } + + return NULL; +} + +/* + * Was the last keyword one of the expected ones? + * E.g. last_keyword("SELECT|FROM|WHERE|GROUP|ORDER", "SELECT") is true if SELECT is the most recent + * of those key words to appear. + */ +static bool +last_keyword_matches(char **previous_words, int previous_words_count, const char *keyword_pattern, const char *accepted_pattern) { + const char *last_kw = last_keyword(previous_words, previous_words_count, keyword_pattern); + if (!last_kw) + return false; + return word_matches(last_kw, accepted_pattern); +} + +/* * The completion function. * * According to readline spec this gets passed the text entered so far and its @@ -1512,6 +1541,14 @@ psql_completion(const char *text, int start, int end) #define prev8_wd (previous_words[7]) #define prev9_wd (previous_words[8]) + /* Macro for matching last keyword, case-insensitively. */ +#define LastKeywordMatches(keywords, pattern) \ + (last_keyword_matches(previous_words, previous_words_count, keywords, pattern)) + + /* Macro for determining (loosely) which part of a DML query we are currently in. */ +#define CurrentQueryPartMatches(pattern) \ + (LastKeywordMatches("SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|GROUP|ORDER|HAVING", pattern)) + /* Macros for matching the last N words before point, case-insensitively. */ #define TailMatches1(p1) \ (previous_words_count >= 1 && \ @@ -1738,6 +1775,10 @@ psql_completion(const char *text, int start, int end) matches = complete_from_variables(text, ":", "", true); } + /* If current word ends with a comma, add a space. */ + if (ends_with(text, ',')) + COMPLETE_WITH_CONST(text); + /* If no previous word, suggest one of the basic sql commands */ else if (previous_words_count == 0) COMPLETE_WITH_LIST(sql_commands); @@ -3457,7 +3498,9 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_CONST("IS"); /* SELECT */ - else if (TailMatches1("SELECT") || TailMatches2("SELECT", "ALL|DISTINCT")) + else if (HeadMatches1("SELECT|WITH") && + CurrentQueryPartMatches("SELECT") && + (ends_with(prev_wd, ',') || TailMatches1("SELECT|ALL|DISTINCT"))) COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_selectable_functions, Query_addon_for_list_of_selectable_attributes);