PDO support for multiple queries (PDO_MYSQL, PDO_MYSQLND)
I do know that PDO does not support multiple queries getting executed in one statement. I've been Googleing and found few posts talking about PDO_MYSQL and PDO_MYSQLND.
PDO_MySQL is a more dangerous application than any other traditional MySQL applications. Traditional MySQL allows only a single SQL query. In PDO_MySQL there is no such limitation, but you risk to be injected with multiple queries.
From: Protection against SQL Injection using PDO and Zend Framework (June 2010; by Julian)
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. Were these projects discontinued? Is there any way now to run multiple queries using PDO.
As I know, PDO_MYSQLND
replaced PDO_MYSQL
in PHP 5.3. Confusing part is that name is still PDO_MYSQL
. So now ND is default driver for MySQL+PDO.
Overall, to execute multiple queries at once you need:
- PHP 5.3+
- mysqlnd
- Emulated prepared statements. Make sure
PDO::ATTR_EMULATE_PREPARES
is set to1
(default). Alternatively you can avoid using prepared statements and use$pdo->exec
directly.
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). Otherwise there can be a slight possibility for SQL injection if some odd encoding is used.
After half a day of fiddling with this, found out that PDO had a bug where...
--
//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;"
, stop on "non-sense;"
and never throw an error. Will not run the "valid-stmt3;"
, return true and lie that everything ran good.
I would expect it to error out on the "non-sense;"
but it doesn't.
Here is where I found this info: Invalid PDO query does not return an error
Here is the bug: 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..
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. 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.
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.
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