Merge pull request #293 from yitam/GH231
For issue #231, force column size to be determined
This commit is contained in:
commit
2610243bef
|
@ -2420,16 +2420,20 @@ void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULE
|
|||
|
||||
// calculate the size of each 'element' represented by column_size. WCHAR is of course 2,
|
||||
// as is a n(var)char/ntext field being returned as a binary field.
|
||||
elem_size = (c_type == SQL_C_WCHAR || (c_type == SQL_C_BINARY && (sql_type == SQL_WCHAR || sql_type == SQL_WVARCHAR))) ? 2 : 1;
|
||||
elem_size = (c_type == SQL_C_WCHAR || (c_type == SQL_C_BINARY && (sql_type == SQL_WCHAR || sql_type == SQL_WVARCHAR || sql_type == SQL_WLONGVARCHAR ))) ? 2 : 1;
|
||||
|
||||
// account for the NULL terminator returned by ODBC and needed by Zend to avoid a "String not null terminated" debug warning
|
||||
expected_len = column_size * elem_size + elem_size;
|
||||
SQLULEN field_size = column_size;
|
||||
if (column_size == SQL_SS_LENGTH_UNLIMITED) {
|
||||
field_size = SQL_SERVER_MAX_FIELD_SIZE / elem_size;
|
||||
}
|
||||
expected_len = field_size * elem_size + elem_size;
|
||||
|
||||
// binary fields aren't null terminated, so we need to account for that in our buffer length calcuations
|
||||
buffer_null_extra = (c_type == SQL_C_BINARY) ? elem_size : 0;
|
||||
|
||||
// this is the size of the string for Zend and for the StrLen parameter to SQLBindParameter
|
||||
without_null_len = column_size * elem_size;
|
||||
without_null_len = field_size * elem_size;
|
||||
|
||||
// increment to include the null terminator since the Zend length doesn't include the null terminator
|
||||
buffer_len += elem_size;
|
||||
|
|
|
@ -2028,11 +2028,10 @@ void parse_param_array( ss_sqlsrv_stmt* stmt, _Inout_ zval* param_array, zend_ul
|
|||
decimal_digits = 0;
|
||||
}
|
||||
|
||||
// if the user for some reason provides an output parameter with a null phptype and a specified
|
||||
// if the user for some reason provides an inout / output parameter with a null phptype and a specified
|
||||
// sql server type, infer the php type from the sql server type.
|
||||
if( direction == SQL_PARAM_OUTPUT && php_type_param_was_null && !sql_type_param_was_null ) {
|
||||
if( direction != SQL_PARAM_INPUT && php_type_param_was_null && !sql_type_param_was_null ) {
|
||||
|
||||
int encoding;
|
||||
sqlsrv_phptype sqlsrv_phptype;
|
||||
|
||||
sqlsrv_phptype = determine_sqlsrv_php_type( stmt, sql_type, (SQLUINTEGER)column_size, true );
|
||||
|
@ -2043,7 +2042,7 @@ void parse_param_array( ss_sqlsrv_stmt* stmt, _Inout_ zval* param_array, zend_ul
|
|||
"validated sql type and column_size" );
|
||||
|
||||
php_out_type = static_cast<SQLSRV_PHPTYPE>( sqlsrv_phptype.typeinfo.type );
|
||||
encoding = sqlsrv_phptype.typeinfo.encoding;
|
||||
encoding = static_cast<SQLSRV_ENCODING>( sqlsrv_phptype.typeinfo.encoding );
|
||||
}
|
||||
|
||||
// verify that the parameter is a valid output param type
|
||||
|
|
189
test/sqlsrv/srv_231_string_truncation_varchar_max.phpt
Normal file
189
test/sqlsrv/srv_231_string_truncation_varchar_max.phpt
Normal file
|
@ -0,0 +1,189 @@
|
|||
--TEST--
|
||||
GitHub issue #231 - String truncation when binding varchar(max)
|
||||
--SKIPIF--
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
sqlsrv_configure( 'WarningsReturnAsErrors', 1 );
|
||||
|
||||
require_once("autonomous_setup.php");
|
||||
|
||||
// Connect
|
||||
$connectionInfo = array( "UID"=>$username, "PWD"=>$password);
|
||||
$conn = sqlsrv_connect($serverName, $connectionInfo);
|
||||
if( $conn === false ) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
|
||||
$tableName = "#testDataTypes_GH231";
|
||||
$columnNames = array( "c1","c2" );
|
||||
|
||||
for ($k = 1; $k <= 8; $k++)
|
||||
{
|
||||
$sqlType = GetSqlType($k);
|
||||
$dataType = "[$columnNames[0]] int, [$columnNames[1]] $sqlType";
|
||||
|
||||
$sql = "CREATE TABLE [$tableName] ($dataType)";
|
||||
$stmt1 = sqlsrv_query($conn, $sql);
|
||||
sqlsrv_free_stmt($stmt1);
|
||||
|
||||
$sql = "INSERT INTO [$tableName] ($columnNames[0], $columnNames[1]) VALUES (?, ?)";
|
||||
$data = GetData($k);
|
||||
$phpType = GetPhpType($k);
|
||||
$driverType = GetDriverType($k, strlen($data));
|
||||
|
||||
$params = array($k, array($data, SQLSRV_PARAM_IN, $phpType, $driverType));
|
||||
$stmt2 = sqlsrv_prepare($conn, $sql, $params);
|
||||
sqlsrv_execute($stmt2);
|
||||
sqlsrv_free_stmt($stmt2);
|
||||
|
||||
ExecProc($conn, $tableName, $columnNames, $k, $data, $sqlType);
|
||||
|
||||
$stmt3 = sqlsrv_query($conn, "DROP TABLE [$tableName]");
|
||||
sqlsrv_free_stmt($stmt3);
|
||||
}
|
||||
|
||||
sqlsrv_close($conn);
|
||||
|
||||
|
||||
function ExecProc($conn, $tableName, $columnNames, $k, $data, $sqlType)
|
||||
{
|
||||
$spArgs = "@p1 int, @p2 $sqlType OUTPUT";
|
||||
$spCode = "SET @p2 = ( SELECT c2 FROM $tableName WHERE c1 = @p1 )";
|
||||
$procName = "testBindOutSp";
|
||||
|
||||
$stmt1 = sqlsrv_query($conn, "CREATE PROC [$procName] ($spArgs) AS BEGIN $spCode END");
|
||||
sqlsrv_free_stmt($stmt1);
|
||||
|
||||
echo "\nData Type: ".$sqlType." binding as \n";
|
||||
|
||||
$direction = SQLSRV_PARAM_OUT;
|
||||
echo "Output parameter: \t";
|
||||
CallProc($conn, $procName, $k, $direction, $data);
|
||||
|
||||
$direction = SQLSRV_PARAM_INOUT;
|
||||
echo "InOut parameter: \t";
|
||||
CallProc($conn, $procName, $k, $direction, $data);
|
||||
|
||||
$stmt2 = sqlsrv_query($conn, "DROP PROC [$procName]");
|
||||
sqlsrv_free_stmt($stmt2);
|
||||
}
|
||||
|
||||
function CallProc($conn, $procName, $k, $direction, $data)
|
||||
{
|
||||
$driverType = GetDriverType($k, strlen($data));
|
||||
$callArgs = "?, ?";
|
||||
|
||||
// Data to initialize $callResult variable. This variable should be shorter than inserted data in the table
|
||||
$initData = "ShortString";
|
||||
$callResult = $initData;
|
||||
|
||||
// Make sure not to specify the PHP type
|
||||
$params = array( array( $k, SQLSRV_PARAM_IN ),
|
||||
array( &$callResult, $direction, null, $driverType ));
|
||||
$stmt = sqlsrv_query($conn, "{ CALL [$procName] ($callArgs)}", $params);
|
||||
if($stmt === false) {
|
||||
die( print_r( sqlsrv_errors(), true ));
|
||||
}
|
||||
|
||||
// $callResult should be updated to the value in the table
|
||||
$matched = ($callResult === $data);
|
||||
if ($matched)
|
||||
echo "data matched!\n";
|
||||
else
|
||||
echo "failed!\n";
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
function GetData($k)
|
||||
{
|
||||
$data = "LongStringForTesting";
|
||||
if ($k == 8) {
|
||||
$data = "<XmlTestData><Letters1>The quick brown fox jumps over the lazy dog</Letters1><Digits1>0123456789</Digits1></XmlTestData>";
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function GetPhpType($k)
|
||||
{
|
||||
$phpType = SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR);
|
||||
if ($k == 7) {
|
||||
$phpType = SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY);
|
||||
}
|
||||
|
||||
return $phpType;
|
||||
}
|
||||
|
||||
function GetSqlType($k)
|
||||
{
|
||||
switch ($k)
|
||||
{
|
||||
case 1: return ("char(512)");
|
||||
case 2: return ("varchar(512)");
|
||||
case 3: return ("varchar(max)");
|
||||
case 4: return ("nchar(512)");
|
||||
case 5: return ("nvarchar(512)");
|
||||
case 6: return ("nvarchar(max)");
|
||||
case 7: return ("varbinary(max)");
|
||||
case 8: return ("xml");
|
||||
default: break;
|
||||
}
|
||||
return ("udt");
|
||||
}
|
||||
|
||||
function GetDriverType($k, $dataSize)
|
||||
{
|
||||
switch ($k)
|
||||
{
|
||||
case 1: return (SQLSRV_SQLTYPE_CHAR($dataSize));
|
||||
case 2: return (SQLSRV_SQLTYPE_VARCHAR($dataSize));
|
||||
case 3: return (SQLSRV_SQLTYPE_VARCHAR('max'));
|
||||
case 4: return (SQLSRV_SQLTYPE_NCHAR($dataSize));
|
||||
case 5: return (SQLSRV_SQLTYPE_NVARCHAR($dataSize));
|
||||
case 6: return (SQLSRV_SQLTYPE_NVARCHAR('max'));
|
||||
case 7: return (SQLSRV_SQLTYPE_VARBINARY('max'));
|
||||
case 8: return (SQLSRV_SQLTYPE_XML);
|
||||
|
||||
default: break;
|
||||
}
|
||||
return (SQLSRV_SQLTYPE_UDT);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
--EXPECT--
|
||||
|
||||
Data Type: char(512) binding as
|
||||
Output parameter: data matched!
|
||||
InOut parameter: data matched!
|
||||
|
||||
Data Type: varchar(512) binding as
|
||||
Output parameter: data matched!
|
||||
InOut parameter: data matched!
|
||||
|
||||
Data Type: varchar(max) binding as
|
||||
Output parameter: data matched!
|
||||
InOut parameter: data matched!
|
||||
|
||||
Data Type: nchar(512) binding as
|
||||
Output parameter: data matched!
|
||||
InOut parameter: data matched!
|
||||
|
||||
Data Type: nvarchar(512) binding as
|
||||
Output parameter: data matched!
|
||||
InOut parameter: data matched!
|
||||
|
||||
Data Type: nvarchar(max) binding as
|
||||
Output parameter: data matched!
|
||||
InOut parameter: data matched!
|
||||
|
||||
Data Type: varbinary(max) binding as
|
||||
Output parameter: data matched!
|
||||
InOut parameter: data matched!
|
||||
|
||||
Data Type: xml binding as
|
||||
Output parameter: data matched!
|
||||
InOut parameter: data matched!
|
||||
|
Loading…
Reference in a new issue