33 *
44 * Copyright (c) 2000-2009, PostgreSQL Global Development Group
55 *
6- * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.66 2009/01/01 17:23:55 momjian Exp $
6+ * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.67 2009/09/13 22:18:22 tgl Exp $
77 */
88#include "postgres_fe.h"
99
10+ #ifndef WIN32
11+ #include <unistd.h>
12+ #endif
13+ #include <fcntl.h>
14+
1015#include "input.h"
1116#include "settings.h"
1217#include "tab-complete.h"
2328#ifdef USE_READLINE
2429static bool useReadline ;
2530static bool useHistory ;
26- char * psql_history ;
31+
32+ static char * psql_history ;
33+
34+ static int history_lines_added ;
35+
2736
2837/*
2938 * Preserve newlines in saved queries by mapping '\n' to NL_IN_HISTORY
@@ -135,6 +144,8 @@ pg_send_history(PQExpBuffer history_buf)
135144 prev_hist = pg_strdup (s );
136145 /* And send it to readline */
137146 add_history (s );
147+ /* Count lines added to history for use later */
148+ history_lines_added ++ ;
138149 }
139150 }
140151
@@ -276,6 +287,7 @@ initializeInput(int flags)
276287
277288 useHistory = true;
278289 using_history ();
290+ history_lines_added = 0 ;
279291
280292 histfile = GetVariable (pset .vars , "HISTFILE" );
281293 if (histfile == NULL )
@@ -310,15 +322,22 @@ initializeInput(int flags)
310322
311323
312324/*
313- * This function is for saving the readline history when user
314- * runs \s command or when psql finishes.
325+ * This function saves the readline history when user
326+ * runs \s command or when psql exits.
327+ *
328+ * fname: pathname of history file. (Should really be "const char *",
329+ * but some ancient versions of readline omit the const-decoration.)
330+ *
331+ * max_lines: if >= 0, limit history file to that many entries.
315332 *
316- * We have an argument named encodeFlag to handle the cases differently.
317- * In case of call via \s we don't really need to encode \n as \x01,
318- * but when we save history for Readline we must do that conversion.
333+ * appendFlag: if true, try to append just our new lines to the file.
334+ * If false, write the whole available history.
335+ *
336+ * encodeFlag: whether to encode \n as \x01. For \s calls we don't wish
337+ * to do that, but must do so when saving the final history file.
319338 */
320339bool
321- saveHistory (char * fname , bool encodeFlag )
340+ saveHistory (char * fname , int max_lines , bool appendFlag , bool encodeFlag )
322341{
323342#ifdef USE_READLINE
324343
@@ -335,14 +354,54 @@ saveHistory(char *fname, bool encodeFlag)
335354 encode_history ();
336355
337356 /*
338- * return value of write_history is not standardized across GNU
357+ * On newer versions of libreadline, truncate the history file as
358+ * needed and then append what we've added. This avoids overwriting
359+ * history from other concurrent sessions (although there are still
360+ * race conditions when two sessions exit at about the same time).
361+ * If we don't have those functions, fall back to write_history().
362+ *
363+ * Note: return value of write_history is not standardized across GNU
339364 * readline and libedit. Therefore, check for errno becoming set to
340- * see if the write failed.
365+ * see if the write failed. Similarly for append_history.
341366 */
342- errno = 0 ;
343- (void ) write_history (fname );
344- if (errno == 0 )
345- return true;
367+ #if defined(HAVE_HISTORY_TRUNCATE_FILE ) && defined(HAVE_APPEND_HISTORY )
368+ if (appendFlag )
369+ {
370+ int nlines ;
371+ int fd ;
372+
373+ /* truncate previous entries if needed */
374+ if (max_lines >= 0 )
375+ {
376+ nlines = Max (max_lines - history_lines_added , 0 );
377+ (void ) history_truncate_file (fname , nlines );
378+ }
379+ /* append_history fails if file doesn't already exist :-( */
380+ fd = open (fname , O_CREAT | O_WRONLY | PG_BINARY , 0600 );
381+ if (fd >= 0 )
382+ close (fd );
383+ /* append the appropriate number of lines */
384+ if (max_lines >= 0 )
385+ nlines = Min (max_lines , history_lines_added );
386+ else
387+ nlines = history_lines_added ;
388+ errno = 0 ;
389+ (void ) append_history (nlines , fname );
390+ if (errno == 0 )
391+ return true;
392+ }
393+ else
394+ #endif
395+ {
396+ /* truncate what we have ... */
397+ if (max_lines >= 0 )
398+ stifle_history (max_lines );
399+ /* ... and overwrite file. Tough luck for concurrent sessions. */
400+ errno = 0 ;
401+ (void ) write_history (fname );
402+ if (errno == 0 )
403+ return true;
404+ }
346405
347406 psql_error ("could not save history to file \"%s\": %s\n" ,
348407 fname , strerror (errno ));
@@ -369,10 +428,7 @@ finishInput(int exitstatus, void *arg)
369428 int hist_size ;
370429
371430 hist_size = GetVariableNum (pset .vars , "HISTSIZE" , 500 , -1 , true);
372- if (hist_size >= 0 )
373- stifle_history (hist_size );
374-
375- saveHistory (psql_history , true);
431+ saveHistory (psql_history , hist_size , true, true);
376432 free (psql_history );
377433 psql_history = NULL ;
378434 }
0 commit comments