In bug #19584[0], the user reported that tid parsing was platform dependent 
due to the way that strtoul() works. Take the following code:

        strtoul("", &endptr, 10);

On glibc, the return value is 0, errno is not set, and endptr points to 
the empty string. On Apple libc, the return value is 0, errno is set to 
EINVAL, and endptr points to the empty string.

The tid parsing code was correctly handling EINVAL, but did not handle 
the case where endptr == input, which we do in quite a few places around 
the codebase. The end effect was that glibc systems accepted "(1,)" and 
"(,1)" as valid tids for parsing purposes, while Apple libc systems did 
not.

The first patch in the series fixes this issue. I did some analysis on 
other uses of strtoul() and friends and found a few more places where 
integers were not being correctly parsed from strings. Those are each 
attached as individual patches to ease backpatching if it is determined 
that we should. Otherwise, I suggest squashing the series. I wonder if 
we should come up with a helper macro for helping callers handle errors 
correctly? Or should we just always check this case?

The tid issue was discovered back in 2004[1] funnily enough. Tom 
diagnosed it correctly, but a patch was seemingly never committed, so 
here we are.

[0]: 
https://www.postgresql.org/message-id/19584-e60c446ba6f57c9c%40postgresql.org
[1]: 
https://www.postgresql.org/message-id/16E80DD4-85B9-11D8-A231-0003935B359C%40cegroup.it

-- 
Tristan Partin
PostgreSQL Contributors Team
AWS (https://aws.amazon.com)
From 7a319dc9b7d087ae19ce00d2248b590a12e549ec Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Thu, 30 Jul 2026 07:40:31 +0000
Subject: [PATCH v1 1/5] 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 ecf902895d9f85d989532058492bc68326a9439f Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Thu, 30 Jul 2026 08:23:25 +0000
Subject: [PATCH v1 2/5] Fix integer parsing in manifest parser for portability

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 0, and Apple libc correctly treats it as an error.

Signed-off-by: Tristan Partin <[email protected]>
---
 src/bin/pg_verifybackup/t/005_bad_manifest.pl | 16 ++++++++++++++++
 src/common/parse_manifest.c                   |  6 +++---
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/bin/pg_verifybackup/t/005_bad_manifest.pl 
b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
index 0413fea02c3..bc9fe4a79d7 100644
--- a/src/bin/pg_verifybackup/t/005_bad_manifest.pl
+++ b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
@@ -39,6 +39,10 @@
 {"PostgreSQL-Backup-Manifest-Version": 9876599}
 EOM
 
+test_parse_error('system identifier in manifest not an integer', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "System-Identifier": ""}
+EOM
+
 test_parse_error('unexpected scalar', <<EOM);
 {"PostgreSQL-Backup-Manifest-Version": 1, "Files": true}
 EOM
@@ -79,6 +83,12 @@
 ]}
 EOM
 
+test_parse_error('file size is not an integer', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
+    {"Path": "x", "Size": ""}
+]}
+EOM
+
 test_parse_error('could not decode file name', <<EOM);
 {"PostgreSQL-Backup-Manifest-Version": 1, "Files": [
     {"Encoded-Path": "123", "Size": 0}
@@ -146,6 +156,12 @@
 ]}
 EOM
 
+test_parse_error('timeline is not an integer', <<EOM);
+{"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
+    {"Timeline": "", "Start-LSN": "0/0", "End-LSN": "0/0"}
+]}
+EOM
+
 test_parse_error('could not parse start LSN', <<EOM);
 {"PostgreSQL-Backup-Manifest-Version": 1, "WAL-Ranges": [
     {"Timeline": 1, "Start-LSN": "oops", "End-LSN": "0/0"}
diff --git a/src/common/parse_manifest.c b/src/common/parse_manifest.c
index 5065c9bf39c..337cae535f4 100644
--- a/src/common/parse_manifest.c
+++ b/src/common/parse_manifest.c
@@ -631,7 +631,7 @@ 
json_manifest_finalize_system_identifier(JsonManifestParseState *parse)
 
        /* Parse system identifier. */
        system_identifier = strtou64(parse->manifest_system_identifier, &ep, 
10);
-       if (*ep)
+       if (ep == parse->manifest_system_identifier || *ep)
                json_manifest_parse_failure(parse->context,
                                                                        "system 
identifier in manifest not an integer");
 
@@ -688,7 +688,7 @@ json_manifest_finalize_file(JsonManifestParseState *parse)
 
        /* Parse size. */
        size = strtou64(parse->size, &ep, 10);
-       if (*ep)
+       if (ep == parse->size || *ep)
                json_manifest_parse_failure(parse->context,
                                                                        "file 
size is not an integer");
 
@@ -766,7 +766,7 @@ json_manifest_finalize_wal_range(JsonManifestParseState 
*parse)
 
        /* Parse timeline. */
        tli = strtoul(parse->timeline, &ep, 10);
-       if (*ep)
+       if (ep == parse->timeline || *ep)
                json_manifest_parse_failure(parse->context,
                                                                        
"timeline is not an integer");
        if (!parse_xlogrecptr(&start_lsn, parse->start_lsn))
-- 
Tristan Partin
https://tristan.partin.io

From 100fd38aec4c045f2b33b03ea62d17be93e6e920 Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Thu, 30 Jul 2026 08:26:26 +0000
Subject: [PATCH v1 3/5] 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 96da8c2831804a3818ecee0b467f0b7b000efe74 Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Thu, 30 Jul 2026 08:33:28 +0000
Subject: [PATCH v1 4/5] 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

From 08c74654d4bd67e6925276958d2d40b61af5feb2 Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Thu, 30 Jul 2026 08:39:48 +0000
Subject: [PATCH v1 5/5] Fix invalid endptr comparison in pg_verifybackup.c

strtoul() will never set endptr to NULL. It's an invalid check. We
should be checking if suffix points to relpath instead.

Signed-off-by: Tristan Partin <[email protected]>
---
 src/bin/pg_verifybackup/pg_verifybackup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c 
b/src/bin/pg_verifybackup/pg_verifybackup.c
index 05385a91e88..796eeb6ec04 100644
--- a/src/bin/pg_verifybackup/pg_verifybackup.c
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -961,7 +961,7 @@ precheck_tar_backup_file(verifier_context *context, char 
*relpath,
                 * Report an error if we didn't consume at least one character, 
if the
                 * result is 0, or if the value is too large to be a valid OID.
                 */
-               if (suffix == NULL || num <= 0 || num > OID_MAX)
+               if (suffix == relpath || num <= 0 || num > OID_MAX)
                {
                        report_backup_error(context,
                                                                "file \"%s\" is 
not expected in a tar format backup",
-- 
Tristan Partin
https://tristan.partin.io

Reply via email to