]> granicus.if.org Git - postgresql/commitdiff
Write psql's ~/.psql_history file using history_truncate_file() and
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 13 Sep 2009 22:18:22 +0000 (22:18 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 13 Sep 2009 22:18:22 +0000 (22:18 +0000)
append_history(), if libreadline is new enough to have those functions
(they seem to be present at least since 4.2; but libedit may not have them).
This gives significantly saner behavior when two or more sessions overlap in
their use of the history file; although having two sessions exit at just the
same time is still perilous to your history.  The behavior of \s remains
unchanged, ie, overwrite whatever was there.
Per bug #5052 from Marek Wójtowicz.

configure
configure.in
src/bin/psql/command.c
src/bin/psql/input.c
src/bin/psql/input.h
src/include/pg_config.h.in
src/include/pg_config.h.win32

index c64f2888d823e69353c806d64bf6494d4e4d90c9..07cc74c729d2306952885fdde717d3edc64a9dd1 100755 (executable)
--- a/configure
+++ b/configure
@@ -19453,7 +19453,8 @@ fi
 done
 
 
-for ac_func in replace_history_entry
+
+for ac_func in append_history history_truncate_file
 do
 as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
index fe76ea1daf633f86e524e54502b8b69c71c6997a..d8cf7ea5d0fa92178b49b79ab40be58195ce58ac 100644 (file)
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-dnl $PostgreSQL: pgsql/configure.in,v 1.610 2009/09/08 16:08:26 tgl Exp $
+dnl $PostgreSQL: pgsql/configure.in,v 1.611 2009/09/13 22:18:22 tgl Exp $
 dnl
 dnl Developers, please strive to achieve this order:
 dnl
@@ -1316,7 +1316,7 @@ fi
 if test "$with_readline" = yes; then
   PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER
   AC_CHECK_FUNCS([rl_completion_matches rl_filename_completion_function])
-  AC_CHECK_FUNCS([replace_history_entry])
+  AC_CHECK_FUNCS([append_history history_truncate_file])
 fi
 
 
index 7c815b8f977f5cd39ce535c43bee85f501cd25e7..67f05a89de0e5c1c0e385d3509e878000621a374 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.206 2009/06/11 14:49:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.207 2009/09/13 22:18:22 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "command.h"
@@ -908,7 +908,7 @@ exec_command(const char *cmd,
 
                expand_tilde(&fname);
                /* This scrolls off the screen when using /dev/tty */
-               success = saveHistory(fname ? fname : DEVTTY, false);
+               success = saveHistory(fname ? fname : DEVTTY, -1, false, false);
                if (success && !pset.quiet && fname)
                        printf(gettext("Wrote history to file \"%s/%s\".\n"),
                                   pset.dirname ? pset.dirname : ".", fname);
index d17c9abb95aa4a73b4f12b3de60528c2738465bd..86409398f5bac0b7e3348f3c343d8d24cf872df5 100644 (file)
@@ -3,10 +3,15 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.66 2009/01/01 17:23:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.67 2009/09/13 22:18:22 tgl Exp $
  */
 #include "postgres_fe.h"
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
 #include "input.h"
 #include "settings.h"
 #include "tab-complete.h"
 #ifdef USE_READLINE
 static bool useReadline;
 static bool useHistory;
-char      *psql_history;
+
+static char *psql_history;
+
+static int     history_lines_added;
+
 
 /*
  *     Preserve newlines in saved queries by mapping '\n' to NL_IN_HISTORY
@@ -135,6 +144,8 @@ pg_send_history(PQExpBuffer history_buf)
                        prev_hist = pg_strdup(s);
                        /* And send it to readline */
                        add_history(s);
+                       /* Count lines added to history for use later */
+                       history_lines_added++;
                }
        }
 
@@ -276,6 +287,7 @@ initializeInput(int flags)
 
                useHistory = true;
                using_history();
+               history_lines_added = 0;
 
                histfile = GetVariable(pset.vars, "HISTFILE");
                if (histfile == NULL)
@@ -310,15 +322,22 @@ initializeInput(int flags)
 
 
 /*
- * This function is for saving the readline history when user
- * runs \s command or when psql finishes.
+ * This function saves the readline history when user
+ * runs \s command or when psql exits.
+ *
+ * fname: pathname of history file.  (Should really be "const char *",
+ * but some ancient versions of readline omit the const-decoration.)
+ *
+ * max_lines: if >= 0, limit history file to that many entries.
  *
- * We have an argument named encodeFlag to handle the cases differently.
- * In case of call via \s we don't really need to encode \n as \x01,
- * but when we save history for Readline we must do that conversion.
+ * appendFlag: if true, try to append just our new lines to the file.
+ * If false, write the whole available history.
+ *
+ * encodeFlag: whether to encode \n as \x01.  For \s calls we don't wish
+ * to do that, but must do so when saving the final history file.
  */
 bool
-saveHistory(char *fname, bool encodeFlag)
+saveHistory(char *fname, int max_lines, bool appendFlag, bool encodeFlag)
 {
 #ifdef USE_READLINE
 
@@ -335,14 +354,54 @@ saveHistory(char *fname, bool encodeFlag)
                        encode_history();
 
                /*
-                * return value of write_history is not standardized across GNU
+                * On newer versions of libreadline, truncate the history file as
+                * needed and then append what we've added.  This avoids overwriting
+                * history from other concurrent sessions (although there are still
+                * race conditions when two sessions exit at about the same time).
+                * If we don't have those functions, fall back to write_history().
+                *
+                * Note: return value of write_history is not standardized across GNU
                 * readline and libedit.  Therefore, check for errno becoming set to
-                * see if the write failed.
+                * see if the write failed.  Similarly for append_history.
                 */
-               errno = 0;
-               (void) write_history(fname);
-               if (errno == 0)
-                       return true;
+#if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY)
+               if (appendFlag)
+               {
+                       int             nlines;
+                       int             fd;
+
+                       /* truncate previous entries if needed */
+                       if (max_lines >= 0)
+                       {
+                               nlines = Max(max_lines - history_lines_added, 0);
+                               (void) history_truncate_file(fname, nlines);
+                       }
+                       /* append_history fails if file doesn't already exist :-( */
+                       fd = open(fname, O_CREAT | O_WRONLY | PG_BINARY, 0600);
+                       if (fd >= 0)
+                               close(fd);
+                       /* append the appropriate number of lines */
+                       if (max_lines >= 0)
+                               nlines = Min(max_lines, history_lines_added);
+                       else
+                               nlines = history_lines_added;
+                       errno = 0;
+                       (void) append_history(nlines, fname);
+                       if (errno == 0)
+                               return true;
+               }
+               else
+#endif
+               {
+                       /* truncate what we have ... */
+                       if (max_lines >= 0)
+                               stifle_history(max_lines);
+                       /* ... and overwrite file.  Tough luck for concurrent sessions. */
+                       errno = 0;
+                       (void) write_history(fname);
+                       if (errno == 0)
+                               return true;
+               }
 
                psql_error("could not save history to file \"%s\": %s\n",
                                   fname, strerror(errno));
@@ -369,10 +428,7 @@ finishInput(int exitstatus, void *arg)
                int                     hist_size;
 
                hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true);
-               if (hist_size >= 0)
-                       stifle_history(hist_size);
-
-               saveHistory(psql_history, true);
+               saveHistory(psql_history, hist_size, true, true);
                free(psql_history);
                psql_history = NULL;
        }
index b2515c42f86001a640919a61343085938dcecda8..515445445aee9cd59e9ffe1a650627862bb00a31 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/input.h,v 1.32 2009/08/24 16:18:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/input.h,v 1.33 2009/09/13 22:18:22 tgl Exp $
  */
 #ifndef INPUT_H
 #define INPUT_H
@@ -45,7 +45,7 @@ char     *gets_interactive(const char *prompt);
 char      *gets_fromFile(FILE *source);
 
 void           initializeInput(int flags);
-bool           saveHistory(char *fname, bool encodeFlag);
+bool           saveHistory(char *fname, int max_lines, bool appendFlag, bool encodeFlag);
 
 void           pg_append_history(const char *s, PQExpBuffer history_buf);
 void           pg_send_history(PQExpBuffer history_buf);
index ba807f4275057878d216dbfe50d72cbdc6dbfb8b..895e64b1444d319d7213ee51db430ed13e11c1cd 100644 (file)
@@ -78,6 +78,9 @@
 # define gettimeofday(a,b) gettimeofday(a)
 #endif
 
+/* Define to 1 if you have the `append_history' function. */
+#undef HAVE_APPEND_HISTORY
+
 /* Define to 1 if you have the `atexit' function. */
 #undef HAVE_ATEXIT
 
 /* Define to 1 if you have the <history.h> header file. */
 #undef HAVE_HISTORY_H
 
+/* Define to 1 if you have the `history_truncate_file' function. */
+#undef HAVE_HISTORY_TRUNCATE_FILE
+
 /* Define to 1 if you have the <ieeefp.h> header file. */
 #undef HAVE_IEEEFP_H
 
 /* Define to 1 if you have the `readlink' function. */
 #undef HAVE_READLINK
 
-/* Define to 1 if you have the `replace_history_entry' function. */
-#undef HAVE_REPLACE_HISTORY_ENTRY
-
 /* Define to 1 if you have the `rint' function. */
 #undef HAVE_RINT
 
index 098bf20bad0d14f45efe426d1cf973ba116bfaf4..a3b3b7374b05d43b36f0e1e5a902dd2d515c092c 100644 (file)
 /* Define to 1 if you have the `readlink' function. */
 /* #undef HAVE_READLINK */
 
-/* Define to 1 if you have the `replace_history_entry' function. */
-/* #undef HAVE_REPLACE_HISTORY_ENTRY */
-
 /* Define to 1 if you have the `rint' function. */
 /*#define HAVE_RINT 1*/