Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 661
Nate Avatar asked Jul 15 '12 14:07

Nate


2 Answers

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".

  1. Just use fetchAll(), which simply fetches all rows, and so it will hit the end of the result set.
  2. 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()) {}
like image 89
goat Avatar answered Sep 20 '22 05:09

goat


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);
like image 26
Chris King Avatar answered Sep 19 '22 05:09

Chris King