mirror of
https://github.com/postgres/postgres.git
synced 2025-06-08 22:02:03 +03:00
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.
This commit is contained in:
parent
eb62398f39
commit
e97281c46c
3
configure
vendored
3
configure
vendored
@ -19453,7 +19453,8 @@ fi
|
|||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
for ac_func in replace_history_entry
|
|
||||||
|
for ac_func in append_history history_truncate_file
|
||||||
do
|
do
|
||||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||||
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
|
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
dnl Process this file with autoconf to produce a configure script.
|
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
|
||||||
dnl Developers, please strive to achieve this order:
|
dnl Developers, please strive to achieve this order:
|
||||||
dnl
|
dnl
|
||||||
@ -1316,7 +1316,7 @@ fi
|
|||||||
if test "$with_readline" = yes; then
|
if test "$with_readline" = yes; then
|
||||||
PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER
|
PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER
|
||||||
AC_CHECK_FUNCS([rl_completion_matches rl_filename_completion_function])
|
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
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
@ -908,7 +908,7 @@ exec_command(const char *cmd,
|
|||||||
|
|
||||||
expand_tilde(&fname);
|
expand_tilde(&fname);
|
||||||
/* This scrolls off the screen when using /dev/tty */
|
/* 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)
|
if (success && !pset.quiet && fname)
|
||||||
printf(gettext("Wrote history to file \"%s/%s\".\n"),
|
printf(gettext("Wrote history to file \"%s/%s\".\n"),
|
||||||
pset.dirname ? pset.dirname : ".", fname);
|
pset.dirname ? pset.dirname : ".", fname);
|
||||||
|
@ -3,10 +3,15 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
|
* 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"
|
#include "postgres_fe.h"
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "tab-complete.h"
|
#include "tab-complete.h"
|
||||||
@ -23,7 +28,11 @@
|
|||||||
#ifdef USE_READLINE
|
#ifdef USE_READLINE
|
||||||
static bool useReadline;
|
static bool useReadline;
|
||||||
static bool useHistory;
|
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
|
* 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);
|
prev_hist = pg_strdup(s);
|
||||||
/* And send it to readline */
|
/* And send it to readline */
|
||||||
add_history(s);
|
add_history(s);
|
||||||
|
/* Count lines added to history for use later */
|
||||||
|
history_lines_added++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,6 +287,7 @@ initializeInput(int flags)
|
|||||||
|
|
||||||
useHistory = true;
|
useHistory = true;
|
||||||
using_history();
|
using_history();
|
||||||
|
history_lines_added = 0;
|
||||||
|
|
||||||
histfile = GetVariable(pset.vars, "HISTFILE");
|
histfile = GetVariable(pset.vars, "HISTFILE");
|
||||||
if (histfile == NULL)
|
if (histfile == NULL)
|
||||||
@ -310,15 +322,22 @@ initializeInput(int flags)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is for saving the readline history when user
|
* This function saves the readline history when user
|
||||||
* runs \s command or when psql finishes.
|
* runs \s command or when psql exits.
|
||||||
*
|
*
|
||||||
* We have an argument named encodeFlag to handle the cases differently.
|
* fname: pathname of history file. (Should really be "const char *",
|
||||||
* In case of call via \s we don't really need to encode \n as \x01,
|
* but some ancient versions of readline omit the const-decoration.)
|
||||||
* but when we save history for Readline we must do that conversion.
|
*
|
||||||
|
* max_lines: if >= 0, limit history file to that many entries.
|
||||||
|
*
|
||||||
|
* 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
|
bool
|
||||||
saveHistory(char *fname, bool encodeFlag)
|
saveHistory(char *fname, int max_lines, bool appendFlag, bool encodeFlag)
|
||||||
{
|
{
|
||||||
#ifdef USE_READLINE
|
#ifdef USE_READLINE
|
||||||
|
|
||||||
@ -335,14 +354,54 @@ saveHistory(char *fname, bool encodeFlag)
|
|||||||
encode_history();
|
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
|
* readline and libedit. Therefore, check for errno becoming set to
|
||||||
* see if the write failed.
|
* see if the write failed. Similarly for append_history.
|
||||||
*/
|
*/
|
||||||
|
#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;
|
errno = 0;
|
||||||
(void) write_history(fname);
|
(void) write_history(fname);
|
||||||
if (errno == 0)
|
if (errno == 0)
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
psql_error("could not save history to file \"%s\": %s\n",
|
psql_error("could not save history to file \"%s\": %s\n",
|
||||||
fname, strerror(errno));
|
fname, strerror(errno));
|
||||||
@ -369,10 +428,7 @@ finishInput(int exitstatus, void *arg)
|
|||||||
int hist_size;
|
int hist_size;
|
||||||
|
|
||||||
hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true);
|
hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true);
|
||||||
if (hist_size >= 0)
|
saveHistory(psql_history, hist_size, true, true);
|
||||||
stifle_history(hist_size);
|
|
||||||
|
|
||||||
saveHistory(psql_history, true);
|
|
||||||
free(psql_history);
|
free(psql_history);
|
||||||
psql_history = NULL;
|
psql_history = NULL;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
|
* 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
|
#ifndef INPUT_H
|
||||||
#define INPUT_H
|
#define INPUT_H
|
||||||
@ -45,7 +45,7 @@ char *gets_interactive(const char *prompt);
|
|||||||
char *gets_fromFile(FILE *source);
|
char *gets_fromFile(FILE *source);
|
||||||
|
|
||||||
void initializeInput(int flags);
|
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_append_history(const char *s, PQExpBuffer history_buf);
|
||||||
void pg_send_history(PQExpBuffer history_buf);
|
void pg_send_history(PQExpBuffer history_buf);
|
||||||
|
@ -78,6 +78,9 @@
|
|||||||
# define gettimeofday(a,b) gettimeofday(a)
|
# define gettimeofday(a,b) gettimeofday(a)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `append_history' function. */
|
||||||
|
#undef HAVE_APPEND_HISTORY
|
||||||
|
|
||||||
/* Define to 1 if you have the `atexit' function. */
|
/* Define to 1 if you have the `atexit' function. */
|
||||||
#undef HAVE_ATEXIT
|
#undef HAVE_ATEXIT
|
||||||
|
|
||||||
@ -212,6 +215,9 @@
|
|||||||
/* Define to 1 if you have the <history.h> header file. */
|
/* Define to 1 if you have the <history.h> header file. */
|
||||||
#undef HAVE_HISTORY_H
|
#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. */
|
/* Define to 1 if you have the <ieeefp.h> header file. */
|
||||||
#undef HAVE_IEEEFP_H
|
#undef HAVE_IEEEFP_H
|
||||||
|
|
||||||
@ -378,9 +384,6 @@
|
|||||||
/* Define to 1 if you have the `readlink' function. */
|
/* Define to 1 if you have the `readlink' function. */
|
||||||
#undef HAVE_READLINK
|
#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 to 1 if you have the `rint' function. */
|
||||||
#undef HAVE_RINT
|
#undef HAVE_RINT
|
||||||
|
|
||||||
|
@ -306,9 +306,6 @@
|
|||||||
/* Define to 1 if you have the `readlink' function. */
|
/* Define to 1 if you have the `readlink' function. */
|
||||||
/* #undef HAVE_READLINK */
|
/* #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 to 1 if you have the `rint' function. */
|
||||||
/*#define HAVE_RINT 1*/
|
/*#define HAVE_RINT 1*/
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user