Compatibilidad con PDO para múltiples consultas (PDO_MYSQL, PDO_MYSQLND)

13 minutos de lectura

Compatibilidad con PDO para multiples consultas PDO MYSQL PDO MYSQLND
Gajus

Sé que PDO no admite la ejecución de múltiples consultas en una declaración. He estado buscando en Google y encontré algunas publicaciones que hablan sobre PDO_MYSQL y PDO_MYSQLND.

PDO_MySQL es una aplicación más peligrosa que cualquier otra aplicación MySQL tradicional. MySQL tradicional permite solo una única consulta SQL. En PDO_MySQL no existe tal limitación, pero corre el riesgo de recibir múltiples consultas.

Desde: Protección contra Inyección SQL usando PDO y Zend Framework (junio de 2010; por Julian)

Parece que PDO_MYSQL y PDO_MYSQLND brindan soporte para múltiples consultas, pero no puedo encontrar más información sobre ellas. ¿Se suspendieron estos proyectos? ¿Hay alguna forma ahora de ejecutar múltiples consultas usando PDO?

  • Utilice transacciones SQL.

    – tereško

    13 de octubre de 2013 a las 12:27

  • ¿Por qué le gustaría usar consultas múltiples? No se tramitan, es lo mismo que los ejecutaría uno tras otro. En mi humilde opinión, no hay pros, solo contras. En el caso de SQLInjection, permite que el atacante haga lo que quiera.

    – mleko

    21 de abril de 2014 a las 12:21


  • Ahora es 2020, y PDO admite esto; vea mi respuesta a continuación.

    – Andrés

    8 de julio de 2020 a las 21:43

1647407830 934 Compatibilidad con PDO para multiples consultas PDO MYSQL PDO MYSQLND
sam oscuro

Que yo sé, PDO_MYSQLND sustituido PDO_MYSQL en PHP 5.3. La parte confusa es que el nombre sigue siendo PDO_MYSQL. Entonces ahora ND es el controlador predeterminado para MySQL + PDO.

En general, para ejecutar múltiples consultas a la vez, necesita:

  • PHP 5.3+
  • mysqlnd
  • Declaraciones preparadas emuladas. Asegurarse PDO::ATTR_EMULATE_PREPARES se establece en 1 (por defecto). Alternativamente, puede evitar usar declaraciones 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');
";

$db->exec($sql);

Uso de 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');
";

$stmt = $db->prepare($sql);
$stmt->execute();

Una nota:

Cuando utilice declaraciones preparadas emuladas, asegúrese de haber establecido la codificación adecuada (que refleje la codificación de datos real) en DSN (disponible desde 5.3.6). De lo contrario, puede haber una pequeña posibilidad de inyección de SQL si se usa alguna codificación extraña.

  • No hay nada malo con la respuesta en sí. Explica cómo ejecutar varias consultas. Su suposición de que la respuesta es defectuosa proviene de la suposición de que la consulta contiene información del usuario. Hay casos de uso válidos en los que enviar varias consultas a la vez puede beneficiar el rendimiento. Podría sugerir el uso de procedimientos como una respuesta alternativa a esta pregunta, pero eso no hace que esta respuesta sea mala.

    – Gajus

    21 de abril de 2014 a las 11:53

  • El código en esta respuesta es malo y promueve algunas prácticas muy dañinas (uso de emulación para declaraciones de preparación, que hacen que el código esté abierto a Vulnerabilidad de inyección SQL). No lo uses.

    – tereško

    24/04/2014 a las 19:32


  • No hay nada malo con esta respuesta y el modo de emulación en particular. Está habilitado por defecto en pdo_mysql, y si hubiera algún problema, ya habría miles de inyecciones. Pero nadie se acercó a uno todavía. Así que va.

    – Tu sentido común

    24/04/2014 a las 20:06

  • De hecho, el único que logró aportar no solo emociones sino algún argumento, fue ircmaxell. Sin embargo, los enlaces que trajo son bastante irrelevantes. El primero es inaplicable en absoluto, ya que dice explícitamente ” PDO siempre es inmune a este error”. Mientras que el segundo es simplemente solucionable configurando la codificación adecuada. Por lo tanto, merece una nota, no una advertencia, y menos llamativa.

    – Tu sentido común

    28 de abril de 2014 a las 8:45


  • Hablando como alguien que está escribiendo una herramienta de migración que usa SQL que solo nuestros desarrolladores han escrito (es decir, la inyección de SQL no es un problema), esto me ha ayudado enormemente, y cualquier comentario que indique que este código es dañino no comprende completamente todos los contextos para su uso.

    – Lucas

    6 de abril de 2016 a las 9:20

1647407830 92 Compatibilidad con PDO para multiples consultas PDO MYSQL PDO MYSQLND
Sai Phaninder Reddy J.

Después de medio día de jugar con esto, descubrí 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;"parar en "non-sense;" y nunca arrojar un error. no ejecutará el "valid-stmt3;"devuelve verdad y mentira que todo salió bien.

Espero que se produzca un error en el "non-sense;" pero no lo hace

Aquí es donde encontré esta información: la consulta de PDO no válida no devuelve un error

Aquí está el error:
https://bugs.php.net/bug.php?id=61613


Entonces, traté de hacer esto con mysqli y realmente no encontré ninguna respuesta sólida sobre cómo funciona, así que pensé en dejarlo 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>";
}

  • ¿Funciona si solo ejecutas $pdo->exec("valid-stmt1; non-sense; valid-stmt3;"); sin los dos ejecutivos anteriores? Puedo hacer que arroje errores en el medio, pero no cuando se ejecuta después ejecutivos exitosos.

    –Jeff Puckett

    18/10/2016 a las 20:08

  • No, no lo hace. Ese es el error con PDO.

    – Sai Phaninder Reddy J.

    19 de octubre de 2016 a las 1:08

  • Mala mía, esos 3 $pdo->exec("") son independientes entre si. Ahora los divido para indicar que no tienen que estar en una secuencia para que surja el problema. Esos 3 son 3 configuraciones para ejecutar múltiples consultas en una declaración ejecutiva.

    – Sai Phaninder Reddy J.

    19 de octubre de 2016 a las 1:24

  • Interesante. ¿Tuviste la oportunidad de ver mi pregunta publicada? Me pregunto si esto se ha parcheado parcialmente porque puedo generar el error si es el único exec en la página, pero si ejecuto múltiples exec cada uno con múltiples declaraciones SQL en ellos, luego reproduzco el mismo error aquí. pero si es el unico exec en la página, entonces no puedo reproducirlo.

    –Jeff Puckett

    19 de octubre de 2016 a las 2:43

  • hizo ese exec en su página tiene varias declaraciones?

    – Sai Phaninder Reddy J.

    19/10/2016 a las 22:16

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 de declaraciones SQL razonables. No hay comprobación de errores, ni protección de inyección. Comprenda su uso antes de usarlo. Personalmente, lo uso para sembrar archivos de migración sin procesar para pruebas de integración.

  • Esto falla si su archivo SQL contiene algún comando incorporado de mysql… Probablemente también extinguirá su límite de memoria PHP, si el archivo SQL es grande… Dividir en ; se rompe si su SQL contiene definiciones de procedimientos o disparadores… Muchas razones por las que no es bueno.

    –Bill Karwin

    19 de octubre de 2016 a las 1:41

Compatibilidad con PDO para multiples consultas PDO MYSQL PDO MYSQLND
Sajad Mirzaei

Como miles de personas, estoy buscando esta pregunta:
Puede ejecutar varias consultas simultáneamente, y si hubiera un error, no se ejecutaría ninguna Fui a esta página en todas partes

Pero aunque los amigos aquí dieron buenas respuestas, estas respuestas no fueron buenas para mi problema.
Así que escribí una función que funciona bien y casi no tiene problemas con la inyección de sql.
Puede ser útil para aquellos que buscan preguntas similares, así que las pongo aquí para usar

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

para uso (ejemplo):

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

y mi conexion:

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

Nota:

Esta solución lo ayuda a ejecutar varias declaraciones juntas,
Si ocurre una declaración incorrecta, no ejecuta ninguna otra declaración.

Intenté seguir el código

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

Luego

 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 agrega $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); después $db = ...

Luego obtuve una página en blanco

si en cambio SELECT intentado DELETEluego en ambos casos obtuve 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 de que no es posible la inyección…

  • Debería haber hecho una nueva pregunta donde se hace referencia a esta

    – Tu sentido común

    30 de abril de 2014 a las 17:07


  • No es tanta la pregunta como resultado de lo que intenté. Y mi conclusión. La pregunta inicial es antigua, posiblemente no actual en este momento.

    – Andrés

    30 de abril de 2014 a las 17:13


  • No estoy seguro de cómo esto es relevante para cualquier cosa en la pregunta.

    – chao

    17 de junio de 2014 a las 3:08

  • en cuestión son palabras but you risk to be injected with multiple queries. Mi respuesta es sobre la inyección.

    – Andrés

    20 de enero de 2015 a las 13:30


Compatibilidad con PDO para multiples consultas PDO MYSQL PDO MYSQLND
hassan b.

Pruebe esta función: múltiples consultas e inserción de múltiples valores.

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

  • Debería haber hecho una nueva pregunta donde se hace referencia a esta

    – Tu sentido común

    30 de abril de 2014 a las 17:07


  • No es tanta la pregunta como resultado de lo que intenté. Y mi conclusión. La pregunta inicial es antigua, posiblemente no actual en este momento.

    – Andrés

    30 de abril de 2014 a las 17:13


  • No estoy seguro de cómo esto es relevante para cualquier cosa en la pregunta.

    – chao

    17 de junio de 2014 a las 3:08

  • en cuestión son palabras but you risk to be injected with multiple queries. Mi respuesta es sobre la inyección.

    – Andrés

    20 de enero de 2015 a las 13:30


PDO admite esto (a partir de 2020). Simplemente haga una llamada query() en un objeto PDO como de costumbre, separando las consultas por ; y luego nextRowset() para pasar al siguiente resultado SELECT, si tiene varios. Los conjuntos de resultados estarán en el mismo orden que las consultas. Obviamente, piense en las implicaciones de seguridad, así que no acepte consultas proporcionadas por el usuario, use parámetros, etc. Lo uso con consultas generadas por código, por ejemplo.

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

  • Nunca entendería este tipo de razonamiento: “Aquí hay un código que es un gran agujero en la seguridad que descuida todas las buenas prácticas recomendadas, por lo que debe pensar en las implicaciones de seguridad”. ¿Quién debería pensar en ello? ¿Cuándo deberían pensar: antes de usar este código o después de ser pirateados? ¿Por qué no lo piensas primero, antes de escribir esta función u ofrecerla a otras personas?

    – Tu sentido común

    9 de julio de 2020 a las 3:04


  • Estimado @YourCommonSense ejecutar varias consultas de una sola vez ayuda con el rendimiento, menos tráfico de red + el servidor puede optimizar las consultas relacionadas. Mi ejemplo (simplificado) solo pretendía presentar el método requerido para usarlo. Es un agujero de seguridad solo si no usas esas buenas prácticas a las que te refieres. Por cierto, sospecho de las personas que dicen “Nunca entendería…” cuando fácilmente podrían… 🙂

    – Andrés

    10 de julio de 2020 a las 20:22

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad