On Mon Aug 3, 2026 at 10:08 PM UTC, Michael Paquier wrote:
> On Mon, Aug 03, 2026 at 08:32:38PM +0000, Tristan Partin wrote:
>> Here are some re-spins of the previous uncommitted patches. They have 
>> been rebased on master. I am really curious how the formatting got 
>> messed up initially. Thanks for committing the the two patches.
>
> It looks to me that you have missed the main material of the
> discussion: the patches themselves.

Uhh... check this email...

-- 
Tristan Partin
PostgreSQL Contributors Team
AWS (https://aws.amazon.com)
From 122aa10d33680d2e36ed975233e7865e07fa6e3c Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Thu, 30 Jul 2026 07:40:31 +0000
Subject: [PATCH v2 1/3] Make tid parsing consistent across libcs

A tid consists of two values: a block number and an offset number. A tid
can be parsed from a string representation of the form "(block,offset)".
This parsing goes through tidin(), which uses strtoul() to parse integer
values.

strtoul() can set two different values for errno: EINVAL and ERANGE.
Quoting the man page for EINVAL:

> The value of base is not supported or no conversion could be performed
> (the last feature is not portable across all platforms).

Emphasis on the portion in parentheses.

Take the following example:

	strtoul("", &endptr, 10);

On glibc, the output value is 0, errno *is not* set, and endptr points
to the empty string.

On Apple libc, the output value is 0, errno *is* set to EINVAL, and
endptr points to the empty string.

With inputs of either "(,1)" or "(1,0)" on glibc, tidin() would produce
a tid of (0,1) or (1,0) respectively. On Apple libc, tidin() would throw
an error. This is inconsistent behavior across platforms, which I don't
think is in the interest of Postgres. Queries should be portable across
platforms to keep applications easier to write.

Fixes: https://www.postgresql.org/message-id/19584-e60c446ba6f57c9c%40postgresql.org

# ------------------------ >8 ------------------------ Do not modify or
# remove the line above. Everything below it will be ignored.
#
# On branch strtoul Changes to be committed: modified:
# src/backend/utils/adt/tid.c modified:
# src/test/regress/expected/tid.out modified:
# src/test/regress/sql/tid.sql
#
# Changes not staged for commit: modified:
# src/backend/access/transam/xlog.c modified:
# src/backend/access/transam/xlogrecovery.c modified:
# src/backend/replication/pgoutput/pgoutput.c modified:
# src/bin/pg_combinebackup/pg_combinebackup.c
#
diff --git c/src/backend/utils/adt/tid.c i/src/backend/utils/adt/tid.c
index 9257886f1da..a97873f91ba 100644 --- c/src/backend/utils/adt/tid.c
+++ i/src/backend/utils/adt/tid.c @@ -73,7 +73,7 @@
tidin(PG_FUNCTION_ARGS)

 	errno = 0;
 	cvt = strtoul(coord[0], &badp, 10);
-	if (errno || *badp != DELIM)
+	if (badp == coord[0] || errno || *badp != DELIM)
 		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type %s: \"%s\"",
@@ -95,8 +95,7 @@ tidin(PG_FUNCTION_ARGS)
 #endif

 	cvt = strtoul(coord[1], &badp, 10);
-	if (errno || *badp != RDELIM ||
-		cvt > USHRT_MAX)
+	if (badp == coord[1] || errno || *badp != RDELIM || cvt > USHRT_MAX)
 		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type %s: \"%s\"",
diff --git c/src/test/regress/expected/tid.out i/src/test/regress/expected/tid.out
index 3497a77688b..45cc61fb1ca 100644
--- c/src/test/regress/expected/tid.out
+++ i/src/test/regress/expected/tid.out
@@ -17,6 +17,14 @@ SELECT '(1,65536)'::tid;  -- error
 ERROR:  invalid input syntax for type tid: "(1,65536)"
 LINE 1: SELECT '(1,65536)'::tid;
                ^
+SELECT '(,1)'::tid;  -- error
+ERROR:  invalid input syntax for type tid: "(,1)"
+LINE 1: SELECT '(,1)'::tid;
+               ^
+SELECT '(1,)'::tid;  -- error
+ERROR:  invalid input syntax for type tid: "(1,)"
+LINE 1: SELECT '(1,)'::tid;
+               ^
 -- Also try it with non-error-throwing API
 SELECT pg_input_is_valid('(0)', 'tid');
  pg_input_is_valid
diff --git c/src/test/regress/sql/tid.sql i/src/test/regress/sql/tid.sql
index c0a70be5cbd..51d00b92074 100644
--- c/src/test/regress/sql/tid.sql
+++ i/src/test/regress/sql/tid.sql
@@ -8,6 +8,8 @@ SELECT

 SELECT '(4294967296,1)'::tid;  -- error
 SELECT '(1,65536)'::tid;  -- error
+SELECT '(,1)'::tid;  -- error
+SELECT '(1,)'::tid;  -- error

 -- Also try it with non-error-throwing API
 SELECT pg_input_is_valid('(0)', 'tid');

Signed-off-by: Tristan Partin <[email protected]>
---
 src/backend/utils/adt/tid.c       | 5 ++---
 src/test/regress/expected/tid.out | 8 ++++++++
 src/test/regress/sql/tid.sql      | 2 ++
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index 9257886f1da..a97873f91ba 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -73,7 +73,7 @@ tidin(PG_FUNCTION_ARGS)
 
 	errno = 0;
 	cvt = strtoul(coord[0], &badp, 10);
-	if (errno || *badp != DELIM)
+	if (badp == coord[0] || errno || *badp != DELIM)
 		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type %s: \"%s\"",
@@ -95,8 +95,7 @@ tidin(PG_FUNCTION_ARGS)
 #endif
 
 	cvt = strtoul(coord[1], &badp, 10);
-	if (errno || *badp != RDELIM ||
-		cvt > USHRT_MAX)
+	if (badp == coord[1] || errno || *badp != RDELIM || cvt > USHRT_MAX)
 		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type %s: \"%s\"",
diff --git a/src/test/regress/expected/tid.out b/src/test/regress/expected/tid.out
index 3497a77688b..45cc61fb1ca 100644
--- a/src/test/regress/expected/tid.out
+++ b/src/test/regress/expected/tid.out
@@ -17,6 +17,14 @@ SELECT '(1,65536)'::tid;  -- error
 ERROR:  invalid input syntax for type tid: "(1,65536)"
 LINE 1: SELECT '(1,65536)'::tid;
                ^
+SELECT '(,1)'::tid;  -- error
+ERROR:  invalid input syntax for type tid: "(,1)"
+LINE 1: SELECT '(,1)'::tid;
+               ^
+SELECT '(1,)'::tid;  -- error
+ERROR:  invalid input syntax for type tid: "(1,)"
+LINE 1: SELECT '(1,)'::tid;
+               ^
 -- Also try it with non-error-throwing API
 SELECT pg_input_is_valid('(0)', 'tid');
  pg_input_is_valid 
diff --git a/src/test/regress/sql/tid.sql b/src/test/regress/sql/tid.sql
index c0a70be5cbd..51d00b92074 100644
--- a/src/test/regress/sql/tid.sql
+++ b/src/test/regress/sql/tid.sql
@@ -8,6 +8,8 @@ SELECT
 
 SELECT '(4294967296,1)'::tid;  -- error
 SELECT '(1,65536)'::tid;  -- error
+SELECT '(,1)'::tid;  -- error
+SELECT '(1,)'::tid;  -- error
 
 -- Also try it with non-error-throwing API
 SELECT pg_input_is_valid('(0)', 'tid');
-- 
Tristan Partin
https://tristan.partin.io

From 9a3fb5d4473469bb227d0bb45a61472b4e3330e7 Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Thu, 30 Jul 2026 08:26:26 +0000
Subject: [PATCH v2 2/3] Fix empty proto_version integer parsing in pgoutput

strtoul() can set two different values for errno: EINVAL and ERANGE.
Quoting the man page for EINVAL:

> The value of base is not supported or no conversion could be performed
> (the last feature is not portable across all platforms).

Emphasis on the portion in parentheses.

Take the following example:

    strtoul("", &endptr, 10);

On glibc, the output value is 0, errno *is not* set, and endptr points
to the empty string.

On Apple libc, the output value is 0, errno *is* set to EINVAL, and
endptr points to the empty string.

In this case, when we try to parse an empty string, glibc treats that as
a protocol version of 0, while Apple libc treats that as an invalid
protocol version.

Signed-off-by: Tristan Partin <[email protected]>
---
 src/backend/replication/pgoutput/pgoutput.c |  5 ++--
 src/test/subscription/t/100_bugs.pl         | 31 +++++++++++++++++++++
 2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index 4ecfcbff7ab..0afdb1432ca 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -316,6 +316,7 @@ parse_output_parameters(List *options, PGOutputData *data)
 		{
 			unsigned long parsed;
 			char	   *endptr;
+			const char *val = strVal(defel->arg);
 
 			if (protocol_version_given)
 				ereport(ERROR,
@@ -324,8 +325,8 @@ parse_output_parameters(List *options, PGOutputData *data)
 			protocol_version_given = true;
 
 			errno = 0;
-			parsed = strtoul(strVal(defel->arg), &endptr, 10);
-			if (errno != 0 || *endptr != '\0')
+			parsed = strtoul(val, &endptr, 10);
+			if (endptr == val || errno != 0 || *endptr != '\0')
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("invalid proto_version")));
diff --git a/src/test/subscription/t/100_bugs.pl b/src/test/subscription/t/100_bugs.pl
index 075c52f98fd..d9045e4d871 100644
--- a/src/test/subscription/t/100_bugs.pl
+++ b/src/test/subscription/t/100_bugs.pl
@@ -650,6 +650,37 @@ BEGIN
 	"DROP PUBLICATION pub_rowfilter_error");
 $node_publisher->safe_psql('postgres', "DROP TABLE tab_upsert");
 
+# Verify that an empty proto_version option is rejected as invalid
+$node_publisher->safe_psql(
+	'postgres', qq(
+	CREATE TABLE tab_proto_version (a INT PRIMARY KEY);
+	CREATE PUBLICATION pub_proto_version FOR TABLE tab_proto_version;
+	SELECT * FROM pg_create_logical_replication_slot('proto_version_slot', 'pgoutput');
+));
+
+($ret, $stdout, $stderr) = $node_publisher->psql(
+	'postgres', qq(
+	SELECT *
+	FROM pg_logical_slot_peek_binary_changes(
+		'proto_version_slot',
+		NULL,
+		NULL,
+		'proto_version', '',
+		'publication_names', 'pub_proto_version'
+	);
+));
+
+ok($stderr =~ qr/invalid proto_version/,
+	'empty proto_version is rejected as invalid'
+);
+
+# Clean up
+$node_publisher->safe_psql('postgres',
+	"SELECT pg_drop_replication_slot('proto_version_slot')");
+$node_publisher->safe_psql('postgres',
+	"DROP PUBLICATION pub_proto_version");
+$node_publisher->safe_psql('postgres', "DROP TABLE tab_proto_version");
+
 $node_publisher->stop('fast');
 
 done_testing();
-- 
Tristan Partin
https://tristan.partin.io

From 198139075386338b28007c75225f26b86f48f327 Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Thu, 30 Jul 2026 08:33:28 +0000
Subject: [PATCH v2 3/3] Fix strto* usage in ecpg

strto*() can set two different values for errno: EINVAL and ERANGE.
Quoting the man page for EINVAL:

> The value of base is not supported or no conversion could be performed
> (the last feature is not portable across all platforms).

Emphasis on the portion in parentheses.

Take the following example:

    strtoul("", &endptr, 10);

On glibc, the output value is 0, errno *is not* set, and endptr points
to the empty string.

On Apple libc, the output value is 0, errno *is* set to EINVAL, and
endptr points to the empty string.

Because of this discrepancy, ECPG was silently converting empty strings
to 0 on glibc, while Apple libc was correctly rejecting them as invalid
integers.

# ------------------------ >8 ------------------------ Do not modify or
# remove the line above. Everything below it will be ignored.
#
# On branch strtoul Changes to be committed: modified:
# src/interfaces/ecpg/ecpglib/data.c modified:
# src/interfaces/ecpg/pgtypeslib/dt_common.c modified:
# src/interfaces/ecpg/test/expected/sql-indicators.c modified:
# src/interfaces/ecpg/test/expected/sql-indicators.stderr modified:
# src/interfaces/ecpg/test/expected/sql-indicators.stdout modified:
# src/interfaces/ecpg/test/sql/indicators.pgc
#
# Changes not staged for commit: modified:
# src/bin/pg_verifybackup/pg_verifybackup.c
#
diff --git c/src/interfaces/ecpg/ecpglib/data.c
i/src/interfaces/ecpg/ecpglib/data.c index d5d40f7b654..932cb568a2d
100644 --- c/src/interfaces/ecpg/ecpglib/data.c +++
i/src/interfaces/ecpg/ecpglib/data.c @@ -373,7 +373,7 @@
ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int
lineno, case ECPGt_int: case ECPGt_long: res = strtol(pval,
&scan_length, 10);
-					if (garbage_left(isarray, &scan_length, compat))
+					if (scan_length == pval || garbage_left(isarray,
&scan_length, compat)) { ecpg_raise(lineno, ECPG_INT_FORMAT,
ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); @@ -402,7 +402,7 @@
ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int
lineno, case ECPGt_unsigned_int: case ECPGt_unsigned_long: ures
= strtoul(pval, &scan_length, 10);
-					if (garbage_left(isarray, &scan_length, compat))
+					if (scan_length == pval || garbage_left(isarray,
&scan_length, compat)) { ecpg_raise(lineno, ECPG_UINT_FORMAT,
ECPG_SQLSTATE_DATATYPE_MISMATCH, pval); @@ -429,7 +429,7 @@
ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int
lineno,

 				case ECPGt_long_long:
 					*((long long int *) (var + offset * act_tuple)) = strtoll(pval, &scan_length, 10);
-					if (garbage_left(isarray, &scan_length, compat))
+					if (scan_length == pval || garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return false;
@@ -440,7 +440,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,

 				case ECPGt_unsigned_long_long:
 					*((unsigned long long int *) (var + offset * act_tuple)) = strtoull(pval, &scan_length, 10);
-					if (garbage_left(isarray, &scan_length, compat))
+					if (scan_length == pval || garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return false;
@@ -457,6 +457,13 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if (!check_special_value(pval, &dres, &scan_length))
 						dres = strtod(pval, &scan_length);

+					if (scan_length == pval)
+					{
+						ecpg_raise(lineno, ECPG_FLOAT_FORMAT,
+								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+						return false;
+					}
+
 					if (isarray && *scan_length == '"')
 						scan_length++;

diff --git c/src/interfaces/ecpg/pgtypeslib/dt_common.c i/src/interfaces/ecpg/pgtypeslib/dt_common.c
index 3ed5ad06c06..958e220b806 100644
--- c/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ i/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -2505,7 +2505,7 @@ pgtypes_defmt_scan(union un_fmt_comb *scan_val, int scan_type, char **pstr, char
 				(*pstr)++;
 			errno = 0;
 			scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
-			if (errno)
+			if (errno || strtol_end == *pstr)
 				err = 1;
 			break;
 		case PGTYPES_TYPE_UINT_LONG:
@@ -2513,7 +2513,7 @@ pgtypes_defmt_scan(union un_fmt_comb *scan_val, int scan_type, char **pstr, char
 				(*pstr)++;
 			errno = 0;
 			scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
-			if (errno)
+			if (errno || strtol_end == *pstr)
 				err = 1;
 			break;
 		case PGTYPES_TYPE_STRING_MALLOCED:
diff --git c/src/interfaces/ecpg/test/expected/sql-indicators.c i/src/interfaces/ecpg/test/expected/sql-indicators.c
index 796bd906af6..017f6c3ad39 100644
--- c/src/interfaces/ecpg/test/expected/sql-indicators.c
+++ i/src/interfaces/ecpg/test/expected/sql-indicators.c
@@ -8,6 +8,7 @@

 #line 1 "indicators.pgc"
 #include <stdio.h>
+#include <ecpgerrno.h>

 #line 1 "sqlca.h"
@@ -78,7 +79,7 @@ struct sqlca_t *ECPGget_sqlca(void);

 #endif

-#line 3 "indicators.pgc"
+#line 4 "indicators.pgc"

 #line 1 "regression.h"
@@ -88,7 +89,7 @@ struct sqlca_t *ECPGget_sqlca(void);

-#line 4 "indicators.pgc"
+#line 5 "indicators.pgc"

 int main(void)
@@ -96,68 +97,72 @@ int main(void)
 	/* exec sql begin declare section */

+

-#line 9 "indicators.pgc"
+#line 10 "indicators.pgc"
  int intvar = 5 ;

-#line 10 "indicators.pgc"
- int nullind = - 1 ;
-/* exec sql end declare section */
 #line 11 "indicators.pgc"
+ int nullind = - 1 ;
+
+#line 12 "indicators.pgc"
+ float floatvar ;
+/* exec sql end declare section */
+#line 13 "indicators.pgc"

 	ECPGdebug(1,stderr);

 	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 15 "indicators.pgc"
+#line 17 "indicators.pgc"

 	{ ECPGsetcommit(__LINE__, "off", NULL);}
-#line 16 "indicators.pgc"
+#line 18 "indicators.pgc"

 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table indicator_test ( \"id\" int primary key , \"str\" text not null , val int null )", ECPGt_EOIT, ECPGt_EORT);}
-#line 21 "indicators.pgc"
+#line 23 "indicators.pgc"

 	{ ECPGtrans(__LINE__, NULL, "commit work");}
-#line 22 "indicators.pgc"
+#line 24 "indicators.pgc"

 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into indicator_test ( id , str , val ) values ( 1 , 'Hello' , 0 )", ECPGt_EOIT, ECPGt_EORT);}
-#line 24 "indicators.pgc"
+#line 26 "indicators.pgc"

 	/* use indicator in insert */
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into indicator_test ( id , str , val ) values ( 2 , 'Hi there' , $1  )",
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int),
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EOIT, ECPGt_EORT);}
-#line 27 "indicators.pgc"
+#line 29 "indicators.pgc"

 	nullind = 0;
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into indicator_test ( id , str , val ) values ( 3 , 'Good evening' , $1  )",
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int),
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EOIT, ECPGt_EORT);}
-#line 29 "indicators.pgc"
+#line 31 "indicators.pgc"

 	{ ECPGtrans(__LINE__, NULL, "commit work");}
-#line 30 "indicators.pgc"
+#line 32 "indicators.pgc"

 	/* use indicators to get information about selects */
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select val from indicator_test where id = 1", ECPGt_EOIT,
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int),
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 33 "indicators.pgc"
+#line 35 "indicators.pgc"

 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select val from indicator_test where id = 2", ECPGt_EOIT,
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int),
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EORT);}
-#line 34 "indicators.pgc"
+#line 36 "indicators.pgc"

 	printf("intvar: %d, nullind: %d\n", intvar, nullind);
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select val from indicator_test where id = 3", ECPGt_EOIT,
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int),
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EORT);}
-#line 36 "indicators.pgc"
+#line 38 "indicators.pgc"

 	printf("intvar: %d, nullind: %d\n", intvar, nullind);

@@ -166,24 +171,57 @@ int main(void)
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update indicator_test set val = $1  where id = 1",
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int),
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EOIT, ECPGt_EORT);}
-#line 41 "indicators.pgc"
+#line 43 "indicators.pgc"

 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select val from indicator_test where id = 1", ECPGt_EOIT,
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int),
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EORT);}
-#line 42 "indicators.pgc"
+#line 44 "indicators.pgc"

 	printf("intvar: %d, nullind: %d\n", intvar, nullind);

-	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table indicator_test", ECPGt_EOIT, ECPGt_EORT);}
-#line 45 "indicators.pgc"
+	/*
+	 * An empty string used to be silently converted to 0 instead of being
+	 * rejected as an invalid integer/float.
+	 */
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into indicator_test ( id , str , val ) values ( 4 , '' , 0 )", ECPGt_EOIT, ECPGt_EORT);}
+#line 51 "indicators.pgc"

 	{ ECPGtrans(__LINE__, NULL, "commit work");}
-#line 46 "indicators.pgc"
+#line 52 "indicators.pgc"
+
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select str from indicator_test where id = 4", ECPGt_EOIT,
+	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int),
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
+#line 54 "indicators.pgc"
+
+	if (sqlca.sqlcode == ECPG_INT_FORMAT)
+		printf("empty string correctly rejected as integer\n");
+	else
+		printf("unexpected sqlcode %ld for empty string as integer: %s\n",
+			   sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select str from indicator_test where id = 4", ECPGt_EOIT,
+	ECPGt_float,&(floatvar),(long)1,(long)1,sizeof(float),
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
+#line 61 "indicators.pgc"
+
+	if (sqlca.sqlcode == ECPG_FLOAT_FORMAT)
+		printf("empty string correctly rejected as float\n");
+	else
+		printf("unexpected sqlcode %ld for empty string as float: %s\n",
+			   sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table indicator_test", ECPGt_EOIT, ECPGt_EORT);}
+#line 68 "indicators.pgc"
+
+	{ ECPGtrans(__LINE__, NULL, "commit work");}
+#line 69 "indicators.pgc"

 	{ ECPGdisconnect(__LINE__, "CURRENT");}
-#line 48 "indicators.pgc"
+#line 71 "indicators.pgc"

 	return 0;
 }
diff --git c/src/interfaces/ecpg/test/expected/sql-indicators.stderr i/src/interfaces/ecpg/test/expected/sql-indicators.stderr
index 5813ce29603..dec3eec2422 100644
--- c/src/interfaces/ecpg/test/expected/sql-indicators.stderr
+++ i/src/interfaces/ecpg/test/expected/sql-indicators.stderr
@@ -2,87 +2,115 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGsetcommit on line 16: action "off"; connection "ecpg1_regression"
+[NO_PID]: ECPGsetcommit on line 18: action "off"; connection "ecpg1_regression"
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 18: query: create table indicator_test ( "id" int primary key , "str" text not null , val int null ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 20: query: create table indicator_test ( "id" int primary key , "str" text not null , val int null ); with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 18: using PQexec
+[NO_PID]: ecpg_execute on line 20: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 18: OK: CREATE TABLE
+[NO_PID]: ecpg_process_output on line 20: OK: CREATE TABLE
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 22: action "commit work"; connection "ecpg1_regression"
+[NO_PID]: ECPGtrans on line 24: action "commit work"; connection "ecpg1_regression"
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: query: insert into indicator_test ( id , str , val ) values ( 1 , 'Hello' , 0 ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 26: query: insert into indicator_test ( id , str , val ) values ( 1 , 'Hello' , 0 ); with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: using PQexec
+[NO_PID]: ecpg_execute on line 26: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 24: OK: INSERT 0 1
+[NO_PID]: ecpg_process_output on line 26: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 27: query: insert into indicator_test ( id , str , val ) values ( 2 , 'Hi there' , $1  ); with 1 parameter(s) on connection ecpg1_regression
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 27: using PQexecParams
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_free_params on line 27: parameter 1 = null
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 27: OK: INSERT 0 1
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 29: query: insert into indicator_test ( id , str , val ) values ( 3 , 'Good evening' , $1  ); with 1 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 29: query: insert into indicator_test ( id , str , val ) values ( 2 , 'Hi there' , $1  ); with 1 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 29: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_free_params on line 29: parameter 1 = 5
+[NO_PID]: ecpg_free_params on line 29: parameter 1 = null
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_process_output on line 29: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 30: action "commit work"; connection "ecpg1_regression"
+[NO_PID]: ecpg_execute on line 31: query: insert into indicator_test ( id , str , val ) values ( 3 , 'Good evening' , $1  ); with 1 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 33: query: select val from indicator_test where id = 1; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 31: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 33: using PQexec
+[NO_PID]: ecpg_free_params on line 31: parameter 1 = 5
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 33: correctly got 1 tuples with 1 fields
+[NO_PID]: ecpg_process_output on line 31: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 33: RESULT: 0 offset: -1; array: no
+[NO_PID]: ECPGtrans on line 32: action "commit work"; connection "ecpg1_regression"
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 34: query: select val from indicator_test where id = 2; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 35: query: select val from indicator_test where id = 1; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 34: using PQexec
+[NO_PID]: ecpg_execute on line 35: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 34: correctly got 1 tuples with 1 fields
+[NO_PID]: ecpg_process_output on line 35: correctly got 1 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 34: RESULT:  offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 35: RESULT: 0 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 36: query: select val from indicator_test where id = 3; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 36: query: select val from indicator_test where id = 2; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 36: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_process_output on line 36: correctly got 1 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 36: RESULT: 5 offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 36: RESULT:  offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 41: query: update indicator_test set val = $1  where id = 1; with 1 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 38: query: select val from indicator_test where id = 3; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 41: using PQexecParams
+[NO_PID]: ecpg_execute on line 38: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_free_params on line 41: parameter 1 = null
+[NO_PID]: ecpg_process_output on line 38: correctly got 1 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 41: OK: UPDATE 1
+[NO_PID]: ecpg_get_data on line 38: RESULT: 5 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 42: query: select val from indicator_test where id = 1; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 43: query: update indicator_test set val = $1  where id = 1; with 1 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 42: using PQexec
+[NO_PID]: ecpg_execute on line 43: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 42: correctly got 1 tuples with 1 fields
+[NO_PID]: ecpg_free_params on line 43: parameter 1 = null
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT:  offset: -1; array: no
+[NO_PID]: ecpg_process_output on line 43: OK: UPDATE 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 45: query: drop table indicator_test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 44: query: select val from indicator_test where id = 1; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 45: using PQexec
+[NO_PID]: ecpg_execute on line 44: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 45: OK: DROP TABLE
+[NO_PID]: ecpg_process_output on line 44: correctly got 1 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 46: action "commit work"; connection "ecpg1_regression"
+[NO_PID]: ecpg_get_data on line 44: RESULT:  offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 51: query: insert into indicator_test ( id , str , val ) values ( 4 , '' , 0 ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 51: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 51: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 52: action "commit work"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 54: query: select str from indicator_test where id = 4; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 54: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 54: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 54: RESULT:  offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -204 on line 54: invalid input syntax for type int: "", on line 54
+[NO_PID]: sqlca: code: -204, state: 42804
+[NO_PID]: ecpg_execute on line 61: query: select str from indicator_test where id = 4; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 61: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 61: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 61: RESULT:  offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -206 on line 61: invalid input syntax for floating-point type: "", on line 61
+[NO_PID]: sqlca: code: -206, state: 42804
+[NO_PID]: ecpg_execute on line 68: query: drop table indicator_test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 68: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 68: OK: DROP TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 69: action "commit work"; 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 c/src/interfaces/ecpg/test/expected/sql-indicators.stdout i/src/interfaces/ecpg/test/expected/sql-indicators.stdout
index e9d6fd17c1c..270addb76c3 100644
--- c/src/interfaces/ecpg/test/expected/sql-indicators.stdout
+++ i/src/interfaces/ecpg/test/expected/sql-indicators.stdout
@@ -1,3 +1,5 @@
 intvar: 0, nullind: -1
 intvar: 5, nullind: 0
 intvar: 5, nullind: -1
+empty string correctly rejected as integer
+empty string correctly rejected as float
diff --git c/src/interfaces/ecpg/test/sql/indicators.pgc i/src/interfaces/ecpg/test/sql/indicators.pgc
index d925faf35ce..16e67ce498c 100644
--- c/src/interfaces/ecpg/test/sql/indicators.pgc
+++ i/src/interfaces/ecpg/test/sql/indicators.pgc
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <ecpgerrno.h>

 exec sql include sqlca;
 exec sql include ../regression;
@@ -8,6 +9,7 @@ int main(void)
 	exec sql begin declare section;
 		int intvar = 5;
 		int nullind = -1;
+		float floatvar;
 	exec sql end declare section;

 	ECPGdebug(1,stderr);
@@ -42,6 +44,27 @@ int main(void)
 	exec sql select val into :intvar :nullind from indicator_test where id = 1;
 	printf("intvar: %d, nullind: %d\n", intvar, nullind);

+	/*
+	 * An empty string used to be silently converted to 0 instead of being
+	 * rejected as an invalid integer/float.
+	 */
+	exec sql insert into indicator_test (id, str, val) values ( 4, '', 0);
+	exec sql commit work;
+
+	exec sql select str into :intvar from indicator_test where id = 4;
+	if (sqlca.sqlcode == ECPG_INT_FORMAT)
+		printf("empty string correctly rejected as integer\n");
+	else
+		printf("unexpected sqlcode %ld for empty string as integer: %s\n",
+			   sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
+
+	exec sql select str into :floatvar from indicator_test where id = 4;
+	if (sqlca.sqlcode == ECPG_FLOAT_FORMAT)
+		printf("empty string correctly rejected as float\n");
+	else
+		printf("unexpected sqlcode %ld for empty string as float: %s\n",
+			   sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
+
 	exec sql drop table indicator_test;
 	exec sql commit work;

Signed-off-by: Tristan Partin <[email protected]>
---
 src/interfaces/ecpg/ecpglib/data.c            |  15 ++-
 src/interfaces/ecpg/pgtypeslib/dt_common.c    |   4 +-
 .../ecpg/test/expected/sql-indicators.c       |  84 +++++++++----
 .../ecpg/test/expected/sql-indicators.stderr  | 110 +++++++++++-------
 .../ecpg/test/expected/sql-indicators.stdout  |   2 +
 src/interfaces/ecpg/test/sql/indicators.pgc   |  23 ++++
 6 files changed, 168 insertions(+), 70 deletions(-)

diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index d5d40f7b654..932cb568a2d 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -373,7 +373,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 				case ECPGt_int:
 				case ECPGt_long:
 					res = strtol(pval, &scan_length, 10);
-					if (garbage_left(isarray, &scan_length, compat))
+					if (scan_length == pval || garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_INT_FORMAT,
 								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
@@ -402,7 +402,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 				case ECPGt_unsigned_int:
 				case ECPGt_unsigned_long:
 					ures = strtoul(pval, &scan_length, 10);
-					if (garbage_left(isarray, &scan_length, compat))
+					if (scan_length == pval || garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_UINT_FORMAT,
 								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
@@ -429,7 +429,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 
 				case ECPGt_long_long:
 					*((long long int *) (var + offset * act_tuple)) = strtoll(pval, &scan_length, 10);
-					if (garbage_left(isarray, &scan_length, compat))
+					if (scan_length == pval || garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return false;
@@ -440,7 +440,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 
 				case ECPGt_unsigned_long_long:
 					*((unsigned long long int *) (var + offset * act_tuple)) = strtoull(pval, &scan_length, 10);
-					if (garbage_left(isarray, &scan_length, compat))
+					if (scan_length == pval || garbage_left(isarray, &scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return false;
@@ -457,6 +457,13 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if (!check_special_value(pval, &dres, &scan_length))
 						dres = strtod(pval, &scan_length);
 
+					if (scan_length == pval)
+					{
+						ecpg_raise(lineno, ECPG_FLOAT_FORMAT,
+								   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+						return false;
+					}
+
 					if (isarray && *scan_length == '"')
 						scan_length++;
 
diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
index 3ed5ad06c06..958e220b806 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -2505,7 +2505,7 @@ pgtypes_defmt_scan(union un_fmt_comb *scan_val, int scan_type, char **pstr, char
 				(*pstr)++;
 			errno = 0;
 			scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
-			if (errno)
+			if (errno || strtol_end == *pstr)
 				err = 1;
 			break;
 		case PGTYPES_TYPE_UINT_LONG:
@@ -2513,7 +2513,7 @@ pgtypes_defmt_scan(union un_fmt_comb *scan_val, int scan_type, char **pstr, char
 				(*pstr)++;
 			errno = 0;
 			scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
-			if (errno)
+			if (errno || strtol_end == *pstr)
 				err = 1;
 			break;
 		case PGTYPES_TYPE_STRING_MALLOCED:
diff --git a/src/interfaces/ecpg/test/expected/sql-indicators.c b/src/interfaces/ecpg/test/expected/sql-indicators.c
index 796bd906af6..017f6c3ad39 100644
--- a/src/interfaces/ecpg/test/expected/sql-indicators.c
+++ b/src/interfaces/ecpg/test/expected/sql-indicators.c
@@ -8,6 +8,7 @@
 
 #line 1 "indicators.pgc"
 #include <stdio.h>
+#include <ecpgerrno.h>
 
 
 #line 1 "sqlca.h"
@@ -78,7 +79,7 @@ struct sqlca_t *ECPGget_sqlca(void);
 
 #endif
 
-#line 3 "indicators.pgc"
+#line 4 "indicators.pgc"
 
 
 #line 1 "regression.h"
@@ -88,7 +89,7 @@ struct sqlca_t *ECPGget_sqlca(void);
 
 
 
-#line 4 "indicators.pgc"
+#line 5 "indicators.pgc"
 
 
 int main(void)
@@ -96,68 +97,72 @@ int main(void)
 	/* exec sql begin declare section */
 		   
 		   
+		 
 	
-#line 9 "indicators.pgc"
+#line 10 "indicators.pgc"
  int intvar = 5 ;
  
-#line 10 "indicators.pgc"
- int nullind = - 1 ;
-/* exec sql end declare section */
 #line 11 "indicators.pgc"
+ int nullind = - 1 ;
+ 
+#line 12 "indicators.pgc"
+ float floatvar ;
+/* exec sql end declare section */
+#line 13 "indicators.pgc"
 
 
 	ECPGdebug(1,stderr);
 
 	{ ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); }
-#line 15 "indicators.pgc"
+#line 17 "indicators.pgc"
 
 	{ ECPGsetcommit(__LINE__, "off", NULL);}
-#line 16 "indicators.pgc"
+#line 18 "indicators.pgc"
 
 
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table indicator_test ( \"id\" int primary key , \"str\" text not null , val int null )", ECPGt_EOIT, ECPGt_EORT);}
-#line 21 "indicators.pgc"
+#line 23 "indicators.pgc"
 
 	{ ECPGtrans(__LINE__, NULL, "commit work");}
-#line 22 "indicators.pgc"
+#line 24 "indicators.pgc"
 
 
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into indicator_test ( id , str , val ) values ( 1 , 'Hello' , 0 )", ECPGt_EOIT, ECPGt_EORT);}
-#line 24 "indicators.pgc"
+#line 26 "indicators.pgc"
 
 
 	/* use indicator in insert */
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into indicator_test ( id , str , val ) values ( 2 , 'Hi there' , $1  )", 
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int), 
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EOIT, ECPGt_EORT);}
-#line 27 "indicators.pgc"
+#line 29 "indicators.pgc"
 
 	nullind = 0;
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into indicator_test ( id , str , val ) values ( 3 , 'Good evening' , $1  )", 
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int), 
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EOIT, ECPGt_EORT);}
-#line 29 "indicators.pgc"
+#line 31 "indicators.pgc"
 
 	{ ECPGtrans(__LINE__, NULL, "commit work");}
-#line 30 "indicators.pgc"
+#line 32 "indicators.pgc"
 
 
 	/* use indicators to get information about selects */
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select val from indicator_test where id = 1", ECPGt_EOIT, 
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
-#line 33 "indicators.pgc"
+#line 35 "indicators.pgc"
 
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select val from indicator_test where id = 2", ECPGt_EOIT, 
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int), 
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EORT);}
-#line 34 "indicators.pgc"
+#line 36 "indicators.pgc"
 
 	printf("intvar: %d, nullind: %d\n", intvar, nullind);
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select val from indicator_test where id = 3", ECPGt_EOIT, 
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int), 
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EORT);}
-#line 36 "indicators.pgc"
+#line 38 "indicators.pgc"
 
 	printf("intvar: %d, nullind: %d\n", intvar, nullind);
 
@@ -166,24 +171,57 @@ int main(void)
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update indicator_test set val = $1  where id = 1", 
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int), 
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EOIT, ECPGt_EORT);}
-#line 41 "indicators.pgc"
+#line 43 "indicators.pgc"
 
 	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select val from indicator_test where id = 1", ECPGt_EOIT, 
 	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int), 
 	ECPGt_int,&(nullind),(long)1,(long)1,sizeof(int), ECPGt_EORT);}
-#line 42 "indicators.pgc"
+#line 44 "indicators.pgc"
 
 	printf("intvar: %d, nullind: %d\n", intvar, nullind);
 
-	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table indicator_test", ECPGt_EOIT, ECPGt_EORT);}
-#line 45 "indicators.pgc"
+	/*
+	 * An empty string used to be silently converted to 0 instead of being
+	 * rejected as an invalid integer/float.
+	 */
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into indicator_test ( id , str , val ) values ( 4 , '' , 0 )", ECPGt_EOIT, ECPGt_EORT);}
+#line 51 "indicators.pgc"
 
 	{ ECPGtrans(__LINE__, NULL, "commit work");}
-#line 46 "indicators.pgc"
+#line 52 "indicators.pgc"
+
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select str from indicator_test where id = 4", ECPGt_EOIT, 
+	ECPGt_int,&(intvar),(long)1,(long)1,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
+#line 54 "indicators.pgc"
+
+	if (sqlca.sqlcode == ECPG_INT_FORMAT)
+		printf("empty string correctly rejected as integer\n");
+	else
+		printf("unexpected sqlcode %ld for empty string as integer: %s\n",
+			   sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select str from indicator_test where id = 4", ECPGt_EOIT, 
+	ECPGt_float,&(floatvar),(long)1,(long)1,sizeof(float), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);}
+#line 61 "indicators.pgc"
+
+	if (sqlca.sqlcode == ECPG_FLOAT_FORMAT)
+		printf("empty string correctly rejected as float\n");
+	else
+		printf("unexpected sqlcode %ld for empty string as float: %s\n",
+			   sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table indicator_test", ECPGt_EOIT, ECPGt_EORT);}
+#line 68 "indicators.pgc"
+
+	{ ECPGtrans(__LINE__, NULL, "commit work");}
+#line 69 "indicators.pgc"
 
 
 	{ ECPGdisconnect(__LINE__, "CURRENT");}
-#line 48 "indicators.pgc"
+#line 71 "indicators.pgc"
 
 	return 0;
 }
diff --git a/src/interfaces/ecpg/test/expected/sql-indicators.stderr b/src/interfaces/ecpg/test/expected/sql-indicators.stderr
index 5813ce29603..dec3eec2422 100644
--- a/src/interfaces/ecpg/test/expected/sql-indicators.stderr
+++ b/src/interfaces/ecpg/test/expected/sql-indicators.stderr
@@ -2,87 +2,115 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>  
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGsetcommit on line 16: action "off"; connection "ecpg1_regression"
+[NO_PID]: ECPGsetcommit on line 18: action "off"; connection "ecpg1_regression"
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 18: query: create table indicator_test ( "id" int primary key , "str" text not null , val int null ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 20: query: create table indicator_test ( "id" int primary key , "str" text not null , val int null ); with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 18: using PQexec
+[NO_PID]: ecpg_execute on line 20: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 18: OK: CREATE TABLE
+[NO_PID]: ecpg_process_output on line 20: OK: CREATE TABLE
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 22: action "commit work"; connection "ecpg1_regression"
+[NO_PID]: ECPGtrans on line 24: action "commit work"; connection "ecpg1_regression"
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: query: insert into indicator_test ( id , str , val ) values ( 1 , 'Hello' , 0 ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 26: query: insert into indicator_test ( id , str , val ) values ( 1 , 'Hello' , 0 ); with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 24: using PQexec
+[NO_PID]: ecpg_execute on line 26: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 24: OK: INSERT 0 1
+[NO_PID]: ecpg_process_output on line 26: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 27: query: insert into indicator_test ( id , str , val ) values ( 2 , 'Hi there' , $1  ); with 1 parameter(s) on connection ecpg1_regression
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 27: using PQexecParams
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_free_params on line 27: parameter 1 = null
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 27: OK: INSERT 0 1
-[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 29: query: insert into indicator_test ( id , str , val ) values ( 3 , 'Good evening' , $1  ); with 1 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 29: query: insert into indicator_test ( id , str , val ) values ( 2 , 'Hi there' , $1  ); with 1 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 29: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_free_params on line 29: parameter 1 = 5
+[NO_PID]: ecpg_free_params on line 29: parameter 1 = null
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_process_output on line 29: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 30: action "commit work"; connection "ecpg1_regression"
+[NO_PID]: ecpg_execute on line 31: query: insert into indicator_test ( id , str , val ) values ( 3 , 'Good evening' , $1  ); with 1 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 33: query: select val from indicator_test where id = 1; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 31: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 33: using PQexec
+[NO_PID]: ecpg_free_params on line 31: parameter 1 = 5
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 33: correctly got 1 tuples with 1 fields
+[NO_PID]: ecpg_process_output on line 31: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 33: RESULT: 0 offset: -1; array: no
+[NO_PID]: ECPGtrans on line 32: action "commit work"; connection "ecpg1_regression"
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 34: query: select val from indicator_test where id = 2; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 35: query: select val from indicator_test where id = 1; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 34: using PQexec
+[NO_PID]: ecpg_execute on line 35: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 34: correctly got 1 tuples with 1 fields
+[NO_PID]: ecpg_process_output on line 35: correctly got 1 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 34: RESULT:  offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 35: RESULT: 0 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 36: query: select val from indicator_test where id = 3; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 36: query: select val from indicator_test where id = 2; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 36: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_process_output on line 36: correctly got 1 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 36: RESULT: 5 offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 36: RESULT:  offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 41: query: update indicator_test set val = $1  where id = 1; with 1 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 38: query: select val from indicator_test where id = 3; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 41: using PQexecParams
+[NO_PID]: ecpg_execute on line 38: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_free_params on line 41: parameter 1 = null
+[NO_PID]: ecpg_process_output on line 38: correctly got 1 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 41: OK: UPDATE 1
+[NO_PID]: ecpg_get_data on line 38: RESULT: 5 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 42: query: select val from indicator_test where id = 1; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 43: query: update indicator_test set val = $1  where id = 1; with 1 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 42: using PQexec
+[NO_PID]: ecpg_execute on line 43: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 42: correctly got 1 tuples with 1 fields
+[NO_PID]: ecpg_free_params on line 43: parameter 1 = null
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 42: RESULT:  offset: -1; array: no
+[NO_PID]: ecpg_process_output on line 43: OK: UPDATE 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 45: query: drop table indicator_test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: ecpg_execute on line 44: query: select val from indicator_test where id = 1; with 0 parameter(s) on connection ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 45: using PQexec
+[NO_PID]: ecpg_execute on line 44: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 45: OK: DROP TABLE
+[NO_PID]: ecpg_process_output on line 44: correctly got 1 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 46: action "commit work"; connection "ecpg1_regression"
+[NO_PID]: ecpg_get_data on line 44: RESULT:  offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 51: query: insert into indicator_test ( id , str , val ) values ( 4 , '' , 0 ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 51: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 51: OK: INSERT 0 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 52: action "commit work"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 54: query: select str from indicator_test where id = 4; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 54: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 54: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 54: RESULT:  offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -204 on line 54: invalid input syntax for type int: "", on line 54
+[NO_PID]: sqlca: code: -204, state: 42804
+[NO_PID]: ecpg_execute on line 61: query: select str from indicator_test where id = 4; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 61: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 61: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 61: RESULT:  offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlcode -206 on line 61: invalid input syntax for floating-point type: "", on line 61
+[NO_PID]: sqlca: code: -206, state: 42804
+[NO_PID]: ecpg_execute on line 68: query: drop table indicator_test; with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 68: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 68: OK: DROP TABLE
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 69: action "commit work"; 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/sql-indicators.stdout b/src/interfaces/ecpg/test/expected/sql-indicators.stdout
index e9d6fd17c1c..270addb76c3 100644
--- a/src/interfaces/ecpg/test/expected/sql-indicators.stdout
+++ b/src/interfaces/ecpg/test/expected/sql-indicators.stdout
@@ -1,3 +1,5 @@
 intvar: 0, nullind: -1
 intvar: 5, nullind: 0
 intvar: 5, nullind: -1
+empty string correctly rejected as integer
+empty string correctly rejected as float
diff --git a/src/interfaces/ecpg/test/sql/indicators.pgc b/src/interfaces/ecpg/test/sql/indicators.pgc
index d925faf35ce..16e67ce498c 100644
--- a/src/interfaces/ecpg/test/sql/indicators.pgc
+++ b/src/interfaces/ecpg/test/sql/indicators.pgc
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <ecpgerrno.h>
 
 exec sql include sqlca;
 exec sql include ../regression;
@@ -8,6 +9,7 @@ int main(void)
 	exec sql begin declare section;
 		int intvar = 5;
 		int nullind = -1;
+		float floatvar;
 	exec sql end declare section;
 
 	ECPGdebug(1,stderr);
@@ -42,6 +44,27 @@ int main(void)
 	exec sql select val into :intvar :nullind from indicator_test where id = 1;
 	printf("intvar: %d, nullind: %d\n", intvar, nullind);
 
+	/*
+	 * An empty string used to be silently converted to 0 instead of being
+	 * rejected as an invalid integer/float.
+	 */
+	exec sql insert into indicator_test (id, str, val) values ( 4, '', 0);
+	exec sql commit work;
+
+	exec sql select str into :intvar from indicator_test where id = 4;
+	if (sqlca.sqlcode == ECPG_INT_FORMAT)
+		printf("empty string correctly rejected as integer\n");
+	else
+		printf("unexpected sqlcode %ld for empty string as integer: %s\n",
+			   sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
+
+	exec sql select str into :floatvar from indicator_test where id = 4;
+	if (sqlca.sqlcode == ECPG_FLOAT_FORMAT)
+		printf("empty string correctly rejected as float\n");
+	else
+		printf("unexpected sqlcode %ld for empty string as float: %s\n",
+			   sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
+
 	exec sql drop table indicator_test;
 	exec sql commit work;
 
-- 
Tristan Partin
https://tristan.partin.io

Reply via email to