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
