Skip to content

Commit e97281c

Browse files
committed
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.
1 parent eb62398 commit e97281c

File tree

7 files changed

+88
-31
lines changed

7 files changed

+88
-31
lines changed

configure

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19453,7 +19453,8 @@ fi
1945319453
done
1945419454

1945519455

19456-
for ac_func in replace_history_entry
19456+
19457+
for ac_func in append_history history_truncate_file
1945719458
do
1945819459
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1945919460
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5

configure.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
dnl Process this file with autoconf to produce a configure script.
2-
dnl $PostgreSQL: pgsql/configure.in,v 1.610 2009/09/08 16:08:26 tgl Exp $
2+
dnl $PostgreSQL: pgsql/configure.in,v 1.611 2009/09/13 22:18:22 tgl Exp $
33
dnl
44
dnl Developers, please strive to achieve this order:
55
dnl
@@ -1316,7 +1316,7 @@ fi
13161316
if test "$with_readline" = yes; then
13171317
PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER
13181318
AC_CHECK_FUNCS([rl_completion_matches rl_filename_completion_function])
1319-
AC_CHECK_FUNCS([replace_history_entry])
1319+
AC_CHECK_FUNCS([append_history history_truncate_file])
13201320
fi
13211321

13221322

src/bin/psql/command.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.206 2009/06/11 14:49:07 momjian Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.207 2009/09/13 22:18:22 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99
#include "command.h"
@@ -908,7 +908,7 @@ exec_command(const char *cmd,
908908

909909
expand_tilde(&fname);
910910
/* This scrolls off the screen when using /dev/tty */
911-
success = saveHistory(fname ? fname : DEVTTY, false);
911+
success = saveHistory(fname ? fname : DEVTTY, -1, false, false);
912912
if (success && !pset.quiet && fname)
913913
printf(gettext("Wrote history to file \"%s/%s\".\n"),
914914
pset.dirname ? pset.dirname : ".", fname);

src/bin/psql/input.c

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
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"
@@ -23,7 +28,11 @@
2328
#ifdef USE_READLINE
2429
static bool useReadline;
2530
static 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
*/
320339
bool
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
}

src/bin/psql/input.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/input.h,v 1.32 2009/08/24 16:18:13 tgl Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/input.h,v 1.33 2009/09/13 22:18:22 tgl Exp $
77
*/
88
#ifndef INPUT_H
99
#define INPUT_H
@@ -45,7 +45,7 @@ char *gets_interactive(const char *prompt);
4545
char *gets_fromFile(FILE *source);
4646

4747
void initializeInput(int flags);
48-
bool saveHistory(char *fname, bool encodeFlag);
48+
bool saveHistory(char *fname, int max_lines, bool appendFlag, bool encodeFlag);
4949

5050
void pg_append_history(const char *s, PQExpBuffer history_buf);
5151
void pg_send_history(PQExpBuffer history_buf);

src/include/pg_config.h.in

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
# define gettimeofday(a,b) gettimeofday(a)
7979
#endif
8080

81+
/* Define to 1 if you have the `append_history' function. */
82+
#undef HAVE_APPEND_HISTORY
83+
8184
/* Define to 1 if you have the `atexit' function. */
8285
#undef HAVE_ATEXIT
8386

@@ -212,6 +215,9 @@
212215
/* Define to 1 if you have the <history.h> header file. */
213216
#undef HAVE_HISTORY_H
214217

218+
/* Define to 1 if you have the `history_truncate_file' function. */
219+
#undef HAVE_HISTORY_TRUNCATE_FILE
220+
215221
/* Define to 1 if you have the <ieeefp.h> header file. */
216222
#undef HAVE_IEEEFP_H
217223

@@ -378,9 +384,6 @@
378384
/* Define to 1 if you have the `readlink' function. */
379385
#undef HAVE_READLINK
380386

381-
/* Define to 1 if you have the `replace_history_entry' function. */
382-
#undef HAVE_REPLACE_HISTORY_ENTRY
383-
384387
/* Define to 1 if you have the `rint' function. */
385388
#undef HAVE_RINT
386389

src/include/pg_config.h.win32

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,6 @@
306306
/* Define to 1 if you have the `readlink' function. */
307307
/* #undef HAVE_READLINK */
308308

309-
/* Define to 1 if you have the `replace_history_entry' function. */
310-
/* #undef HAVE_REPLACE_HISTORY_ENTRY */
311-
312309
/* Define to 1 if you have the `rint' function. */
313310
/*#define HAVE_RINT 1*/
314311

0 commit comments

Comments
 (0)