diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index e1e2d18e3a4e07700445a787158fa8e42d833080..a97e73cf99d321e2f05e4a4c6bad7a1920d1478e 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -16,6 +16,7 @@
 
 #include <ctype.h>
 #include <fcntl.h>
+#include <limits.h>
 
 #include "libpq-fe.h"
 #include "libpq-int.h"
@@ -51,7 +52,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,
@@ -416,18 +418,26 @@ dupEvents(PGEvent *events, int count)
  * equal to PQntuples(res).  If it is equal, a new tuple is created and
  * added to the result.
  * Returns a non-zero value for success and zero for failure.
+ * (On failure, we report the specific problem via pqInternalNotice.)
  */
 int
 PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
 {
 	PGresAttValue *attval;
+	const char *errmsg = NULL;
 
+	/* Note that this check also protects us against null "res" */
 	if (!check_field_number(res, field_num))
 		return FALSE;
 
 	/* Invalid tup_num, must be <= ntups */
 	if (tup_num < 0 || tup_num > res->ntups)
+	{
+		pqInternalNotice(&res->noticeHooks,
+						 "row number %d is out of range 0..%d",
+						 tup_num, res->ntups);
 		return FALSE;
+	}
 
 	/* need to allocate a new tuple? */
 	if (tup_num == res->ntups)
@@ -440,7 +450,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 +460,8 @@ 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))
+			goto fail;
 	}
 
 	attval = &res->tuples[tup_num][field_num];
@@ -471,13 +481,24 @@ 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';
 	}
 
 	return TRUE;
+
+	/*
+	 * Report failure via pqInternalNotice.  If preceding code didn't provide
+	 * an error message, assume "out of memory" was meant.
+	 */
+fail:
+	if (!errmsg)
+		errmsg = libpq_gettext("out of memory");
+	pqInternalNotice(&res->noticeHooks, "%s", errmsg);
+
+	return FALSE;
 }
 
 /*
@@ -847,10 +868,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 prevented 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)
 	{
@@ -865,9 +889,36 @@ pqAddTuple(PGresult *res, PGresAttValue *tup)
 		 * existing allocation. Note that the positions beyond res->ntups are
 		 * garbage, not necessarily NULL.
 		 */
-		int			newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128;
+		int			newSize;
 		PGresAttValue **newTuples;
 
+		/*
+		 * Since we use integers for row numbers, we can't support more than
+		 * INT_MAX rows.  Make sure we allow that many, though.
+		 */
+		if (res->tupArrSize <= INT_MAX / 2)
+			newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128;
+		else if (res->tupArrSize < INT_MAX)
+			newSize = INT_MAX;
+		else
+		{
+			*errmsgp = libpq_gettext("PGresult cannot support more than INT_MAX tuples");
+			return FALSE;
+		}
+
+		/*
+		 * Also, on 32-bit platforms we could, in theory, overflow size_t even
+		 * before newSize gets to INT_MAX.  (In practice we'd doubtless hit
+		 * OOM long before that, but let's check.)
+		 */
+#if INT_MAX >= (SIZE_MAX / 2)
+		if (newSize > SIZE_MAX / sizeof(PGresAttValue *))
+		{
+			*errmsgp = libpq_gettext("size_t overflow");
+			return FALSE;
+		}
+#endif
+
 		if (res->tuples == NULL)
 			newTuples = (PGresAttValue **)
 				malloc(newSize * sizeof(PGresAttValue *));
@@ -1093,7 +1144,7 @@ pqRowProcessor(PGconn *conn, const char **errmsgp)
 	}
 
 	/* And add the tuple to the PGresult's tuple array */
-	if (!pqAddTuple(res, tup))
+	if (!pqAddTuple(res, tup, errmsgp))
 		goto fail;
 
 	/*