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

Gaj*_*jus 96 php mysql pdo

我知道PDO不支持在一个语句中执行多个查询.我一直在谷歌,发现很少有关于PDO_MYSQL和PDO_MYSQLND的帖子.

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

From:使用PDO和Zend Framework防止SQL注入(2010年6月;作者Julian)

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

Sam*_*ark 133

据我所知,在PHP 5.3中PDO_MYSQLND取代PDO_MYSQL.令人困惑的部分是名称仍然是PDO_MYSQL.所以现在ND是MySQL + PDO的默认驱动程序.

总的来说,要一次执行多个查询,您需要:

  • PHP 5.3+
  • mysqlnd
  • 模拟准备好的陈述.确保PDO::ATTR_EMULATE_PREPARES设置为1(默认).或者,您可以避免使用预准备语句并$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');
";

try {
    $db->exec($sql);
}
catch (PDOException $e)
{
    echo $e->getMessage();
    die();
}
Run Code Online (Sandbox Code Playgroud)

使用语句

$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');
";

try {
    $stmt = $db->prepare($sql);
    $stmt->execute();
}
catch (PDOException $e)
{
    echo $e->getMessage();
    die();
}
Run Code Online (Sandbox Code Playgroud)

一张纸条:

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

  • 答案本身没有错.它解释了如何执行多个查询.您认为答案有缺陷的假设来自于查询包含用户输入的假设.有一些有效的用例,一次发送多个查询可以提高性能.你可以建议使用程序作为这个问题的替代答案,但这并不能使这个答案变得糟糕. (34认同)
  • 这个答案没有错,特别是仿真模式.它默认在pdo_mysql中启用,如果有任何问题,已经有数千次注入.但还没有一个人.就这样吧. (17认同)
  • 这个答案中的代码很糟糕,并且促进了一些非常有害的实践(使用模拟语句准备语句,这会使代码对**SQL注入漏洞开放**).不要使用它. (8认同)
  • 作为编写使用SQL的迁移工具的人,只有我们的开发人员编写过(即SQL注入不是问题),这对我有很大帮助,并且任何表明此代码有害的注释都不能完全理解所有其用法的上下文. (6认同)
  • 事实上,只有一个人不仅提供了情感,而且提供了一些论证,是ircmaxell.但是,他带来的链接是无关紧要的.第一个是不适用的,因为它明确地说**"PDO总是免受这个bug."**虽然第二个可以通过设置正确的编码来解决.因此,它值得注意,而不是警告,而不是那么有吸引力. (3认同)

Sai*_*y J 17

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

-

//This would run as expected:
$pdo->exec("valid-stmt1; valid-stmt2;");
Run Code Online (Sandbox Code Playgroud)

-

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

-

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

它会执行"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>";
}
Run Code Online (Sandbox Code Playgroud)


And*_*ris 5

PDO 确实支持这一点(截至 2020 年)。只需像往常一样对 PDO 对象执行 query() 或prepare() 调用,并用 ; 分隔查询。然后 nextRowset() 步进到下一个 SELECT 结果(如果有多个)。结果集的顺序与查询的顺序相同。显然要考虑安全隐患 - 因此不要接受用户提供的查询、使用参数等。例如,我将它与代码生成的查询一起使用。

$connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);
$query = "select * from t1 where param=?;select * from t2 where param=?"
$statement = $connection->prepare($query);
$statement->execute([$param1, $param2]);
do {
    $data[] = $statement->fetchAll(PDO::FETCH_ASSOC);
} while ($statement->nextRowset());
Run Code Online (Sandbox Code Playgroud)

请注意,为了使用此方法,必须打开模拟准备语句。

  • 亲爱的@YourCommonSense 一次性运行多个查询有助于提高性能,更少的网络流量+服务器可以优化相关查询。我的(简化的)示例只是为了介绍使用它所需的方法。仅当您不使用您所提到的那些良好实践时,这才是一个安全漏洞。顺便说一句,我对那些说“我永远不会理解……”的人表示怀疑,而他们很容易就能理解……:-) (9认同)