mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +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:
		| @@ -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. | ||||
|  * | ||||
|  * 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. | ||||
|  * 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. | ||||
|  * | ||||
|  * 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; | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user