From 9f4791f7a660171b6f57b1b336d6412d21ebd549 Mon Sep 17 00:00:00 2001
From: John Naylor <john.naylor@postgresql.org>
Date: Fri, 30 Jul 2021 13:50:23 -0400
Subject: [PATCH v1] Fix range check in ECPG numeric to int conversion

The previous coding guarded against -INT_MAX instead of INT_MIN,
leading to -2147483648 being rejected as out of range.

Per bug #17128 from Kevin Sweet

Discussion: https://www.postgresql.org/message-id/flat/17128-55a8a879727a3e3a%40postgresql.org
Reviewed-by: Tom Lane
Backpatch to all supported branches
---
 doc/src/sgml/ecpg.sgml                        |  2 +-
 src/interfaces/ecpg/pgtypeslib/numeric.c      |  7 ++++++-
 .../ecpg/test/expected/pgtypeslib-num_test.c  | 21 ++++++++++++++-----
 .../test/expected/pgtypeslib-num_test.stderr  |  2 +-
 .../test/expected/pgtypeslib-num_test.stdout  |  2 ++
 .../ecpg/test/pgtypeslib/num_test.pgc         | 13 +++++++++++-
 6 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index 9d5505cb84..04d4a6bdb2 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -8881,7 +8881,7 @@ int dectoint(decimal *np, int *ip);
         Note that the ECPG implementation differs from the <productname>Informix</productname>
         implementation. <productname>Informix</productname> limits an integer to the range from -32767 to
         32767, while the limits in the ECPG implementation depend on the
-        architecture (<literal>-INT_MAX .. INT_MAX</literal>).
+        architecture (<literal>INT_MIN .. INT_MAX</literal>).
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/interfaces/ecpg/pgtypeslib/numeric.c b/src/interfaces/ecpg/pgtypeslib/numeric.c
index 060fad7867..dbe4eb21e2 100644
--- a/src/interfaces/ecpg/pgtypeslib/numeric.c
+++ b/src/interfaces/ecpg/pgtypeslib/numeric.c
@@ -1505,12 +1505,17 @@ PGTYPESnumeric_to_int(numeric *nv, int *ip)
 	if ((i = PGTYPESnumeric_to_long(nv, &l)) != 0)
 		return i;
 
-	if (l < -INT_MAX || l > INT_MAX)
+/* silence compilers that might complain about useless tests */
+#if SIZEOF_LONG > SIZEOF_INT
+
+	if (l < INT_MIN || l > INT_MAX)
 	{
 		errno = PGTYPES_NUM_OVERFLOW;
 		return -1;
 	}
 
+#endif
+
 	*ip = (int) l;
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.c b/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.c
index bf312549b4..ddb78600e3 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.c
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.c
@@ -75,7 +75,7 @@ main(void)
 
 	double d;
 	long l1, l2;
-	int i;
+	int i, min, max;
 
 	ECPGdebug(1, stderr);
 	/* exec sql whenever sqlerror  do sqlprint ( ) ; */
@@ -174,17 +174,28 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
 	PGTYPESnumeric_free(value2);
 	PGTYPESnumeric_free(res);
 
+	/* check conversion of numeric to int */
+	value1 = PGTYPESnumeric_from_asc("-2147483648", NULL);
+	PGTYPESnumeric_to_int(value1, &min);
+	printf("min int = %d\n", min);
+	PGTYPESnumeric_free(value1);
+
+	value2 = PGTYPESnumeric_from_asc("2147483647", NULL);
+	PGTYPESnumeric_to_int(value2, &max);
+	printf("max int = %d\n", max);
+	PGTYPESnumeric_free(value2);
+
 	{ ECPGtrans(__LINE__, NULL, "rollback");
-#line 90 "num_test.pgc"
+#line 101 "num_test.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 90 "num_test.pgc"
+#line 101 "num_test.pgc"
 
 	{ ECPGdisconnect(__LINE__, "CURRENT");
-#line 91 "num_test.pgc"
+#line 102 "num_test.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 91 "num_test.pgc"
+#line 102 "num_test.pgc"
 
 
 	return 0;
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.stderr b/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.stderr
index a7d125402a..10cfd91cec 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.stderr
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.stderr
@@ -26,7 +26,7 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_get_data on line 61: RESULT: 2369.7000000 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 90: action "rollback"; connection "ecpg1_regression"
+[NO_PID]: ECPGtrans on line 101: action "rollback"; connection "ecpg1_regression"
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection ecpg1_regression closed
 [NO_PID]: sqlca: code: 0, state: 00000
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.stdout b/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.stdout
index 204c3cf6c0..a47a044c3a 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.stdout
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-num_test.stdout
@@ -4,3 +4,5 @@ sub = 2369.7
 mul = 13306998429.873000000
 div = 1330699.84298730000 1.3307e+06
 to long(0) = 20000000 14
+min int = -2147483648
+max int = 2147483647
diff --git a/src/interfaces/ecpg/test/pgtypeslib/num_test.pgc b/src/interfaces/ecpg/test/pgtypeslib/num_test.pgc
index 254aeb4129..c6c2858b81 100644
--- a/src/interfaces/ecpg/test/pgtypeslib/num_test.pgc
+++ b/src/interfaces/ecpg/test/pgtypeslib/num_test.pgc
@@ -19,7 +19,7 @@ main(void)
 	exec sql end declare section;
 	double d;
 	long l1, l2;
-	int i;
+	int i, min, max;
 
 	ECPGdebug(1, stderr);
 	exec sql whenever sqlerror do sqlprint();
@@ -87,6 +87,17 @@ main(void)
 	PGTYPESnumeric_free(value2);
 	PGTYPESnumeric_free(res);
 
+	/* check conversion of numeric to int */
+	value1 = PGTYPESnumeric_from_asc("-2147483648", NULL);
+	PGTYPESnumeric_to_int(value1, &min);
+	printf("min int = %d\n", min);
+	PGTYPESnumeric_free(value1);
+
+	value2 = PGTYPESnumeric_from_asc("2147483647", NULL);
+	PGTYPESnumeric_to_int(value2, &max);
+	printf("max int = %d\n", max);
+	PGTYPESnumeric_free(value2);
+
 	exec sql rollback;
 	exec sql disconnect;
 
-- 
2.31.1

