Thread: psql tab completion enhancements

psql tab completion enhancements

From
Joachim Wieland
Date:
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


Attachment

Re: psql tab completion enhancements

From
Neil Conway
Date:
A few minor stylistic gripes...

On Fri, 2006-01-06 at 20:27 +0100, Joachim Wieland wrote:
> *** 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)

NULL is better style than 0 in a pointer context. Also, why are the
casts necessary?

>   /* Forward declaration of functions */
> + static char **get_empty_list();

Should be "static char **get_empty_list(void);", as C89 doesn't check
the parameters passed to a function declared with an empty parameter
list.

> *** 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);

Should be "char **list = ..." -- similarly in several other places.

> *** 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 */

Is this actually useful? The "TABLE" keyword is just noise.

> *** 2315,2320 ****
> --- 2368,2437 ----
>
>   }
>
> + /* LIST HELPER FUNCTIONS */
> +
> + static char**
> + get_empty_list() {
> +       char** list = malloc(sizeof(char*));
> +       list[0] = NULL;
> +       return list;
> + }

Brace style ("{" on newline), "char **", and "(void"), as above.

> + static char**
> + get_query_list(const char *text,
> +                          const char *query,
> +                          const char* completion_info)
> + {
> +       return _get_query_list(0, text, query, completion_info);
> + }

The difference between get_query_list() and _get_query_list() is not
reflected in the names of the methods. An "_internal" suffix would be
better, for example.

> + static char**
> + _get_query_list(int is_schema_query,
> +                               const char *text,
> +                               const char *query,
> +                               const char* completion_info)

"bool" for boolean variables rather than "int", and consistent parameter
declarations ("char *" not "char*").

> + 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;
> + }

That's a terribly inefficient implementation.

-Neil



Re: psql tab completion enhancements

From
Joachim Wieland
Date:
Neil,

thanks for your review. I accepted what you wrote for all items I don't
mention in this reply.

On Sat, Jan 07, 2006 at 10:59:27PM -0500, Neil Conway wrote:
> > + #define COMPLETE_WITH_MALLOCED_LIST(list) \
> > +       do { COMPLETE_WITH_LIST((const char**) list); free(list); list
> > = (char**) 0; } while(0)

> NULL is better style than 0 in a pointer context. Also, why are the
> casts necessary?

The cast "(const char**) list" is necessary because completion_charpp is
declared to be const. Removing the const means that you have to remove it
in a lot of other places as well, do you think this would be cleaner?


> > !               /* if we have only seen LOCK but not LOCK TABLE so
> > far, offer
> > !                * the TABLE keyword as well */

> Is this actually useful? The "TABLE" keyword is just noise.

Yeah, but it is valid SQL, so why not support it? I got used to use long
forms sometimes and get confused with psqls completion not supporting this
sometimes, for example in:

template1=# alter TABLE test ALTER column <TAB>
DROP DEFAULT    SET DEFAULT     SET STATISTICS  TYPE
DROP NOT NULL   SET NOT NULL    SET STORAGE



> > + static char**
> > + get_query_list(const char *text,
> > +                          const char *query,
> > +                          const char* completion_info)
> > + {
> > +       return _get_query_list(0, text, query, completion_info);
> > + }

> The difference between get_query_list() and _get_query_list() is not
> reflected in the names of the methods. An "_internal" suffix would be
> better, for example.

There are

static char *complete_from_query(const char *text, int state);
static char *complete_from_schema_query(const char *text, int state);
static char *_complete_from_query(int is_schema_query,
                     const char *text, int state);

already, so I made analogous versions for

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 **_get_query_list(int is_schema_query,
                              const char *text, const char *query,
                              const char *completion_info);


> "bool" for boolean variables rather than "int", and consistent parameter
> declarations ("char *" not "char*").

See above, I used the "int is_schema_query" because of
_complete_from_query().

I could change both to bool in a new patch if you want to?


> > + 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;
> > + }

> That's a terribly inefficient implementation.

It's not terribly inefficient, it just scales terribly. However psql limits
its queries to return 1000 items at most. At this size even a PII 300 adds
1000 items in

:/tmp$ time ./test
real    0m0.007s
user    0m0.000s
sys     0m0.000s

Program startup overhead for this example and doing the loop without any
work takes 0m0.004s already.

If the consensus is that it has to be better, I'll supply another version,
but I thought that it's not necessary.


Joachim


Re: psql tab completion enhancements

From
Bruce Momjian
Date:
I will adjust this patch based on later feedback emails.

Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

It will be applied as soon as one of the PostgreSQL committers reviews
and approves it.

---------------------------------------------------------------------------


Joachim Wieland wrote:
> 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
>

[ Attachment, skipping... ]

>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: explain analyze is your friend

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: psql tab completion enhancements

From
Joachim Wieland
Date:
On Fri, Feb 03, 2006 at 01:59:19PM -0500, Bruce Momjian wrote:
> I will adjust this patch based on later feedback emails.

I have another version of the patch. Minor style errors are corrected in it.

As far as I can see, open items are:

- Should all the

    const char* foocompletion = { "BAR", "BAZ" };

  be changed to

    char* foocompletion = { "BAR", "BAZ" };

  (including all functions that deal with that and take const char*'s now)
  or should a cast be used only for the growable-list-case.


- is the simple list_add_item() algorithm sufficient for the psql use case
  or not?


I append the version of the patch that I think is the latest I have.


Joachim


Attachment

Re: psql tab completion enhancements

From
Tom Lane
Date:
Joachim Wieland <joe@mcknight.de> writes:
> - Should all the
>     const char* foocompletion = { "BAR", "BAZ" };
>   be changed to
>     char* foocompletion = { "BAR", "BAZ" };

Removing const decoration sure seems like the wrong direction to be
going in.  Why can't you still have const on the functions that expect
to take non-modifiable lists?

            regards, tom lane

Re: psql tab completion enhancements

From
Joachim Wieland
Date:
On Fri, Feb 03, 2006 at 04:15:32PM -0500, Tom Lane wrote:
> > - Should all the
> >     const char* foocompletion = { "BAR", "BAZ" };
> >   be changed to
> >     char* foocompletion = { "BAR", "BAZ" };

> Removing const decoration sure seems like the wrong direction to be
> going in.  Why can't you still have const on the functions that expect
> to take non-modifiable lists?

ok, one could duplicate the functionality of complete_from_list() and add a
function complete_from_malloced_list() or similar (or refactor both such that
they call a third function that does the actual work - if possible). I will
do that if this is what you're proposing.


Joachim

Re: psql tab completion enhancements

From
Joachim Wieland
Date:
On Fri, Feb 03, 2006 at 04:15:32PM -0500, Tom Lane wrote:
> Joachim Wieland <joe@mcknight.de> writes:
> > - Should all the
> >     const char* foocompletion = { "BAR", "BAZ" };
> >   be changed to
> >     char* foocompletion = { "BAR", "BAZ" };

> Removing const decoration sure seems like the wrong direction to be
> going in.  Why can't you still have const on the functions that expect
> to take non-modifiable lists?

Ok, next try. Patch attached.

Joachim

Attachment

Re: psql tab completion enhancements

From
Bruce Momjian
Date:
Modified patch attached and applied.

Rather than create the lists in psql, I used UNION SELECT 'KEYWORD' to
pass the keyword to the backend to be added to the query list.

This was already being done for schema names, and was easy and efficient
to add.  My addition is even simpler because it just concatenates two
adjacent strings.  If you have more modifications, please use this
system.

If you find a macro that needs an 'addon', you can add it as a macro
parameter to all calls and just use "" if you don't need it.  If any
macro call uses a non-constant string, you have to make a new *_ADDON
macro version.

---------------------------------------------------------------------------

Joachim Wieland wrote:
> 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
>

[ Attachment, skipping... ]

>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: explain analyze is your friend

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: src/bin/psql/tab-complete.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v
retrieving revision 1.146
diff -c -c -r1.146 tab-complete.c
*** src/bin/psql/tab-complete.c    12 Feb 2006 03:22:19 -0000    1.146
--- src/bin/psql/tab-complete.c    12 Feb 2006 07:18:33 -0000
***************
*** 140,153 ****
  */
  #define COMPLETE_WITH_QUERY(query) \
  do { completion_charp = query; matches = completion_matches(text, complete_from_query); } while(0)
! #define COMPLETE_WITH_SCHEMA_QUERY(query,addon) \
  do { completion_squery = &(query); completion_charp = addon; matches = completion_matches(text,
complete_from_schema_query);} while(0) 
  #define COMPLETE_WITH_LIST(list) \
  do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0)
  #define COMPLETE_WITH_CONST(string) \
  do { completion_charp = string; matches = completion_matches(text, complete_from_const); } while(0)
! #define COMPLETE_WITH_ATTR(table) \
! do {completion_charp = Query_for_list_of_attributes; completion_info_charp = table; matches =
completion_matches(text,complete_from_query); } while(0) 

  /*
   * Assembly instructions for schema queries
--- 140,155 ----
  */
  #define COMPLETE_WITH_QUERY(query) \
  do { completion_charp = query; matches = completion_matches(text, complete_from_query); } while(0)
! #define COMPLETE_WITH_QUERY_ADDON(query, addon) \
! do { completion_charp = query addon; matches = completion_matches(text, complete_from_query); } while(0)
! #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
  do { completion_squery = &(query); completion_charp = addon; matches = completion_matches(text,
complete_from_schema_query);} while(0) 
  #define COMPLETE_WITH_LIST(list) \
  do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0)
  #define COMPLETE_WITH_CONST(string) \
  do { completion_charp = string; matches = completion_matches(text, complete_from_const); } while(0)
! #define COMPLETE_WITH_ATTR(table, addon) \
! do {completion_charp = Query_for_list_of_attributes addon; completion_info_charp = table; matches =
completion_matches(text,complete_from_query); } while(0) 

  /*
   * Assembly instructions for schema queries
***************
*** 754,767 ****
      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 &&
               pg_strcasecmp(prev2_wd, "RENAME") == 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)
--- 756,783 ----
      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, " UNION SELECT 'COLUMN'");

+     /* If we have TABLE <sth> ALTER COLUMN|RENAME COLUMN, provide list of columns */
+     else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
+              (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
+               pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
+              pg_strcasecmp(prev_wd, "COLUMN") == 0)
+         COMPLETE_WITH_ATTR(prev3_wd, "");
+
      /* ALTER TABLE xxx RENAME yyy */
      else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
               pg_strcasecmp(prev2_wd, "RENAME") == 0 &&
               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)
***************
*** 775,781 ****
      else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
               pg_strcasecmp(prev2_wd, "DROP") == 0 &&
               pg_strcasecmp(prev_wd, "COLUMN") == 0)
!         COMPLETE_WITH_ATTR(prev3_wd);
      /* ALTER TABLE ALTER [COLUMN] <foo> */
      else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
                pg_strcasecmp(prev2_wd, "COLUMN") == 0) ||
--- 791,797 ----
      else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
               pg_strcasecmp(prev2_wd, "DROP") == 0 &&
               pg_strcasecmp(prev_wd, "COLUMN") == 0)
!         COMPLETE_WITH_ATTR(prev3_wd, "");
      /* ALTER TABLE ALTER [COLUMN] <foo> */
      else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
                pg_strcasecmp(prev2_wd, "COLUMN") == 0) ||
***************
*** 1021,1038 ****
               pg_strcasecmp(prev2_wd, "ON") == 0)
      {
          if (find_open_parenthesis(end))
!             COMPLETE_WITH_ATTR(prev_wd);
          else
              COMPLETE_WITH_CONST("(");
      }
      else if (pg_strcasecmp(prev5_wd, "INDEX") == 0 &&
              pg_strcasecmp(prev3_wd, "ON") == 0 &&
              pg_strcasecmp(prev_wd, "(") == 0)
!         COMPLETE_WITH_ATTR(prev2_wd);
      /* same if you put in USING */
      else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
               pg_strcasecmp(prev2_wd, "USING") == 0)
!         COMPLETE_WITH_ATTR(prev3_wd);
      /* Complete USING with an index method */
      else if (pg_strcasecmp(prev_wd, "USING") == 0)
      {
--- 1037,1054 ----
               pg_strcasecmp(prev2_wd, "ON") == 0)
      {
          if (find_open_parenthesis(end))
!             COMPLETE_WITH_ATTR(prev_wd, "");
          else
              COMPLETE_WITH_CONST("(");
      }
      else if (pg_strcasecmp(prev5_wd, "INDEX") == 0 &&
              pg_strcasecmp(prev3_wd, "ON") == 0 &&
              pg_strcasecmp(prev_wd, "(") == 0)
!         COMPLETE_WITH_ATTR(prev2_wd, "");
      /* same if you put in USING */
      else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
               pg_strcasecmp(prev2_wd, "USING") == 0)
!         COMPLETE_WITH_ATTR(prev3_wd, "");
      /* Complete USING with an index method */
      else if (pg_strcasecmp(prev_wd, "USING") == 0)
      {
***************
*** 1420,1426 ****
      else if (rl_line_buffer[start - 1] == '(' &&
               pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
               pg_strcasecmp(prev2_wd, "INTO") == 0)
!         COMPLETE_WITH_ATTR(prev_wd);

      /*
       * Complete INSERT INTO <table> with "VALUES" or "SELECT" or "DEFAULT
--- 1436,1442 ----
      else if (rl_line_buffer[start - 1] == '(' &&
               pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
               pg_strcasecmp(prev2_wd, "INTO") == 0)
!         COMPLETE_WITH_ATTR(prev_wd, "");

      /*
       * Complete INSERT INTO <table> with "VALUES" or "SELECT" or "DEFAULT
***************
*** 1452,1461 ****

  /* LOCK */
      /* Complete LOCK [TABLE] with a list of tables */
!     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 */

--- 1468,1479 ----

  /* LOCK */
      /* Complete LOCK [TABLE] with a list of tables */
!     else if (pg_strcasecmp(prev_wd, "LOCK") == 0)
!         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
!                                    " UNION SELECT 'TABLE'");
!     else if (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
!              pg_strcasecmp(prev2_wd, "LOCK") == 0)
!         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");

      /* For the following, handle the case of a single table only for now */

***************
*** 1498,1504 ****
      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 &&
--- 1516,1522 ----
      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 &&
***************
*** 1639,1645 ****
      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)
--- 1657,1663 ----
      else if (pg_strcasecmp(prev3_wd, "SET") == 0
               && pg_strcasecmp(prev2_wd, "SESSION") == 0
               && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
!         COMPLETE_WITH_QUERY_ADDON(Query_for_list_of_roles, " UNION SELECT 'DEFAULT'");
      /* Complete RESET SESSION with AUTHORIZATION */
      else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
               pg_strcasecmp(prev_wd, "SESSION") == 0)
***************
*** 1706,1712 ****
       * make a list of attributes.
       */
      else if (pg_strcasecmp(prev_wd, "SET") == 0)
!         COMPLETE_WITH_ATTR(prev2_wd);

  /* UPDATE xx SET yy = */
      else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
--- 1724,1730 ----
       * make a list of attributes.
       */
      else if (pg_strcasecmp(prev_wd, "SET") == 0)
!         COMPLETE_WITH_ATTR(prev2_wd, "");

  /* UPDATE xx SET yy = */
      else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
***************
*** 1763,1769 ****
  /* WHERE */
      /* Simple case of the word before the where being the table name */
      else if (pg_strcasecmp(prev_wd, "WHERE") == 0)
!         COMPLETE_WITH_ATTR(prev2_wd);

  /* ... FROM ... */
  /* TODO: also include SRF ? */
--- 1781,1787 ----
  /* WHERE */
      /* Simple case of the word before the where being the table name */
      else if (pg_strcasecmp(prev_wd, "WHERE") == 0)
!         COMPLETE_WITH_ATTR(prev2_wd, "");

  /* ... FROM ... */
  /* TODO: also include SRF ? */

Re: psql tab completion enhancements

From
Bruce Momjian
Date:
I found it was easier to concatenate the "UNION" at the macro call site,
rather than the macro body.  This eliminates the extra macro.  Patch
attached and applied.

---------------------------------------------------------------------------

pgman wrote:
>
> Modified patch attached and applied.
>
> Rather than create the lists in psql, I used UNION SELECT 'KEYWORD' to
> pass the keyword to the backend to be added to the query list.
>
> This was already being done for schema names, and was easy and efficient
> to add.  My addition is even simpler because it just concatenates two
> adjacent strings.  If you have more modifications, please use this
> system.
>
> If you find a macro that needs an 'addon', you can add it as a macro
> parameter to all calls and just use "" if you don't need it.  If any
> macro call uses a non-constant string, you have to make a new *_ADDON
> macro version.
>
> ---------------------------------------------------------------------------
>
> Joachim Wieland wrote:
> > 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

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: src/bin/psql/tab-complete.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v
retrieving revision 1.147
diff -c -c -r1.147 tab-complete.c
*** src/bin/psql/tab-complete.c    12 Feb 2006 07:21:40 -0000    1.147
--- src/bin/psql/tab-complete.c    12 Feb 2006 15:05:09 -0000
***************
*** 140,147 ****
  */
  #define COMPLETE_WITH_QUERY(query) \
  do { completion_charp = query; matches = completion_matches(text, complete_from_query); } while(0)
- #define COMPLETE_WITH_QUERY_ADDON(query, addon) \
- do { completion_charp = query addon; matches = completion_matches(text, complete_from_query); } while(0)
  #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
  do { completion_squery = &(query); completion_charp = addon; matches = completion_matches(text,
complete_from_schema_query);} while(0) 
  #define COMPLETE_WITH_LIST(list) \
--- 140,145 ----
***************
*** 1657,1663 ****
      else if (pg_strcasecmp(prev3_wd, "SET") == 0
               && pg_strcasecmp(prev2_wd, "SESSION") == 0
               && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
!         COMPLETE_WITH_QUERY_ADDON(Query_for_list_of_roles, " UNION SELECT 'DEFAULT'");
      /* Complete RESET SESSION with AUTHORIZATION */
      else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
               pg_strcasecmp(prev_wd, "SESSION") == 0)
--- 1655,1661 ----
      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 " UNION SELECT 'DEFAULT'");
      /* Complete RESET SESSION with AUTHORIZATION */
      else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
               pg_strcasecmp(prev_wd, "SESSION") == 0)

Re: psql tab completion enhancements

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> I found it was easier to concatenate the "UNION" at the macro call site,
> rather than the macro body.  This eliminates the extra macro.  Patch
> attached and applied.

Yeah, that looks like a much safer idea --- avoids making assumptions
about the behavior of the macro expander.

            regards, tom lane