Zend db with prepared statement doesn't fetch all data - php

First the guilty code:
$preparedGetData = $db->prepare("CALL getData(?)");
foreach($userSet as $r) {
$preparedGetData->execute(array($r['id_user']));
$rs = $preparedGetData->fetchAll();
$preparedGetData->closeCursor();
}
Explication
getData = stored procedure in mysql
$db = instance of Zend_Db_Adapter_Pdo_Mysql (using Zend version 1.10.0 )
Symptoms
When I leave out closeCursor in the second cycle I already get error:
PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
But I am using fetchAll !!!
When I add the closeCursor the result arrives incomplete. Invoking CALL getData('3872') in query browser returns 1 row with 72 columns. The same done by the code above returns 1 row with only first 41 columns.
What I'm doing wrong?
Edit2: Semi-solution
Code updated to:
$preparedGetData = $db->prepare("CALL getData(?)");
foreach($userSet as $r) {
$preparedGetData->execute(array($r['id_user']));
$rs=array();
do {
try {
$partial_rowset = $preparedGetData->fetchAll(); // When I put fetchAll() after the end of the cycle, I get empty resultset.
} catch (PDOException $error) { // The PDOException doesn't get caught here. Why?
error_log($error);
} catch (Exception $error) {
error_log($error);
}
if ($partial_rowset) {
$rs=array_merge($rs,$partial_rowset);
}
} while ($preparedGetData->nextRowset());
}
Symptoms
Getting error:
PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error
Next exception 'Zend_Db_Statement_Exception' with message 'SQLSTATE[HY000]: General error' in /home/GIT/includes/Zend/Db/Statement/Pdo.php:294
on the line with fetchAll.
This error I can catch using generic exception.
With this code I get all 72 columns.
I perceive this nasty because I am deliberately catching generic exception and just turning it into log. Which I guess will also become an performance issue (the cycle runs about 10 000 times).

MySQL stored procedures may have multiple result sets. That is, you can run more than one SELECT query inside a stored procedure, and then iterate over these multiple result sets.
This complicates fetching because the whole result is really like an array of arrays of arrays (multiple result sets, each result set may have multiple rows, each row may have multiple columns).
But fetchAll() only fetches all the rows from one result set. So when you call a stored procedure, you need to force it to flush all result sets. That is, keep calling nextRowset() until that function returns null.
The Zend_Db API is modeled off of PDO, so you can see example usage of nextRowset() in the docs for PDOStatement::nextRowset().
Unfortunately, there is no such function like fetchAllRowsets(). You are left to do it yourself as a while loop.

Related

How can I execute multiple procedures continuously?

Here is my code:
$query = "CALL user_top_categories_score(?, 'ALL', 0, 1)";
$sth = $this->dbh->prepare($query);
$sth->execute([$user_id]);
$category = $sth->fetchAll(PDO::FETCH_ASSOC);
$query = "CALL user_top_tags_score(?, 'ALL', 0, 3)";
$sth = $this->dbh->prepare($query);
$sth->execute([$user_id]);
$tags = $sth->fetchAll(PDO::FETCH_ASSOC);
It throws this error:
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute. in C:\xampp\htdocs\myweb\others\user.php:71 Stack trace: #0 C:\xampp\htdocs\myweb\others\user.php(71): PDO->prepare('CALL user_top_t...') #1 C:\xampp\htdocs\myweb\application\other.php(24): user->index() #2 C:\xampp\htdocs\myweb\index.php(152): require_once('C:\xampp\htdocs...') #3 {main} thrown in C:\xampp\htdocs\myweb\others\user.php on line 71
Also I've used closeCursor() right after fetchAll(), based on this solution. But sadly it throws a new error:
Warning: Packets out of order. Expected 1 received 9. Packet size=7 in C:\xampp\htdocs\myweb\others\user.php on line 72
Warning: PDO::prepare(): MySQL server has gone away in C:\xampp\htdocs\myweb\others\user.php on line 72
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away in C:\xampp\htdocs\myweb\others\user.php:72 Stack trace: #0 C:\xampp\htdocs\myweb\others\user.php(72): PDO->prepare('CALL user_top_t...') #1 C:\xampp\htdocs\myweb\application\other.php(24): user->index() #2 C:\xampp\htdocs\myweb\index.php(152): require_once('C:\xampp\htdocs...') #3 {main} thrown in C:\xampp\htdocs\myweb\others\user.php on line 72
Any idea how can I fix the problem?
Noted1: Each of queries above work separately. I mean, when I call a single procedure, it works as well.
Noted2: Each procedure returns a result set. I mean there is a SELECT statement in those procedures.
You may have just one SELECT in your procedure, but the API doesn't know that. It has to assume you might have multiple result sets returned from your procedure, therefore fetchAll() alone doesn't close the cursor.
You have to keep calling nextRowset() until it has returned a false result, indicating that all results have been returned from this statement.
See also my answer to How can I use a stored procedure in a MySql database with Zend Framework? (I answered this in 2009!).
Well, in the case of Stored Procedure, the $result object is more complex. In the case of applying MVC, multiple source can load multiple call statements, and the cursor from the first $result can be in the next to the last element of the array (In thise case, the Result has no more elements, in other way, is set to false). For me, worked clear all stored result.
Code:
function clearStoredResults($mysqli_link){ #------------------------------------
while($mysqli_link->next_result()){ if($l_result = $mysqli_link->store_result()){ $l_result->free(); } } }
And finally:
function execQuery($sql){
global $conexion;
try {
$result=$conexion->query($sql);
while($row = $result->fetch_assoc()){
$new_array[] = $row; // Inside while loop
}
return $new_array;
} catch (Exception $e) {
echo $e->getMessage();
}
finally {
clearStoredResults($conexion);
}
}
I recomend to explore with care. You can use administrative tools form mysql or mariabd and php, to explore the effects.

MySQL Update and Select in one statement

I am attempting to do an UPDATE and a SELECT in the same sql statement. For some reason, the below code is failing.
$sql = "UPDATE mytable SET last_activity=CURRENT_TIMESTAMP,
info1=:info1, info2=:info2 WHERE id = {$id};";
$sql .= "SELECT id, info1, info2 FROM myTable
WHERE info1 >=:valueA AND info2>:valueB;"
$stmt = $conn->prepare($sql);
$stmt->bindParam(":info1", $info1);
$stmt->bindParam(":info2", $info2);
$stmt->bindParam(":valueA", $valueA);
$stmt->bindParam(":valueB", $valueB);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($result);
QUESTION: what might I be doing wrong? I have been spending hours on this issue knowing that it's probably a small error right under my nose.
Edited:
I obtained this error message when loading the page that contains the php code:
Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]:
General error' in ajaxCall.php:89 Stack trace: #0 ajaxCall.php(89):
PDOStatement->fetchAll(2) #1 {main} thrown in ajaxCall.php on line 89
I am using ajax to call the php page that contains the above code, and when I load the php page from the browser, I get the above error message.
Line 89 is: $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
Since you are running two queries, you need to call nextRowset to access the results from the second one.
So, do it like this:
// code
$stmt->execute();
$stmt->nextRowset();
// code
When you run two or more queries, you get a multi-rowset result. That means that you get something like this (representation only, not really this):
Array(
[0] => rowset1,
[1] => rowset2,
...
)
Since you want the second set -the result from the SELECT-, you can consume the first one by calling nextRowset. That way, you'll be able to fetch the results from the 'important' set.
(Even though 'consume' might not be the right word for this, it fits for understanding purposes)
Executing two queries with one call is only allowed when you are using mysqlnd. Even then, you must have PDO::ATTR_EMULATE_PREPARES set to 1 when using prepared statements. You can set this using:
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1);
Alternatively, you can use $conn->exec($sql), which works regardless. However, it will not allow you to bind any data to the executed SQL.
All in all, don't execute multiple queries with one call.

mysql not updating in a while statement

I am trying to extract two fields from tables accesscode and student the update the table borrowers with the data that i have extracted from the previous tables.
$q=$db->query("SELECT regnum, accesscode FROM student,student_accesscode WHERE student.id=student_accesscode.studentid");
while($qd=$q->fetch(PDO::FETCH_ASSOC))
{
$access=$qd['accesscode'];
$regnum=$qd['regnum'];
$q2=$db->exec("UPDATE borrowers SET cardnumber='$access' WHERE cardnumber='$regnum'");
if($q2)
{
echo $access.' '. $regnum.'<br/>';
}
else
{
echo'erro....<br/>';
}
}
?>
Appending $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); before $db->query() probably gives you an Exception...
Prior to the $db->setAttribute, you will get this:
PDO::prepare(): SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
So, instead of fetch(), use fetchAll() with foreach loop, it will make you less insane.
Ref. from php.net
For a statement that you need to issue multiple times, prepare a PDOStatement
object with PDO::prepare() and issue the statement with PDOStatement::execute().
From the PHP exec page at http://www.php.net/manual/en/pdo.exec.php
I don't know if this is the problem, I always use prepare and execute. It could just be for performance reasons. Something to try anyway.

PDO/MySQL error on some queries (SQLSTATE[HY000])

In this snip-it of PHP code, I'm trying to access my database (dbconn is already defined)
public function get($statement) {
echo $statement . '<br/>';
$fetch = $this->dbconn->prepare($statement);
$fetch->execute();
$res = $fetch->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP);
return $res;
}
Some example outputs of the whole program are, with the query = "example1"
SELECT * FROM keywords WHERE 1
SELECT bin FROM bins WHERE place LIKE 'example1'
{"centralLat":0,"centralLon":0,"errors":[],"posts":[],"location":"example1"}
The code returns the intersection of posts a location (that has been hashed out into bins on a map) and the keywords. The code works fine, it does interpretation on the string and does the appropriate querys, finds intersections, yadda yadda. The problem is for some queries, PDO fails to execute the query
in this case of query = "example2"
SELECT * FROM keywords WHERE 1
SELECT bin FROM bins WHERE place LIKE 'example2' (this the the query that errors)
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: Invalid column index'
In the actual cases, it seems to consistently throw out these errors on SOME queries, but not all. For example: it will always run example1 successfully, but never run example2 successfully. But if I run either of the querys on phpMyAdmin, I cannot replicate the errors, and get the desired results.
The only notable difference, between example1 (and the like) and example2 (and the like) is that querys that work are LONGER (40+ characters), while queries that fail are SHORTER (39-)
Im not sure whats causing this error, any help would be appreciated.

PDO “Uncaught exception 'PDOException' .. Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll().”

I know this question has been asked many times, but I've read the answers to many of the questions and still cannot understand why I am receiving this error:
Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[HY000]: General error: 2014 Cannot execute queries while
other unbuffered queries are active. Consider using
PDOStatement::fetchAll(). Alternatively, if your code is only ever
going to run against mysql, you may enable query buffering by setting
the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.'
The first thing that is odd, is that I do not get an error on my localhost (wampserver), but I do get it on my web server. The php version on my localhost is 5.3.10, and on my web server it is 5.3.13.
I have read that the source of this error is making a query when data left in the buffer from a previous query. This is not the case for me -- I have echo'd out all of the data and I know for a fact that every row returned in a query is being fetched.
With that said, I have found that changing one of my queries to fetchAll instead of fetch fixes the problem, but it simply makes no since because I know that all of the rows returned are being read. When I used fetchAll for the query (it is being made in a loop), I printed out the array each loop, and only one item was in the array for each query in the loop.
One more piece of information. It's not the query that I changed to fetchAll (which makes the error go away) that throws the PDO error, there is another query later in my php file that throws the error. My file is basically like this:
... code ...
query 1
... code ...
loop
query 2
end loop
... code ...
query 3
If I comment out query 3, there is no error. If I comment out, or change to fetchAll, query 2, there is no error. query 1 has no affect whatsoever.
I would also like to add that I have tried adding LIMIT 1 to all of the queries on the page (at the same time), and the error is still there. I think this proves there is not unread data in the buffer, right?
I'm really confused, so I would appreciate your advice. Before someone asks, I can't post the full code for this, but here is a simplified version of my code:
$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
makeQuery($stmt, array(':par' => $var));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
for loop
makeQuery($stmt, array(':par' => $var));
$row2 = $stmt->fetch(PDO::FETCH_ASSOC);
... [use row2] ...
end for loop
$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
makeQuery($stmt, array(':par' => $var));
$row3 = $stmt->fetch(PDO::FETCH_ASSOC);
Here is makeQuery().
/**************************************************************************************************************
* Function: makeQuery *
* Desc: Makes a PDO query. *
* Pre conditions: The statement/query and an array of named parameters (may be empty) must be passed. *
* Post conditions: The PDO query is executed. Exceptions are caught, displayed, and page execution stopped. *
**************************************************************************************************************/
function makeQuery($stmt, $array, $errMsg = '')
{
try
{
$stmt->execute($array);
}
catch (PDOException $e)
{
print $errMsg != ''?$errMsg:"Error!: " . $e->getMessage() . "<br/>";
die();
}
}
Thanks for your help!
EDIT: I also tried doing the following after query 2 (since that seems to be the source of the problem:
$row2 = $stmt->fetch(PDO::FETCH_ASSOC); var_dump($row2);
The output was:
bool(false)
Have I stumbled across a PDO bug?
You need to fetch until a row fetch attempt fails. I know you may only have one row in the result set and think one fetch is enough, but its not (when you're using unbuffered queries). PDO doesn't know how many rows there are until it reaches the end, where it tries to fetch the next row, but it fails.
You probably have other statements where you didn't fully "fetch until a fetch failed". Yes, I see that you fetch until the fetch failed for one of the statements, but that doesn't mean you did it for all of them.
To clarify -
When you execute a query via execute(), you create a result set that must be fetched from the db into php. PDO can only handle 1 of these "result set in progress of being fetched" at a time (per connection). You need to completely fetch the result set, all the way to the end of it, before you can start fetching a different result set from a different call to execute().
When you "call fetch() until a fetch() fails", the fact that you reached the end of the results is internally noted by PDO when that final call to fetch() fails due to there being no more results. PDO is then satisfied that the results are fully fetched, and it can clean up whatever internal resources between php and the db that were established for that result set, allowing you to make/fetch other queries.
There's other ways to make PDO "call fetch() until a fetch() fails".
Just use fetchAll(), which simply fetches all rows, and so it will hit the end of the result set.
or just call closeCursor()
*if you look at the source for closeCursor(), the default implementation literally just fetches the rows and discards them until it reaches the end. It's written in c obviously, but it more or less does this:
function closeCursor() {
while ($row = $stmt->fetch()) {}
$this->stmtFullyFetched = true;
}
Some db drivers may have a more efficient implementation that doesn't require them to fetch lots of rows that nobody cares about, but that's the default way PDO does it. Anyway...
Normally you don't have these problems when you use buffered queries. The reason is because with buffered queries, right after you execute them, PDO will automatically fully fetch the db results into php memory, so it does the "call fetch() until a fetch() fails" part for you, automatically. When you later call fetch() or fetchAll() yourself, it's fetching results from php memory, not from the db. So basically, the result set is immediately fully fetched when using buffered queries, so there's no opportunity to have more than 1 "result set in progress of being fetched" at the same time (because php is single threaded, so no chance of 2 queries running at the same time).
Given this:
$sql = "select * from test.a limit 1";
$stmt = $dbh->prepare($sql);
$stmt->execute(array());
Ways to fully fetch the result set (assuming you only want the first row):
$row = $stmt->fetch();
$stmt->closeCursor();
or
list($row) = $stmt->fetchAll(); //tricky
or
$row = $stmt->fetch();
while ($stmt->fetch()) {}
After struggling with this issue for days, I finally found that this worked for me:
$db = new PDO ($cnstring, $user, $pwd);
$db->setAttribute (PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
This also happen if you are trying to fetch a non SELECT query (Eg - UPDATE/INSERT/ALTER/CREATE). Make sure to use fetch or fetchAll only for SELECT queries.
Possible Duplicate Answer/Question

Categories