summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2009-09-13 22:18:22 +0000
committerTom Lane2009-09-13 22:18:22 +0000
commit7e7b2aac1a7a8af020a80c809b1ad0358a3ad4b6 (patch)
tree01ca4e151349713e12b168c536bdfb5528ca8ee6
parentc2379bc7916e373bf2e263e87072af783c8a9e7e (diff)
Write psql's ~/.psql_history file using history_truncate_file() and
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.
-rwxr-xr-xconfigure3
-rw-r--r--configure.in2
-rw-r--r--src/bin/psql/command.c2
-rw-r--r--src/bin/psql/input.c90
-rw-r--r--src/bin/psql/input.h2
-rw-r--r--src/include/pg_config.h.in9
-rw-r--r--src/include/pg_config.h.win323
7 files changed, 84 insertions, 27 deletions
diff --git a/configure b/configure
index c64f2888d8..07cc74c729 100755
--- 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
diff --git a/configure.in b/configure.in
index 87b5f8dbc5..e545a1f682 100644
--- a/configure.in
+++ b/configure.in
@@ -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
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 0955e13d89..da57eb4b29 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -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);
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index ede19d4a14..60bce5662e 100644
--- a/src/bin/psql/input.c
+++ b/src/bin/psql/input.c
@@ -7,6 +7,11 @@
*/
#include "postgres_fe.h"
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
#include "input.h"
#include "settings.h"
#include "tab-complete.h"
@@ -23,7 +28,11 @@
#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;
}
diff --git a/src/bin/psql/input.h b/src/bin/psql/input.h
index d2f8f40fe0..b0ed6eb535 100644
--- a/src/bin/psql/input.h
+++ b/src/bin/psql/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);
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index ba807f4275..895e64b144 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -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
@@ -212,6 +215,9 @@
/* 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
@@ -378,9 +384,6 @@
/* 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
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 098bf20bad..a3b3b7374b 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -306,9 +306,6 @@
/* 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*/