Skip to content

Commit 3dd36aa

Browse files
committed
In libpq, free any partial query result before collecting a server error.
We'd throw away the partial result anyway after parsing the error message. Throwing it away beforehand costs nothing and reduces the risk of out-of-memory failure. Also, at least in systems that behave like glibc/Linux, if the partial result was very large then the error PGresult would get allocated at high heap addresses, preventing the heap storage used by the partial result from being released to the OS until the error PGresult is freed. In psql >= 9.6, we hold onto the error PGresult until another error is received (for \errverbose), so that this behavior causes a seeming memory leak to persist for awhile, as in a recent complaint from Darafei Praliaskouski. This is a potential performance regression from older versions, justifying back-patching at least that far. But similar behavior may occur in other client applications, so it seems worth just back-patching to all supported branches. Discussion: https://postgr.es/m/CAC8Q8tJ=7cOkPePyAbJE_Pf691t8nDFhJp0KZxHvnq_uicfyVg@mail.gmail.com
1 parent f71d803 commit 3dd36aa

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

src/interfaces/libpq/fe-protocol2.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,14 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
967967
char *startp;
968968
char *splitp;
969969

970+
/*
971+
* If this is an error message, pre-emptively clear any incomplete query
972+
* result we may have. We'd just throw it away below anyway, and
973+
* releasing it before collecting the error might avoid out-of-memory.
974+
*/
975+
if (isError)
976+
pqClearAsyncResult(conn);
977+
970978
/*
971979
* Since the message might be pretty long, we create a temporary
972980
* PQExpBuffer rather than using conn->workBuffer. workBuffer is intended
@@ -1039,7 +1047,7 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
10391047
*/
10401048
if (isError)
10411049
{
1042-
pqClearAsyncResult(conn);
1050+
pqClearAsyncResult(conn); /* redundant, but be safe */
10431051
conn->result = res;
10441052
resetPQExpBuffer(&conn->errorMessage);
10451053
if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg)

src/interfaces/libpq/fe-protocol3.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,14 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
869869
const char *querytext = NULL;
870870
int querypos = 0;
871871

872+
/*
873+
* If this is an error message, pre-emptively clear any incomplete query
874+
* result we may have. We'd just throw it away below anyway, and
875+
* releasing it before collecting the error might avoid out-of-memory.
876+
*/
877+
if (isError)
878+
pqClearAsyncResult(conn);
879+
872880
/*
873881
* Since the fields might be pretty long, we create a temporary
874882
* PQExpBuffer rather than using conn->workBuffer. workBuffer is intended
@@ -1030,7 +1038,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
10301038
{
10311039
if (res)
10321040
res->errMsg = pqResultStrdup(res, workBuf.data);
1033-
pqClearAsyncResult(conn);
1041+
pqClearAsyncResult(conn); /* redundant, but be safe */
10341042
conn->result = res;
10351043
if (PQExpBufferDataBroken(workBuf))
10361044
printfPQExpBuffer(&conn->errorMessage,

0 commit comments

Comments
 (0)