psql tab completion improvements, from Greg Sabino Mullane:

* Made DELETE into "DELETE FROM"
* Moved ANALZYE to the end of the list to ease EXPLAIN / VACUUM
  conflicts
* Removed the ANALYZE xx semicolon completion: we don't do that anywhere
  else
* Add DECLARE support
* Add parens for DROP AGGREGATE
* Add "CASCADE | RESTRICT" for DROP xx
* Make EXPLAIN <tab> a lot smarter
* GROUP "BY" and ORDER "BY"
* "ISOLATION" becomes "ISOLATION LEVEL"
* Fix error in which REVOKE xx ON yy was receiving "TO", now gets "FROM"
* Add GRANT/REVOKE xx ON yy TO/FROM choices: usernames, GROUP, PUBLIC
* PREPARE xx <tab> AS "SELECT | INSERT | UPDATE | DELETE"
* Add = at end of UPDATE xx SET yy
* Beef up VACUUM stuff
This commit is contained in:
Neil Conway 2005-05-18 04:47:40 +00:00
parent a9c4c9cd52
commit 4c1f9a0f0b
1 changed files with 158 additions and 30 deletions

View File

@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2005, PostgreSQL Global Development Group * 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 "\ " FROM pg_catalog.pg_user "\
" WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'" " 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 */ /* the silly-looking length condition is just to eat up the current word */
#define Query_for_table_owning_index \ #define Query_for_table_owning_index \
"SELECT pg_catalog.quote_ident(c1.relname) "\ "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[] = { static const char *const sql_commands[] = {
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT", "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", "EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY",
"PREPARE", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT", "PREPARE", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT",
"SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL "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) pg_strcasecmp(prev_wd, "USER") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_users); 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 <table>, complete with semicolon. */
else if (pg_strcasecmp(prev2_wd, "ANALYZE") == 0)
COMPLETE_WITH_CONST(";");
/* BEGIN, END, COMMIT, ABORT */ /* BEGIN, END, COMMIT, ABORT */
else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 || else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
pg_strcasecmp(prev_wd, "END") == 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) pg_strcasecmp(prev_wd, "AS") == 0)
COMPLETE_WITH_CONST("SELECT"); 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" * Complete DELETE with FROM (only if the word before that is not "ON"
* (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT) * (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 */ /* 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 * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
* the list of SQL commands
*/ */
else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0 || else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0)
(pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 && {
pg_strcasecmp(prev_wd, "VERBOSE") == 0)) static const char *const list_EXPLAIN[] =
COMPLETE_WITH_LIST(sql_commands); {"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 */ /* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */ /* 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); COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0) else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else else if (pg_strcasecmp(prev4_wd, "GRANT") == 0)
COMPLETE_WITH_CONST("TO"); COMPLETE_WITH_CONST("TO");
else
COMPLETE_WITH_CONST("FROM");
} }
/* /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
* TODO: to complete with user name we need prev5_wd -- wait for a else if (pg_strcasecmp(prev3_wd, "ON") == 0 &&
* more general solution there same for GRANT <sth> ON { DATABASE | ((pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
* FUNCTION | LANGUAGE | SCHEMA | TABLESPACE } xxx TO 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 */ /* INSERT */
/* Complete INSERT with "INTO" */ /* Complete INSERT with "INTO" */
@ -1360,10 +1426,32 @@ psql_completion(char *text, int start, int end)
/* NOTIFY */ /* NOTIFY */
else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0) 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'"); 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*/ /* OWNER TO - complete with available users*/
else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 && else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 &&
pg_strcasecmp(prev_wd, "TO") == 0) pg_strcasecmp(prev_wd, "TO") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_users); 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 */ /* REINDEX */
else if (pg_strcasecmp(prev_wd, "REINDEX") == 0) 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)) && pg_strcasecmp(prev_wd, "TRANSACTION") == 0))
{ {
static const char *const my_list[] = static const char *const my_list[] =
{"ISOLATION", "READ", NULL}; {"ISOLATION LEVEL", "READ", NULL};
COMPLETE_WITH_LIST(my_list); 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) else if (pg_strcasecmp(prev_wd, "SET") == 0)
COMPLETE_WITH_ATTR(prev2_wd); 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) else if (pg_strcasecmp(prev_wd, "VACUUM") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
" UNION SELECT 'FULL'" " UNION SELECT 'FULL'"
" UNION SELECT 'FREEZE'"
" UNION SELECT 'ANALYZE'" " UNION SELECT 'ANALYZE'"
" UNION SELECT 'VERBOSE'"); " UNION SELECT 'VERBOSE'");
else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 && else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
(pg_strcasecmp(prev_wd, "FULL") == 0 || (pg_strcasecmp(prev_wd, "FULL") == 0 ||
pg_strcasecmp(prev_wd, "ANALYZE") == 0 || pg_strcasecmp(prev_wd, "FREEZE") == 0))
pg_strcasecmp(prev_wd, "VERBOSE") == 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); COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
/* WHERE */ /* WHERE */