Commit 4c1f9a0f authored by Neil Conway's avatar Neil Conway

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
parent a9c4c9cd
...@@ -3,7 +3,7 @@ ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
} }
...@@ -1501,8 +1589,8 @@ psql_completion(char *text, int start, int end) ...@@ -1501,8 +1589,8 @@ psql_completion(char *text, int start, int end)
{ {
static const char *const my_list[] = static const char *const my_list[] =
{"ISO", "SQL", "Postgres", "German", {"ISO", "SQL", "Postgres", "German",
"YMD", "DMY", "MDY", "YMD", "DMY", "MDY",
"US", "European", "NonEuropean", "US", "European", "NonEuropean",
"DEFAULT", NULL}; "DEFAULT", NULL};
COMPLETE_WITH_LIST(my_list); COMPLETE_WITH_LIST(my_list);
...@@ -1551,16 +1639,56 @@ psql_completion(char *text, int start, int end) ...@@ -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 */
......
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