diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 9ce0978b71..731df2a5c0 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -139,7 +139,8 @@ static bool completion_case_sensitive; /* completion is case sensitive */ * string with * 1) The results from a query you pass it. (Perhaps one of those below?) * 2) The results from a schema query you pass it. - * 3) The items from a null-pointer-terminated list. + * 3) The items from a null-pointer-terminated list (with or without + * case-sensitive comparison; see also COMPLETE_WITH_LISTn, below). * 4) A string constant. * 5) The list of attributes of the given table (possibly schema-qualified). * 6/ The list of arguments to the given function (possibly schema-qualified). @@ -228,6 +229,64 @@ do { \ matches = completion_matches(text, complete_from_query); \ } while (0) +/* + * These macros simplify use of COMPLETE_WITH_LIST for short, fixed lists. + * There is no COMPLETE_WITH_LIST1; use COMPLETE_WITH_CONST for that case. + */ +#define COMPLETE_WITH_LIST2(s1, s2) \ +do { \ + static const char *const list[] = { s1, s2, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + +#define COMPLETE_WITH_LIST3(s1, s2, s3) \ +do { \ + static const char *const list[] = { s1, s2, s3, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + +#define COMPLETE_WITH_LIST4(s1, s2, s3, s4) \ +do { \ + static const char *const list[] = { s1, s2, s3, s4, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + +#define COMPLETE_WITH_LIST5(s1, s2, s3, s4, s5) \ +do { \ + static const char *const list[] = { s1, s2, s3, s4, s5, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + +#define COMPLETE_WITH_LIST6(s1, s2, s3, s4, s5, s6) \ +do { \ + static const char *const list[] = { s1, s2, s3, s4, s5, s6, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + +#define COMPLETE_WITH_LIST7(s1, s2, s3, s4, s5, s6, s7) \ +do { \ + static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + +#define COMPLETE_WITH_LIST8(s1, s2, s3, s4, s5, s6, s7, s8) \ +do { \ + static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + +#define COMPLETE_WITH_LIST9(s1, s2, s3, s4, s5, s6, s7, s8, s9) \ +do { \ + static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, s9, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + +#define COMPLETE_WITH_LIST10(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10) \ +do { \ + static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, NULL }; \ + COMPLETE_WITH_LIST(list); \ +} while (0) + /* * Assembly instructions for schema queries */ @@ -864,7 +923,7 @@ static char *pg_strdup_keyword_case(const char *s, const char *ref); static char *escape_string(const char *text); static PGresult *exec_query(const char *query); -static void get_previous_words(int point, char **previous_words, int nwords); +static int get_previous_words(int point, char **previous_words, int nwords); static char *get_guctype(const char *varname); @@ -893,6 +952,66 @@ initialize_readline(void) */ } +/* + * Check if 'word' matches any of the '|'-separated strings in 'pattern', + * using case-insensitive comparisons. + * If pattern is NULL, it's a wild card that matches any word. + * If pattern begins with "!", the result is negated, ie we check that 'word' + * does *not* match any alternative appearing in the rest of 'pattern'. + * + * For readability, callers should use the macros MatchAny and MatchAnyExcept + * to invoke the two special cases for 'pattern'. + */ +#define MatchAny NULL +#define MatchAnyExcept(pattern) ("!" pattern) + +static bool +word_matches(const char *pattern, const char *word) +{ + size_t wordlen; + + /* NULL pattern matches anything. */ + if (pattern == NULL) + return true; + + /* Handle negated patterns from the MatchAnyExcept macro. */ + if (*pattern == '!') + return !word_matches(pattern + 1, word); + + /* Else consider each alternative in the pattern. */ + wordlen = strlen(word); + for (;;) + { + const char *c; + + /* Find end of current alternative. */ + c = pattern; + while (*c != '\0' && *c != '|') + c++; + /* Match? */ + if (wordlen == (c - pattern) && + pg_strncasecmp(word, pattern, wordlen) == 0) + return true; + /* Out of alternatives? */ + if (*c == '\0') + break; + /* Nope, try next alternative. */ + pattern = c + 1; + } + + return false; +} + +/* + * Check if the final character of 's' is 'c'. + */ +static bool +ends_with(const char *s, char c) +{ + size_t length = strlen(s); + + return (length > 0 && s[length - 1] == c); +} /* * The completion function. @@ -911,6 +1030,9 @@ psql_completion(const char *text, int start, int end) /* This array will contain some scannage of the input line. */ char *previous_words[9]; + /* The number of words found on the input line. */ + int previous_words_count; + /* For compactness, we use these macros to reference previous_words[]. */ #define prev_wd (previous_words[0]) #define prev2_wd (previous_words[1]) @@ -922,6 +1044,115 @@ psql_completion(const char *text, int start, int end) #define prev8_wd (previous_words[7]) #define prev9_wd (previous_words[8]) + /* Macros for matching the last N words before point. */ +#define TailMatches1(p1) \ + (previous_words_count >= 1 && \ + word_matches(p1, prev_wd)) + +#define TailMatches2(p2, p1) \ + (previous_words_count >= 2 && \ + word_matches(p1, prev_wd) && \ + word_matches(p2, prev2_wd)) + +#define TailMatches3(p3, p2, p1) \ + (previous_words_count >= 3 && \ + word_matches(p1, prev_wd) && \ + word_matches(p2, prev2_wd) && \ + word_matches(p3, prev3_wd)) + +#define TailMatches4(p4, p3, p2, p1) \ + (previous_words_count >= 4 && \ + word_matches(p1, prev_wd) && \ + word_matches(p2, prev2_wd) && \ + word_matches(p3, prev3_wd) && \ + word_matches(p4, prev4_wd)) + +#define TailMatches5(p5, p4, p3, p2, p1) \ + (previous_words_count >= 5 && \ + word_matches(p1, prev_wd) && \ + word_matches(p2, prev2_wd) && \ + word_matches(p3, prev3_wd) && \ + word_matches(p4, prev4_wd) && \ + word_matches(p5, prev5_wd)) + +#define TailMatches6(p6, p5, p4, p3, p2, p1) \ + (previous_words_count >= 6 && \ + word_matches(p1, prev_wd) && \ + word_matches(p2, prev2_wd) && \ + word_matches(p3, prev3_wd) && \ + word_matches(p4, prev4_wd) && \ + word_matches(p5, prev5_wd) && \ + word_matches(p6, prev6_wd)) + +#define TailMatches7(p7, p6, p5, p4, p3, p2, p1) \ + (previous_words_count >= 7 && \ + word_matches(p1, prev_wd) && \ + word_matches(p2, prev2_wd) && \ + word_matches(p3, prev3_wd) && \ + word_matches(p4, prev4_wd) && \ + word_matches(p5, prev5_wd) && \ + word_matches(p6, prev6_wd) && \ + word_matches(p7, prev7_wd)) + +#define TailMatches8(p8, p7, p6, p5, p4, p3, p2, p1) \ + (previous_words_count >= 8 && \ + word_matches(p1, prev_wd) && \ + word_matches(p2, prev2_wd) && \ + word_matches(p3, prev3_wd) && \ + word_matches(p4, prev4_wd) && \ + word_matches(p5, prev5_wd) && \ + word_matches(p6, prev6_wd) && \ + word_matches(p7, prev7_wd) && \ + word_matches(p8, prev8_wd)) + +#define TailMatches9(p9, p8, p7, p6, p5, p4, p3, p2, p1) \ + (previous_words_count >= 9 && \ + word_matches(p1, prev_wd) && \ + word_matches(p2, prev2_wd) && \ + word_matches(p3, prev3_wd) && \ + word_matches(p4, prev4_wd) && \ + word_matches(p5, prev5_wd) && \ + word_matches(p6, prev6_wd) && \ + word_matches(p7, prev7_wd) && \ + word_matches(p8, prev8_wd) && \ + word_matches(p9, prev9_wd)) + + /* Macros for matching N words beginning at the start of the line. */ +#define Matches1(p1) \ + (previous_words_count == 1 && \ + TailMatches1(p1)) +#define Matches2(p1, p2) \ + (previous_words_count == 2 && \ + TailMatches2(p1, p2)) +#define Matches3(p1, p2, p3) \ + (previous_words_count == 3 && \ + TailMatches3(p1, p2, p3)) +#define Matches4(p1, p2, p3, p4) \ + (previous_words_count == 4 && \ + TailMatches4(p1, p2, p3, p4)) + + /* + * Macros for matching N words at the start of the line, regardless of + * what is after them. These are pretty broken because they can only look + * back lengthof(previous_words) words, but we'll worry about improving + * their implementation some other day. + */ +#define HeadMatches1(p1) \ + (previous_words_count >= 1 && \ + word_matches(p1, previous_words[previous_words_count - 1])) + +#define HeadMatches2(p1, p2) \ + (previous_words_count >= 2 && \ + word_matches(p1, previous_words[previous_words_count - 1]) && \ + word_matches(p2, previous_words[previous_words_count - 2])) + +#define HeadMatches3(p1, p2, p3) \ + (previous_words_count >= 3 && \ + word_matches(p1, previous_words[previous_words_count - 1]) && \ + word_matches(p2, previous_words[previous_words_count - 2]) && \ + word_matches(p3, previous_words[previous_words_count - 3])) + + /* Known command-starting keywords. */ static const char *const sql_commands[] = { "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", @@ -934,6 +1165,7 @@ psql_completion(const char *text, int start, int end) NULL }; + /* psql's backslash commands. */ static const char *const backslash_commands[] = { "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright", "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD", @@ -949,7 +1181,7 @@ psql_completion(const char *text, int start, int end) "\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL }; - (void) end; /* not used */ + (void) end; /* "end" is not used */ #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rl_completion_append_character = ' '; @@ -966,13 +1198,15 @@ psql_completion(const char *text, int start, int end) * According to those we'll make some smart decisions on what the user is * probably intending to type. */ - get_previous_words(start, previous_words, lengthof(previous_words)); + previous_words_count = get_previous_words(start, + previous_words, + lengthof(previous_words)); - /* If a backslash command was started, continue */ + /* If current word is a backslash command, offer completions for that */ if (text[0] == '\\') COMPLETE_WITH_LIST_CS(backslash_commands); - /* Variable interpolation */ + /* If current word is a variable interpolation, handle that case */ else if (text[0] == ':' && text[1] != ':') { if (text[1] == '\'') @@ -984,36 +1218,28 @@ psql_completion(const char *text, int start, int end) } /* If no previous word, suggest one of the basic sql commands */ - else if (prev_wd[0] == '\0') + else if (previous_words_count == 0) COMPLETE_WITH_LIST(sql_commands); /* CREATE */ /* complete with something you can create */ - else if (pg_strcasecmp(prev_wd, "CREATE") == 0) + else if (TailMatches1("CREATE")) matches = completion_matches(text, create_command_generator); /* DROP, but not DROP embedded in other commands */ /* complete with something you can drop */ - else if (pg_strcasecmp(prev_wd, "DROP") == 0 && - prev2_wd[0] == '\0') + else if (Matches1("DROP")) matches = completion_matches(text, drop_command_generator); /* ALTER */ /* ALTER TABLE */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "TABLE") == 0) - { + else if (Matches2("ALTER", "TABLE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "UNION SELECT 'ALL IN TABLESPACE'"); - } - /* - * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're - * in ALTER TABLE sth ALTER - */ - else if (pg_strcasecmp(prev_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") != 0) + /* ALTER something */ + else if (Matches1("ALTER")) { static const char *const list_ALTER[] = {"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", @@ -1026,161 +1252,70 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTER); } /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */ - else if (pg_strcasecmp(prev4_wd, "ALL") == 0 && - pg_strcasecmp(prev3_wd, "IN") == 0 && - pg_strcasecmp(prev2_wd, "TABLESPACE") == 0) - { - static const char *const list_ALTERALLINTSPC[] = - {"SET TABLESPACE", "OWNED BY", NULL}; - - COMPLETE_WITH_LIST(list_ALTERALLINTSPC); - } + else if (TailMatches4("ALL", "IN", "TABLESPACE", MatchAny)) + COMPLETE_WITH_LIST2("SET TABLESPACE", "OWNED BY"); /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY */ - else if (pg_strcasecmp(prev6_wd, "ALL") == 0 && - pg_strcasecmp(prev5_wd, "IN") == 0 && - pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 && - pg_strcasecmp(prev2_wd, "OWNED") == 0 && - pg_strcasecmp(prev_wd, "BY") == 0) - { + else if (TailMatches6("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); - } /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY xxx */ - else if (pg_strcasecmp(prev6_wd, "IN") == 0 && - pg_strcasecmp(prev5_wd, "TABLESPACE") == 0 && - pg_strcasecmp(prev3_wd, "OWNED") == 0 && - pg_strcasecmp(prev2_wd, "BY") == 0) - { + else if (TailMatches7("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny)) COMPLETE_WITH_CONST("SET TABLESPACE"); - } /* ALTER AGGREGATE,FUNCTION */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 || - pg_strcasecmp(prev2_wd, "FUNCTION") == 0)) + else if (TailMatches3("ALTER", "AGGREGATE|FUNCTION", MatchAny)) COMPLETE_WITH_CONST("("); /* ALTER AGGREGATE,FUNCTION (...) */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 || - pg_strcasecmp(prev3_wd, "FUNCTION") == 0)) + else if (TailMatches4("ALTER", "AGGREGATE|FUNCTION", MatchAny, MatchAny)) { - if (prev_wd[strlen(prev_wd) - 1] == ')') - { - static const char *const list_ALTERAGG[] = - {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERAGG); - } + if (ends_with(prev_wd, ')')) + COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); else COMPLETE_WITH_FUNCTION_ARG(prev2_wd); } /* ALTER SCHEMA */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SCHEMA") == 0) - { - static const char *const list_ALTERGEN[] = - {"OWNER TO", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERGEN); - } + else if (TailMatches3("ALTER", "SCHEMA", MatchAny)) + COMPLETE_WITH_LIST2("OWNER TO", "RENAME TO"); /* ALTER COLLATION */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "COLLATION") == 0) - { - static const char *const list_ALTERGEN[] = - {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERGEN); - } + else if (TailMatches3("ALTER", "COLLATION", MatchAny)) + COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); /* ALTER CONVERSION */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "CONVERSION") == 0) - { - static const char *const list_ALTERGEN[] = - {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERGEN); - } + else if (TailMatches3("ALTER", "CONVERSION", MatchAny)) + COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); /* ALTER DATABASE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "DATABASE") == 0) - { - static const char *const list_ALTERDATABASE[] = - {"RESET", "SET", "OWNER TO", "RENAME TO", "IS_TEMPLATE", - "ALLOW_CONNECTIONS", "CONNECTION LIMIT", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDATABASE); - } + else if (TailMatches3("ALTER", "DATABASE", MatchAny)) + COMPLETE_WITH_LIST7("RESET", "SET", "OWNER TO", "RENAME TO", + "IS_TEMPLATE", "ALLOW_CONNECTIONS", + "CONNECTION LIMIT"); /* ALTER EVENT TRIGGER */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "EVENT") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { + else if (TailMatches3("ALTER", "EVENT", "TRIGGER")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); - } /* ALTER EVENT TRIGGER */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "EVENT") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) - { - static const char *const list_ALTER_EVENT_TRIGGER[] = - {"DISABLE", "ENABLE", "OWNER TO", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_EVENT_TRIGGER); - } + else if (TailMatches4("ALTER", "EVENT", "TRIGGER", MatchAny)) + COMPLETE_WITH_LIST4("DISABLE", "ENABLE", "OWNER TO", "RENAME TO"); /* ALTER EVENT TRIGGER ENABLE */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "EVENT") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ENABLE") == 0) - { - static const char *const list_ALTER_EVENT_TRIGGER_ENABLE[] = - {"REPLICA", "ALWAYS", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_EVENT_TRIGGER_ENABLE); - } + else if (TailMatches5("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE")) + COMPLETE_WITH_LIST2("REPLICA", "ALWAYS"); /* ALTER EXTENSION */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "EXTENSION") == 0) - { - static const char *const list_ALTEREXTENSION[] = - {"ADD", "DROP", "UPDATE", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTEREXTENSION); - } + else if (TailMatches3("ALTER", "EXTENSION", MatchAny)) + COMPLETE_WITH_LIST4("ADD", "DROP", "UPDATE", "SET SCHEMA"); /* ALTER FOREIGN */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const list_ALTER_FOREIGN[] = - {"DATA WRAPPER", "TABLE", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_FOREIGN); - } + else if (TailMatches2("ALTER", "FOREIGN")) + COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); /* ALTER FOREIGN DATA WRAPPER */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev3_wd, "DATA") == 0 && - pg_strcasecmp(prev2_wd, "WRAPPER") == 0) - { - static const char *const list_ALTER_FDW[] = - {"HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_FDW); - } + else if (TailMatches5("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny)) + COMPLETE_WITH_LIST4("HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO"); /* ALTER FOREIGN TABLE */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev2_wd, "TABLE") == 0) + else if (TailMatches4("ALTER", "FOREIGN", "TABLE", MatchAny)) { static const char *const list_ALTER_FOREIGN_TABLE[] = {"ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE", "INHERIT", @@ -1191,93 +1326,42 @@ psql_completion(const char *text, int start, int end) } /* ALTER INDEX */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "INDEX") == 0) - { + else if (TailMatches2("ALTER", "INDEX")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, "UNION SELECT 'ALL IN TABLESPACE'"); - } /* ALTER INDEX */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "INDEX") == 0) - { - static const char *const list_ALTERINDEX[] = - {"OWNER TO", "RENAME TO", "SET", "RESET", NULL}; - - COMPLETE_WITH_LIST(list_ALTERINDEX); - } + else if (TailMatches3("ALTER", "INDEX", MatchAny)) + COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET"); /* ALTER INDEX SET */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "INDEX") == 0 && - pg_strcasecmp(prev_wd, "SET") == 0) - { - static const char *const list_ALTERINDEXSET[] = - {"(", "TABLESPACE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERINDEXSET); - } + else if (TailMatches4("ALTER", "INDEX", MatchAny, "SET")) + COMPLETE_WITH_LIST2("(", "TABLESPACE"); /* ALTER INDEX RESET */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "INDEX") == 0 && - pg_strcasecmp(prev_wd, "RESET") == 0) + else if (TailMatches4("ALTER", "INDEX", MatchAny, "RESET")) COMPLETE_WITH_CONST("("); /* ALTER INDEX SET|RESET ( */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "INDEX") == 0 && - pg_strcasecmp(prev2_wd, "RESET") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) - { - static const char *const list_INDEXOPTIONS[] = - {"fillfactor", "fastupdate", "gin_pending_list_limit", NULL}; - - COMPLETE_WITH_LIST(list_INDEXOPTIONS); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "INDEX") == 0 && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) - { - static const char *const list_INDEXOPTIONS[] = - {"fillfactor =", "fastupdate =", "gin_pending_list_limit =", NULL}; - - COMPLETE_WITH_LIST(list_INDEXOPTIONS); - } + else if (TailMatches5("ALTER", "INDEX", MatchAny, "RESET", "(")) + COMPLETE_WITH_LIST3("fillfactor", "fastupdate", + "gin_pending_list_limit"); + else if (TailMatches5("ALTER", "INDEX", MatchAny, "SET", "(")) + COMPLETE_WITH_LIST3("fillfactor =", "fastupdate =", + "gin_pending_list_limit ="); /* ALTER LANGUAGE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "LANGUAGE") == 0) - { - static const char *const list_ALTERLANGUAGE[] = - {"OWNER TO", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERLANGUAGE); - } + else if (TailMatches3("ALTER", "LANGUAGE", MatchAny)) + COMPLETE_WITH_LIST2("OWNER_TO", "RENAME TO"); /* ALTER LARGE OBJECT */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "LARGE") == 0 && - pg_strcasecmp(prev2_wd, "OBJECT") == 0) - { - static const char *const list_ALTERLARGEOBJECT[] = - {"OWNER TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT); - } + else if (TailMatches4("ALTER", "LARGE", "OBJECT", MatchAny)) + COMPLETE_WITH_CONST("OWNER TO"); /* ALTER MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev_wd, "VIEW") == 0) - { + else if (TailMatches3("ALTER", "MATERIALIZED", "VIEW")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, "UNION SELECT 'ALL IN TABLESPACE'"); - } /* ALTER USER,ROLE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && - (pg_strcasecmp(prev2_wd, "USER") == 0 || - pg_strcasecmp(prev2_wd, "ROLE") == 0)) + else if (TailMatches3("ALTER", "USER|ROLE", MatchAny) && + !TailMatches2("USER", "MAPPING")) { static const char *const list_ALTERUSER[] = {"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", @@ -1291,10 +1375,7 @@ psql_completion(const char *text, int start, int end) } /* ALTER USER,ROLE WITH */ - else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 && - (pg_strcasecmp(prev3_wd, "USER") == 0 || - pg_strcasecmp(prev3_wd, "ROLE") == 0) && - pg_strcasecmp(prev_wd, "WITH") == 0)) + else if (TailMatches4("ALTER", "USER|ROLE", MatchAny, "WITH")) { /* Similar to the above, but don't complete "WITH" again. */ static const char *const list_ALTERUSER_WITH[] = @@ -1309,103 +1390,43 @@ psql_completion(const char *text, int start, int end) } /* complete ALTER USER,ROLE ENCRYPTED,UNENCRYPTED with PASSWORD */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0)) - { + else if (TailMatches4("ALTER", "USER|ROLE", MatchAny, "ENCRYPTED|UNENCRYPTED")) COMPLETE_WITH_CONST("PASSWORD"); - } /* ALTER DEFAULT PRIVILEGES */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "DEFAULT") == 0 && - pg_strcasecmp(prev_wd, "PRIVILEGES") == 0) - { - static const char *const list_ALTER_DEFAULT_PRIVILEGES[] = - {"FOR ROLE", "FOR USER", "IN SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES); - } + else if (TailMatches3("ALTER", "DEFAULT", "PRIVILEGES")) + COMPLETE_WITH_LIST3("FOR ROLE", "FOR USER", "IN SCHEMA"); /* ALTER DEFAULT PRIVILEGES FOR */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "DEFAULT") == 0 && - pg_strcasecmp(prev2_wd, "PRIVILEGES") == 0 && - pg_strcasecmp(prev_wd, "FOR") == 0) - { - static const char *const list_ALTER_DEFAULT_PRIVILEGES_FOR[] = - {"ROLE", "USER", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_FOR); - } + else if (TailMatches4("ALTER", "DEFAULT", "PRIVILEGES", "FOR")) + COMPLETE_WITH_LIST2("ROLE", "USER"); /* ALTER DEFAULT PRIVILEGES { FOR ROLE ... | IN SCHEMA ... } */ - else if (pg_strcasecmp(prev5_wd, "DEFAULT") == 0 && - pg_strcasecmp(prev4_wd, "PRIVILEGES") == 0 && - (pg_strcasecmp(prev3_wd, "FOR") == 0 || - pg_strcasecmp(prev3_wd, "IN") == 0)) - { - static const char *const list_ALTER_DEFAULT_PRIVILEGES_REST[] = - {"GRANT", "REVOKE", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_REST); - } + else if (TailMatches5("DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny) || + TailMatches5("DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny)) + COMPLETE_WITH_LIST2("GRANT", "REVOKE"); /* ALTER DOMAIN */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "DOMAIN") == 0) - { - static const char *const list_ALTERDOMAIN[] = - {"ADD", "DROP", "OWNER TO", "RENAME", "SET", "VALIDATE CONSTRAINT", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDOMAIN); - } + else if (TailMatches3("ALTER", "DOMAIN", MatchAny)) + COMPLETE_WITH_LIST6("ADD", "DROP", "OWNER TO", "RENAME", "SET", + "VALIDATE CONSTRAINT"); /* ALTER DOMAIN DROP */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "DOMAIN") == 0 && - pg_strcasecmp(prev_wd, "DROP") == 0) - { - static const char *const list_ALTERDOMAIN2[] = - {"CONSTRAINT", "DEFAULT", "NOT NULL", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDOMAIN2); - } + else if (TailMatches4("ALTER", "DOMAIN", MatchAny, "DROP")) + COMPLETE_WITH_LIST3("CONSTRAINT", "DEFAULT", "NOT NULL"); /* ALTER DOMAIN DROP|RENAME|VALIDATE CONSTRAINT */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "DOMAIN") == 0 && - (pg_strcasecmp(prev2_wd, "DROP") == 0 || - pg_strcasecmp(prev2_wd, "RENAME") == 0 || - pg_strcasecmp(prev2_wd, "VALIDATE") == 0) && - pg_strcasecmp(prev_wd, "CONSTRAINT") == 0) + else if (TailMatches5("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT")) { completion_info_charp = prev3_wd; COMPLETE_WITH_QUERY(Query_for_constraint_of_type); } /* ALTER DOMAIN RENAME */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "DOMAIN") == 0 && - pg_strcasecmp(prev_wd, "RENAME") == 0) - { - static const char *const list_ALTERDOMAIN[] = - {"CONSTRAINT", "TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDOMAIN); - } + else if (TailMatches4("ALTER", "DOMAIN", MatchAny, "RENAME")) + COMPLETE_WITH_LIST2("CONSTRAINT", "TO"); /* ALTER DOMAIN RENAME CONSTRAINT */ - else if (pg_strcasecmp(prev5_wd, "DOMAIN") == 0 && - pg_strcasecmp(prev3_wd, "RENAME") == 0 && - pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0) + else if (TailMatches5("DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny)) COMPLETE_WITH_CONST("TO"); /* ALTER DOMAIN SET */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "DOMAIN") == 0 && - pg_strcasecmp(prev_wd, "SET") == 0) - { - static const char *const list_ALTERDOMAIN3[] = - {"DEFAULT", "NOT NULL", "SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDOMAIN3); - } + else if (TailMatches4("ALTER", "DOMAIN", MatchAny, "SET")) + COMPLETE_WITH_LIST3("DEFAULT", "NOT NULL", "SCHEMA"); /* ALTER SEQUENCE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SEQUENCE") == 0) + else if (TailMatches3("ALTER", "SEQUENCE", MatchAny)) { static const char *const list_ALTERSEQUENCE[] = {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE", @@ -1414,130 +1435,71 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTERSEQUENCE); } /* ALTER SEQUENCE NO */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "SEQUENCE") == 0 && - pg_strcasecmp(prev_wd, "NO") == 0) - { - static const char *const list_ALTERSEQUENCE2[] = - {"MINVALUE", "MAXVALUE", "CYCLE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERSEQUENCE2); - } + else if (TailMatches4("ALTER", "SEQUENCE", MatchAny, "NO")) + COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE"); /* ALTER SERVER */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SERVER") == 0) - { - static const char *const list_ALTER_SERVER[] = - {"VERSION", "OPTIONS", "OWNER TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTER_SERVER); - } + else if (TailMatches3("ALTER", "SERVER", MatchAny)) + COMPLETE_WITH_LIST3("VERSION", "OPTIONS", "OWNER TO"); /* ALTER SYSTEM SET, RESET, RESET ALL */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "SYSTEM") == 0) - { - static const char *const list_ALTERSYSTEM[] = - {"SET", "RESET", NULL}; - - COMPLETE_WITH_LIST(list_ALTERSYSTEM); - } + else if (TailMatches2("ALTER", "SYSTEM")) + COMPLETE_WITH_LIST2("SET", "RESET"); /* ALTER SYSTEM SET|RESET */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SYSTEM") == 0 && - (pg_strcasecmp(prev_wd, "SET") == 0 || - pg_strcasecmp(prev_wd, "RESET") == 0)) + else if (TailMatches4("ALTER", "SYSTEM", "SET|RESET", MatchAny)) COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars); /* ALTER VIEW */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) - { - static const char *const list_ALTERVIEW[] = - {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERVIEW); - } + else if (TailMatches3("ALTER", "VIEW", MatchAny)) + COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO", + "SET SCHEMA"); /* ALTER MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) - { - static const char *const list_ALTERMATVIEW[] = - {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERMATVIEW); - } + else if (TailMatches4("ALTER", "MATERIALIZED", "VIEW", MatchAny)) + COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO", + "SET SCHEMA"); /* ALTER POLICY */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "POLICY") == 0) + else if (TailMatches2("ALTER", "POLICY")) COMPLETE_WITH_QUERY(Query_for_list_of_policies); /* ALTER POLICY ON */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "POLICY") == 0) + else if (TailMatches3("ALTER", "POLICY", MatchAny)) COMPLETE_WITH_CONST("ON"); /* ALTER POLICY ON */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "POLICY") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("ALTER", "POLICY", MatchAny, "ON")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy); } /* ALTER POLICY ON
- show options */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "POLICY") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_ALTERPOLICY[] = - {"RENAME TO", "TO", "USING", "WITH CHECK", NULL}; - - COMPLETE_WITH_LIST(list_ALTERPOLICY); - } + else if (TailMatches5("ALTER", "POLICY", MatchAny, "ON", MatchAny)) + COMPLETE_WITH_LIST4("RENAME TO", "TO", "USING", "WITH CHECK"); /* ALTER POLICY ON
TO */ - else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) + else if (TailMatches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO")) COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); /* ALTER POLICY ON
USING ( */ - else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) + else if (TailMatches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING")) COMPLETE_WITH_CONST("("); /* ALTER POLICY ON
WITH CHECK ( */ - else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "WITH") == 0 && - pg_strcasecmp(prev_wd, "CHECK") == 0) + else if (TailMatches6("POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK")) COMPLETE_WITH_CONST("("); /* ALTER RULE , add ON */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "RULE") == 0) + else if (TailMatches3("ALTER", "RULE", MatchAny)) COMPLETE_WITH_CONST("ON"); /* If we have ALTER RULE ON, then add the correct tablename */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "RULE") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("ALTER", "RULE", MatchAny, "ON")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule); } /* ALTER RULE ON */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "RULE") == 0) + else if (TailMatches5("ALTER", "RULE", MatchAny, "ON", MatchAny)) COMPLETE_WITH_CONST("RENAME TO"); /* ALTER TRIGGER , add ON */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) + else if (TailMatches3("ALTER", "TRIGGER", MatchAny)) COMPLETE_WITH_CONST("ON"); - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0) + else if (TailMatches4("ALTER", "TRIGGER", MatchAny, MatchAny)) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger); @@ -1546,22 +1508,17 @@ psql_completion(const char *text, int start, int end) /* * If we have ALTER TRIGGER ON, then add the correct tablename */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("ALTER", "TRIGGER", MatchAny, "ON")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); /* ALTER TRIGGER ON */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) + else if (TailMatches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny)) COMPLETE_WITH_CONST("RENAME TO"); /* * If we detect ALTER TABLE , suggest sub commands */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TABLE") == 0) + else if (TailMatches3("ALTER", "TABLE", MatchAny)) { static const char *const list_ALTER2[] = {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT", @@ -1571,279 +1528,134 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTER2); } /* ALTER TABLE xxx ENABLE */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "ENABLE") == 0) - { - static const char *const list_ALTERENABLE[] = - {"ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE", "TRIGGER", NULL}; - - COMPLETE_WITH_LIST(list_ALTERENABLE); - } - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "ENABLE") == 0 && - (pg_strcasecmp(prev_wd, "REPLICA") == 0 || - pg_strcasecmp(prev_wd, "ALWAYS") == 0)) - { - static const char *const list_ALTERENABLE2[] = - {"RULE", "TRIGGER", NULL}; - - COMPLETE_WITH_LIST(list_ALTERENABLE2); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "ENABLE") == 0 && - pg_strcasecmp(prev_wd, "RULE") == 0) + else if (TailMatches4("ALTER", "TABLE", MatchAny, "ENABLE")) + COMPLETE_WITH_LIST5("ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE", + "TRIGGER"); + else if (TailMatches4("TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS")) + COMPLETE_WITH_LIST2("RULE", "TRIGGER"); + else if (TailMatches5("ALTER", "TABLE", MatchAny, "ENABLE", "RULE")) { completion_info_charp = prev3_wd; COMPLETE_WITH_QUERY(Query_for_rule_of_table); } - else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && - pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "ENABLE") == 0 && - pg_strcasecmp(prev_wd, "RULE") == 0) + else if (TailMatches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE")) { completion_info_charp = prev4_wd; COMPLETE_WITH_QUERY(Query_for_rule_of_table); } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "ENABLE") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) + else if (TailMatches5("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER")) { completion_info_charp = prev3_wd; COMPLETE_WITH_QUERY(Query_for_trigger_of_table); } - else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 && - pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "ENABLE") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) + else if (TailMatches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER")) { completion_info_charp = prev4_wd; COMPLETE_WITH_QUERY(Query_for_trigger_of_table); } /* ALTER TABLE xxx INHERIT */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "INHERIT") == 0) - { + else if (TailMatches4("ALTER", "TABLE", MatchAny, "INHERIT")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); - } /* ALTER TABLE xxx NO INHERIT */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "NO") == 0 && - pg_strcasecmp(prev_wd, "INHERIT") == 0) - { + else if (TailMatches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); - } /* ALTER TABLE xxx DISABLE */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "DISABLE") == 0) - { - static const char *const list_ALTERDISABLE[] = - {"ROW LEVEL SECURITY", "RULE", "TRIGGER", NULL}; - - COMPLETE_WITH_LIST(list_ALTERDISABLE); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "DISABLE") == 0 && - pg_strcasecmp(prev_wd, "RULE") == 0) + else if (TailMatches4("ALTER", "TABLE", MatchAny, "DISABLE")) + COMPLETE_WITH_LIST3("ROW LEVEL SECURITY", "RULE", "TRIGGER"); + else if (TailMatches5("ALTER", "TABLE", MatchAny, "DISABLE", "RULE")) { completion_info_charp = prev3_wd; COMPLETE_WITH_QUERY(Query_for_rule_of_table); } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "DISABLE") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) + else if (TailMatches5("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER")) { completion_info_charp = prev3_wd; COMPLETE_WITH_QUERY(Query_for_trigger_of_table); } - else if (pg_strcasecmp(prev4_wd, "DISABLE") == 0 && - pg_strcasecmp(prev3_wd, "ROW") == 0 && - pg_strcasecmp(prev2_wd, "LEVEL") == 0 && - pg_strcasecmp(prev_wd, "SECURITY") == 0) - { - static const char *const list_DISABLERLS[] = - {"CASCADE", NULL}; - - COMPLETE_WITH_LIST(list_DISABLERLS); - } + else if (TailMatches4("DISABLE", "ROW", "LEVEL", "SECURITY")) + COMPLETE_WITH_CONST("CASCADE"); /* ALTER TABLE xxx ALTER */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "ALTER") == 0) + else if (TailMatches4("ALTER", "TABLE", MatchAny, "ALTER")) COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'"); /* ALTER TABLE xxx RENAME */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "RENAME") == 0) + else if (TailMatches4("ALTER", "TABLE", MatchAny, "RENAME")) COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'"); /* * If we have TABLE ALTER COLUMN|RENAME COLUMN, provide list of * columns */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - (pg_strcasecmp(prev2_wd, "ALTER") == 0 || - pg_strcasecmp(prev2_wd, "RENAME") == 0) && - pg_strcasecmp(prev_wd, "COLUMN") == 0) + else if (TailMatches4("TABLE", MatchAny, "ALTER|RENAME", "COLUMN")) COMPLETE_WITH_ATTR(prev3_wd, ""); /* ALTER TABLE xxx RENAME yyy */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "RENAME") == 0 && - pg_strcasecmp(prev_wd, "CONSTRAINT") != 0 && - pg_strcasecmp(prev_wd, "TO") != 0) + else if (TailMatches5("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO"))) COMPLETE_WITH_CONST("TO"); /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */ - else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "RENAME") == 0 && - (pg_strcasecmp(prev2_wd, "COLUMN") == 0 || - pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0) && - pg_strcasecmp(prev_wd, "TO") != 0) + else if (TailMatches5("TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO"))) COMPLETE_WITH_CONST("TO"); /* If we have TABLE DROP, provide COLUMN or CONSTRAINT */ - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "DROP") == 0) - { - static const char *const list_TABLEDROP[] = - {"COLUMN", "CONSTRAINT", NULL}; - - COMPLETE_WITH_LIST(list_TABLEDROP); - } + else if (TailMatches3("TABLE", MatchAny, "DROP")) + COMPLETE_WITH_LIST2("COLUMN", "CONSTRAINT"); /* If we have ALTER TABLE DROP COLUMN, provide list of columns */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "COLUMN") == 0) + else if (TailMatches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN")) COMPLETE_WITH_ATTR(prev3_wd, ""); /* * If we have ALTER TABLE ALTER|DROP|RENAME|VALIDATE CONSTRAINT, * provide list of constraints */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLE") == 0 && - (pg_strcasecmp(prev2_wd, "ALTER") == 0 || - pg_strcasecmp(prev2_wd, "DROP") == 0 || - pg_strcasecmp(prev2_wd, "RENAME") == 0 || - pg_strcasecmp(prev2_wd, "VALIDATE") == 0) && - pg_strcasecmp(prev_wd, "CONSTRAINT") == 0) + else if (TailMatches5("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME|VALIDATE", "CONSTRAINT")) { completion_info_charp = prev3_wd; COMPLETE_WITH_QUERY(Query_for_constraint_of_table); } /* ALTER TABLE ALTER [COLUMN] */ - else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "COLUMN") == 0) || - (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "ALTER") == 0)) - { - static const char *const list_COLUMNALTER[] = - {"TYPE", "SET", "RESET", "DROP", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNALTER); - } + else if (TailMatches3("ALTER", "COLUMN", MatchAny) || + TailMatches4("TABLE", MatchAny, "ALTER", MatchAny)) + COMPLETE_WITH_LIST4("TYPE", "SET", "RESET", "DROP"); /* ALTER TABLE ALTER [COLUMN] SET */ - else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "COLUMN") == 0) || - (pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "ALTER") == 0)) && - pg_strcasecmp(prev_wd, "SET") == 0) - { - static const char *const list_COLUMNSET[] = - {"(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNSET); - } + else if (TailMatches4("ALTER", "COLUMN", MatchAny, "SET") || + TailMatches5("TABLE", MatchAny, "ALTER", MatchAny, "SET")) + COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE"); /* ALTER TABLE ALTER [COLUMN] SET ( */ - else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "COLUMN") == 0) || - pg_strcasecmp(prev4_wd, "ALTER") == 0) && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) - { - static const char *const list_COLUMNOPTIONS[] = - {"n_distinct", "n_distinct_inherited", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNOPTIONS); - } + else if (TailMatches5("ALTER", "COLUMN", MatchAny, "SET", "(") || + TailMatches4("ALTER", MatchAny, "SET", "(")) + COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited"); /* ALTER TABLE ALTER [COLUMN] SET STORAGE */ - else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "COLUMN") == 0) || - pg_strcasecmp(prev4_wd, "ALTER") == 0) && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "STORAGE") == 0) - { - static const char *const list_COLUMNSTORAGE[] = - {"PLAIN", "EXTERNAL", "EXTENDED", "MAIN", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNSTORAGE); - } + else if (TailMatches5("ALTER", "COLUMN", MatchAny, "SET", "STORAGE") || + TailMatches4("ALTER", MatchAny, "SET", "STORAGE")) + COMPLETE_WITH_LIST4("PLAIN", "EXTERNAL", "EXTENDED", "MAIN"); /* ALTER TABLE ALTER [COLUMN] DROP */ - else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "COLUMN") == 0) || - (pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "ALTER") == 0)) && - pg_strcasecmp(prev_wd, "DROP") == 0) - { - static const char *const list_COLUMNDROP[] = - {"DEFAULT", "NOT NULL", NULL}; - - COMPLETE_WITH_LIST(list_COLUMNDROP); - } - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "CLUSTER") == 0) + else if (TailMatches4("ALTER", "COLUMN", MatchAny, "DROP") || + TailMatches5("TABLE", MatchAny, "ALTER", MatchAny, "DROP")) + COMPLETE_WITH_LIST2("DEFAULT", "NOT NULL"); + else if (TailMatches3("TABLE", MatchAny, "CLUSTER")) COMPLETE_WITH_CONST("ON"); - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("TABLE", MatchAny, "CLUSTER", "ON")) { completion_info_charp = prev3_wd; COMPLETE_WITH_QUERY(Query_for_index_of_table); } /* If we have TABLE SET, provide list of attributes and '(' */ - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "SET") == 0) - { - static const char *const list_TABLESET[] = - {"(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT", NULL}; - - COMPLETE_WITH_LIST(list_TABLESET); - } + else if (TailMatches3("TABLE", MatchAny, "SET")) + COMPLETE_WITH_LIST7("(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED", + "WITH", "WITHOUT"); /* If we have TABLE SET TABLESPACE provide a list of tablespaces */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "TABLESPACE") == 0) + else if (TailMatches4("TABLE", MatchAny, "SET", "TABLESPACE")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); /* If we have TABLE SET WITHOUT provide CLUSTER or OIDS */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "WITHOUT") == 0) - { - static const char *const list_TABLESET2[] = - {"CLUSTER", "OIDS", NULL}; - - COMPLETE_WITH_LIST(list_TABLESET2); - } + else if (TailMatches4("TABLE", MatchAny, "SET", "WITHOUT")) + COMPLETE_WITH_LIST2("CLUSTER", "OIDS"); /* ALTER TABLE RESET */ - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "RESET") == 0) + else if (TailMatches3("TABLE", MatchAny, "RESET")) COMPLETE_WITH_CONST("("); /* ALTER TABLE SET|RESET ( */ - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - (pg_strcasecmp(prev2_wd, "SET") == 0 || - pg_strcasecmp(prev2_wd, "RESET") == 0) && - pg_strcasecmp(prev_wd, "(") == 0) + else if (TailMatches4("TABLE", MatchAny, "SET|RESET", "(")) { static const char *const list_TABLEOPTIONS[] = { @@ -1880,246 +1692,112 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_TABLEOPTIONS); } - else if (pg_strcasecmp(prev4_wd, "REPLICA") == 0 && - pg_strcasecmp(prev3_wd, "IDENTITY") == 0 && - pg_strcasecmp(prev2_wd, "USING") == 0 && - pg_strcasecmp(prev_wd, "INDEX") == 0) + else if (TailMatches4("REPLICA", "IDENTITY", "USING", "INDEX")) { completion_info_charp = prev5_wd; COMPLETE_WITH_QUERY(Query_for_index_of_table); } - else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "REPLICA") == 0 && - pg_strcasecmp(prev2_wd, "IDENTITY") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) - { + else if (TailMatches5("TABLE", MatchAny, "REPLICA", "IDENTITY", "USING")) COMPLETE_WITH_CONST("INDEX"); - } - else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "REPLICA") == 0 && - pg_strcasecmp(prev_wd, "IDENTITY") == 0) - { - static const char *const list_REPLICAID[] = - {"FULL", "NOTHING", "DEFAULT", "USING", NULL}; - - COMPLETE_WITH_LIST(list_REPLICAID); - } - else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev_wd, "REPLICA") == 0) - { + else if (TailMatches4("TABLE", MatchAny, "REPLICA", "IDENTITY")) + COMPLETE_WITH_LIST4("FULL", "NOTHING", "DEFAULT", "USING"); + else if (TailMatches3("TABLE", MatchAny, "REPLICA")) COMPLETE_WITH_CONST("IDENTITY"); - } /* ALTER TABLESPACE with RENAME TO, OWNER TO, SET, RESET */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TABLESPACE") == 0) - { - static const char *const list_ALTERTSPC[] = - {"RENAME TO", "OWNER TO", "SET", "RESET", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTSPC); - } + else if (TailMatches3("ALTER", "TABLESPACE", MatchAny)) + COMPLETE_WITH_LIST4("RENAME TO", "OWNER TO", "SET", "RESET"); /* ALTER TABLESPACE SET|RESET */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLESPACE") == 0 && - (pg_strcasecmp(prev_wd, "SET") == 0 || - pg_strcasecmp(prev_wd, "RESET") == 0)) + else if (TailMatches4("ALTER", "TABLESPACE", MatchAny, "SET|RESET")) COMPLETE_WITH_CONST("("); /* ALTER TABLESPACE SET|RESET ( */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 && - (pg_strcasecmp(prev2_wd, "SET") == 0 || - pg_strcasecmp(prev2_wd, "RESET") == 0) && - pg_strcasecmp(prev_wd, "(") == 0) - { - static const char *const list_TABLESPACEOPTIONS[] = - {"seq_page_cost", "random_page_cost", "effective_io_concurrency", NULL}; - - COMPLETE_WITH_LIST(list_TABLESPACEOPTIONS); - } + else if (TailMatches5("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "(")) + COMPLETE_WITH_LIST3("seq_page_cost", "random_page_cost", + "effective_io_concurrency"); /* ALTER TEXT SEARCH */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TEXT") == 0 && - pg_strcasecmp(prev_wd, "SEARCH") == 0) - { - static const char *const list_ALTERTEXTSEARCH[] = - {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH); - } - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - (pg_strcasecmp(prev2_wd, "TEMPLATE") == 0 || - pg_strcasecmp(prev2_wd, "PARSER") == 0)) - { - static const char *const list_ALTERTEXTSEARCH2[] = - {"RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2); - } - - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - pg_strcasecmp(prev2_wd, "DICTIONARY") == 0) - { - static const char *const list_ALTERTEXTSEARCH3[] = - {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3); - } - - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0) - { - static const char *const list_ALTERTEXTSEARCH4[] = - {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH4); - } + else if (TailMatches3("ALTER", "TEXT", "SEARCH")) + COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); + else if (TailMatches5("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny)) + COMPLETE_WITH_LIST2("RENAME TO", "SET SCHEMA"); + else if (TailMatches5("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny)) + COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); + else if (TailMatches5("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny)) + COMPLETE_WITH_LIST6("ADD MAPPING FOR", "ALTER MAPPING", + "DROP MAPPING FOR", + "OWNER TO", "RENAME TO", "SET SCHEMA"); /* complete ALTER TYPE with actions */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "TYPE") == 0) - { - static const char *const list_ALTERTYPE[] = - {"ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE", - "OWNER TO", "RENAME", "SET SCHEMA", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTYPE); - } + else if (TailMatches3("ALTER", "TYPE", MatchAny)) + COMPLETE_WITH_LIST7("ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE", + "DROP ATTRIBUTE", + "OWNER TO", "RENAME", "SET SCHEMA"); /* complete ALTER TYPE ADD with actions */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TYPE") == 0 && - pg_strcasecmp(prev_wd, "ADD") == 0) - { - static const char *const list_ALTERTYPE[] = - {"ATTRIBUTE", "VALUE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTYPE); - } + else if (TailMatches4("ALTER", "TYPE", MatchAny, "ADD")) + COMPLETE_WITH_LIST2("ATTRIBUTE", "VALUE"); /* ALTER TYPE RENAME */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TYPE") == 0 && - pg_strcasecmp(prev_wd, "RENAME") == 0) - { - static const char *const list_ALTERTYPE[] = - {"ATTRIBUTE", "TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTYPE); - } + else if (TailMatches4("ALTER", "TYPE", MatchAny, "RENAME")) + COMPLETE_WITH_LIST2("ATTRIBUTE", "TO"); /* ALTER TYPE xxx RENAME ATTRIBUTE yyy */ - else if (pg_strcasecmp(prev5_wd, "TYPE") == 0 && - pg_strcasecmp(prev3_wd, "RENAME") == 0 && - pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0) + else if (TailMatches5("TYPE", MatchAny, "RENAME", "ATTRIBUTE", MatchAny)) COMPLETE_WITH_CONST("TO"); /* * If we have TYPE ALTER/DROP/RENAME ATTRIBUTE, provide list of * attributes */ - else if (pg_strcasecmp(prev4_wd, "TYPE") == 0 && - (pg_strcasecmp(prev2_wd, "ALTER") == 0 || - pg_strcasecmp(prev2_wd, "DROP") == 0 || - pg_strcasecmp(prev2_wd, "RENAME") == 0) && - pg_strcasecmp(prev_wd, "ATTRIBUTE") == 0) + else if (TailMatches4("TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE")) COMPLETE_WITH_ATTR(prev3_wd, ""); /* ALTER TYPE ALTER ATTRIBUTE */ - else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0)) - { + else if (TailMatches3("ALTER", "ATTRIBUTE", MatchAny)) COMPLETE_WITH_CONST("TYPE"); - } /* complete ALTER GROUP */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "GROUP") == 0) - { - static const char *const list_ALTERGROUP[] = - {"ADD USER", "DROP USER", "RENAME TO", NULL}; - - COMPLETE_WITH_LIST(list_ALTERGROUP); - } + else if (TailMatches3("ALTER", "GROUP", MatchAny)) + COMPLETE_WITH_LIST3("ADD USER", "DROP USER", "RENAME TO"); /* complete ALTER GROUP ADD|DROP with USER */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "GROUP") == 0 && - (pg_strcasecmp(prev_wd, "ADD") == 0 || - pg_strcasecmp(prev_wd, "DROP") == 0)) + else if (TailMatches4("ALTER", "GROUP", MatchAny, "ADD|DROP")) COMPLETE_WITH_CONST("USER"); - /* complete {ALTER} GROUP ADD|DROP USER with a user name */ - else if (pg_strcasecmp(prev4_wd, "GROUP") == 0 && - (pg_strcasecmp(prev2_wd, "ADD") == 0 || - pg_strcasecmp(prev2_wd, "DROP") == 0) && - pg_strcasecmp(prev_wd, "USER") == 0) + /* complete ALTER GROUP ADD|DROP USER with a user name */ + else if (TailMatches5("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); /* BEGIN, END, ABORT */ - else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 || - pg_strcasecmp(prev_wd, "END") == 0 || - pg_strcasecmp(prev_wd, "ABORT") == 0) - { - static const char *const list_TRANS[] = - {"WORK", "TRANSACTION", NULL}; - - COMPLETE_WITH_LIST(list_TRANS); - } + else if (TailMatches1("BEGIN|END|ABORT")) + COMPLETE_WITH_LIST2("WORK", "TRANSACTION"); /* COMMIT */ - else if (pg_strcasecmp(prev_wd, "COMMIT") == 0) - { - static const char *const list_COMMIT[] = - {"WORK", "TRANSACTION", "PREPARED", NULL}; - - COMPLETE_WITH_LIST(list_COMMIT); - } + else if (TailMatches1("COMMIT")) + COMPLETE_WITH_LIST3("WORK", "TRANSACTION", "PREPARED"); /* RELEASE SAVEPOINT */ - else if (pg_strcasecmp(prev_wd, "RELEASE") == 0) + else if (TailMatches1("RELEASE")) COMPLETE_WITH_CONST("SAVEPOINT"); /* ROLLBACK*/ - else if (pg_strcasecmp(prev_wd, "ROLLBACK") == 0) - { - static const char *const list_TRANS[] = - {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL}; - - COMPLETE_WITH_LIST(list_TRANS); - } + else if (TailMatches1("ROLLBACK")) + COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED"); /* CLUSTER */ /* * If the previous word is CLUSTER and not WITHOUT produce list of tables */ - else if (pg_strcasecmp(prev_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev2_wd, "WITHOUT") != 0) + else if (TailMatches1("CLUSTER") && !TailMatches2("WITHOUT", "CLUSTER")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'"); /* * If the previous words are CLUSTER VERBOSE produce list of tables */ - else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 && - pg_strcasecmp(prev2_wd, "CLUSTER") == 0) + else if (TailMatches2("CLUSTER", "VERBOSE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); /* If we have CLUSTER , then add "USING" */ - else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev_wd, "ON") != 0 && - pg_strcasecmp(prev_wd, "VERBOSE") != 0) - { + else if (TailMatches2("CLUSTER", MatchAnyExcept("VERBOSE|ON"))) COMPLETE_WITH_CONST("USING"); - } /* If we have CLUSTER VERBOSE , then add "USING" */ - else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev2_wd, "VERBOSE") == 0) - { + else if (TailMatches3("CLUSTER", "VERBOSE", MatchAny)) COMPLETE_WITH_CONST("USING"); - } /* * If we have CLUSTER USING, then add the index as well. */ - else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) + else if (TailMatches3("CLUSTER", MatchAny, "USING")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_index_of_table); @@ -2128,19 +1806,16 @@ psql_completion(const char *text, int start, int end) /* * If we have CLUSTER VERBOSE USING, then add the index as well. */ - else if (pg_strcasecmp(prev4_wd, "CLUSTER") == 0 && - pg_strcasecmp(prev3_wd, "VERBOSE") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) + else if (TailMatches4("CLUSTER", "VERBOSE", MatchAny, "USING")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_index_of_table); } /* COMMENT */ - else if (pg_strcasecmp(prev_wd, "COMMENT") == 0) + else if (TailMatches1("COMMENT")) COMPLETE_WITH_CONST("ON"); - else if (pg_strcasecmp(prev2_wd, "COMMENT") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches2("COMMENT", "ON")) { static const char *const list_COMMENT[] = {"CAST", "COLLATION", "CONVERSION", "DATABASE", "EVENT TRIGGER", "EXTENSION", @@ -2152,66 +1827,26 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_COMMENT); } - else if (pg_strcasecmp(prev3_wd, "COMMENT") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const list_TRANS2[] = - {"DATA WRAPPER", "TABLE", NULL}; - - COMPLETE_WITH_LIST(list_TRANS2); - } - else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "TEXT") == 0 && - pg_strcasecmp(prev_wd, "SEARCH") == 0) - { - static const char *const list_TRANS2[] = - {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - - COMPLETE_WITH_LIST(list_TRANS2); - } - else if (pg_strcasecmp(prev3_wd, "COMMENT") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "CONSTRAINT") == 0) - { + else if (TailMatches3("COMMENT", "ON", "FOREIGN")) + COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); + else if (TailMatches4("COMMENT", "ON", "TEXT", "SEARCH")) + COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); + else if (TailMatches3("COMMENT", "ON", "CONSTRAINT")) COMPLETE_WITH_QUERY(Query_for_all_table_constraints); - } - else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0) - { + else if (TailMatches4("COMMENT", "ON", "CONSTRAINT", MatchAny)) COMPLETE_WITH_CONST("ON"); - } - else if (pg_strcasecmp(prev5_wd, "COMMENT") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev3_wd, "CONSTRAINT") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches5("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint); } - else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev_wd, "VIEW") == 0) - { + else if (TailMatches4("COMMENT", "ON", "MATERIALIZED", "VIEW")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); - } - else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "EVENT") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { + else if (TailMatches4("COMMENT", "ON", "EVENT", "TRIGGER")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); - } - else if (((pg_strcasecmp(prev4_wd, "COMMENT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) || - (pg_strcasecmp(prev5_wd, "COMMENT") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0) || - (pg_strcasecmp(prev6_wd, "COMMENT") == 0 && - pg_strcasecmp(prev5_wd, "ON") == 0)) && - pg_strcasecmp(prev_wd, "IS") != 0) + else if (TailMatches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) || + TailMatches5("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")) || + TailMatches6("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS"))) COMPLETE_WITH_CONST("IS"); /* COPY */ @@ -2220,478 +1855,216 @@ psql_completion(const char *text, int start, int end) * If we have COPY [BINARY] (which you'd have to type yourself), offer * list of tables (Also cover the analogous backslash command) */ - else if (pg_strcasecmp(prev_wd, "COPY") == 0 || - pg_strcasecmp(prev_wd, "\\copy") == 0 || - (pg_strcasecmp(prev2_wd, "COPY") == 0 && - pg_strcasecmp(prev_wd, "BINARY") == 0)) + else if (TailMatches1("COPY|\\copy") || TailMatches2("COPY", "BINARY")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); /* If we have COPY|BINARY , complete it with "TO" or "FROM" */ - else if (pg_strcasecmp(prev2_wd, "COPY") == 0 || - pg_strcasecmp(prev2_wd, "\\copy") == 0 || - pg_strcasecmp(prev2_wd, "BINARY") == 0) - { - static const char *const list_FROMTO[] = - {"FROM", "TO", NULL}; - - COMPLETE_WITH_LIST(list_FROMTO); - } + else if (TailMatches2("COPY|\\copy|BINARY", MatchAny)) + COMPLETE_WITH_LIST2("FROM", "TO"); /* If we have COPY|BINARY FROM|TO, complete with filename */ - else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 || - pg_strcasecmp(prev3_wd, "\\copy") == 0 || - pg_strcasecmp(prev3_wd, "BINARY") == 0) && - (pg_strcasecmp(prev_wd, "FROM") == 0 || - pg_strcasecmp(prev_wd, "TO") == 0)) + else if (TailMatches3("COPY|\\copy|BINARY", MatchAny, "FROM|TO")) { completion_charp = ""; matches = completion_matches(text, complete_from_files); } /* Handle COPY|BINARY FROM|TO filename */ - else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 || - pg_strcasecmp(prev4_wd, "\\copy") == 0 || - pg_strcasecmp(prev4_wd, "BINARY") == 0) && - (pg_strcasecmp(prev2_wd, "FROM") == 0 || - pg_strcasecmp(prev2_wd, "TO") == 0)) - { - static const char *const list_COPY[] = - {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", "ENCODING", NULL}; - - COMPLETE_WITH_LIST(list_COPY); - } + else if (TailMatches4("COPY|\\copy|BINARY", MatchAny, "FROM|TO", MatchAny)) + COMPLETE_WITH_LIST6("BINARY", "OIDS", "DELIMITER", "NULL", "CSV", + "ENCODING"); /* Handle COPY|BINARY FROM|TO filename CSV */ - else if (pg_strcasecmp(prev_wd, "CSV") == 0 && - (pg_strcasecmp(prev3_wd, "FROM") == 0 || - pg_strcasecmp(prev3_wd, "TO") == 0)) - { - static const char *const list_CSV[] = - {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", "FORCE NOT NULL", NULL}; - - COMPLETE_WITH_LIST(list_CSV); - } + else if (TailMatches3("FROM|TO", MatchAny, "CSV")) + COMPLETE_WITH_LIST5("HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", + "FORCE NOT NULL"); /* CREATE DATABASE */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "DATABASE") == 0) - { - static const char *const list_DATABASE[] = - {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "IS_TEMPLATE", - "ALLOW_CONNECTIONS", "CONNECTION LIMIT", "LC_COLLATE", "LC_CTYPE", - NULL}; + else if (TailMatches3("CREATE", "DATABASE", MatchAny)) + COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", + "IS_TEMPLATE", + "ALLOW_CONNECTIONS", "CONNECTION LIMIT", + "LC_COLLATE", "LC_CTYPE"); - COMPLETE_WITH_LIST(list_DATABASE); - } - - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "DATABASE") == 0 && - pg_strcasecmp(prev_wd, "TEMPLATE") == 0) + else if (TailMatches4("CREATE", "DATABASE", MatchAny, "TEMPLATE")) COMPLETE_WITH_QUERY(Query_for_list_of_template_databases); /* CREATE EXTENSION */ /* Complete with available extensions rather than installed ones. */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "EXTENSION") == 0) + else if (TailMatches2("CREATE", "EXTENSION")) COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions); /* CREATE EXTENSION */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "EXTENSION") == 0) - { - static const char *const list_CREATE_EXTENSION[] = - {"WITH SCHEMA", "CASCADE", "VERSION", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_EXTENSION); - } + else if (TailMatches3("CREATE", "EXTENSION", MatchAny)) + COMPLETE_WITH_LIST3("WITH SCHEMA", "CASCADE", "VERSION"); /* CREATE EXTENSION VERSION */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "EXTENSION") == 0 && - pg_strcasecmp(prev_wd, "VERSION") == 0) + else if (TailMatches4("CREATE", "EXTENSION", MatchAny, "VERSION")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions); } /* CREATE FOREIGN */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const list_CREATE_FOREIGN[] = - {"DATA WRAPPER", "TABLE", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_FOREIGN); - } + else if (TailMatches2("CREATE", "FOREIGN")) + COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); /* CREATE FOREIGN DATA WRAPPER */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev3_wd, "DATA") == 0 && - pg_strcasecmp(prev2_wd, "WRAPPER") == 0) - { - static const char *const list_CREATE_FOREIGN_DATA_WRAPPER[] = - {"HANDLER", "VALIDATOR", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_FOREIGN_DATA_WRAPPER); - } + else if (TailMatches5("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny)) + COMPLETE_WITH_LIST2("HANDLER", "VALIDATOR"); /* CREATE INDEX */ /* First off we complete CREATE UNIQUE with "INDEX" */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "UNIQUE") == 0) + else if (TailMatches2("CREATE", "UNIQUE")) COMPLETE_WITH_CONST("INDEX"); /* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */ - else if (pg_strcasecmp(prev_wd, "INDEX") == 0 && - (pg_strcasecmp(prev2_wd, "CREATE") == 0 || - pg_strcasecmp(prev2_wd, "UNIQUE") == 0)) + else if (TailMatches2("CREATE|UNIQUE", "INDEX")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, " UNION SELECT 'ON'" " UNION SELECT 'CONCURRENTLY'"); /* Complete ... INDEX [] ON with a list of tables */ - else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 || - pg_strcasecmp(prev2_wd, "INDEX") == 0 || - pg_strcasecmp(prev2_wd, "CONCURRENTLY") == 0) && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches3("INDEX", MatchAny, "ON") || + TailMatches2("INDEX|CONCURRENTLY", "ON")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); /* If we have CREATE|UNIQUE INDEX CONCURRENTLY, then add "ON" */ - else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 || - pg_strcasecmp(prev2_wd, "INDEX") == 0) && - pg_strcasecmp(prev_wd, "CONCURRENTLY") == 0) + else if (TailMatches3("INDEX", MatchAny, "CONCURRENTLY") || + TailMatches2("INDEX", "CONCURRENTLY")) COMPLETE_WITH_CONST("ON"); /* If we have CREATE|UNIQUE INDEX , then add "ON" or "CONCURRENTLY" */ - else if ((pg_strcasecmp(prev3_wd, "CREATE") == 0 || - pg_strcasecmp(prev3_wd, "UNIQUE") == 0) && - pg_strcasecmp(prev2_wd, "INDEX") == 0) - { - static const char *const list_CREATE_INDEX[] = - {"CONCURRENTLY", "ON", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_INDEX); - } + else if (TailMatches3("CREATE|UNIQUE", "INDEX", MatchAny)) + COMPLETE_WITH_LIST2("CONCURRENTLY", "ON"); /* * Complete INDEX ON
with a list of table columns (which * should really be in parens) */ - else if ((pg_strcasecmp(prev4_wd, "INDEX") == 0 || - pg_strcasecmp(prev3_wd, "INDEX") == 0 || - pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0) && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_CREATE_INDEX2[] = - {"(", "USING", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_INDEX2); - } - else if ((pg_strcasecmp(prev5_wd, "INDEX") == 0 || - pg_strcasecmp(prev4_wd, "INDEX") == 0 || - pg_strcasecmp(prev4_wd, "CONCURRENTLY") == 0) && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) + else if (TailMatches4("INDEX", MatchAny, "ON", MatchAny) || + TailMatches3("INDEX|CONCURRENTLY", "ON", MatchAny)) + COMPLETE_WITH_LIST2("(", "USING"); + else if (TailMatches5("INDEX", MatchAny, "ON", MatchAny, "(") || + TailMatches4("INDEX|CONCURRENTLY", "ON", MatchAny, "(")) COMPLETE_WITH_ATTR(prev2_wd, ""); /* same if you put in USING */ - else if (pg_strcasecmp(prev5_wd, "ON") == 0 && - pg_strcasecmp(prev3_wd, "USING") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) + else if (TailMatches5("ON", MatchAny, "USING", MatchAny, "(")) COMPLETE_WITH_ATTR(prev4_wd, ""); /* Complete USING with an index method */ - else if ((pg_strcasecmp(prev6_wd, "INDEX") == 0 || - pg_strcasecmp(prev5_wd, "INDEX") == 0 || - pg_strcasecmp(prev4_wd, "INDEX") == 0) && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) + else if (TailMatches6("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING") || + TailMatches5("INDEX", MatchAny, "ON", MatchAny, "USING") || + TailMatches4("INDEX", "ON", MatchAny, "USING")) COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); - else if (pg_strcasecmp(prev4_wd, "ON") == 0 && - (!(pg_strcasecmp(prev6_wd, "POLICY") == 0) && - !(pg_strcasecmp(prev4_wd, "FOR") == 0)) && - pg_strcasecmp(prev2_wd, "USING") == 0) + else if (TailMatches4("ON", MatchAny, "USING", MatchAny) && + !TailMatches6("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) && + !TailMatches4("FOR", MatchAny, MatchAny, MatchAny)) COMPLETE_WITH_CONST("("); /* CREATE POLICY */ /* Complete "CREATE POLICY ON" */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "POLICY") == 0) + else if (TailMatches3("CREATE", "POLICY", MatchAny)) COMPLETE_WITH_CONST("ON"); /* Complete "CREATE POLICY ON
" */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "POLICY") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("CREATE", "POLICY", MatchAny, "ON")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); /* Complete "CREATE POLICY ON
FOR|TO|USING|WITH CHECK" */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "POLICY") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_POLICYOPTIONS[] = - {"FOR", "TO", "USING", "WITH CHECK", NULL}; - - COMPLETE_WITH_LIST(list_POLICYOPTIONS); - } - - /* - * Complete "CREATE POLICY ON
FOR - * ALL|SELECT|INSERT|UPDATE|DELETE" - */ - else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "FOR") == 0) - { - static const char *const list_POLICYCMDS[] = - {"ALL", "SELECT", "INSERT", "UPDATE", "DELETE", NULL}; - - COMPLETE_WITH_LIST(list_POLICYCMDS); - } + else if (TailMatches5("CREATE", "POLICY", MatchAny, "ON", MatchAny)) + COMPLETE_WITH_LIST4("FOR", "TO", "USING", "WITH CHECK"); + /* CREATE POLICY ON
FOR ALL|SELECT|INSERT|UPDATE|DELETE */ + else if (TailMatches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR")) + COMPLETE_WITH_LIST5("ALL", "SELECT", "INSERT", "UPDATE", "DELETE"); /* Complete "CREATE POLICY ON
FOR INSERT TO|WITH CHECK" */ - else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0 && - pg_strcasecmp(prev_wd, "INSERT") == 0) - { - static const char *const list_POLICYOPTIONS[] = - {"TO", "WITH CHECK", NULL}; - - COMPLETE_WITH_LIST(list_POLICYOPTIONS); - } - - /* - * Complete "CREATE POLICY ON
FOR SELECT TO|USING" Complete - * "CREATE POLICY ON
FOR DELETE TO|USING" - */ - else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0 && - (pg_strcasecmp(prev_wd, "SELECT") == 0 || - pg_strcasecmp(prev_wd, "DELETE") == 0)) - { - static const char *const list_POLICYOPTIONS[] = - {"TO", "USING", NULL}; - - COMPLETE_WITH_LIST(list_POLICYOPTIONS); - } - - /* - * Complete "CREATE POLICY ON
FOR ALL TO|USING|WITH CHECK" - * Complete "CREATE POLICY ON
FOR UPDATE TO|USING|WITH - * CHECK" - */ - else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0 && - (pg_strcasecmp(prev_wd, "ALL") == 0 || - pg_strcasecmp(prev_wd, "UPDATE") == 0)) - { - static const char *const list_POLICYOPTIONS[] = - {"TO", "USING", "WITH CHECK", NULL}; - - COMPLETE_WITH_LIST(list_POLICYOPTIONS); - } + else if (TailMatches6("POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT")) + COMPLETE_WITH_LIST2("TO", "WITH CHECK"); + /* Complete "CREATE POLICY ON
FOR SELECT|DELETE TO|USING" */ + else if (TailMatches6("POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE")) + COMPLETE_WITH_LIST2("TO", "USING"); + /* CREATE POLICY ON
FOR ALL|UPDATE TO|USING|WITH CHECK */ + else if (TailMatches6("POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE")) + COMPLETE_WITH_LIST3("TO", "USING", "WITH CHECK"); /* Complete "CREATE POLICY ON
TO " */ - else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) + else if (TailMatches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO")) COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); /* Complete "CREATE POLICY ON
USING (" */ - else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 && - pg_strcasecmp(prev5_wd, "POLICY") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "USING") == 0) + else if (TailMatches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING")) COMPLETE_WITH_CONST("("); /* CREATE RULE */ /* Complete "CREATE RULE " with "AS" */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "RULE") == 0) + else if (TailMatches3("CREATE", "RULE", MatchAny)) COMPLETE_WITH_CONST("AS"); - /* Complete "CREATE RULE AS with "ON" */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "RULE") == 0 && - pg_strcasecmp(prev_wd, "AS") == 0) + /* Complete "CREATE RULE AS" with "ON" */ + else if (TailMatches4("CREATE", "RULE", MatchAny, "AS")) COMPLETE_WITH_CONST("ON"); - /* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */ - else if (pg_strcasecmp(prev4_wd, "RULE") == 0 && - pg_strcasecmp(prev2_wd, "AS") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - static const char *const rule_events[] = - {"SELECT", "UPDATE", "INSERT", "DELETE", NULL}; - - COMPLETE_WITH_LIST(rule_events); - } - /* Complete "AS ON " with a "TO" */ - else if (pg_strcasecmp(prev3_wd, "AS") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0 && - (pg_toupper((unsigned char) prev_wd[4]) == 'T' || - pg_toupper((unsigned char) prev_wd[5]) == 'T')) + /* Complete "RULE * AS ON" with SELECT|UPDATE|INSERT|DELETE */ + else if (TailMatches4("RULE", MatchAny, "AS", "ON")) + COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE"); + /* Complete "AS ON SELECT|UPDATE|INSERT|DELETE" with a "TO" */ + else if (TailMatches3("AS", "ON", "SELECT|UPDATE|INSERT|DELETE")) COMPLETE_WITH_CONST("TO"); /* Complete "AS ON TO" with a table name */ - else if (pg_strcasecmp(prev4_wd, "AS") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) + else if (TailMatches4("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); /* CREATE TEMP/TEMPORARY SEQUENCE */ - else if ((pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "SEQUENCE") == 0) || - (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "TEMP") == 0 || - pg_strcasecmp(prev3_wd, "TEMPORARY") == 0) && - pg_strcasecmp(prev2_wd, "SEQUENCE") == 0)) - { - static const char *const list_CREATESEQUENCE[] = - {"INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE", - "CYCLE", "OWNED BY", "START WITH", NULL}; - - COMPLETE_WITH_LIST(list_CREATESEQUENCE); - } + else if (TailMatches3("CREATE", "SEQUENCE", MatchAny) || + TailMatches4("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny)) + COMPLETE_WITH_LIST8("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE", + "CYCLE", "OWNED BY", "START WITH"); /* CREATE TEMP/TEMPORARY SEQUENCE NO */ - else if (((pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "SEQUENCE") == 0) || - (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - (pg_strcasecmp(prev4_wd, "TEMP") == 0 || - pg_strcasecmp(prev4_wd, "TEMPORARY") == 0) && - pg_strcasecmp(prev3_wd, "SEQUENCE") == 0)) && - pg_strcasecmp(prev_wd, "NO") == 0) - { - static const char *const list_CREATESEQUENCE2[] = - {"MINVALUE", "MAXVALUE", "CYCLE", NULL}; - - COMPLETE_WITH_LIST(list_CREATESEQUENCE2); - } + else if (TailMatches4("CREATE", "SEQUENCE", MatchAny, "NO") || + TailMatches5("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO")) + COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE"); /* CREATE SERVER */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "SERVER") == 0) - { - static const char *const list_CREATE_SERVER[] = - {"TYPE", "VERSION", "FOREIGN DATA WRAPPER", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_SERVER); - } + else if (TailMatches3("CREATE", "SERVER", MatchAny)) + COMPLETE_WITH_LIST3("TYPE", "VERSION", "FOREIGN DATA WRAPPER"); /* CREATE TABLE */ /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - (pg_strcasecmp(prev_wd, "TEMP") == 0 || - pg_strcasecmp(prev_wd, "TEMPORARY") == 0)) - { - static const char *const list_TEMP[] = - {"SEQUENCE", "TABLE", "VIEW", NULL}; - - COMPLETE_WITH_LIST(list_TEMP); - } - /* Complete "CREATE UNLOGGED" with TABLE */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "UNLOGGED") == 0) - { - static const char *const list_UNLOGGED[] = - {"TABLE", "MATERIALIZED VIEW", NULL}; - - COMPLETE_WITH_LIST(list_UNLOGGED); - } + else if (TailMatches2("CREATE", "TEMP|TEMPORARY")) + COMPLETE_WITH_LIST3("SEQUENCE", "TABLE", "VIEW"); + /* Complete "CREATE UNLOGGED" with TABLE or MATVIEW */ + else if (TailMatches2("CREATE", "UNLOGGED")) + COMPLETE_WITH_LIST2("TABLE", "MATERIALIZED VIEW"); /* CREATE TABLESPACE */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "TABLESPACE") == 0) - { - static const char *const list_CREATETABLESPACE[] = - {"OWNER", "LOCATION", NULL}; - - COMPLETE_WITH_LIST(list_CREATETABLESPACE); - } + else if (TailMatches3("CREATE", "TABLESPACE", MatchAny)) + COMPLETE_WITH_LIST2("OWNER", "LOCATION"); /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 && - pg_strcasecmp(prev2_wd, "OWNER") == 0) - { + else if (TailMatches5("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny)) COMPLETE_WITH_CONST("LOCATION"); - } /* CREATE TEXT SEARCH */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "TEXT") == 0 && - pg_strcasecmp(prev_wd, "SEARCH") == 0) - { - static const char *const list_CREATETEXTSEARCH[] = - {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - - COMPLETE_WITH_LIST(list_CREATETEXTSEARCH); - } - else if (pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0) + else if (TailMatches3("CREATE", "TEXT", "SEARCH")) + COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); + else if (TailMatches4("TEXT", "SEARCH", "CONFIGURATION", MatchAny)) COMPLETE_WITH_CONST("("); /* CREATE TRIGGER */ - /* complete CREATE TRIGGER with BEFORE,AFTER */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) - { - static const char *const list_CREATETRIGGER[] = - {"BEFORE", "AFTER", "INSTEAD OF", NULL}; - - COMPLETE_WITH_LIST(list_CREATETRIGGER); - } + /* complete CREATE TRIGGER with BEFORE,AFTER,INSTEAD OF */ + else if (TailMatches3("CREATE", "TRIGGER", MatchAny)) + COMPLETE_WITH_LIST3("BEFORE", "AFTER", "INSTEAD OF"); /* complete CREATE TRIGGER BEFORE,AFTER with an event */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - (pg_strcasecmp(prev_wd, "BEFORE") == 0 || - pg_strcasecmp(prev_wd, "AFTER") == 0)) - { - static const char *const list_CREATETRIGGER_EVENTS[] = - {"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL}; - - COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS); - } + else if (TailMatches4("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER")) + COMPLETE_WITH_LIST4("INSERT", "DELETE", "UPDATE", "TRUNCATE"); /* complete CREATE TRIGGER INSTEAD OF with an event */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev2_wd, "INSTEAD") == 0 && - pg_strcasecmp(prev_wd, "OF") == 0) - { - static const char *const list_CREATETRIGGER_EVENTS[] = - {"INSERT", "DELETE", "UPDATE", NULL}; - - COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS); - } + else if (TailMatches5("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF")) + COMPLETE_WITH_LIST3("INSERT", "DELETE", "UPDATE"); /* complete CREATE TRIGGER BEFORE,AFTER sth with OR,ON */ - else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - (pg_strcasecmp(prev2_wd, "BEFORE") == 0 || - pg_strcasecmp(prev2_wd, "AFTER") == 0)) || - (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev3_wd, "INSTEAD") == 0 && - pg_strcasecmp(prev2_wd, "OF") == 0)) - { - static const char *const list_CREATETRIGGER2[] = - {"ON", "OR", NULL}; - - COMPLETE_WITH_LIST(list_CREATETRIGGER2); - } + else if (TailMatches5("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) || + TailMatches6("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny)) + COMPLETE_WITH_LIST2("ON", "OR"); /* * complete CREATE TRIGGER BEFORE,AFTER event ON with a list of * tables */ - else if (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 && - (pg_strcasecmp(prev3_wd, "BEFORE") == 0 || - pg_strcasecmp(prev3_wd, "AFTER") == 0) && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches5("TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */ - else if (pg_strcasecmp(prev4_wd, "INSTEAD") == 0 && - pg_strcasecmp(prev3_wd, "OF") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("INSTEAD", "OF", MatchAny, "ON")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */ - else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0 && - !(pg_strcasecmp(prev2_wd, "GRANT") == 0 && prev3_wd[0] == '\0') && - prev2_wd[0] != '\0') + else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE")) COMPLETE_WITH_CONST("PROCEDURE"); /* CREATE ROLE,USER,GROUP */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && - (pg_strcasecmp(prev2_wd, "ROLE") == 0 || - pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0)) + else if (TailMatches3("CREATE", "ROLE|GROUP|USER", MatchAny) && + !TailMatches2("USER", "MAPPING")) { static const char *const list_CREATEROLE[] = {"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", @@ -2705,11 +2078,7 @@ psql_completion(const char *text, int start, int end) } /* CREATE ROLE,USER,GROUP WITH */ - else if ((pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || - pg_strcasecmp(prev3_wd, "GROUP") == 0 || - pg_strcasecmp(prev3_wd, "USER") == 0) && - pg_strcasecmp(prev_wd, "WITH") == 0)) + else if (TailMatches4("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH")) { /* Similar to the above, but don't complete "WITH" again. */ static const char *const list_CREATEROLE_WITH[] = @@ -2727,323 +2096,153 @@ psql_completion(const char *text, int start, int end) * complete CREATE ROLE,USER,GROUP ENCRYPTED,UNENCRYPTED with * PASSWORD */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || - pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0)) - { + else if (TailMatches4("CREATE", "ROLE|USER|GROUP", MatchAny, "ENCRYPTED|UNENCRYPTED")) COMPLETE_WITH_CONST("PASSWORD"); - } /* complete CREATE ROLE,USER,GROUP IN with ROLE,GROUP */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - (pg_strcasecmp(prev3_wd, "ROLE") == 0 || - pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) && - pg_strcasecmp(prev_wd, "IN") == 0) - { - static const char *const list_CREATEROLE3[] = - {"GROUP", "ROLE", NULL}; - - COMPLETE_WITH_LIST(list_CREATEROLE3); - } + else if (TailMatches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN")) + COMPLETE_WITH_LIST2("GROUP", "ROLE"); /* CREATE VIEW */ /* Complete CREATE VIEW with AS */ - else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) + else if (TailMatches3("CREATE", "VIEW", MatchAny)) COMPLETE_WITH_CONST("AS"); /* Complete "CREATE VIEW AS with "SELECT" */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "VIEW") == 0 && - pg_strcasecmp(prev_wd, "AS") == 0) + else if (TailMatches4("CREATE", "VIEW", MatchAny, "AS")) COMPLETE_WITH_CONST("SELECT"); /* CREATE MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "MATERIALIZED") == 0) + else if (TailMatches2("CREATE", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); /* Complete CREATE MATERIALIZED VIEW with AS */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) + else if (TailMatches4("CREATE", "MATERIALIZED", "VIEW", MatchAny)) COMPLETE_WITH_CONST("AS"); /* Complete "CREATE MATERIALIZED VIEW AS with "SELECT" */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev3_wd, "VIEW") == 0 && - pg_strcasecmp(prev_wd, "AS") == 0) + else if (TailMatches5("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS")) COMPLETE_WITH_CONST("SELECT"); /* CREATE EVENT TRIGGER */ - else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && - pg_strcasecmp(prev_wd, "EVENT") == 0) + else if (TailMatches2("CREATE", "EVENT")) COMPLETE_WITH_CONST("TRIGGER"); /* Complete CREATE EVENT TRIGGER with ON */ - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "EVENT") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) + else if (TailMatches4("CREATE", "EVENT", "TRIGGER", MatchAny)) COMPLETE_WITH_CONST("ON"); /* Complete CREATE EVENT TRIGGER ON with event_type */ - else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 && - pg_strcasecmp(prev4_wd, "EVENT") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) - { - static const char *const list_CREATE_EVENT_TRIGGER_ON[] = - {"ddl_command_start", "ddl_command_end", "sql_drop", NULL}; - - COMPLETE_WITH_LIST(list_CREATE_EVENT_TRIGGER_ON); - } + else if (TailMatches5("CREATE", "EVENT", "TRIGGER", MatchAny, "ON")) + COMPLETE_WITH_LIST3("ddl_command_start", "ddl_command_end", "sql_drop"); /* DECLARE */ - else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0) - { - static const char *const list_DECLARE[] = - {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL}; - - COMPLETE_WITH_LIST(list_DECLARE); - } + else if (TailMatches2("DECLARE", MatchAny)) + COMPLETE_WITH_LIST5("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", + "CURSOR"); /* CURSOR */ - else if (pg_strcasecmp(prev_wd, "CURSOR") == 0) - { - static const char *const list_DECLARECURSOR[] = - {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL}; - - COMPLETE_WITH_LIST(list_DECLARECURSOR); - } - + else if (TailMatches1("CURSOR")) + COMPLETE_WITH_LIST3("WITH HOLD", "WITHOUT HOLD", "FOR"); /* DELETE */ - /* - * Complete DELETE with FROM (only if the word before that is not "ON" - * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT) - */ - else if (pg_strcasecmp(prev_wd, "DELETE") == 0 && - !(pg_strcasecmp(prev2_wd, "ON") == 0 || - pg_strcasecmp(prev2_wd, "GRANT") == 0 || - pg_strcasecmp(prev2_wd, "BEFORE") == 0 || - pg_strcasecmp(prev2_wd, "AFTER") == 0)) + /* Complete DELETE with FROM, only at start of line */ + else if (Matches1("DELETE")) COMPLETE_WITH_CONST("FROM"); /* Complete DELETE FROM with a list of tables */ - else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 && - pg_strcasecmp(prev_wd, "FROM") == 0) + else if (TailMatches2("DELETE", "FROM")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); /* Complete DELETE FROM
*/ - else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 && - pg_strcasecmp(prev2_wd, "FROM") == 0) - { - static const char *const list_DELETE[] = - {"USING", "WHERE", "SET", NULL}; - - COMPLETE_WITH_LIST(list_DELETE); - } + else if (TailMatches3("DELETE", "FROM", MatchAny)) + COMPLETE_WITH_LIST3("USING", "WHERE", "SET"); /* XXX: implement tab completion for DELETE ... USING */ /* DISCARD */ - else if (pg_strcasecmp(prev_wd, "DISCARD") == 0) - { - static const char *const list_DISCARD[] = - {"ALL", "PLANS", "SEQUENCES", "TEMP", NULL}; - - COMPLETE_WITH_LIST(list_DISCARD); - } + else if (TailMatches1("DISCARD")) + COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP"); /* DO */ /* * Complete DO with LANGUAGE. */ - else if (pg_strcasecmp(prev_wd, "DO") == 0) - { - static const char *const list_DO[] = - {"LANGUAGE", NULL}; - - COMPLETE_WITH_LIST(list_DO); - } + else if (TailMatches1("DO")) + COMPLETE_WITH_CONST("LANGUAGE"); /* DROP (when not the previous word) */ - /* DROP AGGREGATE */ - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "AGGREGATE") == 0) - COMPLETE_WITH_CONST("("); - /* DROP object with CASCADE / RESTRICT */ - else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 && - (pg_strcasecmp(prev2_wd, "COLLATION") == 0 || - pg_strcasecmp(prev2_wd, "CONVERSION") == 0 || - pg_strcasecmp(prev2_wd, "DOMAIN") == 0 || - pg_strcasecmp(prev2_wd, "EXTENSION") == 0 || - pg_strcasecmp(prev2_wd, "FUNCTION") == 0 || - pg_strcasecmp(prev2_wd, "INDEX") == 0 || - pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 || - pg_strcasecmp(prev2_wd, "SCHEMA") == 0 || - pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 || - pg_strcasecmp(prev2_wd, "SERVER") == 0 || - pg_strcasecmp(prev2_wd, "TABLE") == 0 || - pg_strcasecmp(prev2_wd, "TYPE") == 0 || - pg_strcasecmp(prev2_wd, "VIEW") == 0)) || - (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 && - prev_wd[strlen(prev_wd) - 1] == ')') || - (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "EVENT") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) || - (pg_strcasecmp(prev5_wd, "DROP") == 0 && - pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev3_wd, "DATA") == 0 && - pg_strcasecmp(prev2_wd, "WRAPPER") == 0) || - (pg_strcasecmp(prev5_wd, "DROP") == 0 && - pg_strcasecmp(prev4_wd, "TEXT") == 0 && - pg_strcasecmp(prev3_wd, "SEARCH") == 0 && - (pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0 || - pg_strcasecmp(prev2_wd, "DICTIONARY") == 0 || - pg_strcasecmp(prev2_wd, "PARSER") == 0 || - pg_strcasecmp(prev2_wd, "TEMPLATE") == 0)) - ) - { - if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "FUNCTION") == 0) - { - COMPLETE_WITH_CONST("("); - } - else - { - static const char *const list_DROPCR[] = - {"CASCADE", "RESTRICT", NULL}; + else if (TailMatches3("DROP", + "COLLATION|CONVERSION|DOMAIN|EXTENSION|INDEX|LANGUAGE|SCHEMA|SEQUENCE|SERVER|TABLE|TYPE|VIEW", + MatchAny) || + (TailMatches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) && + ends_with(prev_wd, ')')) || + TailMatches4("DROP", "EVENT", "TRIGGER", MatchAny) || + TailMatches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) || + TailMatches4("DROP", "FOREIGN", "TABLE", MatchAny) || + TailMatches5("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny)) + COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); - COMPLETE_WITH_LIST(list_DROPCR); - } - } - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const drop_CREATE_FOREIGN[] = - {"DATA WRAPPER", "TABLE", NULL}; - - COMPLETE_WITH_LIST(drop_CREATE_FOREIGN); - } + /* help completing some of the variants */ + else if (TailMatches3("DROP", "AGGREGATE|FUNCTION", MatchAny)) + COMPLETE_WITH_CONST("("); + else if (TailMatches4("DROP", "AGGREGATE|FUNCTION", MatchAny, "(")) + COMPLETE_WITH_FUNCTION_ARG(prev2_wd); + else if (TailMatches2("DROP", "FOREIGN")) + COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); /* DROP MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "MATERIALIZED") == 0) - { + else if (TailMatches2("DROP", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); - } - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev_wd, "VIEW") == 0) - { + else if (TailMatches3("DROP", "MATERIALIZED", "VIEW")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); - } - else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && - (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 || - pg_strcasecmp(prev3_wd, "FUNCTION") == 0) && - pg_strcasecmp(prev_wd, "(") == 0) - COMPLETE_WITH_FUNCTION_ARG(prev2_wd); /* DROP OWNED BY */ - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "OWNED") == 0) + else if (TailMatches2("DROP", "OWNED")) COMPLETE_WITH_CONST("BY"); - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "OWNED") == 0 && - pg_strcasecmp(prev_wd, "BY") == 0) + else if (TailMatches3("DROP", "OWNED", "BY")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "TEXT") == 0 && - pg_strcasecmp(prev_wd, "SEARCH") == 0) - { - static const char *const list_ALTERTEXTSEARCH[] = - {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; - - COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH); - } + else if (TailMatches3("DROP", "TEXT", "SEARCH")) + COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); /* DROP TRIGGER */ - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) - { + else if (TailMatches3("DROP", "TRIGGER", MatchAny)) COMPLETE_WITH_CONST("ON"); - } - else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("DROP", "TRIGGER", MatchAny, "ON")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger); } - else if (pg_strcasecmp(prev5_wd, "DROP") == 0 && - pg_strcasecmp(prev4_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_DROPCR[] = - {"CASCADE", "RESTRICT", NULL}; - - COMPLETE_WITH_LIST(list_DROPCR); - } + else if (TailMatches5("DROP", "TRIGGER", MatchAny, "ON", MatchAny)) + COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); /* DROP EVENT TRIGGER */ - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "EVENT") == 0) - { + else if (TailMatches2("DROP", "EVENT")) COMPLETE_WITH_CONST("TRIGGER"); - } - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "EVENT") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) - { + else if (TailMatches3("DROP", "EVENT", "TRIGGER")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); - } /* DROP POLICY */ - else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && - pg_strcasecmp(prev_wd, "POLICY") == 0) - { + else if (TailMatches2("DROP", "POLICY")) COMPLETE_WITH_QUERY(Query_for_list_of_policies); - } /* DROP POLICY ON */ - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "POLICY") == 0) - { + else if (TailMatches3("DROP", "POLICY", MatchAny)) COMPLETE_WITH_CONST("ON"); - } /* DROP POLICY ON
*/ - else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "POLICY") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("DROP", "POLICY", MatchAny, "ON")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy); } /* DROP RULE */ - else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && - pg_strcasecmp(prev2_wd, "RULE") == 0) - { + else if (TailMatches3("DROP", "RULE", MatchAny)) COMPLETE_WITH_CONST("ON"); - } - else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "RULE") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches4("DROP", "RULE", MatchAny, "ON")) { completion_info_charp = prev2_wd; COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule); } - else if (pg_strcasecmp(prev5_wd, "DROP") == 0 && - pg_strcasecmp(prev4_wd, "RULE") == 0 && - pg_strcasecmp(prev2_wd, "ON") == 0) - { - static const char *const list_DROPCR[] = - {"CASCADE", "RESTRICT", NULL}; + else if (TailMatches5("DROP", "RULE", MatchAny, "ON", MatchAny)) + COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); - COMPLETE_WITH_LIST(list_DROPCR); - } - -/* EXECUTE, but not EXECUTE embedded in other commands */ - else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0 && - prev2_wd[0] == '\0') +/* EXECUTE */ + else if (Matches1("EXECUTE")) COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements); /* EXPLAIN */ @@ -3051,91 +2250,50 @@ psql_completion(const char *text, int start, int end) /* * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands */ - else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0) - { - static const char *const list_EXPLAIN[] = - {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL}; - - COMPLETE_WITH_LIST(list_EXPLAIN); - } - else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 && - pg_strcasecmp(prev_wd, "ANALYZE") == 0) - { - static const char *const list_EXPLAIN[] = - {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL}; - - COMPLETE_WITH_LIST(list_EXPLAIN); - } - else if ((pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0) || - (pg_strcasecmp(prev3_wd, "EXPLAIN") == 0 && - pg_strcasecmp(prev2_wd, "ANALYZE") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0)) - { - static const char *const list_EXPLAIN[] = - {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL}; - - COMPLETE_WITH_LIST(list_EXPLAIN); - } + else if (TailMatches1("EXPLAIN")) + COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", + "ANALYZE", "VERBOSE"); + else if (TailMatches2("EXPLAIN", "ANALYZE")) + COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", + "VERBOSE"); + else if (TailMatches2("EXPLAIN", "VERBOSE") || + TailMatches3("EXPLAIN", "ANALYZE", "VERBOSE")) + COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE"); /* FETCH && MOVE */ /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */ - else if (pg_strcasecmp(prev_wd, "FETCH") == 0 || - pg_strcasecmp(prev_wd, "MOVE") == 0) - { - static const char *const list_FETCH1[] = - {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL}; - - COMPLETE_WITH_LIST(list_FETCH1); - } + else if (TailMatches1("FETCH|MOVE")) + COMPLETE_WITH_LIST4("ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE"); /* Complete FETCH with one of ALL, NEXT, PRIOR */ - else if (pg_strcasecmp(prev2_wd, "FETCH") == 0 || - pg_strcasecmp(prev2_wd, "MOVE") == 0) - { - static const char *const list_FETCH2[] = - {"ALL", "NEXT", "PRIOR", NULL}; - - COMPLETE_WITH_LIST(list_FETCH2); - } + else if (TailMatches2("FETCH|MOVE", MatchAny)) + COMPLETE_WITH_LIST3("ALL", "NEXT", "PRIOR"); /* * Complete FETCH with "FROM" or "IN". These are equivalent, * but we may as well tab-complete both: perhaps some users prefer one * variant or the other. */ - else if (pg_strcasecmp(prev3_wd, "FETCH") == 0 || - pg_strcasecmp(prev3_wd, "MOVE") == 0) - { - static const char *const list_FROMIN[] = - {"FROM", "IN", NULL}; - - COMPLETE_WITH_LIST(list_FROMIN); - } + else if (TailMatches3("FETCH|MOVE", MatchAny, MatchAny)) + COMPLETE_WITH_LIST2("FROM", "IN"); /* FOREIGN DATA WRAPPER */ /* applies in ALTER/DROP FDW and in CREATE SERVER */ - else if (pg_strcasecmp(prev4_wd, "CREATE") != 0 && - pg_strcasecmp(prev3_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev2_wd, "DATA") == 0 && - pg_strcasecmp(prev_wd, "WRAPPER") == 0) + else if (TailMatches3("FOREIGN", "DATA", "WRAPPER") && + !TailMatches4("CREATE", MatchAny, MatchAny, MatchAny)) COMPLETE_WITH_QUERY(Query_for_list_of_fdws); /* FOREIGN TABLE */ - else if (pg_strcasecmp(prev3_wd, "CREATE") != 0 && - pg_strcasecmp(prev2_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev_wd, "TABLE") == 0) + else if (TailMatches2("FOREIGN", "TABLE") && + !TailMatches3("CREATE", MatchAny, MatchAny)) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); /* FOREIGN SERVER */ - else if (pg_strcasecmp(prev2_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev_wd, "SERVER") == 0) + else if (TailMatches2("FOREIGN", "SERVER")) COMPLETE_WITH_QUERY(Query_for_list_of_servers); /* GRANT && REVOKE */ /* Complete GRANT/REVOKE with a list of roles and privileges */ - else if (pg_strcasecmp(prev_wd, "GRANT") == 0 || - pg_strcasecmp(prev_wd, "REVOKE") == 0) - { + else if (TailMatches1("GRANT|REVOKE")) COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'SELECT'" " UNION SELECT 'INSERT'" @@ -3150,53 +2308,33 @@ psql_completion(const char *text, int start, int end) " UNION SELECT 'EXECUTE'" " UNION SELECT 'USAGE'" " UNION SELECT 'ALL'"); - } /* * Complete GRANT/REVOKE with "ON", GRANT/REVOKE with * TO/FROM */ - else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 || - pg_strcasecmp(prev2_wd, "REVOKE") == 0) + else if (TailMatches2("GRANT|REVOKE", MatchAny)) { - if (pg_strcasecmp(prev_wd, "SELECT") == 0 - || pg_strcasecmp(prev_wd, "INSERT") == 0 - || pg_strcasecmp(prev_wd, "UPDATE") == 0 - || pg_strcasecmp(prev_wd, "DELETE") == 0 - || pg_strcasecmp(prev_wd, "TRUNCATE") == 0 - || pg_strcasecmp(prev_wd, "REFERENCES") == 0 - || pg_strcasecmp(prev_wd, "TRIGGER") == 0 - || pg_strcasecmp(prev_wd, "CREATE") == 0 - || pg_strcasecmp(prev_wd, "CONNECT") == 0 - || pg_strcasecmp(prev_wd, "TEMPORARY") == 0 - || pg_strcasecmp(prev_wd, "TEMP") == 0 - || pg_strcasecmp(prev_wd, "EXECUTE") == 0 - || pg_strcasecmp(prev_wd, "USAGE") == 0 - || pg_strcasecmp(prev_wd, "ALL") == 0) + if (TailMatches1("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL")) COMPLETE_WITH_CONST("ON"); + else if (TailMatches2("GRANT", MatchAny)) + COMPLETE_WITH_CONST("TO"); else - { - if (pg_strcasecmp(prev2_wd, "GRANT") == 0) - COMPLETE_WITH_CONST("TO"); - else - COMPLETE_WITH_CONST("FROM"); - } + COMPLETE_WITH_CONST("FROM"); } /* * Complete GRANT/REVOKE ON with a list of tables, views, and * sequences. * - * Keywords like DATABASE, FUNCTION, LANGUAGE and SCHEMA added to - * query result via UNION; seems to work intuitively. + * Keywords like DATABASE, FUNCTION, LANGUAGE and SCHEMA added to query + * result via UNION; seems to work intuitively. * * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented * here will only work if the privilege list contains exactly one * privilege. */ - else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 || - pg_strcasecmp(prev3_wd, "REVOKE") == 0) && - pg_strcasecmp(prev_wd, "ON") == 0) + else if (TailMatches3("GRANT|REVOKE", MatchAny, "ON")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, " UNION SELECT 'ALL FUNCTIONS IN SCHEMA'" " UNION SELECT 'ALL SEQUENCES IN SCHEMA'" @@ -3214,99 +2352,40 @@ psql_completion(const char *text, int start, int end) " UNION SELECT 'TABLESPACE'" " UNION SELECT 'TYPE'"); - else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 || - pg_strcasecmp(prev4_wd, "REVOKE") == 0) && - pg_strcasecmp(prev2_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "ALL") == 0) - { - static const char *const list_privilege_all[] = - {"FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA", "TABLES IN SCHEMA", - NULL}; + else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL")) + COMPLETE_WITH_LIST3("FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA", + "TABLES IN SCHEMA"); - COMPLETE_WITH_LIST(list_privilege_all); - } - - else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 || - pg_strcasecmp(prev4_wd, "REVOKE") == 0) && - pg_strcasecmp(prev2_wd, "ON") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) - { - static const char *const list_privilege_foreign[] = - {"DATA WRAPPER", "SERVER", NULL}; - - COMPLETE_WITH_LIST(list_privilege_foreign); - } + else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN")) + COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER"); /* - * Complete "GRANT/REMOVE * ON DATABASE/DOMAIN/..." with a list of + * Complete "GRANT/REVOKE * ON DATABASE/DOMAIN/..." with a list of * appropriate objects. * - * Complete "GRANT/REVOKE * ON * " with "TO/FROM". + * Complete "GRANT/REVOKE * ON *" with "TO/FROM". */ - else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 || - pg_strcasecmp(prev4_wd, "REVOKE") == 0) && - pg_strcasecmp(prev2_wd, "ON") == 0) + else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", MatchAny)) { - if (pg_strcasecmp(prev_wd, "DATABASE") == 0) + if (TailMatches1("DATABASE")) COMPLETE_WITH_QUERY(Query_for_list_of_databases); - else if (pg_strcasecmp(prev_wd, "DOMAIN") == 0) + else if (TailMatches1("DOMAIN")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); - else if (pg_strcasecmp(prev_wd, "FUNCTION") == 0) + else if (TailMatches1("FUNCTION")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); - else if (pg_strcasecmp(prev_wd, "LANGUAGE") == 0) + else if (TailMatches1("LANGUAGE")) COMPLETE_WITH_QUERY(Query_for_list_of_languages); - else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0) + else if (TailMatches1("SCHEMA")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); - else if (pg_strcasecmp(prev_wd, "SEQUENCE") == 0) + else if (TailMatches1("SEQUENCE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); - else if (pg_strcasecmp(prev_wd, "TABLE") == 0) + else if (TailMatches1("TABLE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); - else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0) + else if (TailMatches1("TABLESPACE")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); - else if (pg_strcasecmp(prev_wd, "TYPE") == 0) + else if (TailMatches1("TYPE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); - else if (pg_strcasecmp(prev4_wd, "GRANT") == 0) - COMPLETE_WITH_CONST("TO"); - else - COMPLETE_WITH_CONST("FROM"); - } - - /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */ - else if ((pg_strcasecmp(prev8_wd, "GRANT") == 0 || - pg_strcasecmp(prev8_wd, "REVOKE") == 0) && - pg_strcasecmp(prev6_wd, "ON") == 0 && - pg_strcasecmp(prev5_wd, "ALL") == 0 && - pg_strcasecmp(prev3_wd, "IN") == 0 && - pg_strcasecmp(prev2_wd, "SCHEMA") == 0) - { - if (pg_strcasecmp(prev8_wd, "GRANT") == 0) - COMPLETE_WITH_CONST("TO"); - else - COMPLETE_WITH_CONST("FROM"); - } - - /* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */ - else if ((pg_strcasecmp(prev7_wd, "GRANT") == 0 || - pg_strcasecmp(prev7_wd, "REVOKE") == 0) && - pg_strcasecmp(prev5_wd, "ON") == 0 && - pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev3_wd, "DATA") == 0 && - pg_strcasecmp(prev2_wd, "WRAPPER") == 0) - { - if (pg_strcasecmp(prev7_wd, "GRANT") == 0) - COMPLETE_WITH_CONST("TO"); - else - COMPLETE_WITH_CONST("FROM"); - } - - /* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */ - else if ((pg_strcasecmp(prev6_wd, "GRANT") == 0 || - pg_strcasecmp(prev6_wd, "REVOKE") == 0) && - pg_strcasecmp(prev4_wd, "ON") == 0 && - pg_strcasecmp(prev3_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev2_wd, "SERVER") == 0) - { - if (pg_strcasecmp(prev6_wd, "GRANT") == 0) + else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny)) COMPLETE_WITH_CONST("TO"); else COMPLETE_WITH_CONST("FROM"); @@ -3316,167 +2395,128 @@ psql_completion(const char *text, int start, int end) * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC, * CURRENT_USER, or SESSION_USER. */ - else if (((pg_strcasecmp(prev9_wd, "GRANT") == 0 || - pg_strcasecmp(prev8_wd, "GRANT") == 0 || - pg_strcasecmp(prev7_wd, "GRANT") == 0 || - pg_strcasecmp(prev6_wd, "GRANT") == 0 || - pg_strcasecmp(prev5_wd, "GRANT") == 0) && - pg_strcasecmp(prev_wd, "TO") == 0) || - ((pg_strcasecmp(prev9_wd, "REVOKE") == 0 || - pg_strcasecmp(prev8_wd, "REVOKE") == 0 || - pg_strcasecmp(prev7_wd, "REVOKE") == 0 || - pg_strcasecmp(prev6_wd, "REVOKE") == 0 || - pg_strcasecmp(prev5_wd, "REVOKE") == 0) && - pg_strcasecmp(prev_wd, "FROM") == 0)) + else if ((HeadMatches1("GRANT") && TailMatches1("TO")) || + (HeadMatches1("REVOKE") && TailMatches1("FROM"))) COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); - /* Complete "GRANT/REVOKE * ON * *" with TO/FROM */ - else if (pg_strcasecmp(prev5_wd, "GRANT") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) + /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */ + else if (HeadMatches1("GRANT") && TailMatches3("ON", MatchAny, MatchAny)) COMPLETE_WITH_CONST("TO"); - - else if (pg_strcasecmp(prev5_wd, "REVOKE") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) + else if (HeadMatches1("REVOKE") && TailMatches3("ON", MatchAny, MatchAny)) COMPLETE_WITH_CONST("FROM"); - /* - * Complete "GRANT/REVOKE * TO/FROM" with username, PUBLIC, - * CURRENT_USER, or SESSION_USER. - */ - else if (pg_strcasecmp(prev3_wd, "GRANT") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) + /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */ + else if (TailMatches8("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny)) { - COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); + if (TailMatches8("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny)) + COMPLETE_WITH_CONST("TO"); + else + COMPLETE_WITH_CONST("FROM"); } - else if (pg_strcasecmp(prev3_wd, "REVOKE") == 0 && - pg_strcasecmp(prev_wd, "FROM") == 0) + + /* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */ + else if (TailMatches7("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny)) { - COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); + if (TailMatches7("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny)) + COMPLETE_WITH_CONST("TO"); + else + COMPLETE_WITH_CONST("FROM"); + } + + /* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */ + else if (TailMatches6("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny)) + { + if (TailMatches6("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny)) + COMPLETE_WITH_CONST("TO"); + else + COMPLETE_WITH_CONST("FROM"); } /* GROUP BY */ - else if (pg_strcasecmp(prev3_wd, "FROM") == 0 && - pg_strcasecmp(prev_wd, "GROUP") == 0) + else if (TailMatches3("FROM", MatchAny, "GROUP")) COMPLETE_WITH_CONST("BY"); /* IMPORT FOREIGN SCHEMA */ - else if (pg_strcasecmp(prev_wd, "IMPORT") == 0) + else if (TailMatches1("IMPORT")) COMPLETE_WITH_CONST("FOREIGN SCHEMA"); - else if (pg_strcasecmp(prev2_wd, "IMPORT") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) + else if (TailMatches2("IMPORT", "FOREIGN")) COMPLETE_WITH_CONST("SCHEMA"); /* INSERT */ /* Complete INSERT with "INTO" */ - else if (pg_strcasecmp(prev_wd, "INSERT") == 0) + else if (TailMatches1("INSERT")) COMPLETE_WITH_CONST("INTO"); /* Complete INSERT INTO with table names */ - else if (pg_strcasecmp(prev2_wd, "INSERT") == 0 && - pg_strcasecmp(prev_wd, "INTO") == 0) + else if (TailMatches2("INSERT", "INTO")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); /* Complete "INSERT INTO
(" with attribute names */ - else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 && - pg_strcasecmp(prev3_wd, "INTO") == 0 && - pg_strcasecmp(prev_wd, "(") == 0) + else if (TailMatches4("INSERT", "INTO", MatchAny, "(")) COMPLETE_WITH_ATTR(prev2_wd, ""); /* * Complete INSERT INTO
with "(" or "VALUES" or "SELECT" or * "TABLE" or "DEFAULT VALUES" */ - else if (pg_strcasecmp(prev3_wd, "INSERT") == 0 && - pg_strcasecmp(prev2_wd, "INTO") == 0) - { - static const char *const list_INSERT[] = - {"(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", NULL}; - - COMPLETE_WITH_LIST(list_INSERT); - } + else if (TailMatches3("INSERT", "INTO", MatchAny)) + COMPLETE_WITH_LIST5("(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES"); /* * Complete INSERT INTO
(attribs) with "VALUES" or "SELECT" or * "TABLE" */ - else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 && - pg_strcasecmp(prev3_wd, "INTO") == 0 && - prev_wd[strlen(prev_wd) - 1] == ')') - { - static const char *const list_INSERT[] = - {"SELECT", "TABLE", "VALUES", NULL}; - - COMPLETE_WITH_LIST(list_INSERT); - } + else if (TailMatches4("INSERT", "INTO", MatchAny, MatchAny) && + ends_with(prev_wd, ')')) + COMPLETE_WITH_LIST3("SELECT", "TABLE", "VALUES"); /* Insert an open parenthesis after "VALUES" */ - else if (pg_strcasecmp(prev_wd, "VALUES") == 0 && - pg_strcasecmp(prev2_wd, "DEFAULT") != 0) + else if (TailMatches1("VALUES") && !TailMatches2("DEFAULT", "VALUES")) COMPLETE_WITH_CONST("("); /* LOCK */ /* Complete LOCK [TABLE] with a list of tables */ - else if (pg_strcasecmp(prev_wd, "LOCK") == 0) + else if (TailMatches1("LOCK")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, " UNION SELECT 'TABLE'"); - else if (pg_strcasecmp(prev_wd, "TABLE") == 0 && - pg_strcasecmp(prev2_wd, "LOCK") == 0) + else if (TailMatches2("LOCK", "TABLE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); /* For the following, handle the case of a single table only for now */ /* Complete LOCK [TABLE]
with "IN" */ - else if ((pg_strcasecmp(prev2_wd, "LOCK") == 0 && - pg_strcasecmp(prev_wd, "TABLE") != 0) || - (pg_strcasecmp(prev2_wd, "TABLE") == 0 && - pg_strcasecmp(prev3_wd, "LOCK") == 0)) + else if (TailMatches2("LOCK", MatchAnyExcept("TABLE")) || + TailMatches3("LOCK", "TABLE", MatchAny)) COMPLETE_WITH_CONST("IN"); /* Complete LOCK [TABLE]
IN with a lock mode */ - else if (pg_strcasecmp(prev_wd, "IN") == 0 && - (pg_strcasecmp(prev3_wd, "LOCK") == 0 || - (pg_strcasecmp(prev3_wd, "TABLE") == 0 && - pg_strcasecmp(prev4_wd, "LOCK") == 0))) - { - static const char *const lock_modes[] = - {"ACCESS SHARE MODE", - "ROW SHARE MODE", "ROW EXCLUSIVE MODE", - "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE", - "SHARE ROW EXCLUSIVE MODE", - "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE", NULL}; - - COMPLETE_WITH_LIST(lock_modes); - } + else if (TailMatches3("LOCK", MatchAny, "IN") || + TailMatches4("LOCK", "TABLE", MatchAny, "IN")) + COMPLETE_WITH_LIST8("ACCESS SHARE MODE", + "ROW SHARE MODE", "ROW EXCLUSIVE MODE", + "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE", + "SHARE ROW EXCLUSIVE MODE", + "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE"); /* NOTIFY */ - else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0) + else if (TailMatches1("NOTIFY")) COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s'"); /* OPTIONS */ - else if (pg_strcasecmp(prev_wd, "OPTIONS") == 0) + else if (TailMatches1("OPTIONS")) COMPLETE_WITH_CONST("("); /* OWNER TO - complete with available roles */ - else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 && - pg_strcasecmp(prev_wd, "TO") == 0) + else if (TailMatches2("OWNER", "TO")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); /* ORDER BY */ - else if (pg_strcasecmp(prev3_wd, "FROM") == 0 && - pg_strcasecmp(prev_wd, "ORDER") == 0) + else if (TailMatches3("FROM", MatchAny, "ORDER")) COMPLETE_WITH_CONST("BY"); - else if (pg_strcasecmp(prev4_wd, "FROM") == 0 && - pg_strcasecmp(prev2_wd, "ORDER") == 0 && - pg_strcasecmp(prev_wd, "BY") == 0) + else if (TailMatches4("FROM", MatchAny, "ORDER", "BY")) COMPLETE_WITH_ATTR(prev3_wd, ""); /* PREPARE xx AS */ - else if (pg_strcasecmp(prev_wd, "AS") == 0 && - pg_strcasecmp(prev3_wd, "PREPARE") == 0) - { - static const char *const list_PREPARE[] = - {"SELECT", "UPDATE", "INSERT", "DELETE", NULL}; - - COMPLETE_WITH_LIST(list_PREPARE); - } + else if (TailMatches3("PREPARE", MatchAny, "AS")) + COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE"); /* * PREPARE TRANSACTION is missing on purpose. It's intended for transaction @@ -3484,116 +2524,62 @@ psql_completion(const char *text, int start, int end) */ /* REASSIGN OWNED BY xxx TO yyy */ - else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0) - COMPLETE_WITH_CONST("OWNED"); - else if (pg_strcasecmp(prev_wd, "OWNED") == 0 && - pg_strcasecmp(prev2_wd, "REASSIGN") == 0) + else if (TailMatches1("REASSIGN")) + COMPLETE_WITH_CONST("OWNED BY"); + else if (TailMatches2("REASSIGN", "OWNED")) COMPLETE_WITH_CONST("BY"); - else if (pg_strcasecmp(prev_wd, "BY") == 0 && - pg_strcasecmp(prev2_wd, "OWNED") == 0 && - pg_strcasecmp(prev3_wd, "REASSIGN") == 0) + else if (TailMatches3("REASSIGN", "OWNED", "BY")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); - else if (pg_strcasecmp(prev2_wd, "BY") == 0 && - pg_strcasecmp(prev3_wd, "OWNED") == 0 && - pg_strcasecmp(prev4_wd, "REASSIGN") == 0) + else if (TailMatches4("REASSIGN", "OWNED", "BY", MatchAny)) COMPLETE_WITH_CONST("TO"); - else if (pg_strcasecmp(prev_wd, "TO") == 0 && - pg_strcasecmp(prev3_wd, "BY") == 0 && - pg_strcasecmp(prev4_wd, "OWNED") == 0 && - pg_strcasecmp(prev5_wd, "REASSIGN") == 0) + else if (TailMatches5("REASSIGN", "OWNED", "BY", MatchAny, "TO")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); /* REFRESH MATERIALIZED VIEW */ - else if (pg_strcasecmp(prev_wd, "REFRESH") == 0) + else if (TailMatches1("REFRESH")) COMPLETE_WITH_CONST("MATERIALIZED VIEW"); - else if (pg_strcasecmp(prev2_wd, "REFRESH") == 0 && - pg_strcasecmp(prev_wd, "MATERIALIZED") == 0) + else if (TailMatches2("REFRESH", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); - else if (pg_strcasecmp(prev3_wd, "REFRESH") == 0 && - pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev_wd, "VIEW") == 0) + else if (TailMatches3("REFRESH", "MATERIALIZED", "VIEW")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, " UNION SELECT 'CONCURRENTLY'"); - else if (pg_strcasecmp(prev4_wd, "REFRESH") == 0 && - pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0 && - pg_strcasecmp(prev_wd, "CONCURRENTLY") == 0) + else if (TailMatches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); - else if (pg_strcasecmp(prev4_wd, "REFRESH") == 0 && - pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev2_wd, "VIEW") == 0) + else if (TailMatches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny)) COMPLETE_WITH_CONST("WITH"); - else if (pg_strcasecmp(prev5_wd, "REFRESH") == 0 && - pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev3_wd, "VIEW") == 0 && - pg_strcasecmp(prev2_wd, "CONCURRENTLY") == 0) + else if (TailMatches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny)) COMPLETE_WITH_CONST("WITH DATA"); - else if (pg_strcasecmp(prev5_wd, "REFRESH") == 0 && - pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev3_wd, "VIEW") == 0 && - pg_strcasecmp(prev_wd, "WITH") == 0) - { - static const char *const list_WITH_DATA[] = - {"NO DATA", "DATA", NULL}; - - COMPLETE_WITH_LIST(list_WITH_DATA); - } - else if (pg_strcasecmp(prev6_wd, "REFRESH") == 0 && - pg_strcasecmp(prev5_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev4_wd, "VIEW") == 0 && - pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0 && - pg_strcasecmp(prev_wd, "WITH") == 0) + else if (TailMatches5("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH")) + COMPLETE_WITH_LIST2("NO DATA", "DATA"); + else if (TailMatches6("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH")) COMPLETE_WITH_CONST("DATA"); - else if (pg_strcasecmp(prev6_wd, "REFRESH") == 0 && - pg_strcasecmp(prev5_wd, "MATERIALIZED") == 0 && - pg_strcasecmp(prev4_wd, "VIEW") == 0 && - pg_strcasecmp(prev2_wd, "WITH") == 0 && - pg_strcasecmp(prev_wd, "NO") == 0) + else if (TailMatches6("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO")) COMPLETE_WITH_CONST("DATA"); /* REINDEX */ - else if (pg_strcasecmp(prev_wd, "REINDEX") == 0) + else if (TailMatches1("REINDEX")) + COMPLETE_WITH_LIST5("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE"); + else if (TailMatches2("REINDEX", MatchAny)) { - static const char *const list_REINDEX[] = - {"TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE", NULL}; - - COMPLETE_WITH_LIST(list_REINDEX); - } - else if (pg_strcasecmp(prev2_wd, "REINDEX") == 0) - { - if (pg_strcasecmp(prev_wd, "TABLE") == 0) + if (TailMatches1("TABLE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); - else if (pg_strcasecmp(prev_wd, "INDEX") == 0) + else if (TailMatches1("INDEX")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); - else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0) + else if (TailMatches1("SCHEMA")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); - else if (pg_strcasecmp(prev_wd, "SYSTEM") == 0 || - pg_strcasecmp(prev_wd, "DATABASE") == 0) + else if (TailMatches1("SYSTEM|DATABASE")) COMPLETE_WITH_QUERY(Query_for_list_of_databases); } /* SECURITY LABEL */ - else if (pg_strcasecmp(prev_wd, "SECURITY") == 0) + else if (TailMatches1("SECURITY")) COMPLETE_WITH_CONST("LABEL"); - else if (pg_strcasecmp(prev2_wd, "SECURITY") == 0 && - pg_strcasecmp(prev_wd, "LABEL") == 0) - { - static const char *const list_SECURITY_LABEL_preposition[] = - {"ON", "FOR"}; - - COMPLETE_WITH_LIST(list_SECURITY_LABEL_preposition); - } - else if (pg_strcasecmp(prev4_wd, "SECURITY") == 0 && - pg_strcasecmp(prev3_wd, "LABEL") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0) + else if (TailMatches2("SECURITY", "LABEL")) + COMPLETE_WITH_LIST2("ON", "FOR"); + else if (TailMatches4("SECURITY", "LABEL", "FOR", MatchAny)) COMPLETE_WITH_CONST("ON"); - else if ((pg_strcasecmp(prev3_wd, "SECURITY") == 0 && - pg_strcasecmp(prev2_wd, "LABEL") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0) || - (pg_strcasecmp(prev5_wd, "SECURITY") == 0 && - pg_strcasecmp(prev4_wd, "LABEL") == 0 && - pg_strcasecmp(prev3_wd, "FOR") == 0 && - pg_strcasecmp(prev_wd, "ON") == 0)) + else if (TailMatches3("SECURITY", "LABEL", "ON") || + TailMatches5("SECURITY", "LABEL", "FOR", MatchAny, "ON")) { static const char *const list_SECURITY_LABEL[] = {"LANGUAGE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", @@ -3602,9 +2588,7 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_SECURITY_LABEL); } - else if (pg_strcasecmp(prev5_wd, "SECURITY") == 0 && - pg_strcasecmp(prev4_wd, "LABEL") == 0 && - pg_strcasecmp(prev3_wd, "ON") == 0) + else if (TailMatches5("SECURITY", "LABEL", "ON", MatchAny, MatchAny)) COMPLETE_WITH_CONST("IS"); /* SELECT */ @@ -3612,136 +2596,52 @@ psql_completion(const char *text, int start, int end) /* SET, RESET, SHOW */ /* Complete with a variable name */ - else if ((pg_strcasecmp(prev_wd, "SET") == 0 && - pg_strcasecmp(prev3_wd, "UPDATE") != 0) || - pg_strcasecmp(prev_wd, "RESET") == 0) + else if (TailMatches1("SET|RESET") && !Matches3("UPDATE", MatchAny, "SET")) COMPLETE_WITH_QUERY(Query_for_list_of_set_vars); - else if (pg_strcasecmp(prev_wd, "SHOW") == 0) + else if (TailMatches1("SHOW")) COMPLETE_WITH_QUERY(Query_for_list_of_show_vars); /* Complete "SET TRANSACTION" */ - else if ((pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "TRANSACTION") == 0) - || (pg_strcasecmp(prev2_wd, "START") == 0 - && pg_strcasecmp(prev_wd, "TRANSACTION") == 0) - || (pg_strcasecmp(prev2_wd, "BEGIN") == 0 - && pg_strcasecmp(prev_wd, "WORK") == 0) - || (pg_strcasecmp(prev2_wd, "BEGIN") == 0 - && pg_strcasecmp(prev_wd, "TRANSACTION") == 0) - || (pg_strcasecmp(prev4_wd, "SESSION") == 0 - && pg_strcasecmp(prev3_wd, "CHARACTERISTICS") == 0 - && pg_strcasecmp(prev2_wd, "AS") == 0 - && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)) - { - static const char *const my_list[] = - {"ISOLATION LEVEL", "READ", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if ((pg_strcasecmp(prev3_wd, "SET") == 0 - || pg_strcasecmp(prev3_wd, "BEGIN") == 0 - || pg_strcasecmp(prev3_wd, "START") == 0 - || (pg_strcasecmp(prev4_wd, "CHARACTERISTICS") == 0 - && pg_strcasecmp(prev3_wd, "AS") == 0)) - && (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 - || pg_strcasecmp(prev2_wd, "WORK") == 0) - && pg_strcasecmp(prev_wd, "ISOLATION") == 0) + else if (TailMatches2("SET|BEGIN|START", "TRANSACTION") || + TailMatches2("BEGIN", "WORK") || + TailMatches4("SESSION", "CHARACTERISTICS", "AS", "TRANSACTION")) + COMPLETE_WITH_LIST2("ISOLATION LEVEL", "READ"); + else if (TailMatches3("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION") || + TailMatches4("CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION")) COMPLETE_WITH_CONST("LEVEL"); - else if ((pg_strcasecmp(prev4_wd, "SET") == 0 - || pg_strcasecmp(prev4_wd, "BEGIN") == 0 - || pg_strcasecmp(prev4_wd, "START") == 0 - || pg_strcasecmp(prev4_wd, "AS") == 0) - && (pg_strcasecmp(prev3_wd, "TRANSACTION") == 0 - || pg_strcasecmp(prev3_wd, "WORK") == 0) - && pg_strcasecmp(prev2_wd, "ISOLATION") == 0 - && pg_strcasecmp(prev_wd, "LEVEL") == 0) - { - static const char *const my_list[] = - {"READ", "REPEATABLE", "SERIALIZABLE", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 || - pg_strcasecmp(prev4_wd, "WORK") == 0) && - pg_strcasecmp(prev3_wd, "ISOLATION") == 0 && - pg_strcasecmp(prev2_wd, "LEVEL") == 0 && - pg_strcasecmp(prev_wd, "READ") == 0) - { - static const char *const my_list[] = - {"UNCOMMITTED", "COMMITTED", NULL}; - - COMPLETE_WITH_LIST(my_list); - } - else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 || - pg_strcasecmp(prev4_wd, "WORK") == 0) && - pg_strcasecmp(prev3_wd, "ISOLATION") == 0 && - pg_strcasecmp(prev2_wd, "LEVEL") == 0 && - pg_strcasecmp(prev_wd, "REPEATABLE") == 0) + else if (TailMatches4("SET|BEGIN|START|AS", "TRANSACTION|WORK", "ISOLATION", "LEVEL")) + COMPLETE_WITH_LIST3("READ", "REPEATABLE READ", "SERIALIZABLE"); + else if (TailMatches4("TRANSACTION|WORK", "ISOLATION", "LEVEL", "READ")) + COMPLETE_WITH_LIST2("UNCOMMITTED", "COMMITTED"); + else if (TailMatches4("TRANSACTION|WORK", "ISOLATION", "LEVEL", "REPEATABLE")) COMPLETE_WITH_CONST("READ"); - else if ((pg_strcasecmp(prev3_wd, "SET") == 0 || - pg_strcasecmp(prev3_wd, "BEGIN") == 0 || - pg_strcasecmp(prev3_wd, "START") == 0 || - pg_strcasecmp(prev3_wd, "AS") == 0) && - (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 || - pg_strcasecmp(prev2_wd, "WORK") == 0) && - pg_strcasecmp(prev_wd, "READ") == 0) - { - static const char *const my_list[] = - {"ONLY", "WRITE", NULL}; - - COMPLETE_WITH_LIST(my_list); - } + else if (TailMatches3("SET|BEGIN|START|AS", "TRANSACTION|WORK", "READ")) + COMPLETE_WITH_LIST2("ONLY", "WRITE"); /* SET CONSTRAINTS */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "CONSTRAINTS") == 0) - { + else if (TailMatches2("SET", "CONSTRAINTS")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'"); - } /* Complete SET CONSTRAINTS with DEFERRED|IMMEDIATE */ - else if (pg_strcasecmp(prev3_wd, "SET") == 0 && - pg_strcasecmp(prev2_wd, "CONSTRAINTS") == 0) - { - static const char *const constraint_list[] = - {"DEFERRED", "IMMEDIATE", NULL}; - - COMPLETE_WITH_LIST(constraint_list); - } + else if (TailMatches3("SET", "CONSTRAINTS", MatchAny)) + COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE"); /* Complete SET ROLE */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "ROLE") == 0) + else if (TailMatches2("SET", "ROLE")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev_wd, "SESSION") == 0) - { - static const char *const my_list[] = - {"AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION", NULL}; - - COMPLETE_WITH_LIST(my_list); - } + else if (TailMatches2("SET", "SESSION")) + COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION"); /* Complete SET SESSION AUTHORIZATION with username */ - else if (pg_strcasecmp(prev3_wd, "SET") == 0 - && pg_strcasecmp(prev2_wd, "SESSION") == 0 - && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0) + else if (TailMatches3("SET", "SESSION", "AUTHORIZATION")) COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'"); /* Complete RESET SESSION with AUTHORIZATION */ - else if (pg_strcasecmp(prev2_wd, "RESET") == 0 && - pg_strcasecmp(prev_wd, "SESSION") == 0) + else if (TailMatches2("RESET", "SESSION")) COMPLETE_WITH_CONST("AUTHORIZATION"); /* Complete SET with "TO" */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev4_wd, "UPDATE") != 0 && - pg_strcasecmp(prev_wd, "TABLESPACE") != 0 && - pg_strcasecmp(prev_wd, "SCHEMA") != 0 && - prev_wd[strlen(prev_wd) - 1] != ')' && - prev_wd[strlen(prev_wd) - 1] != '=' && - pg_strcasecmp(prev4_wd, "DOMAIN") != 0) + else if (Matches2("SET", MatchAny)) COMPLETE_WITH_CONST("TO"); /* Suggest possible variable values */ - else if (pg_strcasecmp(prev3_wd, "SET") == 0 && - (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0)) + else if (Matches3("SET", MatchAny, "TO|=")) { /* special cased code for individual GUCs */ - if (pg_strcasecmp(prev2_wd, "DateStyle") == 0) + if (TailMatches2("DateStyle", "TO|=")) { static const char *const my_list[] = {"ISO", "SQL", "Postgres", "German", @@ -3751,40 +2651,28 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(my_list); } - else if (pg_strcasecmp(prev2_wd, "search_path") == 0) - { + else if (TailMatches2("search_path", "TO|=")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas " AND nspname not like 'pg\\_toast%%' " " AND nspname not like 'pg\\_temp%%' " " UNION SELECT 'DEFAULT' "); - } else { /* generic, type based, GUC support */ - char *guctype = get_guctype(prev2_wd); if (guctype && strcmp(guctype, "enum") == 0) { char querybuf[1024]; - snprintf(querybuf, 1024, Query_for_enum, prev2_wd); + snprintf(querybuf, sizeof(querybuf), Query_for_enum, prev2_wd); COMPLETE_WITH_QUERY(querybuf); } else if (guctype && strcmp(guctype, "bool") == 0) - { - static const char *const my_list[] = - {"on", "off", "true", "false", "yes", "no", "1", "0", "DEFAULT", NULL}; - - COMPLETE_WITH_LIST(my_list); - } + COMPLETE_WITH_LIST9("on", "off", "true", "false", "yes", "no", + "1", "0", "DEFAULT"); else - { - static const char *const my_list[] = - {"DEFAULT", NULL}; - - COMPLETE_WITH_LIST(my_list); - } + COMPLETE_WITH_CONST("DEFAULT"); if (guctype) free(guctype); @@ -3792,35 +2680,34 @@ psql_completion(const char *text, int start, int end) } /* START TRANSACTION */ - else if (pg_strcasecmp(prev_wd, "START") == 0) + else if (Matches1("START")) COMPLETE_WITH_CONST("TRANSACTION"); /* TABLE, but not TABLE embedded in other commands */ - else if (pg_strcasecmp(prev_wd, "TABLE") == 0 && - prev2_wd[0] == '\0') + else if (Matches1("TABLE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); /* TABLESAMPLE */ - else if (pg_strcasecmp(prev_wd, "TABLESAMPLE") == 0) + else if (TailMatches1("TABLESAMPLE")) COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods); - else if (pg_strcasecmp(prev2_wd, "TABLESAMPLE") == 0) + else if (TailMatches2("TABLESAMPLE", MatchAny)) COMPLETE_WITH_CONST("("); /* TRUNCATE */ - else if (pg_strcasecmp(prev_wd, "TRUNCATE") == 0) + else if (TailMatches1("TRUNCATE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); /* UNLISTEN */ - else if (pg_strcasecmp(prev_wd, "UNLISTEN") == 0) + else if (TailMatches1("UNLISTEN")) COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s' UNION SELECT '*'"); /* UPDATE */ /* If prev. word is UPDATE suggest a list of tables */ - else if (pg_strcasecmp(prev_wd, "UPDATE") == 0) + else if (TailMatches1("UPDATE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); /* Complete UPDATE
with "SET" */ - else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0) + else if (TailMatches2("UPDATE", MatchAny)) COMPLETE_WITH_CONST("SET"); /* @@ -3828,83 +2715,53 @@ psql_completion(const char *text, int start, int end) * word) the word before it was (hopefully) a table name and we'll now * make a list of attributes. */ - else if (pg_strcasecmp(prev_wd, "SET") == 0) + else if (TailMatches2(MatchAny, "SET")) COMPLETE_WITH_ATTR(prev2_wd, ""); /* UPDATE xx SET yy = */ - else if (pg_strcasecmp(prev2_wd, "SET") == 0 && - pg_strcasecmp(prev4_wd, "UPDATE") == 0) + else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny)) COMPLETE_WITH_CONST("="); /* USER MAPPING */ - else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 || - pg_strcasecmp(prev3_wd, "CREATE") == 0 || - pg_strcasecmp(prev3_wd, "DROP") == 0) && - pg_strcasecmp(prev2_wd, "USER") == 0 && - pg_strcasecmp(prev_wd, "MAPPING") == 0) + else if (TailMatches3("ALTER|CREATE|DROP", "USER", "MAPPING")) COMPLETE_WITH_CONST("FOR"); - else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 && - pg_strcasecmp(prev3_wd, "USER") == 0 && - pg_strcasecmp(prev2_wd, "MAPPING") == 0 && - pg_strcasecmp(prev_wd, "FOR") == 0) + else if (TailMatches4("CREATE", "USER", "MAPPING", "FOR")) COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'CURRENT_USER'" " UNION SELECT 'PUBLIC'" " UNION SELECT 'USER'"); - else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 || - pg_strcasecmp(prev4_wd, "DROP") == 0) && - pg_strcasecmp(prev3_wd, "USER") == 0 && - pg_strcasecmp(prev2_wd, "MAPPING") == 0 && - pg_strcasecmp(prev_wd, "FOR") == 0) + else if (TailMatches4("ALTER|DROP", "USER", "MAPPING", "FOR")) COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); - else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 || - pg_strcasecmp(prev5_wd, "ALTER") == 0 || - pg_strcasecmp(prev5_wd, "DROP") == 0) && - pg_strcasecmp(prev4_wd, "USER") == 0 && - pg_strcasecmp(prev3_wd, "MAPPING") == 0 && - pg_strcasecmp(prev2_wd, "FOR") == 0) + else if (TailMatches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny)) COMPLETE_WITH_CONST("SERVER"); /* * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ] * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] */ - else if (pg_strcasecmp(prev_wd, "VACUUM") == 0) + else if (Matches1("VACUUM")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'FULL'" " UNION SELECT 'FREEZE'" " UNION SELECT 'ANALYZE'" " UNION SELECT 'VERBOSE'"); - else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && - (pg_strcasecmp(prev_wd, "FULL") == 0 || - pg_strcasecmp(prev_wd, "FREEZE") == 0)) + else if (Matches2("VACUUM", "FULL|FREEZE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'ANALYZE'" " UNION SELECT 'VERBOSE'"); - else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 && - pg_strcasecmp(prev_wd, "ANALYZE") == 0 && - (pg_strcasecmp(prev2_wd, "FULL") == 0 || - pg_strcasecmp(prev2_wd, "FREEZE") == 0)) + else if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'VERBOSE'"); - else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0 && - (pg_strcasecmp(prev2_wd, "FULL") == 0 || - pg_strcasecmp(prev2_wd, "FREEZE") == 0)) + else if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'ANALYZE'"); - else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0) + else if (Matches2("VACUUM", "VERBOSE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'ANALYZE'"); - else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && - pg_strcasecmp(prev_wd, "ANALYZE") == 0) + else if (Matches2("VACUUM", "ANALYZE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'VERBOSE'"); - else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 && - pg_strcasecmp(prev2_wd, "VERBOSE") == 0) || - (pg_strcasecmp(prev_wd, "VERBOSE") == 0 && - pg_strcasecmp(prev2_wd, "ANALYZE") == 0)) + else if (HeadMatches1("VACUUM")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); /* WITH [RECURSIVE] */ @@ -3913,29 +2770,26 @@ psql_completion(const char *text, int start, int end) * Only match when WITH is the first word, as WITH may appear in many * other contexts. */ - else if (pg_strcasecmp(prev_wd, "WITH") == 0 && - prev2_wd[0] == '\0') + else if (Matches1("WITH")) COMPLETE_WITH_CONST("RECURSIVE"); /* ANALYZE */ /* If the previous word is ANALYZE, produce list of tables */ - else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0) + else if (TailMatches1("ANALYZE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL); /* WHERE */ /* Simple case of the word before the where being the table name */ - else if (pg_strcasecmp(prev_wd, "WHERE") == 0) + else if (TailMatches2(MatchAny, "WHERE")) COMPLETE_WITH_ATTR(prev2_wd, ""); /* ... FROM ... */ /* TODO: also include SRF ? */ - else if (pg_strcasecmp(prev_wd, "FROM") == 0 && - pg_strcasecmp(prev3_wd, "COPY") != 0 && - pg_strcasecmp(prev3_wd, "\\copy") != 0) + else if (TailMatches1("FROM") && !Matches3("COPY|\\copy", MatchAny, "FROM")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); /* ... JOIN ... */ - else if (pg_strcasecmp(prev_wd, "JOIN") == 0) + else if (TailMatches1("JOIN")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); /* Backslash commands */ @@ -4782,11 +3636,13 @@ exec_query(const char *query) * that is, previous_words[0] gets the last word before point. * If we run out of words, remaining array elements are set to empty strings. * Each array element is filled with a malloc'd string. + * Returns the number of words that were actually found (up to nwords). */ -static void +static int get_previous_words(int point, char **previous_words, int nwords) { const char *buf = rl_line_buffer; /* alias */ + int words_found = 0; int i; /* first we look for a non-word char before the current point */ @@ -4856,10 +3712,14 @@ get_previous_words(int point, char **previous_words, int nwords) /* make a copy of chars from start to end inclusive */ s = pg_malloc(end - start + 2); strlcpy(s, &buf[start], end - start + 2); + + words_found++; } *previous_words++ = s; } + + return words_found; } /*