Skip to content

Commit c21d4c4

Browse files
committed
Further review for re-implementation of psql's FETCH_COUNT feature.
Alexander Lakhin noted an obsolete comment, which led me to revisit some other important comments in the patch, and that study turned up a couple of unintended ways in which the chunked-fetch code path didn't match the normal code path in ExecQueryAndProcessResults. The only nontrivial problem is that it didn't call PrintQueryStatus, so that we'd not print the final status result from DML ... RETURNING commands. To avoid code duplication, move the filter for whether a result is from RETURNING from PrintQueryResult to PrintQueryStatus. Discussion: https://postgr.es/m/0023bea5-79c0-476e-96c8-dad599cc3ad8@gmail.com
1 parent 0fe5f64 commit c21d4c4

File tree

1 file changed

+49
-29
lines changed

1 file changed

+49
-29
lines changed

src/bin/psql/common.c

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -832,10 +832,7 @@ ExecQueryTuples(const PGresult *result)
832832
c;
833833

834834
/*
835-
* We must turn off gexec_flag to avoid infinite recursion. Note that
836-
* this allows ExecQueryUsingCursor to be applied to the individual query
837-
* results. SendQuery prevents it from being applied when fetching the
838-
* queries-to-execute, because it can't handle recursion either.
835+
* We must turn off gexec_flag to avoid infinite recursion.
839836
*/
840837
pset.gexec_flag = false;
841838

@@ -955,30 +952,39 @@ HandleCopyResult(PGresult **resultp, FILE *copystream)
955952

956953
/*
957954
* PrintQueryStatus: report command status as required
958-
*
959-
* Note: Utility function for use by PrintQueryResult() only.
960955
*/
961956
static void
962957
PrintQueryStatus(PGresult *result, FILE *printQueryFout)
963958
{
964959
char buf[16];
960+
const char *cmdstatus = PQcmdStatus(result);
965961
FILE *fout = printQueryFout ? printQueryFout : pset.queryFout;
966962

963+
/* Do nothing if it's a TUPLES_OK result that isn't from RETURNING */
964+
if (PQresultStatus(result) == PGRES_TUPLES_OK)
965+
{
966+
if (!(strncmp(cmdstatus, "INSERT", 6) == 0 ||
967+
strncmp(cmdstatus, "UPDATE", 6) == 0 ||
968+
strncmp(cmdstatus, "DELETE", 6) == 0 ||
969+
strncmp(cmdstatus, "MERGE", 5) == 0))
970+
return;
971+
}
972+
967973
if (!pset.quiet)
968974
{
969975
if (pset.popt.topt.format == PRINT_HTML)
970976
{
971977
fputs("<p>", fout);
972-
html_escaped_print(PQcmdStatus(result), fout);
978+
html_escaped_print(cmdstatus, fout);
973979
fputs("</p>\n", fout);
974980
}
975981
else
976-
fprintf(fout, "%s\n", PQcmdStatus(result));
982+
fprintf(fout, "%s\n", cmdstatus);
977983
fflush(fout);
978984
}
979985

980986
if (pset.logfile)
981-
fprintf(pset.logfile, "%s\n", PQcmdStatus(result));
987+
fprintf(pset.logfile, "%s\n", cmdstatus);
982988

983989
snprintf(buf, sizeof(buf), "%u", (unsigned int) PQoidValue(result));
984990
SetVariable(pset.vars, "LASTOID", buf);
@@ -988,8 +994,6 @@ PrintQueryStatus(PGresult *result, FILE *printQueryFout)
988994
/*
989995
* PrintQueryResult: print out (or store or execute) query result as required
990996
*
991-
* Note: Utility function for use by SendQuery() only.
992-
*
993997
* last is true if this is the last result of a command string.
994998
* opt and printQueryFout are defined as for PrintQueryTuples.
995999
* printStatusFout is where to send command status; NULL means pset.queryFout.
@@ -1002,7 +1006,6 @@ PrintQueryResult(PGresult *result, bool last,
10021006
FILE *printStatusFout)
10031007
{
10041008
bool success;
1005-
const char *cmdstatus;
10061009

10071010
if (!result)
10081011
return false;
@@ -1027,14 +1030,7 @@ PrintQueryResult(PGresult *result, bool last,
10271030
* status.
10281031
*/
10291032
if (last || pset.show_all_results)
1030-
{
1031-
cmdstatus = PQcmdStatus(result);
1032-
if (strncmp(cmdstatus, "INSERT", 6) == 0 ||
1033-
strncmp(cmdstatus, "UPDATE", 6) == 0 ||
1034-
strncmp(cmdstatus, "DELETE", 6) == 0 ||
1035-
strncmp(cmdstatus, "MERGE", 5) == 0)
1036-
PrintQueryStatus(result, printStatusFout);
1037-
}
1033+
PrintQueryStatus(result, printStatusFout);
10381034

10391035
break;
10401036

@@ -1443,6 +1439,9 @@ DescribeQuery(const char *query, double *elapsed_msec)
14431439
* For other commands, the results are processed normally, depending on their
14441440
* status.
14451441
*
1442+
* When invoked from \watch, is_watch is true and min_rows is the value
1443+
* of that option, or 0 if it wasn't set.
1444+
*
14461445
* Returns 1 on complete success, 0 on interrupt and -1 or errors. Possible
14471446
* failure modes include purely client-side problems; check the transaction
14481447
* status for the server-side opinion.
@@ -1488,11 +1487,21 @@ ExecQueryAndProcessResults(const char *query,
14881487
}
14891488

14901489
/*
1491-
* Fetch the result in chunks if FETCH_COUNT is set. But we don't enable
1492-
* chunking if SHOW_ALL_RESULTS is false, since that requires us to
1493-
* accumulate all rows before we can tell what should be displayed, which
1494-
* would counter the idea of FETCH_COUNT. Chunking is also disabled when
1495-
* \crosstab, \gexec, \gset or \watch is used.
1490+
* Fetch the result in chunks if FETCH_COUNT is set, except when:
1491+
*
1492+
* * SHOW_ALL_RESULTS is false, since that requires us to complete the
1493+
* query before we can tell if its results should be displayed.
1494+
*
1495+
* * We're doing \crosstab, which likewise needs to see all the rows at
1496+
* once.
1497+
*
1498+
* * We're doing \gexec: we must complete the data fetch to make the
1499+
* connection free for issuing the resulting commands.
1500+
*
1501+
* * We're doing \gset: only one result row is allowed anyway.
1502+
*
1503+
* * We're doing \watch: users probably don't want us to force use of the
1504+
* pager for that, plus chunking could break the min_rows check.
14961505
*/
14971506
if (pset.fetch_count > 0 && pset.show_all_results &&
14981507
!pset.crosstab_flag && !pset.gexec_flag &&
@@ -1644,8 +1653,8 @@ ExecQueryAndProcessResults(const char *query,
16441653
/* If we have a chunked result, collect and print all chunks */
16451654
if (result_status == PGRES_TUPLES_CHUNK)
16461655
{
1647-
FILE *tuples_fout = printQueryFout ? printQueryFout : stdout;
1648-
printQueryOpt my_popt = pset.popt;
1656+
FILE *tuples_fout = printQueryFout ? printQueryFout : pset.queryFout;
1657+
printQueryOpt my_popt = opt ? *opt : pset.popt;
16491658
int64 total_tuples = 0;
16501659
bool is_pager = false;
16511660
int flush_error = 0;
@@ -1670,8 +1679,13 @@ ExecQueryAndProcessResults(const char *query,
16701679
do
16711680
{
16721681
/*
1673-
* display the current chunk of results, unless the output
1674-
* stream stopped working or we got cancelled
1682+
* Display the current chunk of results, unless the output
1683+
* stream stopped working or we got cancelled. We skip use of
1684+
* PrintQueryResult and go directly to printQuery, so that we
1685+
* can pass the correct is_pager value and because we don't
1686+
* want PrintQueryStatus to happen yet. Above, we rejected
1687+
* use of chunking for all cases in which PrintQueryResult
1688+
* would send the result to someplace other than printQuery.
16751689
*/
16761690
if (success && !flush_error && !cancel_pressed)
16771691
{
@@ -1710,6 +1724,12 @@ ExecQueryAndProcessResults(const char *query,
17101724
if (is_pager)
17111725
ClosePager(tuples_fout);
17121726

1727+
/*
1728+
* It's possible the data is from a RETURNING clause, in which
1729+
* case we need to print query status.
1730+
*/
1731+
PrintQueryStatus(result, printQueryFout);
1732+
17131733
/*
17141734
* We must do a fake SetResultVariables(), since we don't have
17151735
* a PGresult corresponding to the whole query.

0 commit comments

Comments
 (0)