I have a function which does the following things
$stmt = mysqli_prepare..
//check for preparation failure
mysqli_stmt_bind_param
//Check for bind error
mysqli_stmt_execute
//Check for execution error
mysqli_stmt_store_result //Problem is here
//Check for error
return $stmt
I have these steps in a function because I need it in two placeds the same piece of code. After getting the stmt, I need to check number of rows [mysqli_Stmt_num_rows]. So only I have that stored result stmt at the end of the function.
After checking the rows, i need to get a column value from the result. So I tried to execute mysqli_stmt_get_result on the returned statement object. It fails.
I just removed the store result part and just returned the executed stmt from the function. Get result works then. But I cannot count rows until I store the result.
Then I have done simple trick that tried to clone the returned statement. Unfortunately, mysqli stmt is not clonable.
How do I solve this issue?
mysqli_result class also has a property called num_rows
$result = $stmt->get_result();
echo $result->num_rows;
If you want to stick to procedural style, which I do not recommend, there is a corresponding function mysqli_num_rows($result)
Related
I have the following code:
$post_title = "Some dynamic POST data";
$stmt = $conn->prepare("SELECT * FROM `posts` WHERE title=? ");
$stmt->bind_param("s", $post_title);
$stmt->execute();
$rows = $stmt->get_result()->num_rows;
$stmt->get_result()->free_result(); // throws error: commands out of sync
$stmt = $conn->prepare("... some new condition");
$stmt->bind_param(...);
$stmt->execute();
$rows = $stmt->get_result()->num_rows;
I know I can simply use $stmt->free_result, but the php docs https://www.php.net/manual/en/mysqli-result.free.php mention you can use free_result on mysqli_result object as well so why can't we use it on mysqli_stmt->get_result(), which is a result object as well?
mysqli_stmt::get_result() is not idempotent. You can only call it once. If you call it again you will get an error:
Commands out of sync; you can't run this command now
This error has nothing to do with free_result() which you probably should not be using in the way you showed anyway.
You need to store the result in a variable and only then you can perform all the operations you want.
$stmt = $mysqli->prepare("SELECT ? ");
$stmt->bind_param("s", $post_title);
$stmt->execute();
$result = $stmt->get_result();
$rows = $result->num_rows;
$result->free_result();
I would also recommend that you don't ever use free_result().
Explanation:
When mysqli makes a call to MySQL server to execute a prepared statement, the server will produce a result set. This is not the outcome of the EXECUTE call, but the actual output of the SQL. By default, mysqli prepared statements are running in unbuffered mode, which means that upon execution PHP will not fetch the results from the server. You must use one of the functions to retrieve it from the MySQL server. You can do it row by row using fetch(), you can tell PHP to buffer the result set in internal memory using store_result() or you can ask PHP to buffer the result encapsulated in a mysqli_result object using get_result(). The connection line will be busy as long as there are pending rows on the MySQL server.
Once you fetch the results from MySQL, there is nothing else to read. If you try to read again, you will get OOS error mentioned above. This is why you can't call get_result() multiple times and expect the same result. Once the data is fetched to PHP, it's gone from the line, and is now stored in PHP memory. The prepared statement can of course be executed again and a new result set will be produced.
See also mysqli - Executing statements.
The following code:
$con = ConnectDB::getConnection();
if ($stmt = $con->prepare('---SQL here with 1 param---')) {
$stmt->bind_param('i', $this->id);
$stmt->execute();
$stmt->bind_result($device_id, $device_identifier);
while ($stmt->fetch()) {
$device = new Device($device_identifier, $device_id);
$all_devices[] = $device;
}
fetches an empty result? The first time through the loop, the $device_id and $device_identifier is empty, but the fetch() method returns true so it runs. HOWEVER - the SECOND iteration actually contains my result.
I could simply check if they are empty and ignore them, but i really can't understand why it is returning an empty pair? I tried running the SQL directly on the DB and it just returns 1 result row?
Any ideas?
As you may imagine, MySQLi prepared statement fetch does not return an empty first row. As such a behavior would make a very little sense.
You need to learn to create a correct proof for your assertions.
The code you posted is incorrect one. For some reason you are trying to judge a matter by indirect consequences. Why not to examine the matter itself?
$con = ConnectDB::getConnection();
if ($stmt = $con->prepare('---SQL here with 1 param---')) {
$stmt->bind_param('i', $this->id);
$stmt->execute();
$stmt->bind_result($device_id, $device_identifier);
$stmt->fetch();
var_dump($device_identifier, $device_id);
-- this code will tell you unambiguously, if your assumption on empty result was right or wrong.
After learning that, you may start debugging, to find a spot in your own code that is responsible for the current behavior.
Hope it helps.
This question already has an answer here:
Why does mysqli num_rows always return 0?
(1 answer)
Closed 1 year ago.
after reading tons of threads, tutorials and whatever - I feel like posting here and hope that someone can help me.
I tried out every advice I could get, but it's still not working.
Here is my code :
$prep_stmt = "SELECT id, DATE_FORMAT(added,'%d.%M'), title FROM offers ORDER BY added DESC LIMIT ?, ?;";
$stmt = $mysqli->prepare($prep_stmt);
$stmt->bind_param ('ii',$lowlimit, $page);
$stmt->execute();
$stmt->bind_result($id, $added, $title);
while ($stmt->fetch()) {
... # some random output here
}
$count = $stmt->num_rows;
echo "c -> ". $count;exit;
I always get "c -> 0" ... but there IS output already ... so what am I doing wrong ? :/
You need to call the store_result() method before accessing the num_rows property.
From a comment on PHP manual documentation:
If you do not use mysqli_stmt_store_result(), and immediately call this function after executing a prepared statement, this function will usually return 0 as it has no way to know how many rows are in the result set as the result set is not saved in memory yet.
mysqli_stmt_store_result() saves the result set in memory thus you can immedietly use this function after you both execute the statement AND save the result set.
Your code should look like:
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($id, $added, $title);
while ($stmt->fetch()) {
# some random output here...
}
$count = $stmt->num_rows;
Taken from php.net You have to store it first.
$stmt->store_result();
$count = $stmt->num_rows
From the PHP manual, first comment http://fr2.php.net/manual/fr/mysqli-stmt.num-rows.php :
Please be advised, for people who sometimes miss to read this important Manual entry for this function:
If you do not use mysqli_stmt_store_result( ), and immediatley call
this function after executing a prepared statement, this function will
usually return 0 as it has no way to know how many rows are in the
result set as the result set is not saved in memory yet.
mysqli_stmt_store_result( ) saves the result set in memory thus you
can immedietly use this function after you both execute the statement
AND save the result set.
If you do not save the result set but still want to use this function
you have to actually loop through the result set one row at a time
using mysqli_stmt_fetch( ) before using this function to determine the
number of rows.
A thought though, if you want to determine the number of rows without
storing the result set and after looping through it, why not just
simply keep an internal counter in your loop every time a row is
fetched and save the function call.
In short, this function is only really useful if you save the result
set and want to determine the number of rows before looping through
it, otherwise you can pretty much recreate its use like I suggested.
I writing an accounting website which has quite a few MySQL statements in it. To prevent SQL injection I use prepared statements for any data which is put in by the user.
In order to prevent having to write the steps of preparing and binding statements I have the following function:
function executeSql($mysqli,$query_string,$params=null,$paramtypes=null){
$nr_params=strlen($paramtypes);
$query_type = substr($query_string,0,3);
$stmt = $mysqli->prepare($query_string);
$queryParams[] = $paramtypes;
$counter=1;
if($nr_params>1){
while($counter<=$nr_params){
$queryParams[$counter]=&$params[$counter-1];
$counter++;
}
} else {
$queryParams[1]=&$params;
}
// Actual binding of the statement. Taking into account a variable numbers of '?' in the query string.
call_user_func_array(array($stmt,'bind_param'),$queryParams);
// Execution of the statement
$stmt->execute();
// Part where i'd like to have a substitute for:
$result = $stmt->get_result();
return $result;
}
In the last part I'd like to return the result because then using the result I can treat each row. The problem is that the mysqlnd driver is not installed on the production server so the function $stmt->get_result() cannot be used. I tried to bind the result into variables but then again, every query returns a different number of columns.
Anyone has an idea how to tackle this?
So in summary (in response to the comments):
How can I retrieve a results object of an executed MySQLi statement while I cannot use $stmt->get_result();
Kind regards,
EJG
PS I know the code is not flawless, e.g. if strings are used as variables to bind to the statement but that is easily fixed.
UPDATE:
I came across the function $stmt->result_metadata(); Although supposedly the function name suggests only the meta data the php documentation states that:
"If a statement passed to mysqli_prepare() is one that produces a result set, mysqli_stmt_result_metadata() returns the result object"...
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