【问题标题】:Import from mysqldump not working as expected从 mysqldump 导入未按预期工作
【发布时间】:2013-05-03 11:27:47
【问题描述】:

我有一个从 mysqldump 生成的 sql 转储。该文件包括特定于 mysql 版本的 cmets (/*!{MySQL version number} {Code} */)。 如果我在这个块之后插入一个 sql 语法错误,PDO 不会触发异常。

php代码

$sql = file_get_contents('FooBar.sql');
$pdo = new \PDO('mysql:host=localhost;dbname=FooBar', 'root');
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$pdo->exec($sql);

FooBar.sql

/*!40101 SET @Foo='Bar' */;
ERROR
INSERT INTO Foo VALUES ('Bar');

执行此操作不会导致任何异常或错误。如果我删除 /*!40101 SET @Foo='Bar' */; 语句,或者将错误移到队列上,则会引发 PDOException。

【问题讨论】:

    标签: php mysql pdo doctrine-orm doctrine


    【解决方案1】:

    感谢 hek2mgl 让我走上了正确的道路。

    PDO 不支持多个查询。如果您执行包含多个查询的语句,它们会被执行,但 PDO 似乎在执行第一个查询后停止运行。 /*!{MySQL version number} {Code} */ 样式的注释被 MySql 作为常规查询执行,之后的任何内容都被 PDO 忽略,即使它被 MySql 执行。

    以下查询将触发相同的错误:

    SET @Foo='Bar';
    ERROR
    INSERT INTO Foo VALUES ('Bar');
    

    要使用 PDO 完成这项工作,我需要拆分语句。

    $sql = file_get_contents('FooBar.sql');
    $lines = explode(';', $sql);
    $pdo = new \PDO('mysql:host=localhost;dbname=FooBar', 'root');
    $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    foreach ($lines as $line) {
        $trimmedLine = trim($line, " \t\n\r\0\x0B");
        if (strlen($trimmedLine) > 0) {
            $pdo->exec($trimmedLine.';');
        }
    }
    

    编辑:

    另一种解决方案是使用 pdo 准备好的语句。

    $sql = file_get_contents('FooBar.sql');
    $pdo = new \PDO('mysql:host=localhost;dbname=FooBar', 'root');
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    do {
        ... Do stuff ...
    } while ($stmt->nextRowset());
    if ($stmt->errorCode() != '00000') {
        ... Handle error ...
    }
    

    【讨论】:

    • 这不正确。当然 PDO 支持多个查询。试试SELECT 1 AS num; SELECT 1 AS num;
    • 如果在查询之间插入错误,它会被 PDO 吞并,剩余的语句将不会被执行。如果我在单个查询中尝试多个语句,则只会返回来自第一个查询的数据(错误代码、受影响的行等)。
    • 看来你是对的。 PDO 允许多次查询,但无法正确处理错误,或者更确切地说,无法将错误报告给客户端。我已经更新了我的帖子。你可以使用Mysqli
    【解决方案2】:

    这是因为评论末尾的;。我现在不知道为什么。将进一步调查...

    找到这个bug report。但是不要指望 char 编码会成为问题,因为我已经使用 wireshark 调查了网络流量,并且 MySQL 返回了一个语法错误(如预期的那样)。仍然不知道为什么 PDO 不能正确处理这个问题。


    一种解决方法是使用Mysqli,它似乎可以正确处理这个问题。以下示例演示了这一点:

    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'secret');
    
    $result = $pdo->query('
    SELECT 1 AS num;
    ERROR
    SELECT 1 AS num;
    ');
    
    if(!$result) {
        var_dump($pdo->errorInfo); // silence ...
    }
    
    $mysqli = new Mysqli('localhost', 'root', 'user', 'secret');
    $result = $mysqli->query('
    SELECT 1 AS num;
    ERROR
    SELECT 1 AS num;
    ');
    
    if(!$result) {
        print( $mysqli->error);
        // Output: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ERROR SELECT 1 AS num' at line 2
    }
    

    【讨论】:

    • 你必须使用mysqli->multi_query 否则第二个语句会失败。但是你必须迭代所有行集来检测错误。
    • 可能是,将验证这一点。顺便说一句,为什么不使用LOAD DATA INFILE
    • 在我的项目中,数据库可能仍然会发生变化。我想尽量减少重复的配置和特定的数据库依赖。 PDO 连接是从框架(Symfony/Doctrine)生成的。我的数据来自一个 mysqldump,包括几个表。如果我现在更改数据库,我只需要更改用于生成 sqldump 的 shell 脚本。
    • 好的,明白了。虽然您的答案可能对您来说是一个可行的解决方案(+1),但我仍然认为这是一个错误。您应该考虑提交它或扩展现有的错误报告。 (非常有趣的问题,谢谢;)我将添加doctrine 标签。也许我们会得到更多的答案……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-18
    • 2023-03-21
    • 1970-01-01
    • 2016-05-20
    • 1970-01-01
    • 2018-06-19
    相关资源
    最近更新 更多