4

I am using PHP 5.3.3 on a CentOS 6.2 box, connecting to an instance of Microsoft SQL Server 2008R2. The connection works, and I am able to retrieve data, so long as my queries contain no parameters. When I add parameters, I get the error, "String data, right truncation".

Here's some example code:

<?php

$dbh = new PDO("odbc:myDSN", 'myUsername', 'myPassword');

$testCase = 1;
switch ($testCase) {
case 1:
  //  This case fails with this error:
  //    Error 22001: [Microsoft][ODBC Driver 11 for SQL Server]String data, right truncation (SQLExecute[0] at /builddir/build/BUILD/php-5.3.3/ext/pdo_odbc/odbc_stmt.c:254)
  $query = "select * from [myDatabase].[sys].[objects] WHERE (([name]=?))";
  $stmt = $dbh->prepare($query);
  $param1 = 'testtable1';
  $stmt->bindParam(1, $param1, PDO::PARAM_STR);   //  Note:  '1' is correct; it should not be '0'
  break;
case 2:
  //  This case works properly
  $query = "select * from [myDatabase].[sys].[objects] WHERE (([name]='testtable1'))";
  $stmt = $dbh->prepare($query);
  break;
}
$execResult = $stmt->execute();
if ($execResult) {
  print "Success!\n";
} else {
  $errorInfo = $stmt->errorInfo();
  print "Error " . $stmt->errorCode() . ": " . $errorInfo[2] . "\n";
}

$rowCount = 0;
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
  echo "Row " . $rowCount . ":\n";
  foreach ($row as $key => $value) {
    printf("  %-20s  %s\n", $key, $value);
  }
  $rowCount++;
}

Note that both test cases in the code above should do the same thing. Test case 1 uses parameters (as all code should), and test case 2 explicitly puts the relevant value in the SQL query. Test case 2 works. Test case 1 does not. I have tried replacing 'bindParam()' with 'bindValue()', but this has no effect. I've also tried using named parameters (e.g., :name) instead of positional parameters, but this also has no effect. I've tried adding an explicit length argument to bindParam() (using strlen($param1) as a value), but that gives a really bizarre error message (Incorrect syntax near 'OUTPUT'), and I can only assume that I'm doing it wrong. Integer parameters work properly. Only string parameters fail.

Any ideas why this isn't working?

Of course it's possible that there's a bug in the ODBC driver, or that it's not compatible with my version of PHP, or any number of similar problems, but I hope that I'm simply using the API improperly.

Edit:

Per Anda Iancu's suggestion, I delved into SQL Server Profiler. When traced, case 1 gives two nearly-identical records, one of class SQL:BatchStarting, and one of class SQL:BatchCompleted, both containing the text:

set fmtonly on select [name] from [myDatabase].[sys].[objects] where 1=2 set fmtonly off

Case 2 gives two records, both of class "RPC:Completed". The first contains the text:

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,NULL,N'select * from [myDatabase].[sys].[objects] WHERE (([name]=''testtable1''))'
select @p1

and the second contains the text:

exec sp_unprepare 1

Update:

In a desperate move, hoping there might be some kind of problem with dropping a new version of unixODBC into an existing version of PHP, I recompiled PHP from source. This turns out to be harder than you might think, on CentOS. Unfortunately, this had no effect. Same errors all around.

Rick Koshi
  • 945
  • 1
  • 11
  • 21
  • can you display (get the query sent) the query for case 1? – Anda Iancu Jan 31 '13 at 14:55
  • @Anda: I don't understand your question. The query for case 1 gives me an error, as noted above and in the code. I'm not familiar with MSSQL (this is the only time I've had to use it), so I don't know if there's a way to extract the query it received. If so, that would be helpful. Can you tell me how? – Rick Koshi Jan 31 '13 at 15:06
  • do you have SQL profiler? Tools > Sql Server Profiler - you can get the exact query statement sent; and try to run that query in a new query window ( is just a select -select * from [myDatabase].[sys].[objects] WHERE (([name]='testtable1')). steps are: open profiler and connect to your db, then run the code. – Anda Iancu Jan 31 '13 at 15:17
  • just try to check the query sent is correct – Anda Iancu Jan 31 '13 at 15:18
  • If I'm reading this correctly, SQL Server Profiler says that the query is `set fmtonly on select [name] from [myDatabase].[sys].[objects] where 1=2 set fmtonly off`, which just doesn't make any sense to me. Also, the record structure is very different from that produced by case 2. Case two gives something vaguely reasonable. – Rick Koshi Jan 31 '13 at 15:24
  • and if you run for case 2? – Anda Iancu Jan 31 '13 at 15:26
  • case 2 gives the query as in the code. The series of records in Profiler is very different. Case 1 gives just a "SQL:BatchStarting" and "SQL:BatchEnding" record, and case 2 gives two "RPC:Completed" records. The text inside of case 1 is exactly what I said above, and the text for case 2 includes some manipulation of a temporary variable, @p1 – Rick Koshi Jan 31 '13 at 15:28
  • I've added the SQL Server Profiler information to the question's text above. – Rick Koshi Jan 31 '13 at 15:35
  • and if you try: $query = "select * from [myDatabase].[sys].[objects] WHERE id = ( select id from [myDatabase].[sys].[objects] WHERE (([name]=?)) )"; – Anda Iancu Jan 31 '13 at 15:35
  • Oddly, when I try that, I get the same error message, but the trace no longer shows anything about the query. It still shows the "Audit Login" and "Audit Logout" records that I didn't bother mentioning before, but there are no SQL: or RPC: records of any kind. – Rick Koshi Jan 31 '13 at 15:42
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/23719/discussion-between-anda-iancu-and-rick-koshi) – Anda Iancu Jan 31 '13 at 15:43
  • What do you get if you turn on ODBC logging? /etc/odbcinst.ini [ODBC] Trace = yes TraceFile = /tmp/odbc.log – Benny Hill Feb 01 '13 at 15:39
  • Possible duplicate of ['PDOException' with message 'SQLSTATE\[22001\]: String data, right truncated: 0](http://stackoverflow.com/questions/38255659/pdoexception-with-message-sqlstate22001-string-data-right-truncated-0) – Jeff Puckett Jul 14 '16 at 14:37

2 Answers2

7

After much tweaking and searching, and a whole lot of shot-in-the-dark troubleshooting, I finally decided that this is an ODBC driver problem.

Specifically, I was using a driver downloaded from Microsoft, supposedly designed to work with PHP and unixODBC on RHEL/CentOS6. It's known as "Microsoft ODBC Driver 11 for SQL Server" in its own README file, and comes in a file called msodbcsql-11.0.2270.0.tar.gz. (These details provided for the benefit of anyone else trying to do the same thing)

In light of my experience, I do not recommend this driver.

I downloaded, compiled, and installed the latest "stable" version of FreeTDS instead. If it matters to you, the version I got is 0.91 (the download file doesn't say this, but it unpacks into a directory with this number). This had/has its own minor configuration problems, but ultimately seems to be working much better than the Microsoft-provided driver. I don't know if this is still being actively maintained, as the most recent timestamps in the distribution were August 17, 2011.

Silly me, thinking that I should use the Microsoft driver to access a Microsoft database server, and expect it to actually do what it says it will do.

Rick Koshi
  • 945
  • 1
  • 11
  • 21
  • The Microsoft driver works fine for me. The problem is definitely with the pdo_odbc as referenced in [David's answer](http://stackoverflow.com/a/21943836/4233593). This was patched in PHP 5.6, so the Microsoft driver is not the issue (tested works with 11 and 13). Moreover, you can verify the driver works fine when using the [odbc_* functions](http://php.net/manual/en/book.uodbc.php). I had [the same problem](http://stackoverflow.com/q/38255659/4233593) with PHP 5.5.9. – Jeff Puckett Jul 14 '16 at 14:35
  • 1
    You can also fix this problem using Microsoft's driver just by adding 'CharacterSet' => "UTF-8" to the connection settings. As stated here: http://php.net/manual/es/function.sqlsrv-connect.php – Marco Mar 06 '19 at 13:53
  • @Marco's solution is the way to fix this. Worked for me. Thanks! – schmoove Apr 03 '20 at 16:31
2

I think you have probably encountered a bug in Microsoft's ODBC driver, but it is worth pointing out that pdo_odbc has a critical bug related to the bindValue() function. The bug only affects 64-bit but - ha! - Microsoft's ODBC driver is only 64-bit.

It's been a while, but I think which bug you hit depends on your data types and the SQL statement.

David Sickmiller
  • 1,145
  • 8
  • 8