Thread: psql: customizable readline history filename

psql: customizable readline history filename

From
Andreas Seltenreich
Date:
Hi,

the following patch makes the filename used to store the readline
history customizable through a variable named HISTFILE, analogous to
psql's already implemented HISTCONTROL and HISTSIZE variables, and
bash's HISTFILE-Variable.

The motivation was to be able to get psql to maintain separate
histories for separate databases.  This is now easily achievable
through a line like the following in ~/.psqlrc:

\set HISTFILE ~/.psql_history- :DBNAME

regards,
Andreas

Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.123
diff -c -r1.123 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml    6 Oct 2004 18:39:15 -0000    1.123
--- doc/src/sgml/ref/psql-ref.sgml    26 Oct 2004 05:57:57 -0000
***************
*** 1973,1978 ****
--- 1973,2001 ----
        </varlistentry>

        <varlistentry>
+         <term><varname>HISTFILE</varname></term>
+         <listitem>
+         <para>
+     This variable contains the filename used to save the history.
+         Its default value is <filename>~/.psql_history</filename>.
+         When unset or empty, the command history is not saved upon
+         program termination.  For example,
+ <programlisting>
+ \set HISTFILE ~/.psql_history- :DBNAME
+ </programlisting>
+         in your <filename>~/.psqlrc</filename> will get psql to
+         maintain a separate history for each database.
+         </para>
+         <note>
+         <para>
+         This feature was shamelessly plagiarized from
+         <application>Bash</application>.
+         </para>
+         </note>
+         </listitem>
+       </varlistentry>
+
+       <varlistentry>
          <term><varname>HISTSIZE</varname></term>
          <listitem>
          <para>
Index: src/bin/psql/input.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/input.c,v
retrieving revision 1.41
diff -c -r1.41 input.c
*** src/bin/psql/input.c    12 Oct 2004 21:54:44 -0000    1.41
--- src/bin/psql/input.c    26 Oct 2004 05:57:57 -0000
***************
*** 38,46 ****
  static void finishInput(int, void *);
  #endif

- #define PSQLHISTORY ".psql_history"
-
-
  #ifdef USE_READLINE
  static enum histcontrol
  GetHistControlConfig(void)
--- 38,43 ----
***************
*** 167,173 ****
  #ifdef USE_READLINE
      if (flags & 1)
      {
!         char        home[MAXPGPATH];

          useReadline = true;
          initialize_readline();
--- 164,171 ----
  #ifdef USE_READLINE
      if (flags & 1)
      {
!         const char *psql_history;
!         char       *tilde_expanded;

          useReadline = true;
          initialize_readline();
***************
*** 176,191 ****
          if (GetVariable(pset.vars, "HISTSIZE") == NULL)
              SetVariable(pset.vars, "HISTSIZE", "500");
          using_history();
-         if (get_home_path(home))
-         {
-             char       *psql_history;

!             psql_history = pg_malloc(strlen(home) + 1 +
!                                      strlen(PSQLHISTORY) + 1);
!             sprintf(psql_history, "%s/%s", home, PSQLHISTORY);
!             read_history(psql_history);
!             free(psql_history);
!         }
      }
  #endif

--- 174,189 ----
          if (GetVariable(pset.vars, "HISTSIZE") == NULL)
              SetVariable(pset.vars, "HISTSIZE", "500");
          using_history();

!         if (GetVariable(pset.vars, "HISTFILE") == NULL)
!             SetVariable(pset.vars, "HISTFILE", "~/.psql_history");
!
!         psql_history = GetVariable(pset.vars, "HISTFILE");
!
!         tilde_expanded = pg_strdup(psql_history);
!         expand_tilde(&tilde_expanded);
!         read_history(tilde_expanded);
!         free(tilde_expanded);
      }
  #endif

***************
*** 228,252 ****
  #ifdef USE_READLINE
      if (useHistory)
      {
!         char        home[MAXPGPATH];
!
!         if (get_home_path(home))
!         {
!             char       *psql_history;
!             int            hist_size;

!             psql_history = pg_malloc(strlen(home) + 1 +
!                                      strlen(PSQLHISTORY) + 1);

!             hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true);

!             if (hist_size >= 0)
!                 stifle_history(hist_size);
!
!             sprintf(psql_history, "%s/%s", home, PSQLHISTORY);
!             write_history(psql_history);
!             free(psql_history);
!         }
      }
  #endif
  }
--- 226,251 ----
  #ifdef USE_READLINE
      if (useHistory)
      {
!         const char *psql_history;
!         char       *tilde_expanded;
!         int            hist_size;
!
!         psql_history = GetVariable(pset.vars, "HISTFILE");
!
!         if (!psql_history || !strlen(psql_history))
!             return;
!
!         tilde_expanded = pg_strdup(psql_history);
!         expand_tilde(&tilde_expanded);
!
!         hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true);

!         if (hist_size >= 0)
!             stifle_history(hist_size);

!         saveHistory(tilde_expanded);

!         free(tilde_expanded);
      }
  #endif
  }

Re: psql: customizable readline history filename

From
Bruce Momjian
Date:
This has been saved for the 8.1 release:

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

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

Andreas Seltenreich wrote:
> Hi,
>
> the following patch makes the filename used to store the readline
> history customizable through a variable named HISTFILE, analogous to
> psql's already implemented HISTCONTROL and HISTSIZE variables, and
> bash's HISTFILE-Variable.
>
> The motivation was to be able to get psql to maintain separate
> histories for separate databases.  This is now easily achievable
> through a line like the following in ~/.psqlrc:
>
> \set HISTFILE ~/.psql_history- :DBNAME
>
> regards,
> Andreas
>

> Index: doc/src/sgml/ref/psql-ref.sgml
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
> retrieving revision 1.123
> diff -c -r1.123 psql-ref.sgml
> *** doc/src/sgml/ref/psql-ref.sgml    6 Oct 2004 18:39:15 -0000    1.123
> --- doc/src/sgml/ref/psql-ref.sgml    26 Oct 2004 05:57:57 -0000
> ***************
> *** 1973,1978 ****
> --- 1973,2001 ----
>         </varlistentry>
>
>         <varlistentry>
> +         <term><varname>HISTFILE</varname></term>
> +         <listitem>
> +         <para>
> +     This variable contains the filename used to save the history.
> +         Its default value is <filename>~/.psql_history</filename>.
> +         When unset or empty, the command history is not saved upon
> +         program termination.  For example,
> + <programlisting>
> + \set HISTFILE ~/.psql_history- :DBNAME
> + </programlisting>
> +         in your <filename>~/.psqlrc</filename> will get psql to
> +         maintain a separate history for each database.
> +         </para>
> +         <note>
> +         <para>
> +         This feature was shamelessly plagiarized from
> +         <application>Bash</application>.
> +         </para>
> +         </note>
> +         </listitem>
> +       </varlistentry>
> +
> +       <varlistentry>
>           <term><varname>HISTSIZE</varname></term>
>           <listitem>
>           <para>
> Index: src/bin/psql/input.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/bin/psql/input.c,v
> retrieving revision 1.41
> diff -c -r1.41 input.c
> *** src/bin/psql/input.c    12 Oct 2004 21:54:44 -0000    1.41
> --- src/bin/psql/input.c    26 Oct 2004 05:57:57 -0000
> ***************
> *** 38,46 ****
>   static void finishInput(int, void *);
>   #endif
>
> - #define PSQLHISTORY ".psql_history"
> -
> -
>   #ifdef USE_READLINE
>   static enum histcontrol
>   GetHistControlConfig(void)
> --- 38,43 ----
> ***************
> *** 167,173 ****
>   #ifdef USE_READLINE
>       if (flags & 1)
>       {
> !         char        home[MAXPGPATH];
>
>           useReadline = true;
>           initialize_readline();
> --- 164,171 ----
>   #ifdef USE_READLINE
>       if (flags & 1)
>       {
> !         const char *psql_history;
> !         char       *tilde_expanded;
>
>           useReadline = true;
>           initialize_readline();
> ***************
> *** 176,191 ****
>           if (GetVariable(pset.vars, "HISTSIZE") == NULL)
>               SetVariable(pset.vars, "HISTSIZE", "500");
>           using_history();
> -         if (get_home_path(home))
> -         {
> -             char       *psql_history;
>
> !             psql_history = pg_malloc(strlen(home) + 1 +
> !                                      strlen(PSQLHISTORY) + 1);
> !             sprintf(psql_history, "%s/%s", home, PSQLHISTORY);
> !             read_history(psql_history);
> !             free(psql_history);
> !         }
>       }
>   #endif
>
> --- 174,189 ----
>           if (GetVariable(pset.vars, "HISTSIZE") == NULL)
>               SetVariable(pset.vars, "HISTSIZE", "500");
>           using_history();
>
> !         if (GetVariable(pset.vars, "HISTFILE") == NULL)
> !             SetVariable(pset.vars, "HISTFILE", "~/.psql_history");
> !
> !         psql_history = GetVariable(pset.vars, "HISTFILE");
> !
> !         tilde_expanded = pg_strdup(psql_history);
> !         expand_tilde(&tilde_expanded);
> !         read_history(tilde_expanded);
> !         free(tilde_expanded);
>       }
>   #endif
>
> ***************
> *** 228,252 ****
>   #ifdef USE_READLINE
>       if (useHistory)
>       {
> !         char        home[MAXPGPATH];
> !
> !         if (get_home_path(home))
> !         {
> !             char       *psql_history;
> !             int            hist_size;
>
> !             psql_history = pg_malloc(strlen(home) + 1 +
> !                                      strlen(PSQLHISTORY) + 1);
>
> !             hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true);
>
> !             if (hist_size >= 0)
> !                 stifle_history(hist_size);
> !
> !             sprintf(psql_history, "%s/%s", home, PSQLHISTORY);
> !             write_history(psql_history);
> !             free(psql_history);
> !         }
>       }
>   #endif
>   }
> --- 226,251 ----
>   #ifdef USE_READLINE
>       if (useHistory)
>       {
> !         const char *psql_history;
> !         char       *tilde_expanded;
> !         int            hist_size;
> !
> !         psql_history = GetVariable(pset.vars, "HISTFILE");
> !
> !         if (!psql_history || !strlen(psql_history))
> !             return;
> !
> !         tilde_expanded = pg_strdup(psql_history);
> !         expand_tilde(&tilde_expanded);
> !
> !         hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true);
>
> !         if (hist_size >= 0)
> !             stifle_history(hist_size);
>
> !         saveHistory(tilde_expanded);
>
> !         free(tilde_expanded);
>       }
>   #endif
>   }

>
> ---------------------------(end of broadcast)---------------------------
> TIP 7: don't forget to increase your free space map settings

--
  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: customizable readline history filename

From
Bruce Momjian
Date:
Andreas Seltenreich wrote:
> Hi,
>
> the following patch makes the filename used to store the readline
> history customizable through a variable named HISTFILE, analogous to
> psql's already implemented HISTCONTROL and HISTSIZE variables, and
> bash's HISTFILE-Variable.
>
> The motivation was to be able to get psql to maintain separate
> histories for separate databases.  This is now easily achievable
> through a line like the following in ~/.psqlrc:
>
> \set HISTFILE ~/.psql_history- :DBNAME

I have applied your patch with slight modifications; new version
attached.

Win32 doesn't have tilde expansion, so your idea of using
"~/.psql_history" as a default would not work --- I had to keep the
get_home_path() code in there.

I decided to make psql_history a file static variable, so we would not
have to recompute its value when writing the history file.

I noticed your documentation example had a space before :DBNAME --- I
removed the space.

You had the idea of not saving the history on exit if HISTFILE is not
set.  I don't think we have community agreement on that change, and I
bet we wouldn't get it.  (I have removed your documentation mention of
this.)  I think setting HISTSIZE to zero has that effect already if
people want it.

--
  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: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.139
diff -c -c -r1.139 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml    9 Jun 2005 15:27:26 -0000    1.139
--- doc/src/sgml/ref/psql-ref.sgml    10 Jun 2005 15:33:22 -0000
***************
*** 1988,1993 ****
--- 1988,2015 ----
        </varlistentry>

        <varlistentry>
+         <term><varname>HISTFILE</varname></term>
+         <listitem>
+         <para>
+         This variable contains the filename used to save the history.
+         Its default value is <filename>~/.psql_history</filename>.
+         For example, use:
+ <programlisting>
+ \set HISTFILE ~/.psql_history-:DBNAME
+ </programlisting>
+         in your <filename>~/.psqlrc</filename> will get psql to
+         maintain a separate history for each database.
+         </para>
+         <note>
+         <para>
+         This feature was shamelessly plagiarized from
+         <application>Bash</application>.
+         </para>
+         </note>
+         </listitem>
+       </varlistentry>
+
+       <varlistentry>
          <term><varname>HISTSIZE</varname></term>
          <listitem>
          <para>
Index: src/bin/psql/input.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/input.c,v
retrieving revision 1.43
diff -c -c -r1.43 input.c
*** src/bin/psql/input.c    6 Jan 2005 18:29:09 -0000    1.43
--- src/bin/psql/input.c    10 Jun 2005 15:33:23 -0000
***************
*** 24,29 ****
--- 24,31 ----
  #ifdef USE_READLINE
  static bool useReadline;
  static bool useHistory;
+ char  *psql_history;
+

  enum histcontrol
  {
***************
*** 177,192 ****
          if (GetVariable(pset.vars, "HISTSIZE") == NULL)
              SetVariable(pset.vars, "HISTSIZE", "500");
          using_history();
!         if (get_home_path(home))
          {
!             char       *psql_history;

!             psql_history = pg_malloc(strlen(home) + 1 +
!                                      strlen(PSQLHISTORY) + 1);
!             sprintf(psql_history, "%s/%s", home, PSQLHISTORY);
              read_history(psql_history);
-             free(psql_history);
-         }
      }
  #endif

--- 179,202 ----
          if (GetVariable(pset.vars, "HISTSIZE") == NULL)
              SetVariable(pset.vars, "HISTSIZE", "500");
          using_history();
!
!         if (GetVariable(pset.vars, "HISTFILE") == NULL)
!         {
!             if (get_home_path(home))
!             {
!                 psql_history = pg_malloc(strlen(home) + 1 +
!                                          strlen(PSQLHISTORY) + 1);
!                 snprintf(psql_history, MAXPGPATH, "%s/%s", home, PSQLHISTORY);
!             }
!         }
!         else
          {
!             psql_history = pg_strdup(GetVariable(pset.vars, "HISTFILE"));
!             expand_tilde(&psql_history);
!         }

!         if (psql_history)
              read_history(psql_history);
      }
  #endif

***************
*** 227,251 ****
  #endif
  {
  #ifdef USE_READLINE
!     if (useHistory)
      {
!         char        home[MAXPGPATH];

!         if (get_home_path(home))
!         {
!             char       *psql_history;
!             int            hist_size;
!
!             hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true);
!             if (hist_size >= 0)
!                 stifle_history(hist_size);
!
!             psql_history = pg_malloc(strlen(home) + 1 +
!                                      strlen(PSQLHISTORY) + 1);
!             sprintf(psql_history, "%s/%s", home, PSQLHISTORY);
!             write_history(psql_history);
!             free(psql_history);
!         }
      }
  #endif
  }
--- 237,253 ----
  #endif
  {
  #ifdef USE_READLINE
!     if (useHistory && psql_history)
      {
!         int            hist_size;

!         hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true);
!         if (hist_size >= 0)
!             stifle_history(hist_size);
!
!         saveHistory(psql_history);
!         free(psql_history);
!         psql_history = NULL;
      }
  #endif
  }

Re: psql: customizable readline history filename

From
Andreas Seltenreich
Date:
Bruce Momjian schrob:

> I noticed your documentation example had a space before :DBNAME --- I
> removed the space.

This space is necessary for proper expansion of the parameter. The
\set-Command concatenates all its arguments after expansion:

--8<---------------cut here---------------start------------->8---
scratch=# \set HISTFILE .psql_history-:DBNAME
scratch=# \echo :HISTFILE
.psql_history-:DBNAME
scratch=# \set HISTFILE .psql_history- :DBNAME
scratch=# \echo :HISTFILE
.psql_history-scratch
scratch=#
--8<---------------cut here---------------end--------------->8---

> You had the idea of not saving the history on exit if HISTFILE is not
> set.  I don't think we have community agreement on that change, and I
> bet we wouldn't get it.  (I have removed your documentation mention of
> this.)  I think setting HISTSIZE to zero has that effect already if
> people want it.

I went for bash's behaviour back then, since we copied bash's
semantics with the other history options too.

Thanks!
Andreas

Re: psql: customizable readline history filename

From
Bruce Momjian
Date:
Andreas Seltenreich wrote:
> Bruce Momjian schrob:
>
> > I noticed your documentation example had a space before :DBNAME --- I
> > removed the space.
>
> This space is necessary for proper expansion of the parameter. The
> \set-Command concatenates all its arguments after expansion:
>
> --8<---------------cut here---------------start------------->8---
> scratch=# \set HISTFILE .psql_history-:DBNAME
> scratch=# \echo :HISTFILE
> .psql_history-:DBNAME
> scratch=# \set HISTFILE .psql_history- :DBNAME
> scratch=# \echo :HISTFILE
> .psql_history-scratch
> scratch=#
> --8<---------------cut here---------------end--------------->8---
>
> > You had the idea of not saving the history on exit if HISTFILE is not
> > set.  I don't think we have community agreement on that change, and I
> > bet we wouldn't get it.  (I have removed your documentation mention of
> > this.)  I think setting HISTSIZE to zero has that effect already if
> > people want it.
>
> I went for bash's behaviour back then, since we copied bash's
> semantics with the other history options too.

Ah, I see.  I tested ':' expansion in queries and found the space wasn't
needed, but I see now that with \set it is needed:

    test=> \set x a
    test=> \echo :x
    a
    test=> \set y b:x
    test=> \echo :y
    b:x
    test=> \set y b :x
    test=> \echo :y
    ba

I will update the documentation to add the space.  Thanks.

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