当PDO::exec()中的任何语句失败时,是否有方法抛出异常?


Is there a way to throw exceptions when any statement fails in PDO::exec()?

PDO::exec()允许(至少对于某些驱动程序,如mysqlnd)一次执行多条语句。

这工作得很好,当我传递几个查询到PDO::exec()他们都得到执行:

$pdo->exec('DROP TABLE a; DROP TABLE b;');

我的PDO实例被配置为抛出异常:

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

如果第一个查询失败,它会像预期的那样抛出一个异常:

$pdo->exec('DROP TABLE does_not_exist; DROP TABLE ok;'); // PDOException

但是当任何后续查询失败时,它会默默地忽略这个事实,并且您似乎没有办法知道它:

$pdo->exec('DROP TABLE ok; DROP TABLE does_not_exist;'); // no exception
var_export($pdo->errorInfo()); // array (0 => '00000', 1 => NULL, 2 => NULL)

是否有任何方法配置PDO,以便exec()任何语句失败时抛出异常?

请注意,我目前没有明显更好的选择在自己的exec()调用中运行每个查询,因为我正在编写一个读取SQL转储文件的工具。

有趣的问题…我相信(如果我说错了请纠正我),这个"多重"高管会一个接一个地给每个高管打电话……因此,一旦你得到异常,它被返回,你的查询的执行被停止。

使用例子:

表a和表b
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "BEFORE:<br/>";
foreach($db->query("show tables;") as $row) echo $row[0] . "<br />";
$a = $db->exec("drop table b; drop table c");
echo "AFTER:<br/>";
foreach($db->query("show tables;") as $row) echo $row[0] . "<br />";

这里我得到结果:

BEFORE:
a
b
AFTER:
a
1              <--- ^.^

有点愚蠢。但正确的解决方案可能是使用PDO事务。如果某些代码失败,您应该能够恢复更改。基本上,如果你启动事务,你将在每次查询后关闭自动提交(最后提到的3个查询除外!!)。"还原"或"提交"将再次打开自动提交。

尝试代替$db->exec("q1;q2;第三季")……像这样:

try {
    $db->beginTransaction();
    $db->exec("drop table b;");           // -- note at the end of post!
    $db->exec("drop table c;");
    $db->commit();
} catch (PDOException $e) {
    print_r($e);
    $db->rollBack();
}

基本上这种方法是有效的。然而!

请注意,不能使用TRUNCATE TABLE,因为这个语句会像CREATE TABLE或DROP TABLE一样触发提交。

所以如果你正在处理像DROP TABLE等查询…在这种特殊情况下,正确的解决方案是使用这个查询而不是简单的drop:

SQL: DROP TABLE IF EXISTS `tablename`;

这个语句不会触发异常;)

希望有帮助^。^

如bug #61613中所述,如果任何语句失败,可能会获得异常。

解决方案是使用模拟准备(默认为on), PDOStatement::nextRowset():

$pdo = new PDO(...);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// on by default, not necessary unless you need to override a previous configuration
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
try {
    $statement = $pdo->query('DELETE FROM unknown_a; DELETE FROM unknown_b;');
    // loop through all the statements
    while ($statement->nextRowset());
} catch (PDOException $e) {
    echo $e->getMessage(), PHP_EOL;
}

如果第一条语句失败,query()将抛出异常。

如果第一条语句成功,第二条语句失败,query()将正常工作,nextRowset()将抛出异常。

警告:在一个失败的语句之后不会再执行其他语句。假设您有一个包含4条语句的SQL字符串,第三条语句失败:

  • 第一条语句:query()将成功
  • 第二条语句:nextRowset() will succeed and return true
  • 第三条语句:nextRowset()将失败,出现异常
  • 第4条语句:nextRowset()将返回false;语句未执行

如果你使用上面的代码,它会在第一个异常时停止。