So far I have been using PDO->bindParam however while reading the manual I found PDO->bindValue from what I can tell PDO->bindValue passes by value where as PDO->bindParam passes by reference, is this the only difference?
$modThread = db()->prepare("UPDATE `threads` SET `modtime` = UNIX_TIMESTAMP( ) WHERE `threadid` =:id LIMIT 1");
while(something)
{
$modThread->bindParam(':id', $thread);
$modThread->execute();
//*******************HERE********************//
}
Again while reading the manual I found: PDO->closeCursor should I place it where marked? Is it optional/automatically called? Seems only certain drivers need it. Will calling it on a driver that doesn't need/support it cause errors? How about MySQL?
This isn't true. If you find yourself needing to use closeCursor, one of the most optimal times is for insert/update/delete commands, and rarely for SELECT statements for which you have already fetched results.
For example, if you select all records from a table, then issue $stmt->fetch(), this actually accomplishes the goal for closeCursor immediately as the rows are now no longer in an unfetched status.
From the manual:
This method is useful for database drivers that do not support executing a PDOStatement object when a previously executed PDOStatement object still has unfetched rows. If your database driver suffers from this limitation, the problem may manifest itself in an out-of-sequence error.
When you will really need closeCursor is during any of the following instances:
If your DB driver doesn't allow for a new stmt to be executed while unfetched rows are available from the previous execute
You have multiple prepared statements and would like to execute them one-after-another ($stmt1->execute(); $stmt->closeCursor(); $stmt2->execute(); $stmt2->closeCursor(); $stmt3...etc)
You have multiple stmts that must execute insert/update/delete inside the same block. This is true because, while you dont get mysql row results back, you DO get number of affected rows result set back (which is still a result).
When using transactions
When you want to issue select-style prepared statements and execute them, but not retrieve the data until later
When you don't need the closeCursor statement:
If you have already fetched the rows (as with $stmt->fetch()) before your next statement is to be executed. At this point the rows are in a "fetched" state and frees up the driver to execute new statements.
Just as useful for closing a cursor is unset() (ie: unset($stmt)) and setting the statement to null ($stmt = null), opening the doors for the built-in Garbage Collector to clear everything up.
See the manual for more information: http://php.net/manual/en/pdostatement.closecursor.php
The 'recurring' bindParam() here is not really necessary:
$thread = 0;
$modThread->bindParam(':id', $thread);
while($thread < 20)
{
$thread++;
$modThread->execute(); //executing with the new value, which you couldn't do with bindValue
}
You don't need a closeCursor() when there is no resultset (i.e, only with SELECT s or procedures giving results back) , but usually I've already done a fetchAll somewhere in a previous statement / row.
Related
For the sake of avoiding unnecessary information, roughly my code flows as follows:
$db = new PDO(DSN, DB_USER, DB_PW);
$sql1 = "SELECT * FROM Table1";
// fetching the first result
$stt1 = $db->prepare($sql1);
if ($stt1->execute()) {
$result = $stt1->fetch(PDO::FETCH_ASSOC);
}
// doing update in the middle by using the SAME $db object, but different statement variable
$sql2 = "UPDATE Table1 SET field1 = 'footest1' WHERE id = 1";
$stt2 = $db->prepare($sql2);
$stt2->execute();
// fetching the next result
$result = $stt1->fetch(PDO::FETCH_ASSOC);
Ok, I ran this, and to my surprise, when I am fetching my next result, I get false. Does preparing another statement ($stt2) in the middle interrupt my already created $stt1?
And I have 15+ records on that table.
Update: It seems like the execute method of the statement object is the reason my second fetch is returning false. For this to work, calling $stt1->execute() again before fetching the second time solves this problem... But this shows that there is some connection via the execute method between all the statement object?
Different databases have different constraints. This is not an issue with PHP or PDO, but with the database connection.
While you can rely on any DB connector to support at least one cursor with pending rows, many databases will limit you at one, and require you to either fully fetch or explicitly close the underlying cursor before executing a new statement.
This is indeed a property of the database connection, as all PDO statements are bound to one. (Where would they fetch the data from if they didn't remain bound to the database connection?) If you're using a database that only supports one open prepared statement at a time, you'll have no choice but to either serialize your accesses or open multiple connections to the database. You might also want to take a look at the closeCursor method of the PDOStatement class.
This question already has answers here:
PDO Cannot execute queries while other unbuffered queries are active
(4 answers)
Closed 5 years ago.
In PHP, when I call a MySQL stored procedure using PDO, and then another PDO query, just like this:
$dbh = new PDO('mysql:host=localhost;dbname=db1','user1','password1');
$query = "CALL get_token()";
$stmt = $dbh->query($query);
$array = $stmt->fetchAll();
$query = "SELECT * FROM `table1`";
$stmt = $dbh->query($query);
$array = $stmt->fetchAll();
The MySQL stored procedure is about like this:
CREATE PROCEDURE `get_token`()
BEGIN
DECLARE token CHAR(64);
DECLARE expire SMALLINT;
SELECT `token`, `expire` INTO token, expire FROM `token`;
SELECT token, expire;
END$$
And I got the following error message (using try...catch to catch it):
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.
Even if I followed the instructions described in the above error message (that means using fetchAll() and setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute), I still got the same error message.
If I change the first query to a normal SELECT SQL query, instead of a stored procedure, I won't get this error. So it seems that the problem arises from the stored procedure.
But how can I fix this?
That's because you are not freeing the cursor of the first query. It still waits for another fetchAll. From http://php.net/manual/en/pdo.query.php
If you do not fetch all of the data in a result set before issuing your next call to PDO::query(), your call may fail. Call PDOStatement::closeCursor() to release the database resources associated with the PDOStatement object before issuing your next call to PDO::query().
So $stmt->closeCursor(); after first $array = $stmt->fetchAll(); should be sufficient.
You should use either PDO::MYSQL_ATTR_USE_BUFFERED_QUERY OR fetchAll()
as they conflict
According to the documentation :
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (integer)
If this attribute is set to TRUE on a PDOStatement, the MySQL driver will use the buffered versions of the MySQL API. If you're writing portable code, you should use PDOStatement::fetchAll() instead.
In other words,
fetchAll() tries to close the buffer
but PDO::MYSQL_ATTR_USE_BUFFERED_QUERY keeps it open
I am migrating all my mysqli queries to STORED PROCEDURE.
It should be as easy as changing one line in the mysqli call, howver, the two following codes give different results:
Regular query, which works correctly:
$query = $this->mysqli->query("SELECT DISTINCT ID FROM user
WHERE
MATCH (name) AGAINST ('* *$sanitized* *') ");
if ($query) {
$nrows = $query -> num_rows;
if ($nrows > 0) {
$searchResult = 'We found '. $nrows .' results';
}
}
CALL to PROCEDURE, which returns a "fetch_array() on boolean" error:
$query = $this->mysqli->query("CALL myfunction('.$sanitized.')");
where the procedures is defined as:
DELIMITER $$
CREATE PROCEDURE myfunction (sanitized VARCHAR(124))
BEGIN
SELECT DISTINCT ID FROM user
WHERE
MATCH (name) AGAINST ('* *sanitized* *');
END
$$
DELIMITER ;
I can't find a solution and it seems that no one has a similar issue in this forum.
consider Prepared Statements used with concat() as they often are.
DROP PROCEDURE if exists myStoredProc101;
DELIMITER $$
CREATE PROCEDURE myStoredProc101
( pSanitized VARCHAR(124)
)
BEGIN
set #mySql:=concat("SELECT DISTINCT ID FROM user where match(name) against ('* *",pSanitized,"* *')");
PREPARE stmt1 FROM #mySql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END
$$
DELIMITER ;
Your stored proc had no chance of working as it wasn't even using your parameter. What you did was bury something inside of a string literal. Also, varchar(124) is a bit odd :p
About the only success people have with prepared statements is with using a User Variable (with an #) versus failed attempts of using Local Variables (from DECLARE). So, that may save you a few hours of head banging in the future.
From the PHP Manual Page Stored Procedures:
Handling result sets
Stored procedures can return result sets. Result sets returned from a
stored procedure cannot be fetched correctly using mysqli_query. The
mysqli_query function combines statement execution and fetching the
first result set into a buffered result set, if any. However, there
are additional stored procedure result sets hidden from the user which
cause mysqli_query to fail returning the user expected result sets.
Result sets returned from a stored procedure are fetched using
mysqli_real_query or mysqli_multi_query. Both functions allow fetching
any number of result sets returned by a statement, such as CALL.
Failing to fetch all result sets returned by a stored procedure causes
an error.
As for calling the stored proc from mysqli, please take a look at the Answer from Pablo Tobar. It does not look especially pleasant with many variables, but that seems to be where it is at. Spoiler Alert: use mysql variables, not PHP variables.
Granted, Pablo was not returning a resultset, but rather writing to an OUT var in the stored proc. Perhaps you need to do what he did for the IN parameters, and call multi_query(), then a store_result(), then a fetch_all() (in short, the PHP reference a page up).
Alternatively, a call would be made as done by Palladium here.
In either case, case must be taken to avoid the known vulnerability of passing SQL Injection over to stored procedure routines.
I new there are lots of answer as well as accepted answers related to this question but none of them solve my problem. Still I am getting this error.
Procedures:
CREATE PROCEDURE getAllProducts()
BEGIN
SELECT * FROM products;
END //
CREATE PROCEDURE getAllCategories()
BEGIN
SELECT * FROM category;
END //
Connection & calling:
$link = mysql_connect($host,$username,$password) or die(mysql_error());
mysql_select_db($database, $link) or die(mysql_error());
$allProducts = mysql_query("CALL getAllProducts");
while($row = mysql_fetch_array($allProducts)) { }
$allCategory = mysql_query("CALL getAllCategories");
while($row = mysql_fetch_array($allCategory)) { }
I've even called mysql_free_result($allProducts) before executing the next query. But nothing happens.
mysql_get_client_info() return mysqlnd 5.0.5-dev - 081106 - $Revision: 1.3.2.27 $
I found that the problem only arises if I run two queries.
As the MySQL-Documentation for 'Commands out of sync' points out:
[...] It can also happen if you try to execute two queries that return data
without calling mysql_use_result() or mysql_store_result() in between.
The Documentation for mysql_use_result() says e.g.:
After invoking mysql_query() or mysql_real_query(), you must call
mysql_store_result() or mysql_use_result() for every statement that
successfully produces a result set (SELECT, SHOW, DESCRIBE, EXPLAIN,
CHECK TABLE, and so forth). You must also call mysql_free_result()
after you are done with the result set.
Basically you need to tell your client what it should do with the result.
Well, usually this error occurs because there are still results pending from the query. There are mysqli_store_result and mysqli_free_result functions available. Since you are using mysql and mysql extension does not have such functions, you can try closing the connection after executing the first procedure and establishing the connection again to execute next procedure. Though this is not the perfect solution, but it will work in your case.
mysql_close($connection);
$connection = mysql_connect("localhost","username","password");
You can also try
mysql_free_result($allProducts);
Stored procedures always return an extra result set with errors/warnings information. As such, your stored procedures return multiple result sets (the actual result set from your select query and the extra errors/warnings result set). Calling mysql_fetch_array in a loop you only saturate one of them, leaving the other still pending, causing the error you see.
I don't know how to fix it with vanilla mysql_ library, with mysqli_ you can issue mysqli_multi_query, and then only use the first result set. See the example in the docs.
It's not a fix per se, but if you insist on staying with mysql_* functions, and assuming that you actually want to work with more complicated stored procedures (i.e. that the ones you supplied are just a simplified example) - you can change the stored procedures code to write the resultset into a temporary table (e.g. tmp_getAllProducts) instead of returning it, and then SELECT from it in your PHP.
This is what worked for me a while back when I was stuck with mysql_* and couldn't upgrade...
This is a known limitation of the mysql extension. You must either not use more than one stored procedure per connection or upgrade to mysqli.
https://bugs.php.net/bug.php?id=39727
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