On 2022-Nov-22, Peter Eisentraut wrote: > I added tests using the new psql \bind command to test this functionality in > the extended query protocol, which showed that this got broken since I first > wrote this patch. This "blame" is on the pipeline mode in libpq patch > (acb7e4eb6b1c614c68a62fb3a6a5bba1af0a2659). I need to spend more time on > this and figure out how to repair it.
Applying this patch, your test queries seem to work correctly. This is quite WIP, especially because there's a couple of scenarios uncovered by tests that I'd like to ensure correctness about, but if you would like to continue adding tests for extended query and dynamic result sets, it may be helpful. -- Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/ "How strange it is to find the words "Perl" and "saner" in such close proximity, with no apparent sense of irony. I doubt that Larry himself could have managed it." (ncm, http://lwn.net/Articles/174769/)
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index ec62550e38..b530c51ccd 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -2109,19 +2109,19 @@ PQgetResult(PGconn *conn) break; case PGASYNC_READY: + res = pqPrepareAsyncResult(conn); /* - * For any query type other than simple query protocol, we advance - * the command queue here. This is because for simple query - * protocol we can get the READY state multiple times before the - * command is actually complete, since the command string can - * contain many queries. In simple query protocol, the queue - * advance is done by fe-protocol3 when it receives ReadyForQuery. + * When an error has occurred, we consume one command from the + * queue for each result we return. (Normally, the command would + * be consumed as each result is read from the server.) */ if (conn->cmd_queue_head && - conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE) + (conn->error_result || + (conn->result != NULL && + conn->result->resultStatus == PGRES_FATAL_ERROR))) pqCommandQueueAdvance(conn); - res = pqPrepareAsyncResult(conn); + if (conn->pipelineStatus != PQ_PIPELINE_OFF) { /* diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 8ab6a88416..2ed74aa0f1 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -205,6 +205,10 @@ pqParseInput3(PGconn *conn) pqSaveErrorResult(conn); } } + if (conn->cmd_queue_head && + conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE) + pqCommandQueueAdvance(conn); + if (conn->result) strlcpy(conn->result->cmdStatus, conn->workBuffer.data, CMDSTATUS_LEN); @@ -231,6 +235,7 @@ pqParseInput3(PGconn *conn) else { conn->pipelineStatus = PQ_PIPELINE_ON; + pqCommandQueueAdvance(conn); conn->asyncStatus = PGASYNC_READY; } } @@ -257,6 +262,7 @@ pqParseInput3(PGconn *conn) pqSaveErrorResult(conn); } } + /* XXX should we advance the command queue here? */ conn->asyncStatus = PGASYNC_READY; break; case '1': /* Parse Complete */ @@ -274,6 +280,7 @@ pqParseInput3(PGconn *conn) pqSaveErrorResult(conn); } } + pqCommandQueueAdvance(conn); conn->asyncStatus = PGASYNC_READY; } break; @@ -324,6 +331,10 @@ pqParseInput3(PGconn *conn) * really possible with the current backend.) We stop * parsing until the application accepts the current * result. + * + * Note that we must have already read one 'T' message + * previously for the same command, so we do not + * advance the command queue here. */ conn->asyncStatus = PGASYNC_READY; return; @@ -355,6 +366,7 @@ pqParseInput3(PGconn *conn) } } conn->asyncStatus = PGASYNC_READY; + pqCommandQueueAdvance(conn); } break; case 't': /* Parameter Description */ @@ -593,14 +605,20 @@ getRowDescriptions(PGconn *conn, int msgLength) /* * If we're doing a Describe, we're done, and ready to pass the result * back to the client. + * + * XXX this coding here is a bit suspicious ... */ - if ((!conn->cmd_queue_head) || - (conn->cmd_queue_head && - conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE)) + if (!conn->cmd_queue_head) { conn->asyncStatus = PGASYNC_READY; return 0; } + else if (conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE) + { + conn->asyncStatus = PGASYNC_READY; + pqCommandQueueAdvance(conn); + return 0; + } /* * We could perform additional setup for the new result set here, but for