X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbin%2Fpsql%2Finput.c;h=d28fe9c0ba4ef8f0047871477b01e8d123ae6922;hb=65e806cba1f0f154d51caa7478e7192ce58d1056;hp=9d2b8c9d96299abae27e638f6460097abd4fe442;hpb=506a9893b7c078ad8dc739ea6872f9f6cc7bd59e;p=postgresql diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index 9d2b8c9d96..d28fe9c0ba 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -1,12 +1,17 @@ /* * psql - the PostgreSQL interactive terminal * - * Copyright (c) 2000-2006, PostgreSQL Global Development Group + * Copyright (c) 2000-2010, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.61 2006/12/24 19:14:28 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.69 2010/02/26 02:01:18 momjian Exp $ */ #include "postgres_fe.h" +#ifndef WIN32 +#include +#endif +#include + #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++; } } @@ -147,7 +158,7 @@ pg_send_history(PQExpBuffer history_buf) * gets_fromFile * * Gets a line of noninteractive input from a file (which could be stdin). - * The result is a malloc'd string. + * The result is a malloc'd string, or NULL on EOF or input error. * * Caller *must* have set up sigint_interrupt_jmp before calling. * @@ -179,12 +190,26 @@ gets_fromFile(FILE *source) /* Disable SIGINT again */ sigint_interrupt_enabled = false; - /* EOF? */ + /* EOF or error? */ if (result == NULL) + { + if (ferror(source)) + { + psql_error("could not read from input file: %s\n", + strerror(errno)); + return NULL; + } break; + } appendPQExpBufferStr(buffer, line); + if (PQExpBufferBroken(buffer)) + { + psql_error("out of memory\n"); + return NULL; + } + /* EOL? */ if (buffer->data[buffer->len - 1] == '\n') { @@ -262,6 +287,7 @@ initializeInput(int flags) useHistory = true; using_history(); + history_lines_added = 0; histfile = GetVariable(pset.vars, "HISTFILE"); if (histfile == NULL) @@ -296,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 @@ -321,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)); @@ -355,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; }