'PDOException' 消息 'SQLSTATE[22001]:字符串数据,右截断:0
'PDOException' with message 'SQLSTATE[22001]: String data, right truncated: 0
注意:我已经将这个问题缩小到特定的 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());
Array ( [col] => value [0] => value )
无效:
$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['this'];
$stmt->execute($params);
print_r($stmt->fetch());
Web 服务器是 运行 PHP 5.5.9 on Linux Ubuntu 14.04 with ODBC Driver 13 for SQL 服务器并连接到 Microsoft SQL Server 2012 Windows 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
我也试过设置:
$pdoDB->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );
并使用命名参数:
$sql = "SELECT 'value' AS col where 'this' = :myVal";
$stmt = $pdoDB->prepare($sql);
$params = ['myVal' => 'this'];
$stmt->execute($params);
print_r($stmt->fetch());
即使有明确的冒号:
$params = [':myVal' => 'this'];
我也试过只使用 bindParam
as demonstrated in this answer:
$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$param = 'this';
$stmt->bindParam(1, $param);
$stmt->execute();
print_r($stmt->fetch());
以及命名参数:
$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());
如果我尝试明确设置长度:
$stmt->bindParam(':myVal', $param, PDO::PARAM_STR, 4);
我得到一个奖励错误:
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'.
是的,所有这些都是一个没有 tables 的简单示例,因此您可以轻松地重现它,但可以肯定的是,我实际上已经用真实的 table 尝试过这个。
CREATE TABLE myTable (
id INT IDENTITY PRIMARY KEY,
val NVARCHAR(255)
);
INSERT INTO myTable (val) VALUES ('hello world');
作品:
$sql = "SELECT * FROM myTable WHERE val = 'hello world'";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);
print_r($stmt->fetch());
Array ( [id] => 1 [0] => 1 [val] => hello world [1] => hello world )
无效:
$sql = "SELECT * FROM myTable WHERE val = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['hello world'];
$stmt->execute($params);
print_r($stmt->fetch());
所有路径都导致相同的错误:
String data, right truncated
这可能不是您想听到的,但这具有 PHP 用于 PDO 的 ODBC 驱动程序中的错误的所有特征(它没有被大量使用,因为 PHP 程序员倾向于支持像 MySQL/SQLite/Postgres 这样的开源数据库而不是商业产品),或者在底层 SQL 服务器驱动程序中(由于类似的原因,Linux 对其支持不佳),但如果 odbc_* 可以工作它可能不是底层驱动程序。
如果您尝试执行完全相同的任务,除了使用 "sqlite::memory:"
作为 DSN,您的所有示例都有效。这使得您不太可能做错任何事(除非 MS Server 有一些我不知道的非常奇怪的不符合 SQL 语法)。当启用和禁用 ATTR_EMULATE_PREPARES
时,您的示例对 SQLite 很适用。
我认为您实际上可以做的就是 file a bug report 并希望有人能接受它。不过,您可能要等很长时间。
至于您的问题的实际解决方案,您的选择是 a) 切换到 PHP 支持的 DBMS 或 b) 求助于 SQL 字符串构造而不是准备好的语句并准备好接受避免 SQL injection attacks yourself. This should be considered a last resort though! Using PDO::quote() 的负担可能会有所帮助,但我也会确保您的数据也经过非常彻底的验证。我知道这不是一个理想的解决方案,但如果您必须使用 MS SQL 并且不能等待 PHP 团队修复它,那么我真的看不出您有多少选择。
或者有选项 c),它使用 odbc_* 函数代替,如果它们有效的话。当然,如果您想使用 OOP 样式,那么您必须实现自己的 class,将过程性 odbc 函数包装在 OO 方法中,这样可能需要大量工作。
编辑:我发现 another question on Stack Overflow 提问者似乎有类似的问题。他的解决方案是放弃 "official" MS 驱动程序,转而使用 FreeTDS。这可能是在黑暗中尝试,但值得一试。
不幸的是,
这是一个 PDO_ODBC
64 位不兼容问题 (#61777, #64824),毫无疑问,您使用的是不允许绑定参数的 64 位版本。
幸运的是,
It has a patch 首次包含在 5.6 版本中:
This bug is also referenced in
#61777 and is still present
in the latest stable release of the 5.5 branch. I see two tickets
exist for this problem already, and I'm just submitting these changes
via github as a reminder that this is a serious problem for anyone
using PDO_ODBC
on the x64 builds.
你的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;
我们看到唯一改变的是 SDWORD
(16 位有符号整数),它被新的 ODBC 类型 SQLULEN
取代,即 64 bits in a 64-bit ODBC application and 32 bits in a 32-bit ODBC application.
我相信提交者不知道 colsize
数据类型只是因为在下一行 SQLLEN
被正确定义。
我现在该怎么办?
- 升级到 PHP 版本 >= 5.6
- 坚持使用
odbc_*
函数作为可行的解决方案。
- 使用提供的补丁编译 PHP v5.5.9。
- 按照@GordonM 的建议构建您自己的 PDO 包装器
注意:我已经将这个问题缩小到特定的 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());
Array ( [col] => value [0] => value )
无效:
$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['this'];
$stmt->execute($params);
print_r($stmt->fetch());
Web 服务器是 运行 PHP 5.5.9 on Linux Ubuntu 14.04 with ODBC Driver 13 for SQL 服务器并连接到 Microsoft SQL Server 2012 Windows 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
我也试过设置:
$pdoDB->setAttribute( PDO::ATTR_EMULATE_PREPARES, true );
并使用命名参数:
$sql = "SELECT 'value' AS col where 'this' = :myVal";
$stmt = $pdoDB->prepare($sql);
$params = ['myVal' => 'this'];
$stmt->execute($params);
print_r($stmt->fetch());
即使有明确的冒号:
$params = [':myVal' => 'this'];
我也试过只使用 bindParam
as demonstrated in this answer:
$sql = "SELECT 'value' AS col where 'this' = ?";
$stmt = $pdoDB->prepare($sql);
$param = 'this';
$stmt->bindParam(1, $param);
$stmt->execute();
print_r($stmt->fetch());
以及命名参数:
$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());
如果我尝试明确设置长度:
$stmt->bindParam(':myVal', $param, PDO::PARAM_STR, 4);
我得到一个奖励错误:
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'.
是的,所有这些都是一个没有 tables 的简单示例,因此您可以轻松地重现它,但可以肯定的是,我实际上已经用真实的 table 尝试过这个。
CREATE TABLE myTable (
id INT IDENTITY PRIMARY KEY,
val NVARCHAR(255)
);
INSERT INTO myTable (val) VALUES ('hello world');
作品:
$sql = "SELECT * FROM myTable WHERE val = 'hello world'";
$stmt = $pdoDB->prepare($sql);
$params = [];
$stmt->execute($params);
print_r($stmt->fetch());
Array ( [id] => 1 [0] => 1 [val] => hello world [1] => hello world )
无效:
$sql = "SELECT * FROM myTable WHERE val = ?";
$stmt = $pdoDB->prepare($sql);
$params = ['hello world'];
$stmt->execute($params);
print_r($stmt->fetch());
所有路径都导致相同的错误:
String data, right truncated
这可能不是您想听到的,但这具有 PHP 用于 PDO 的 ODBC 驱动程序中的错误的所有特征(它没有被大量使用,因为 PHP 程序员倾向于支持像 MySQL/SQLite/Postgres 这样的开源数据库而不是商业产品),或者在底层 SQL 服务器驱动程序中(由于类似的原因,Linux 对其支持不佳),但如果 odbc_* 可以工作它可能不是底层驱动程序。
如果您尝试执行完全相同的任务,除了使用 "sqlite::memory:"
作为 DSN,您的所有示例都有效。这使得您不太可能做错任何事(除非 MS Server 有一些我不知道的非常奇怪的不符合 SQL 语法)。当启用和禁用 ATTR_EMULATE_PREPARES
时,您的示例对 SQLite 很适用。
我认为您实际上可以做的就是 file a bug report 并希望有人能接受它。不过,您可能要等很长时间。
至于您的问题的实际解决方案,您的选择是 a) 切换到 PHP 支持的 DBMS 或 b) 求助于 SQL 字符串构造而不是准备好的语句并准备好接受避免 SQL injection attacks yourself. This should be considered a last resort though! Using PDO::quote() 的负担可能会有所帮助,但我也会确保您的数据也经过非常彻底的验证。我知道这不是一个理想的解决方案,但如果您必须使用 MS SQL 并且不能等待 PHP 团队修复它,那么我真的看不出您有多少选择。
或者有选项 c),它使用 odbc_* 函数代替,如果它们有效的话。当然,如果您想使用 OOP 样式,那么您必须实现自己的 class,将过程性 odbc 函数包装在 OO 方法中,这样可能需要大量工作。
编辑:我发现 another question on Stack Overflow 提问者似乎有类似的问题。他的解决方案是放弃 "official" MS 驱动程序,转而使用 FreeTDS。这可能是在黑暗中尝试,但值得一试。
不幸的是,
这是一个 PDO_ODBC
64 位不兼容问题 (#61777, #64824),毫无疑问,您使用的是不允许绑定参数的 64 位版本。
幸运的是,
It has a patch 首次包含在 5.6 版本中:
This bug is also referenced in #61777 and is still present in the latest stable release of the 5.5 branch. I see two tickets exist for this problem already, and I'm just submitting these changes via github as a reminder that this is a serious problem for anyone using
PDO_ODBC
on the x64 builds.
你的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;
我们看到唯一改变的是 SDWORD
(16 位有符号整数),它被新的 ODBC 类型 SQLULEN
取代,即 64 bits in a 64-bit ODBC application and 32 bits in a 32-bit ODBC application.
我相信提交者不知道 colsize
数据类型只是因为在下一行 SQLLEN
被正确定义。
我现在该怎么办?
- 升级到 PHP 版本 >= 5.6
- 坚持使用
odbc_*
函数作为可行的解决方案。 - 使用提供的补丁编译 PHP v5.5.9。
- 按照@GordonM 的建议构建您自己的 PDO 包装器