diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index e16b4d54c24..a66093abb3a 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -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; diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 676e2680af6..0f83799f7e0 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -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 diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index f58c54519a7..108cab967be 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -12,6 +12,8 @@ #include #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);