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 @@
*
* 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 <table>, 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 <sth> 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 */
......
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