commit 819f40496d1f9dda7428707ef1a6a908875862f6
Author: okbob@github.com <pavel.stehule@gmail.com>
Date:   Mon Jan 4 20:19:07 2021 +0100

    initial

diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index ec1fb4d1b9..cf4ba68f5b 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -880,26 +880,31 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 		else
 		{
 			/* end position */
-			int			E = S + length;
+			int			E;
 
-			/*
-			 * A negative value for L is the only way for the end position to
-			 * be before the start. SQL99 says to throw an error.
-			 */
-			if (E < S)
-				ereport(ERROR,
-						(errcode(ERRCODE_SUBSTRING_ERROR),
-						 errmsg("negative substring length not allowed")));
+			if (!pg_add_s32_overflow(S, length, &E))
+			{
+				/*
+				 * A negative value for L is the only way for the end position to
+				 * be before the start. SQL99 says to throw an error.
+				 */
+				if (E < S)
+					ereport(ERROR,
+							(errcode(ERRCODE_SUBSTRING_ERROR),
+							 errmsg("negative substring length not allowed")));
 
-			/*
-			 * A zero or negative value for the end position can happen if the
-			 * start was negative or one. SQL99 says to return a zero-length
-			 * string.
-			 */
-			if (E < 1)
-				return cstring_to_text("");
+				/*
+				 * A zero or negative value for the end position can happen if the
+				 * start was negative or one. SQL99 says to return a zero-length
+				 * string.
+				 */
+				if (E < 1)
+					return cstring_to_text("");
 
-			L1 = E - S1;
+				L1 = E - S1;
+			}
+			else
+				L1 = -1;
 		}
 
 		/*
@@ -944,36 +949,41 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 			slice_size = L1 = -1;
 		else
 		{
-			int			E = S + length;
+			int		E;
 
-			/*
-			 * A negative value for L is the only way for the end position to
-			 * be before the start. SQL99 says to throw an error.
-			 */
-			if (E < S)
-				ereport(ERROR,
-						(errcode(ERRCODE_SUBSTRING_ERROR),
-						 errmsg("negative substring length not allowed")));
+			if (!pg_add_s32_overflow(S, length, &E))
+			{
+				/*
+				 * A negative value for L is the only way for the end position to
+				 * be before the start. SQL99 says to throw an error.
+				 */
+				if (E < S)
+					ereport(ERROR,
+							(errcode(ERRCODE_SUBSTRING_ERROR),
+							 errmsg("negative substring length not allowed")));
 
-			/*
-			 * A zero or negative value for the end position can happen if the
-			 * start was negative or one. SQL99 says to return a zero-length
-			 * string.
-			 */
-			if (E < 1)
-				return cstring_to_text("");
+				/*
+				 * A zero or negative value for the end position can happen if the
+				 * start was negative or one. SQL99 says to return a zero-length
+				 * string.
+				 */
+				if (E < 1)
+					return cstring_to_text("");
 
-			/*
-			 * if E is past the end of the string, the tuple toaster will
-			 * truncate the length for us
-			 */
-			L1 = E - S1;
+				/*
+				 * if E is past the end of the string, the tuple toaster will
+				 * truncate the length for us
+				 */
+				L1 = E - S1;
 
-			/*
-			 * Total slice size in bytes can't be any longer than the start
-			 * position plus substring length times the encoding max length.
-			 */
-			slice_size = (S1 + L1) * eml;
+				/*
+				 * Total slice size in bytes can't be any longer than the start
+				 * position plus substring length times the encoding max length.
+				 */
+				slice_size = (S1 + L1) * eml;
+			}
+			else
+				slice_size = L1 = -1;
 		}
 
 		/*
