From 0bc4a3695849d7662b747bf60869ca6a6ae901b8 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Mon, 14 May 2018 10:12:53 -0700 Subject: [PATCH 1/8] Added test case for Issue 699 --- .../sqlsrv/srv_699_out_param_integer.phpt | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 test/functional/sqlsrv/srv_699_out_param_integer.phpt diff --git a/test/functional/sqlsrv/srv_699_out_param_integer.phpt b/test/functional/sqlsrv/srv_699_out_param_integer.phpt new file mode 100644 index 00000000..4a6a5631 --- /dev/null +++ b/test/functional/sqlsrv/srv_699_out_param_integer.phpt @@ -0,0 +1,94 @@ +--TEST-- +GitHub issue #699 - binding integer as output parameter failed +--DESCRIPTION-- +This test uses the sample stored procedure provided by the user, in which an +error situation caused the binding to fail with an irrelevant error message about UTF-8 translation. This test proves that this issue has been fixed. +--ENV-- +PHPT_EXEC=true +--SKIPIF-- + +--FILE-- + "UTF-8", "ConnectionPooling"=>1); +$conn = connect($connectionOptions); + +$tableName1 = "table_issue699_1"; +$tableName2 = "table_issue699_2"; +$procName = "proc_issue699"; + +dropTable($conn, $tableName1); +dropTable($conn, $tableName2); +dropProc($conn, $procName); + +// Create two test tables without encryption +$sql = "CREATE TABLE $tableName1 (correio_electronico NVARCHAR(50), nome NVARCHAR(50), telefones NVARCHAR(15), id_entidade INT)"; +$stmt = sqlsrv_query($conn, $sql); +if (!$stmt) { + fatalError("Failed to create table $tableName1\n"); +} + +$sql = "CREATE TABLE $tableName2 (estado TINYINT NOT NULL DEFAULT 0)"; +$stmt = sqlsrv_query($conn, $sql); +if (!$stmt) { + fatalError("Failed to create table $tableName2\n"); +} + +// Create the stored procedure +$sql = "CREATE PROCEDURE $procName @outparam INT OUTPUT AS + BEGIN + SET @outparam = 100; + INSERT INTO $tableName1 (correio_electronico, nome, telefones, id_entidade) + SELECT 'membros@membros.pt', 'Teste', 'xxx', 1 + FROM $tableName2 CC + WHERE CC.estado = 100 + BEGIN TRY + SET @outparam = 123 + END TRY + BEGIN CATCH + END CATCH + END"; + +$stmt = sqlsrv_query($conn, $sql); +if (!$stmt) { + fatalError("Error in creating the stored procedure $procName\n"); +} + +$sql_callSP = "{call $procName(?)}"; + +// Initialize the output parameter +$outParam = 1; +$params = array(array(&$outParam, SQLSRV_PARAM_OUT)); +$stmt = sqlsrv_query($conn, $sql_callSP, $params); +if (!$stmt) { + fatalError("Error in calling $procName\n"); +} + +while ($res = sqlsrv_next_result($stmt)); + +if ($outParam != 123) { + echo "The output param value $outParam is unexpected!\n"; +} + +dropTable($conn, $tableName1); +dropTable($conn, $tableName2); +dropProc($conn, $procName); + +// Free handles +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); + +echo "Done\n"; + +?> +--EXPECT-- +Done \ No newline at end of file From 999312be8b2d495d4ee96edea0b34ae111831b9f Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 May 2018 09:33:52 -0700 Subject: [PATCH 2/8] Tried using INT_MAX instead --- source/shared/core_stmt.cpp | 2 +- test/functional/sqlsrv/srv_699_out_param_integer.phpt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 30fe36bb..7c5fbe49 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -487,7 +487,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ // if it is boolean, set the lval to 0 or 1 convert_to_long( param_z ); buffer = ¶m_z->value; - buffer_len = sizeof( Z_LVAL_P( param_z )); + buffer_len = INT_MAX; //sizeof( Z_LVAL_P( param_z )); ind_ptr = buffer_len; if( direction != SQL_PARAM_INPUT ){ // save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned diff --git a/test/functional/sqlsrv/srv_699_out_param_integer.phpt b/test/functional/sqlsrv/srv_699_out_param_integer.phpt index 4a6a5631..caf939b3 100644 --- a/test/functional/sqlsrv/srv_699_out_param_integer.phpt +++ b/test/functional/sqlsrv/srv_699_out_param_integer.phpt @@ -9,14 +9,14 @@ PHPT_EXEC=true --FILE-- "UTF-8", "ConnectionPooling"=>1); From 9a82e9daa377379b6060644bb75a613d1160b265 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 May 2018 10:00:36 -0700 Subject: [PATCH 3/8] Used sizeof not INT_MAX --- source/shared/core_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 7c5fbe49..c55e72cc 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -487,7 +487,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ // if it is boolean, set the lval to 0 or 1 convert_to_long( param_z ); buffer = ¶m_z->value; - buffer_len = INT_MAX; //sizeof( Z_LVAL_P( param_z )); + buffer_len = sizeof(int); //sizeof( Z_LVAL_P( param_z )); ind_ptr = buffer_len; if( direction != SQL_PARAM_INPUT ){ // save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned From bca311081d8b3ee3c920d0edfc6e5ceaed0b59fb Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 May 2018 12:41:28 -0700 Subject: [PATCH 4/8] Modified the output param initial value --- source/shared/core_stmt.cpp | 2 +- .../functional/sqlsrv/srv_699_out_param_integer.phpt | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index c55e72cc..bc090e07 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -487,7 +487,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ // if it is boolean, set the lval to 0 or 1 convert_to_long( param_z ); buffer = ¶m_z->value; - buffer_len = sizeof(int); //sizeof( Z_LVAL_P( param_z )); + buffer_len = sizeof(int); // do not use size of a zend_long ind_ptr = buffer_len; if( direction != SQL_PARAM_INPUT ){ // save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned diff --git a/test/functional/sqlsrv/srv_699_out_param_integer.phpt b/test/functional/sqlsrv/srv_699_out_param_integer.phpt index caf939b3..e760b9bf 100644 --- a/test/functional/sqlsrv/srv_699_out_param_integer.phpt +++ b/test/functional/sqlsrv/srv_699_out_param_integer.phpt @@ -9,14 +9,14 @@ PHPT_EXEC=true --FILE-- "UTF-8", "ConnectionPooling"=>1); @@ -65,8 +65,8 @@ if (!$stmt) { $sql_callSP = "{call $procName(?)}"; -// Initialize the output parameter -$outParam = 1; +// Initialize the output parameter to any number +$outParam = -1; $params = array(array(&$outParam, SQLSRV_PARAM_OUT)); $stmt = sqlsrv_query($conn, $sql_callSP, $params); if (!$stmt) { From 4c7c08515d9d93efb0bbdb07529766675fe2cad5 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 May 2018 14:28:26 -0700 Subject: [PATCH 5/8] Added workaround for the test plus use BIGINT for 64-bit systems --- source/shared/core_stmt.cpp | 16 ++++++++++++---- .../sqlsrv/srv_699_out_param_integer.phpt | 18 +++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index bc090e07..c2fc18c5 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -487,7 +487,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ // if it is boolean, set the lval to 0 or 1 convert_to_long( param_z ); buffer = ¶m_z->value; - buffer_len = sizeof(int); // do not use size of a zend_long + buffer_len = sizeof( Z_LVAL_P( param_z )); ind_ptr = buffer_len; if( direction != SQL_PARAM_INPUT ){ // save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned @@ -1864,8 +1864,12 @@ SQLSMALLINT default_c_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, sql_c_type = SQL_C_SBIGINT; } else { - sql_c_type = SQL_C_SLONG; - } +#ifdef ZEND_ENABLE_ZVAL_LONG64 + sql_c_type = SQL_C_SBIGINT; +#else + sql_c_type = SQL_C_SLONG; +#endif + } break; case IS_DOUBLE: sql_c_type = SQL_C_DOUBLE; @@ -1934,8 +1938,12 @@ void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ sql_type = SQL_BIGINT; } else { +#ifdef ZEND_ENABLE_ZVAL_LONG64 + sql_type = SQL_BIGINT; +#else sql_type = SQL_INTEGER; - } +#endif + } break; case IS_DOUBLE: sql_type = SQL_FLOAT; diff --git a/test/functional/sqlsrv/srv_699_out_param_integer.phpt b/test/functional/sqlsrv/srv_699_out_param_integer.phpt index e760b9bf..bd48716b 100644 --- a/test/functional/sqlsrv/srv_699_out_param_integer.phpt +++ b/test/functional/sqlsrv/srv_699_out_param_integer.phpt @@ -9,14 +9,6 @@ PHPT_EXEC=true --FILE-- "UTF-8", "ConnectionPooling"=>1); @@ -63,7 +55,15 @@ if (!$stmt) { fatalError("Error in creating the stored procedure $procName\n"); } -$sql_callSP = "{call $procName(?)}"; +$set_no_count = ""; +if (strtoupper(substr(PHP_OS, 0, 3)) === 'LIN') { + // This test, when running outside of Windows, requires unixODBC 2.3.4 + // or above (see the list of bug fixes in www.unixodbc.org) + // Add this workaround for Linux platforms + $set_no_count = "SET NOCOUNT ON; "; +} + +$sql_callSP = $set_no_count . "{call $procName(?)}"; // Initialize the output parameter to any number $outParam = -1; From 0b5ceb001dc2ea462d5c46f8cc977306961dd16e Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 May 2018 15:57:47 -0700 Subject: [PATCH 6/8] Reverted the changes --- source/shared/core_stmt.cpp | 8 -------- test/functional/sqlsrv/srv_699_out_param_integer.phpt | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index c2fc18c5..31420442 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -1864,11 +1864,7 @@ SQLSMALLINT default_c_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, sql_c_type = SQL_C_SBIGINT; } else { -#ifdef ZEND_ENABLE_ZVAL_LONG64 - sql_c_type = SQL_C_SBIGINT; -#else sql_c_type = SQL_C_SLONG; -#endif } break; case IS_DOUBLE: @@ -1938,11 +1934,7 @@ void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ sql_type = SQL_BIGINT; } else { -#ifdef ZEND_ENABLE_ZVAL_LONG64 - sql_type = SQL_BIGINT; -#else sql_type = SQL_INTEGER; -#endif } break; case IS_DOUBLE: diff --git a/test/functional/sqlsrv/srv_699_out_param_integer.phpt b/test/functional/sqlsrv/srv_699_out_param_integer.phpt index bd48716b..39f19569 100644 --- a/test/functional/sqlsrv/srv_699_out_param_integer.phpt +++ b/test/functional/sqlsrv/srv_699_out_param_integer.phpt @@ -66,7 +66,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) === 'LIN') { $sql_callSP = $set_no_count . "{call $procName(?)}"; // Initialize the output parameter to any number -$outParam = -1; +$outParam = 1; $params = array(array(&$outParam, SQLSRV_PARAM_OUT)); $stmt = sqlsrv_query($conn, $sql_callSP, $params); if (!$stmt) { From 4747537a967927d325cc8cb03b75e894892f7a32 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 May 2018 16:13:36 -0700 Subject: [PATCH 7/8] Fixed the formatting --- source/shared/core_stmt.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 31420442..bad0e38d 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -1856,14 +1856,14 @@ SQLSMALLINT default_c_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, break; case IS_TRUE: case IS_FALSE: - sql_c_type = SQL_C_SLONG; - break; + sql_c_type = SQL_C_SLONG; + break; case IS_LONG: - //ODBC 64-bit long and integer type are 4 byte values. - if ( ( Z_LVAL_P( param_z ) < INT_MIN ) || ( Z_LVAL_P( param_z ) > INT_MAX ) ) { - sql_c_type = SQL_C_SBIGINT; - } - else { + //ODBC 64-bit long and integer type are 4 byte values. + if ((Z_LVAL_P(param_z) < INT_MIN) || (Z_LVAL_P(param_z) > INT_MAX)) { + sql_c_type = SQL_C_SBIGINT; + } + else { sql_c_type = SQL_C_SLONG; } break; @@ -1929,12 +1929,12 @@ void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ sql_type = SQL_INTEGER; break; case IS_LONG: - //ODBC 64-bit long and integer type are 4 byte values. - if ( ( Z_LVAL_P( param_z ) < INT_MIN ) || ( Z_LVAL_P( param_z ) > INT_MAX ) ) { - sql_type = SQL_BIGINT; - } - else { - sql_type = SQL_INTEGER; + //ODBC 64-bit long and integer type are 4 byte values. + if ((Z_LVAL_P(param_z) < INT_MIN) || (Z_LVAL_P(param_z) > INT_MAX)) { + sql_type = SQL_BIGINT; + } + else { + sql_type = SQL_INTEGER; } break; case IS_DOUBLE: From 0a1113697f7b227f8b2ece4a163df8bc3a1af70f Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 15 May 2018 16:25:54 -0700 Subject: [PATCH 8/8] Modified the test to address VSO 2915 --- .../pdo_sqlsrv/pdo_ae_output_param_decimals.phpt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/functional/pdo_sqlsrv/pdo_ae_output_param_decimals.phpt b/test/functional/pdo_sqlsrv/pdo_ae_output_param_decimals.phpt index 339c6821..e1fd26cb 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_output_param_decimals.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_output_param_decimals.phpt @@ -133,12 +133,14 @@ function testOutputDecimals($inout) // call stored procedure $outSql = getCallProcSqlPlaceholders($spname, 2); foreach ($pdoParamTypes as $pdoParamType) { - $det = $rand = 0.0; + // Do not initialize $det or $rand as empty strings + // See VSO 2915 for details. The string must be a numeric + // string, and to make it work for all precisions, we + // simply set it to a single-digit string. + $det = $rand = '0'; $stmt = $conn->prepare($outSql); $len = 2048; - // Do not initialize $det or $rand as empty strings - // See VSO 2915 for details if ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) { $len = PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE; $det = $rand = 0;