다중 쿼리에 대한 PDO 지원 (PDO_MYSQL, PDO_MYSQLND)


102

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를 사용하여 여러 쿼리를 실행할 수있는 방법이 있습니까?


4
SQL 트랜잭션을 사용하십시오.
tereško 2013-10-13

여러 쿼리를 사용하려는 이유는 무엇입니까? 그것들은 거래되지 않고, 당신이 그것들을 차례로 실행하는 것과 똑같습니다. IMHO 장점은없고 단점 만 있습니다. SQLInjection의 경우 공격자가 원하는대로 할 수 있습니다.
mleko 2014

이제 2020 년이고 PDO는 이것을 지원합니다. 아래의 내 대답을 참조하십시오.
Andris

답변:


141

아시다시피 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');
";

$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 삽입이 약간 발생할 수 있습니다 .


37
대답 자체에는 잘못된 것이 없습니다. 여러 쿼리를 실행하는 방법을 설명합니다. 답변에 결함이 있다는 가정은 쿼리에 사용자 입력이 포함되어 있다는 가정에서 비롯됩니다. 한 번에 여러 쿼리를 통해 전송하면 성능에 도움이 될 수있는 유효한 사용 사례가 있습니다. 이 질문에 대한 대안으로 절차 사용을 제안 할 수 있지만 그렇다고이 대답이 나쁘지는 않습니다.
Gajus 2014-04-21

9
이 답변의 코드는 나쁘고 일부 매우 해로운 관행을 조장합니다 ( SQL 주입 취약점에 대한 코드를 열어주는 prepares 문에 에뮬레이션 사용 ). 그것을 사용하지 마십시오.
tereško 2014

17
이 답변과 특히 에뮬레이션 모드에는 문제가 없습니다. pdo_mysql에서 기본적으로 활성화되어 있으며 문제가있는 경우 이미 수천 번의 주입이있을 것입니다. 그러나 아직 아무도 가까이 있지 않았습니다. 그래서 간다.
당신의 상식

3
사실 감정뿐 아니라 논증도 제공 한 사람은 ircmaxell 뿐이었다. 그러나 그가 가져온 링크는 관련이 없습니다. 첫 번째는 "PDO는 항상이 버그에 영향을받지 않습니다." 라고 명시되어 있으므로 전혀 적용 할 수 없습니다 . 두 번째는 적절한 인코딩을 설정하여 간단히 해결할 수 있습니다. 따라서 경고가 아니라 덜 매력적인 메모가 필요합니다.
당신의 상식

6
우리 개발자 만 작성한 SQL을 사용하는 마이그레이션 도구를 작성하는 사람 (즉, SQL 삽입은 문제가되지 않음)이라고 말하면이 코드가 크게 도움이되었으며이 코드가 해롭다는 의견은 모든 것을 완전히 이해하지 못합니다. 사용법에 대한 컨텍스트.
누가 복음

17

반나절 만에 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>";
}

$pdo->exec("valid-stmt1; non-sense; valid-stmt3;");이전 두 exec없이 만 실행하면 작동합니까 ? 중간에 오류던질 수는 있지만 성공적인 execs 후에 실행될 때는 아닙니다 .
Jeff Puckett

아니, 그렇지 않습니다. 이것이 PDO의 버그입니다.
Sai Phaninder Reddy J

1
내 잘못은, 그 3 $pdo->exec("")은 서로 독립적입니다. 이제 문제가 발생하기 위해 순서대로있을 필요가 없음을 나타 내기 위해 분할했습니다. 이 세 가지는 하나의 exec 문에서 여러 쿼리를 실행하는 세 가지 구성입니다.
Sai Phaninder Reddy J

흥미 롭군. 내 게시 된 질문을 볼 기회가 있었습니까? exec페이지 에만 있으면 오류가 발생할 수 있기 때문에 부분적으로 패치되었는지 궁금 하지만 exec여러 SQL 문을 사용하여 여러 개를 실행하면 여기에서 동일한 버그를 재현합니다. 하지만 exec페이지에 유일한 것이라면 재현 할 수 없습니다.
Jeff Puckett

하나습니까 exec페이지에 여러 개의 문을 가지고?
Sai Phaninder Reddy J

3

빠르고 더러운 접근 방식 :

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

합리적인 SQL 문 끝점에서 분할합니다. 오류 검사, 주입 방지 기능이 없습니다. 사용하기 전에 사용법을 이해하십시오. 개인적으로 통합 테스트를 위해 원시 마이그레이션 파일을 시드하는 데 사용합니다.


1
SQL 파일에 mysql 내장 명령이 포함되어 있으면 실패합니다 ... SQL 파일이 크면 PHP 메모리 제한도 날려 버릴 것입니다 ... ;SQL에 프로 시저 또는 트리거 정의가 포함 된 경우 중단시 분할 ... 좋지 않은 이유.
Bill Karwin

1

수천 명의 사람들과 마찬가지로이 질문을 찾고
있습니다. 여러 쿼리를 동시에 실행할 수 있고 오류가 하나만 발생하면 아무 것도 실행되지 않습니다.이 페이지를 어디에서나 방문
했지만 여기에있는 친구들이 좋은 대답을했지만이 대답은 좋지 않았습니다. 내 문제
그래서 나는 잘 작동하고 SQL 주입에 거의 문제가없는 함수를 작성했습니다.
비슷한 질문을 찾는 분들에게 도움이 될 수 있으니 여기에 넣어서

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

사용을 위해 (예) :

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

그리고 내 연결 :

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

참고 :
이 솔루션은 여러 문을 함께 실행하는 데 도움이됩니다
. 잘못된 문이 발생하면 다른 문을 실행하지 않습니다.


0

다음 코드 시도

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

그때

 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>';
 }

그리고있어

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

$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);이후에 추가하면$db = ...

그런 다음 빈 페이지가 생겼습니다.

대신 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

그래서 주사가 불가능하다는 결론은 ...


3
이 질문을 참조하는 새로운 질문으로 만들어야합니다
Your Common Sense

내가 시도한 결과로 많은 질문이 아닙니다. 그리고 내 결론. 초기 질문은 오래되었으며 현재로서는 실제가 아닐 수 있습니다.
Andris

이것이 질문의 어떤 것과 관련이 있는지 확실하지 않습니다.
cHao

문제의 단어는 but you risk to be injected with multiple queries.내 대답은 주입에 관한 것입니다
안드리

0

이 기능을 시도하십시오 : mltiple 쿼리 및 다중 값 삽입.

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

0

PDO는이를 지원합니다 (2020 년 기준). 평상시처럼 PDO 객체에 대해 query () 호출을 수행하여 쿼리를; 다음 SELECT 결과가 여러 개인 경우 nextRowset ()을 사용하여 다음 SELECT 결과로 이동합니다. 결과 집합은 쿼리와 동일한 순서입니다. 보안에 미치는 영향에 대해 분명히 생각해보십시오. 사용자가 제공 한 쿼리를 받아들이거나 매개 변수를 사용하지 마십시오. 예를 들어 코드에 의해 생성 된 쿼리와 함께 사용합니다.

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

나는 이런 종류의 추론을 결코 이해하지 못할 것이다. "여기에 권장되는 모든 모범 사례를 무시하는 보안에 큰 구멍이있는 코드가 있으므로 보안 관련 사항에 대해 생각해야합니다." 누가 그것에 대해 생각해야합니까? 이 코드를 사용하기 전이나 해킹당한 후에 생각해야 할 때 이 함수를 작성하거나 다른 사람에게 제공하기 전에 먼저 생각해 보는 것이 어떻습니까?
당신의 상식

친애하는 @YourCommonSense는 한 번에 여러 쿼리를 실행하여 성능을 높이고 네트워크 트래픽을 줄이고 서버는 관련 쿼리를 최적화 할 수 있습니다. 내 (간단한) 예제는 사용하는 데 필요한 방법을 소개하는 것을 의미했습니다. 당신이 언급하는 좋은 관행을 사용하지 않는 경우에만 보안 구멍입니다. BTW, 나는 그들이 쉽게 할 수있을 때 "나는 결코 이해할 수 없을 것이다 ..."라고 말하는 사람들을 의심한다. :-)
Andris
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.