PDO-MYSQL: query annidate e le "unbuffered query"

Da quando TopHost ha aggiornato Mysql sul nodo dove risiede il mio sito web, ho riscontrato alcuni problemi nell'eseguire query annidate attraverso il magico PDO.
In sostanza, nell'eseguire un codice del genere:
//la connessione è già inizializziata
$stpdo = $mypdo->prepare("Query 1");
if ($stpdo->execute()){
while ($row = $stpdo->fetch()){
$idarticolo = $row[idarticolo];
[... altri parametri fetchati ...]
//Nella query 2 viene usato un valore recuperato con la query 1
$stpdo2 = $mypdo->prepare('Query2');
$stpdo2->bindValue(':id', $idarticolo, PDO::PARAM_INT);
$commenti = 0;
if ($stpdo2->execute()){
while ($row2 = $stpdo2->fetch()){
$commenti++;
}
}
// uso tutti i dati estratti
}
}
$stpdo = $mypdo->prepare("Query 1");
if ($stpdo->execute()){
while ($row = $stpdo->fetch()){
$idarticolo = $row[idarticolo];
[... altri parametri fetchati ...]
//Nella query 2 viene usato un valore recuperato con la query 1
$stpdo2 = $mypdo->prepare('Query2');
$stpdo2->bindValue(':id', $idarticolo, PDO::PARAM_INT);
$commenti = 0;
if ($stpdo2->execute()){
while ($row2 = $stpdo2->fetch()){
$commenti++;
}
}
// uso tutti i dati estratti
}
}
Si incorreva brutalmente in un:
Fatal error: Call to a member function bindValue() on a non-object in /home/mhd-01/pagina.php
Spiegazione
Di default il driver PDO_mysql fa uso delle query unbuffered e per questo motivo:
The requirement is actually ‘need to finish consuming one query before using the same connection for another’, and is a characteristic of using unbuffered mysql connections (i.e. unavoidable aspect of the C client library). Unbuffered connections are used by default in PDO_MySQL.
Nel mio caso è probabile che con Mysql 4 questo comportamento non fosse applicato e PDO usasse comunque le buffered query.
Per scavalcare questo ostacolo possiamo percorrere due strade:
• recuperare i dati attraverso fetchAll() creando in questo modo un array da poter scorrere "a piacere" e nel mentre eseguire ulteriori query (in poche parole, si eseguono solo query in successione e non annidate perchè nel momento in cui si crea l'array la query è terminata).
Vantaggio: il codice PHP rimane portabile su più DB
Svantaggio: dovendo creare un array, in alcuni casi (recupero di molte righe), ci può essere un elevato spreco di risorse in termini di memoria
• modificare la costante PDO::MYSQL_ATTR_USE_BUFFERED_QUERY a true. Per fare ciò basta precedere le query (o meglio ancora subito dopo aver avviato la connessione) con:
$connessione_pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
E' possibile variare la constante anche nell'esecuzione della singola query.
Vantaggio: possono essere eseguire query annidate in modo abbastanza efficiente
Svantaggio: il codice non è più portabile al 100% su altri DB, avendo sfruttato una particolare caratteristica del driver PDO_mysql.
Ma quali sono le principali differenze tra una query buffered e una unbuffered? Riporto da un articolo di PHPnews.it alcune considerazioni:
• con le query buffered i dati vengono conservati da PHP che ha quindi la possibilità di poterli manipolare più agevolmente. E' infatti possibile utilizzare mysql_data_seek() per spostarsi liberamente all'interno dei risultati. Inoltre è possibile richiamare mysql_num_rows() per sapere subito quante righe sono state restituite dall'ultima query. mysql_data_seek() e mysql_num_rows() non sono disponibili per una unbuffered query.
• Liberando immediatamente MySQL, le query buffered permettono al server di sbloccare subito i dati coinvolti dalla query in modo che altri processi (altri utenti) possano accedervi. Se ad esempio uno script legge un dato dal database, questo stesso dato non potrà essere modificato da un altro script fino a che non si chiude il set di risultati (mysql_free_result).
• Le query buffered costringono il PHP a conservare nella sua memoria tutto il set di risultati e questo compito può risultare oneroso se vengono gestite grosse quantità di dati.
• Le query buffered constringono lo script a sospendere la sua esecuzione fino a quando tutti i dati non hanno raggiunto il PHP, quindi il primo risultato è disponibile solo quando tutti i risultati sono estratti da MySQL, inviati e memorizzati dal PHP.
• Con una query unbuffered i risultati sono accessibili al PHP esclusivamente in maniera sequenziale ma sono disponibili non appena MySQL inizia a restituirli.
• Le query unbuffered hanno un ulteriore inconveniente. Poiché l'interazione tra PHP e MySQL non si conclude con l'invio della query ma continua per tutta la fase di fetch, per lanciare una nuova query è necessario prima prelevare tutti i risultati della precedente oppure chiudere il set di risultati (mysql_free_result).
• Liberando immediatamente MySQL, le query buffered permettono al server di sbloccare subito i dati coinvolti dalla query in modo che altri processi (altri utenti) possano accedervi. Se ad esempio uno script legge un dato dal database, questo stesso dato non potrà essere modificato da un altro script fino a che non si chiude il set di risultati (mysql_free_result).
• Le query buffered costringono il PHP a conservare nella sua memoria tutto il set di risultati e questo compito può risultare oneroso se vengono gestite grosse quantità di dati.
• Le query buffered constringono lo script a sospendere la sua esecuzione fino a quando tutti i dati non hanno raggiunto il PHP, quindi il primo risultato è disponibile solo quando tutti i risultati sono estratti da MySQL, inviati e memorizzati dal PHP.
• Con una query unbuffered i risultati sono accessibili al PHP esclusivamente in maniera sequenziale ma sono disponibili non appena MySQL inizia a restituirli.
• Le query unbuffered hanno un ulteriore inconveniente. Poiché l'interazione tra PHP e MySQL non si conclude con l'invio della query ma continua per tutta la fase di fetch, per lanciare una nuova query è necessario prima prelevare tutti i risultati della precedente oppure chiudere il set di risultati (mysql_free_result).
Ovviamente ci sarebbe molto da parlare su questo argomento, ma per ora non ho le compotenze necessarie per pronunciarmi adeguatamente
.Segnalo ulteriormente anche il post di Wez Furlong:
I've recently discovered a few things about how the mysql client library does things that seem a bit silly to me, so I'm going to share them with you.
* native prepared statements cannot take advantage of the query cache, resulting in lower performance.
* native prepared statements cannot execute certains types of queries, like "SHOW TABLES"
* native prepared statements don't correctly communicate column lengths for certain other "SHOW" queries, resulting in garbled results.
* calling stored procedures multiple times using native prepared statements causes the connection to drop.
I recommend that you use the following attribute when working with PDO::MYSQL, available in the current PHP 5.1.3 release candidates and snapshots:
This causes the PDO native query parser to be used instead of the native prepared statements APIs in the mysql client, and effectively eliminates those problems.
* native prepared statements cannot take advantage of the query cache, resulting in lower performance.
* native prepared statements cannot execute certains types of queries, like "SHOW TABLES"
* native prepared statements don't correctly communicate column lengths for certain other "SHOW" queries, resulting in garbled results.
* calling stored procedures multiple times using native prepared statements causes the connection to drop.
I recommend that you use the following attribute when working with PDO::MYSQL, available in the current PHP 5.1.3 release candidates and snapshots:
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
This causes the PDO native query parser to be used instead of the native prepared statements APIs in the mysql client, and effectively eliminates those problems.
• Nessun commento • Inserisci un commento • Pubblicato il 1 giugno 2008 •
