diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2009-09-13 22:18:22 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2009-09-13 22:18:22 +0000 |
commit | e97281c46cb14ecb91c473b6bd2b0435a50c0875 (patch) | |
tree | 82490966eb37a17515419cd61a42e7a58c34e201 | |
parent | eb62398f391eedee7953becb410bf3ae86b9872b (diff) | |
download | postgresql-e97281c46cb14ecb91c473b6bd2b0435a50c0875.tar.gz postgresql-e97281c46cb14ecb91c473b6bd2b0435a50c0875.zip |
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-x | configure | 3 | ||||
-rw-r--r-- | configure.in | 4 | ||||
-rw-r--r-- | src/bin/psql/command.c | 4 | ||||
-rw-r--r-- | src/bin/psql/input.c | 92 | ||||
-rw-r--r-- | src/bin/psql/input.h | 4 | ||||
-rw-r--r-- | src/include/pg_config.h.in | 9 | ||||
-rw-r--r-- | src/include/pg_config.h.win32 | 3 |
7 files changed, 88 insertions, 31 deletions
diff --git a/configure b/configure index c64f2888d82..07cc74c729d 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 fe76ea1daf6..d8cf7ea5d0f 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 7c815b8f977..67f05a89de0 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -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); diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index d17c9abb95a..86409398f5b 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -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" @@ -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 b2515c42f86..515445445ae 100644 --- a/src/bin/psql/input.h +++ b/src/bin/psql/input.h @@ -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); diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index ba807f42750..895e64b1444 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 098bf20bad0..a3b3b7374b0 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*/ |