mysqli_multi_query: need to check single result? - php

I'm using this code to insert a record with a unique id and then return the id-string just created:
$sql ="set #id=UUID();";
$sql .="INSERT INTO `items` (`id`,`parent`, `text`, `type`) VALUES(#id,'".$parent."', '".$text."', '".$type."');";
$sql .="select #id;";
if (mysqli_multi_query($conn, $sql)) {
while (mysqli_more_results($conn)) {
mysqli_use_result($conn);
mysqli_next_result($conn);
}//while
$result = array('id' => mysqli_store_result($conn)->fetch_row()[0]);
}//if
If everything works as it should, the three queries should return:
1/true (I guess)
1/true
object
I never used this function before and I was wondering: what happens if the insert query fails?
The third query will still be executed?
And in that case, how can I check the result of the second query?
Edit:
Or in general:
having a set of 10 queries, in case of failure how can I check which one has failed?

After some test...
Let's assume we have the following 3 queries:
$sql ="INSERT INTO `items` (`id`,`parent`) VALUES ('id',WRONG_NO_QUOTES);";
$sql .="INSERT INTO `items` (`id`,`parent`) VALUES ('id','right2');";
$sql .="INSERT INTO `items` (`id`,`parent`) VALUES ('id','right3');";
Using the code provided by the manual...
if (mysqli_multi_query($conn,$sql)) {
do {
/* store first result set */
if ($result = mysqli_store_result($conn)) {
while ($row = $result->fetch_row()) {
printf("%s\n", $row[0]);
}
$result->free();
}
/* print divider */
if (mysqli_more_results($conn)) {
printf("-----------------\n");
}
} while (mysqli_next_result($conn));
}
//...plus this....
if(mysqli_errno($conn)){
echo 'mysqli_errno:';
var_dump(mysqli_errno($conn));
echo'<br>';
}
...the output is:
mysqli_errno:
[...]test.php:39:int 1146
...and there are no changes to the database.
Conclusion:
When an error occours, the next queries are NOT executed.
AND the error can be easly catched at the end of the do/while loop.
EDIT
The code above generates a strict standards notice.
I edited it to avoid the notice and get a (basic) backtrace of the error:
$n=1;
$i=1;
if (mysqli_multi_query($conn,$sql)) {
do {
/* store first result set */
if ($result = mysqli_store_result($conn)) {
while ($row = $result->fetch_row()) {
echo $row[0];
}
$result->free();
}
/* print divider */
if (mysqli_more_results($conn)) {
echo "<hr>";
$n++;
mysqli_next_result($conn);
}else{$i=0;}
} while ($i>0);
}
if(mysqli_errno($conn)){
echo 'mysqli error on query number '.$n;
var_dump(mysqli_errno($conn));
}

Since my actual problem was to avoid competition between queries and get the right identifier (check: this question) I realized there is a simpler (and maybe better) way to reach the desired result (here using pdo extension):
$db = new PDO ("mysql:host=$hostname;dbname=$dbname", $user, $pass);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//No user defined variable here inside (safe)
$identifier=db->query("SELECT UUID();")->fetch()[0];
Having the identifier stored into a PHP variable, then I can treat every following query separately using pdo and prepared statements.

Related

Fetch multiple rows in mysql single query in php with different id

I have different id's, i am getting the values of these id from users
$id=array();
$id[0]=$_GET["id0"];
$id[1]=$_GET["id1"];
$id[2]=$_GET["id2"];
now to fetch data from database i am using following query:
for($j=0;$j<count($id);$j++)
{
$res=mysql_query("SELECT * FROM mutable WHERE id='$id[$j]'")
while($row=mysql_fetch_array($res))
{
$row[]=array("email"=>$row[2],"name"=>$row[3],"address"=>$row[5]);
echo JSON_encode($row);
}
}
now i am getting proper result from this query using for loop but the result is not in proper JSON format, is there any way to do it more efficentyly and getting proper result in JSON array and JSON object format
Place json_encode() outside of your loops.
Let's modernize and refine things...
*Unfortunately prepared statements that use an IN clause suffer from convolution. pdo does not suffer in the same fashion.
Code: (untested)
if(isset($_GET['id0'],$_GET['id1'],$_GET['id2'])){
$params=[$_GET['id0'],$_GET['id1'],$_GET['id2']]; // array of ids (validation/filtering can be done here)
$count=count($params); // number of ids
$csph=implode(',',array_fill(0,$count,'?')); // comma-separated placeholders
$query="SELECT * FROM mutable WHERE id IN ($csph)";
$stmt=$mysqli->prepare($query); // for security reasons
array_unshift($params,str_repeat('s',$count)); // prepend the type values string
$ref=[]; // add references
foreach($params as $i=>$v){
$ref[$i]=&$params[$i]; // pass by reference as required/advised by the manual
}
call_user_func_array([$stmt,'bind_param'],$ref);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_array(MYSQLI_NUM))
$rows=["email"=>$row[2],"name"=>$row[3],"address"=>$row[5]];
}
$stmt->close();
echo json_encode($rows);
}
Three things:
Always, always, always used prepared statements and bound parameters when dealing with untrusted (i.e., $_GET) input. Just do it.
As regards your problem with JSON, you only need to run json_encode once:
$results = [];
for($j=0;$j<count($id);$j++) {
...
while($row=mysql_fetch_array($res)) {
results[] = ...
}
}
json_encode( $results );
Use a single SQL statement, since you have a known number of IDs to collect:
$dbh = new PDO($dsn, $user, $password);
$sql = "SELECT * FROM mutable WHERE id IN (?, ?, ?)";
$sth = $dbh->prepare( $sql );
foreach ( $sth->execute( [$_GET['id0'], $_GET['id1'], $_GET['id2']] ) as $row ) {
...
This is more efficient then multiple round trips to the database. For this contrived case it probably doesn't matter, but getting into good habits now will serve you in the long run.
you have used $row wrongly, declare array variable outside of loop like
$json_response = array();
for($j=0;$j<count($id);$j++) {
$res=mysql_query("SELECT * FROM mutable WHERE id='$id[$j]'")
while($row=mysql_fetch_array($res)) {
$json_response[]=array("email"=>$row[2],"name"=>$row[3],"address"=>$row[5]);
echo json_encode($json_response); // not good to echo here
}
}
// echo json_encode($json_response); // ideally echo should be here

Querying unique column data

EDIT
Why is only the first row being echoed and not all rows that meet the WHERE condition?
$sql="SELECT from_name, to_name FROM private_messages WHERE from_id='$var' OR to_id='$var'" ;
$sql2 = mysql_query($sql);
$row = mysql_fetch_array($sql2);
echo $row['from_name'];
echo $row['to_name'];
Use a "while fetch" loop.
Here's an example of the pattern, using the mysqli interface.
(NOTE: The PHP mysql interface is deprecated. New development should use PDO or mysqli.)
if ($sth = mysqli_query($con, $sql) {
//echo "#debug: query returned a result set";
while ($row = mysqli_fetch_assoc($sth)) {
//echo "#debug: fetched next row";
echo $row['from_name'];
}
//echo "#debug: exited while loop, last row already fetched";
} else {
//echo "#debug: query execution returned FALSE, handle error";
}
This is the same as the pattern used with the deprecated mysql interface. (Is there some reason you are using that interface? N.B. Do not mix mysqli_ and mysql_ functions.

PHP Commands Out of Sync error

I am using two prepared statements in PHP/MySQLi to retrieve data from a mysql database. However, when I run the statements, I get the "Commands out of sync, you can't run the command now" error.
Here is my code:
$stmt = $mysqli->prepare("SELECT id, username, password, firstname, lastname, salt FROM members WHERE email = ? LIMIT 1";
$stmt->bind_param('s', $loweredEmail);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($user_id, $username, $db_password, $firstname, $lastname, $salt);
$stmt->fetch();
$stmt->free_result();
$stmt->close();
while($mysqli->more_results()){
$mysqli->next_result();
}
$stmt1 = $mysqli->prepare("SELECT privileges FROM delegations WHERE id = ? LIMIT 1");
//This is where the error is generated
$stmt1->bind_param('s', $user_id);
$stmt1->execute();
$stmt1->store_result();
$stmt1->bind_result($privileges);
$stmt1->fetch();
What I've tried:
Moving the prepared statements to two separate objects.
Using the code:
while($mysqli->more_results()){
$mysqli->next_result();
}
//To make sure that no stray result data is left in buffer between the first
//and second statements
Using free_result() and mysqli_stmt->close()
PS: The 'Out of Sync' error comes from the second statement's '$stmt1->error'
In mysqli::query If you use MYSQLI_USE_RESULT all subsequent calls will return error Commands out of sync unless you call mysqli_free_result()
When calling multiple stored procedures, you can run into the following error: "Commands out of sync; you can't run this command now".
This can happen even when using the close() function on the result object between calls.
To fix the problem, remember to call the next_result() function on the mysqli object after each stored procedure call. See example below:
<?php
// New Connection
$db = new mysqli('localhost','user','pass','database');
// Check for errors
if(mysqli_connect_errno()){
echo mysqli_connect_error();
}
// 1st Query
$result = $db->query("call getUsers()");
if($result){
// Cycle through results
while ($row = $result->fetch_object()){
$user_arr[] = $row;
}
// Free result set
$result->close();
$db->next_result();
}
// 2nd Query
$result = $db->query("call getGroups()");
if($result){
// Cycle through results
while ($row = $result->fetch_object()){
$group_arr[] = $row;
}
// Free result set
$result->close();
$db->next_result();
}
else echo($db->error);
// Close connection
$db->close();
?>
I hope this will help
"Commands out of sync; you can't run this command now"
Details about this error can be found in the mysql docs. Reading those details makes it clear that the result sets of a prepared statement execution need to be fetched completely before executing another prepared statement on the same connection.
Fixing the issue can be accomplished by using the store result call. Here is an example of what I initially was trying to do:
<?php
$db_connection = new mysqli('127.0.0.1', 'user', '', 'test');
$post_stmt = $db_connection->prepare("select id, title from post where id = 1000");
$comment_stmt = $db_connection->prepare("select user_id from comment where post_id = ?");
if ($post_stmt->execute())
{
$post_stmt->bind_result($post_id, $post_title);
if ($post_stmt->fetch())
{
$comments = array();
$comment_stmt->bind_param('i', $post_id);
if ($comment_stmt->execute())
{
$comment_stmt->bind_result($user_id);
while ($comment_stmt->fetch())
{
array_push($comments, array('user_id' => $user_id));
}
}
else
{
printf("Comment statement error: %s\n", $comment_stmt->error);
}
}
}
else
{
printf("Post statement error: %s\n", $post_stmt->error);
}
$post_stmt->close();
$comment_stmt->close();
$db_connection->close();
printf("ID: %d -> %s\n", $post_id, $post_title);
print_r($comments);
?>
The above will result in the following error:
Comment statement error: Commands out of sync; you can't run this command now
PHP Notice: Undefined variable: post_title in error.php on line 41
ID: 9033 ->
Array
(
)
Here is what needs to be done to make it work correctly:
<?php
$db_connection = new mysqli('127.0.0.1', 'user', '', 'test');
$post_stmt = $db_connection->prepare("select id, title from post where id = 1000");
$comment_stmt = $db_connection->prepare("select user_id from comment where post_id = ?");
if ($post_stmt->execute())
{
$post_stmt->store_result();
$post_stmt->bind_result($post_id, $post_title);
if ($post_stmt->fetch())
{
$comments = array();
$comment_stmt->bind_param('i', $post_id);
if ($comment_stmt->execute())
{
$comment_stmt->bind_result($user_id);
while ($comment_stmt->fetch())
{
array_push($comments, array('user_id' => $user_id));
}
}
else
{
printf("Comment statement error: %s\n", $comment_stmt->error);
}
}
$post_stmt->free_result();
}
else
{
printf("Post statement error: %s\n", $post_stmt->error);
}
$post_stmt->close();
$comment_stmt->close();
$db_connection->close();
printf("ID: %d -> %s\n", $post_id, $post_title);
print_r($comments);
?>
A couple things to note about the above example:
The bind and fetch on the statement still works correctly.
Make sure the results are freed when the processing is done.
For those of you who do the right thing and use stored procedures with prepared statements.
For some reason mysqli cannot free the resources when using an output variable as a parameter in the stored proc. To fix this simply return a recordset within the body of the procedure instead of storing the value in an output variable/parameter.
For example, instead of having SET outputVar = LAST_INSERT_ID(); you can have SELECT LAST_INSERT_ID(); Then in PHP I get the returned value like this:
$query= "CALL mysp_Insert_SomeData(?,?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("is", $input_param_1, $input_param_2);
$stmt->execute() or trigger_error($mysqli->error); // trigger_error here is just for troubleshooting, remove when productionizing the code
$stmt->store_result();
$stmt->bind_result($output_value);
$stmt->fetch();
$stmt->free_result();
$stmt->close();
$mysqli->next_result();
echo $output_value;
Now you are ready to execute a second stored procedure without having the "Commands out of sync, you can't run the command now" error. If you were returning more than one value in the record set you can loop through and fetch all of them like this:
while ($stmt->fetch()) {
echo $output_value;
}
If you are returning more than one record set from the stored proc (you have multiple selects), then make sure to go through all of those record sets by using $stmt->next_result();

How to get all errors after calling to mysqli_multi_query?

I'm using this code:
$mysqli = new mysqli(...);
$sql = file_get_contents("my_sql_file.sql");
$result = $mysqli->multi_query($sql);
if (!$result)
report_error(); //my function
while ($mysqli->more_results()) {
$result = $mysqli->next_result();
if (!$result)
report_error();
}
However the 'while' loop in the code above turned out to be an infinite loop. Anything wrong?
Actually your code doesn't really make sense. The proper way to handle multiqueries is the following (see php manual)
if ($mysqli->multi_query($query)) {
do {
// store first result set
if ($result = $mysqli->store_result()) {
while ($row = $result->fetch_row()) {
// do something with the row
}
$result->free();
}
else { error_report(); }
} while ($mysqli->next_result());
}
else { error_report(); }
The code provided in the question reaches to an infitie loop because "If your second or late query returns no result or even if your query is not a valid SQL query, more_results(); returns true in any case.", see this note on php.net: http://us3.php.net/manual/en/mysqli.multi-query.php#104076
And further more, mysqli_more_results always returns true in the code because the results are not discarded, must call mysqli_store_results to discard result after each call to mysqli_next_result. See: http://us3.php.net/manual/en/mysqli.multi-query.php#91677
There is no official way to catch all errors when executing MySQL text (multi-commands separated by semicolons) by mysqli_multi_query. The function mysqli_multi_query will stop execution when it faces a bad SQL command, so it is only possible to catch the first error (no matter where the error occurs, in the first SQL command or any other SQL command in the SQL text).
Related to Jon's answer to this question: When does mysqli_multi_query stop execution?
And as noted in http://www.php.net/manual/en/mysqli.multi-query.php#106126 The first error can be catched by scanning mysqli_next_result coz: $mysqli->next_result() will return false if it runs out of statements OR if the next statement has an error.
Finally the answer is that results must be discarded after calling to mysqli_next_result using mysqli_store_result:
$mysqli = new mysqli(...);
$sql = file_get_contents("my_sql_file.sql");
$result = $mysqli->multi_query($sql);
if (!$result)
report_error(); //my function
while ($mysqli->more_results()) {
$result = $mysqli->next_result();
//important to make mysqli_more_results false:
$discard = $mysqli->store_result();
if (!$result)
report_error();
}

PHP MySQLi num_rows Always Returns 0

I have built a class which leverages the abilities of PHP's built-in MySQLi class, and it is intended to simplify database interaction. However, using an OOP approach, I am having a difficult time with the num_rows instance variable returning the correct number of rows after a query is run. Take a look at a snapshot of my class...
class Database {
//Connect to the database, all goes well ...
//Run a basic query on the database
public function query($query) {
//Run a query on the database an make sure is executed successfully
try {
//$this->connection->query uses MySQLi's built-in query method, not this one
if ($result = $this->connection->query($query, MYSQLI_USE_RESULT)) {
return $result;
} else {
$error = debug_backtrace();
throw new Exception(/* A long error message is thrown here */);
}
} catch (Exception $e) {
$this->connection->close();
die($e->getMessage());
}
}
//More methods, nothing of interest ...
}
Here is a sample usage:
$db = new Database();
$result = $db->query("SELECT * FROM `pages`"); //Contains at least one entry
echo $result->num_rows; //Returns "0"
exit;
How come this is not accurate? Other values from result object are accurate, such as "field_count".
Possible Bug: http://www.php.net/manual/en/mysqli-result.num-rows.php#104630
Code is from source above (Johan Abildskov):
$sql = "valid select statement that yields results";
if($result = mysqli-connection->query($sql, MYSQLI_USE_RESULT))
{
echo $result->num_rows; //zero
while($row = $result->fetch_row())
{
echo $result->num_rows; //incrementing by one each time
}
echo $result->num_rows; // Finally the total count
}
Could also validate with the Procedural style:
/* determine number of rows result set */
$row_cnt = mysqli_num_rows($result);
I had the same problem and found the solution was to put:
$result->store_result();
..after the execution of the $query and before
echo $result->num_rows;
This could be normal behavior when you disable buffering of the result rows with MYSQLI_USE_RESULT
Disabling the buffer means that it`s up to you to fetch, store and COUNT the rows.
You should use the default flag
$this->connection->query($query, MYSQLI_STORE_RESULT);
Equivalent of
$this->connection->query($query)

Categories