agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH 02/12] Make keywords' case follow to input 7+ messages / 4 participants [nested] [flat]
* [PATCH 02/12] Make keywords' case follow to input @ 2016-09-14 03:48 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 7+ messages in thread From: Kyotaro Horiguchi @ 2016-09-14 03:48 UTC (permalink / raw) Currently some keywords suggested along with database objects are always in upper case. This patch changes the behavior so that the case of the additional keywords follow the setting of COMP_KEYWORD_CASE. COMPLETE_WITH_ATTR needs completion_charp to be appendable, so this patch changes it to a PQExpBuffer and adjust COMPLET_WITH_* macros to that change. Since COMPLETE_WITH_(QUERY|SCHEMA_QUERY|ATTR) are not given addons in most cases so each of them are split into two macros that requires addons (suffixed by _KW) and that don't. This leaves keywords contained in Query_for_list_of_grant_roles and Query_for_enum, but it is another problem. --- src/bin/psql/tab-complete-macros.h | 70 ++++++-- src/bin/psql/tab-complete.c | 358 ++++++++++++++++++++----------------- 2 files changed, 246 insertions(+), 182 deletions(-) diff --git a/src/bin/psql/tab-complete-macros.h b/src/bin/psql/tab-complete-macros.h index 97ffcd1..44fd547 100644 --- a/src/bin/psql/tab-complete-macros.h +++ b/src/bin/psql/tab-complete-macros.h @@ -231,16 +231,46 @@ * 5) The list of attributes of the given table (possibly schema-qualified). * 6) The list of arguments to the given function (possibly schema-qualified). */ -#define COMPLETE_WITH_QUERY(query) \ +#define APPEND_COMP_CHARP(charp) \ + appendPQExpBufferStr(completion_charp, charp); + +#define SET_COMP_CHARP(charp) \ + resetPQExpBuffer(completion_charp); \ + APPEND_COMP_CHARP(charp); + +#define COMPLETION_CHARP (completion_charp->data) + +#define COMPLETE_WITH_QUERY(query) \ +do { \ + SET_COMP_CHARP(query); \ + return completion_matches(text, complete_from_query); \ +} while (0) + +/* + * COMPLETE_WITH_QUERY with additional keywords. Keywords are complete + * case-sensitively + */ +#define COMPLETE_WITH_QUERY_KW(query, addon) \ do { \ - completion_charp = query; \ + SET_COMP_CHARP(query); \ + APPEND_COMP_CHARP(addon); \ return completion_matches(text, complete_from_query); \ } while (0) -#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \ +#define COMPLETE_WITH_SCHEMA_QUERY(query) \ do { \ completion_squery = &(query); \ - completion_charp = addon; \ + return completion_matches(text, complete_from_schema_query); \ +} while (0) + +/* + * COMPLETE_WITH_SCHEMA_QUERY with additional keywords. Keywords are complete + * case-sensitively + */ +#define COMPLETE_WITH_SCHEMA_QUERY_KW(query, addon) \ +do { \ + completion_squery = &(query); \ + SET_COMP_CHARP(addon); \ return completion_matches(text, complete_from_schema_query); \ } while (0) @@ -260,12 +290,12 @@ do { \ #define COMPLETE_WITH_CONST(string) \ do { \ - completion_charp = string; \ + SET_COMP_CHARP(string); \ completion_case_sensitive = false; \ return completion_matches(text, complete_from_const); \ } while (0) -#define COMPLETE_WITH_ATTR(relation, addon) \ +#define COMPLETE_WITH_ATTR_KW(relation, addon) \ do { \ char *_completion_schema; \ char *_completion_table; \ @@ -278,18 +308,22 @@ do { \ false, false, pset.encoding); \ if (_completion_table == NULL) \ { \ - completion_charp = Query_for_list_of_attributes addon; \ + SET_COMP_CHARP(Query_for_list_of_attributes); \ + APPEND_COMP_CHARP(addon); \ completion_info_charp = relation; \ } \ else \ { \ - completion_charp = Query_for_list_of_attributes_with_schema addon; \ + SET_COMP_CHARP(Query_for_list_of_attributes_with_schema); \ + APPEND_COMP_CHARP(addon); \ completion_info_charp = _completion_table; \ completion_info_charp2 = _completion_schema; \ } \ return completion_matches(text, complete_from_query); \ } while (0) +#define COMPLETE_WITH_ATTR(query) COMPLETE_WITH_ATTR_KW((query), "") + #define COMPLETE_WITH_ENUM_VALUE(type) \ do { \ char *_completion_schema; \ @@ -303,12 +337,12 @@ do { \ false, false, pset.encoding); \ if (_completion_type == NULL)\ { \ - completion_charp = Query_for_list_of_enum_values; \ + SET_COMP_CHARP(Query_for_list_of_enum_values); \ completion_info_charp = type; \ } \ else \ { \ - completion_charp = Query_for_list_of_enum_values_with_schema; \ + SET_COMP_CHARP(Query_for_list_of_enum_values_with_schema); \ completion_info_charp = _completion_type; \ completion_info_charp2 = _completion_schema; \ } \ @@ -328,12 +362,12 @@ do { \ false, false, pset.encoding); \ if (_completion_function == NULL) \ { \ - completion_charp = Query_for_list_of_arguments; \ + SET_COMP_CHARP(Query_for_list_of_arguments); \ completion_info_charp = function; \ } \ else \ { \ - completion_charp = Query_for_list_of_arguments_with_schema; \ + SET_COMP_CHARP(Query_for_list_of_arguments_with_schema); \ completion_info_charp = _completion_function; \ completion_info_charp2 = _completion_schema; \ } \ @@ -425,6 +459,16 @@ do { \ COMPLETE_WITH_LIST_CS(list); \ } while (0) - +#define ADDLIST1(s1) additional_kw_query(text, 1, s1) +#define ADDLIST2(s1, s2) additional_kw_query(text, 2, s1, s2) +#define ADDLIST3(s1, s2, s3) additional_kw_query(text, 3, s1, s2, s3) +#define ADDLIST4(s1, s2, s3, s4) \ + additional_kw_query(text, 4, s1, s2, s3, s4) +#define ADDLIST13(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13) \ + additional_kw_query(text, 12, s1, s2, s3, s4, s5, s6, s7, \ + s8, s9, s10, s11, s12, s13) +#define ADDLIST15(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15) \ + additional_kw_query(text, 12, s1, s2, s3, s4, s5, s6, s7, \ + s8, s9, s10, s11, s12, s13, s14, s15) #endif /* TAB_COMPLETE_MACROS_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 24669d0..21eb7ab 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -126,7 +126,7 @@ static int completion_max_records; * Communication variables set by COMPLETE_WITH_FOO macros and then used by * the completion callback functions. Ugly but there is no better way. */ -static const char *completion_charp; /* to pass a string */ +static PQExpBuffer completion_charp = NULL; /* to pass a string */ static const char *const * completion_charpp; /* to pass a list of strings */ static const char *completion_info_charp; /* to pass a second string */ static const char *completion_info_charp2; /* to pass a third string */ @@ -796,6 +796,7 @@ static char **complete_from_variables(const char *text, static char *complete_from_files(const char *text, int state); static char *pg_strdup_keyword_case(const char *s, const char *ref); +static char *additional_kw_query( const char *ref, int n, ...); static char *escape_string(const char *text); static PGresult *exec_query(const char *query); @@ -967,7 +968,8 @@ psql_completion(const char *text, int start, int end) #endif /* Clear a few things. */ - completion_charp = NULL; + if (completion_charp == NULL) + completion_charp = createPQExpBuffer(); completion_charpp = NULL; completion_info_charp = NULL; completion_info_charp2 = NULL; @@ -1076,8 +1078,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER TABLE */ if (Matches2("ALTER", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER something */ if (Matches1("ALTER")) @@ -1193,8 +1195,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER INDEX */ if (Matches2("ALTER", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER INDEX <name> */ if (Matches3("ALTER", "INDEX", MatchAny)) COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET"); @@ -1222,8 +1224,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER MATERIALIZED VIEW */ if (Matches3("ALTER", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_matviews, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER USER,ROLE <name> */ if (Matches3("ALTER", "USER|ROLE", MatchAny) && @@ -1378,7 +1380,7 @@ psql_completion_internal(const char *text, char **previous_words, * If we have ALTER TRIGGER <sth> ON, then add the correct tablename */ if (Matches4("ALTER", "TRIGGER", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TRIGGER <name> ON <name> */ if (Matches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny)) @@ -1424,10 +1426,10 @@ psql_completion_internal(const char *text, char **previous_words, } /* ALTER TABLE xxx INHERIT */ if (Matches4("ALTER", "TABLE", MatchAny, "INHERIT")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TABLE xxx NO INHERIT */ if (Matches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TABLE xxx DISABLE */ if (Matches4("ALTER", "TABLE", MatchAny, "DISABLE")) COMPLETE_WITH_LIST3("ROW LEVEL SECURITY", "RULE", "TRIGGER"); @@ -1444,13 +1446,13 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER TABLE xxx ALTER */ if (Matches4("ALTER", "TABLE", MatchAny, "ALTER")) - COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'"); + COMPLETE_WITH_ATTR_KW(prev2_wd, ADDLIST2("COLUMN", "CONSTRAINT")); /* ALTER TABLE xxx RENAME */ if (Matches4("ALTER", "TABLE", MatchAny, "RENAME")) - COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'"); + COMPLETE_WITH_ATTR_KW(prev2_wd, ADDLIST3("COLUMN", "CONSTRAINT", "TO")); if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN")) - COMPLETE_WITH_ATTR(prev3_wd, ""); + COMPLETE_WITH_ATTR(prev3_wd); /* ALTER TABLE xxx RENAME yyy */ if (Matches5("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO"))) @@ -1465,7 +1467,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("COLUMN", "CONSTRAINT"); /* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */ if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN")) - COMPLETE_WITH_ATTR(prev3_wd, ""); + COMPLETE_WITH_ATTR(prev3_wd); /* * If we have ALTER TABLE <sth> ALTER|DROP|RENAME|VALIDATE CONSTRAINT, @@ -1616,7 +1618,7 @@ psql_completion_internal(const char *text, char **previous_words, * of attributes */ if (Matches5("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE")) - COMPLETE_WITH_ATTR(prev3_wd, ""); + COMPLETE_WITH_ATTR(prev3_wd); /* ALTER TYPE ALTER ATTRIBUTE <foo> */ if (Matches6("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny)) COMPLETE_WITH_CONST("TYPE"); @@ -1653,9 +1655,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED"); /* CLUSTER */ if (Matches1("CLUSTER")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (Matches2("CLUSTER", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* If we have CLUSTER <sth>, then add "USING" */ if (Matches2("CLUSTER", MatchAnyExcept("VERBOSE|ON"))) COMPLETE_WITH_CONST("USING"); @@ -1702,7 +1705,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint); } if (Matches4("COMMENT", "ON", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (Matches4("COMMENT", "ON", "EVENT", "TRIGGER")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); if (Matches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) || @@ -1717,11 +1720,11 @@ psql_completion_internal(const char *text, char **previous_words, * backslash command). */ if (Matches1("COPY|\\copy")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - " UNION ALL SELECT '('"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("(")); /* If we have COPY BINARY, complete with list of tables */ if (Matches2("COPY", "BINARY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* If we have COPY (, complete it with legal commands */ if (Matches2("COPY|\\copy", "(")) COMPLETE_WITH_LIST7("SELECT", "TABLE", "VALUES", "INSERT", "UPDATE", "DELETE", "WITH"); @@ -1733,7 +1736,7 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches3("COPY|\\copy", MatchAny, "FROM|TO") || Matches4("COPY", "BINARY", MatchAny, "FROM|TO")) { - completion_charp = ""; + SET_COMP_CHARP(""); return completion_matches(text, complete_from_files); } @@ -1802,21 +1805,20 @@ psql_completion_internal(const char *text, char **previous_words, * existing indexes */ if (TailMatches2("CREATE|UNIQUE", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'ON'" - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST2("ON", "CONCURRENTLY")); /* Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of tables */ if (TailMatches3("INDEX|CONCURRENTLY", MatchAny, "ON") || TailMatches2("INDEX|CONCURRENTLY", "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing * indexes */ if (TailMatches3("CREATE|UNIQUE", "INDEX", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'ON'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("ON")); /* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */ if (TailMatches3("CREATE|UNIQUE", "INDEX", MatchAny) || TailMatches4("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny)) @@ -1831,10 +1833,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("(", "USING"); if (TailMatches5("INDEX", MatchAny, "ON", MatchAny, "(") || TailMatches4("INDEX|CONCURRENTLY", "ON", MatchAny, "(")) - COMPLETE_WITH_ATTR(prev2_wd, ""); + COMPLETE_WITH_ATTR(prev2_wd); /* same if you put in USING */ if (TailMatches5("ON", MatchAny, "USING", MatchAny, "(")) - COMPLETE_WITH_ATTR(prev4_wd, ""); + COMPLETE_WITH_ATTR(prev4_wd); /* Complete USING with an index method */ if (TailMatches6("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING") || TailMatches5("INDEX", MatchAny, "ON", MatchAny, "USING") || @@ -1851,7 +1853,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("ON"); /* Complete "CREATE POLICY <name> ON <table>" */ if (Matches4("CREATE", "POLICY", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* Complete "CREATE POLICY <name> ON <table> FOR|TO|USING|WITH CHECK" */ if (Matches5("CREATE", "POLICY", MatchAny, "ON", MatchAny)) COMPLETE_WITH_LIST4("FOR", "TO", "USING (", "WITH CHECK ("); @@ -1889,7 +1891,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("TO"); /* Complete "AS ON <sth> TO" with a table name */ if (TailMatches4("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */ if (TailMatches3("CREATE", "SEQUENCE", MatchAny) || @@ -1945,47 +1947,47 @@ psql_completion_internal(const char *text, char **previous_words, * tables */ if (TailMatches6("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */ if (TailMatches7("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("ON", MatchAny)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("ON", MatchAny)) COMPLETE_WITH_LIST7("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY", "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && + if (HeadMatches2("CREATE", "TRIGGER") && (TailMatches1("DEFERRABLE") || TailMatches2("INITIALLY", "IMMEDIATE|DEFERRED"))) COMPLETE_WITH_LIST4("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("REFERENCING")) + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("REFERENCING")) COMPLETE_WITH_LIST2("OLD TABLE", "NEW TABLE"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("OLD|NEW", "TABLE")) + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("OLD|NEW", "TABLE")) COMPLETE_WITH_CONST("AS"); - else if (HeadMatches2("CREATE", "TRIGGER") && - (TailMatches5("REFERENCING", "OLD", "TABLE", "AS", MatchAny) || - TailMatches4("REFERENCING", "OLD", "TABLE", MatchAny))) + if (HeadMatches2("CREATE", "TRIGGER") && + (TailMatches5("REFERENCING", "OLD", "TABLE", "AS", MatchAny) || + TailMatches4("REFERENCING", "OLD", "TABLE", MatchAny))) COMPLETE_WITH_LIST4("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && - (TailMatches5("REFERENCING", "NEW", "TABLE", "AS", MatchAny) || - TailMatches4("REFERENCING", "NEW", "TABLE", MatchAny))) + if (HeadMatches2("CREATE", "TRIGGER") && + (TailMatches5("REFERENCING", "NEW", "TABLE", "AS", MatchAny) || + TailMatches4("REFERENCING", "NEW", "TABLE", MatchAny))) COMPLETE_WITH_LIST4("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && - (TailMatches9("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || - TailMatches8("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || - TailMatches8("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) || - TailMatches7("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))) + if (HeadMatches2("CREATE", "TRIGGER") && + (TailMatches9("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || + TailMatches8("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || + TailMatches8("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) || + TailMatches7("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))) COMPLETE_WITH_LIST3("FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("FOR")) + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("FOR")) COMPLETE_WITH_LIST3("EACH", "ROW", "STATEMENT"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("FOR", "EACH")) + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("FOR", "EACH")) COMPLETE_WITH_LIST2("ROW", "STATEMENT"); - else if (HeadMatches2("CREATE", "TRIGGER") && + if (HeadMatches2("CREATE", "TRIGGER") && (TailMatches3("FOR", "EACH", "ROW|STATEMENT") || TailMatches2("FOR", "ROW|STATEMENT"))) COMPLETE_WITH_LIST2("WHEN (", "EXECUTE PROCEDURE"); /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */ if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE")) COMPLETE_WITH_CONST("PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("EXECUTE", "PROCEDURE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("EXECUTE", "PROCEDURE")) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); /* CREATE ROLE,USER,GROUP <name> */ if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) && @@ -2068,7 +2070,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("FROM"); /* Complete DELETE FROM with a list of tables */ if (TailMatches2("DELETE", "FROM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete DELETE FROM <table> */ if (TailMatches3("DELETE", "FROM", MatchAny)) COMPLETE_WITH_LIST2("USING", "WHERE"); @@ -2106,10 +2108,10 @@ psql_completion_internal(const char *text, char **previous_words, /* DROP INDEX */ if (Matches2("DROP", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("CONCURRENTLY")); if (Matches3("DROP", "INDEX", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (Matches3("DROP", "INDEX", MatchAny)) COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); if (Matches4("DROP", "INDEX", "CONCURRENTLY", MatchAny)) @@ -2119,7 +2121,7 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches2("DROP", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); if (Matches3("DROP", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); /* DROP OWNED BY */ if (Matches2("DROP", "OWNED")) @@ -2225,7 +2227,7 @@ psql_completion_internal(const char *text, char **previous_words, /* FOREIGN TABLE */ if (TailMatches2("FOREIGN", "TABLE") && !TailMatches3("CREATE", MatchAny, MatchAny)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables); /* FOREIGN SERVER */ if (TailMatches2("FOREIGN", "SERVER")) @@ -2234,20 +2236,10 @@ psql_completion_internal(const char *text, char **previous_words, /* GRANT && REVOKE --- is allowed inside CREATE SCHEMA, so use TailMatches */ /* Complete GRANT/REVOKE with a list of roles and privileges */ if (TailMatches1("GRANT|REVOKE")) - COMPLETE_WITH_QUERY(Query_for_list_of_roles - " UNION SELECT 'SELECT'" - " UNION SELECT 'INSERT'" - " UNION SELECT 'UPDATE'" - " UNION SELECT 'DELETE'" - " UNION SELECT 'TRUNCATE'" - " UNION SELECT 'REFERENCES'" - " UNION SELECT 'TRIGGER'" - " UNION SELECT 'CREATE'" - " UNION SELECT 'CONNECT'" - " UNION SELECT 'TEMPORARY'" - " UNION SELECT 'EXECUTE'" - " UNION SELECT 'USAGE'" - " UNION SELECT 'ALL'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST13("SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", + "REFERENCES", "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", + "EXECUTE", "USAGE", "ALL")); /* * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with @@ -2275,22 +2267,22 @@ psql_completion_internal(const char *text, char **previous_words, * privilege. */ 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'" - " UNION SELECT 'ALL TABLES IN SCHEMA'" - " UNION SELECT 'DATABASE'" - " UNION SELECT 'DOMAIN'" - " UNION SELECT 'FOREIGN DATA WRAPPER'" - " UNION SELECT 'FOREIGN SERVER'" - " UNION SELECT 'FUNCTION'" - " UNION SELECT 'LANGUAGE'" - " UNION SELECT 'LARGE OBJECT'" - " UNION SELECT 'SCHEMA'" - " UNION SELECT 'SEQUENCE'" - " UNION SELECT 'TABLE'" - " UNION SELECT 'TABLESPACE'" - " UNION SELECT 'TYPE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tsvmf, + ADDLIST15("ALL FUNCTIONS IN SCHEMA", + "ALL SEQUENCES IN SCHEMA", + "ALL TABLES IN SCHEMA", + "DATABASE", + "DOMAIN", + "FOREIGN DATA WRAPPER", + "FOREIGN SERVER", + "FUNCTION", + "LANGUAGE", + "LARGE OBJECT", + "SCHEMA", + "SEQUENCE", + "TABLE", + "TABLESPACE", + "TYPE")); if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL")) COMPLETE_WITH_LIST3("FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA", @@ -2310,21 +2302,21 @@ psql_completion_internal(const char *text, char **previous_words, if (TailMatches1("DATABASE")) COMPLETE_WITH_QUERY(Query_for_list_of_databases); if (TailMatches1("DOMAIN")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains); if (TailMatches1("FUNCTION")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatches1("LANGUAGE")) COMPLETE_WITH_QUERY(Query_for_list_of_languages); if (TailMatches1("SCHEMA")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (TailMatches1("SEQUENCE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences); if (TailMatches1("TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); if (TailMatches1("TABLESPACE")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); if (TailMatches1("TYPE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes); if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny)) COMPLETE_WITH_CONST("TO"); else @@ -2388,10 +2380,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("INTO"); /* Complete INSERT INTO with table names */ if (TailMatches2("INSERT", "INTO")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete "INSERT INTO <table> (" with attribute names */ if (TailMatches4("INSERT", "INTO", MatchAny, "(")) - COMPLETE_WITH_ATTR(prev2_wd, ""); + COMPLETE_WITH_ATTR(prev2_wd); /* * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or @@ -2415,10 +2407,10 @@ psql_completion_internal(const char *text, char **previous_words, /* LOCK */ /* Complete LOCK [TABLE] with a list of tables */ if (Matches1("LOCK")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - " UNION SELECT 'TABLE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("TABLE")); if (Matches2("LOCK", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* For the following, handle the case of a single table only for now */ @@ -2463,7 +2455,7 @@ psql_completion_internal(const char *text, char **previous_words, if (TailMatches3("FROM", MatchAny, "ORDER")) COMPLETE_WITH_CONST("BY"); if (TailMatches4("FROM", MatchAny, "ORDER", "BY")) - COMPLETE_WITH_ATTR(prev3_wd, ""); + COMPLETE_WITH_ATTR(prev3_wd); /* PREPARE xx AS */ if (Matches3("PREPARE", MatchAny, "AS")) @@ -2492,10 +2484,10 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches2("REFRESH", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); if (Matches3("REFRESH", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_matviews, + ADDLIST1("CONCURRENTLY")); if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny)) COMPLETE_WITH_CONST("WITH"); if (Matches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny)) @@ -2513,9 +2505,9 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches1("REINDEX")) COMPLETE_WITH_LIST5("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE"); if (Matches2("REINDEX", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); if (Matches2("REINDEX", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (Matches2("REINDEX", "SCHEMA")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (Matches2("REINDEX", "SYSTEM|DATABASE")) @@ -2585,7 +2577,8 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("ONLY", "WRITE"); /* SET CONSTRAINTS */ if (Matches2("SET", "CONSTRAINTS")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_constraints_with_schema, + ADDLIST1("ALL")); /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */ if (Matches3("SET", "CONSTRAINTS", MatchAny)) COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE"); @@ -2597,7 +2590,8 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION"); /* Complete SET SESSION AUTHORIZATION with username */ if (Matches3("SET", "SESSION", "AUTHORIZATION")) - COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST1("DEFAULT")); /* Complete RESET SESSION with AUTHORIZATION */ if (Matches2("RESET", "SESSION")) COMPLETE_WITH_CONST("AUTHORIZATION"); @@ -2623,10 +2617,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST(my_list); } 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' "); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_schemas + " AND nspname not like 'pg\\_toast%%' " + " AND nspname not like 'pg\\_temp%%' ", + ADDLIST1("DEFAULT")); else { /* generic, type based, GUC support, guctype is malloc'ed */ @@ -2661,7 +2655,7 @@ psql_completion_internal(const char *text, char **previous_words, /* TABLE, but not TABLE embedded in other commands */ if (Matches1("TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations); /* TABLESAMPLE */ if (TailMatches1("TABLESAMPLE")) @@ -2671,7 +2665,7 @@ psql_completion_internal(const char *text, char **previous_words, /* TRUNCATE */ if (Matches1("TRUNCATE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* UNLISTEN */ if (Matches1("UNLISTEN")) @@ -2680,13 +2674,13 @@ psql_completion_internal(const char *text, char **previous_words, /* UPDATE --- can be inside EXPLAIN, RULE, etc */ /* If prev. word is UPDATE suggest a list of tables */ if (TailMatches1("UPDATE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete UPDATE <table> with "SET" */ if (TailMatches2("UPDATE", MatchAny)) COMPLETE_WITH_CONST("SET"); /* Complete UPDATE <table> SET with list of attributes */ if (TailMatches3("UPDATE", MatchAny, "SET")) - COMPLETE_WITH_ATTR(prev2_wd, ""); + COMPLETE_WITH_ATTR(prev2_wd); /* UPDATE <table> SET <attr> = */ if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny)) COMPLETE_WITH_CONST("="); @@ -2695,10 +2689,8 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING")) COMPLETE_WITH_CONST("FOR"); if (Matches4("CREATE", "USER", "MAPPING", "FOR")) - COMPLETE_WITH_QUERY(Query_for_list_of_roles - " UNION SELECT 'CURRENT_USER'" - " UNION SELECT 'PUBLIC'" - " UNION SELECT 'USER'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST3("CURRENT_USER", "PUBLIC", "USER")); if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR")) COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny)) @@ -2711,29 +2703,26 @@ psql_completion_internal(const char *text, char **previous_words, * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] */ if (Matches1("VACUUM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'FULL'" - " UNION SELECT 'FREEZE'" - " UNION SELECT 'ANALYZE'" - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST4("FULL", "FREEZE", + "ANALYZE", "VERBOSE")); if (Matches2("VACUUM", "FULL|FREEZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'" - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST2("ANALYZE", "VERBOSE")); if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("ANALYZE")); if (Matches2("VACUUM", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("ANALYZE")); if (Matches2("VACUUM", "ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (HeadMatches1("VACUUM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* WITH [RECURSIVE] */ @@ -2747,21 +2736,21 @@ psql_completion_internal(const char *text, char **previous_words, /* ANALYZE */ /* Complete with list of tables */ if (Matches1("ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf); /* WHERE */ /* Simple case of the word before the where being the table name */ if (TailMatches2(MatchAny, "WHERE")) - COMPLETE_WITH_ATTR(prev2_wd, ""); + COMPLETE_WITH_ATTR(prev2_wd); /* ... FROM ... */ /* TODO: also include SRF ? */ if (TailMatches1("FROM") && !Matches3("COPY|\\copy", MatchAny, "FROM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); /* ... JOIN ... */ if (TailMatches1("JOIN")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); /* Backslash commands */ /* TODO: \dc \dd \dl */ @@ -2778,13 +2767,13 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_roles); } if (TailMatchesCS1("\\da*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates); if (TailMatchesCS1("\\dA*")) COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); if (TailMatchesCS1("\\db*")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); if (TailMatchesCS1("\\dD*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains); if (TailMatchesCS1("\\des*")) COMPLETE_WITH_QUERY(Query_for_list_of_servers); if (TailMatchesCS1("\\deu*")) @@ -2792,7 +2781,7 @@ psql_completion_internal(const char *text, char **previous_words, if (TailMatchesCS1("\\dew*")) COMPLETE_WITH_QUERY(Query_for_list_of_fdws); if (TailMatchesCS1("\\df*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\dFd*")) COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries); @@ -2805,40 +2794,40 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations); if (TailMatchesCS1("\\di*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (TailMatchesCS1("\\dL*")) COMPLETE_WITH_QUERY(Query_for_list_of_languages); if (TailMatchesCS1("\\dn*")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (TailMatchesCS1("\\dp") || TailMatchesCS1("\\z")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); if (TailMatchesCS1("\\ds*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences); if (TailMatchesCS1("\\dt*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); if (TailMatchesCS1("\\dT*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes); if (TailMatchesCS1("\\du*") || TailMatchesCS1("\\dg*")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); if (TailMatchesCS1("\\dv*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\dx*")) COMPLETE_WITH_QUERY(Query_for_list_of_extensions); if (TailMatchesCS1("\\dm*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (TailMatchesCS1("\\dE*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables); if (TailMatchesCS1("\\dy*")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); /* must be at end of \d alternatives: */ if (TailMatchesCS1("\\d*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations); if (TailMatchesCS1("\\ef")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\ev")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\encoding")) COMPLETE_WITH_QUERY(Query_for_list_of_encodings); @@ -2907,14 +2896,14 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST_CS3("default", "verbose", "terse"); } if (TailMatchesCS1("\\sf*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\sv*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|" "\\ir|\\include_relative|\\o|\\out|" "\\s|\\w|\\write|\\lo_import")) { - completion_charp = "\\"; + SET_COMP_CHARP("\\"); return completion_matches(text, complete_from_files); } @@ -2934,8 +2923,7 @@ psql_completion_internal(const char *text, char **previous_words, if (words_after_create[i].query) COMPLETE_WITH_QUERY(words_after_create[i].query); if (words_after_create[i].squery) - COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery, - NULL); + COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery); break; } } @@ -3182,13 +3170,13 @@ _complete_from_query(int is_schema_query, const char *text, int state) char_length, e_text); /* If an addon query was provided, use it */ - if (completion_charp) - appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); + if (COMPLETION_CHARP[0]) + appendPQExpBuffer(&query_buffer, "\n%s", COMPLETION_CHARP); } else { /* completion_charp is an sprintf-style format string */ - appendPQExpBuffer(&query_buffer, completion_charp, + appendPQExpBuffer(&query_buffer, COMPLETION_CHARP, char_length, e_text, e_info_charp, e_info_charp, e_info_charp2, e_info_charp2); @@ -3303,18 +3291,17 @@ complete_from_list(const char *text, int state) static char * complete_from_const(const char *text, int state) { - Assert(completion_charp != NULL); if (state == 0) { if (completion_case_sensitive) - return pg_strdup(completion_charp); + return pg_strdup(COMPLETION_CHARP); else /* * If case insensitive matching was requested initially, adjust * the case according to setting. */ - return pg_strdup_keyword_case(completion_charp, text); + return pg_strdup_keyword_case(COMPLETION_CHARP, text); } else return NULL; @@ -3415,7 +3402,7 @@ complete_from_files(const char *text, int state) if (state == 0) { /* Initialization: stash the unquoted input. */ - unquoted_text = strtokx(text, "", NULL, "'", *completion_charp, + unquoted_text = strtokx(text, "", NULL, "'", COMPLETION_CHARP[0], false, true, pset.encoding); /* expect a NULL return for the empty string only */ if (!unquoted_text) @@ -3436,7 +3423,7 @@ complete_from_files(const char *text, int state) * bother providing a macro to simplify this. */ ret = quote_if_needed(unquoted_match, " \t\r\n\"`", - '\'', *completion_charp, pset.encoding); + '\'', COMPLETION_CHARP[0], pset.encoding); if (ret) free(unquoted_match); else @@ -3480,6 +3467,39 @@ pg_strdup_keyword_case(const char *s, const char *ref) return ret; } +/* Construct codelet to append given keywords */ +static char * +additional_kw_query(const char *ref, int n, ...) +{ + va_list ap; + static PQExpBuffer qbuf = NULL; + int i; + + if (qbuf == NULL) + qbuf = createPQExpBuffer(); + else + resetPQExpBuffer(qbuf); + + /* Construct an additional queriy to append keywords */ + appendPQExpBufferStr(qbuf, " UNION ALL SELECT * FROM (VALUES "); + + va_start(ap, n); + for (i = 0 ; i < n ; i++) + { + char *item = pg_strdup_keyword_case(va_arg(ap, char *), ref); + if (i > 0) appendPQExpBufferChar(qbuf, ','); + appendPQExpBufferStr(qbuf, "('"); + appendPQExpBufferStr(qbuf, item); + appendPQExpBufferStr(qbuf, "')"); + pg_free(item); + } + va_end(ap); + + appendPQExpBufferStr(qbuf, ") as x"); + + return qbuf->data; +} + /* * escape_string - Escape argument for use as string literal. -- 2.9.2 ----Next_Part(Mon_Nov_28_19_13_50_2016_736)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="0003-Introduce-word-shift-and-removal-feature-to-psql-com.patch" ^ permalink raw reply [nested|flat] 7+ messages in thread
* [PATCH 02/17] Make keywords' case follow to input @ 2016-09-14 03:48 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 7+ messages in thread From: Kyotaro Horiguchi @ 2016-09-14 03:48 UTC (permalink / raw) Currently some keywords suggested along with database objects are always in upper case. This patch changes the behavior so that the case of the additional keywords follow the setting of COMP_KEYWORD_CASE. COMPLETE_WITH_ATTR needs completion_charp to be appendable, so this patch changes it to a PQExpBuffer and adjust COMPLET_WITH_* macros to that change. Since COMPLETE_WITH_(QUERY|SCHEMA_QUERY|ATTR) are not given addons in most cases so each of them are split into two macros that requires addons (suffixed by _KW) and that don't. This leaves keywords contained in Query_for_list_of_grant_roles and Query_for_enum, but it is another problem. --- src/bin/psql/tab-complete-macros.h | 75 ++++++-- src/bin/psql/tab-complete.c | 354 +++++++++++++++++++++---------------- 2 files changed, 259 insertions(+), 170 deletions(-) diff --git a/src/bin/psql/tab-complete-macros.h b/src/bin/psql/tab-complete-macros.h index 97ffcd1..e0cbf49 100644 --- a/src/bin/psql/tab-complete-macros.h +++ b/src/bin/psql/tab-complete-macros.h @@ -231,16 +231,46 @@ * 5) The list of attributes of the given table (possibly schema-qualified). * 6) The list of arguments to the given function (possibly schema-qualified). */ -#define COMPLETE_WITH_QUERY(query) \ +#define APPEND_COMP_CHARP(charp) \ + appendPQExpBufferStr(completion_charp, charp); + +#define SET_COMP_CHARP(charp) \ + resetPQExpBuffer(completion_charp); \ + APPEND_COMP_CHARP(charp); + +#define COMPLETION_CHARP (completion_charp->data) + +#define COMPLETE_WITH_QUERY(query) \ +do { \ + SET_COMP_CHARP(query); \ + return completion_matches(text, complete_from_query); \ +} while (0) + +/* + * COMPLETE_WITH_QUERY with additional keywords. Keywords are complete + * case-sensitively + */ +#define COMPLETE_WITH_QUERY_KW(query, addon) \ do { \ - completion_charp = query; \ + SET_COMP_CHARP(query); \ + APPEND_COMP_CHARP(addon); \ return completion_matches(text, complete_from_query); \ } while (0) -#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \ +#define COMPLETE_WITH_SCHEMA_QUERY(query) \ do { \ completion_squery = &(query); \ - completion_charp = addon; \ + return completion_matches(text, complete_from_schema_query); \ +} while (0) + +/* + * COMPLETE_WITH_SCHEMA_QUERY with additional keywords. Keywords are complete + * case-sensitively + */ +#define COMPLETE_WITH_SCHEMA_QUERY_KW(query, addon) \ +do { \ + completion_squery = &(query); \ + SET_COMP_CHARP(addon); \ return completion_matches(text, complete_from_schema_query); \ } while (0) @@ -260,12 +290,12 @@ do { \ #define COMPLETE_WITH_CONST(string) \ do { \ - completion_charp = string; \ + SET_COMP_CHARP(string); \ completion_case_sensitive = false; \ return completion_matches(text, complete_from_const); \ } while (0) -#define COMPLETE_WITH_ATTR(relation, addon) \ +#define COMPLETE_WITH_ATTR_KW(relation, addon) \ do { \ char *_completion_schema; \ char *_completion_table; \ @@ -278,18 +308,22 @@ do { \ false, false, pset.encoding); \ if (_completion_table == NULL) \ { \ - completion_charp = Query_for_list_of_attributes addon; \ + SET_COMP_CHARP(Query_for_list_of_attributes); \ + APPEND_COMP_CHARP(addon); \ completion_info_charp = relation; \ } \ else \ { \ - completion_charp = Query_for_list_of_attributes_with_schema addon; \ + SET_COMP_CHARP(Query_for_list_of_attributes_with_schema); \ + APPEND_COMP_CHARP(addon); \ completion_info_charp = _completion_table; \ completion_info_charp2 = _completion_schema; \ } \ return completion_matches(text, complete_from_query); \ } while (0) +#define COMPLETE_WITH_ATTR(query) COMPLETE_WITH_ATTR_KW((query), "") + #define COMPLETE_WITH_ENUM_VALUE(type) \ do { \ char *_completion_schema; \ @@ -303,12 +337,12 @@ do { \ false, false, pset.encoding); \ if (_completion_type == NULL)\ { \ - completion_charp = Query_for_list_of_enum_values; \ + SET_COMP_CHARP(Query_for_list_of_enum_values); \ completion_info_charp = type; \ } \ else \ { \ - completion_charp = Query_for_list_of_enum_values_with_schema; \ + SET_COMP_CHARP(Query_for_list_of_enum_values_with_schema); \ completion_info_charp = _completion_type; \ completion_info_charp2 = _completion_schema; \ } \ @@ -328,12 +362,12 @@ do { \ false, false, pset.encoding); \ if (_completion_function == NULL) \ { \ - completion_charp = Query_for_list_of_arguments; \ + SET_COMP_CHARP(Query_for_list_of_arguments); \ completion_info_charp = function; \ } \ else \ { \ - completion_charp = Query_for_list_of_arguments_with_schema; \ + SET_COMP_CHARP(Query_for_list_of_arguments_with_schema); \ completion_info_charp = _completion_function; \ completion_info_charp2 = _completion_schema; \ } \ @@ -425,6 +459,21 @@ do { \ COMPLETE_WITH_LIST_CS(list); \ } while (0) - +#define ADDLIST1(s1) additional_kw_query(text, 1, s1) +#define ADDLIST2(s1, s2) additional_kw_query(text, 2, s1, s2) +#define ADDLIST3(s1, s2, s3) additional_kw_query(text, 3, s1, s2, s3) +#define ADDLIST4(s1, s2, s3, s4) \ + additional_kw_query(text, 4, s1, s2, s3, s4) +#define ADDLIST6(s1, s2, s3, s4, s5, s6) \ + additional_kw_query(text, 6, s1, s2, s3, s4, s5, s6) +#define ADDLIST13(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13) \ + additional_kw_query(text, 13, s1, s2, s3, s4, s5, s6, s7, \ + s8, s9, s10, s11, s12, s13) +#define ADDLIST15(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15) \ + additional_kw_query(text, 15, s1, s2, s3, s4, s5, s6, s7, \ + s8, s9, s10, s11, s12, s13, s14, s15) +#define ADDLIST16(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16) \ + additional_kw_query(text, 16, s1, s2, s3, s4, s5, s6, s7, \ + s8, s9, s10, s11, s12, s13, s14, s15, s16) #endif /* TAB_COMPLETE_MACROS_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 95587f7..8842dae 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -126,7 +126,7 @@ static int completion_max_records; * Communication variables set by COMPLETE_WITH_FOO macros and then used by * the completion callback functions. Ugly but there is no better way. */ -static const char *completion_charp; /* to pass a string */ +static PQExpBuffer completion_charp = NULL; /* to pass a string */ static const char *const * completion_charpp; /* to pass a list of strings */ static const char *completion_info_charp; /* to pass a second string */ static const char *completion_info_charp2; /* to pass a third string */ @@ -500,12 +500,11 @@ static const SchemaQuery Query_for_list_of_matviews = { " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'" #define Query_for_list_of_grant_roles \ -" SELECT pg_catalog.quote_ident(rolname) "\ -" FROM pg_catalog.pg_roles "\ -" WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\ -" UNION ALL SELECT 'PUBLIC'"\ -" UNION ALL SELECT 'CURRENT_USER'"\ -" UNION ALL SELECT 'SESSION_USER'" + concatenate_strings( \ + " SELECT pg_catalog.quote_ident(rolname) " \ + " FROM pg_catalog.pg_roles " \ + " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'", \ + ADDLIST3("PUBLIC", "CURRENT_USER", "SESSION_USER")) /* the silly-looking length condition is just to eat up the current word */ #define Query_for_table_owning_index \ @@ -796,6 +795,8 @@ static char **complete_from_variables(const char *text, static char *complete_from_files(const char *text, int state); static char *pg_strdup_keyword_case(const char *s, const char *ref); +static char *concatenate_strings(const char *s1, const char *s2); +static char *additional_kw_query( const char *ref, int n, ...); static char *escape_string(const char *text); static PGresult *exec_query(const char *query); @@ -967,7 +968,8 @@ psql_completion(const char *text, int start, int end) #endif /* Clear a few things. */ - completion_charp = NULL; + if (completion_charp == NULL) + completion_charp = createPQExpBuffer(); completion_charpp = NULL; completion_info_charp = NULL; completion_info_charp2 = NULL; @@ -1076,8 +1078,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER TABLE */ if (Matches2("ALTER", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER something */ if (Matches1("ALTER")) @@ -1193,8 +1195,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER INDEX */ if (Matches2("ALTER", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER INDEX <name> */ if (Matches3("ALTER", "INDEX", MatchAny)) COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET"); @@ -1222,8 +1224,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER MATERIALIZED VIEW */ if (Matches3("ALTER", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_matviews, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER USER,ROLE <name> */ if (Matches3("ALTER", "USER|ROLE", MatchAny) && @@ -1396,7 +1398,7 @@ psql_completion_internal(const char *text, char **previous_words, * If we have ALTER TRIGGER <sth> ON, then add the correct tablename */ if (Matches4("ALTER", "TRIGGER", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TRIGGER <name> ON <name> */ if (Matches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny)) @@ -1442,10 +1444,10 @@ psql_completion_internal(const char *text, char **previous_words, } /* ALTER TABLE xxx INHERIT */ if (Matches4("ALTER", "TABLE", MatchAny, "INHERIT")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TABLE xxx NO INHERIT */ if (Matches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TABLE xxx DISABLE */ if (Matches4("ALTER", "TABLE", MatchAny, "DISABLE")) COMPLETE_WITH_LIST3("ROW LEVEL SECURITY", "RULE", "TRIGGER"); @@ -1462,13 +1464,13 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER TABLE xxx ALTER */ if (Matches4("ALTER", "TABLE", MatchAny, "ALTER")) - COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'"); + COMPLETE_WITH_ATTR_KW(prev2_wd, ADDLIST2("COLUMN", "CONSTRAINT")); /* ALTER TABLE xxx RENAME */ if (Matches4("ALTER", "TABLE", MatchAny, "RENAME")) - COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'"); + COMPLETE_WITH_ATTR_KW(prev2_wd, ADDLIST3("COLUMN", "CONSTRAINT", "TO")); if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN")) - COMPLETE_WITH_ATTR(prev3_wd, ""); + COMPLETE_WITH_ATTR(prev3_wd); /* ALTER TABLE xxx RENAME yyy */ if (Matches5("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO"))) @@ -1483,7 +1485,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("COLUMN", "CONSTRAINT"); /* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */ if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN")) - COMPLETE_WITH_ATTR(prev3_wd, ""); + COMPLETE_WITH_ATTR(prev3_wd); /* * If we have ALTER TABLE <sth> ALTER|DROP|RENAME|VALIDATE CONSTRAINT, @@ -1634,7 +1636,7 @@ psql_completion_internal(const char *text, char **previous_words, * of attributes */ if (Matches5("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE")) - COMPLETE_WITH_ATTR(prev3_wd, ""); + COMPLETE_WITH_ATTR(prev3_wd); /* ALTER TYPE ALTER ATTRIBUTE <foo> */ if (Matches6("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny)) COMPLETE_WITH_CONST("TYPE"); @@ -1671,9 +1673,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED"); /* CLUSTER */ if (Matches1("CLUSTER")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (Matches2("CLUSTER", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* If we have CLUSTER <sth>, then add "USING" */ if (Matches2("CLUSTER", MatchAnyExcept("VERBOSE|ON"))) COMPLETE_WITH_CONST("USING"); @@ -1720,7 +1723,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint); } if (Matches4("COMMENT", "ON", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (Matches4("COMMENT", "ON", "EVENT", "TRIGGER")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); if (Matches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) || @@ -1735,11 +1738,11 @@ psql_completion_internal(const char *text, char **previous_words, * backslash command). */ if (Matches1("COPY|\\copy")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - " UNION ALL SELECT '('"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("(")); /* If we have COPY BINARY, complete with list of tables */ if (Matches2("COPY", "BINARY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* If we have COPY (, complete it with legal commands */ if (Matches2("COPY|\\copy", "(")) COMPLETE_WITH_LIST7("SELECT", "TABLE", "VALUES", "INSERT", "UPDATE", "DELETE", "WITH"); @@ -1751,7 +1754,7 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches3("COPY|\\copy", MatchAny, "FROM|TO") || Matches4("COPY", "BINARY", MatchAny, "FROM|TO")) { - completion_charp = ""; + SET_COMP_CHARP(""); return completion_matches(text, complete_from_files); } @@ -1820,21 +1823,20 @@ psql_completion_internal(const char *text, char **previous_words, * existing indexes */ if (TailMatches2("CREATE|UNIQUE", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'ON'" - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST2("ON", "CONCURRENTLY")); /* Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of tables */ if (TailMatches3("INDEX|CONCURRENTLY", MatchAny, "ON") || TailMatches2("INDEX|CONCURRENTLY", "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing * indexes */ if (TailMatches3("CREATE|UNIQUE", "INDEX", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'ON'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("ON")); /* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */ if (TailMatches3("CREATE|UNIQUE", "INDEX", MatchAny) || TailMatches4("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny)) @@ -1849,10 +1851,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("(", "USING"); if (TailMatches5("INDEX", MatchAny, "ON", MatchAny, "(") || TailMatches4("INDEX|CONCURRENTLY", "ON", MatchAny, "(")) - COMPLETE_WITH_ATTR(prev2_wd, ""); + COMPLETE_WITH_ATTR(prev2_wd); /* same if you put in USING */ if (TailMatches5("ON", MatchAny, "USING", MatchAny, "(")) - COMPLETE_WITH_ATTR(prev4_wd, ""); + COMPLETE_WITH_ATTR(prev4_wd); /* Complete USING with an index method */ if (TailMatches6("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING") || TailMatches5("INDEX", MatchAny, "ON", MatchAny, "USING") || @@ -1869,8 +1871,8 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("ON"); /* Complete "CREATE POLICY <name> ON <table>" */ if (Matches4("CREATE", "POLICY", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - /* Complete "CREATE POLICY <name> ON <table> AS|FOR|TO|USING|WITH CHECK" */ + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); + /* Complete "CREATE POLICY <name> ON <table> FOR|TO|USING|WITH CHECK" */ if (Matches5("CREATE", "POLICY", MatchAny, "ON", MatchAny)) COMPLETE_WITH_LIST5("AS", "FOR", "TO", "USING (", "WITH CHECK ("); /* CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE */ @@ -1932,7 +1934,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("TO"); /* Complete "AS ON <sth> TO" with a table name */ if (TailMatches4("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */ if (TailMatches3("CREATE", "SEQUENCE", MatchAny) || @@ -1988,10 +1990,10 @@ psql_completion_internal(const char *text, char **previous_words, * tables */ if (TailMatches6("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */ if (TailMatches7("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("ON", MatchAny)) COMPLETE_WITH_LIST7("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY", "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE"); @@ -2028,7 +2030,7 @@ psql_completion_internal(const char *text, char **previous_words, if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE")) COMPLETE_WITH_CONST("PROCEDURE"); if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("EXECUTE", "PROCEDURE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); /* CREATE ROLE,USER,GROUP <name> */ if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) && @@ -2111,7 +2113,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("FROM"); /* Complete DELETE FROM with a list of tables */ if (TailMatches2("DELETE", "FROM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete DELETE FROM <table> */ if (TailMatches3("DELETE", "FROM", MatchAny)) COMPLETE_WITH_LIST2("USING", "WHERE"); @@ -2149,10 +2151,10 @@ psql_completion_internal(const char *text, char **previous_words, /* DROP INDEX */ if (Matches2("DROP", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("CONCURRENTLY")); if (Matches3("DROP", "INDEX", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (Matches3("DROP", "INDEX", MatchAny)) COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); if (Matches4("DROP", "INDEX", "CONCURRENTLY", MatchAny)) @@ -2162,7 +2164,7 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches2("DROP", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); if (Matches3("DROP", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); /* DROP OWNED BY */ if (Matches2("DROP", "OWNED")) @@ -2268,7 +2270,7 @@ psql_completion_internal(const char *text, char **previous_words, /* FOREIGN TABLE */ if (TailMatches2("FOREIGN", "TABLE") && !TailMatches3("CREATE", MatchAny, MatchAny)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables); /* FOREIGN SERVER */ if (TailMatches2("FOREIGN", "SERVER")) @@ -2290,20 +2292,10 @@ psql_completion_internal(const char *text, char **previous_words, "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", "EXECUTE", "USAGE", "ALL"); else - COMPLETE_WITH_QUERY(Query_for_list_of_roles - " UNION SELECT 'SELECT'" - " UNION SELECT 'INSERT'" - " UNION SELECT 'UPDATE'" - " UNION SELECT 'DELETE'" - " UNION SELECT 'TRUNCATE'" - " UNION SELECT 'REFERENCES'" - " UNION SELECT 'TRIGGER'" - " UNION SELECT 'CREATE'" - " UNION SELECT 'CONNECT'" - " UNION SELECT 'TEMPORARY'" - " UNION SELECT 'EXECUTE'" - " UNION SELECT 'USAGE'" - " UNION SELECT 'ALL'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST13("SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", + "REFERENCES", "TRIGGER", "CREATE", "CONNECT", + "TEMPORARY", "EXECUTE", "USAGE", "ALL")); } /* * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with @@ -2339,22 +2331,22 @@ psql_completion_internal(const char *text, char **previous_words, if (HeadMatches3("ALTER","DEFAULT","PRIVILEGES")) COMPLETE_WITH_LIST4("TABLES", "SEQUENCES", "FUNCTIONS", "TYPES"); else - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, - " UNION SELECT 'ALL FUNCTIONS IN SCHEMA'" - " UNION SELECT 'ALL SEQUENCES IN SCHEMA'" - " UNION SELECT 'ALL TABLES IN SCHEMA'" - " UNION SELECT 'DATABASE'" - " UNION SELECT 'DOMAIN'" - " UNION SELECT 'FOREIGN DATA WRAPPER'" - " UNION SELECT 'FOREIGN SERVER'" - " UNION SELECT 'FUNCTION'" - " UNION SELECT 'LANGUAGE'" - " UNION SELECT 'LARGE OBJECT'" - " UNION SELECT 'SCHEMA'" - " UNION SELECT 'SEQUENCE'" - " UNION SELECT 'TABLE'" - " UNION SELECT 'TABLESPACE'" - " UNION SELECT 'TYPE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tsvmf, + ADDLIST15("ALL FUNCTIONS IN SCHEMA", + "ALL SEQUENCES IN SCHEMA", + "ALL TABLES IN SCHEMA", + "DATABASE", + "DOMAIN", + "FOREIGN DATA WRAPPER", + "FOREIGN SERVER", + "FUNCTION", + "LANGUAGE", + "LARGE OBJECT", + "SCHEMA", + "SEQUENCE", + "TABLE", + "TABLESPACE", + "TYPE")); } if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL")) COMPLETE_WITH_LIST3("FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA", @@ -2374,21 +2366,21 @@ psql_completion_internal(const char *text, char **previous_words, if (TailMatches1("DATABASE")) COMPLETE_WITH_QUERY(Query_for_list_of_databases); if (TailMatches1("DOMAIN")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains); if (TailMatches1("FUNCTION")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatches1("LANGUAGE")) COMPLETE_WITH_QUERY(Query_for_list_of_languages); if (TailMatches1("SCHEMA")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (TailMatches1("SEQUENCE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences); if (TailMatches1("TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); if (TailMatches1("TABLESPACE")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); if (TailMatches1("TYPE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes); if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny)) COMPLETE_WITH_CONST("TO"); else @@ -2454,10 +2446,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("INTO"); /* Complete INSERT INTO with table names */ if (TailMatches2("INSERT", "INTO")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete "INSERT INTO <table> (" with attribute names */ if (TailMatches4("INSERT", "INTO", MatchAny, "(")) - COMPLETE_WITH_ATTR(prev2_wd, ""); + COMPLETE_WITH_ATTR(prev2_wd); /* * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or @@ -2481,10 +2473,10 @@ psql_completion_internal(const char *text, char **previous_words, /* LOCK */ /* Complete LOCK [TABLE] with a list of tables */ if (Matches1("LOCK")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - " UNION SELECT 'TABLE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("TABLE")); if (Matches2("LOCK", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* For the following, handle the case of a single table only for now */ @@ -2529,7 +2521,7 @@ psql_completion_internal(const char *text, char **previous_words, if (TailMatches3("FROM", MatchAny, "ORDER")) COMPLETE_WITH_CONST("BY"); if (TailMatches4("FROM", MatchAny, "ORDER", "BY")) - COMPLETE_WITH_ATTR(prev3_wd, ""); + COMPLETE_WITH_ATTR(prev3_wd); /* PREPARE xx AS */ if (Matches3("PREPARE", MatchAny, "AS")) @@ -2558,10 +2550,10 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches2("REFRESH", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); if (Matches3("REFRESH", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_matviews, + ADDLIST1("CONCURRENTLY")); if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny)) COMPLETE_WITH_CONST("WITH"); if (Matches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny)) @@ -2579,9 +2571,9 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches1("REINDEX")) COMPLETE_WITH_LIST5("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE"); if (Matches2("REINDEX", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); if (Matches2("REINDEX", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (Matches2("REINDEX", "SCHEMA")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (Matches2("REINDEX", "SYSTEM|DATABASE")) @@ -2651,7 +2643,8 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("ONLY", "WRITE"); /* SET CONSTRAINTS */ if (Matches2("SET", "CONSTRAINTS")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_constraints_with_schema, + ADDLIST1("ALL")); /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */ if (Matches3("SET", "CONSTRAINTS", MatchAny)) COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE"); @@ -2663,7 +2656,8 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION"); /* Complete SET SESSION AUTHORIZATION with username */ if (Matches3("SET", "SESSION", "AUTHORIZATION")) - COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST1("DEFAULT")); /* Complete RESET SESSION with AUTHORIZATION */ if (Matches2("RESET", "SESSION")) COMPLETE_WITH_CONST("AUTHORIZATION"); @@ -2689,10 +2683,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST(my_list); } 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' "); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_schemas + " AND nspname not like 'pg\\_toast%%' " + " AND nspname not like 'pg\\_temp%%' ", + ADDLIST1("DEFAULT")); else { /* generic, type based, GUC support, guctype is malloc'ed */ @@ -2727,7 +2721,7 @@ psql_completion_internal(const char *text, char **previous_words, /* TABLE, but not TABLE embedded in other commands */ if (Matches1("TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations); /* TABLESAMPLE */ if (TailMatches1("TABLESAMPLE")) @@ -2737,7 +2731,7 @@ psql_completion_internal(const char *text, char **previous_words, /* TRUNCATE */ if (Matches1("TRUNCATE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* UNLISTEN */ if (Matches1("UNLISTEN")) @@ -2746,13 +2740,13 @@ psql_completion_internal(const char *text, char **previous_words, /* UPDATE --- can be inside EXPLAIN, RULE, etc */ /* If prev. word is UPDATE suggest a list of tables */ if (TailMatches1("UPDATE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete UPDATE <table> with "SET" */ if (TailMatches2("UPDATE", MatchAny)) COMPLETE_WITH_CONST("SET"); /* Complete UPDATE <table> SET with list of attributes */ if (TailMatches3("UPDATE", MatchAny, "SET")) - COMPLETE_WITH_ATTR(prev2_wd, ""); + COMPLETE_WITH_ATTR(prev2_wd); /* UPDATE <table> SET <attr> = */ if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny)) COMPLETE_WITH_CONST("="); @@ -2761,10 +2755,8 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING")) COMPLETE_WITH_CONST("FOR"); if (Matches4("CREATE", "USER", "MAPPING", "FOR")) - COMPLETE_WITH_QUERY(Query_for_list_of_roles - " UNION SELECT 'CURRENT_USER'" - " UNION SELECT 'PUBLIC'" - " UNION SELECT 'USER'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST3("CURRENT_USER", "PUBLIC", "USER")); if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR")) COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny)) @@ -2777,29 +2769,26 @@ psql_completion_internal(const char *text, char **previous_words, * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] */ if (Matches1("VACUUM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'FULL'" - " UNION SELECT 'FREEZE'" - " UNION SELECT 'ANALYZE'" - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST4("FULL", "FREEZE", + "ANALYZE", "VERBOSE")); if (Matches2("VACUUM", "FULL|FREEZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'" - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST2("ANALYZE", "VERBOSE")); if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("ANALYZE")); if (Matches2("VACUUM", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("ANALYZE")); if (Matches2("VACUUM", "ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (HeadMatches1("VACUUM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* WITH [RECURSIVE] */ @@ -2813,21 +2802,21 @@ psql_completion_internal(const char *text, char **previous_words, /* ANALYZE */ /* Complete with list of tables */ if (Matches1("ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf); /* WHERE */ /* Simple case of the word before the where being the table name */ if (TailMatches2(MatchAny, "WHERE")) - COMPLETE_WITH_ATTR(prev2_wd, ""); + COMPLETE_WITH_ATTR(prev2_wd); /* ... FROM ... */ /* TODO: also include SRF ? */ if (TailMatches1("FROM") && !Matches3("COPY|\\copy", MatchAny, "FROM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); /* ... JOIN ... */ if (TailMatches1("JOIN")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); /* Backslash commands */ /* TODO: \dc \dd \dl */ @@ -2844,13 +2833,13 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_roles); } if (TailMatchesCS1("\\da*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates); if (TailMatchesCS1("\\dA*")) COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); if (TailMatchesCS1("\\db*")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); if (TailMatchesCS1("\\dD*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains); if (TailMatchesCS1("\\des*")) COMPLETE_WITH_QUERY(Query_for_list_of_servers); if (TailMatchesCS1("\\deu*")) @@ -2858,7 +2847,7 @@ psql_completion_internal(const char *text, char **previous_words, if (TailMatchesCS1("\\dew*")) COMPLETE_WITH_QUERY(Query_for_list_of_fdws); if (TailMatchesCS1("\\df*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\dFd*")) COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries); @@ -2871,40 +2860,40 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations); if (TailMatchesCS1("\\di*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (TailMatchesCS1("\\dL*")) COMPLETE_WITH_QUERY(Query_for_list_of_languages); if (TailMatchesCS1("\\dn*")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (TailMatchesCS1("\\dp") || TailMatchesCS1("\\z")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); if (TailMatchesCS1("\\ds*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences); if (TailMatchesCS1("\\dt*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); if (TailMatchesCS1("\\dT*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes); if (TailMatchesCS1("\\du*") || TailMatchesCS1("\\dg*")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); if (TailMatchesCS1("\\dv*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\dx*")) COMPLETE_WITH_QUERY(Query_for_list_of_extensions); if (TailMatchesCS1("\\dm*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (TailMatchesCS1("\\dE*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables); if (TailMatchesCS1("\\dy*")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); /* must be at end of \d alternatives: */ if (TailMatchesCS1("\\d*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations); if (TailMatchesCS1("\\ef")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\ev")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\encoding")) COMPLETE_WITH_QUERY(Query_for_list_of_encodings); @@ -2973,14 +2962,14 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST_CS3("default", "verbose", "terse"); } if (TailMatchesCS1("\\sf*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\sv*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|" "\\ir|\\include_relative|\\o|\\out|" "\\s|\\w|\\write|\\lo_import")) { - completion_charp = "\\"; + SET_COMP_CHARP("\\"); return completion_matches(text, complete_from_files); } @@ -3000,8 +2989,7 @@ psql_completion_internal(const char *text, char **previous_words, if (words_after_create[i].query) COMPLETE_WITH_QUERY(words_after_create[i].query); if (words_after_create[i].squery) - COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery, - NULL); + COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery); break; } } @@ -3248,13 +3236,13 @@ _complete_from_query(int is_schema_query, const char *text, int state) char_length, e_text); /* If an addon query was provided, use it */ - if (completion_charp) - appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); + if (COMPLETION_CHARP[0]) + appendPQExpBuffer(&query_buffer, "\n%s", COMPLETION_CHARP); } else { /* completion_charp is an sprintf-style format string */ - appendPQExpBuffer(&query_buffer, completion_charp, + appendPQExpBuffer(&query_buffer, COMPLETION_CHARP, char_length, e_text, e_info_charp, e_info_charp, e_info_charp2, e_info_charp2); @@ -3369,18 +3357,17 @@ complete_from_list(const char *text, int state) static char * complete_from_const(const char *text, int state) { - Assert(completion_charp != NULL); if (state == 0) { if (completion_case_sensitive) - return pg_strdup(completion_charp); + return pg_strdup(COMPLETION_CHARP); else /* * If case insensitive matching was requested initially, adjust * the case according to setting. */ - return pg_strdup_keyword_case(completion_charp, text); + return pg_strdup_keyword_case(COMPLETION_CHARP, text); } else return NULL; @@ -3481,7 +3468,7 @@ complete_from_files(const char *text, int state) if (state == 0) { /* Initialization: stash the unquoted input. */ - unquoted_text = strtokx(text, "", NULL, "'", *completion_charp, + unquoted_text = strtokx(text, "", NULL, "'", COMPLETION_CHARP[0], false, true, pset.encoding); /* expect a NULL return for the empty string only */ if (!unquoted_text) @@ -3502,7 +3489,7 @@ complete_from_files(const char *text, int state) * bother providing a macro to simplify this. */ ret = quote_if_needed(unquoted_match, " \t\r\n\"`", - '\'', *completion_charp, pset.encoding); + '\'', COMPLETION_CHARP[0], pset.encoding); if (ret) free(unquoted_match); else @@ -3546,6 +3533,59 @@ pg_strdup_keyword_case(const char *s, const char *ref) return ret; } +/* + * Returns concatenated string. + * Note: the internal buffer is static so this cannot be called recursively. + */ +static char * +concatenate_strings(const char *s1, const char *s2) +{ + static PQExpBuffer qbuf = NULL; + + if (qbuf == NULL) + qbuf = createPQExpBuffer(); + else + resetPQExpBuffer(qbuf); + + appendPQExpBufferStr(qbuf, s1); + appendPQExpBufferStr(qbuf, s2); + + return qbuf->data; +} + +/* Construct codelet to append given keywords */ +static char * +additional_kw_query(const char *ref, int n, ...) +{ + va_list ap; + static PQExpBuffer qbuf = NULL; + int i; + + if (qbuf == NULL) + qbuf = createPQExpBuffer(); + else + resetPQExpBuffer(qbuf); + + /* Construct an additional queriy to append keywords */ + appendPQExpBufferStr(qbuf, " UNION ALL SELECT * FROM (VALUES "); + + va_start(ap, n); + for (i = 0 ; i < n ; i++) + { + char *item = pg_strdup_keyword_case(va_arg(ap, char *), ref); + if (i > 0) appendPQExpBufferChar(qbuf, ','); + appendPQExpBufferStr(qbuf, "('"); + appendPQExpBufferStr(qbuf, item); + appendPQExpBufferStr(qbuf, "')"); + pg_free(item); + } + va_end(ap); + + appendPQExpBufferStr(qbuf, ") as x"); + + return qbuf->data; +} + /* * escape_string - Escape argument for use as string literal. -- 2.9.2 ----Next_Part(Mon_Dec_26_17_40_58_2016_393)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="0003-Introduce-word-shift-and-removal-feature-to-psql-com.patch" ^ permalink raw reply [nested|flat] 7+ messages in thread
* [PATCH 2/6] Make keywords' case follow to input @ 2016-09-14 03:48 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 7+ messages in thread From: Kyotaro Horiguchi @ 2016-09-14 03:48 UTC (permalink / raw) Currently some keywords suggested along with database objects are always in upper case. This patch changes the behavior so that the case of the additional keywords follow the setting of COMP_KEYWORD_CASE. COMPLETE_WITH_ATTR needs completion_charp to be appendable, so this patch changes it to a PQExpBuffer and adjust COMPLET_WITH_* macros to that change. This leaves keywords contained in Query_for_list_of_grant_roles and Query_for_enum, but it is another problem. --- src/bin/psql/tab-complete-macros.h | 66 +++++-- src/bin/psql/tab-complete.c | 340 ++++++++++++++++++++----------------- 2 files changed, 234 insertions(+), 172 deletions(-) diff --git a/src/bin/psql/tab-complete-macros.h b/src/bin/psql/tab-complete-macros.h index 97ffcd1..bd78f5d 100644 --- a/src/bin/psql/tab-complete-macros.h +++ b/src/bin/psql/tab-complete-macros.h @@ -231,16 +231,46 @@ * 5) The list of attributes of the given table (possibly schema-qualified). * 6) The list of arguments to the given function (possibly schema-qualified). */ -#define COMPLETE_WITH_QUERY(query) \ +#define APPEND_COMP_CHARP(charp) \ + appendPQExpBufferStr(completion_charp, charp); + +#define SET_COMP_CHARP(charp) \ + resetPQExpBuffer(completion_charp); \ + APPEND_COMP_CHARP(charp); + +#define COMPLETION_CHARP (completion_charp->data) + +#define COMPLETE_WITH_QUERY(query) \ +do { \ + SET_COMP_CHARP(query); \ + return completion_matches(text, complete_from_query); \ +} while (0) + +/* + * COMPLETE_WITH_QUERY with additional keywords. Keywords are complete + * case-sensitively + */ +#define COMPLETE_WITH_QUERY_KW(query, addon) \ do { \ - completion_charp = query; \ + SET_COMP_CHARP(query); \ + APPEND_COMP_CHARP(addon); \ return completion_matches(text, complete_from_query); \ } while (0) -#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \ +#define COMPLETE_WITH_SCHEMA_QUERY(query) \ do { \ completion_squery = &(query); \ - completion_charp = addon; \ + return completion_matches(text, complete_from_schema_query); \ +} while (0) + +/* + * COMPLETE_WITH_SCHEMA_QUERY with additional keywords. Keywords are complete + * case-sensitively + */ +#define COMPLETE_WITH_SCHEMA_QUERY_KW(query, addon) \ +do { \ + completion_squery = &(query); \ + SET_COMP_CHARP(addon); \ return completion_matches(text, complete_from_schema_query); \ } while (0) @@ -260,7 +290,7 @@ do { \ #define COMPLETE_WITH_CONST(string) \ do { \ - completion_charp = string; \ + SET_COMP_CHARP(string); \ completion_case_sensitive = false; \ return completion_matches(text, complete_from_const); \ } while (0) @@ -278,12 +308,14 @@ do { \ false, false, pset.encoding); \ if (_completion_table == NULL) \ { \ - completion_charp = Query_for_list_of_attributes addon; \ + SET_COMP_CHARP(Query_for_list_of_attributes); \ + APPEND_COMP_CHARP(addon); \ completion_info_charp = relation; \ } \ else \ { \ - completion_charp = Query_for_list_of_attributes_with_schema addon; \ + SET_COMP_CHARP(Query_for_list_of_attributes_with_schema); \ + APPEND_COMP_CHARP(addon); \ completion_info_charp = _completion_table; \ completion_info_charp2 = _completion_schema; \ } \ @@ -303,12 +335,12 @@ do { \ false, false, pset.encoding); \ if (_completion_type == NULL)\ { \ - completion_charp = Query_for_list_of_enum_values; \ + SET_COMP_CHARP(Query_for_list_of_enum_values); \ completion_info_charp = type; \ } \ else \ { \ - completion_charp = Query_for_list_of_enum_values_with_schema; \ + SET_COMP_CHARP(Query_for_list_of_enum_values_with_schema); \ completion_info_charp = _completion_type; \ completion_info_charp2 = _completion_schema; \ } \ @@ -328,12 +360,12 @@ do { \ false, false, pset.encoding); \ if (_completion_function == NULL) \ { \ - completion_charp = Query_for_list_of_arguments; \ + SET_COMP_CHARP(Query_for_list_of_arguments); \ completion_info_charp = function; \ } \ else \ { \ - completion_charp = Query_for_list_of_arguments_with_schema; \ + SET_COMP_CHARP(Query_for_list_of_arguments_with_schema); \ completion_info_charp = _completion_function; \ completion_info_charp2 = _completion_schema; \ } \ @@ -425,6 +457,16 @@ do { \ COMPLETE_WITH_LIST_CS(list); \ } while (0) - +#define ADDLIST1(s1) additional_kw_query(text, 1, s1) +#define ADDLIST2(s1, s2) additional_kw_query(text, 2, s1, s2) +#define ADDLIST3(s1, s2, s3) additional_kw_query(text, 3, s1, s2, s3) +#define ADDLIST4(s1, s2, s3, s4) \ + additional_kw_query(text, 4, s1, s2, s3, s4) +#define ADDLIST13(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13) \ + additional_kw_query(text, 12, s1, s2, s3, s4, s5, s6, s7, \ + s8, s9, s10, s11, s12, s13) +#define ADDLIST15(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15) \ + additional_kw_query(text, 12, s1, s2, s3, s4, s5, s6, s7, \ + s8, s9, s10, s11, s12, s13, s14, s15) #endif /* TAB_COMPLETE_MACROS_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index fd3f68e..9860292 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -126,7 +126,7 @@ static int completion_max_records; * Communication variables set by COMPLETE_WITH_FOO macros and then used by * the completion callback functions. Ugly but there is no better way. */ -static const char *completion_charp; /* to pass a string */ +static PQExpBuffer completion_charp = NULL; /* to pass a string */ static const char *const * completion_charpp; /* to pass a list of strings */ static const char *completion_info_charp; /* to pass a second string */ static const char *completion_info_charp2; /* to pass a third string */ @@ -796,6 +796,7 @@ static char **complete_from_variables(const char *text, static char *complete_from_files(const char *text, int state); static char *pg_strdup_keyword_case(const char *s, const char *ref); +static char *additional_kw_query( const char *ref, int n, ...); static char *escape_string(const char *text); static PGresult *exec_query(const char *query); @@ -967,7 +968,8 @@ psql_completion(const char *text, int start, int end) #endif /* Clear a few things. */ - completion_charp = NULL; + if (completion_charp == NULL) + completion_charp = createPQExpBuffer(); completion_charpp = NULL; completion_info_charp = NULL; completion_info_charp2 = NULL; @@ -1076,8 +1078,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER TABLE */ if (Matches2("ALTER", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER something */ if (Matches1("ALTER")) @@ -1193,8 +1195,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER INDEX */ if (Matches2("ALTER", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER INDEX <name> */ if (Matches3("ALTER", "INDEX", MatchAny)) COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET"); @@ -1222,8 +1224,8 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER MATERIALIZED VIEW */ if (Matches3("ALTER", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, - "UNION SELECT 'ALL IN TABLESPACE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_matviews, + ADDLIST1("ALL IN TABLESPACE")); /* ALTER USER,ROLE <name> */ if (Matches3("ALTER", "USER|ROLE", MatchAny) && @@ -1378,7 +1380,7 @@ psql_completion_internal(const char *text, char **previous_words, * If we have ALTER TRIGGER <sth> ON, then add the correct tablename */ if (Matches4("ALTER", "TRIGGER", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TRIGGER <name> ON <name> */ if (Matches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny)) @@ -1424,10 +1426,10 @@ psql_completion_internal(const char *text, char **previous_words, } /* ALTER TABLE xxx INHERIT */ if (Matches4("ALTER", "TABLE", MatchAny, "INHERIT")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TABLE xxx NO INHERIT */ if (Matches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* ALTER TABLE xxx DISABLE */ if (Matches4("ALTER", "TABLE", MatchAny, "DISABLE")) COMPLETE_WITH_LIST3("ROW LEVEL SECURITY", "RULE", "TRIGGER"); @@ -1444,11 +1446,11 @@ psql_completion_internal(const char *text, char **previous_words, /* ALTER TABLE xxx ALTER */ if (Matches4("ALTER", "TABLE", MatchAny, "ALTER")) - COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'"); + COMPLETE_WITH_ATTR(prev2_wd, ADDLIST2("COLUMN", "CONSTRAINT")); /* ALTER TABLE xxx RENAME */ if (Matches4("ALTER", "TABLE", MatchAny, "RENAME")) - COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'"); + COMPLETE_WITH_ATTR(prev2_wd, ADDLIST3("COLUMN", "CONSTRAINT", "TO")); if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN")) COMPLETE_WITH_ATTR(prev3_wd, ""); @@ -1653,9 +1655,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED"); /* CLUSTER */ if (Matches1("CLUSTER")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (Matches2("CLUSTER", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* If we have CLUSTER <sth>, then add "USING" */ if (Matches2("CLUSTER", MatchAnyExcept("VERBOSE|ON"))) COMPLETE_WITH_CONST("USING"); @@ -1702,7 +1705,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint); } if (Matches4("COMMENT", "ON", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (Matches4("COMMENT", "ON", "EVENT", "TRIGGER")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); if (Matches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) || @@ -1717,11 +1720,11 @@ psql_completion_internal(const char *text, char **previous_words, * backslash command). */ if (Matches1("COPY|\\copy")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - " UNION ALL SELECT '('"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("(")); /* If we have COPY BINARY, complete with list of tables */ if (Matches2("COPY", "BINARY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* If we have COPY (, complete it with legal commands */ if (Matches2("COPY|\\copy", "(")) COMPLETE_WITH_LIST7("SELECT", "TABLE", "VALUES", "INSERT", "UPDATE", "DELETE", "WITH"); @@ -1733,7 +1736,7 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches3("COPY|\\copy", MatchAny, "FROM|TO") || Matches4("COPY", "BINARY", MatchAny, "FROM|TO")) { - completion_charp = ""; + SET_COMP_CHARP(""); return completion_matches(text, complete_from_files); } @@ -1802,21 +1805,20 @@ psql_completion_internal(const char *text, char **previous_words, * existing indexes */ if (TailMatches2("CREATE|UNIQUE", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'ON'" - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST2("ON", "CONCURRENTLY")); /* Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of tables */ if (TailMatches3("INDEX|CONCURRENTLY", MatchAny, "ON") || TailMatches2("INDEX|CONCURRENTLY", "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing * indexes */ if (TailMatches3("CREATE|UNIQUE", "INDEX", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'ON'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("ON")); /* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */ if (TailMatches3("CREATE|UNIQUE", "INDEX", MatchAny) || TailMatches4("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny)) @@ -1851,7 +1853,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("ON"); /* Complete "CREATE POLICY <name> ON <table>" */ if (Matches4("CREATE", "POLICY", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* Complete "CREATE POLICY <name> ON <table> FOR|TO|USING|WITH CHECK" */ if (Matches5("CREATE", "POLICY", MatchAny, "ON", MatchAny)) COMPLETE_WITH_LIST4("FOR", "TO", "USING (", "WITH CHECK ("); @@ -1889,7 +1891,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("TO"); /* Complete "AS ON <sth> TO" with a table name */ if (TailMatches4("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */ if (TailMatches3("CREATE", "SEQUENCE", MatchAny) || @@ -1945,47 +1947,47 @@ psql_completion_internal(const char *text, char **previous_words, * tables */ if (TailMatches6("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */ if (TailMatches7("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("ON", MatchAny)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("ON", MatchAny)) COMPLETE_WITH_LIST7("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY", "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && + if (HeadMatches2("CREATE", "TRIGGER") && (TailMatches1("DEFERRABLE") || TailMatches2("INITIALLY", "IMMEDIATE|DEFERRED"))) COMPLETE_WITH_LIST4("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("REFERENCING")) + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("REFERENCING")) COMPLETE_WITH_LIST2("OLD TABLE", "NEW TABLE"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("OLD|NEW", "TABLE")) + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("OLD|NEW", "TABLE")) COMPLETE_WITH_CONST("AS"); - else if (HeadMatches2("CREATE", "TRIGGER") && - (TailMatches5("REFERENCING", "OLD", "TABLE", "AS", MatchAny) || - TailMatches4("REFERENCING", "OLD", "TABLE", MatchAny))) + if (HeadMatches2("CREATE", "TRIGGER") && + (TailMatches5("REFERENCING", "OLD", "TABLE", "AS", MatchAny) || + TailMatches4("REFERENCING", "OLD", "TABLE", MatchAny))) COMPLETE_WITH_LIST4("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && - (TailMatches5("REFERENCING", "NEW", "TABLE", "AS", MatchAny) || - TailMatches4("REFERENCING", "NEW", "TABLE", MatchAny))) + if (HeadMatches2("CREATE", "TRIGGER") && + (TailMatches5("REFERENCING", "NEW", "TABLE", "AS", MatchAny) || + TailMatches4("REFERENCING", "NEW", "TABLE", MatchAny))) COMPLETE_WITH_LIST4("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && - (TailMatches9("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || - TailMatches8("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || - TailMatches8("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) || - TailMatches7("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))) + if (HeadMatches2("CREATE", "TRIGGER") && + (TailMatches9("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || + TailMatches8("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || + TailMatches8("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) || + TailMatches7("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))) COMPLETE_WITH_LIST3("FOR", "WHEN (", "EXECUTE PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("FOR")) + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("FOR")) COMPLETE_WITH_LIST3("EACH", "ROW", "STATEMENT"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("FOR", "EACH")) + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("FOR", "EACH")) COMPLETE_WITH_LIST2("ROW", "STATEMENT"); - else if (HeadMatches2("CREATE", "TRIGGER") && + if (HeadMatches2("CREATE", "TRIGGER") && (TailMatches3("FOR", "EACH", "ROW|STATEMENT") || TailMatches2("FOR", "ROW|STATEMENT"))) COMPLETE_WITH_LIST2("WHEN (", "EXECUTE PROCEDURE"); /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */ if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE")) COMPLETE_WITH_CONST("PROCEDURE"); - else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("EXECUTE", "PROCEDURE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("EXECUTE", "PROCEDURE")) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); /* CREATE ROLE,USER,GROUP <name> */ if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) && @@ -2068,7 +2070,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("FROM"); /* Complete DELETE FROM with a list of tables */ if (TailMatches2("DELETE", "FROM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete DELETE FROM <table> */ if (TailMatches3("DELETE", "FROM", MatchAny)) COMPLETE_WITH_LIST2("USING", "WHERE"); @@ -2106,10 +2108,10 @@ psql_completion_internal(const char *text, char **previous_words, /* DROP INDEX */ if (Matches2("DROP", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes, + ADDLIST1("CONCURRENTLY")); if (Matches3("DROP", "INDEX", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (Matches3("DROP", "INDEX", MatchAny)) COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); if (Matches4("DROP", "INDEX", "CONCURRENTLY", MatchAny)) @@ -2119,7 +2121,7 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches2("DROP", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); if (Matches3("DROP", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); /* DROP OWNED BY */ if (Matches2("DROP", "OWNED")) @@ -2225,7 +2227,7 @@ psql_completion_internal(const char *text, char **previous_words, /* FOREIGN TABLE */ if (TailMatches2("FOREIGN", "TABLE") && !TailMatches3("CREATE", MatchAny, MatchAny)) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables); /* FOREIGN SERVER */ if (TailMatches2("FOREIGN", "SERVER")) @@ -2234,20 +2236,10 @@ psql_completion_internal(const char *text, char **previous_words, /* GRANT && REVOKE --- is allowed inside CREATE SCHEMA, so use TailMatches */ /* Complete GRANT/REVOKE with a list of roles and privileges */ if (TailMatches1("GRANT|REVOKE")) - COMPLETE_WITH_QUERY(Query_for_list_of_roles - " UNION SELECT 'SELECT'" - " UNION SELECT 'INSERT'" - " UNION SELECT 'UPDATE'" - " UNION SELECT 'DELETE'" - " UNION SELECT 'TRUNCATE'" - " UNION SELECT 'REFERENCES'" - " UNION SELECT 'TRIGGER'" - " UNION SELECT 'CREATE'" - " UNION SELECT 'CONNECT'" - " UNION SELECT 'TEMPORARY'" - " UNION SELECT 'EXECUTE'" - " UNION SELECT 'USAGE'" - " UNION SELECT 'ALL'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST13("SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", + "REFERENCES", "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", + "EXECUTE", "USAGE", "ALL")); /* * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with @@ -2275,22 +2267,22 @@ psql_completion_internal(const char *text, char **previous_words, * privilege. */ 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'" - " UNION SELECT 'ALL TABLES IN SCHEMA'" - " UNION SELECT 'DATABASE'" - " UNION SELECT 'DOMAIN'" - " UNION SELECT 'FOREIGN DATA WRAPPER'" - " UNION SELECT 'FOREIGN SERVER'" - " UNION SELECT 'FUNCTION'" - " UNION SELECT 'LANGUAGE'" - " UNION SELECT 'LARGE OBJECT'" - " UNION SELECT 'SCHEMA'" - " UNION SELECT 'SEQUENCE'" - " UNION SELECT 'TABLE'" - " UNION SELECT 'TABLESPACE'" - " UNION SELECT 'TYPE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tsvmf, + ADDLIST15("ALL FUNCTIONS IN SCHEMA", + "ALL SEQUENCES IN SCHEMA", + "ALL TABLES IN SCHEMA", + "DATABASE", + "DOMAIN", + "FOREIGN DATA WRAPPER", + "FOREIGN SERVER", + "FUNCTION", + "LANGUAGE", + "LARGE OBJECT", + "SCHEMA", + "SEQUENCE", + "TABLE", + "TABLESPACE", + "TYPE")); if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL")) COMPLETE_WITH_LIST3("FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA", @@ -2310,21 +2302,21 @@ psql_completion_internal(const char *text, char **previous_words, if (TailMatches1("DATABASE")) COMPLETE_WITH_QUERY(Query_for_list_of_databases); if (TailMatches1("DOMAIN")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains); if (TailMatches1("FUNCTION")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatches1("LANGUAGE")) COMPLETE_WITH_QUERY(Query_for_list_of_languages); if (TailMatches1("SCHEMA")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (TailMatches1("SEQUENCE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences); if (TailMatches1("TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); if (TailMatches1("TABLESPACE")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); if (TailMatches1("TYPE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes); if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny)) COMPLETE_WITH_CONST("TO"); else @@ -2388,7 +2380,7 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_CONST("INTO"); /* Complete INSERT INTO with table names */ if (TailMatches2("INSERT", "INTO")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete "INSERT INTO <table> (" with attribute names */ if (TailMatches4("INSERT", "INTO", MatchAny, "(")) COMPLETE_WITH_ATTR(prev2_wd, ""); @@ -2415,10 +2407,10 @@ psql_completion_internal(const char *text, char **previous_words, /* LOCK */ /* Complete LOCK [TABLE] with a list of tables */ if (Matches1("LOCK")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, - " UNION SELECT 'TABLE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables, + ADDLIST1("TABLE")); if (Matches2("LOCK", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* For the following, handle the case of a single table only for now */ @@ -2492,10 +2484,10 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches2("REFRESH", "MATERIALIZED")) COMPLETE_WITH_CONST("VIEW"); if (Matches3("REFRESH", "MATERIALIZED", "VIEW")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, - " UNION SELECT 'CONCURRENTLY'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_matviews, + ADDLIST1("CONCURRENTLY")); if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny)) COMPLETE_WITH_CONST("WITH"); if (Matches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny)) @@ -2513,9 +2505,9 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches1("REINDEX")) COMPLETE_WITH_LIST5("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE"); if (Matches2("REINDEX", "TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); if (Matches2("REINDEX", "INDEX")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (Matches2("REINDEX", "SCHEMA")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (Matches2("REINDEX", "SYSTEM|DATABASE")) @@ -2585,7 +2577,8 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("ONLY", "WRITE"); /* SET CONSTRAINTS */ if (Matches2("SET", "CONSTRAINTS")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_constraints_with_schema, + ADDLIST1("ALL")); /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */ if (Matches3("SET", "CONSTRAINTS", MatchAny)) COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE"); @@ -2597,7 +2590,8 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION"); /* Complete SET SESSION AUTHORIZATION with username */ if (Matches3("SET", "SESSION", "AUTHORIZATION")) - COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST1("DEFAULT")); /* Complete RESET SESSION with AUTHORIZATION */ if (Matches2("RESET", "SESSION")) COMPLETE_WITH_CONST("AUTHORIZATION"); @@ -2623,10 +2617,10 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST(my_list); } 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' "); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_schemas + " AND nspname not like 'pg\\_toast%%' " + " AND nspname not like 'pg\\_temp%%' ", + ADDLIST1("DEFAULT")); else { /* generic, type based, GUC support, guctype is malloc'ed */ @@ -2661,7 +2655,7 @@ psql_completion_internal(const char *text, char **previous_words, /* TABLE, but not TABLE embedded in other commands */ if (Matches1("TABLE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations); /* TABLESAMPLE */ if (TailMatches1("TABLESAMPLE")) @@ -2671,7 +2665,7 @@ psql_completion_internal(const char *text, char **previous_words, /* TRUNCATE */ if (Matches1("TRUNCATE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); /* UNLISTEN */ if (Matches1("UNLISTEN")) @@ -2680,7 +2674,7 @@ psql_completion_internal(const char *text, char **previous_words, /* UPDATE --- can be inside EXPLAIN, RULE, etc */ /* If prev. word is UPDATE suggest a list of tables */ if (TailMatches1("UPDATE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete UPDATE <table> with "SET" */ if (TailMatches2("UPDATE", MatchAny)) COMPLETE_WITH_CONST("SET"); @@ -2695,10 +2689,8 @@ psql_completion_internal(const char *text, char **previous_words, if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING")) COMPLETE_WITH_CONST("FOR"); if (Matches4("CREATE", "USER", "MAPPING", "FOR")) - COMPLETE_WITH_QUERY(Query_for_list_of_roles - " UNION SELECT 'CURRENT_USER'" - " UNION SELECT 'PUBLIC'" - " UNION SELECT 'USER'"); + COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles, + ADDLIST3("CURRENT_USER", "PUBLIC", "USER")); if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR")) COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny)) @@ -2711,29 +2703,26 @@ psql_completion_internal(const char *text, char **previous_words, * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] */ if (Matches1("VACUUM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'FULL'" - " UNION SELECT 'FREEZE'" - " UNION SELECT 'ANALYZE'" - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST4("FULL", "FREEZE", + "ANALYZE", "VERBOSE")); if (Matches2("VACUUM", "FULL|FREEZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'" - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST2("ANALYZE", "VERBOSE")); if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("ANALYZE")); if (Matches2("VACUUM", "VERBOSE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'ANALYZE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("ANALYZE")); if (Matches2("VACUUM", "ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, - " UNION SELECT 'VERBOSE'"); + COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm, + ADDLIST1("VERBOSE")); if (HeadMatches1("VACUUM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm); /* WITH [RECURSIVE] */ @@ -2747,7 +2736,7 @@ psql_completion_internal(const char *text, char **previous_words, /* ANALYZE */ /* Complete with list of tables */ if (Matches1("ANALYZE")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf); /* WHERE */ /* Simple case of the word before the where being the table name */ @@ -2757,11 +2746,11 @@ psql_completion_internal(const char *text, char **previous_words, /* ... FROM ... */ /* TODO: also include SRF ? */ if (TailMatches1("FROM") && !Matches3("COPY|\\copy", MatchAny, "FROM")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); /* ... JOIN ... */ if (TailMatches1("JOIN")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); /* Backslash commands */ /* TODO: \dc \dd \dl */ @@ -2778,13 +2767,13 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_roles); } if (TailMatchesCS1("\\da*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates); if (TailMatchesCS1("\\dA*")) COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); if (TailMatchesCS1("\\db*")) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); if (TailMatchesCS1("\\dD*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains); if (TailMatchesCS1("\\des*")) COMPLETE_WITH_QUERY(Query_for_list_of_servers); if (TailMatchesCS1("\\deu*")) @@ -2792,7 +2781,7 @@ psql_completion_internal(const char *text, char **previous_words, if (TailMatchesCS1("\\dew*")) COMPLETE_WITH_QUERY(Query_for_list_of_fdws); if (TailMatchesCS1("\\df*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\dFd*")) COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries); @@ -2805,40 +2794,40 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations); if (TailMatchesCS1("\\di*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes); if (TailMatchesCS1("\\dL*")) COMPLETE_WITH_QUERY(Query_for_list_of_languages); if (TailMatchesCS1("\\dn*")) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); if (TailMatchesCS1("\\dp") || TailMatchesCS1("\\z")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf); if (TailMatchesCS1("\\ds*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences); if (TailMatchesCS1("\\dt*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables); if (TailMatchesCS1("\\dT*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes); if (TailMatchesCS1("\\du*") || TailMatchesCS1("\\dg*")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); if (TailMatchesCS1("\\dv*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\dx*")) COMPLETE_WITH_QUERY(Query_for_list_of_extensions); if (TailMatchesCS1("\\dm*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews); if (TailMatchesCS1("\\dE*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables); if (TailMatchesCS1("\\dy*")) COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); /* must be at end of \d alternatives: */ if (TailMatchesCS1("\\d*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations); if (TailMatchesCS1("\\ef")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\ev")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\encoding")) COMPLETE_WITH_QUERY(Query_for_list_of_encodings); @@ -2907,14 +2896,14 @@ psql_completion_internal(const char *text, char **previous_words, COMPLETE_WITH_LIST_CS3("default", "verbose", "terse"); } if (TailMatchesCS1("\\sf*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions); if (TailMatchesCS1("\\sv*")) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views); if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|" "\\ir|\\include_relative|\\o|\\out|" "\\s|\\w|\\write|\\lo_import")) { - completion_charp = "\\"; + SET_COMP_CHARP("\\"); return completion_matches(text, complete_from_files); } @@ -2934,8 +2923,7 @@ psql_completion_internal(const char *text, char **previous_words, if (words_after_create[i].query) COMPLETE_WITH_QUERY(words_after_create[i].query); if (words_after_create[i].squery) - COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery, - NULL); + COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery); break; } } @@ -3182,13 +3170,13 @@ _complete_from_query(int is_schema_query, const char *text, int state) char_length, e_text); /* If an addon query was provided, use it */ - if (completion_charp) - appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); + if (COMPLETION_CHARP[0]) + appendPQExpBuffer(&query_buffer, "\n%s", COMPLETION_CHARP); } else { /* completion_charp is an sprintf-style format string */ - appendPQExpBuffer(&query_buffer, completion_charp, + appendPQExpBuffer(&query_buffer, COMPLETION_CHARP, char_length, e_text, e_info_charp, e_info_charp, e_info_charp2, e_info_charp2); @@ -3303,18 +3291,17 @@ complete_from_list(const char *text, int state) static char * complete_from_const(const char *text, int state) { - Assert(completion_charp != NULL); if (state == 0) { if (completion_case_sensitive) - return pg_strdup(completion_charp); + return pg_strdup(COMPLETION_CHARP); else /* * If case insensitive matching was requested initially, adjust * the case according to setting. */ - return pg_strdup_keyword_case(completion_charp, text); + return pg_strdup_keyword_case(COMPLETION_CHARP, text); } else return NULL; @@ -3415,7 +3402,7 @@ complete_from_files(const char *text, int state) if (state == 0) { /* Initialization: stash the unquoted input. */ - unquoted_text = strtokx(text, "", NULL, "'", *completion_charp, + unquoted_text = strtokx(text, "", NULL, "'", COMPLETION_CHARP[0], false, true, pset.encoding); /* expect a NULL return for the empty string only */ if (!unquoted_text) @@ -3436,7 +3423,7 @@ complete_from_files(const char *text, int state) * bother providing a macro to simplify this. */ ret = quote_if_needed(unquoted_match, " \t\r\n\"`", - '\'', *completion_charp, pset.encoding); + '\'', COMPLETION_CHARP[0], pset.encoding); if (ret) free(unquoted_match); else @@ -3480,6 +3467,39 @@ pg_strdup_keyword_case(const char *s, const char *ref) return ret; } +/* Construct codelet to append given keywords */ +static char * +additional_kw_query(const char *ref, int n, ...) +{ + va_list ap; + static PQExpBuffer qbuf = NULL; + int i; + + if (qbuf == NULL) + qbuf = createPQExpBuffer(); + else + resetPQExpBuffer(qbuf); + + /* Construct an additional queriy to append keywords */ + appendPQExpBufferStr(qbuf, " UNION ALL SELECT * FROM (VALUES "); + + va_start(ap, n); + for (i = 0 ; i < n ; i++) + { + char *item = pg_strdup_keyword_case(va_arg(ap, char *), ref); + if (i > 0) appendPQExpBufferChar(qbuf, ','); + appendPQExpBufferStr(qbuf, "('"); + appendPQExpBufferStr(qbuf, item); + appendPQExpBufferStr(qbuf, "')"); + pg_free(item); + } + va_end(ap); + + appendPQExpBufferStr(qbuf, ") as x"); + + return qbuf->data; +} + /* * escape_string - Escape argument for use as string literal. -- 2.9.2 ----Next_Part(Tue_Nov_15_20_26_42_2016_708)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="0003-Fix-suggested-keywords-to-follow-input-in-tab-comple.patch" ^ permalink raw reply [nested|flat] 7+ messages in thread
* [PATCH v49 2/3] Filling gaps in jsonb @ 2020-12-31 14:19 Dmitrii Dolgov <[email protected]> 0 siblings, 0 replies; 7+ messages in thread From: Dmitrii Dolgov @ 2020-12-31 14:19 UTC (permalink / raw) Introduces two new modes for jsonb assignment: * Appending array elements on the specified position, gaps filled with nulls (similar to JavaScript behavior). This mode also instructs to create the whole path in a jsonb object if some part of the path (more than just the last element) is not present. * Assigning keeps array positions consistent by prevent prepending of elements. Originally proposed by Nikita Glukhov based on polymorphic subscripting patch, but transformed into an independent change. --- doc/src/sgml/json.sgml | 24 +++ src/backend/utils/adt/jsonfuncs.c | 226 ++++++++++++++++++++++++++-- src/test/regress/expected/jsonb.out | 135 +++++++++++++++++ src/test/regress/sql/jsonb.sql | 81 ++++++++++ 4 files changed, 451 insertions(+), 15 deletions(-) diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml index 3ace5e444b..07bd19f974 100644 --- a/doc/src/sgml/json.sgml +++ b/doc/src/sgml/json.sgml @@ -648,6 +648,30 @@ UPDATE table_name SET jsonb_field['a'] = '1'; -- Where jsonb_field was NULL, it is now [1] UPDATE table_name SET jsonb_field[0] = '1'; +</programlisting> + + If an index is specified for an array containing too few elements, + <literal>NULL</literal> elements will be appended until the index is reachable + and the value can be set. + +<programlisting> +-- Where jsonb_field was [], it is now [null, null, 2]; +-- where jsonb_field was [0], it is now [0, null, 2] +UPDATE table_name SET jsonb_field[2] = '2'; +</programlisting> + + A <type>jsonb</type> value will accept assignments to nonexistent subscript + paths as long as the last existing path key is an object or an array. Since + the final subscript is not traversed, it may be an object key. Nested arrays + will be created and <literal>NULL</literal>-padded according to the path until + the value can be placed appropriately. + +<programlisting> +-- Where jsonb_field was {}, it is now {'a': [{'b': 1}]} +UPDATE table_name SET jsonb_field['a'][0]['b'] = '1'; + +-- Where jsonb_field was [], it is now [{'a': 1}] +UPDATE table_name SET jsonb_field[0]['a'] = '1'; </programlisting> </para> diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 5a0ba6b220..f14f6c3191 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -44,6 +44,8 @@ #define JB_PATH_INSERT_AFTER 0x0010 #define JB_PATH_CREATE_OR_INSERT \ (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER | JB_PATH_CREATE) +#define JB_PATH_FILL_GAPS 0x0020 +#define JB_PATH_CONSISTENT_POSITION 0x0040 /* state for json_object_keys */ typedef struct OkeysState @@ -1634,14 +1636,116 @@ jsonb_set_element(Jsonb* jb, Datum *path, int path_len, it = JsonbIteratorInit(&jb->root); - res = setPath(&it, path, path_nulls, path_len, &state, 0, - newval, JB_PATH_CREATE); + res = setPath(&it, path, path_nulls, path_len, &state, 0, newval, + JB_PATH_CREATE | JB_PATH_FILL_GAPS | + JB_PATH_CONSISTENT_POSITION); pfree(path_nulls); PG_RETURN_JSONB_P(JsonbValueToJsonb(res)); } +static void +push_null_elements(JsonbParseState **ps, int num) +{ + JsonbValue null; + + null.type = jbvNull; + + while (num-- > 0) + pushJsonbValue(ps, WJB_ELEM, &null); +} + +/* + * Prepare a new structure containing nested empty objects and arrays + * corresponding to the specified path, and assign a new value at the end of + * this path. E.g. the path [a][0][b] with the new value 1 will produce the + * structure {a: [{b: 1}]}. + * + * Called is responsible to make sure such path does not exist yet. + */ +static void +push_path(JsonbParseState **st, int level, Datum *path_elems, + bool *path_nulls, int path_len, JsonbValue *newval) +{ + /* + * tpath contains expected type of an empty jsonb created at each level + * higher or equal than the current one, either jbvObject or jbvArray. + * Since it contains only information about path slice from level to the + * end, the access index must be normalized by level. + */ + enum jbvType *tpath = palloc0((path_len - level) * sizeof(enum jbvType)); + long lindex; + JsonbValue newkey; + + /* + * Create first part of the chain with beginning tokens. For the current + * level WJB_BEGIN_OBJECT/WJB_BEGIN_ARRAY was already created, so start + * with the next one. + */ + for(int i = level + 1; i < path_len; i++) + { + char *c, *badp; + + if (path_nulls[i]) + break; + + /* + * Try to convert to an integer to find out the expected type, + * object or array. + */ + c = TextDatumGetCString(path_elems[i]); + errno = 0; + lindex = strtol(c, &badp, 10); + if (errno != 0 || badp == c || *badp != '\0' || lindex > INT_MAX || + lindex < INT_MIN) + { + /* text, an object is expected */ + newkey.type = jbvString; + newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[i]); + newkey.val.string.val = VARDATA_ANY(path_elems[i]); + + (void) pushJsonbValue(st, WJB_BEGIN_OBJECT, NULL); + (void) pushJsonbValue(st, WJB_KEY, &newkey); + + tpath[i - level] = jbvObject; + } + else + { + /* integer, an array is expected */ + (void) pushJsonbValue(st, WJB_BEGIN_ARRAY, NULL); + + push_null_elements(st, lindex); + + tpath[i - level] = jbvArray; + } + + } + + /* Insert an actual value for either an object or array */ + if (tpath[(path_len - level) - 1] == jbvArray) + { + (void) pushJsonbValue(st, WJB_ELEM, newval); + } + else + (void) pushJsonbValue(st, WJB_VALUE, newval); + + /* + * Close everything up to the last but one level. The last one will be + * closed outside of this function. + */ + for(int i = path_len - 1; i > level; i--) + { + if (path_nulls[i]) + break; + + if (tpath[i - level] == jbvObject) + (void) pushJsonbValue(st, WJB_END_OBJECT, NULL); + else + (void) pushJsonbValue(st, WJB_END_ARRAY, NULL); + } +} + /* * Return the text representation of the given JsonbValue. */ @@ -4782,6 +4886,21 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2, * Bits JB_PATH_INSERT_BEFORE and JB_PATH_INSERT_AFTER in op_type * behave as JB_PATH_CREATE if new value is inserted in JsonbObject. * + * If JB_PATH_FILL_GAPS bit is set, this will change an assignment logic in + * case if target is an array. The assignment index will not be restricted by + * number of elements in the array, and if there are any empty slots between + * last element of the array and a new one they will be filled with nulls. If + * the index is negative, it still will be considered an an index from the end + * of the array. Of a part of the path is not present and this part is more + * than just one last element, this flag will instruct to create the whole + * chain of corresponding objects and insert the value. + * + * JB_PATH_CONSISTENT_POSITION for an array indicates that the called wants to + * keep values with fixed indices. Indices for existing elements could be + * changed (shifted forward) in case if the array is prepended with a new value + * and a negative index out of the range, so this behavior will be prevented + * and return an error. + * * All path elements before the last must already exist * whatever bits in op_type are set, or nothing is done. */ @@ -4876,6 +4995,8 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, memcmp(k.val.string.val, VARDATA_ANY(path_elems[level]), k.val.string.len) == 0) { + done = true; + if (level == path_len - 1) { /* @@ -4895,7 +5016,6 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, (void) pushJsonbValue(st, WJB_KEY, &k); (void) pushJsonbValue(st, WJB_VALUE, newval); } - done = true; } else { @@ -4940,6 +5060,31 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, } } } + + /* + * If we got here there are only few possibilities: + * - no target path was found, and an open object with some keys/values was + * pushed into the state + * - an object is empty, only WJB_BEGIN_OBJECT is pushed + * + * In both cases if instructed to create the path when not present, + * generate the whole chain of empty objects and insert the new value + * there. + */ + if (!done && (op_type & JB_PATH_FILL_GAPS) && (level < path_len - 1)) + { + JsonbValue newkey; + + newkey.type = jbvString; + newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]); + newkey.val.string.val = VARDATA_ANY(path_elems[level]); + + (void) pushJsonbValue(st, WJB_KEY, &newkey); + (void) push_path(st, level, path_elems, path_nulls, + path_len, newval); + + /* Result is closed with WJB_END_OBJECT outside of this function */ + } } /* @@ -4978,25 +5123,48 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (idx < 0) { if (-idx > nelems) - idx = INT_MIN; + { + /* + * If asked to keep elements position consistent, it's not allowed + * to prepend the array. + */ + if (op_type & JB_PATH_CONSISTENT_POSITION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("path element at position %d is out of range: %d", + level + 1, idx))); + else + idx = INT_MIN; + } else idx = nelems + idx; } - if (idx > 0 && idx > nelems) - idx = nelems; + /* + * Filling the gaps means there are no limits on the positive index are + * imposed, we can set any element. Otherwise limit the index by nelems. + */ + if (!(op_type & JB_PATH_FILL_GAPS)) + { + if (idx > 0 && idx > nelems) + idx = nelems; + } /* * if we're creating, and idx == INT_MIN, we prepend the new value to the * array also if the array is empty - in which case we don't really care * what the idx value is */ - if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) && (op_type & JB_PATH_CREATE_OR_INSERT)) { Assert(newval != NULL); + + if (op_type & JB_PATH_FILL_GAPS && nelems == 0 && idx > 0) + push_null_elements(st, idx); + (void) pushJsonbValue(st, WJB_ELEM, newval); + done = true; } @@ -5007,6 +5175,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (i == idx && level < path_len) { + done = true; + if (level == path_len - 1) { r = JsonbIteratorNext(it, &v, true); /* skip */ @@ -5024,8 +5194,6 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE)) (void) pushJsonbValue(st, WJB_ELEM, newval); - - done = true; } else (void) setPath(it, path_elems, path_nulls, path_len, @@ -5053,14 +5221,42 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); } } - - if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && - level == path_len - 1 && i == nelems - 1) - { - (void) pushJsonbValue(st, WJB_ELEM, newval); - } } } + + if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && level == path_len - 1) + { + /* + * If asked to fill the gaps, idx could be bigger than nelems, + * so prepend the new element with nulls if that's the case. + */ + if (op_type & JB_PATH_FILL_GAPS && idx > nelems) + push_null_elements(st, idx - nelems); + + (void) pushJsonbValue(st, WJB_ELEM, newval); + done = true; + } + + /* + * If we got here there are only few possibilities: + * - no target path was found, and an open array with some keys/values was + * pushed into the state + * - an array is empty, only WJB_BEGIN_ARRAY is pushed + * + * In both cases if instructed to create the path when not present, + * generate the whole chain of empty objects and insert the new value + * there. + */ + if (!done && (op_type & JB_PATH_FILL_GAPS) && (level < path_len - 1)) + { + if (idx > 0) + push_null_elements(st, idx - nelems); + + (void) push_path(st, level, path_elems, path_nulls, + path_len, newval); + + /* Result is closed with WJB_END_OBJECT outside of this function */ + } } /* diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index 46bf2e2353..5b5510c4fd 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -4999,6 +4999,141 @@ select * from test_jsonb_subscript; 3 | [1] (3 rows) +-- Fill the gaps logic +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '[0]'); +update test_jsonb_subscript set test_json[5] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+-------------------------------- + 1 | [0, null, null, null, null, 1] +(1 row) + +update test_jsonb_subscript set test_json[-4] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+----------------------------- + 1 | [0, null, 1, null, null, 1] +(1 row) + +update test_jsonb_subscript set test_json[-8] = '1'; +ERROR: path element at position 1 is out of range: -8 +select * from test_jsonb_subscript; + id | test_json +----+----------------------------- + 1 | [0, null, 1, null, null, 1] +(1 row) + +-- keep consistent values position +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '[]'); +update test_jsonb_subscript set test_json[5] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+----------------------------------- + 1 | [null, null, null, null, null, 1] +(1 row) + +-- create the whole path +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json['a'][0]['b'][0]['c'] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+---------------------------- + 1 | {"a": [{"b": [{"c": 1}]}]} +(1 row) + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json['a'][2]['b'][2]['c'][2] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+------------------------------------------------------------------ + 1 | {"a": [null, null, {"b": [null, null, {"c": [null, null, 1]}]}]} +(1 row) + +-- create the whole path with already existing keys +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{"b": 1}'); +update test_jsonb_subscript set test_json['a'][0] = '2'; +select * from test_jsonb_subscript; + id | test_json +----+-------------------- + 1 | {"a": [2], "b": 1} +(1 row) + +-- the start jsonb is an object, first subscript is treated as a key +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json[0]['a'] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+----------------- + 1 | {"0": {"a": 1}} +(1 row) + +-- the start jsonb is an array +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '[]'); +update test_jsonb_subscript set test_json[0]['a'] = '1'; +update test_jsonb_subscript set test_json[2]['b'] = '2'; +select * from test_jsonb_subscript; + id | test_json +----+---------------------------- + 1 | [{"a": 1}, null, {"b": 2}] +(1 row) + +-- overwriting an existing path +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json['a']['b'][1] = '1'; +update test_jsonb_subscript set test_json['a']['b'][10] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+---------------------------------------------------------------------------- + 1 | {"a": {"b": [null, 1, null, null, null, null, null, null, null, null, 1]}} +(1 row) + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '[]'); +update test_jsonb_subscript set test_json[0][0][0] = '1'; +update test_jsonb_subscript set test_json[0][0][1] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+------------ + 1 | [[[1, 1]]] +(1 row) + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json['a']['b'][10] = '1'; +update test_jsonb_subscript set test_json['a'][10][10] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+------------------------------------------------------------------------------------------------------------------------------------------------------ + 1 | {"a": {"b": [null, null, null, null, null, null, null, null, null, null, 1], "10": [null, null, null, null, null, null, null, null, null, null, 1]}} +(1 row) + +-- an empty sub element +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{"a": {}}'); +update test_jsonb_subscript set test_json['a']['b']['c'][2] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+-------------------------------------- + 1 | {"a": {"b": {"c": [null, null, 1]}}} +(1 row) + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{"a": []}'); +update test_jsonb_subscript set test_json['a'][1]['c'][2] = '1'; +select * from test_jsonb_subscript; + id | test_json +----+--------------------------------------- + 1 | {"a": [null, {"c": [null, null, 1]}]} +(1 row) + -- jsonb to tsvector select to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::jsonb); to_tsvector diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index 20aa8fe0e2..0320db0ea4 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -1290,6 +1290,87 @@ update test_jsonb_subscript set test_json = NULL where id = 3; update test_jsonb_subscript set test_json[0] = '1'; select * from test_jsonb_subscript; +-- Fill the gaps logic +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '[0]'); + +update test_jsonb_subscript set test_json[5] = '1'; +select * from test_jsonb_subscript; + +update test_jsonb_subscript set test_json[-4] = '1'; +select * from test_jsonb_subscript; + +update test_jsonb_subscript set test_json[-8] = '1'; +select * from test_jsonb_subscript; + +-- keep consistent values position +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '[]'); + +update test_jsonb_subscript set test_json[5] = '1'; +select * from test_jsonb_subscript; + +-- create the whole path +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json['a'][0]['b'][0]['c'] = '1'; +select * from test_jsonb_subscript; + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json['a'][2]['b'][2]['c'][2] = '1'; +select * from test_jsonb_subscript; + +-- create the whole path with already existing keys +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{"b": 1}'); +update test_jsonb_subscript set test_json['a'][0] = '2'; +select * from test_jsonb_subscript; + +-- the start jsonb is an object, first subscript is treated as a key +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json[0]['a'] = '1'; +select * from test_jsonb_subscript; + +-- the start jsonb is an array +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '[]'); +update test_jsonb_subscript set test_json[0]['a'] = '1'; +update test_jsonb_subscript set test_json[2]['b'] = '2'; +select * from test_jsonb_subscript; + +-- overwriting an existing path +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json['a']['b'][1] = '1'; +update test_jsonb_subscript set test_json['a']['b'][10] = '1'; +select * from test_jsonb_subscript; + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '[]'); +update test_jsonb_subscript set test_json[0][0][0] = '1'; +update test_jsonb_subscript set test_json[0][0][1] = '1'; +select * from test_jsonb_subscript; + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{}'); +update test_jsonb_subscript set test_json['a']['b'][10] = '1'; +update test_jsonb_subscript set test_json['a'][10][10] = '1'; +select * from test_jsonb_subscript; + +-- an empty sub element + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{"a": {}}'); +update test_jsonb_subscript set test_json['a']['b']['c'][2] = '1'; +select * from test_jsonb_subscript; + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values (1, '{"a": []}'); +update test_jsonb_subscript set test_json['a'][1]['c'][2] = '1'; +select * from test_jsonb_subscript; + -- jsonb to tsvector select to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::jsonb); -- 2.21.0 --3x42k255okwqcjpg Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v49-0003-Replace-assuming-a-composite-object-on-a-scalar.patch" ^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: How about using dirty snapshots to locate dependent objects? @ 2024-06-07 04:36 Dilip Kumar <[email protected]> 0 siblings, 1 reply; 7+ messages in thread From: Dilip Kumar @ 2024-06-07 04:36 UTC (permalink / raw) To: Ashutosh Sharma <[email protected]>; +Cc: pgsql-hackers On Thu, Jun 6, 2024 at 7:39 PM Ashutosh Sharma <[email protected]> wrote: > > On Thu, Jun 6, 2024 at 6:20 PM Dilip Kumar <[email protected]> wrote: >> >> On Thu, Jun 6, 2024 at 5:59 PM Ashutosh Sharma <[email protected]> wrote: >> > >> > Hello everyone, >> > >> > At present, we use MVCC snapshots to identify dependent objects. This implies that if a new dependent object is inserted within a transaction that is still ongoing, our search for dependent objects won't include this recently added one. Consequently, if someone attempts to drop the referenced object, it will be dropped, and when the ongoing transaction completes, we will end up having an entry for a referenced object that has already been dropped. This situation can lead to an inconsistent state. Below is an example illustrating this scenario: >> >> I don't think it's correct to allow the index to be dropped while a >> transaction is creating it. Instead, the right solution should be for >> the create index operation to protect the object it is using from >> being dropped. Specifically, the create index operation should acquire >> a shared lock on the Access Method (AM) to ensure it doesn't get >> dropped concurrently while the transaction is still in progress. > > > If I'm following you correctly, that's exactly what the patch is trying to do; while the index creation is in progress, if someone tries to drop the object referenced by the index under creation, the referenced object being dropped is able to know about the dependent object (in this case the index being created) using dirty snapshot and hence, it is unable to acquire the lock on the dependent object, and as a result of that, it is unable to drop it. You are aiming for the same outcome, but not in the conventional way. In my opinion, the correct approach is not to find objects being created using a dirty snapshot. Instead, when creating an object, you should acquire a proper lock on any dependent objects to prevent them from being dropped during the creation process. For instance, when creating an index that depends on the btree_gist access method, the create index operation should protect btree_gist from being dropped by acquiring the appropriate lock. It is not the responsibility of the drop extension to identify in-progress index creations. -- Regards, Dilip Kumar EnterpriseDB: http://www.enterprisedb.com ^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: How about using dirty snapshots to locate dependent objects? @ 2024-06-07 06:23 Ashutosh Sharma <[email protected]> parent: Dilip Kumar <[email protected]> 0 siblings, 1 reply; 7+ messages in thread From: Ashutosh Sharma @ 2024-06-07 06:23 UTC (permalink / raw) To: Dilip Kumar <[email protected]>; +Cc: pgsql-hackers On Fri, Jun 7, 2024 at 10:06 AM Dilip Kumar <[email protected]> wrote: > > On Thu, Jun 6, 2024 at 7:39 PM Ashutosh Sharma <[email protected]> wrote: > > > > On Thu, Jun 6, 2024 at 6:20 PM Dilip Kumar <[email protected]> wrote: > >> > >> On Thu, Jun 6, 2024 at 5:59 PM Ashutosh Sharma <[email protected]> wrote: > >> > > >> > Hello everyone, > >> > > >> > At present, we use MVCC snapshots to identify dependent objects. This implies that if a new dependent object is inserted within a transaction that is still ongoing, our search for dependent objects won't include this recently added one. Consequently, if someone attempts to drop the referenced object, it will be dropped, and when the ongoing transaction completes, we will end up having an entry for a referenced object that has already been dropped. This situation can lead to an inconsistent state. Below is an example illustrating this scenario: > >> > >> I don't think it's correct to allow the index to be dropped while a > >> transaction is creating it. Instead, the right solution should be for > >> the create index operation to protect the object it is using from > >> being dropped. Specifically, the create index operation should acquire > >> a shared lock on the Access Method (AM) to ensure it doesn't get > >> dropped concurrently while the transaction is still in progress. > > > > > > If I'm following you correctly, that's exactly what the patch is trying to do; while the index creation is in progress, if someone tries to drop the object referenced by the index under creation, the referenced object being dropped is able to know about the dependent object (in this case the index being created) using dirty snapshot and hence, it is unable to acquire the lock on the dependent object, and as a result of that, it is unable to drop it. > > You are aiming for the same outcome, but not in the conventional way. > In my opinion, the correct approach is not to find objects being > created using a dirty snapshot. Instead, when creating an object, you > should acquire a proper lock on any dependent objects to prevent them > from being dropped during the creation process. For instance, when > creating an index that depends on the btree_gist access method, the > create index operation should protect btree_gist from being dropped by > acquiring the appropriate lock. It is not the responsibility of the > drop extension to identify in-progress index creations. Thanks for sharing your thoughts, I appreciate your inputs and completely understand your perspective, but I wonder if that is feasible? For example, if an object (index in this case) has dependency on lets say 'n' number of objects, and those 'n' number of objects belong to say 'n' different catalog tables, so should we acquire locks on each of them until the create index command succeeds, or, should we just check for the presence of dependent objects and record their dependency inside the pg_depend table. Talking about this particular case, we are trying to create gist index that has dependency on gist_int4 opclass, it is one of the tuple inside pg_opclass catalog table, so should acquire lock in this tuple/table until the create index command succeeds and is that the thing to be done for all the dependent objects? -- With Regards, Ashutosh Sharma. ^ permalink raw reply [nested|flat] 7+ messages in thread
* Re: How about using dirty snapshots to locate dependent objects? @ 2024-06-07 06:43 Dilip Kumar <[email protected]> parent: Ashutosh Sharma <[email protected]> 0 siblings, 0 replies; 7+ messages in thread From: Dilip Kumar @ 2024-06-07 06:43 UTC (permalink / raw) To: Ashutosh Sharma <[email protected]>; +Cc: pgsql-hackers On Fri, Jun 7, 2024 at 11:53 AM Ashutosh Sharma <[email protected]> wrote: > > On Fri, Jun 7, 2024 at 10:06 AM Dilip Kumar <[email protected]> wrote: > > > > On Thu, Jun 6, 2024 at 7:39 PM Ashutosh Sharma <[email protected]> wrote: > > > > > > On Thu, Jun 6, 2024 at 6:20 PM Dilip Kumar <[email protected]> wrote: > > >> > > >> On Thu, Jun 6, 2024 at 5:59 PM Ashutosh Sharma <[email protected]> wrote: > > >> > > > >> > Hello everyone, > > >> > > > >> > At present, we use MVCC snapshots to identify dependent objects. This implies that if a new dependent object is inserted within a transaction that is still ongoing, our search for dependent objects won't include this recently added one. Consequently, if someone attempts to drop the referenced object, it will be dropped, and when the ongoing transaction completes, we will end up having an entry for a referenced object that has already been dropped. This situation can lead to an inconsistent state. Below is an example illustrating this scenario: > > >> > > >> I don't think it's correct to allow the index to be dropped while a > > >> transaction is creating it. Instead, the right solution should be for > > >> the create index operation to protect the object it is using from > > >> being dropped. Specifically, the create index operation should acquire > > >> a shared lock on the Access Method (AM) to ensure it doesn't get > > >> dropped concurrently while the transaction is still in progress. > > > > > > > > > If I'm following you correctly, that's exactly what the patch is trying to do; while the index creation is in progress, if someone tries to drop the object referenced by the index under creation, the referenced object being dropped is able to know about the dependent object (in this case the index being created) using dirty snapshot and hence, it is unable to acquire the lock on the dependent object, and as a result of that, it is unable to drop it. > > > > You are aiming for the same outcome, but not in the conventional way. > > In my opinion, the correct approach is not to find objects being > > created using a dirty snapshot. Instead, when creating an object, you > > should acquire a proper lock on any dependent objects to prevent them > > from being dropped during the creation process. For instance, when > > creating an index that depends on the btree_gist access method, the > > create index operation should protect btree_gist from being dropped by > > acquiring the appropriate lock. It is not the responsibility of the > > drop extension to identify in-progress index creations. > > Thanks for sharing your thoughts, I appreciate your inputs and > completely understand your perspective, but I wonder if that is > feasible? For example, if an object (index in this case) has > dependency on lets say 'n' number of objects, and those 'n' number of > objects belong to say 'n' different catalog tables, so should we > acquire locks on each of them until the create index command succeeds, > or, should we just check for the presence of dependent objects and > record their dependency inside the pg_depend table. Talking about this > particular case, we are trying to create gist index that has > dependency on gist_int4 opclass, it is one of the tuple inside > pg_opclass catalog table, so should acquire lock in this tuple/table > until the create index command succeeds and is that the thing to be > done for all the dependent objects? I am not sure what is the best way to do it, but if you are creating an object which is dependent on the other object then you need to check the existence of those objects, record dependency on those objects, and also lock them so that those object doesn't get dropped while you are creating your object. I haven't looked into the patch but something similar is being achieved in the thread Bertrand has pointed out by locking the database object while recording the dependency on those. -- Regards, Dilip Kumar EnterpriseDB: http://www.enterprisedb.com ^ permalink raw reply [nested|flat] 7+ messages in thread
end of thread, other threads:[~2024-06-07 06:43 UTC | newest] Thread overview: 7+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2016-09-14 03:48 [PATCH 2/6] Make keywords' case follow to input Kyotaro Horiguchi <[email protected]> 2016-09-14 03:48 [PATCH 02/12] Make keywords' case follow to input Kyotaro Horiguchi <[email protected]> 2016-09-14 03:48 [PATCH 02/17] Make keywords' case follow to input Kyotaro Horiguchi <[email protected]> 2020-12-31 14:19 [PATCH v49 2/3] Filling gaps in jsonb Dmitrii Dolgov <[email protected]> 2024-06-07 04:36 Re: How about using dirty snapshots to locate dependent objects? Dilip Kumar <[email protected]> 2024-06-07 06:23 ` Re: How about using dirty snapshots to locate dependent objects? Ashutosh Sharma <[email protected]> 2024-06-07 06:43 ` Re: How about using dirty snapshots to locate dependent objects? Dilip Kumar <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox