PDO不会抛出带有未绑定参数的异常(并且查询中没有变量)

Mas*_*din 7 php postgresql pdo

所以我不知道这里发生了什么

$link = new PDO('pgsql:dbname=' . $name . ';host=' . $host, $user, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $stmt = $link->prepare("SELECT s.*, d.invalid_column FROM students s ORDER BY s.student_id");
    $stmt->execute(array(1));
}
catch (PDOException $e) {
    print $e->getMessage();
}
Run Code Online (Sandbox Code Playgroud)

当我运行这个小代码示例时,我希望抛出一个异常(因为d.invalid_column不是一个真正的列,而且我传入了无法绑定的参数),但唯一发生的是execute返回false没有其他的.此外$stmt->errorInfo()是空白的,代码00000使得很难添加一个超出通用的正常异常,除了日志之外没有别的东西可以帮助我在某些最终用户报告错误时跟踪错误.

如果我加一个'?' 在查询的某个地方d.invalid_column,即使我添加了更多不绑定到任何内容的参数,也会抛出正确的执行(这不是有效的列).

所以如何让这个查询正确错误:
1)摆脱所有参数
2)添加'?' 到查询

这只是PDO中的一个错误还是什么?

编辑:将引发异常的设置(无效列):

    $stmt = $link->prepare("SELECT s.*, d.invalid_column, ? FROM students s ORDER BY s.student_id");
    $stmt->execute(array(1));

    $stmt = $link->prepare("SELECT s.*, d.invalid_column, ? FROM students s ORDER BY s.student_id");
    $stmt->execute(array(1,2,3));

    $stmt = $link->prepare("SELECT s.*, d.invalid_column, ? FROM students s ORDER BY s.student_id");
    $stmt->execute();

    $stmt = $link->prepare("SELECT s.*, d.invalid_column FROM students s ORDER BY s.student_id");
    $stmt->execute();
Run Code Online (Sandbox Code Playgroud)

只有当我?在查询中没有并将某些execute()内容传递给那些东西时,才会默默地失败并且没有来自PDO的解释.

Dan*_*ité 6

使用当前的PHP(5.6.13)可以重现该行为,并且查询甚至不会发送到服务器.

您的案例在文档中描述为:

您不能绑定多于指定值的值; 如果input_parameters中存在的键多于PDO :: prepare()中指定的SQL,则语句将失败并发出错误.

预期值为0,给出1个值,并且语句失败,false返回.到目前为止,工作记录.

您可能会争辩说" 发出错误 "意味着当ERRMODE_EXCEPTION启用时会抛出异常.这是一个争论,但PDO开发人员不同意这一点并不明显.

更新:

为什么SQLCode不设置?

查看PDO源代码,特别static PHP_METHOD(PDOStatement, execute)是处理PDO :: execute()的源代码,您可以看到所有错误都由宏处理:PDO_HANDLE_STMT_ERR()

#define PDO_HANDLE_STMT_ERR()   if (strcmp(stmt->error_code, PDO_ERR_NONE)) { pdo_handle_error(stmt->dbh, stmt TSRMLS_CC); }
Run Code Online (Sandbox Code Playgroud)

关键是,当PDO预期为none时传递绑定参数时,查询永远不会进入SQL引擎,因此SQL引擎永远不会有机会报告错误并伴随SQLSTATE

PDO本身不创建一个假SQLSTATE在这种情况下对自己,至少没有,所以stmt->error_code停留在PDO_ERR_NONE"00000".

可以理解的是,您希望引发异常,但是您应该建议通过https://bugs.php.net

与MySQL相同吗?

是的,根行为是相同的,除了使用MySQL驱动程序,它prepare会立即发送到SQL引擎,因此如果它由于错误的列而不正确,它会提前失败并出现真正的SQL错误.另一方面,PgSQL驱动程序具有不同的实现,使其延迟服务器端prepare.PHP Postgres中详细讨论了这种特殊行为PDO驱动程序不支持预处理语句?

无论如何,这是一个MySQL的案例,演示了我的解释,即:

  • 查询需要0参数,给出1
  • $stmt->execute 返回false
  • 没有异常被提出
  • PDO :: errorCode是 00000

码:

$link = new PDO('mysql:dbname=' . $name . ';host=' . $host, $user, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $stmt = $link->prepare("SELECT 1");
    $rc=$stmt->execute(array(1));
   if ($rc===false)
    echo "query failed, errorCode=", $link->errorCode(), "\n";
   else
    echo "query succeeded, errorCode=", $link->errorCode(), "\n";
}
catch (PDOException $e) {
    print "A PDOException has occurred";
    print $e->getMessage();
}
Run Code Online (Sandbox Code Playgroud)

结果:

查询失败,errorCode = 00000

引擎盖下发生的事情prepare是发送到服务器并成功,但execute由于参数不匹配,PDO取消了该步骤.

这是一个不同的情况,即查询引用了一个不存在的列.我正在添加一个打印来显示$stmt->execute甚至没有调用,因为异常是由提出的$stmt->prepare

码:

$link = new PDO('mysql:dbname=' . $name . ';host=' . $host, $user, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    $stmt = $link->prepare("SELECT nonexisting");
    echo "Executing query\n";
    $rc=$stmt->execute(array(1));
   if ($rc===false)
    echo "query failed, errorCode=", $link->errorCode(), "\n";
   else
    echo "query succeeded, errorCode=", $link->errorCode(), "\n";
}
catch (PDOException $e) {
  print "A PDOException has occurred";
    print $e->getMessage();
}
Run Code Online (Sandbox Code Playgroud)

结果:

发生PDOExceptionSQLSTATE [42S22]:找不到列:1054'字段列表'中的未知列'不存在'

请注意"执行查询"步骤永远不会发生,因为它prepare是失败的,服务器端.

结论

  • 当查询被发送到服务器时,无论是在prepare()还是execute()中,并且它是生成错误的服务器,那么我们可以期望引发PDOException.

  • 当查询没有发送到执行步骤的服务器时,PDO execute()可能会失败(返回false)但没有抛出异常并errorCode()保持在00000