PDO 支持多个查询(PDO_MYSQL、PDO_MYSQLND)

2022-08-30 07:26:43

我知道PDO不支持在一个语句中执行多个查询。我一直在谷歌上搜索,发现很少有帖子谈论PDO_MYSQL和PDO_MYSQLND。

PDO_MySQL是一个比任何其他传统MySQL应用程序更危险的应用程序。传统的MySQL只允许一个SQL查询。在PDO_MySQL中没有这样的限制,但是您可能会被注入多个查询。

来自:使用PDO和Zend Framework防止SQL注入(2010年6月;由Julian撰写)

似乎PDO_MYSQL和PDO_MYSQLND确实为多个查询提供支持,但我无法找到有关它们的更多信息。这些项目是否已经停止?现在有没有办法使用PDO运行多个查询。


答案 1

据我所知,在 PHP 5.3 中替换了。令人困惑的部分是,名称仍然是.所以现在ND是MySQL + PDO的默认驱动程序。PDO_MYSQLNDPDO_MYSQLPDO_MYSQL

总体而言,要一次执行多个查询,您需要:

  • 5.3 菲律宾比索
  • mysqlnd
  • 模拟预准备语句。确保 设置为(默认值)。或者,您可以避免使用预准备语句并直接使用。PDO::ATTR_EMULATE_PREPARES1$pdo->exec

使用 exec

$db = new PDO("mysql:host=localhost;dbname=test", 'root', '');

// works regardless of statements emulation
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);

$sql = "
DELETE FROM car; 
INSERT INTO car(name, type) VALUES ('car1', 'coupe'); 
INSERT INTO car(name, type) VALUES ('car2', 'coupe');
";

$db->exec($sql);

使用语句

$db = new PDO("mysql:host=localhost;dbname=test", 'root', '');

// works not with the following set to 0. You can comment this line as 1 is default
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);

$sql = "
DELETE FROM car; 
INSERT INTO car(name, type) VALUES ('car1', 'coupe'); 
INSERT INTO car(name, type) VALUES ('car2', 'coupe');
";

$stmt = $db->prepare($sql);
$stmt->execute();

注意:

使用模拟预准备语句时,请确保在 DSN 中设置了正确的编码(反映实际数据编码)(自 5.3.6 起可用)。否则,如果使用一些奇怪的编码,则SQL注入的可能性很小


答案 2

经过半天的摆弄,发现PDO有一个错误,其中...

--

//This would run as expected:
$pdo->exec("valid-stmt1; valid-stmt2;");

--

//This would error out, as expected:
$pdo->exec("non-sense; valid-stmt1;");

--

//Here is the bug:
$pdo->exec("valid-stmt1; non-sense; valid-stmt3;");

它将执行 ,停止打开,并且永远不会抛出错误。不会运行,返回真实并谎称一切运行良好。"valid-stmt1;""non-sense;""valid-stmt3;"

我希望它会出错,但它没有。"non-sense;"

以下是我找到此信息的位置:无效的PDO查询不返回错误

这是错误:https://bugs.php.net/bug.php?id=61613


所以,我尝试用mysqli做这件事,并没有真正找到任何关于它如何工作的可靠答案,所以我想我只是把它留在这里给那些想要使用它的人。

try{
    // db connection
    $mysqli = new mysqli("host", "user" , "password", "database");
    if($mysqli->connect_errno){
        throw new Exception("Connection Failed: [".$mysqli->connect_errno. "] : ".$mysqli->connect_error );
        exit();
    }

    // read file.
    // This file has multiple sql statements.
    $file_sql = file_get_contents("filename.sql");

    if($file_sql == "null" || empty($file_sql) || strlen($file_sql) <= 0){
        throw new Exception("File is empty. I wont run it..");
    }

    //run the sql file contents through the mysqli's multi_query function.
    // here is where it gets complicated...
    // if the first query has errors, here is where you get it.
    $sqlFileResult = $mysqli->multi_query($file_sql);
    // this returns false only if there are errros on first sql statement, it doesn't care about the rest of the sql statements.

    $sqlCount = 1;
    if( $sqlFileResult == false ){
        throw new Exception("File: '".$fullpath."' , Query#[".$sqlCount."], [".$mysqli->errno."]: '".$mysqli->error."' }");
    }

    // so handle the errors on the subsequent statements like this.
    // while I have more results. This will start from the second sql statement. The first statement errors are thrown above on the $mysqli->multi_query("SQL"); line
    while($mysqli->more_results()){
        $sqlCount++;
        // load the next result set into mysqli's active buffer. if this fails the $mysqli->error, $mysqli->errno will have appropriate error info.
        if($mysqli->next_result() == false){
            throw new Exception("File: '".$fullpath."' , Query#[".$sqlCount."], Error No: [".$mysqli->errno."]: '".$mysqli->error."' }");
        }
    }
}
catch(Exception $e){
    echo $e->getMessage(). " <pre>".$e->getTraceAsString()."</pre>";
}

推荐