From 98fd299a311e7ae10e25ceec48942428203ee807 Mon Sep 17 00:00:00 2001 From: v-dareck Date: Thu, 16 Feb 2017 11:50:24 -0800 Subject: [PATCH 1/3] Add check for SQL_NO_TOTAL for SQLBindParameter out parameter. --- source/shared/core_stmt.cpp | 9 + test/sqlsrv/sqlsrv_bind_param_out_string.phpt | 454 ++++++++++++++++++ 2 files changed, 463 insertions(+) create mode 100644 test/sqlsrv/sqlsrv_bind_param_out_string.phpt diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 052884ee..09c8d9bf 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -2030,6 +2030,15 @@ void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC ) throw core::CoreException(); } + // For ODBC 11+ see https://msdn.microsoft.com/en-us/library/jj219209.aspx + // A length value of SQL_NO_TOTAL for SQLBindParameter indicates that the buffer contains up to + // output_param->original_buffer_len data and is NULL terminated. + // The IF statement can be true when using connection pooling with unixODBC 2.3.4. + if ( str_len == SQL_NO_TOTAL ) + { + str_len = output_param->original_buffer_len - null_size; + } + // if it's not in the 8 bit encodings, then it's in UTF-16 if( output_param->encoding != SQLSRV_ENCODING_CHAR && output_param->encoding != SQLSRV_ENCODING_BINARY ) { bool converted = convert_zval_string_from_utf16(output_param->encoding, value_z, str_len); diff --git a/test/sqlsrv/sqlsrv_bind_param_out_string.phpt b/test/sqlsrv/sqlsrv_bind_param_out_string.phpt new file mode 100644 index 00000000..3c4d21c4 --- /dev/null +++ b/test/sqlsrv/sqlsrv_bind_param_out_string.phpt @@ -0,0 +1,454 @@ +--TEST-- +Verify the Binary and Char encoding output when binding output string with SQLSTYPE option with different size +--FILE-- + +"$username", "PWD"=>"$password"); +$conn = sqlsrv_connect($serverName, $connectionInfo); +if( $conn === false ) { + die( print_r( sqlsrv_errors(), true )); +} +$conn = null; + +// with unixODBC 2.3.4 + connection pooling the NVARCHAR(1) section below may seg fault +$conn = sqlsrv_connect($serverName, $connectionInfo); +if( $conn === false ) { + die( print_r( sqlsrv_errors(), true )); +} + + +$bindtable = "#BindStringTest"; +$sproc = "#uspPerson"; + +// Create table +$stmt = sqlsrv_query( $conn, "CREATE TABLE ".$bindtable." (PersonID int, Name nvarchar(50))" ); +if( $stmt === false ) { + die( print_r( sqlsrv_errors(), true )); +} + +$stmt = sqlsrv_query( $conn, "INSERT INTO ".$bindtable." (PersonID, Name) VALUES (10, N'Miller')" ); +if( $stmt === false ) { + die( print_r( sqlsrv_errors(), true )); +} + +$stmt = sqlsrv_query( $conn, "INSERT INTO ".$bindtable." (PersonID, Name) VALUES (11, N'JSmith')" ); +if( $stmt === false ) { + die( print_r( sqlsrv_errors(), true )); +} + +$tsql_createSP = "CREATE PROCEDURE ".$sproc." + @id int, @return nvarchar(50) OUTPUT + AS + BEGIN + SET NOCOUNT ON; + SET @return = (SELECT Name FROM ".$bindtable." WHERE PersonID = @id) + END"; + +$stmt = sqlsrv_query( $conn, $tsql_createSP); +if( $stmt === false ) +{ + echo "Error in executing statement 2.\n"; + die( print_r( sqlsrv_errors(), true)); +} + +$tsql_callSP = "{call ".$sproc."( ? , ?)}"; + + +//*********************************************************************************************** + +echo "NVARCHAR(32)\n"; +echo "---------Encoding char-----------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + SQLSRV_SQLTYPE_NVARCHAR(32) + )); + +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) === false) +{ + print_r( sqlsrv_errors(), true); +} + +$expectedLength = 6; +$expectedValue = "Miller"; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +echo "---------Encoding binary---------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_NVARCHAR(32) + )); +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) == false) + { + print_r( sqlsrv_errors(), true); + } + +$expectedLength = 12; +$expectedValue = "M\0i\0l\0l\0e\0r\0"; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +//*********************************************************************************************** +echo "\n\n"; +echo "NVARCHAR(50)\n"; +echo "---------Encoding char-----------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + SQLSRV_SQLTYPE_NVARCHAR(50) + )); + +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) === false) +{ + print_r( sqlsrv_errors(), true); +} + +$expectedLength = 6; +$expectedValue = "Miller"; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + + +echo "---------Encoding binary---------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_NVARCHAR(50) + )); +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) == false) + { + print_r( sqlsrv_errors(), true); + } + +$expectedLength = 12; +$expectedValue = "M\0i\0l\0l\0e\0r\0"; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +//*********************************************************************************************** +echo "\n\n"; +echo "NVARCHAR(1)\n"; +echo "---------Encoding char-----------\n"; +$id = 10; +$return = ""; + + +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + SQLSRV_SQLTYPE_NVARCHAR(1) + )); + +// with unixODBC 2.3.4 connection pooling the statement may not fail. +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) === false) +{ + echo "Statement should fail\n"; +} + +$expectedLength = 1; +$expectedValue = "M"; +$actualValue = $return; +$actualLength = strlen($return); +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +echo "---------Encoding binary---------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_NVARCHAR(1) + )); +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) == false) + { + echo "Statement should fail\n"; + } + +$expectedLength = 2; +$expectedValue = "M\0"; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +//*********************************************************************************************** +echo "\n\n"; +echo "NCHAR(32)\n"; +echo "---------Encoding char-----------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + SQLSRV_SQLTYPE_NCHAR(32) + )); + +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) === false) +{ + print_r( sqlsrv_errors(), true); +} + +$expectedLength = 32; +$expectedValue = "Miller "; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +echo "---------Encoding binary---------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_NCHAR(32) + )); +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) == false) + { + print_r( sqlsrv_errors(), true); + } + +$expectedLength = 64; +$expectedValue = "M\0i\0l\0l\0e\0r\0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0"; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +//*********************************************************************************************** +echo "\n\n"; +echo "NCHAR(0)\n"; +echo "---------Encoding char-----------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + SQLSRV_SQLTYPE_NCHAR(0) + )); + +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) === false) +{ + echo "Statement should fail\n"; +} + +$expectedLength = 0; +$expectedValue = ""; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +echo "---------Encoding binary---------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_NCHAR(0) + )); +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) == false) + { + echo "Statement should fail\n"; + } + +$expectedLength = 0; +$expectedValue = ""; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +//*********************************************************************************************** +echo "\n\n"; +echo "NCHAR(50)\n"; +echo "---------Encoding char-----------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + SQLSRV_SQLTYPE_NCHAR(50) + )); + +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) === false) +{ + print_r( sqlsrv_errors(), true); +} + +$expectedLength = 50; +$expectedValue = "Miller "; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +echo "---------Encoding binary---------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_NCHAR(50) + )); +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) == false) + { + print_r( sqlsrv_errors(), true); + } + +$expectedLength = 100; +$expectedValue = "M\0i\0l\0l\0e\0r\0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0"; +$actualLength = strlen($return); +$actualValue = $return; +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +//*********************************************************************************************** +// NCHAR 1: less than length of the returned value +echo "\n\n"; +echo "NCHAR(1)\n"; +echo "---------Encoding char-----------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), + SQLSRV_SQLTYPE_NCHAR(1) + )); + +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) === false) +{ + print_r( sqlsrv_errors(), true); +} + +$expectedLength = 1; +$expectedValue = "M"; +$actualValue = $return; +$actualLength = strlen($return); +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +echo "---------Encoding binary---------\n"; +$id = 10; +$return = ""; +$params = array( + array($id, SQLSRV_PARAM_IN), + array(&$return, SQLSRV_PARAM_OUT, + SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), + SQLSRV_SQLTYPE_NCHAR(1) + )); +if( $stmt = sqlsrv_query($conn, $tsql_callSP, $params) == false) + { + print_r( sqlsrv_errors(), true); + } + +$expectedLength = 2; +$expectedValue = "M\0"; +$actualValue = $return; +$actualLength = strlen($return); +compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ); + +sqlsrv_close($conn); + +$status = true; + +/** +* Compares actual output to expected one +* @param $expectedLength The length of the expected value +* @param $expectedValue The expected value +* @param $actualLength The length of the actual value +* @param $actualValue The actual value +*/ +function compareResults ( $expectedLength, $expectedValue, $actualLength, $actualValue ) +{ + $match = false; + if ( $expectedLength == $actualLength) + { + if ( strncmp ( $actualValue, $expectedValue, $expectedLength ) == 0 ) + { + $match = true; + } + } + if ( !$match ) + { + echo "The actual result is different from the expected one \n"; + } + else + { + echo "The actual result is the same as the expected one \n"; + } +} +?> +--EXPECT-- +NVARCHAR(32) +---------Encoding char----------- +The actual result is the same as the expected one +---------Encoding binary--------- +The actual result is the same as the expected one + + +NVARCHAR(50) +---------Encoding char----------- +The actual result is the same as the expected one +---------Encoding binary--------- +The actual result is the same as the expected one + + +NVARCHAR(1) +---------Encoding char----------- +Statement should fail +The actual result is the same as the expected one +---------Encoding binary--------- +Statement should fail +The actual result is the same as the expected one + + +NCHAR(32) +---------Encoding char----------- +The actual result is the same as the expected one +---------Encoding binary--------- +The actual result is the same as the expected one + + +NCHAR(0) +---------Encoding char----------- +Statement should fail +The actual result is the same as the expected one +---------Encoding binary--------- +Statement should fail +The actual result is the same as the expected one + + +NCHAR(50) +---------Encoding char----------- +The actual result is the same as the expected one +---------Encoding binary--------- +The actual result is the same as the expected one + + +NCHAR(1) +---------Encoding char----------- +The actual result is the same as the expected one +---------Encoding binary--------- +The actual result is the same as the expected one From b96499ff003f2edfba8a5f802b47a3d1e82c54e5 Mon Sep 17 00:00:00 2001 From: v-dareck Date: Thu, 16 Feb 2017 13:56:02 -0800 Subject: [PATCH 2/3] Update PHPT comment. --- test/sqlsrv/sqlsrv_bind_param_out_string.phpt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/sqlsrv/sqlsrv_bind_param_out_string.phpt b/test/sqlsrv/sqlsrv_bind_param_out_string.phpt index 3c4d21c4..9829233b 100644 --- a/test/sqlsrv/sqlsrv_bind_param_out_string.phpt +++ b/test/sqlsrv/sqlsrv_bind_param_out_string.phpt @@ -12,7 +12,8 @@ if( $conn === false ) { } $conn = null; -// with unixODBC 2.3.4 + connection pooling the NVARCHAR(1) section below may seg fault +// with unixODBC 2.3.4 + connection pooling the NVARCHAR(1) section older versions may error as +// unixODBC error returns behave differently with connection pooling. $conn = sqlsrv_connect($serverName, $connectionInfo); if( $conn === false ) { die( print_r( sqlsrv_errors(), true )); From 5bff3c5468fcf25c923a4f91708ccf1fb43f2930 Mon Sep 17 00:00:00 2001 From: v-dareck Date: Thu, 16 Feb 2017 15:25:41 -0800 Subject: [PATCH 3/3] Update PHPT comment and script. --- test/sqlsrv/sqlsrv_bind_param_out_string.phpt | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/test/sqlsrv/sqlsrv_bind_param_out_string.phpt b/test/sqlsrv/sqlsrv_bind_param_out_string.phpt index 9829233b..4cb11131 100644 --- a/test/sqlsrv/sqlsrv_bind_param_out_string.phpt +++ b/test/sqlsrv/sqlsrv_bind_param_out_string.phpt @@ -1,5 +1,11 @@ --TEST-- -Verify the Binary and Char encoding output when binding output string with SQLSTYPE option with different size +Verify the Binary and Char encoding output when binding output string with SQLSTYPE option with different size. +--DESCRIPTION-- +Tests different sizes of output string which may cause ODBC to return trunc error info. +With unixODBC 2.3.4, when connection pooling is enabled, error information maybe returned differently +than older versions (or with pooling disabled). +The NVARCHAR(1) section would cause an ODBC call to return an errorinfo to the driver causing the statement to fail. +With unixODBC 2.3.4 + pooling the statement executes without error. --FILE--