diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out index 674ed270a8f85..fb97f6873700c 100644 --- a/contrib/pg_stat_statements/expected/pg_stat_statements.out +++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out @@ -50,28 +50,8 @@ BEGIN \; SELECT 2.0 AS "float" \; SELECT 'world' AS "text" \; COMMIT; - float -------- - 2.0 -(1 row) - - text -------- - world -(1 row) - -- compound with empty statements and spurious leading spacing \;\; SELECT 3 + 3 \;\;\; SELECT ' ' || ' !' \;\; SELECT 1 + 4 \;; - ?column? ----------- - 6 -(1 row) - - ?column? ----------- - ! -(1 row) - ?column? ---------- 5 @@ -81,11 +61,6 @@ COMMIT; SELECT 1 + 1 + 1 AS "add" \gset SELECT :add + 1 + 1 AS "add" \; SELECT :add + 1 + 1 AS "add" \gset - add ------ - 5 -(1 row) - -- set operator SELECT 1 AS i UNION SELECT 2 ORDER BY i; i diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 67e527124d9f4..bd4f26e6cc877 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -127,11 +127,18 @@ echo '\x \\ SELECT * FROM foo;' | psql commands included in the string to divide it into multiple transactions. (See for more details about how the server handles multi-query strings.) + Also, psql only prints the + result of the last SQL command in the string. + This is different from the behavior when the same string is read from + a file or fed to psql's standard input, + because then psql sends + each SQL command separately. - If having several commands executed in one transaction is not desired, - use repeated commands or feed multiple commands to - psql's standard input, + Because of this behavior, putting more than one SQL command in a + single string often has unexpected results. + It's better to use repeated commands or feed + multiple commands to psql's standard input, either using echo as illustrated above, or via a shell here-document, for example: @@ -3525,6 +3532,10 @@ select 1\; select 2\; select 3; commands included in the string to divide it into multiple transactions. (See for more details about how the server handles multi-query strings.) + psql prints only the last query result + it receives for each request; in this example, although all + three SELECTs are indeed executed, psql + only prints the 3. @@ -4111,18 +4122,6 @@ bar - SHOW_ALL_RESULTS - - - When this variable is set to off, only the last - result of a combined query (\;) is shown instead of - all of them. The default is on. The off behavior - is for compatibility with older versions of psql. - - - - - SHOW_CONTEXT diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 028a357991fd2..7a95465111ad1 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -33,7 +33,6 @@ static bool DescribeQuery(const char *query, double *elapsed_msec); static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec); static bool command_no_begin(const char *query); static bool is_select_command(const char *query); -static int SendQueryAndProcessResults(const char *query, double *pelapsed_msec, bool is_watch); /* @@ -354,7 +353,7 @@ CheckConnection(void) * Returns true for valid result, false for error state. */ static bool -AcceptResult(const PGresult *result, bool show_error) +AcceptResult(const PGresult *result) { bool OK; @@ -385,7 +384,7 @@ AcceptResult(const PGresult *result, bool show_error) break; } - if (!OK && show_error) + if (!OK) { const char *error = PQerrorMessage(pset.db); @@ -473,18 +472,6 @@ ClearOrSaveResult(PGresult *result) } } -/* - * Consume all results - */ -static void -ClearOrSaveAllResults() -{ - PGresult *result; - - while ((result = PQgetResult(pset.db)) != NULL) - ClearOrSaveResult(result); -} - /* * Print microtiming output. Always print raw milliseconds; if the interval @@ -585,7 +572,7 @@ PSQLexec(const char *query) ResetCancelConn(); - if (!AcceptResult(res, true)) + if (!AcceptResult(res)) { ClearOrSaveResult(res); res = NULL; @@ -607,8 +594,10 @@ PSQLexec(const char *query) int PSQLexecWatch(const char *query, const printQueryOpt *opt) { + PGresult *res; double elapsed_msec = 0; - int res; + instr_time before; + instr_time after; if (!pset.db) { @@ -617,16 +606,75 @@ PSQLexecWatch(const char *query, const printQueryOpt *opt) } SetCancelConn(pset.db); - res = SendQueryAndProcessResults(query, &elapsed_msec, true); + + if (pset.timing) + INSTR_TIME_SET_CURRENT(before); + + res = PQexec(pset.db, query); + ResetCancelConn(); + if (!AcceptResult(res)) + { + ClearOrSaveResult(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, false, pset.logfile); + break; + + case PGRES_COMMAND_OK: + fprintf(pset.queryFout, "%s\n%s\n\n", opt->title, PQcmdStatus(res)); + break; + + case PGRES_EMPTY_QUERY: + pg_log_error("\\watch cannot be used with an empty query"); + PQclear(res); + return -1; + + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + case PGRES_COPY_BOTH: + pg_log_error("\\watch cannot be used with COPY"); + PQclear(res); + return -1; + + default: + pg_log_error("unexpected result status for \\watch"); + PQclear(res); + return -1; + } + + PQclear(res); + fflush(pset.queryFout); /* Possible microtiming output */ if (pset.timing) PrintTiming(elapsed_msec); - return res; + return 1; } @@ -839,114 +887,197 @@ ExecQueryTuples(const PGresult *result) /* - * Marshal the COPY data. Either subroutine will get the - * connection out of its COPY state, then call PQresultStatus() - * once and report any error. Return whether all was ok. + * ProcessResult: utility function for use by SendQuery() only + * + * When our command string contained a COPY FROM STDIN or COPY TO STDOUT, + * PQexec() has stopped at the PGresult associated with the first such + * command. In that event, we'll marshal data for the COPY and then cycle + * through any subsequent PGresult objects. * - * For COPY OUT, direct the output to pset.copyStream if it's set, - * otherwise to pset.gfname if it's set, otherwise to queryFout. - * For COPY IN, use pset.copyStream as data source if it's set, - * otherwise cur_cmd_source. + * When the command string contained no such COPY command, this function + * degenerates to an AcceptResult() call. * - * Update result if further processing is necessary, or NULL otherwise. - * Return a result when queryFout can safely output a result status: - * on COPY IN, or on COPY OUT if written to something other than pset.queryFout. - * Returning NULL prevents the command status from being printed, which - * we want if the status line doesn't get taken as part of the COPY data. + * Changes its argument to point to the last PGresult of the command string, + * or NULL if that result was for a COPY TO STDOUT. (Returning NULL prevents + * the command status from being printed, which we want in that case so that + * the status line doesn't get taken as part of the COPY data.) + * + * Returns true on complete success, false otherwise. Possible failure modes + * include purely client-side problems; check the transaction status for the + * server-side opinion. */ static bool -HandleCopyResult(PGresult **result) +ProcessResult(PGresult **results) { - bool success; - FILE *copystream; - PGresult *copy_result; - ExecStatusType result_status = PQresultStatus(*result); - - Assert(result_status == PGRES_COPY_OUT || - result_status == PGRES_COPY_IN); - - SetCancelConn(pset.db); + bool success = true; + bool first_cycle = true; - if (result_status == PGRES_COPY_OUT) + for (;;) { - bool need_close = false; - bool is_pipe = false; + ExecStatusType result_status; + bool is_copy; + PGresult *next_result; - if (pset.copyStream) - { - /* invoked by \copy */ - copystream = pset.copyStream; - } - else if (pset.gfname) + if (!AcceptResult(*results)) { - /* invoked by \g */ - if (openQueryOutputFile(pset.gfname, - ©stream, &is_pipe)) - { - need_close = true; - if (is_pipe) - disable_sigpipe_trap(); - } - else - copystream = NULL; /* discard COPY data entirely */ + /* + * Failure at this point is always a server-side failure or a + * failure to submit the command string. Either way, we're + * finished with this command string. + */ + success = false; + break; } - else + + result_status = PQresultStatus(*results); + switch (result_status) { - /* fall back to the generic query output stream */ - copystream = pset.queryFout; - } + case PGRES_EMPTY_QUERY: + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + is_copy = false; + break; - success = handleCopyOut(pset.db, - copystream, - ©_result) - && (copystream != NULL); + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + is_copy = true; + break; - /* - * Suppress status printing if the report would go to the same - * place as the COPY data just went. Note this doesn't - * prevent error reporting, since handleCopyOut did that. - */ - if (copystream == pset.queryFout) - { - PQclear(copy_result); - copy_result = NULL; + default: + /* AcceptResult() should have caught anything else. */ + is_copy = false; + pg_log_error("unexpected PQresultStatus: %d", result_status); + break; } - if (need_close) + if (is_copy) { - /* close \g argument file/pipe */ - if (is_pipe) + /* + * Marshal the COPY data. Either subroutine will get the + * connection out of its COPY state, then call PQresultStatus() + * once and report any error. + * + * For COPY OUT, direct the output to pset.copyStream if it's set, + * otherwise to pset.gfname if it's set, otherwise to queryFout. + * For COPY IN, use pset.copyStream as data source if it's set, + * otherwise cur_cmd_source. + */ + FILE *copystream; + PGresult *copy_result; + + SetCancelConn(pset.db); + if (result_status == PGRES_COPY_OUT) { - pclose(copystream); - restore_sigpipe_trap(); + bool need_close = false; + bool is_pipe = false; + + if (pset.copyStream) + { + /* invoked by \copy */ + copystream = pset.copyStream; + } + else if (pset.gfname) + { + /* invoked by \g */ + if (openQueryOutputFile(pset.gfname, + ©stream, &is_pipe)) + { + need_close = true; + if (is_pipe) + disable_sigpipe_trap(); + } + else + copystream = NULL; /* discard COPY data entirely */ + } + else + { + /* fall back to the generic query output stream */ + copystream = pset.queryFout; + } + + success = handleCopyOut(pset.db, + copystream, + ©_result) + && success + && (copystream != NULL); + + /* + * Suppress status printing if the report would go to the same + * place as the COPY data just went. Note this doesn't + * prevent error reporting, since handleCopyOut did that. + */ + if (copystream == pset.queryFout) + { + PQclear(copy_result); + copy_result = NULL; + } + + if (need_close) + { + /* close \g argument file/pipe */ + if (is_pipe) + { + pclose(copystream); + restore_sigpipe_trap(); + } + else + { + fclose(copystream); + } + } } else { - fclose(copystream); + /* COPY IN */ + copystream = pset.copyStream ? pset.copyStream : pset.cur_cmd_source; + success = handleCopyIn(pset.db, + copystream, + PQbinaryTuples(*results), + ©_result) && success; } + ResetCancelConn(); + + /* + * Replace the PGRES_COPY_OUT/IN result with COPY command's exit + * status, or with NULL if we want to suppress printing anything. + */ + PQclear(*results); + *results = copy_result; } - } - else - { - /* COPY IN */ - copystream = pset.copyStream ? pset.copyStream : pset.cur_cmd_source; - success = handleCopyIn(pset.db, - copystream, - PQbinaryTuples(*result), - ©_result); + else if (first_cycle) + { + /* fast path: no COPY commands; PQexec visited all results */ + break; + } + + /* + * Check PQgetResult() again. In the typical case of a single-command + * string, it will return NULL. Otherwise, we'll have other results + * to process that may include other COPYs. We keep the last result. + */ + next_result = PQgetResult(pset.db); + if (!next_result) + break; + + PQclear(*results); + *results = next_result; + first_cycle = false; } - ResetCancelConn(); - PQclear(*result); - *result = copy_result; + SetResultVariables(*results, success); + + /* may need this to recover from conn loss during COPY */ + if (!first_cycle && !CheckConnection()) + return false; return success; } + /* * PrintQueryStatus: report command status as required * - * Note: Utility function for use by HandleQueryResult() only. + * Note: Utility function for use by PrintQueryResults() only. */ static void PrintQueryStatus(PGresult *results) @@ -974,50 +1105,43 @@ PrintQueryStatus(PGresult *results) /* - * HandleQueryResult: print out, store or execute one query result - * as required. + * PrintQueryResults: print out (or store or execute) query results as required + * + * Note: Utility function for use by SendQuery() only. * * Returns true if the query executed successfully, false otherwise. */ static bool -HandleQueryResult(PGresult *result, bool last) +PrintQueryResults(PGresult *results) { bool success; const char *cmdstatus; - if (result == NULL) + if (!results) return false; - switch (PQresultStatus(result)) + switch (PQresultStatus(results)) { case PGRES_TUPLES_OK: /* store or execute or print the data ... */ - if (last && pset.gset_prefix) - success = StoreQueryTuple(result); - else if (last && pset.gexec_flag) - success = ExecQueryTuples(result); - else if (last && pset.crosstab_flag) - success = PrintResultsInCrosstab(result); - else if (last || pset.show_all_results) - success = PrintQueryTuples(result); + if (pset.gset_prefix) + success = StoreQueryTuple(results); + else if (pset.gexec_flag) + success = ExecQueryTuples(results); + else if (pset.crosstab_flag) + success = PrintResultsInCrosstab(results); else - success = true; - + success = PrintQueryTuples(results); /* if it's INSERT/UPDATE/DELETE RETURNING, also print status */ - if (last || pset.show_all_results) - { - cmdstatus = PQcmdStatus(result); - if (strncmp(cmdstatus, "INSERT", 6) == 0 || - strncmp(cmdstatus, "UPDATE", 6) == 0 || - strncmp(cmdstatus, "DELETE", 6) == 0) - PrintQueryStatus(result); - } - + cmdstatus = PQcmdStatus(results); + if (strncmp(cmdstatus, "INSERT", 6) == 0 || + strncmp(cmdstatus, "UPDATE", 6) == 0 || + strncmp(cmdstatus, "DELETE", 6) == 0) + PrintQueryStatus(results); break; case PGRES_COMMAND_OK: - if (last || pset.show_all_results) - PrintQueryStatus(result); + PrintQueryStatus(results); success = true; break; @@ -1027,7 +1151,7 @@ HandleQueryResult(PGresult *result, bool last) case PGRES_COPY_OUT: case PGRES_COPY_IN: - /* nothing to do here: already processed */ + /* nothing to do here */ success = true; break; @@ -1040,7 +1164,7 @@ HandleQueryResult(PGresult *result, bool last) default: success = false; pg_log_error("unexpected PQresultStatus: %d", - PQresultStatus(result)); + PQresultStatus(results)); break; } @@ -1049,217 +1173,6 @@ HandleQueryResult(PGresult *result, bool last) return success; } -/* - * Data structure and functions to record notices while they are - * emitted, so that they can be shown later. - * - * We need to know which result is last, which requires to extract - * one result in advance, hence two buffers are needed. - */ -typedef struct { - bool in_flip; - PQExpBufferData flip; - PQExpBufferData flop; -} t_notice_messages; - -/* - * Store notices in appropriate buffer, for later display. - */ -static void -AppendNoticeMessage(void *arg, const char *msg) -{ - t_notice_messages *notes = (t_notice_messages*) arg; - appendPQExpBufferStr(notes->in_flip ? ¬es->flip : ¬es->flop, msg); -} - -/* - * Show notices stored in buffer, which is then reset. - */ -static void -ShowNoticeMessage(t_notice_messages *notes) -{ - PQExpBufferData *current = notes->in_flip ? ¬es->flip : ¬es->flop; - if (current->data != NULL && *current->data != '\0') - pg_log_info("%s", current->data); - resetPQExpBuffer(current); -} - -/* - * SendQueryAndProcessResults: utility function for use by SendQuery() - * and PSQLexecWatch(). - * - * Sends query and cycles through PGresult objects. - * - * When not under \watch and if our command string contained a COPY FROM STDIN - * or COPY TO STDOUT, the PGresult associated with these commands must be - * processed by providing an input or output stream. In that event, we'll - * marshal data for the COPY. - * - * For other commands, the results are processed normally, depending on their - * status. - * - * Returns 1 on complete success, 0 on interrupt and -1 or errors. Possible - * failure modes include purely client-side problems; check the transaction - * status for the server-side opinion. - * - * Note that on a combined query, failure does not mean that nothing was - * committed. - */ -static int -SendQueryAndProcessResults(const char *query, double *pelapsed_msec, bool is_watch) -{ - bool success; - instr_time before; - PGresult *result; - t_notice_messages notes; - - if (pset.timing) - INSTR_TIME_SET_CURRENT(before); - - success = PQsendQuery(pset.db, query); - ResetCancelConn(); - - if (!success) - { - const char *error = PQerrorMessage(pset.db); - - if (strlen(error)) - pg_log_info("%s", error); - - CheckConnection(); - - return -1; - } - - /* - * 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 (is_watch && cancel_pressed) - { - ClearOrSaveAllResults(); - return 0; - } - - /* intercept notices */ - notes.in_flip = true; - initPQExpBuffer(¬es.flip); - initPQExpBuffer(¬es.flop); - PQsetNoticeProcessor(pset.db, AppendNoticeMessage, ¬es); - - /* first result */ - result = PQgetResult(pset.db); - - while (result != NULL) - { - ExecStatusType result_status; - PGresult *next_result; - bool last; - - if (!AcceptResult(result, false)) - { - /* - * Some error occured, either a server-side failure or - * a failure to submit the command string. Record that. - */ - const char *error = PQerrorMessage(pset.db); - - ShowNoticeMessage(¬es); - if (strlen(error)) - pg_log_info("%s", error); - CheckConnection(); - if (!is_watch) - SetResultVariables(result, false); - ClearOrSaveResult(result); - success = false; - - /* and switch to next result */ - result = PQgetResult(pset.db); - continue; - } - - /* must handle COPY before changing the current result */ - result_status = PQresultStatus(result); - Assert(result_status != PGRES_COPY_BOTH); - if (result_status == PGRES_COPY_IN || - result_status == PGRES_COPY_OUT) - { - ShowNoticeMessage(¬es); - - if (is_watch) - { - ClearOrSaveAllResults(); - pg_log_error("\\watch cannot be used with COPY"); - return -1; - } - - /* use normal notice processor during COPY */ - PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL); - - success &= HandleCopyResult(&result); - - PQsetNoticeProcessor(pset.db, AppendNoticeMessage, ¬es); - } - - /* - * Check PQgetResult() again. In the typical case of a single-command - * string, it will return NULL. Otherwise, we'll have other results - * to process. - */ - notes.in_flip = !notes.in_flip; - next_result = PQgetResult(pset.db); - notes.in_flip = !notes.in_flip; - last = (next_result == NULL); - - /* - * Get timing measure before printing the last result. - * - * It will include the display of previous results, if any. - * This cannot be helped because the server goes on processing - * further queries anyway while the previous ones are being displayed. - * The parallel execution of the client display hides the server time - * when it is shorter. - * - * With combined queries, timing must be understood as an upper bound - * of the time spent processing them. - */ - if (last && pset.timing) - { - instr_time now; - INSTR_TIME_SET_CURRENT(now); - INSTR_TIME_SUBTRACT(now, before); - *pelapsed_msec = INSTR_TIME_GET_MILLISEC(now); - } - - /* notices already shown above for copy */ - ShowNoticeMessage(¬es); - - /* this may or may not print something depending on settings */ - if (result != NULL) - success &= HandleQueryResult(result, last); - - /* set variables on last result if all went well */ - if (!is_watch && last && success) - SetResultVariables(result, true); - - ClearOrSaveResult(result); - notes.in_flip = !notes.in_flip; - result = next_result; - } - - /* reset notice hook */ - PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL); - termPQExpBuffer(¬es.flip); - termPQExpBuffer(¬es.flop); - - /* may need this to recover from conn loss during COPY */ - if (!CheckConnection()) - return -1; - - return success ? 1 : -1; -} - /* * SendQuery: send the query string to the backend @@ -1381,9 +1294,28 @@ SendQuery(const char *query) pset.crosstab_flag || !is_select_command(query)) { /* Default fetch-it-all-and-print mode */ - int res = SendQueryAndProcessResults(query, &elapsed_msec, false); - OK = (res >= 0); - results = NULL; + instr_time before, + after; + + if (pset.timing) + INSTR_TIME_SET_CURRENT(before); + + results = PQexec(pset.db, query); + + /* these operations are included in the timing result: */ + ResetCancelConn(); + OK = ProcessResult(&results); + + if (pset.timing) + { + INSTR_TIME_SET_CURRENT(after); + INSTR_TIME_SUBTRACT(after, before); + elapsed_msec = INSTR_TIME_GET_MILLISEC(after); + } + + /* but printing results isn't: */ + if (OK && results) + OK = PrintQueryResults(results); } else { @@ -1565,7 +1497,7 @@ DescribeQuery(const char *query, double *elapsed_msec) PQclear(results); results = PQdescribePrepared(pset.db, ""); - OK = AcceptResult(results, true) && + OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); if (OK && results) { @@ -1613,7 +1545,7 @@ DescribeQuery(const char *query, double *elapsed_msec) PQclear(results); results = PQexec(pset.db, buf.data); - OK = AcceptResult(results, true); + OK = AcceptResult(results); if (pset.timing) { @@ -1623,7 +1555,7 @@ DescribeQuery(const char *query, double *elapsed_msec) } if (OK && results) - OK = HandleQueryResult(results, true); + OK = PrintQueryResults(results); termPQExpBuffer(&buf); } @@ -1682,7 +1614,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec) if (PQtransactionStatus(pset.db) == PQTRANS_IDLE) { results = PQexec(pset.db, "BEGIN"); - OK = AcceptResult(results, true) && + OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ClearOrSaveResult(results); if (!OK) @@ -1696,7 +1628,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec) query); results = PQexec(pset.db, buf.data); - OK = AcceptResult(results, true) && + OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); if (!OK) SetResultVariables(results, OK); @@ -1769,7 +1701,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec) is_pager = false; } - OK = AcceptResult(results, true); + OK = AcceptResult(results); Assert(!OK); SetResultVariables(results, OK); ClearOrSaveResult(results); @@ -1878,7 +1810,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec) results = PQexec(pset.db, "CLOSE _psql_cursor"); if (OK) { - OK = AcceptResult(results, true) && + OK = AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ClearOrSaveResult(results); } @@ -1888,7 +1820,7 @@ ExecQueryUsingCursor(const char *query, double *elapsed_msec) if (started_txn) { results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK"); - OK &= AcceptResult(results, true) && + OK &= AcceptResult(results) && (PQresultStatus(results) == PGRES_COMMAND_OK); ClearOrSaveResult(results); } diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 36501d5e2b99d..8e3bb38ab1e05 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -411,8 +411,6 @@ helpVariables(unsigned short int pager) fprintf(output, _(" SERVER_VERSION_NAME\n" " SERVER_VERSION_NUM\n" " server's version (in short string or numeric format)\n")); - fprintf(output, _(" SHOW_ALL_RESULTS\n" - " show all results of a combined query (\\;) instead of only the last\n")); fprintf(output, _(" SHOW_CONTEXT\n" " controls display of message context fields [never, errors, always]\n")); fprintf(output, _(" SINGLELINE\n" diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h index 62583ad6ca6fc..83f2e6f254edd 100644 --- a/src/bin/psql/settings.h +++ b/src/bin/psql/settings.h @@ -148,7 +148,6 @@ typedef struct _psqlSettings const char *prompt2; const char *prompt3; PGVerbosity verbosity; /* current error verbosity level */ - bool show_all_results; PGContextVisibility show_context; /* current context display level */ } PsqlSettings; diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 17437ce07dd13..110906a4e959b 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -196,7 +196,6 @@ main(int argc, char *argv[]) SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1); SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2); SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3); - SetVariableBool(pset.vars, "SHOW_ALL_RESULTS"); parse_psql_options(argc, argv, &options); @@ -1131,12 +1130,6 @@ verbosity_hook(const char *newval) return true; } -static bool -show_all_results_hook(const char *newval) -{ - return ParseVariableBool(newval, "SHOW_ALL_RESULTS", &pset.show_all_results); -} - static char * show_context_substitute_hook(char *newval) { @@ -1238,9 +1231,6 @@ EstablishVariableSpace(void) SetVariableHooks(pset.vars, "VERBOSITY", verbosity_substitute_hook, verbosity_hook); - SetVariableHooks(pset.vars, "SHOW_ALL_RESULTS", - bool_substitute_hook, - show_all_results_hook); SetVariableHooks(pset.vars, "SHOW_CONTEXT", show_context_substitute_hook, show_context_hook); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index d34271e3b8799..cfd0a840c7ca4 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -4139,7 +4139,7 @@ psql_completion(const char *text, int start, int end) matches = complete_from_variables(text, "", "", false); else if (TailMatchesCS("\\set", MatchAny)) { - if (TailMatchesCS("AUTOCOMMIT|ON_ERROR_STOP|QUIET|SHOW_ALL_RESULTS|" + if (TailMatchesCS("AUTOCOMMIT|ON_ERROR_STOP|QUIET|" "SINGLELINE|SINGLESTEP")) COMPLETE_WITH_CS("on", "off"); else if (TailMatchesCS("COMP_KEYWORD_CASE")) diff --git a/src/test/regress/expected/copyselect.out b/src/test/regress/expected/copyselect.out index bb9e026f913ae..72865fe1ebeea 100644 --- a/src/test/regress/expected/copyselect.out +++ b/src/test/regress/expected/copyselect.out @@ -126,7 +126,7 @@ copy (select 1) to stdout\; select 1/0; -- row, then error ERROR: division by zero select 1/0\; copy (select 1) to stdout; -- error only ERROR: division by zero -copy (select 1) to stdout\; copy (select 2) to stdout\; select 3\; select 4; -- 1 2 3 4 +copy (select 1) to stdout\; copy (select 2) to stdout\; select 0\; select 3; -- 1 2 3 1 2 ?column? @@ -134,18 +134,8 @@ copy (select 1) to stdout\; copy (select 2) to stdout\; select 3\; select 4; -- 3 (1 row) - ?column? ----------- - 4 -(1 row) - create table test3 (c int); -select 0\; copy test3 from stdin\; copy test3 from stdin\; select 1; -- 0 1 - ?column? ----------- - 0 -(1 row) - +select 0\; copy test3 from stdin\; copy test3 from stdin\; select 1; -- 1 ?column? ---------- 1 diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 672937b2f8872..49139dd3633bd 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -5179,96 +5179,3 @@ List of access methods pg_catalog | && | anyarray | anyarray | boolean | overlaps (1 row) --- --- combined queries --- -CREATE FUNCTION warn(msg TEXT) RETURNS BOOLEAN LANGUAGE plpgsql -AS $$ - BEGIN RAISE NOTICE 'warn %', msg ; RETURN TRUE ; END -$$; --- show both -SELECT 1 AS one \; SELECT warn('1.5') \; SELECT 2 AS two ; - one ------ - 1 -(1 row) - -NOTICE: warn 1.5 -CONTEXT: PL/pgSQL function warn(text) line 2 at RAISE - warn ------- - t -(1 row) - - two ------ - 2 -(1 row) - --- \gset applies to last query only -SELECT 3 AS three \; SELECT warn('3.5') \; SELECT 4 AS four \gset - three -------- - 3 -(1 row) - -NOTICE: warn 3.5 -CONTEXT: PL/pgSQL function warn(text) line 2 at RAISE - warn ------- - t -(1 row) - -\echo :three :four -:three 4 --- syntax error stops all processing -SELECT 5 \; SELECT 6 + \; SELECT warn('6.5') \; SELECT 7 ; -ERROR: syntax error at or near ";" -LINE 1: SELECT 5 ; SELECT 6 + ; SELECT warn('6.5') ; SELECT 7 ; - ^ --- with aborted transaction, stop on first error -BEGIN \; SELECT 8 AS eight \; SELECT 9/0 AS nine \; ROLLBACK \; SELECT 10 AS ten ; - eight -------- - 8 -(1 row) - -ERROR: division by zero --- close previously aborted transaction -ROLLBACK; --- misc SQL commands --- (non SELECT output is sent to stderr, thus is not shown in expected results) -SELECT 'ok' AS "begin" \; -CREATE TABLE psql_comics(s TEXT) \; -INSERT INTO psql_comics VALUES ('Calvin'), ('hobbes') \; -COPY psql_comics FROM STDIN \; -UPDATE psql_comics SET s = 'Hobbes' WHERE s = 'hobbes' \; -DELETE FROM psql_comics WHERE s = 'Moe' \; -COPY psql_comics TO STDOUT \; -TRUNCATE psql_comics \; -DROP TABLE psql_comics \; -SELECT 'ok' AS "done" ; - begin -------- - ok -(1 row) - -Calvin -Susie -Hobbes - done ------- - ok -(1 row) - -\set SHOW_ALL_RESULTS off -SELECT 1 AS one \; SELECT warn('1.5') \; SELECT 2 AS two ; -NOTICE: warn 1.5 -CONTEXT: PL/pgSQL function warn(text) line 2 at RAISE - two ------ - 2 -(1 row) - -\set SHOW_ALL_RESULTS on -DROP FUNCTION warn(TEXT); diff --git a/src/test/regress/expected/transactions.out b/src/test/regress/expected/transactions.out index be1db0d5c0b53..61862d595d1ab 100644 --- a/src/test/regress/expected/transactions.out +++ b/src/test/regress/expected/transactions.out @@ -900,18 +900,8 @@ DROP TABLE abc; -- tests rely on the fact that psql will not break SQL commands apart at a -- backslash-quoted semicolon, but will send them as one Query. create temp table i_table (f1 int); --- psql will show all results of a multi-statement Query +-- psql will show only the last result in a multi-statement Query SELECT 1\; SELECT 2\; SELECT 3; - ?column? ----------- - 1 -(1 row) - - ?column? ----------- - 2 -(1 row) - ?column? ---------- 3 @@ -926,12 +916,6 @@ insert into i_table values(1)\; select * from i_table; -- 1/0 error will cause rolling back the whole implicit transaction insert into i_table values(2)\; select * from i_table\; select 1/0; - f1 ----- - 1 - 2 -(2 rows) - ERROR: division by zero select * from i_table; f1 @@ -951,18 +935,8 @@ WARNING: there is no transaction in progress -- begin converts implicit transaction into a regular one that -- can extend past the end of the Query select 1\; begin\; insert into i_table values(5); - ?column? ----------- - 1 -(1 row) - commit; select 1\; begin\; insert into i_table values(6); - ?column? ----------- - 1 -(1 row) - rollback; -- commit in implicit-transaction state commits but issues a warning. insert into i_table values(7)\; commit\; insert into i_table values(8)\; select 1/0; @@ -989,52 +963,22 @@ rollback; -- we are not in a transaction at this point WARNING: there is no transaction in progress -- implicit transaction block is still a transaction block, for e.g. VACUUM SELECT 1\; VACUUM; - ?column? ----------- - 1 -(1 row) - ERROR: VACUUM cannot run inside a transaction block SELECT 1\; COMMIT\; VACUUM; WARNING: there is no transaction in progress - ?column? ----------- - 1 -(1 row) - ERROR: VACUUM cannot run inside a transaction block -- we disallow savepoint-related commands in implicit-transaction state SELECT 1\; SAVEPOINT sp; - ?column? ----------- - 1 -(1 row) - ERROR: SAVEPOINT can only be used in transaction blocks SELECT 1\; COMMIT\; SAVEPOINT sp; WARNING: there is no transaction in progress - ?column? ----------- - 1 -(1 row) - ERROR: SAVEPOINT can only be used in transaction blocks ROLLBACK TO SAVEPOINT sp\; SELECT 2; ERROR: ROLLBACK TO SAVEPOINT can only be used in transaction blocks SELECT 2\; RELEASE SAVEPOINT sp\; SELECT 3; - ?column? ----------- - 2 -(1 row) - ERROR: RELEASE SAVEPOINT can only be used in transaction blocks -- but this is OK, because the BEGIN converts it to a regular xact SELECT 1\; BEGIN\; SAVEPOINT sp\; ROLLBACK TO SAVEPOINT sp\; COMMIT; - ?column? ----------- - 1 -(1 row) - -- Tests for AND CHAIN in implicit transaction blocks SET TRANSACTION READ ONLY\; COMMIT AND CHAIN; -- error ERROR: COMMIT AND CHAIN can only be used in transaction blocks diff --git a/src/test/regress/sql/copyselect.sql b/src/test/regress/sql/copyselect.sql index e32a4f8e38e52..1d98dad3c8c55 100644 --- a/src/test/regress/sql/copyselect.sql +++ b/src/test/regress/sql/copyselect.sql @@ -84,10 +84,10 @@ drop table test1; -- psql handling of COPY in multi-command strings copy (select 1) to stdout\; select 1/0; -- row, then error select 1/0\; copy (select 1) to stdout; -- error only -copy (select 1) to stdout\; copy (select 2) to stdout\; select 3\; select 4; -- 1 2 3 4 +copy (select 1) to stdout\; copy (select 2) to stdout\; select 0\; select 3; -- 1 2 3 create table test3 (c int); -select 0\; copy test3 from stdin\; copy test3 from stdin\; select 1; -- 0 1 +select 0\; copy test3 from stdin\; copy test3 from stdin\; select 1; -- 1 1 \. 2 diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql index f90a0270fc335..68121d171cd91 100644 --- a/src/test/regress/sql/psql.sql +++ b/src/test/regress/sql/psql.sql @@ -1241,41 +1241,3 @@ drop role regress_partitioning_role; \dfa bit* small* \do - pg_catalog.int4 \do && anyarray * - --- --- combined queries --- -CREATE FUNCTION warn(msg TEXT) RETURNS BOOLEAN LANGUAGE plpgsql -AS $$ - BEGIN RAISE NOTICE 'warn %', msg ; RETURN TRUE ; END -$$; --- show both -SELECT 1 AS one \; SELECT warn('1.5') \; SELECT 2 AS two ; --- \gset applies to last query only -SELECT 3 AS three \; SELECT warn('3.5') \; SELECT 4 AS four \gset -\echo :three :four --- syntax error stops all processing -SELECT 5 \; SELECT 6 + \; SELECT warn('6.5') \; SELECT 7 ; --- with aborted transaction, stop on first error -BEGIN \; SELECT 8 AS eight \; SELECT 9/0 AS nine \; ROLLBACK \; SELECT 10 AS ten ; --- close previously aborted transaction -ROLLBACK; --- misc SQL commands --- (non SELECT output is sent to stderr, thus is not shown in expected results) -SELECT 'ok' AS "begin" \; -CREATE TABLE psql_comics(s TEXT) \; -INSERT INTO psql_comics VALUES ('Calvin'), ('hobbes') \; -COPY psql_comics FROM STDIN \; -UPDATE psql_comics SET s = 'Hobbes' WHERE s = 'hobbes' \; -DELETE FROM psql_comics WHERE s = 'Moe' \; -COPY psql_comics TO STDOUT \; -TRUNCATE psql_comics \; -DROP TABLE psql_comics \; -SELECT 'ok' AS "done" ; -Moe -Susie -\. -\set SHOW_ALL_RESULTS off -SELECT 1 AS one \; SELECT warn('1.5') \; SELECT 2 AS two ; -\set SHOW_ALL_RESULTS on -DROP FUNCTION warn(TEXT); diff --git a/src/test/regress/sql/transactions.sql b/src/test/regress/sql/transactions.sql index 7fc9f094680b4..8886280c0a628 100644 --- a/src/test/regress/sql/transactions.sql +++ b/src/test/regress/sql/transactions.sql @@ -504,7 +504,7 @@ DROP TABLE abc; create temp table i_table (f1 int); --- psql will show all results of a multi-statement Query +-- psql will show only the last result in a multi-statement Query SELECT 1\; SELECT 2\; SELECT 3; -- this implicitly commits: