Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

String truncation when binding varchar(max) #231

Closed
david-puglielli opened this issue Jan 13, 2017 · 4 comments

Comments

5 participants
@david-puglielli
Copy link
Member

commented Jan 13, 2017

When binding output parameters as varchar(max) or nvarchar(max), a string truncation error message may result. This does not happen if the argument is not 'max'. This problem is seen on both Windows and Linux.

<?php 
$server = "myserver";
$connectionInfo = array( "Database"=>"$databaseName", "UID"=>"$uid", "PWD"=>"$pwd");

$tableName = "datatypes";
$columnNames = array( "c1","c2" );

$conn = sqlsrv_connect($server, $connectionInfo);

for ($k = 1; $k <= 6; $k++)
{
    $sqlType = GetSqlType($k);
    $dataType = "[$columnNames[0]] int, [$columnNames[1]] $sqlType";
    
    $sql = "CREATE TABLE [$tableName] ($dataType)";
    $stmt1 = sqlsrv_query($conn, $sql);
    
    $data = "LongStringForTesting";
    
    $dataValues = array($k, $data);
    
    $tsql = "INSERT INTO [$tableName] ($columnNames[0], $columnNames[1]) VALUES (?, ?)";    
    $stmt2 = sqlsrv_query( $conn, $tsql, $dataValues );

    ExecProc($conn, $tableName, $columnNames, $k, $data, $sqlType);
    
    $stmt3 = sqlsrv_query($conn, "DROP TABLE [$tableName]");
    sqlsrv_free_stmt($stmt1);
    sqlsrv_free_stmt($stmt2);
    sqlsrv_free_stmt($stmt3);
}

sqlsrv_close($conn);


function ExecProc($conn, $tableName, $columnNames, $k, $data, $sqlType)
{
    $phpDriverType = GetDriverType($k, strlen($data));
    
    $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);
    
    $callArgs = "?, ?";
    
    // Data to initialize $callResult variable. This variable should be different than inserted data in the table
    $initData = "ShortString";
    $callResult = $initData;
    
    $params = array( array( $k, SQLSRV_PARAM_IN ), 
                     array( &$callResult, SQLSRV_PARAM_OUT, null, $phpDriverType ));

    echo "\nData Type: ".$sqlType."\n";
    echo "Table data:          ".$data."\nInitial data:        ".$initData."\nInitial call result: ".$callResult."\n";
    
    $stmt2 = sqlsrv_query($conn, "{ CALL [$procName] ($callArgs)}", $params);
    
    // $callResult should be updated to the value in the table
    echo "New call result:     ".$callResult."\n\n";
    
    if($stmt2 === false)
    {
        var_dump( sqlsrv_errors());
    }
    else
    {
        sqlsrv_free_stmt($stmt2);
    }
    
    $stmt3 = sqlsrv_query($conn, "DROP PROC [$procName]");
    sqlsrv_free_stmt($stmt3);
}

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)");
        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'));
        default: break;
    }
    return (SQLSRV_SQLTYPE_UDT);
}
?>

The expected output is

Data Type: char(512)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting


Data Type: varchar(512)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting


Data Type: varchar(max)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting

Data Type: nchar(512)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting


Data Type: nvarchar(512)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting


Data Type: nvarchar(max)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting

The actual output:

Data Type: char(512)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting


Data Type: varchar(512)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting


Data Type: varchar(max)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringF

array(1) {
  [0]=>
  array(6) {
    [0]=>
    string(5) "01004"
    ["SQLSTATE"]=>
    string(5) "01004"
    [1]=>
    int(0)
    ["code"]=>
    int(0)
    [2]=>
    string(71) "[Microsoft][ODBC Driver 13 for SQL Server]String data, right truncation"
    ["message"]=>
    string(71) "[Microsoft][ODBC Driver 13 for SQL Server]String data, right truncation"
  }
}

Data Type: nchar(512)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting


Data Type: nvarchar(512)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringForTesting


Data Type: nvarchar(max)
Table data:          LongStringForTesting
Initial data:        ShortString
Initial call result: ShortString
New call result:     LongStringF

array(1) {
  [0]=>
  array(6) {
    [0]=>
    string(5) "01004"
    ["SQLSTATE"]=>
    string(5) "01004"
    [1]=>
    int(0)
    ["code"]=>
    int(0)
    [2]=>
    string(71) "[Microsoft][ODBC Driver 13 for SQL Server]String data, right truncation"
    ["message"]=>
    string(71) "[Microsoft][ODBC Driver 13 for SQL Server]String data, right truncation"
  }
}

@david-puglielli david-puglielli added the bug label Jan 13, 2017

@Hadis-Fard Hadis-Fard added the SQLSRV label Jan 14, 2017

@yitam yitam self-assigned this Feb 15, 2017

yitam added a commit to yitam/msphpsql that referenced this issue Feb 16, 2017

@yitam yitam added this to Need Review in msphpsql Feb 20, 2017

v-dareck added a commit that referenced this issue Feb 22, 2017

Merge pull request #293 from yitam/GH231
For issue #231, force column size to be determined

@yitam yitam moved this from Need Review to Completed in msphpsql Feb 27, 2017

@yitam yitam added the Resolved label Feb 27, 2017

@yitam yitam closed this Feb 27, 2017

@yitam yitam reopened this Apr 18, 2017

@yitam

This comment has been minimized.

Copy link
Member

commented Apr 18, 2017

Using a similar repro script, with text type the script failed with an ODBC error as follows:

[Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Invalid parameter 2 (''): Data type 0x23 is a deprecated large object, or LOB, but is marked as output parameter. Deprecated types are not supported as output parameters. Use current large object types instead.

Similar case with ntext type

To repro, just use the followings for the above scenario:

function GetSqlType($k)
{
    switch ($k)
    {
        case 1:  return ("text");
        case 2:  return ("ntext");
        default: break;
    }
    return ("udt");
}

function GetDriverType($k, $dataSize)
{
    switch ($k)
    {
        case 1:  return (SQLSRV_SQLTYPE_TEXT);
        case 2:  return (SQLSRV_SQLTYPE_NTEXT);

        default: break;
    }
    return (SQLSRV_SQLTYPE_UDT);
}

@v-kaywon v-kaywon removed the Resolved label Apr 19, 2017

@Hadis-Fard

This comment has been minimized.

Copy link

commented May 17, 2017

@yitam does this issue sill exist?

@Hadis-Fard Hadis-Fard moved this from Completed to Backlog in msphpsql May 17, 2017

@shemi

This comment has been minimized.

Copy link

commented Sep 24, 2017

There is a solution to this problem?
There is a workaround?

@yitam

This comment has been minimized.

Copy link
Member

commented Sep 25, 2017

Hello @shemi
There are actually two problems that are somewhat related, so which one did you refer to? If it's the original issue, it has been resolved. If you currently experience a similar problem, please create a new issue with your repro steps / scripts / env and link your new issue with this one. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.