From 22ebfb7e3eb0d32d0889e5934c3fd3741fc8a917 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 3 Oct 2025 17:17:06 -0300 Subject: [PATCH 1/2] Try to use out_length for bound fetched values I have no idea why it's using strlen in the first place, but if SQL/CLI returns junk in the buffer, then the length might be correct. This matches what we did with column names on IBM i. The SQL_BINARY case is more puzzling, but we can take a look at it later if this works for strings. Why is it ifdef'd to use strlen/out_length depending on platform? --- ibm_db2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ibm_db2.c b/ibm_db2.c index f1b61aa..d119cdc 100644 --- a/ibm_db2.c +++ b/ibm_db2.c @@ -6289,7 +6289,7 @@ static void _php_db2_bind_fetch_helper(INTERNAL_FUNCTION_PARAMETERS, int op) case SQL_DECFLOAT: #ifdef PASE /* i5/OS trim spaces */ if (stmt_res->s_i5_conn_parent->c_i5_char_trim > 0) { - i5trim = strlen((char *)row_data->str_val); + i5trim = out_length; for(; i5trim >= 0; i5trim--) { i5char = (char)(((char *)row_data->str_val)[i5trim]); if (i5char == 0x00 || i5char == 0x20) { @@ -6316,11 +6316,11 @@ static void _php_db2_bind_fetch_helper(INTERNAL_FUNCTION_PARAMETERS, int op) #endif /* PASE */ if ( op & DB2_FETCH_ASSOC ) { add_assoc_stringl(return_value, (char *)stmt_res->column_info[i].name, - (char *)row_data->str_val, strlen((char *)row_data->str_val)); + (char *)row_data->str_val, out_length); } if ( op & DB2_FETCH_INDEX ) { add_index_stringl(return_value, i, (char *)row_data->str_val, - strlen((char *)row_data->str_val)); + out_length); } break; case SQL_BOOLEAN: From ad38bae29b2cba94c9d1279432159558d95e97fb Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 3 Oct 2025 17:55:40 -0300 Subject: [PATCH 2/2] Explain reasoning, fall back to strlen if lower --- ibm_db2.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ibm_db2.c b/ibm_db2.c index d119cdc..f83bf8b 100644 --- a/ibm_db2.c +++ b/ibm_db2.c @@ -6171,6 +6171,7 @@ static void _php_db2_bind_fetch_helper(INTERNAL_FUNCTION_PARAMETERS, int op) unsigned char *out_ptr; int i5trim = 0; int i5char; + size_t string_length; if (zend_parse_parameters(argc, "r|l", &stmt, &row_number) == FAILURE) { return; } @@ -6287,9 +6288,20 @@ static void _php_db2_bind_fetch_helper(INTERNAL_FUNCTION_PARAMETERS, int op) case SQL_DECIMAL: case SQL_NUMERIC: case SQL_DECFLOAT: + /* CB20251003: Sometimes SQL/CLI (at least on IBM i) may + * return junk at the end of a buffer. If that's the case, + * we should trust out_length. However, it seems we do want + * to truncate at the first nul character like strlen does, + * (see tests/test_6572_SQLStringsContNULLChar.phpt), so + * pick the lower number between the two. Alternatively, + * maybe we could consider changing the behaviour around + * nul characters? It would make sense for binaries... + */ + string_length = strlen((char *)row_data->str_val); + string_length = out_length < string_length ? out_length : string_length; #ifdef PASE /* i5/OS trim spaces */ if (stmt_res->s_i5_conn_parent->c_i5_char_trim > 0) { - i5trim = out_length; + i5trim = string_length; for(; i5trim >= 0; i5trim--) { i5char = (char)(((char *)row_data->str_val)[i5trim]); if (i5char == 0x00 || i5char == 0x20) { @@ -6316,11 +6328,11 @@ static void _php_db2_bind_fetch_helper(INTERNAL_FUNCTION_PARAMETERS, int op) #endif /* PASE */ if ( op & DB2_FETCH_ASSOC ) { add_assoc_stringl(return_value, (char *)stmt_res->column_info[i].name, - (char *)row_data->str_val, out_length); + (char *)row_data->str_val, string_length); } if ( op & DB2_FETCH_INDEX ) { add_index_stringl(return_value, i, (char *)row_data->str_val, - out_length); + string_length); } break; case SQL_BOOLEAN: