o Improve psql's handling of multi-line statements
authorBruce Momjian <bruce@momjian.us>
Sat, 11 Feb 2006 21:55:35 +0000 (21:55 +0000)
committerBruce Momjian <bruce@momjian.us>
Sat, 11 Feb 2006 21:55:35 +0000 (21:55 +0000)
Currently, while \e saves a single statement as one entry, interactive
statements are saved one line at a time.  Ideally all statements
would be saved like \e does.

Sergey E. Koposov

src/bin/psql/help.c
src/bin/psql/input.c
src/bin/psql/input.h
src/bin/psql/mainloop.c
src/bin/psql/prompt.c
src/bin/psql/tab-complete.c

index 879cdecd0e3da51a030345b3f51e0410f70ab569..59c8617755584b786b827f0dcd899c9fb2f8434a 100644 (file)
@@ -3,10 +3,11 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.106 2005/10/15 02:49:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.107 2006/02/11 21:55:35 momjian Exp $
  */
 #include "postgres_fe.h"
 #include "common.h"
+#include "pqexpbuffer.h"
 #include "input.h"
 #include "print.h"
 #include "help.h"
index 4272fcb2e50859a2449ffd32a6a9a2ae77ed651c..1b6e48cff3c24b1f9711d3b4abaeb7c860c512cb 100644 (file)
@@ -3,12 +3,12 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.46 2005/10/15 02:49:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.47 2006/02/11 21:55:35 momjian Exp $
  */
 #include "postgres_fe.h"
 
-#include "input.h"
 #include "pqexpbuffer.h"
+#include "input.h"
 #include "settings.h"
 #include "tab-complete.h"
 #include "common.h"
@@ -90,18 +90,55 @@ gets_interactive(const char *prompt)
 #ifdef USE_READLINE
    char       *s;
 
-   static char *prev_hist = NULL;
-
    if (useReadline)
        /* On some platforms, readline is declared as readline(char *) */
        s = readline((char *) prompt);
    else
        s = gets_basic(prompt);
 
-   if (useHistory && s && s[0])
+   return s;
+#else
+   return gets_basic(prompt);
+#endif
+}
+
+
+/* Put the line in the history buffer and also add the trailing \n */
+void pgadd_history(char *s, PQExpBuffer history_buf)
+{
+#ifdef USE_READLINE
+
+   int slen;
+   if (useReadline && useHistory && s && s[0])
    {
-       enum histcontrol HC;
+       slen = strlen(s);
+       if (s[slen-1] == '\n')
+           appendPQExpBufferStr(history_buf, s);
+       else
+       {
+           appendPQExpBufferStr(history_buf, s);
+           appendPQExpBufferChar(history_buf, '\n');
+       }
+   }   
+#endif 
+}
 
+
+/* Feed the contents of the history buffer to readline */
+void pgflush_history(PQExpBuffer history_buf)
+{
+#ifdef USE_READLINE    
+   char *s;
+   static char *prev_hist;
+   int slen, i;
+   
+   if (useReadline && useHistory )
+   {
+       enum histcontrol HC;
+       
+       s = history_buf->data;
+       prev_hist = NULL;
+           
        HC = GetHistControlConfig();
 
        if (((HC & hctl_ignorespace) && s[0] == ' ') ||
@@ -112,17 +149,27 @@ gets_interactive(const char *prompt)
        else
        {
            free(prev_hist);
+           slen = strlen(s);
+           /* Trim the trailing \n's */
+           for (i = slen-1; i >= 0 && s[i] == '\n'; i--)
+               ;
+           s[i + 1] = '\0';
            prev_hist = pg_strdup(s);
            add_history(s);
        }
+       
+       resetPQExpBuffer(history_buf);
    }
-
-   return s;
-#else
-   return gets_basic(prompt);
 #endif
 }
 
+void pgclear_history(PQExpBuffer history_buf)
+{
+#ifdef USE_READLINE    
+   if (useReadline && useHistory)
+       resetPQExpBuffer(history_buf);
+#endif
+}
 
 
 /*
@@ -157,6 +204,30 @@ gets_fromFile(FILE *source)
 }
 
 
+static void encode_history()
+{
+   HIST_ENTRY *cur_hist;
+   char *cur_ptr;
+
+   for (history_set_pos(0), cur_hist = current_history();
+        cur_hist; cur_hist = next_history())
+       for (cur_ptr = cur_hist->line; *cur_ptr; cur_ptr++)
+           if (*cur_ptr == '\n')
+               *cur_ptr = '\0';
+}
+
+static void decode_history()
+{
+   HIST_ENTRY *cur_hist;
+   char *cur_ptr;
+
+   for (history_set_pos(0), cur_hist = current_history();
+        cur_hist; cur_hist = next_history())
+       for (cur_ptr = cur_hist->line; *cur_ptr; cur_ptr++)
+           if (*cur_ptr == '\0')
+               *cur_ptr = '\n';
+}
+
 
 /*
  * Put any startup stuff related to input in here. It's good to maintain
@@ -197,6 +268,8 @@ initializeInput(int flags)
 
        if (psql_history)
            read_history(psql_history);
+           
+       decode_history();
    }
 #endif
 
@@ -215,6 +288,7 @@ saveHistory(char *fname)
 #ifdef USE_READLINE
    if (useHistory && fname)
    {
+       encode_history();       
        if (write_history(fname) == 0)
            return true;
 
index bddc174b12a010bfd302988285c146aabe2ae87b..bd4ed99a2d871bf60007bfafee493a3fe72e3635 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/input.h,v 1.23 2005/01/01 05:43:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/input.h,v 1.24 2006/02/11 21:55:35 momjian Exp $
  */
 #ifndef INPUT_H
 #define INPUT_H
@@ -39,4 +39,9 @@ char     *gets_fromFile(FILE *source);
 void       initializeInput(int flags);
 bool       saveHistory(char *fname);
 
+void pgadd_history(char *s, PQExpBuffer history_buf);
+void pgclear_history(PQExpBuffer history_buf);
+void pgflush_history(PQExpBuffer history_buf);
+
+
 #endif   /* INPUT_H */
index cebeda70c00e6383764721c027300fc3c770d66d..00b7969f18fce5ae2b11562c8b9ca9a24ee824a7 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.69 2005/12/18 02:17:16 petere Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.70 2006/02/11 21:55:35 momjian Exp $
  */
 #include "postgres_fe.h"
 #include "mainloop.h"
@@ -37,6 +37,7 @@ MainLoop(FILE *source)
    PQExpBuffer query_buf;      /* buffer for query being accumulated */
    PQExpBuffer previous_buf;   /* if there isn't anything in the new buffer
                                 * yet, use this one for \e, etc. */
+   PQExpBuffer history_buf;
    char       *line;           /* current line of input */
    int         added_nl_pos;
    bool        success;
@@ -66,7 +67,9 @@ MainLoop(FILE *source)
 
    query_buf = createPQExpBuffer();
    previous_buf = createPQExpBuffer();
-   if (!query_buf || !previous_buf)
+   history_buf = createPQExpBuffer();
+
+   if (!query_buf || !previous_buf || !history_buf)
    {
        psql_error("out of memory\n");
        exit(EXIT_FAILURE);
@@ -90,7 +93,7 @@ MainLoop(FILE *source)
                successResult = EXIT_USER;
                break;
            }
-
+           pgclear_history(history_buf);           
            cancel_pressed = false;
        }
 
@@ -106,6 +109,8 @@ MainLoop(FILE *source)
            count_eof = 0;
            slashCmdStatus = PSQL_CMD_UNKNOWN;
            prompt_status = PROMPT_READY;
+           if (pset.cur_cmd_interactive)
+               pgclear_history(history_buf);           
 
            if (pset.cur_cmd_interactive)
                putc('\n', stdout);
@@ -138,11 +143,15 @@ MainLoop(FILE *source)
            psql_scan_reset(scan_state);
            slashCmdStatus = PSQL_CMD_UNKNOWN;
            prompt_status = PROMPT_READY;
+           
+           if (pset.cur_cmd_interactive)
+               /*
+                *  Pass all the contents of history_buf to readline
+                *  and free the history buffer.
+                */
+               pgflush_history(history_buf);
        }
-
-       /*
-        * otherwise, get another line
-        */
+       /* otherwise, get another line */
        else if (pset.cur_cmd_interactive)
        {
            /* May need to reset prompt, eg after \r command */
@@ -212,7 +221,11 @@ MainLoop(FILE *source)
         */
        psql_scan_setup(scan_state, line, strlen(line));
        success = true;
-
+       
+       if (pset.cur_cmd_interactive)
+           /* Put current line in the history buffer */
+           pgadd_history(line, history_buf);
+       
        while (success || !die_on_error)
        {
            PsqlScanResult scan_result;
@@ -287,6 +300,13 @@ MainLoop(FILE *source)
                scan_result == PSCAN_EOL)
                break;
        }
+       
+       if (pset.cur_cmd_interactive && prompt_status != PROMPT_CONTINUE)
+           /*
+            *  Pass all the contents of history_buf to readline
+            *  and free the history buffer.
+            */
+           pgflush_history(history_buf);
 
        psql_scan_finish(scan_state);
        free(line);
@@ -333,6 +353,7 @@ MainLoop(FILE *source)
 
    destroyPQExpBuffer(query_buf);
    destroyPQExpBuffer(previous_buf);
+   destroyPQExpBuffer(history_buf);
 
    psql_scan_destroy(scan_state);
 
index 5afbfc68c49ae535326905a57448c4ac519036d1..f56ce6bf5bcc9dc82c26823859ca886eda9ce030 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/prompt.c,v 1.41 2006/01/03 23:32:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/prompt.c,v 1.42 2006/02/11 21:55:35 momjian Exp $
  */
 #include "postgres_fe.h"
 #include "prompt.h"
@@ -12,6 +12,7 @@
 
 #include "settings.h"
 #include "common.h"
+#include "pqexpbuffer.h"
 #include "input.h"
 #include "variables.h"
 
index 1a99dc9ca98d4ee602a433346a1752dac7665787..fd32a520fd47667d3752cb49a1ff821ddc95353e 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.144 2006/01/11 08:43:12 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.145 2006/02/11 21:55:35 momjian Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -43,6 +43,7 @@
 
 #include "postgres_fe.h"
 #include "tab-complete.h"
+#include "pqexpbuffer.h"
 #include "input.h"
 
 /* If we don't have this, we might as well forget about the whole thing: */
@@ -50,7 +51,6 @@
 
 #include <ctype.h>
 #include "libpq-fe.h"
-#include "pqexpbuffer.h"
 #include "common.h"
 #include "settings.h"