Soporte de PDO para múltiples consultas (PDO MYSQL, PDO MYSQLND)
Sé que PDO no admite que se ejecuten varias consultas en una sola instrucción. He estado buscando en Google y he encontrado algunos mensajes que hablan de PDO_MYSQL y PDO_MYSQLND.
PDO_MySQL es un método más peligroso aplicación que cualquier otra tradicional Aplicaciones MySQL. MySQL tradicional permite una sola consulta SQL. En PDO_MySQL no existe tal limitación, pero corre el riesgo de ser inyectado con múltiples consultas.
Desde: Protección contra SQL Inyección usando DOP y Zend Framework (junio de 2010; por Julian)
Parece que PDO_MYSQL y PDO_MYSQLND proporcionan soporte para múltiples consultas, pero no puedo encontrar más información sobre ellas. ¿Se suspendieron esos proyectos? ¿Hay alguna manera ahora de ejecutar múltiples consultas usando PDO?
5 answers
Como sé, PDO_MYSQLND
reemplazó a PDO_MYSQL
en PHP 5.3. La parte confusa es que el nombre sigue siendo PDO_MYSQL
. Así que ahora ND es el controlador predeterminado para MySQL + PDO.
En general, para ejecutar múltiples consultas a la vez necesita:
- PHP 5.3 +
- mysqlnd
- Emuló declaraciones preparadas. Asegúrese de que
PDO::ATTR_EMULATE_PREPARES
esté establecido en1
(predeterminado). Alternativamente, puede evitar el uso de instrucciones preparadas y usar$pdo->exec
directamente.
Usando 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();
}
Usando declaraciones
$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();
}
Una nota:
Cuando utilice instrucciones preparadas emuladas, asegúrese de haber establecido una codificación adecuada (que refleje la codificación de datos real) en DSN (disponible desde 5.3.6). De lo contrario puede haber una ligera posibilidad de inyección SQL si se usa alguna codificación extraña.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:20
Después de medio día de juguetear con esto, se enteró de que PDO tenía un error donde...
--
//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;");
Ejecutaría el "valid-stmt1;"
, se detendría en "non-sense;"
y nunca lanzaría un error. No se ejecutará el "valid-stmt3;"
, devolver verdadero y mentira que todo funcionó bien.
Esperaría que se equivoque en el "non-sense;"
pero no lo hace.
Aquí es donde encontré esta información: La consulta PDO no válida no devuelve un error
Aquí está el error: https://bugs.php.net/bug.php?id=61613
Así que intenté hacer esto con mysqli y realmente no he encontrado ninguna respuesta sólida sobre cómo funciona, así que pensé que solo lo dejaría aquí para aquellos que quieran usarlo..
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>";
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:25:52
Un enfoque rápido y sucio:
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);
}
}
Se divide en puntos finales razonables de la sentencia SQL. No hay comprobación de errores, no hay protección de inyección. Comprenda su uso antes de usarlo. Personalmente, lo uso para sembrar archivos de migración raw para pruebas de integración.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-06 16:26:48
Pruebe esta función : mltiple queries and multiple values insertion.
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();
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-17 16:00:59
Intentó seguir el código
$db = new PDO("mysql:host={$dbhost};dbname={$dbname};charset=utf8", $dbuser, $dbpass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Entonces
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>';
}
Y consiguió
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
Si se añade $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
después de $db = ...
Entonces tengo una página en blanco
Si en cambio SELECT
intentó DELETE
, entonces en ambos casos obtuvo un error como
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
Así que mi conclusión es que no hay inyección posible...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-30 17:04:57