Hi,
psql's tab completion has the following problem:
If we have the following syntax for example:
SET SESSION AUTHORIZATION <user>;
SET SESSION AUTHORIZATION DEFAULT;
After "SET SESSION AUTHORIZATION", the tab completion can offer a list of
roles or the string constant "DEFAULT". However it can't offer both because
it can't get a list of roles and add a string constant to this list.
The appended patch adds the functionality of lists that can be extended with
constants.
Then you get:
template1=# SET session AUTHORIZATION <tab>
DEFAULT fred joe john
I did proof-of-concept examples to add a constant to a
- list from a query
- list from a schema query
- list of table attributes
Joachim
diff -cr cvs/pgsql/src/bin/psql/tab-complete.c
cvs.build/pgsql/src/bin/psql/tab-complete.c
*** cvs/pgsql/src/bin/psql/tab-complete.c 2005-12-23 16:58:19.000000000
+0100
--- cvs.build/pgsql/src/bin/psql/tab-complete.c 2006-01-06 19:38:26.000000000
+0100
***************
*** 137,142 ****
--- 137,143 ----
3) The items from a null-pointer-terminated list.
4) A string constant
5) The list of attributes to the given table.
+ 6) A list that can contain several of the above (malloc'ed list).
*/
#define COMPLETE_WITH_QUERY(query) \
do { completion_charp = query; matches = completion_matches(text,
complete_from_query); } while(0)
***************
*** 150,155 ****
--- 151,171 ----
do {completion_charp = Query_for_list_of_attributes; completion_info_charp =
table; matches = completion_matches(text, complete_from_query); } while(0)
/*
+ * Keep the "malloced" keyword in all the names such that we remember that
+ * memory got allocated here. COMPLETE_WITH_MALLOCED_LIST frees this memory.
+ */
+ #define COMPLETE_WITH_MALLOCED_LIST(list) \
+ do { COMPLETE_WITH_LIST((const char**) list); free(list); list =
(char**) 0; } while(0)
+ #define MALLOCED_LIST_ADD_ITEM(list, item) \
+ ((list) = list_add_item((list), (item)))
+ #define GET_MALLOCED_LIST_WITH_ATTR(table) \
+ (get_query_list(text, Query_for_list_of_attributes, (table)))
+ #define GET_MALLOCED_LIST_WITH_QUERY(query) \
+ (get_query_list(text, (query), NULL))
+ #define GET_MALLOCED_LIST_WITH_SCHEMA_QUERY(query,addon) \
+ (get_schema_query_list(text, &(query), addon))
+
+ /*
* Assembly instructions for schema queries
*/
***************
*** 463,468 ****
--- 479,494 ----
/* Forward declaration of functions */
+ static char **get_empty_list();
+ static char **_get_query_list(int is_schema_query,
+ const char *text,
const char *query,
+ const char
*completion_info);
+ static char **get_query_list(const char *text, const char *query,
+ const char
*completion_info);
+ static char **get_schema_query_list(const char *text, const SchemaQuery*
squery,
+ const
char *completion_info);
+ static char **list_add_item(char **list, char *item);
+
static char **psql_completion(char *text, int start, int end);
static char *create_command_generator(const char *text, int state);
static char *complete_from_query(const char *text, int state);
***************
*** 754,760 ****
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
(pg_strcasecmp(prev_wd, "ALTER") == 0 ||
pg_strcasecmp(prev_wd, "RENAME") == 0))
! COMPLETE_WITH_ATTR(prev2_wd);
/* ALTER TABLE xxx RENAME yyy */
else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
--- 780,796 ----
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
(pg_strcasecmp(prev_wd, "ALTER") == 0 ||
pg_strcasecmp(prev_wd, "RENAME") == 0))
! {
! char** list = GET_MALLOCED_LIST_WITH_ATTR(prev2_wd);
! MALLOCED_LIST_ADD_ITEM(list, "COLUMN");
! COMPLETE_WITH_MALLOCED_LIST(list);
! }
! /* If we have TABLE <sth> ALTER COLUMN|RENAME COLUMN, provide list of
columns */
! else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
! pg_strcasecmp(prev_wd, "COLUMN") == 0 &&
! (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
! pg_strcasecmp(prev2_wd, "RENAME") == 0))
! COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER TABLE xxx RENAME yyy */
else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
***************
*** 762,767 ****
--- 798,810 ----
pg_strcasecmp(prev_wd, "TO") != 0)
COMPLETE_WITH_CONST("TO");
+ /* ALTER TABLE xxx RENAME COLUMN yyy */
+ else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
+ pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
+ pg_strcasecmp(prev2_wd, "COLUMN") == 0 &&
+ pg_strcasecmp(prev_wd, "TO") != 0)
+ COMPLETE_WITH_CONST("TO");
+
/* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "DROP") == 0)
***************
*** 1454,1461 ****
else if (pg_strcasecmp(prev_wd, "LOCK") == 0 ||
(pg_strcasecmp(prev_wd, "TABLE") == 0 &&
pg_strcasecmp(prev2_wd, "LOCK") == 0))
! COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
!
/* For the following, handle the case of a single table only for now */
/* Complete LOCK [TABLE] <table> with "IN" */
--- 1497,1510 ----
else if (pg_strcasecmp(prev_wd, "LOCK") == 0 ||
(pg_strcasecmp(prev_wd, "TABLE") == 0 &&
pg_strcasecmp(prev2_wd, "LOCK") == 0))
! {
! char** list =
GET_MALLOCED_LIST_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
! /* if we have only seen LOCK but not LOCK TABLE so far, offer
! * the TABLE keyword as well */
! if (pg_strcasecmp(prev_wd, "LOCK") == 0)
! MALLOCED_LIST_ADD_ITEM(list, "TABLE");
! COMPLETE_WITH_MALLOCED_LIST(list);
! }
/* For the following, handle the case of a single table only for now */
/* Complete LOCK [TABLE] <table> with "IN" */
***************
*** 1638,1644 ****
else if (pg_strcasecmp(prev3_wd, "SET") == 0
&& pg_strcasecmp(prev2_wd, "SESSION") == 0
&& pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
! COMPLETE_WITH_QUERY(Query_for_list_of_roles);
/* Complete RESET SESSION with AUTHORIZATION */
else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
pg_strcasecmp(prev_wd, "SESSION") == 0)
--- 1687,1697 ----
else if (pg_strcasecmp(prev3_wd, "SET") == 0
&& pg_strcasecmp(prev2_wd, "SESSION") == 0
&& pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
! {
! char** list =
GET_MALLOCED_LIST_WITH_QUERY(Query_for_list_of_roles);
! MALLOCED_LIST_ADD_ITEM(list, "DEFAULT");
! COMPLETE_WITH_MALLOCED_LIST(list);
! }
/* Complete RESET SESSION with AUTHORIZATION */
else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
pg_strcasecmp(prev_wd, "SESSION") == 0)
***************
*** 2315,2320 ****
--- 2368,2437 ----
}
+ /* LIST HELPER FUNCTIONS */
+
+ static char**
+ get_empty_list() {
+ char** list = malloc(sizeof(char*));
+ list[0] = NULL;
+ return list;
+ }
+
+ /* the following two functions are wrappers for _get_query_list */
+ static char**
+ get_schema_query_list(const char *text,
+ const SchemaQuery* squery,
+ const char* addon)
+ {
+ completion_squery = squery;
+ return _get_query_list(1, text, addon, NULL);
+ }
+
+ static char**
+ get_query_list(const char *text,
+ const char *query,
+ const char* completion_info)
+ {
+ return _get_query_list(0, text, query, completion_info);
+ }
+
+ static char**
+ _get_query_list(int is_schema_query,
+ const char *text,
+ const char *query,
+ const char* completion_info)
+ {
+ char **list = get_empty_list();
+ char *entry;
+ int state = 0;
+
+ completion_charp = query;
+ completion_info_charp = completion_info;
+
+ do {
+ if (is_schema_query)
+ entry = complete_from_schema_query(text, state++);
+ else
+ entry = complete_from_query(text, state++);
+ list = list_add_item(list, entry);
+ } while (entry);
+
+ return list;
+ }
+
+ static char**
+ list_add_item(char **list, char *item)
+ {
+ int size = 0;
+ while (list[size])
+ size++;
+ list = realloc(list, sizeof(char*) * (size + 1 + 1));
+ list[size] = item;
+ list[size+1] = NULL;
+ return list;
+ }
+
+
#if 0
/*
---------------------------(end of broadcast)---------------------------
TIP 6: explain analyze is your friend