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

时间:2015-09-25 19:11:21

标签: 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();
}

当我运行这个小代码示例时,我希望抛出一个异常(因为d.invalid_column不是一个真正的列,而且我传递了无法绑定的参数),但唯一发生的事情是执行返回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();

仅当我的查询中没有?并将某些内容传递给execute()时,事情就会无声地失败并且没有来自PDO的解释。

2 个答案:

答案 0 :(得分:5)

使用当前的PHP(5.6.13)可以重现这种行为,甚至不会将查询发送到服务器。

您的案例在the doc中描述为:

  

您无法绑定超过指定值的值;如果有更多的密钥   input_parameters比在PDO :: prepare()中指定的SQL,然后   语句将失败并发出错误。

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

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

更新

为什么SQLCode未设置?

查看PDO源代码,特别是处理PDO :: execute()的static PHP_METHOD(PDOStatement, 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); }

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

PDO本身不会自行创建假SQLSTATE,在这种情况下至少没有,所以stmt->error_code保持PDO_ERR_NONE "00000"

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

与MySQL相同吗?

是的,除了使用MySQL驱动程序之外,根行为是相同的,prepare会立即发送到SQL引擎,因此如果由于列错误而导致它不正确,则它会提前失败并且一个真正的SQL错误。另一方面,PgSQL驱动程序具有不同的实现,使其延迟服务器端preparePHP Postgres PDO driver does not support prepared statement?

详细讨论了这种特殊行为

无论如何,这是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();
}

结果:

  

查询失败,errorCode = 00000

幕后发生的事情是prepare被发送到服务器并成功,但由于参数不匹配,PDO取消了execute步骤。

这里的情况与查询引用不存在的列的情况不同。我添加了一个打印件,表明甚至没有调用$stmt->execute,因为[{1}}提出了异常

代码:

$stmt->prepare

结果:

  

PDOException已发生SQLSTATE [42S22]:未找到列:1054   未知列'不存在'在'字段列表'

注意"执行查询"步骤永远不会发生,因为它是$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(); } 失败的,服务器端。

<强>结论

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

  • 当查询没有发送到服务器执行步骤时,PDO execute()可能会失败(返回false)但不抛出任何异常且prepare保持在errorCode() < / p>

答案 1 :(得分:2)

这是 PDO 中的一个错误,NikiC 最近修复了它。查看错误 #72368#79131

问题是 PDO 没有检查来自 EVT_ALLOC 的错误。这是过去几个月修复的与错误报告相关的众多问题之一。

如果PDO中的任何一个方法在异常模式下返回false而不抛出异常,那么就是一个bug。请向 https://bugs.php.net/ 报告任何未来的错误,并在可能的情况下建议 GitHub PR 来修复它。