diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 4aed36c66a..015dbd4779 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.127 2005/05/07 02:22:49 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.128 2005/05/18 04:47:40 neilc Exp $ */ /*---------------------------------------------------------------------- @@ -368,6 +368,12 @@ static const SchemaQuery Query_for_list_of_views = { " FROM pg_catalog.pg_user "\ " WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'" +#define Query_for_list_of_grant_users \ +" SELECT pg_catalog.quote_ident(usename) "\ +" FROM pg_catalog.pg_user "\ +" WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'"\ +" UNION SELECT 'PUBLIC' UNION SELECT 'GROUP'" + /* the silly-looking length condition is just to eat up the current word */ #define Query_for_table_owning_index \ "SELECT pg_catalog.quote_ident(c1.relname) "\ @@ -494,7 +500,7 @@ psql_completion(char *text, int start, int end) static const char *const sql_commands[] = { "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT", - "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE", "DROP", "END", "EXECUTE", + "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 @@ -920,14 +926,6 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev_wd, "USER") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_users); -/* ANALYZE */ - /* If the previous word is ANALYZE, produce list of tables. */ - else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0) - COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); - /* If we have ANALYZE , complete with semicolon. */ - else if (pg_strcasecmp(prev2_wd, "ANALYZE") == 0) - COMPLETE_WITH_CONST(";"); - /* BEGIN, END, COMMIT, ABORT */ else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 || pg_strcasecmp(prev_wd, "END") == 0 || @@ -1149,8 +1147,23 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev_wd, "AS") == 0) COMPLETE_WITH_CONST("SELECT"); -/* DELETE */ +/* DECLARE */ + else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0) + { + static const char *const list_DECLARE[] = + {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL}; + COMPLETE_WITH_LIST(list_DECLARE); + } + else if (pg_strcasecmp(prev_wd, "CURSOR") == 0) + { + static const char *const list_DECLARECURSOR[] = + {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL}; + COMPLETE_WITH_LIST(list_DECLARECURSOR); + } + + +/* DELETE */ /* * Complete DELETE with FROM (only if the word before that is not "ON" * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT) @@ -1176,16 +1189,60 @@ psql_completion(char *text, int start, int end) } /* XXX: implement tab completion for DELETE ... USING */ -/* EXPLAIN */ +/* DROP (when not the previous word) */ + /* DROP AGGREGATE */ + else if (pg_strcasecmp(prev3_wd, "DROP") == 0 && + pg_strcasecmp(prev2_wd, "AGGREGATE") == 0) + COMPLETE_WITH_CONST("("); + /* DROP object with CASCADE / RESTRICT */ + else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 && + (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 || + pg_strcasecmp(prev2_wd, "DOMAIN") == 0 || + pg_strcasecmp(prev2_wd, "FUNCTION") == 0 || + pg_strcasecmp(prev2_wd, "INDEX") == 0 || + pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 || + pg_strcasecmp(prev2_wd, "SCHEMA") == 0 || + pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 || + pg_strcasecmp(prev2_wd, "TABLE") == 0 || + pg_strcasecmp(prev2_wd, "TYPE") == 0 || + pg_strcasecmp(prev2_wd, "VIEW") == 0)) || + (pg_strcasecmp(prev4_wd, "DROP") == 0 && + pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 && + prev_wd[strlen(prev_wd) - 1] == ')')) + { + static const char *const list_DROPCR[] = + {"CASCADE", "RESTRICT", NULL}; + COMPLETE_WITH_LIST(list_DROPCR); + } + +/* EXPLAIN */ /* - * Complete EXPLAIN [VERBOSE] (which you'd have to type yourself) with - * the list of SQL commands + * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands */ - else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0 || - (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 && - pg_strcasecmp(prev_wd, "VERBOSE") == 0)) - COMPLETE_WITH_LIST(sql_commands); + else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0) + { + static const char *const list_EXPLAIN[] = + {"SELECT","INSERT","DELETE","UPDATE","DECLARE","ANALYZE","VERBOSE",NULL}; + COMPLETE_WITH_LIST(list_EXPLAIN); + } + else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 && + pg_strcasecmp(prev_wd, "ANALYZE") == 0) + { + static const char *const list_EXPLAIN[] = + {"SELECT","INSERT","DELETE","UPDATE","DECLARE","VERBOSE",NULL}; + COMPLETE_WITH_LIST(list_EXPLAIN); + } + else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 && + pg_strcasecmp(prev3_wd, "VACUUM") != 0 && + pg_strcasecmp(prev4_wd, "VACUUM") != 0 && + (pg_strcasecmp(prev2_wd, "ANALYZE") == 0 || + pg_strcasecmp(prev2_wd, "EXPLAIN") == 0)) + { + static const char *const list_EXPLAIN[] = + {"SELECT","INSERT","DELETE","UPDATE","DECLARE",NULL}; + COMPLETE_WITH_LIST(list_EXPLAIN); + } /* FETCH && MOVE */ /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */ @@ -1273,15 +1330,24 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_QUERY(Query_for_list_of_schemas); else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); - else + else if (pg_strcasecmp(prev4_wd, "GRANT") == 0) COMPLETE_WITH_CONST("TO"); + else + COMPLETE_WITH_CONST("FROM"); } - /* - * TODO: to complete with user name we need prev5_wd -- wait for a - * more general solution there same for GRANT ON { DATABASE | - * FUNCTION | LANGUAGE | SCHEMA | TABLESPACE } xxx TO - */ + /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */ + else if (pg_strcasecmp(prev3_wd, "ON") == 0 && + ((pg_strcasecmp(prev5_wd, "GRANT") == 0 && + pg_strcasecmp(prev_wd, "TO") == 0) || + (pg_strcasecmp(prev5_wd, "REVOKE") == 0 && + pg_strcasecmp(prev_wd, "FROM") == 0))) + COMPLETE_WITH_QUERY(Query_for_list_of_grant_users); + +/* GROUP BY */ + else if (pg_strcasecmp(prev3_wd, "FROM") == 0 && + pg_strcasecmp(prev_wd, "GROUP") == 0) + COMPLETE_WITH_CONST("BY"); /* INSERT */ /* Complete INSERT with "INTO" */ @@ -1360,10 +1426,32 @@ psql_completion(char *text, int start, int end) /* NOTIFY */ 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 users*/ else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 && pg_strcasecmp(prev_wd, "TO") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_users); + +/* ORDER BY */ + else if (pg_strcasecmp(prev3_wd, "FROM") == 0 && + pg_strcasecmp(prev_wd, "ORDER") == 0) + COMPLETE_WITH_CONST("BY"); + else if (pg_strcasecmp(prev4_wd, "FROM") == 0 && + pg_strcasecmp(prev2_wd, "ORDER") == 0 && + pg_strcasecmp(prev_wd, "BY") == 0) + COMPLETE_WITH_ATTR(prev3_wd); + +/* PREPARE xx AS */ + else if (pg_strcasecmp(prev_wd, "AS") == 0 && + pg_strcasecmp(prev3_wd, "PREPARE") == 0) + { + static const char *const list_PREPARE[] = + {"SELECT", "UPDATE", "INSERT", "DELETE", NULL}; + + COMPLETE_WITH_LIST(list_PREPARE); + } + + /* REINDEX */ else if (pg_strcasecmp(prev_wd, "REINDEX") == 0) { @@ -1407,7 +1495,7 @@ psql_completion(char *text, int start, int end) && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)) { static const char *const my_list[] = - {"ISOLATION", "READ", NULL}; + {"ISOLATION LEVEL", "READ", NULL}; COMPLETE_WITH_LIST(my_list); } @@ -1501,8 +1589,8 @@ psql_completion(char *text, int start, int end) { static const char *const my_list[] = {"ISO", "SQL", "Postgres", "German", - "YMD", "DMY", "MDY", - "US", "European", "NonEuropean", + "YMD", "DMY", "MDY", + "US", "European", "NonEuropean", "DEFAULT", NULL}; COMPLETE_WITH_LIST(my_list); @@ -1551,16 +1639,56 @@ psql_completion(char *text, int start, int end) else if (pg_strcasecmp(prev_wd, "SET") == 0) COMPLETE_WITH_ATTR(prev2_wd); -/* VACUUM */ +/* UPDATE xx SET yy = */ + else if (pg_strcasecmp(prev2_wd, "SET") == 0 && + pg_strcasecmp(prev4_wd, "UPDATE") == 0) + COMPLETE_WITH_CONST("="); + +/* + * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ] + * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] + */ else if (pg_strcasecmp(prev_wd, "VACUUM") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, " UNION SELECT 'FULL'" + " UNION SELECT 'FREEZE'" " UNION SELECT 'ANALYZE'" " UNION SELECT 'VERBOSE'"); else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && (pg_strcasecmp(prev_wd, "FULL") == 0 || - pg_strcasecmp(prev_wd, "ANALYZE") == 0 || - pg_strcasecmp(prev_wd, "VERBOSE") == 0)) + pg_strcasecmp(prev_wd, "FREEZE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, + " UNION SELECT 'ANALYZE'" + " UNION SELECT 'VERBOSE'"); + else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 && + pg_strcasecmp(prev_wd, "ANALYZE") == 0 && + (pg_strcasecmp(prev2_wd, "FULL") == 0 || + pg_strcasecmp(prev2_wd, "FREEZE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, + " UNION SELECT 'VERBOSE'"); + else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 && + pg_strcasecmp(prev_wd, "VERBOSE") == 0 && + (pg_strcasecmp(prev2_wd, "FULL") == 0 || + pg_strcasecmp(prev2_wd, "FREEZE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, + " UNION SELECT 'ANALYZE'"); + else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && + pg_strcasecmp(prev_wd, "VERBOSE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, + " UNION SELECT 'ANALYZE'"); + else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && + pg_strcasecmp(prev_wd, "ANALYZE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, + " UNION SELECT 'VERBOSE'"); + else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 && + pg_strcasecmp(prev2_wd, "VERBOSE") == 0) || + (pg_strcasecmp(prev_wd, "VERBOSE") == 0 && + pg_strcasecmp(prev2_wd, "ANALYZE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + +/* ANALZYE */ + /* If the previous word is ANALYZE, produce list of tables */ + else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); /* WHERE */