Skip to content

Commit

Permalink
psql tab completion improvements, from Greg Sabino Mullane:
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Neil Conway committed May 18, 2005
1 parent a9c4c9c commit 4c1f9a0
Showing 1 changed file with 158 additions and 30 deletions.
188 changes: 158 additions & 30 deletions src/bin/psql/tab-complete.c
Expand Up @@ -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 $
*/

/*----------------------------------------------------------------------
Expand Down Expand Up @@ -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) "\
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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)
Expand All @@ -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 */
Expand Down Expand Up @@ -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" */
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit 4c1f9a0

Please sign in to comment.