Re: Out-of-memory error reports in libpq - Mailing list pgsql-hackers
| From | Tom Lane |
|---|---|
| Subject | Re: Out-of-memory error reports in libpq |
| Date | |
| Msg-id | 670953.1627518330@sss.pgh.pa.us Whole thread Raw |
| In response to | Re: Out-of-memory error reports in libpq (Tom Lane <tgl@sss.pgh.pa.us>) |
| Responses |
Re: Out-of-memory error reports in libpq
|
| List | pgsql-hackers |
I wrote:
> Andres Freund <andres@anarazel.de> writes:
>> Hm. It seems we should be able to guarantee that the recovery path can print
>> something, at least in the PGconn case. Is it perhaps worth pre-sizing
>> PGConn->errorMessage so it'd fit an error like this?
>> But perhaps that's more effort than it's worth.
> Yeah. I considered changing things so that oom_buffer contains
> "out of memory\n" rather than an empty string, but I'm afraid
> that that's making unsupportable assumptions about what PQExpBuffers
> are used for.
Actually, wait a minute. There are only a couple of places that ever
read out the value of conn->errorMessage, so let's make those places
responsible for dealing with OOM scenarios. That leads to a nicely
small patch, as attached, and it looks to me like it makes us quite
bulletproof against such scenarios.
It might still be worth doing the "pqReportOOM" changes to save a
few bytes of code space, but I'm less excited about that now.
regards, tom lane
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index e950b41374..49eec3e835 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -6739,6 +6739,14 @@ PQerrorMessage(const PGconn *conn)
if (!conn)
return libpq_gettext("connection pointer is NULL\n");
+ /*
+ * The errorMessage buffer might be marked "broken" due to having
+ * previously failed to allocate enough memory for the message. In that
+ * case, tell the application we ran out of memory.
+ */
+ if (PQExpBufferBroken(&conn->errorMessage))
+ return libpq_gettext("out of memory\n");
+
return conn->errorMessage.data;
}
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index aca81890bb..87f348f3dc 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -191,7 +191,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
/* non-error cases */
break;
default:
- pqSetResultError(result, conn->errorMessage.data);
+ pqSetResultError(result, &conn->errorMessage);
break;
}
@@ -662,14 +662,28 @@ pqResultStrdup(PGresult *res, const char *str)
* assign a new error message to a PGresult
*/
void
-pqSetResultError(PGresult *res, const char *msg)
+pqSetResultError(PGresult *res, PQExpBuffer errorMessage)
{
+ char *msg;
+
if (!res)
return;
- if (msg && *msg)
- res->errMsg = pqResultStrdup(res, msg);
+
+ /*
+ * We handle two OOM scenarios here. The errorMessage buffer might be
+ * marked "broken" due to having previously failed to allocate enough
+ * memory for the message, or it might be fine but pqResultStrdup fails
+ * and returns NULL. In either case, just make res->errMsg point directly
+ * at a constant "out of memory" string.
+ */
+ if (!PQExpBufferBroken(errorMessage))
+ msg = pqResultStrdup(res, errorMessage->data);
+ else
+ msg = NULL;
+ if (msg)
+ res->errMsg = msg;
else
- res->errMsg = NULL;
+ res->errMsg = libpq_gettext("out of memory\n");
}
/*
@@ -2122,7 +2136,7 @@ PQgetResult(PGconn *conn)
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
res->events[i].name);
- pqSetResultError(res, conn->errorMessage.data);
+ pqSetResultError(res, &conn->errorMessage);
res->resultStatus = PGRES_FATAL_ERROR;
break;
}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index e9f214b61b..490458adef 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -637,7 +637,7 @@ extern pgthreadlock_t pg_g_threadlock;
/* === in fe-exec.c === */
-extern void pqSetResultError(PGresult *res, const char *msg);
+extern void pqSetResultError(PGresult *res, PQExpBuffer errorMessage);
extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
extern char *pqResultStrdup(PGresult *res, const char *str);
extern void pqClearAsyncResult(PGconn *conn);
pgsql-hackers by date: