Strict Standards: MySQLi_Next_Result() Error With MySQLi_Multi_Query

Strict Standards: mysqli_next_result() error with mysqli_multi_query

While pipodesign corrected the error within the $querystring and alleviated the problem, the actual solution was not provided regarding the Strict Standards error.

I disagree with SirBT's advice, changing from DO WHILE to WHILE is not necessary.

The Strict Standards message that you receive is quite informative.
To obey, use this:

do{} while(mysqli_more_results($db) && mysqli_next_result($db));

Then, there is no need for you to write a conditional exit or break inside of the loop because the while condition will break the loop on the first occurrence of an error. *note, the if statement before the do-while will deny entry to the loop if the first query has an error.

In your example, you are only running INSERT queries, so you won't receive any result sets to process. If you want to count how many rows you've added, use mysqli_affected_rows().

As a complete solution for your question:

if(mysqli_multi_query($db,$querystring)){
do{
$cumulative_rows+=mysqli_affected_rows($db);
} while(mysqli_more_results($db) && mysqli_next_result($db));
}
if($error_mess=mysqli_error($db)){echo "Error: $error_mess";}
echo "Cumulative Affected Rows: $cumulative_rows";

Output:

 // if no errors
Cumulative Affected Rows: 2

// if error on second query
Error: [something]
Cumulative Affected Rows: 1

// if error on first query
Error: [something]
Cumulative Affected Rows: 0

LATE EDIT:

Since people new to mysqli are stumbling across this post, I'll offer a general yet robust snippet to handle queries with/without result sets using multi_query() and add a feature to display which query in the array is being handled...

Classic "IF(){DO{} WHILE}" Syntax:

if(mysqli_multi_query($mysqli,implode(';',$queries))){
do{
echo "<br><br>",key($queries),": ",current($queries); // display key:value @ pointer
if($result=mysqli_store_result($mysqli)){ // if a result set
while($rows=mysqli_fetch_assoc($result)){
echo "<br>Col = {$rows["Col"]}";
}
mysqli_free_result($result);
}
echo "<br>Rows = ",mysqli_affected_rows($mysqli); // acts like num_rows on SELECTs
} while(next($queries) && mysqli_more_results($mysqli) && mysqli_next_result($mysqli));
}
if($mysqli_error=mysqli_error($mysqli)){
echo "<br><br>",key($queries),": ",current($queries),"Syntax Error:<br>$mysqli_error"; // display array pointer key:value
}
//if you want to use the snippet again...
$mysqli_error=null; // clear variables
reset($queries); // reset pointer

Reinvented Wheel "WHILE{}" Syntax (...for those who don't like post-test loops):

while((isset($multi_query) && (next($queries) && mysqli_more_results($mysqli) && mysqli_next_result($mysqli))) || (!isset($multi_query) && $multi_query=mysqli_multi_query($mysqli,implode(';',$queries)))){
echo "<br><br>",key($queries),": ",current($queries); // display array pointer key:value
if($result=mysqli_store_result($mysqli)){
while($rows=mysqli_fetch_assoc($result)){
echo "<br>Col = {$rows["Col"]}";
}
mysqli_free_result($result);
}
echo "<br>Rows = ",mysqli_affected_rows($mysqli); // acts like num_rows on SELECTs
}
if($mysqli_error=mysqli_error($mysqli)){
echo "<br><br>",key($queries),": ",current($queries),"Syntax Error:<br>$mysqli_error"; // display array pointer key:value
}
//if you want to use the snippet again...
$multi_query=$mysqli_error=null; // clear variables
reset($queries); // reset pointer

So, either snippet given the following queries will offer the same output:

Query array:

$queries[]="SELECT * FROM `TEST`";
$queries[]="INSERT INTO `TEST` (Col) VALUES ('string1'),('string2')";
$queries[]="SELECT * FROM `TEST`";
$queries[]="DELETE FROM `TEST` WHERE Col LIKE 'string%'";

Output:

0: SELECT * FROM `TEST`
Rows = 0

1: INSERT INTO `TEST` (Col) VALUES ('string1'),('string2')
Rows = 2

2: SELECT * FROM `TEST`
Col = string1
Col = string2
Rows = 2

3: DELETE FROM `TEST` WHERE Col LIKE 'string%'
Rows = 2

Modify my snippets per your needs. Leave a comment if you discover a bug.

Why this PHP error occurs: Strict standards: mysqli::next_result(): There is no next result set.?

Try it with

} while ($mysqli->more_results() && $mysqli->next_result());

sscce:

<?php
ini_set('display_errors', 'on');
error_reporting(E_ALL|E_STRICT);

$mysqli = new mysqli("localhost", "localonly", "localonly", "test");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

$mysqli->query('CREATE TEMPORARY TABLE City (ID int auto_increment, `Name` varchar(32), primary key(ID))') or die($mysqli->error);

$stmt = $mysqli->prepare("INSERT INTO City (`Name`) VALUES (?)") or die($mysqli->error);
$stmt->bind_param('s', $city) or die($stmt->error);
foreach(range('A','Z') as $c) {
$city = 'city'.$c;
$stmt->execute() or die($stmt->error);
}

$query = "SELECT CURRENT_USER();";
$query .= "SELECT Name FROM City ORDER BY ID LIMIT 20, 5";

/* execute multi query */
if (!$mysqli->multi_query($query)) {
trigger_error('multi_query failed: '.$mysqli->error, E_USER_ERROR);
}
else {
do {
/* store first result set */
if ($result = $mysqli->store_result()) {
while ($row = $result->fetch_row()) {
printf("'%s'\n", $row[0]);
}
$result->free();
}
/* print divider */
if ($mysqli->more_results()) {
printf("-----------------\n");
}
} while ($mysqli->more_results() && $mysqli->next_result());
}

prints

'localonly@localhost'
-----------------
'cityU'
'cityV'
'cityW'
'cityX'
'cityY'

without warnings/notices.

How to identify the query that caused the error using mysqli_multi_query?

Here is an approach that will not only improve the quality of your error messages, it will improve the way you handle your result sets.

$q["Orders"] = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20";
$q["Inventory"] = "SELECT * FRO inventory";

if (!$link = mysqli_connect("host", "user", "pass", "db")) {
echo "Failed to connect to MySQL: " , mysqli_connect_error();
} elseif (mysqli_multi_query($link, implode(';', $q))) {
do {
$q_key = key($q); // current query's key name (Orders or Inventory)
if ($result = mysqli_store_result($link)) { // if a result set... SELECTs do
while ($row = mysqli_fetch_assoc($result)) { // if one or more rows, iterate all
$rows[$q_key][] = $row;
}
mysqli_free_result($result);
echo "<div><pre>" . var_export($rows[$q_key], true) . "</pre></div>";
}
} while (next($q) && mysqli_more_results($link) && mysqli_next_result($link));
}
if ($mysqli_error = mysqli_error($link)) { // check & declare variable in same step to avoid duplicate func call
echo "<div style=\"color:red;\">Query Key = " , key($q) , ", Query = " , current($q) , ", Syntax Error = $mysqli_error</div>";
}

Error on first query:
If your first query tries to access a table that doesn't exist in the nominated database like: ordersXYZ
Array $rows will not exist, no var_export() will occur, and you will see this response:

Query Key = Orders, Query = SELECT * FROM ordersXYZ WHERE location='IN' ORDER BY orderNum DESC LIMIT 20, Syntax Error = Table '[someDB].ordersXYZ' doesn't exist

Error on second query:
If your first query is successful, but your second query tries to access a non-existent table like: inventory2

$rows["Orders"] will hold the desired row data and will be var_export()'ed, $row["Inventory"] will not exist, and you will see this response:

Query Key = Inventory, Query = SELECT * FROM inventory2, Syntax Error = Table '[someDB].inventory2' doesn't exist

No errors:
If both queries are error free, your $rows array will be filled with the desired data and var_export()'ed, and there will be no error response. With the queried data saved in $rows, you can access what you want from $rows["Orders"] and $rows["Inventory"].


Things to note:

  1. You may notice that I am making variable declarations and conditional checks at the same time, this makes the code more concise but some devs prefer to avoid this.

  2. As my approach uses implode() with a semi-colon on the elseif line, be sure not to add a trailing semi-colon to your queries.

  3. This set of queries always returns a result set because all are SELECT queries, if you have a mixed collection of queries that affect_rows, you may find some useful information at this link(https://stackoverflow.com/a/22469722/2943403).

  4. mysqli_multi_query() will stop running queries as soon as there is an error. If you are expecting to catch "all" errors, you will discover that there will never be more than one.

  5. Writing conditional break points like in the OP's question and solution is not advisable. While custom break points may be rightly used in other circumstances, for this case the break points should be positioned inside of the while() statement of the do() block.

  6. A query that returns zero rows will not cause a error message -- it just won't create any subarrays in $rows because the while() loop will not be entered.

  7. By using the key() function, the OP's if/elseif condition that counts the columns in each resultset row can be avoided. This is better practice because running a condition on every iteration can become expensive in some cases. Notice that the array pointer is advanced inside of $q at the end of each do() iteration. This is an additional technique that you will not find on the php manual page; it allows key() to work as intended.

  8. And, of course, the <div><pre>var_export()...</pre></div> line can be removed from your working code -- it was purely for demonstration.

  9. If you are going to run any more queries after this code block that reuse variables, be sure to clear all used variables so that residual data does not interfere. e.g. $mysqli_error=null; // clear errors & reset($q); // reset array pointer.

  10. Take heed to this somewhat vague warning at your own discretion: http://php.net/manual/en/mysqli.use-result.php :

One should not use mysqli_use_result() if a lot of processing on the
client side is performed, since this will tie up the server and
prevent other threads from updating any tables from which the data is
being fetched.


  1. Lastly and MOST IMPORTANTLY for security reasons, do not display query or query error information publicly -- you don't want sinister people to see this kind of feedback. Equally important, always protect your queries from injection hacks. If your queries include user-provided data, you need to filter/sanitize the data to death before using it in mysqli_multi_query(). In fact when dealing with user input, my very strong recommendation is to move away from mysqli_multi_query() and use either mysqli or pdo prepared statements for your database interactions for a higher level of security.

How do I ensure I caught all errors from MySQLi::multi_query?

Despite the code example in the docs, perhaps the better method would be something like this:

if ($mysqli->multi_query(...)) {
do {
// fetch results

if (!$mysqli->more_results()) {
break;
}
if (!$mysqli->next_result()) {
// report error
break;
}
} while (true);
}

mysqli error only on subsequent calls to same function - 'there is no next result set.'

The best way to fix this error is to avoid multi_query() altogether. While it might sound like a reasonable use case with stored procedures, the truth is this function is mostly useless and very dangerous. You can achieve the same result using the normal way with prepared statements.

function xtended_to_db(mysqli $cn, $id, $jsonarray) {
$stmt = $cn->prepare('CALL import_extended_data_sp(?,?,@out)');
$stmt->bind_param('ss', $id, $jsonarray);
$stmt->execute();

$stmt = $cn->prepare('select @out as _out');
$stmt->execute();
$rs = $stmt->get_result();
return $rs->fetch_object()->_out;
}

If you are stuborn and you want to keep on using multi_query() then you need to be more careful with how you fetch results. This function is extremely difficult to get right. I am not going to show you how to fix multi_query() as I consider it too dangerous with variable input.

One last note, you really should think about getting rid of stored procedures. They are cumbersome and offer pretty much no benefit. There definitely is a better way to achieve what you want rather than calling stored procedure from PHP, but without seeing its contents I can't give you better advice.



Related Topics



Leave a reply



Submit