]> granicus.if.org Git - postgresql/commitdiff
Allow \watch to display query execution time if \timing is enabled.
authorFujii Masao <fujii@postgresql.org>
Thu, 4 Sep 2014 03:31:48 +0000 (12:31 +0900)
committerFujii Masao <fujii@postgresql.org>
Thu, 4 Sep 2014 03:31:48 +0000 (12:31 +0900)
Previously \watch could not display the query execution time even
when \timing was enabled because it used PSQLexec instead of
SendQuery and that function didn't support \timing. This patch
introduces PSQLexecWatch and changes \watch so as to use it, instead.
PSQLexecWatch is the function to run the query, print its results and
display how long it took (only when \timing is enabled).

This patch also changes --echo-hidden so that it doesn't print
the query that \watch executes. Since \watch cannot execute
backslash command queries, they should not be printed even
when --echo-hidden is set.

Patch by me, review by Heikki Linnakangas and Michael Paquier

src/bin/psql/command.c
src/bin/psql/common.c
src/bin/psql/common.h

index e16b4d54c24cf01574eea68e36883f423d6c4996..a66093abb3a9804da4ccb753cd6fdac0678245d0 100644 (file)
@@ -2687,7 +2687,7 @@ do_watch(PQExpBuffer query_buf, long sleep)
 
        for (;;)
        {
-               PGresult   *res;
+               int     res;
                time_t          timer;
                long            i;
 
@@ -2700,65 +2700,22 @@ do_watch(PQExpBuffer query_buf, long sleep)
                                 sleep, asctime(localtime(&timer)));
                myopt.title = title;
 
-               /*
-                * Run the query.  We use PSQLexec, which is kind of cheating, but
-                * SendQuery doesn't let us suppress autocommit behavior.
-                */
-               res = PSQLexec(query_buf->data, false);
-
-               /* PSQLexec handles failure results and returns NULL */
-               if (res == NULL)
-                       break;
+               /* Run the query and print out the results */
+               res = PSQLexecWatch(query_buf->data, &myopt);
 
                /*
-                * If SIGINT is sent while the query is processing, PSQLexec will
-                * consume the interrupt.  The user's intention, though, is to cancel
-                * the entire watch process, so detect a sent cancellation request and
-                * exit in this case.
+                * PSQLexecWatch handles the case where we can no longer
+                * repeat the query, and returns 0 or -1.
                 */
-               if (cancel_pressed)
-               {
-                       PQclear(res);
+               if (res == 0)
                        break;
-               }
-
-               switch (PQresultStatus(res))
-               {
-                       case PGRES_TUPLES_OK:
-                               printQuery(res, &myopt, pset.queryFout, pset.logfile);
-                               break;
-
-                       case PGRES_COMMAND_OK:
-                               fprintf(pset.queryFout, "%s\n%s\n\n", title, PQcmdStatus(res));
-                               break;
-
-                       case PGRES_EMPTY_QUERY:
-                               psql_error(_("\\watch cannot be used with an empty query\n"));
-                               PQclear(res);
-                               return false;
-
-                       case PGRES_COPY_OUT:
-                       case PGRES_COPY_IN:
-                       case PGRES_COPY_BOTH:
-                               psql_error(_("\\watch cannot be used with COPY\n"));
-                               PQclear(res);
-                               return false;
-
-                       default:
-                               /* other cases should have been handled by PSQLexec */
-                               psql_error(_("unexpected result status for \\watch\n"));
-                               PQclear(res);
-                               return false;
-               }
-
-               PQclear(res);
-
-               fflush(pset.queryFout);
+               if (res == -1)
+                       return false;
 
                /*
                 * Set up cancellation of 'watch' via SIGINT.  We redo this each time
-                * through the loop since it's conceivable something inside PSQLexec
-                * could change sigint_interrupt_jmp.
+                * through the loop since it's conceivable something inside
+                * PSQLexecWatch could change sigint_interrupt_jmp.
                 */
                if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
                        break;
index 676e2680af67c525ceff47b69120dabe0d9ed883..0f83799f7e0a909a43fb22f43fbbcb33690d77d6 100644 (file)
@@ -497,6 +497,102 @@ PSQLexec(const char *query, bool start_xact)
 }
 
 
+/*
+ * PSQLexecWatch
+ *
+ * This function is used for \watch command to send the query to
+ * the server and print out the results.
+ *
+ * Returns 1 if the query executed successfully, 0 if it cannot be repeated,
+ * e.g., because of the interrupt, -1 on error.
+ */
+int
+PSQLexecWatch(const char *query, const printQueryOpt *opt)
+{
+       PGresult   *res;
+       double  elapsed_msec = 0;
+       instr_time      before;
+       instr_time      after;
+
+       if (!pset.db)
+       {
+               psql_error("You are currently not connected to a database.\n");
+               return 0;
+       }
+
+       SetCancelConn();
+
+       if (pset.timing)
+               INSTR_TIME_SET_CURRENT(before);
+
+       res = PQexec(pset.db, query);
+
+       ResetCancelConn();
+
+       if (!AcceptResult(res))
+       {
+               PQclear(res);
+               return 0;
+       }
+
+       if (pset.timing)
+       {
+               INSTR_TIME_SET_CURRENT(after);
+               INSTR_TIME_SUBTRACT(after, before);
+               elapsed_msec = INSTR_TIME_GET_MILLISEC(after);
+       }
+
+       /*
+        * If SIGINT is sent while the query is processing, the interrupt
+        * will be consumed.  The user's intention, though, is to cancel
+        * the entire watch process, so detect a sent cancellation request and
+        * exit in this case.
+        */
+       if (cancel_pressed)
+       {
+               PQclear(res);
+               return 0;
+       }
+
+       switch (PQresultStatus(res))
+       {
+               case PGRES_TUPLES_OK:
+                       printQuery(res, opt, pset.queryFout, pset.logfile);
+                       break;
+
+               case PGRES_COMMAND_OK:
+                       fprintf(pset.queryFout, "%s\n%s\n\n", opt->title, PQcmdStatus(res));
+                       break;
+
+               case PGRES_EMPTY_QUERY:
+                       psql_error(_("\\watch cannot be used with an empty query\n"));
+                       PQclear(res);
+                       return -1;
+
+               case PGRES_COPY_OUT:
+               case PGRES_COPY_IN:
+               case PGRES_COPY_BOTH:
+                       psql_error(_("\\watch cannot be used with COPY\n"));
+                       PQclear(res);
+                       return -1;
+
+               default:
+                       psql_error(_("unexpected result status for \\watch\n"));
+                       PQclear(res);
+                       return -1;
+       }
+
+       PQclear(res);
+
+       fflush(pset.queryFout);
+
+       /* Possible microtiming output */
+       if (pset.timing)
+               printf(_("Time: %.3f ms\n"), elapsed_msec);
+
+       return 1;
+}
+
 
 /*
  * PrintNotifications: check for asynchronous notifications, and print them out
index f58c54519a7604d226dd3a192a9688841409ed89..108cab967bed430de5ca8250341d9a476a8ec574 100644 (file)
@@ -12,6 +12,8 @@
 #include <setjmp.h>
 #include "libpq-fe.h"
 
+#include "print.h"
+
 #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
 
 extern bool setQFout(const char *fname);
@@ -37,6 +39,7 @@ extern void SetCancelConn(void);
 extern void ResetCancelConn(void);
 
 extern PGresult *PSQLexec(const char *query, bool start_xact);
+extern int PSQLexecWatch(const char *query, const printQueryOpt *opt);
 
 extern bool SendQuery(const char *query);