注意:我已将此问题缩小到专门的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服务器在 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
我也尝试过设置:
$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
中所示的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'.
是的,所有这些都是一个没有表格的简单例子,因此您可以轻松地重现它,但是为了确定,我实际上已经尝试了一个真正的表格。
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());
所有路径都会导致相同的错误:
字符串数据,右截断
答案 0 :(得分:21)
这是PDO_ODBC
64位不兼容问题(#61777,#64824),毫无疑问,您使用的是64位版本,不允许绑定参数。
It has a patch首次包含在5.6版本中:
此错误也在中引用 #61777并且仍在场 在5.5分支的最新稳定版本中。我看到两张票 已存在此问题,我只是提交这些更改 通过github提醒一下,这对任何人来说都是一个严重的问题 在x64版本上使用
PDO_ODBC
。
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位有符号整数),它被64 bits in a 64-bit ODBC application and 32 bits in a 32-bit ODBC application的新ODBC类型SQLULEN
所取代。
我认为提交者不知道colsize
数据类型,因为在下一行SQLLEN
中已正确定义。
odbc_*
功能作为工作解决方案。答案 1 :(得分:4)
这可能不是你想听到的,但这有一个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的负担你自己。这应该被认为是最后的手段!使用PDO::quote()可能有所帮助,但我也要确保您的数据也经过了彻底的验证。我知道这不是一个理想的解决方案,但是如果你必须使用MS SQL并且不能等待PHP团队修复它,那么我真的没有看到你有很多选择。
或者有选项c)使用odbc_ *函数,如果它们有效。当然,如果你想使用OOP样式,那么你必须实现你自己的类,它在OO方法中包含过程odbc函数,这样可能会有很多工作。
编辑:我发现another question on Stack Overflow提问者似乎有类似的问题。他的解决方案是放弃“官方”MS驱动程序,转而使用FreeTDS。这可能是在黑暗中拍摄的东西,但它可能值得一试。