8000 Fix unwanted flushing of libpq's input buffer when socket EOF is seen. · home201448/postgres@db6e8e1 · GitHub
[go: up one dir, main page]

Skip to content

Commit db6e8e1

Browse files
committed
Fix unwanted flushing of libpq's input buffer when socket EOF is seen.
In commit 210eb9b I centralized libpq's logic for closing down the backend communication socket, and made the new pqDropConnection routine always reset the I/O buffers to empty. Many of the call sites previously had not had such code, and while that amounted to an oversight in some cases, there was one place where it was intentional and necessary *not* to flush the input buffer: pqReadData should never cause that to happen, since we probably still want to process whatever data we read. This is the true cause of the problem Robert was attempting to fix in c3e7c24, namely that libpq no longer reported the backend's final ERROR message before reporting "server closed the connection unexpectedly". But that only accidentally fixed it, by invoking parseInput before the input buffer got flushed; and very likely there are timing scenarios where we'd still lose the message before processing it. To fix, pass a flag to pqDropConnection to tell it whether to flush the input buffer or not. On review I think flushing is actually correct for every other call site. Back-patch to 9.3 where the problem was introduced. In HEAD, also improve the comments added by c3e7c24.
1 parent baa4228 commit db6e8e1

File tree

4 files changed

+27
-20
lines changed
  • src/interfaces/libpq

4 files changed

+27
-20
lines changed

src/interfaces/libpq/fe-connect.c

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -391,18 +391,24 @@ pgthreadlock_t pg_g_threadlock = default_threadlock;
391391
* Close any physical connection to the server, and reset associated
392392
* state inside the connection object. We don't release state that
393393
* would be needed to reconnect, though.
394+
*
395+
* We can always flush the output buffer, since there's no longer any hope
396+
* of sending that data. However, unprocessed input data might still be
397+
* valuable, so the caller must tell us whether to flush that or not.
394398
*/
395399
void
396-
pqDropConnection(PGconn *conn)
400+
pqDropConnection(PGconn *conn, bool flushInput)
397401
{
398402
/* Drop any SSL state */
399403
pqsecure_close(conn);
400404
/* Close the socket itself */
401405
if (conn->sock >= 0)
402406
closesocket(conn->sock);
403407
conn->sock = -1;
404-
/* Discard any unread/unsent data */
405-
conn->inStart = conn->inCursor = conn->inEnd = 0;
408+
/* Optionally discard any unread data */
409+
if (flushInput)
410+
conn->inStart = conn->inCursor = conn->inEnd = 0;
411+
/* Always discard any unsent data */
406412
conn->outCount = 0;
407413
}
408414

@@ -1493,7 +1499,7 @@ connectDBStart(PGconn *conn)
14931499
return 1;
14941500

14951501
connect_errReturn:
1496-
pqDropConnection(conn);
1502+
pqDropConnection(conn, true);
14971503
conn->status = CONNECTION_BAD;
14981504
return 0;
14991505
}
@@ -1731,7 +1737,7 @@ PQconnectPoll(PGconn *conn)
17311737
{
17321738
if (!connectNoDelay(conn))
17331739
{
1734-
pqDropConnection(conn);
1740+
pqDropConnection(conn, true);
17351741
conn->addr_cur = addr_cur->ai_next;
17361742
continue;
17371743
}
@@ -1741,7 +1747,7 @@ PQconnectPoll(PGconn *conn)
17411747
appendPQExpBuffer(&conn->errorMessage,
17421748
libpq_gettext("could not set socket to nonblocking mode: %s\n"),
17431749
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
1744-
pqDropConnection(conn);
1750+
pqDropConnection(conn, true);
17451751
conn->addr_cur = addr_cur->ai_next;
17461752
continue;
17471753
}
@@ -1752,7 +1758,7 @@ PQconnectPoll(PGconn *conn)
17521758
appendPQExpBuffer(&conn->errorMessage,
17531759
libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
17541760
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
1755-
pqDropConnection(conn);
1761+
pqDropConnection(conn, true);
17561762
conn->addr_cur = addr_cur->ai_next;
17571763
continue;
17581764
}
@@ -1799,7 +1805,7 @@ PQconnectPoll(PGconn *conn)
17991805

18001806
if (err)
18011807
{
1802-
pqDropConnection(conn);
1808+
pqDropConnection(conn, true);
18031809
conn->addr_cur = addr_cur->ai_next;
18041810
continue;
18051811
}
@@ -1886,7 +1892,7 @@ PQconnectPoll(PGconn *conn)
18861892
* failure and keep going if there are more addresses.
18871893
*/
18881894
connectFailureMessage(conn, SOCK_ERRNO);
1889-
pqDropConnection(conn);
1895+
pqDropConnection(conn, true);
18901896

18911897
/*
18921898
* Try the next address, if any.
@@ -1931,7 +1937,7 @@ PQconnectPoll(PGconn *conn)
19311937
* error message.
19321938
*/
19331939
connectFailureMessage(conn, optval);
1934-
pqDropConnection(conn);
1940+
pqDropConnection(conn, true);
19351941

19361942
/*
19371943
* If more addresses remain, keep trying, just as in the
@@ -2213,7 +2219,7 @@ PQconnectPoll(PGconn *conn)
22132219
/* only retry once */
22142220
conn->allow_ssl_try = false;
22152221
/* Must drop the old connection */
2216-
pqDropConnection(conn);
2222+
67E6 pqDropConnection(conn, true);
22172223
conn->status = CONNECTION_NEEDED;
22182224
goto keep_going;
22192225
}
@@ -2324,7 +2330,7 @@ PQconnectPoll(PGconn *conn)
23242330
{
23252331
conn->pversion = PG_PROTOCOL(2, 0);
23262332
/* Must drop the old connection */
2327-
pqDropConnection(conn);
2333+
pqDropConnection(conn, true);
23282334
conn->status = CONNECTION_NEEDED;
23292335
goto keep_going;
23302336
}
@@ -2390,7 +2396,7 @@ PQconnectPoll(PGconn *conn)
23902396
/* only retry once */
23912397
conn->wait_ssl_try = false;
23922398
/* Must drop the old connection */
2393-
pqDropConnection(conn);
2399+
pqDropConnection(conn, true);
23942400
conn->status = CONNECTION_NEEDED;
23952401
goto keep_going;
23962402
}
@@ -2406,7 +2412,7 @@ PQconnectPoll(PGconn *conn)
24062412
/* only retry once */
24072413
conn->allow_ssl_try = false;
24082414
/* Must drop the old connection */
2409-
pqDropConnection(conn);
2415+
pqDropConnection(conn, true);
24102416
conn->status = CONNECTION_NEEDED;
24112417
goto keep_going;
24122418
}
@@ -2567,7 +2573,7 @@ PQconnectPoll(PGconn *conn)
25672573
PQclear(res);
25682574
conn->send_appname = false;
25692575
/* Must drop the old connection */
2570-
pqDropConnection(conn);
2576+
pqDropConnection(conn, true);
25712577
conn->status = CONNECTION_NEEDED;
25722578
goto keep_going;
25732579
}
@@ -2962,7 +2968,7 @@ closePGconn(PGconn *conn)
29622968
/*
29632969
* Close the connection, reset all transient state, flush I/O buffers.
29642970
*/
2965-
pqDropConnection(conn);
2971+
pqDropConnection(conn, true);
29662972
conn F438 ->status = CONNECTION_BAD; /* Well, not really _bad_ - just
29672973
* absent */
29682974
conn->asyncStatus = PGASYNC_IDLE;

src/interfaces/libpq/fe-misc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,8 @@ pqReadData(PGconn *conn)
814814

815815
/* Come here if lower-level code already set a suitable errorMessage */
816816
definitelyFailed:
817-
pqDropConnection(conn);
817+
/* Do *not* drop any already-read data; caller still wants it */
818+
pqDropConnection(conn, false);
818819
conn->status = CONNECTION_BAD; /* No more connection to backend */
819820
return -1;
820821
}

src/interfaces/libpq/fe-protocol3.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,8 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
446446
/* build an error result holding the error message */
447447
pqSaveErrorResult(conn);
448448
conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */
449-
450-
pqDropConnection(conn);
449+
/* flush input data since we're giving up on processing it */
450+
pqDropConnection(conn, true);
451451
conn->status = CONNECTION_BAD; /* No more connection to backend */
452452
}
453453

src/interfaces/libpq/libpq-int.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ extern char *const pgresStatus[];
492492

493493
/* === in fe-connect.c === */
494494

495-
extern void pqDropConnection(PGconn *conn);
495+
extern void pqDropConnection(PGconn *conn, bool flushInput);
496496
extern int pqPacketSend(PGconn *conn, char pack_type,
497497
const void *buf, size_t buf_len);
498498
extern bool pqGetHomeDirectory(char *buf, int bufsize);

0 commit comments

Comments
 (0)
0