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

Reply via email to