Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf896818

Browse files
committed
Fix handling of errors in libpq pipelines
The logic to keep the libpq command queue in sync with queries that havebeen processed had a bug when errors were returned for reasons otherthan problems in queries -- for example, when a connection is lost. Weincorrectly consumed an element from the command queue every time, butthis is wrong and can lead to the queue becoming empty ahead of time,leading to later malfunction: PQgetResult would return nothing,potentially causing the calling application to enter a busy loop.Fix by making the SYNC queue element a barrier that can only be consumedwhen a SYNC message is received.Backpatch to 14.Reported by: Иван Трофимов (Ivan Trofimov) <i.trofimow@yandex.ru>Discussion:https://postgr.es/m/17948-fcace7557e449957@postgresql.org
1 parentbe7b4d9 commitf896818

File tree

3 files changed

+49
-33
lines changed

3 files changed

+49
-33
lines changed

‎src/interfaces/libpq/fe-exec.c

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,30 +2078,22 @@ PQgetResult(PGconn *conn)
20782078

20792079
/*
20802080
* We're about to return the NULL that terminates the round of
2081-
* results from the current query; prepare to send the results
2082-
* of the next query, if any, when we're called next. If there's
2083-
* no next element in the command queue, this gets us in IDLE
2084-
* state.
2081+
* results from the current query; prepare to send the results of
2082+
* the next query, if any, when we're called next. If there's no
2083+
* next element in the command queue, this gets us in IDLE state.
20852084
*/
20862085
resetPQExpBuffer(&conn->errorMessage);
20872086
pqPipelineProcessQueue(conn);
20882087
res=NULL;/* query is complete */
20892088
break;
20902089

20912090
casePGASYNC_READY:
2092-
2093-
/*
2094-
* For any query type other than simple query protocol, we advance
2095-
* the command queue here. This is because for simple query
2096-
* protocol we can get the READY state multiple times before the
2097-
* command is actually complete, since the command string can
2098-
* contain many queries. In simple query protocol, the queue
2099-
* advance is done by fe-protocol3 when it receives ReadyForQuery.
2100-
*/
2101-
if (conn->cmd_queue_head&&
2102-
conn->cmd_queue_head->queryclass!=PGQUERY_SIMPLE)
2103-
pqCommandQueueAdvance(conn);
21042091
res=pqPrepareAsyncResult(conn);
2092+
2093+
/* Advance the queue as appropriate */
2094+
pqCommandQueueAdvance(conn, false,
2095+
res->resultStatus==PGRES_PIPELINE_SYNC);
2096+
21052097
if (conn->pipelineStatus!=PQ_PIPELINE_OFF)
21062098
{
21072099
/*
@@ -2165,7 +2157,7 @@ PQgetResult(PGconn *conn)
21652157
}
21662158
else
21672159
/* we won't ever see the Close */
2168-
pqCommandQueueAdvance(conn);
2160+
pqCommandQueueAdvance(conn, false, false);
21692161
}
21702162

21712163
if (res)
@@ -3027,26 +3019,52 @@ PQexitPipelineMode(PGconn *conn)
30273019

30283020
/*
30293021
* pqCommandQueueAdvance
3030-
*Remove one query from the command queue, when we receive
3031-
*all results from the server that pertain to it.
3022+
*Remove one query from the command queue, if appropriate.
3023+
*
3024+
* If we have received all results corresponding to the head element
3025+
* in the command queue, remove it.
3026+
*
3027+
* In simple query protocol we must not advance the command queue until the
3028+
* ReadyForQuery message has been received. This is because in simple mode a
3029+
* command can have multiple queries, and we must process result for all of
3030+
* them before moving on to the next command.
3031+
*
3032+
* Another consideration is synchronization during error processing in
3033+
* extended query protocol: we refuse to advance the queue past a SYNC queue
3034+
* element, unless the result we've received is also a SYNC. In particular
3035+
* this protects us from advancing when an error is received at an
3036+
* inappropriate moment.
30323037
*/
30333038
void
3034-
pqCommandQueueAdvance(PGconn*conn)
3039+
pqCommandQueueAdvance(PGconn*conn,boolisReadyForQuery,boolgotSync)
30353040
{
30363041
PGcmdQueueEntry*prevquery;
30373042

30383043
if (conn->cmd_queue_head==NULL)
30393044
return;
30403045

3041-
/* delink from queue */
3046+
/*
3047+
* If processing a query of simple query protocol, we only advance the
3048+
* queue when we receive the ReadyForQuery message for it.
3049+
*/
3050+
if (conn->cmd_queue_head->queryclass==PGQUERY_SIMPLE&& !isReadyForQuery)
3051+
return;
3052+
3053+
/*
3054+
* If we're waiting for a SYNC, don't advance the queue until we get one.
3055+
*/
3056+
if (conn->cmd_queue_head->queryclass==PGQUERY_SYNC&& !gotSync)
3057+
return;
3058+
3059+
/* delink element from queue */
30423060
prevquery=conn->cmd_queue_head;
30433061
conn->cmd_queue_head=conn->cmd_queue_head->next;
30443062

30453063
/* If the queue is now empty, reset the tail too */
30463064
if (conn->cmd_queue_head==NULL)
30473065
conn->cmd_queue_tail=NULL;
30483066

3049-
/* and makeit recyclable */
3067+
/* and makethe queue element recyclable */
30503068
prevquery->next=NULL;
30513069
pqRecycleCmdQueueEntry(conn,prevquery);
30523070
}
@@ -3070,6 +3088,7 @@ pqPipelineProcessQueue(PGconn *conn)
30703088
return;
30713089

30723090
casePGASYNC_IDLE:
3091+
30733092
/*
30743093
* If we're in IDLE mode and there's some command in the queue,
30753094
* get us into PIPELINE_IDLE mode and process normally. Otherwise

‎src/interfaces/libpq/fe-protocol3.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,8 @@ pqParseInput3(PGconn *conn)
240240
}
241241
else
242242
{
243-
/*
244-
* In simple query protocol, advance the command queue
245-
* (see PQgetResult).
246-
*/
247-
if (conn->cmd_queue_head&&
248-
conn->cmd_queue_head->queryclass==PGQUERY_SIMPLE)
249-
pqCommandQueueAdvance(conn);
243+
/* Advance the command queue and set us idle */
244+
pqCommandQueueAdvance(conn, true, false);
250245
conn->asyncStatus=PGASYNC_IDLE;
251246
}
252247
break;
@@ -287,19 +282,20 @@ pqParseInput3(PGconn *conn)
287282
/* Nothing to do for this message type */
288283
break;
289284
case'3':/* Close Complete */
285+
290286
/*
291287
* If we get CloseComplete when waiting for it, consume
292288
* the queue element and keep going. A result is not
293-
* expected from this message; it is just there so that
294-
*weknow to wait for it when PQsendQuery is used in
289+
* expected from this message; it is just there so that we
290+
* know to wait for it when PQsendQuery is used in
295291
* pipeline mode, before going in IDLE state. Failing to
296292
* do this makes us receive CloseComplete when IDLE, which
297293
* creates problems.
298294
*/
299295
if (conn->cmd_queue_head&&
300296
conn->cmd_queue_head->queryclass==PGQUERY_CLOSE)
301297
{
302-
pqCommandQueueAdvance(conn);
298+
pqCommandQueueAdvance(conn, false, false);
303299
}
304300

305301
break;

‎src/interfaces/libpq/libpq-int.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,8 @@ extern void pqSaveMessageField(PGresult *res, char code,
656656
externvoidpqSaveParameterStatus(PGconn*conn,constchar*name,
657657
constchar*value);
658658
externintpqRowProcessor(PGconn*conn,constchar**errmsgp);
659-
externvoidpqCommandQueueAdvance(PGconn*conn);
659+
externvoidpqCommandQueueAdvance(PGconn*conn,boolisReadyForQuery,
660+
boolgotSync);
660661
externintPQsendQueryContinue(PGconn*conn,constchar*query);
661662

662663
/* === in fe-protocol3.c === */

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp