Поддержка PDO для нескольких запросов (PDO_MYSQL, PDO_MYSQLND)
Я знаю, что PDO не поддерживает несколько запросов, выполняемых в одном из операторов. Я был Google и нашел несколько сообщений о PDO_MYSQL и PDO_MYSQLND.
PDO_MySQL является более опасным приложения, чем любые другие традиционные Приложений MySQL. Традиционный MySQL допускает только один SQL-запрос. В PDO_MySQL нет такого ограничения, но вы рискуете быть введенными несколько запросов.
От: Защита от SQL-инъекций с использованием PDO и Zend Framework (июнь 2010 г., Julian)
Кажется, что PDO_MYSQL и PDO_MYSQLND предоставляют поддержку нескольких запросов, но я не могу найти больше информации о них. Были ли эти проекты прекращены? Есть ли способ запустить несколько запросов с помощью PDO.
Ответы
Ответ 1
Как я знаю, PDO_MYSQLND
заменил PDO_MYSQL
на PHP 5.3. Сбивая с толку часть, это имя еще 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();
}
Использование операторов
$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();
}
Примечание:
При использовании эмулированных подготовленных операторов убедитесь, что вы установили правильное кодирование (которое отражает фактическое кодирование данных) в 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 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 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>";
}
Ответ 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. Проверка ошибок отсутствует, защита от инъекций отсутствует. Поймите свое использование перед использованием. Лично я использую его для загрузки необработанных файлов миграции для тестирования интеграции.
Ответ 4
Пробовал следующий код
$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
Итак, мой вывод, что инъекция невозможна...
Ответ 5
Попробуйте эту функцию: 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();
}
Ответ 6
Как и тысячи людей, я ищу этот вопрос:
Может запускать несколько запросов одновременно, и если бы произошла одна ошибка, ни одна из них не запустилась.
Но хотя друзья здесь дали хорошие ответы, эти ответы не были хороши для моей проблемы
Поэтому я написал функцию, которая работает хорошо и практически не имеет проблем с 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();
}
Замечания:
Это решение поможет вам запустить несколько операторов вместе,
Если происходит неверное утверждение, оно не выполняет никакого другого оператора