]> granicus.if.org Git - postgresql/blobdiff - src/bin/psql/input.c
pgindent run for 9.0
[postgresql] / src / bin / psql / input.c
index 9d2b8c9d96299abae27e638f6460097abd4fe442..d28fe9c0ba4ef8f0047871477b01e8d123ae6922 100644 (file)
@@ -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 <unistd.h>
+#endif
+#include <fcntl.h>
+
 #include "input.h"
 #include "settings.h"
 #include "tab-complete.h"
 #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;
        }