From 0661ed15366b921e73b5c2317bdefb848d00e100 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 28 Aug 2017 14:41:14 +0900
Subject: [PATCH 1/2] Refactor error message handling in pqAddTuple()

This routine is used by the plain PQsetvalue for example to copy a full
result set or by pqRowProcessor to process a new row received from the
backend on the frontend.

Thanks to this refactoring, this routine can allocate a proper error
message that the caller can use for proper sanity checks. This is now
rather useless, but additional sanity checks related to counter overflows
in pqAddTuple() will make use of this.

Per original complain from Igor Korot, which led to a patch suggested
by Tom Lane, written by Michael Paquier.

Discussion: http://postgr.es/m/CA+FnnTxyLWyjY1goewmJNxC==HQCCF4fKkoCTa9qR36oRAHDPw@mail.gmail.com
---
 src/interfaces/libpq/fe-exec.c | 64 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 54 insertions(+), 10 deletions(-)

diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index e1e2d18e3a..10961e679b 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -51,7 +51,8 @@ static bool static_std_strings = false;
 
 
 static PGEvent *dupEvents(PGEvent *events, int count);
-static bool pqAddTuple(PGresult *res, PGresAttValue *tup);
+static bool pqAddTuple(PGresult *res, PGresAttValue *tup,
+					   const char **errmsgp);
 static bool PQsendQueryStart(PGconn *conn);
 static int PQsendQueryGuts(PGconn *conn,
 				const char *command,
@@ -421,13 +422,27 @@ int
 PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
 {
 	PGresAttValue *attval;
+	PQExpBufferData errorBuf;
+	const char *errmsg = NULL;	/* NULL means "out of memory" */
+
+	initPQExpBuffer(&errorBuf);
 
 	if (!check_field_number(res, field_num))
-		return FALSE;
+	{
+		printfPQExpBuffer(&errorBuf,
+				  libpq_gettext("column number %d is out of range 0..%d"),
+				  field_num, res->numAttributes - 1);
+		goto fail;
+	}
 
 	/* Invalid tup_num, must be <= ntups */
 	if (tup_num < 0 || tup_num > res->ntups)
-		return FALSE;
+	{
+		printfPQExpBuffer(&errorBuf,
+						  libpq_gettext("tuple number %d is out of range 0..%d"),
+						  tup_num, res->ntups);
+		goto fail;
+	}
 
 	/* need to allocate a new tuple? */
 	if (tup_num == res->ntups)
@@ -440,7 +455,7 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
 						  TRUE);
 
 		if (!tup)
-			return FALSE;
+			goto fail;
 
 		/* initialize each column to NULL */
 		for (i = 0; i < res->numAttributes; i++)
@@ -450,8 +465,16 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
 		}
 
 		/* add it to the array */
-		if (!pqAddTuple(res, tup))
-			return FALSE;
+		if (!pqAddTuple(res, tup, &errmsg))
+		{
+			/*
+			 * Note that pqAddTuple() has already applied libpq_gettext,
+			 * and that the error message is NULL on out-of-memory.
+			 */
+			if (errmsg)
+				appendPQExpBufferStr(&errorBuf, errmsg);
+			goto fail;
+		}
 	}
 
 	attval = &res->tuples[tup_num][field_num];
@@ -471,13 +494,23 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
 	{
 		attval->value = (char *) pqResultAlloc(res, len + 1, TRUE);
 		if (!attval->value)
-			return FALSE;
+			goto fail;
 		attval->len = len;
 		memcpy(attval->value, value, len);
 		attval->value[len] = '\0';
 	}
 
+	termPQExpBuffer(&errorBuf);
 	return TRUE;
+
+fail:
+	/* save any error, nothing saved means "out of memory" */
+	if (errorBuf.len == 0)
+		printfPQExpBuffer(&errorBuf, libpq_gettext("out of memory"));
+
+	pqSetResultError(res, errorBuf.data);
+	termPQExpBuffer(&errorBuf);
+	return FALSE;
 }
 
 /*
@@ -847,10 +880,13 @@ pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
 /*
  * pqAddTuple
  *	  add a row pointer to the PGresult structure, growing it if necessary
- *	  Returns TRUE if OK, FALSE if not enough memory to add the row
+ *	  Returns TRUE if OK, FALSE if an error happened when adding the row.
+ *
+ * On error, *errmsgp can be set to an error string to be returned. If it
+ * is left NULL, the error is presumed to be "out of memory".
  */
 static bool
-pqAddTuple(PGresult *res, PGresAttValue *tup)
+pqAddTuple(PGresult *res, PGresAttValue *tup, const char **errmsgp)
 {
 	if (res->ntups >= res->tupArrSize)
 	{
@@ -875,7 +911,14 @@ pqAddTuple(PGresult *res, PGresAttValue *tup)
 			newTuples = (PGresAttValue **)
 				realloc(res->tuples, newSize * sizeof(PGresAttValue *));
 		if (!newTuples)
+		{
+			/*
+			 * malloc or realloc failed, let caller know about that
+			 * with a NULL error message.
+			 */
+			*errmsgp = NULL;
 			return FALSE;		/* malloc or realloc failed */
+		}
 		res->tupArrSize = newSize;
 		res->tuples = newTuples;
 	}
@@ -1093,7 +1136,8 @@ pqRowProcessor(PGconn *conn, const char **errmsgp)
 	}
 
 	/* And add the tuple to the PGresult's tuple array */
-	if (!pqAddTuple(res, tup))
+	*errmsgp = NULL;
+	if (!pqAddTuple(res, tup, errmsgp))
 		goto fail;
 
 	/*
-- 
2.14.1

