85

I'm trying to get my head around MySQli and I'm confused by the error reporting. I am using the return value of the MySQLi 'prepare' statement to detect errors when executing SQL, like this:

$stmt_test =  $mysqliDatabaseConnection->stmt_init();
if($stmt_test->prepare("INSERT INTO testtable VALUES (23,44,56)"))
{
 $stmt_test->execute();
 $stmt_test->close();
}
else echo("Statement failed: ". $stmt_test->error . "<br>");

But, is the return value of the prepare statement only detecting if there is an error in the preperation of the SQL statement and not detecting execution errors? If so should I therefore change my execute line to flag errors as well like this:

if($stmt_test->execute()) $errorflag=true;

And then just to be safe should I also do the following after the statement has executed:

if($stmt_test->errno) {$errorflag=true;}

...Or was I OK to start with and the return value on the MySQLi prepare' statement captures all errors associated with the complete execution of the query it defines?

Thanks C

MPelletier
  • 16,256
  • 15
  • 86
  • 137
Columbo
  • 2,896
  • 7
  • 44
  • 54
  • Why do you use prepare/execute() instead of query() in the first place when there is no variable part in your query string? Or is this just an oversimplified example? – VolkerK Mar 31 '10 at 14:20
  • Yes, sorry. It was simplified to show how I was having difficulty understanding where to get definitive error reporting from a prepared staement. – Columbo Mar 31 '10 at 14:25

3 Answers3

164

Each method of mysqli can fail. Luckily, nowadays mysqli can report every problem to you, all you need is ask. Simply add this single line to the connection code,

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

And after that every error will reveal itself. No need to test any return values ever, just write your statements right away:

$stmt = $mysqli->prepare("INSERT INTO testtable VALUES (?,?,?)");
$stmt->bind_param('iii', $x, $y, $z);
$stmt->execute();

When the error occurs at any step, it will throw a usual PHP Exception that can be handled or just reported the same way as any other PHP error. Just make sure you configured PHP error reporting properly, i.e. on the dev server errors are displayed on-screen and on the production server errors are never displayed but logged instead.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
VolkerK
  • 95,432
  • 20
  • 163
  • 226
  • 2
    Thanks. I've just done some testing and can see what you are saying. If I create a quesry that inserts a duplicate primary key value into a table then checking the prepare only will not reveal that the insertion failed. On the other hand if I don't check the prepare then the execute will never happen (I'll get some warnings IF warnings are switched on). So I can see that you're right. Thanks. – Columbo Mar 31 '10 at 14:53
  • Oh yes, constraints are a perfect example. If possible I'd give _the question_ another +1 for that alone ;-) – VolkerK Mar 31 '10 at 14:57
  • 2
    if `bind_param()` fails due to Number of elements in type definition string doesn't match number of bind variables. it triggers an E_WARNING error. and you will not find that error in $stmt->error property – Accountant م Jan 16 '17 at 10:10
  • @Accountantم luckily, this nuisance is in the past. In the modern PHP versions bind_param also throws exceptions. – Your Common Sense Sep 22 '22 at 06:58
21

Completeness

You need to check both $mysqli and $statement. If they are false, you need to output $mysqli->error or $statement->error respectively.

Efficiency

For simple scripts that may terminate, I use simple one-liners that trigger a PHP error with the message. For a more complex application, an error warning system should be activated instead, for example by throwing an exception.

Usage example 1: Simple script

# This is in a simple command line script
$mysqli = new mysqli('localhost', 'buzUser', 'buzPassword');
$q = "UPDATE foo SET bar=1";
($statement = $mysqli->prepare($q)) or trigger_error($mysqli->error, E_USER_ERROR);
$statement->execute() or trigger_error($statement->error, E_USER_ERROR);

Usage example 2: Application

# This is part of an application
class FuzDatabaseException extends Exception {
}

class Foo {
  public $mysqli;
  public function __construct(mysqli $mysqli) {
    $this->mysqli = $mysqli;
  }
  public function updateBar() {
    $q = "UPDATE foo SET bar=1";
    $statement = $this->mysqli->prepare($q);
    if (!$statement) {
      throw new FuzDatabaseException($mysqli->error);
    }

    if (!$statement->execute()) {
      throw new FuzDatabaseException($statement->error);
    }
  }
}

$foo = new Foo(new mysqli('localhost','buzUser','buzPassword'));
try {
  $foo->updateBar();
} catch (FuzDatabaseException $e)
  $msg = $e->getMessage();
  // Now send warning emails, write log
}
cmc
  • 4,294
  • 2
  • 35
  • 34
  • 1. die() shouldn't be used ever. 2. mysqli is able to throw exceptions by itself, see mysqli_report() 2. Writing code for "send warning emails, write log" after *every* database-driven functions is awfully redundant – Your Common Sense Dec 13 '13 at 14:12
  • Anyway you should never use mysqli API as is, but only wrapped in some higher level library. – Your Common Sense Dec 13 '13 at 14:53
  • Again, bullshit. You do just that when you want performance, low memory footprint, and few dependencies. – cmc Dec 13 '13 at 15:22
  • 3
    Mentioning both $mysqli->error AND $statement->error just saved my day, thks^^ – Fernando Silva Jan 03 '14 at 16:57
  • 2
    @FernandoSilva Glad to hear that Fernando, and you just made mine! – cmc Jan 06 '14 at 21:51
  • @YourCommonSense well, the mysqli_report pointer was interesting. Reluctant to write code that relies on a global config setting to report errors though. – cmc Jan 08 '14 at 23:39
  • @YourCommonSense I'll give you that trigger_error is a better solution because it gives you the line number and exit code (edited answer), but I maintain that "not ever" is unnecessarily limiting. – cmc Jan 08 '14 at 23:45
  • 1
    @YourCommonSense For the app, yeah it would likely be good to reduce the boilerplate, but how to best do that depends on the app. The example is certainly a decent starting point. – cmc Jan 09 '14 at 00:07
6

Not sure if this answers your question or not. Sorry if not

To get the error reported from the mysql database about your query you need to use your connection object as the focus.

so:

echo $mysqliDatabaseConnection->error

would echo the error being sent from mysql about your query.

Hope that helps

andyface
  • 937
  • 1
  • 9
  • 25
  • Thanks. So if I request the error for the actual connection object then it will give me the last error for that connection. Since the execution will only succeed if all the previous steps have succeeded then this will tell me if all went well. I suppose the same result could also be generated by just checking the error for the execute command as endorsed below by Col Shrapnel. Am I right in thinking therefore that checking the success/fail flag of the prepare statement serves no real purpose? – Columbo Mar 31 '10 at 13:27
  • I think you may already have your answer above, but will still reply out of courtesy. Essentially Prepare can fail, as VolekrK said, but it wont return a mysql error. So you need to find out why prepare failed by getting the mysql connection error which will give you an indication of where the query in your prepare statement failed. I'm not sure about the execute command error. – andyface Apr 06 '10 at 09:38