简体   繁体   English

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

[英]PDO support for multiple queries (PDO_MYSQL, PDO_MYSQLND)

I do know that PDO does not support multiple queries getting executed in one statement.我知道 PDO 不支持在一个语句中执行多个查询。 I've been Googleing and found few posts talking about PDO_MYSQL and PDO_MYSQLND.我一直在用 Google 搜索,发现很少有关于 PDO_MYSQL 和 PDO_MYSQLND 的帖子。

PDO_MySQL is a more dangerous application than any other traditional MySQL applications. PDO_MySQL 是比任何其他传统 MySQL 应用程序更危险的应用程序。 Traditional MySQL allows only a single SQL query.传统的 MySQL 只允许单个 SQL 查询。 In PDO_MySQL there is no such limitation, but you risk to be injected with multiple queries.在 PDO_MySQL 中没有这样的限制,但是您可能会被注入多个查询。

From: Protection against SQL Injection using PDO and Zend Framework (June 2010; by Julian)来自: 使用 PDO 和 Zend 框架防止 SQL 注入(2010 年 6 月;朱利安)

It seems like PDO_MYSQL and PDO_MYSQLND do provide support for multiple queries, but I am not able to find more information about them.似乎 PDO_MYSQL 和 PDO_MYSQLND 确实提供了对多个查询的支持,但我无法找到有关它们的更多信息。 Were these projects discontinued?这些项目停止了吗? Is there any way now to run multiple queries using PDO.现在有什么方法可以使用 PDO 运行多个查询。

As I know, PDO_MYSQLND replaced PDO_MYSQL in PHP 5.3.据我所知, PDO_MYSQLND取代了PDO_MYSQL 5.3 中的 PDO_MYSQL。 Confusing part is that name is still PDO_MYSQL .令人困惑的部分是名称仍然是PDO_MYSQL So now ND is default driver for MySQL+PDO.所以现在ND是MySQL+PDO的默认驱动。

Overall, to execute multiple queries at once you need:总体而言,要一次执行多个查询,您需要:

  • PHP 5.3+ PHP 5.3+
  • mysqlnd mysqlnd
  • Emulated prepared statements.模拟准备好的语句。 Make sure PDO::ATTR_EMULATE_PREPARES is set to 1 (default).确保PDO::ATTR_EMULATE_PREPARES设置为1 (默认)。 Alternatively you can avoid using prepared statements and use $pdo->exec directly.或者,您可以避免使用准备好的语句并直接使用$pdo->exec

Using 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);

Using statements使用语句

$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();

A note:一张纸条:

When using emulated prepared statements, make sure you have set proper encoding (that reflects actual data encoding) in DSN (available since 5.3.6).使用模拟的预处理语句时,请确保您已在DSN中设置正确的编码(反映实际数据编码)(自 5.3.6 起可用)。 Otherwise there can be a slight possibility for SQL injection if some odd encoding is used .否则,如果使用了一些奇怪的编码,SQL 注入的可能性很小

After half a day of fiddling with this, found out that PDO had a bug where...经过半天的摆弄,发现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;");

It would execute the "valid-stmt1;"它将执行"valid-stmt1;" , stop on "non-sense;" ,停止"non-sense;" and never throw an error.并且永远不会抛出错误。 Will not run the "valid-stmt3;"不会运行"valid-stmt3;" , return true and lie that everything ran good. ,返回真和谎言,一切运行良好。

I would expect it to error out on the "non-sense;"我希望它会在"non-sense;" but it doesn't.但事实并非如此。

Here is where I found this info: Invalid PDO query does not return an error这是我找到此信息的地方: Invalid PDO query does not return an error

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


So, I tried doing this with mysqli and haven't really found any solid answer on how it works so I thought I's just leave it here for those who want to use it..所以,我试着用 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>";
}

A quick-and-dirty approach:一种快速而肮脏的方法:

function exec_sql_from_file($path, PDO $pdo) {
    if (! preg_match_all("/('(\\\\.|.)*?'|[^;])+/s", file_get_contents($path), $m))
        return;

    foreach ($m[0] as $sql) {
        if (strlen(trim($sql)))
            $pdo->exec($sql);
    }
}

Splits at reasonable SQL statement end points.在合理的 SQL 语句端点处拆分。 There is no error checking, no injection protection.没有错误检查,没有注入保护。 Understand your use before using it.使用前了解您的用途。 Personally, I use it for seeding raw migration files for integration testing.就个人而言,我使用它来为集成测试播种原始迁移文件。

Try this function: mltiple queries and multiple values insertion.试试这个 function:多查询和多值插入。

function employmentStatus($Status) {
$pdo = PDO2::getInstance();

$sql_parts = array(); 
for($i=0; $i<count($Status); $i++){
    $sql_parts[] = "(:userID, :val$i)";
}

$requete = $pdo->dbh->prepare("DELETE FROM employment_status WHERE userid = :userID; INSERT INTO employment_status (userid, status) VALUES ".implode(",", $sql_parts));
$requete->bindParam(":userID", $_SESSION['userID'],PDO::PARAM_INT);
for($i=0; $i<count($Status); $i++){
    $requete->bindParam(":val$i", $Status[$i],PDO::PARAM_STR);
}
if ($requete->execute()) {
    return true;
}
return $requete->errorInfo();
}

Like thousands of people, I'm looking for this question:像成千上万的人一样,我正在寻找这个问题:
Can run multiple queries simultaneously, and if there was one error, none would run I went to this page everywhere可以同时运行多个查询,如果有一个错误,就不会运行我到处去这个页面
But although the friends here gave good answers, these answers were not good for my problem但是虽然这里的朋友给出了很好的答案,但是这些答案对我的问题没有好处
So I wrote a function that works well and has almost no problem with sql Injection.所以我写了一个 function 效果很好,几乎没有 sql 注入问题。
It might be helpful for those who are looking for similar questions so I put them here to use对于那些正在寻找类似问题的人可能会有所帮助,所以我把它们放在这里使用

function arrayOfQuerys($arrayQuery)
{
    $mx = true;
    $conn->beginTransaction();
    try {
        foreach ($arrayQuery AS $item) {
            $stmt = $conn->prepare($item["query"]);
            $stmt->execute($item["params"]);
            $result = $stmt->rowCount();
            if($result == 0)
                $mx = false;
         }
         if($mx == true)
             $conn->commit();
         else
             $conn->rollBack();
    } catch (Exception $e) {
        $conn->rollBack();
        echo "Failed: " . $e->getMessage();
    }
    return $mx;
}

for use(example):使用(示例):

 $arrayQuery = Array(
    Array(
        "query" => "UPDATE test SET title = ? WHERE test.id = ?",
        "params" => Array("aa1", 1)
    ),
    Array(
        "query" => "UPDATE test SET title = ? WHERE test.id = ?",
        "params" => Array("bb1", 2)
    )
);
arrayOfQuerys($arrayQuery);

and my connection:和我的联系:

    try {
        $options = array(
            //For updates where newvalue = oldvalue PDOStatement::rowCount()   returns zero. You can use this:
            PDO::MYSQL_ATTR_FOUND_ROWS => true
        );
        $conn = new PDO("mysql:host=$servername;dbname=$database", $username, $password, $options);
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch (PDOException $e) {
        echo "Error connecting to SQL Server: " . $e->getMessage();
    }

Note:笔记:
This solution helps you to run multiple statement together,该解决方案可帮助您同时运行多个语句,
If an incorrect a statement occurs, it does not execute any other statement如果出现不正确的a语句,它不会执行任何其他语句

PDO does support this (as of 2020). PDO 确实支持这一点(截至 2020 年)。 Just do a query() call on a PDO object as usual, separating queries by;只需像往常一样对 PDO object 进行 query() 调用,将查询分开; and then nextRowset() to step to the next SELECT result, if you have multiple.然后 nextRowset() 到下一个 SELECT 结果,如果你有多个。 Resultsets will be in the same order as the queries.结果集将与查询的顺序相同。 Obviously think about the security implications - so don't accept user supplied queries, use parameters, etc. I use it with queries generated by code for example.显然要考虑安全隐患——所以不要接受用户提供的查询、使用参数等。例如,我将它与代码生成的查询一起使用。

$statement = $connection->query($query);
do {
  $data[] = $statement->fetchAll(PDO::FETCH_ASSOC);
} while ($statement->nextRowset());

Tried following code尝试了以下代码

 $db = new PDO("mysql:host={$dbhost};dbname={$dbname};charset=utf8", $dbuser, $dbpass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

Then然后

 try {
 $db->query('SET NAMES gbk');
 $stmt = $db->prepare('SELECT * FROM 2_1_paidused WHERE NumberRenamed = ? LIMIT 1');
 $stmt->execute(array("\xbf\x27 OR 1=1 /*"));
 }
 catch (PDOException $e){
 echo "DataBase Errorz: " .$e->getMessage() .'<br>';
 }
 catch (Exception $e) {
 echo "General Errorz: ".$e->getMessage() .'<br>';
 }

And got并得到

DataBase Errorz: SQLSTATE[42000]: Syntax error or access violation: 1064 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 '/*' LIMIT 1' at line 1

If added $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);如果添加$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); after $db =...$db =...

Then got blank page然后得到空白页

If instead SELECT tried DELETE , then in both cases got error like如果改为SELECT尝试DELETE ,那么在这两种情况下都会出现错误

 DataBase Errorz: SQLSTATE[42000]: Syntax error or access violation: 1064 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 '* FROM 2_1_paidused WHERE NumberRenamed = '¿\' OR 1=1 /*' LIMIT 1' at line 1

So my conclusion that no injection possible...所以我的结论是不可能注射...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM