diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index fca3aa3ffe..a90d7eb72c 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.149 2006/03/05 15:58:52 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.150 2006/04/02 09:02:41 alvherre Exp $ */ /*---------------------------------------------------------------------- @@ -465,6 +465,7 @@ static const pgsql_thing_t words_after_create[] = { /* Forward declaration of functions */ static char **psql_completion(char *text, int start, int end); static char *create_command_generator(const char *text, int state); +static char *drop_command_generator(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_query(int is_schema_query, @@ -521,11 +522,13 @@ psql_completion(char *text, int start, int end) *prev5_wd; static const char *const sql_commands[] = { - "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT", - "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE FROM", "DROP", "END", "EXECUTE", - "EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", - "PREPARE", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT", - "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL + "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", + "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", + "DELETE FROM", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH", "GRANT", + "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE", + "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", + "SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", + "UPDATE", "VACUUM", NULL }; static const char *const backslash_commands[] = { @@ -536,7 +539,8 @@ psql_completion(char *text, int start, int end) "\\e", "\\echo", "\\encoding", "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", - "\\o", "\\p", "\\password", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T", + "\\o", "\\p", "\\password", "\\pset", "\\q", "\\qecho", "\\r", + "\\set", "\\t", "\\T", "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL }; @@ -570,15 +574,19 @@ psql_completion(char *text, int start, int end) else if (!prev_wd) COMPLETE_WITH_LIST(sql_commands); -/* CREATE or DROP but not ALTER (TABLE|DOMAIN|GROUP) sth DROP */ - /* complete with something you can create or drop */ - else if (pg_strcasecmp(prev_wd, "CREATE") == 0 || - (pg_strcasecmp(prev_wd, "DROP") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") != 0 && - pg_strcasecmp(prev3_wd, "DOMAIN") != 0 && - pg_strcasecmp(prev3_wd, "GROUP") != 0)) +/* CREATE */ + /* complete with something you can create */ + else if (pg_strcasecmp(prev_wd, "CREATE") == 0) matches = completion_matches(text, create_command_generator); +/* DROP, except ALTER (TABLE|DOMAIN|GROUP) sth DROP */ + /* complete with something you can drop */ + else if (pg_strcasecmp(prev_wd, "DROP") == 0 && + pg_strcasecmp(prev3_wd, "TABLE") != 0 && + pg_strcasecmp(prev3_wd, "DOMAIN") != 0 && + pg_strcasecmp(prev3_wd, "GROUP") != 0) + matches = completion_matches(text, drop_command_generator); + /* ALTER */ /* @@ -1248,23 +1256,22 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 && prev_wd[strlen(prev_wd) - 1] == ')')) { - if ((pg_strcasecmp(prev3_wd, "DROP") == 0) && (pg_strcasecmp(prev2_wd, "FUNCTION") == 0)) - { - if (find_open_parenthesis(end)) + { + if (find_open_parenthesis(end)) { static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'"; - char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev_wd)); - sprintf(tmp_buf, func_args_query, prev_wd); - COMPLETE_WITH_QUERY(tmp_buf); - free(tmp_buf); + char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev_wd)); + sprintf(tmp_buf, func_args_query, prev_wd); + COMPLETE_WITH_QUERY(tmp_buf); + free(tmp_buf); } else { - COMPLETE_WITH_CONST("("); + COMPLETE_WITH_CONST("("); } - } - else + } + else { static const char *const list_DROPCR[] = {"CASCADE", "RESTRICT", NULL}; @@ -1274,7 +1281,7 @@ psql_completion(char *text, int start, int end) } else if (pg_strcasecmp(prev4_wd, "DROP") == 0 && pg_strcasecmp(prev3_wd, "FUNCTION") == 0 && - pg_strcasecmp(prev_wd, "(") == 0 ) + pg_strcasecmp(prev_wd, "(") == 0) { static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'"; char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev2_wd)); @@ -1282,6 +1289,14 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_QUERY(tmp_buf); free(tmp_buf); } + /* DROP OWNED BY */ + else if (pg_strcasecmp(prev2_wd, "DROP") == 0 && + pg_strcasecmp(prev_wd, "OWNED") == 0) + COMPLETE_WITH_CONST("BY"); + else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && + pg_strcasecmp(prev2_wd, "OWNED") == 0 && + pg_strcasecmp(prev_wd, "BY") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); @@ -1502,7 +1517,7 @@ psql_completion(char *text, int start, int end) else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0) COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s'"); -/* OWNER TO - complete with available roles*/ +/* OWNER TO - complete with available roles */ else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 && pg_strcasecmp(prev_wd, "TO") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_roles); @@ -1526,6 +1541,25 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_PREPARE); } +/* REASSIGN OWNED BY xxx TO yyy */ + else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0) + COMPLETE_WITH_CONST("OWNED"); + else if (pg_strcasecmp(prev_wd, "OWNED") == 0 && + pg_strcasecmp(prev2_wd, "REASSIGN") == 0) + COMPLETE_WITH_CONST("BY"); + else if (pg_strcasecmp(prev_wd, "BY") == 0 && + pg_strcasecmp(prev2_wd, "OWNED") == 0 && + pg_strcasecmp(prev3_wd, "REASSIGN") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + else if (pg_strcasecmp(prev2_wd, "BY") == 0 && + pg_strcasecmp(prev3_wd, "OWNED") == 0 && + pg_strcasecmp(prev4_wd, "REASSIGN") == 0) + COMPLETE_WITH_CONST("TO"); + else if (pg_strcasecmp(prev_wd, "TO") == 0 && + pg_strcasecmp(prev3_wd, "BY") == 0 && + pg_strcasecmp(prev4_wd, "OWNED") == 0 && + pg_strcasecmp(prev5_wd, "REASSIGN") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); /* REINDEX */ else if (pg_strcasecmp(prev_wd, "REINDEX") == 0) @@ -1909,7 +1943,7 @@ psql_completion(char *text, int start, int end) something of that sort. */ -/* This one gives you one from a list of things you can put after CREATE or DROP +/* This one gives you one from a list of things you can put after CREATE as defined above. */ static char * @@ -1935,6 +1969,51 @@ create_command_generator(const char *text, int state) return NULL; } +/* + * This function gives you a list of things you can put after a DROP command. + * Very similar to create_command_generator, but has an additional entry for + * OWNED BY. (We do it this way in order not to duplicate the + * words_after_create list.) + */ +static char * +drop_command_generator(const char *text, int state) +{ + static int list_index, + string_length; + const char *name; + + if (state == 0) + { + /* If this is the first time for this completion, init some values */ + list_index = 0; + string_length = strlen(text); + + /* + * DROP can be followed by "OWNED BY", which is not found in the list + * for CREATE matches, so make it the first state. (We do not make it + * the last state because it would be more difficult to detect when we + * have to return NULL instead.) + * + * Make sure we advance to the next state. + */ + list_index++; + if (pg_strncasecmp("OWNED", text, string_length) == 0) + return pg_strdup("OWNED"); + } + + /* + * In subsequent attempts, try to complete with the same items we use for + * CREATE + */ + while ((name = words_after_create[list_index++ - 1].name)) + { + if (pg_strncasecmp(name, text, string_length) == 0) + return pg_strdup(name); + } + + /* if nothing matches, return NULL */ + return NULL; +} /* The following two functions are wrappers for _complete_from_query */