Added new commands and intelligence to psql tab completion.

This commit is contained in:
Peter Eisentraut 2000-06-25 14:25:51 +00:00
parent c7558b3bae
commit c4e63f70fa
1 changed files with 130 additions and 50 deletions

View File

@ -3,34 +3,44 @@
*
* Copyright 2000 by PostgreSQL Global Development Group
*
* $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.19 2000/05/05 08:44:27 petere Exp $
* $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.20 2000/06/25 14:25:51 petere Exp $
*/
/*-----------
This file implements a somewhat more sophisticated readline "TAB completion"
in psql. It is not intended to be AI, to replace learning SQL, or to relieve
you from thinking about what you're doing. Also it does not always give you
all the syntactically legal completions, only those that are the most common
or the ones that the programmer felt most like implementing.
CAVEAT: Tab completion causes queries to be sent to the backend. The number
tuples returned gets limited, in most default installations to 101, but if
you still don't like this prospect, you can turn off tab completion in your
~/.inputrc (or else ${INPUTRC}) file so:
$if psql
TAB: self-insert
$endif
See `man 3 readline` or `info readline` for the full details. Also, hence the
BUGS:
* If you split your queries across lines, this whole things gets confused.
(To fix this, one would have to read psql's query buffer rather than
readline's line buffer, which would require some major revisions of
things.)
* Table or attribute names with spaces in it will equally confuse it.
* Quotes, parenthesis, and other funny characters are not handled all that
gracefully.
-------------*/
/*----------------------------------------------------------------------
* This file implements a somewhat more sophisticated readline "TAB
* completion" in psql. It is not intended to be AI, to replace
* learning SQL, or to relieve you from thinking about what you're
* doing. Also it does not always give you all the syntactically legal
* completions, only those that are the most common or the ones that
* the programmer felt most like implementing.
*
* CAVEAT: Tab completion causes queries to be sent to the backend.
* The number tuples returned gets limited, in most default
* installations to 101, but if you still don't like this prospect,
* you can turn off tab completion in your ~/.inputrc (or else
* ${INPUTRC}) file so:
*
* $if psql
* set disable-completion on
* $endif
*
* See `man 3 readline' or `info readline' for the full details. Also,
* hence the
*
* BUGS:
*
* - If you split your queries across lines, this whole things gets
* confused. (To fix this, one would have to read psql's query
* buffer rather than readline's line buffer, which would require
* some major revisions of things.)
*
* - Table or attribute names with spaces in it will equally confuse
* it.
*
* - Quotes, parenthesis, and other funny characters are not handled
* all that gracefully.
*----------------------------------------------------------------------
*/
#include "postgres.h"
#include "tab-complete.h"
@ -120,6 +130,7 @@ pgsql_thing_t words_after_create[] = {
{"AGGREGATE", "SELECT distinct aggname FROM pg_aggregate WHERE substr(aggname,1,%d)='%s'"},
{"DATABASE", "SELECT datname FROM pg_database WHERE substr(datname,1,%d)='%s'"},
{"FUNCTION", "SELECT distinct proname FROM pg_proc WHERE substr(proname,1,%d)='%s'"},
{"GROUP", "SELECT groname FROM pg_group WHERE substr(groname,1,%d)='%s'"},
{"INDEX", "SELECT relname FROM pg_class WHERE relkind='i' and substr(relname,1,%d)='%s'"},
{"OPERATOR", NULL}, /* Querying for this is probably not such
* a good idea. */
@ -138,11 +149,11 @@ pgsql_thing_t words_after_create[] = {
/* The query to get a list of tables and a list of indexes, which are used at
various places. */
#define Query_for_list_of_tables words_after_create[7].query
#define Query_for_list_of_indexes words_after_create[3].query
#define Query_for_list_of_tables words_after_create[8].query
#define Query_for_list_of_indexes words_after_create[4].query
#define Query_for_list_of_databases words_after_create[1].query
#define Query_for_list_of_attributes "SELECT a.attname FROM pg_attribute a, pg_class c WHERE c.oid = a.attrelid and a.attnum>0 and substr(a.attname,1,%d)='%s' and c.relname='%s'"
#define Query_for_list_of_users words_after_create[13].query
/* A couple of macros to ease typing. You can use these to complete the given
string with
@ -179,25 +190,27 @@ psql_completion(char *text, int start, int end)
*prev4_wd;
static char *sql_commands[] = {
"ABORT", "ALTER", "BEGIN", "CLOSE", "CLUSTER", "COMMIT", "COPY",
"ABORT", "ALTER", "BEGIN", "CLOSE", "CLUSTER", "COMMENT", "COMMIT", "COPY",
"CREATE", "DECLARE", "DELETE", "DROP", "EXPLAIN", "FETCH", "GRANT",
"INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "RESET",
"REVOKE", "ROLLBACK", "SELECT", "SET", "SHOW", "UNLISTEN", "UPDATE",
"REVOKE", "ROLLBACK", "SELECT", "SET", "SHOW", "TRUNCATE", "UNLISTEN", "UPDATE",
"VACUUM", NULL
};
static char *pgsql_variables[] = {
/* these SET arguments are known in gram.y */
"TRANSACTION ISOLATION LEVEL",
"CONSTRAINTS",
"NAMES",
/* rest should match table in src/backend/commands/variable.c */
"TRANSACTION ISOLATION LEVEL",
/* these are treated in backend/commands/variable.c */
"DateStyle",
"TimeZone",
"effective_cache_size",
"random_page_cost",
"cpu_tuple_cost",
"cpu_index_tuple_cost",
"cpu_operator_cost",
"client_encoding",
"server_encoding",
"random_seed",
/* the rest should match USERSET and SUSET entries in
* backend/utils/misc/guc.c, but feel free to leave out the
* esoteric or debug settings */
"enable_seqscan",
"enable_indexscan",
"enable_tidscan",
@ -205,13 +218,17 @@ psql_completion(char *text, int start, int end)
"enable_nestloop",
"enable_mergejoin",
"enable_hashjoin",
"GEQO",
"client_encoding",
"server_encoding",
"KSQO",
"geqo",
"ksqo",
"sql_inheritance",
"sort_mem",
"debug_level",
"max_expr_depth",
"XactIsoLevel",
"PG_Options",
"effective_cache_size",
"random_page_cost",
"cpu_tuple_cost",
"cpu_index_tuple_cost",
"cpu_operator_cost",
NULL
};
@ -260,25 +277,41 @@ psql_completion(char *text, int start, int end)
matches = completion_matches(text, create_command_generator);
/* ALTER */
/* complete with what you can alter (TABLE or USER) */
/* complete with what you can alter (TABLE, GROUP, USER) */
else if (strcasecmp(prev_wd, "ALTER") == 0)
{
char *list_ALTER[] = {"TABLE", "USER", NULL};
char *list_ALTER[] = {"GROUP", "TABLE", "USER", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
/* If we detect ALTER TABLE <name>, suggest either "ADD" or "RENAME" */
/* If we detect ALTER TABLE <name>, suggest either ADD, ALTER, or RENAME */
else if (strcasecmp(prev3_wd, "ALTER") == 0 && strcasecmp(prev2_wd, "TABLE") == 0)
{
char *list_ALTER2[] = {"ADD", "RENAME", NULL};
char *list_ALTER2[] = {"ADD", "ALTER", "RENAME", NULL};
COMPLETE_WITH_LIST(list_ALTER2);
}
/* If we have TABLE <sth> ADD|RENAME, provide list of columns */
/* If we have TABLE <sth> ALTER|RENAME, provide list of columns */
else if (strcasecmp(prev3_wd, "TABLE") == 0 &&
(strcasecmp(prev_wd, "ADD") == 0 || strcasecmp(prev_wd, "RENAME") == 0))
(strcasecmp(prev_wd, "ALTER") == 0 || strcasecmp(prev_wd, "RENAME") == 0))
COMPLETE_WITH_ATTR(prev2_wd);
/* complete ALTER GROUP <foo> with ADD or DROP */
else if (strcasecmp(prev3_wd, "ALTER") == 0 && strcasecmp(prev2_wd, "GROUP") == 0)
{
char *list_ALTERGROUP[] = {"ADD", "DROP", NULL};
COMPLETE_WITH_LIST(list_ALTERGROUP);
}
/* complete ALTER GROUP <foo> ADD|DROP with USER */
else if (strcasecmp(prev4_wd, "ALTER") == 0 && strcasecmp(prev3_wd, "GROUP") == 0
&& (strcasecmp(prev_wd, "ADD")==0 || strcasecmp(prev_wd, "DROP")==0))
COMPLETE_WITH_CONST("USER");
/* complete {ALTER} GROUP <foo> ADD|DROP USER with a user name */
else if (strcasecmp(prev4_wd, "GROUP") == 0
&& (strcasecmp(prev2_wd, "ADD")==0 || strcasecmp(prev2_wd, "DROP")==0)
&& strcasecmp(prev_wd, "USER") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_users);
/* CLUSTER */
/* If the previous word is CLUSTER, produce list of indexes. */
else if (strcasecmp(prev_wd, "CLUSTER") == 0)
@ -304,6 +337,19 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_QUERY(query_buffer);
}
/* COMMENT */
else if (strcasecmp(prev_wd, "COMMENT")==0)
COMPLETE_WITH_CONST("ON");
else if (strcasecmp(prev2_wd, "COMMENT")==0 && strcasecmp(prev_wd, "ON")==0)
{
char * list_COMMENT[] =
{"DATABASE", "INDEX", "RULE", "SEQUENCE", "TABLE", "TYPE", "VIEW",
"COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (strcasecmp(prev4_wd, "COMMENT")==0 && strcasecmp(prev3_wd, "ON")==0)
COMPLETE_WITH_CONST("IS");
/* COPY */
/*
@ -510,6 +556,26 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
/* (If you want more with LOCK, you better think about it yourself.) */
/* NOTIFY */
else if (strcasecmp(prev_wd, "NOTIFY")==0)
COMPLETE_WITH_QUERY("SELECT relname FROM pg_listener WHERE substr(relname,1,%d)='%s'");
/* REINDEX */
else if (strcasecmp(prev_wd, "REINDEX") == 0)
{
char *list_REINDEX[] = {"TABLE", "DATABASE", "INDEX", NULL};
COMPLETE_WITH_LIST(list_REINDEX);
}
else if (strcasecmp(prev2_wd, "REINDEX")==0)
{
if (strcasecmp(prev_wd, "TABLE")==0)
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
else if (strcasecmp(prev_wd, "DATABASE")==0)
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
else if (strcasecmp(prev_wd, "INDEX")==0)
COMPLETE_WITH_QUERY(Query_for_list_of_indexes);
}
/* SELECT */
/* naah . . . */
@ -540,6 +606,12 @@ psql_completion(char *text, int start, int end)
strcasecmp(prev2_wd, "LEVEL") == 0 &&
strcasecmp(prev_wd, "READ") == 0)
COMPLETE_WITH_CONST("COMMITTED");
/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
else if (strcasecmp(prev3_wd, "SET")==0 && strcasecmp(prev2_wd, "CONSTRAINTS")==0)
{
char *constraint_list[] = {"DEFERRED", "IMMEDIATE", NULL};
COMPLETE_WITH_LIST(constraint_list);
}
/* Complete SET <var> with "TO" */
else if (strcasecmp(prev2_wd, "SET") == 0 &&
strcasecmp(prev4_wd, "UPDATE") != 0)
@ -568,6 +640,14 @@ psql_completion(char *text, int start, int end)
}
}
/* TRUNCATE */
else if (strcasecmp(prev_wd, "TRUNCATE")==0)
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
/* UNLISTEN */
else if (strcasecmp(prev_wd, "UNLISTEN")==0)
COMPLETE_WITH_QUERY("SELECT relname FROM pg_listener WHERE substr(relname,1,%d)='%s' UNION SELECT '*'::text");
/* UPDATE */
/* If prev. word is UPDATE suggest a list of tables */
else if (strcasecmp(prev_wd, "UPDATE") == 0)