[PATCH v2] Make PQgetResult() not return NULL on out-of-memory error
Yugo Nagata <nagata@sraoss.co.jp>
From: Yugo Nagata <nagata@sraoss.co.jp>
To:
Date: 2025-11-10T16:51:01Z
Lists: pgsql-hackers
Previously, PQgetResult() returned NULL not only when no results remained
for a sent query, but also when an out-of-memory error occurred, except when
PGconn itself was NULL. As a result, users could not distinguish between query
completion and an out-of-memory error when PQgetResult() returned NULL.
This commit changes PQgetResult() to not return NULL in the case of an
out-of-memory error by modifying getCopyResult() so that it never returns
NULL, but instead returns OOM_result, as pqPipelineProcessQueue() does.
---
src/interfaces/libpq/fe-exec.c | 45 +++++++++++++++++++++++++++++-----
1 file changed, 39 insertions(+), 6 deletions(-)
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 0b1e37ec30b..8ca306687bf 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -905,6 +905,10 @@ pqPrepareAsyncResult(PGconn *conn)
*/
res = unconstify(PGresult *, &OOM_result);
+ pqDropConnection(conn, true);
+ conn->status = CONNECTION_BAD; /* No more connection to backend */
+ conn->asyncStatus = PGASYNC_IDLE;
+
/*
* Don't advance errorReported. Perhaps we'll be able to report
* the text later.
@@ -2061,8 +2065,7 @@ PQisBusy(PGconn *conn)
/*
* PQgetResult
* Get the next PGresult produced by a query. Returns NULL if no
- * query work remains or an error has occurred (e.g. out of
- * memory).
+ * query work remains.
*
* In pipeline mode, once all the result of a query have been returned,
* PQgetResult returns NULL to let the user know that the next
@@ -2170,7 +2173,12 @@ PQgetResult(PGconn *conn)
pqCommandQueueAdvance(conn, false,
res->resultStatus == PGRES_PIPELINE_SYNC);
- if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ if ((const PGresult *) res == &OOM_result)
+ {
+ /* In the OOM case, set the state to IDLE */
+ conn->asyncStatus = PGASYNC_IDLE;
+ }
+ else if (conn->pipelineStatus != PQ_PIPELINE_OFF)
{
/*
* We're about to send the results of the current query. Set
@@ -2200,8 +2208,16 @@ PQgetResult(PGconn *conn)
break;
case PGASYNC_READY_MORE:
res = pqPrepareAsyncResult(conn);
- /* Set the state back to BUSY, allowing parsing to proceed. */
- conn->asyncStatus = PGASYNC_BUSY;
+ if ((const PGresult *) res == &OOM_result)
+ {
+ /* In the OOM case, set the state to IDLE */
+ conn->asyncStatus = PGASYNC_IDLE;
+ }
+ else
+ {
+ /* Set the state back to BUSY, allowing parsing to proceed. */
+ conn->asyncStatus = PGASYNC_BUSY;
+ }
break;
case PGASYNC_COPY_IN:
res = getCopyResult(conn, PGRES_COPY_IN);
@@ -2234,6 +2250,8 @@ PQgetResult(PGconn *conn)
static PGresult *
getCopyResult(PGconn *conn, ExecStatusType copytype)
{
+ PGresult *res;
+
/*
* If the server connection has been lost, don't pretend everything is
* hunky-dory; instead return a PGRES_FATAL_ERROR result, and reset the
@@ -2254,7 +2272,22 @@ getCopyResult(PGconn *conn, ExecStatusType copytype)
return pqPrepareAsyncResult(conn);
/* Otherwise, invent a suitable PGresult */
- return PQmakeEmptyPGresult(conn, copytype);
+ res = PQmakeEmptyPGresult(conn, copytype);
+
+ /*
+ * Return the static OOM_result if out-of-memory. See the comments
+ * in pqPrepareAsyncResult().
+ */
+ if (!res)
+ {
+ res = unconstify(PGresult *, &OOM_result);
+
+ pqDropConnection(conn, true);
+ conn->status = CONNECTION_BAD; /* No more connection to backend */
+ conn->asyncStatus = PGASYNC_IDLE;
+ }
+
+ return res;
}
--
2.43.0
--Multipart=_Wed__12_Nov_2025_16_52_16_+0900_7X=O1DT3LSl/qfnM--