Commit d7d77f38 authored by Peter Eisentraut's avatar Peter Eisentraut

psql: Add completion for \help DROP|ALTER

While \help CREATE would complete usefully, \help DROP or \help ALTER
did not complete anything.

Expand the list of things after CREATE and DROP to cover ALTER as well,
and use that for the ALTER completion.  Also make minor tweaks to that
list.

Also add support for completing \help on multiword commands like CREATE
TEXT SEARCH ...

Author: Andreas Karlsson <andreas@proxel.se>
parent 1914c5ea
...@@ -982,10 +982,11 @@ typedef struct ...@@ -982,10 +982,11 @@ typedef struct
#define THING_NO_CREATE (1 << 0) /* should not show up after CREATE */ #define THING_NO_CREATE (1 << 0) /* should not show up after CREATE */
#define THING_NO_DROP (1 << 1) /* should not show up after DROP */ #define THING_NO_DROP (1 << 1) /* should not show up after DROP */
#define THING_NO_SHOW (THING_NO_CREATE | THING_NO_DROP) #define THING_NO_ALTER (1 << 2) /* should not show up after ALTER */
#define THING_NO_SHOW (THING_NO_CREATE | THING_NO_DROP | THING_NO_ALTER)
static const pgsql_thing_t words_after_create[] = { static const pgsql_thing_t words_after_create[] = {
{"ACCESS METHOD", NULL, NULL}, {"ACCESS METHOD", NULL, NULL, THING_NO_ALTER},
{"AGGREGATE", NULL, &Query_for_list_of_aggregates}, {"AGGREGATE", NULL, &Query_for_list_of_aggregates},
{"CAST", NULL, NULL}, /* Casts have complex structures for names, so {"CAST", NULL, NULL}, /* Casts have complex structures for names, so
* skip it */ * skip it */
...@@ -999,6 +1000,7 @@ static const pgsql_thing_t words_after_create[] = { ...@@ -999,6 +1000,7 @@ static const pgsql_thing_t words_after_create[] = {
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"}, {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases}, {"DATABASE", Query_for_list_of_databases},
{"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW}, {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW},
{"DEFAULT PRIVILEGES", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
{"DOMAIN", NULL, &Query_for_list_of_domains}, {"DOMAIN", NULL, &Query_for_list_of_domains},
{"EVENT TRIGGER", NULL, NULL}, {"EVENT TRIGGER", NULL, NULL},
{"EXTENSION", Query_for_list_of_extensions}, {"EXTENSION", Query_for_list_of_extensions},
...@@ -1006,12 +1008,13 @@ static const pgsql_thing_t words_after_create[] = { ...@@ -1006,12 +1008,13 @@ static const pgsql_thing_t words_after_create[] = {
{"FOREIGN TABLE", NULL, NULL}, {"FOREIGN TABLE", NULL, NULL},
{"FUNCTION", NULL, &Query_for_list_of_functions}, {"FUNCTION", NULL, &Query_for_list_of_functions},
{"GROUP", Query_for_list_of_roles}, {"GROUP", Query_for_list_of_roles},
{"LANGUAGE", Query_for_list_of_languages},
{"INDEX", NULL, &Query_for_list_of_indexes}, {"INDEX", NULL, &Query_for_list_of_indexes},
{"LANGUAGE", Query_for_list_of_languages},
{"LARGE OBJECT", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
{"MATERIALIZED VIEW", NULL, &Query_for_list_of_matviews}, {"MATERIALIZED VIEW", NULL, &Query_for_list_of_matviews},
{"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a
* good idea. */ * good idea. */
{"OWNED", NULL, NULL, THING_NO_CREATE}, /* for DROP OWNED BY ... */ {"OWNED", NULL, NULL, THING_NO_CREATE | THING_NO_ALTER}, /* for DROP OWNED BY ... */
{"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW}, {"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW},
{"POLICY", NULL, NULL}, {"POLICY", NULL, NULL},
{"PUBLICATION", Query_for_list_of_publications}, {"PUBLICATION", Query_for_list_of_publications},
...@@ -1021,15 +1024,18 @@ static const pgsql_thing_t words_after_create[] = { ...@@ -1021,15 +1024,18 @@ static const pgsql_thing_t words_after_create[] = {
{"SEQUENCE", NULL, &Query_for_list_of_sequences}, {"SEQUENCE", NULL, &Query_for_list_of_sequences},
{"SERVER", Query_for_list_of_servers}, {"SERVER", Query_for_list_of_servers},
{"SUBSCRIPTION", Query_for_list_of_subscriptions}, {"SUBSCRIPTION", Query_for_list_of_subscriptions},
{"SYSTEM", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
{"TABLE", NULL, &Query_for_list_of_tables}, {"TABLE", NULL, &Query_for_list_of_tables},
{"TABLESPACE", Query_for_list_of_tablespaces}, {"TABLESPACE", Query_for_list_of_tablespaces},
{"TEMP", NULL, NULL, THING_NO_DROP}, /* for CREATE TEMP TABLE ... */ {"TEMP", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMP TABLE ... */
{"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW}, {"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW},
{"TEMPORARY", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMPORARY TABLE ... */
{"TEXT SEARCH", NULL, NULL}, {"TEXT SEARCH", NULL, NULL},
{"TRANSFORM", NULL, NULL},
{"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s' AND NOT tgisinternal"}, {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s' AND NOT tgisinternal"},
{"TYPE", NULL, &Query_for_list_of_datatypes}, {"TYPE", NULL, &Query_for_list_of_datatypes},
{"UNIQUE", NULL, NULL, THING_NO_DROP}, /* for CREATE UNIQUE INDEX ... */ {"UNIQUE", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE INDEX ... */
{"UNLOGGED", NULL, NULL, THING_NO_DROP}, /* for CREATE UNLOGGED TABLE {"UNLOGGED", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNLOGGED TABLE
* ... */ * ... */
{"USER", Query_for_list_of_roles}, {"USER", Query_for_list_of_roles},
{"USER MAPPING FOR", NULL, NULL}, {"USER MAPPING FOR", NULL, NULL},
...@@ -1042,6 +1048,7 @@ static const pgsql_thing_t words_after_create[] = { ...@@ -1042,6 +1048,7 @@ static const pgsql_thing_t words_after_create[] = {
static char **psql_completion(const char *text, int start, int end); static char **psql_completion(const char *text, int start, int end);
static char *create_command_generator(const char *text, int state); static char *create_command_generator(const char *text, int state);
static char *drop_command_generator(const char *text, int state); static char *drop_command_generator(const char *text, int state);
static char *alter_command_generator(const char *text, int state);
static char *complete_from_query(const char *text, int state); static char *complete_from_query(const char *text, int state);
static char *complete_from_schema_query(const char *text, int state); static char *complete_from_schema_query(const char *text, int state);
static char *_complete_from_query(int is_schema_query, static char *_complete_from_query(int is_schema_query,
...@@ -1316,6 +1323,17 @@ psql_completion(const char *text, int start, int end) ...@@ -1316,6 +1323,17 @@ psql_completion(const char *text, int start, int end)
(previous_words_count >= 2 && \ (previous_words_count >= 2 && \
word_matches_cs(p1, prev_wd) && \ word_matches_cs(p1, prev_wd) && \
word_matches_cs(p2, prev2_wd)) word_matches_cs(p2, prev2_wd))
#define TailMatchesCS3(p3, p2, p1) \
(previous_words_count >= 3 && \
word_matches_cs(p1, prev_wd) && \
word_matches_cs(p2, prev2_wd) && \
word_matches_cs(p3, prev3_wd))
#define TailMatchesCS4(p4, p3, p2, p1) \
(previous_words_count >= 4 && \
word_matches_cs(p1, prev_wd) && \
word_matches_cs(p2, prev2_wd) && \
word_matches_cs(p3, prev3_wd) && \
word_matches_cs(p4, prev4_wd))
/* /*
* Macros for matching N words beginning at the start of the line, * Macros for matching N words beginning at the start of the line,
...@@ -1459,17 +1477,7 @@ psql_completion(const char *text, int start, int end) ...@@ -1459,17 +1477,7 @@ psql_completion(const char *text, int start, int end)
/* ALTER something */ /* ALTER something */
else if (Matches1("ALTER")) else if (Matches1("ALTER"))
{ matches = completion_matches(text, alter_command_generator);
static const char *const list_ALTER[] =
{"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
"EVENT TRIGGER", "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "MATERIALIZED VIEW", "OPERATOR",
"POLICY", "PUBLICATION", "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE",
"SUBSCRIPTION", "SYSTEM", "TABLE", "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
"USER", "USER MAPPING FOR", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
/* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */ /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */
else if (TailMatches4("ALL", "IN", "TABLESPACE", MatchAny)) else if (TailMatches4("ALL", "IN", "TABLESPACE", MatchAny))
COMPLETE_WITH_LIST2("SET TABLESPACE", "OWNED BY"); COMPLETE_WITH_LIST2("SET TABLESPACE", "OWNED BY");
...@@ -2622,6 +2630,7 @@ psql_completion(const char *text, int start, int end) ...@@ -2622,6 +2630,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches3("DROP", "OWNED", "BY")) else if (Matches3("DROP", "OWNED", "BY"))
COMPLETE_WITH_QUERY(Query_for_list_of_roles); COMPLETE_WITH_QUERY(Query_for_list_of_roles);
/* DROP TEXT SEARCH */
else if (Matches3("DROP", "TEXT", "SEARCH")) else if (Matches3("DROP", "TEXT", "SEARCH"))
COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
...@@ -3353,8 +3362,45 @@ psql_completion(const char *text, int start, int end) ...@@ -3353,8 +3362,45 @@ psql_completion(const char *text, int start, int end)
else if (TailMatchesCS1("\\encoding")) else if (TailMatchesCS1("\\encoding"))
COMPLETE_WITH_QUERY(Query_for_list_of_encodings); COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
else if (TailMatchesCS1("\\h") || TailMatchesCS1("\\help")) else if (TailMatchesCS1("\\h|\\help"))
COMPLETE_WITH_LIST(sql_commands); COMPLETE_WITH_LIST(sql_commands);
else if (TailMatchesCS2("\\h|\\help", MatchAny))
{
if (TailMatches1("DROP"))
matches = completion_matches(text, drop_command_generator);
else if (TailMatches1("ALTER"))
matches = completion_matches(text, alter_command_generator);
/* CREATE is recognized by tail match elsewhere, so doesn't need to be
* repeated here */
}
else if (TailMatchesCS3("\\h|\\help", MatchAny, MatchAny))
{
if (TailMatches2("CREATE|DROP", "ACCESS"))
COMPLETE_WITH_CONST("METHOD");
else if (TailMatches2("ALTER", "DEFAULT"))
COMPLETE_WITH_CONST("PRIVILEGES");
else if (TailMatches2("CREATE|ALTER|DROP", "EVENT"))
COMPLETE_WITH_CONST("TRIGGER");
else if (TailMatches2("CREATE|ALTER|DROP", "FOREIGN"))
COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
else if (TailMatches2("ALTER", "LARGE"))
COMPLETE_WITH_CONST("OBJECT");
else if (TailMatches2("CREATE|ALTER|DROP", "MATERIALIZED"))
COMPLETE_WITH_CONST("VIEW");
else if (TailMatches2("CREATE|ALTER|DROP", "TEXT"))
COMPLETE_WITH_CONST("SEARCH");
else if (TailMatches2("CREATE|ALTER|DROP", "USER"))
COMPLETE_WITH_CONST("MAPPING FOR");
}
else if (TailMatchesCS4("\\h|\\help", MatchAny, MatchAny, MatchAny))
{
if (TailMatches3("CREATE|ALTER|DROP", "FOREIGN", "DATA"))
COMPLETE_WITH_CONST("WRAPPER");
else if (TailMatches3("CREATE|ALTER|DROP", "TEXT", "SEARCH"))
COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
else if (TailMatches3("CREATE|ALTER|DROP", "USER", "MAPPING"))
COMPLETE_WITH_CONST("FOR");
}
else if (TailMatchesCS1("\\l*") && !TailMatchesCS1("\\lo*")) else if (TailMatchesCS1("\\l*") && !TailMatchesCS1("\\lo*"))
COMPLETE_WITH_QUERY(Query_for_list_of_databases); COMPLETE_WITH_QUERY(Query_for_list_of_databases);
else if (TailMatchesCS1("\\password")) else if (TailMatchesCS1("\\password"))
...@@ -3536,6 +3582,15 @@ drop_command_generator(const char *text, int state) ...@@ -3536,6 +3582,15 @@ drop_command_generator(const char *text, int state)
return create_or_drop_command_generator(text, state, THING_NO_DROP); return create_or_drop_command_generator(text, state, THING_NO_DROP);
} }
/*
* This function gives you a list of things you can put after an ALTER command.
*/
static char *
alter_command_generator(const char *text, int state)
{
return create_or_drop_command_generator(text, state, THING_NO_ALTER);
}
/* The following two functions are wrappers for _complete_from_query */ /* The following two functions are wrappers for _complete_from_query */
static char * static char *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment