带有消息'SQLSTATE [22001]的'PDOException':字符串数据,右截断:0

Jef*_*ett 24 php sql-server odbc pdo prepared-statement

注意:我已将此问题缩小到专门的PDO,因为我能够使用odbc_*函数成功准备和执行语句.

为什么我不能将此参数绑定到PDO预处理语句?

这有效:

$mssqldriver = 'ODBC Driver 13 for SQL Server';
$pdoDB = new PDO("odbc:Driver=$mssqldriver;Server=$hostname;Database=$dbname", $username, $password);
$pdoDB->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

$sql = "SELECT 'value' AS col where 'this' = 'this'";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);
print_r($stmt->fetch());
Run Code Online (Sandbox Code Playgroud)
Array ( [col] => value [0] => value )
Run Code Online (Sandbox Code Playgroud)

不起作用:

$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['this'];
$stmt->execute($params);
print_r($stmt->fetch());
Run Code Online (Sandbox Code Playgroud)

Web服务器在Linux Ubuntu 14.04上运行PHP 5.5.9,带有用于SQL Server的ODBC驱动程序13,并在Windows Server 2012上连接到Microsoft SQL Server 2012

这是完整的错误:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[22001]:
String data, right truncated: 0
[Microsoft][ODBC Driver 13 for SQL Server]
String data, right truncation
(SQLExecute[0] at /build/buildd/php5-5.5.9+dfsg/ext/pdo_odbc/odbc_stmt.c:254)' in /var/www/scratch.php:46
Stack trace:
#0 /var/www/scratch.php(46): PDOStatement->execute(Array)
#1 {main} thrown in /var/www/scratch.php on line 46
Run Code Online (Sandbox Code Playgroud)

我也尝试过设置:

$pdoDB->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );
Run Code Online (Sandbox Code Playgroud)

并使用命名参数:

$sql = "SELECT 'value' AS col where 'this' = :myVal";
$stmt = $pdoDB->prepare($sql);
$params = ['myVal' => 'this'];
$stmt->execute($params);
print_r($stmt->fetch());
Run Code Online (Sandbox Code Playgroud)

即使有明确的冒号:

$params = [':myVal' => 'this'];
Run Code Online (Sandbox Code Playgroud)

我也尝试只是用bindParam作为证明这个答案:

$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$param = 'this';
$stmt->bindParam(1, $param);
$stmt->execute();
print_r($stmt->fetch());
Run Code Online (Sandbox Code Playgroud)

与命名参数一样:

$sql = "SELECT 'value' AS col where 'this' = :myVal";
$stmt = $pdoDB->prepare($sql);
$param = 'this';
$stmt->bindParam(':myVal', $param, PDO::PARAM_STR);
$stmt->execute();
print_r($stmt->fetch());
Run Code Online (Sandbox Code Playgroud)

如果我尝试明确设置长度:

$stmt->bindParam(':myVal', $param, PDO::PARAM_STR, 4);
Run Code Online (Sandbox Code Playgroud)

我收到奖金错误:

Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[42000]: Syntax error or access violation: 102
[Microsoft][ODBC Driver 13 for SQL Server][SQL Server]
Incorrect syntax near 'OUTPUT'.
Run Code Online (Sandbox Code Playgroud)

是的,所有这些都是一个没有表格的简单例子,因此您可以轻松地重现它,但是为了确定,我实际上已经尝试了一个真正的表格.

CREATE TABLE myTable (
    id INT IDENTITY PRIMARY KEY,
    val NVARCHAR(255)
);
INSERT INTO myTable (val) VALUES ('hello world');
Run Code Online (Sandbox Code Playgroud)

作品:

$sql = "SELECT * FROM myTable WHERE val = 'hello world'";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);
print_r($stmt->fetch());
Run Code Online (Sandbox Code Playgroud)
Array ( [id] => 1 [0] => 1 [val] => hello world [1] => hello world )
Run Code Online (Sandbox Code Playgroud)

不起作用:

$sql = "SELECT * FROM myTable WHERE val = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['hello world'];
$stmt->execute($params);
print_r($stmt->fetch());
Run Code Online (Sandbox Code Playgroud)

所有路径都会导致相同的错误:

字符串数据,右截断

rev*_*evo 22

不幸,

这是一个PDO_ODBC64位不兼容问题(#61777,#64824),毫无疑问,你使用的是64位版本,它不允许你绑定参数.

幸好,

它有一个补丁,最初包含在5.6版本中:

此错误也在#61777中引用, 并且仍然存在于5.5分支的最新稳定版本中.我已经看到这个问题已经存在两张票了,我只是通过github提交这些更改作为提醒,这对任何使用PDO_ODBCx64版本的人来说都是一个严重的问题.

您的PHP出货有PDO_ODBC什么问题?

通过查看其中一个推荐的补丁:

diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c
index 8b0ccf3..1d275cd 100644
--- a/ext/pdo_odbc/odbc_stmt.c
+++ b/ext/pdo_odbc/odbc_stmt.c
@@ -551,7 +551,7 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
    struct pdo_column_data *col = &stmt->columns[colno];
    RETCODE rc;
    SWORD   colnamelen;
-   SDWORD  colsize;
+   SQLULEN colsize;
    SQLLEN displaysize;
Run Code Online (Sandbox Code Playgroud)

我们看到,已经改变的唯一事情是SDWORD取代有新的ODBC类型(16位带符号整数)SQLULEN在64位ODBC应用64个比特,并在一个32位ODBC应用程序32位.

我认为提交者不知道colsize数据类型,因为在下一行SQLLEN中定义得当.

我现在应该怎么做?

  1. 升级到PHP版本> = 5.6
  2. 坚持使用odbc_*功能作为工作解决方案.
  3. 使用提供的补丁编译PHP v5.5.9.
  4. 按照@GordonM的建议构建自己的PDO包装器