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

时间:2014-07-15 22:04:26

标签: php pdo

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,以便在{em>任何语句失败时exec()抛出异常?

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

2 个答案:

答案 0 :(得分:1)

有趣的问题......我相信(如果我错了,请纠正我)这个"倍数" exec会在每个exec之后调用每个exec ...所以一旦你得到异常就会返回它,并且你的查询的执行就会停止。

用例: (dbname)测试包含2个表' 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-&gt; exec(&#34; q1; q2; q3&#34;)......这样的事情:

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`;

此声明不会触发异常;)

希望它有所帮助^。^

答案 1 :(得分:0)

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

解决方案是使用模拟的准备工作(默认情况下处于启用状态)和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()将成功并返回true
  • 第三条语句:nextRowset()会因异常而失败
  • 第4条语句:nextRowset()将返回false该语句未执行

如果您使用的是上面的代码,则无论如何它都会在遇到第一次异常时停止。