$stmt->execute() : How to know if db insert was successful? - php

With the following piece of code, how do i know that anything was inserted in to the db?
if ($stmt = $connection->prepare("insert into table (blah) values (?)")) {
$stmt->bind_param("s", $blah);
$stmt->execute();
$stmt->close();
}
I had thought adding the following line would have worked but apparently not.
if($stmt->affected_rows==-1){$updateAdded="N"; echo "failed";}
And then use the $updatedAdded="N" to then skip other pieces of code further down the page that are dependent on the above insert being successful.
Any ideas?

The execute() method returns a boolean ... so just do this :
if ($stmt->execute()) {
// it worked
} else {
// it didn't
}
Update: since 2022 and beyond, a failed query will throw an error Exception. So you won't have to write any code to "skip other pieces of code further down the page" - it will be skipped automatically. Therefore you shouldn't add any conditions and just write the code right away:
$stmt = $connection->prepare("insert into table (blah) values (?)");
$stmt->bind_param("s", $blah);
$stmt->execute();
If you need to do something in case of success, then just do it right away, like
echo "success";
You will see it only if the query was successful. Otherwise it will be the error message.

Check the return value of $stmt->execute()
if(!$stmt->execute()) echo $stmt->error;
Note that line of code does perform the execute() command so use it in place of your current $stmt->execute() not after it.

Starting on PHP/8.1.0, the default setting is to throw exceptions on error, so you don't need to do anything special. Your global exception handler will take care of it, or you can try/catch for specific handling.
For older versions, you can check the manual pages of whatever function you are using:
prepare() - returns a statement object or FALSE if an error occurred.
bind_param() - Returns TRUE on success or FALSE on failure.
execute() - Returns TRUE on success or FALSE on failure.
close() - Returns TRUE on success or FALSE on failure.
In practice, though, this gets annoying and it's error prone. It's better to configure mysqli to throw exceptions on error and get rid of all specific error handling except for the few occasions where an error is expected (e.g., a tentative insert that might violate a unique constraint):
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
Default value used to be MYSQLI_REPORT_OFF. On PHP/8.1.0 it changed to MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT.

You can check the returned value after the execute :
if ($stmt->execute()) {
// ok :-)
$count = $stmt->rowCount();
echo count . ' rows updated properly!';
} else {
// KO :-(
print_r($stmt->errorInfo());
}

if you mean that you want to know the number of affected rows you can use rowCount on the pdo statement
$stmt->rowCount();
after execute;
if you are talking about error handling I think the best option is to set the errmode to throwing exteptions and wrap everything in a try/catch block
try
{
//----
}
catch(PDOException $e)
{
echo $e->getMessage();
}

Other way:
if ($stmt->error){
echo "Error";
}
else{
echo "Ok";
}

Related

How to debug PDO script?

I have a PHP script that is executed daily by my server thanks to cron.
This script contains PDO queries to add, edit, and delete data from my MySQL database.
The script does not work as expected, especially the last part of the query which is supposed to remove some rows:
$stmt = $conn->prepare("DELETE FROM `mkgaction` WHERE score IS NULL");
$stmt->execute();
if($stmt->execute()) {
echo "delete succeeded<br>";
} else {
echo "delete failed<br>";
}
When executed manually via PHPMyAdmin, every query works fine. When executed via this script it does not work despite the message showing "delete succeeded".
I suppose the best way to understand what actually happens is to read the response from the database, but I don't know how to do that.
Would you help me? :-)
Thanks
Always check the return value of prepare() and execute(). They return the boolean value false if there's a problem.
Then you should check the specific error and report that error to help debugging.
$stmt = $conn->prepare("DELETE FROM `mkgaction` WHERE score IS NULL");
if ($stmt === false) {
die(print_r($conn->errorInfo(), true));
}
$ok = $stmt->execute();
if ($ok === false) {
die(print_r($stmt->errorInfo(), true));
}
echo "delete succeeded<br>";
Admittedly, checking every call gets to be a lot of repetitive code. An alternative is to enable exceptions, if you're comfortable writing code to handle exceptions. See https://www.php.net/manual/en/pdo.error-handling.php

Why should I check the return value of prepare?

I have a PHP program to select album names from album MySQL table. I did not understand usage of if at preparing the statement? Why should I use if at all?
Line 2: Why should I use if statement, can't we just write it without the if statement?
$sql = "SELECT album_name FROM albums WHERE artist_id=?";
if($stmt = $link->prepare($sql)) // line 2
{
$stmt->bind_param('i', $_POST['artist']);
$stmt->execute();
$stmt->bind_result($album);
while($stmt->fetch()) {
printf("Album: %s<br />", $album);
}
$stmt->close();
}
// Close the connection
$link->close();
The if statement is checking for an error. When $link->prepare() gets an error, it returns FALSE instead of a MySQL statement object. If you don't check for errors, you'll get an error when you try to call $stmt->bind_param(), because FALSE isn't an object.
It's more common to write it like this:
$stmt = $link->prepare($sql);
if ($stmt) {
$stmt->bind_param('i', $_POST['artist']);
$stmt->execute();
$stmt->bind_result($album);
while($stmt->fetch()) {
printf("Album: %s<br />", $album);
}
$stmt->close();
} else {
report_error($link->error);
}
The two versions are equivalent, they just combined the assignment with testing its value.
Sometimes it's written like this, mainly when initially debugging (since you usually want better error reporting and logging in a production application):
$stmt = $link->prepare($sql) or die($link->error);
$stmt->bind_param('i', $_POST['artist']);
$stmt->execute();
$stmt->bind_result($album);
while($stmt->fetch()) {
printf("Album: %s<br />", $album);
}
$stmt->close();
Since die() never returns because it terminates the script, you don't need an if statement to skip the rest of the code.
The if statement is not necessary and in fact it does not make much sense to check the result of prepare, but ignore bind_param, execute and bind_result. Your code should be consistent and check the return value of all or none of them.
Checking for return values of these functions is not needed if you have the MySQLi error reporting switched on. To enable the errors, put this line before you make your DB connection:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
If there was a problem in any of these functions then an exception will be thrown with an error message. The error will be displayed on the screen, but you should remember to switch the ini setting display_errors to false in your production environment, otherwise your errors could leak sensitive information.
Your code could look a little shorter and cleaner:
$sql = "SELECT album_name FROM albums WHERE artist_id=?";
$stmt = $link->prepare($sql);
$stmt->bind_param('i', $_POST['artist']);
$stmt->execute();
$stmt->bind_result($album);
while ($stmt->fetch()) {
printf("Album: %s<br />", $album);
}
Closing the connection is not necessary either, unless you have a good reason to do so.
Whatever you do never echo or die the contents of $link->error yourself. It is a very bad habit of many PHP developers, which is completely redundant. PHP can print the errors for you and it will do much better job at it than you ever would.

Why doesn't execute() return true on error?

Please take a look at my code:
try {
// db connection here
$stm = $dbh->prepare("INSERT INTO mytable(id,token) values(NULL,$token)")->execute();
} catch(PDOException $e){
if ( $stm ){
echo 'inserting fails';
} else {
echo 'something else is wrong';
}
}
-- `token` column is unique
Current outputs:
The row inserted successfully.
It prints something else is wrong error for both {duplicate entry} and {SQL syntax}
Expected outputs:
The row inserted successfully.
It prints inserting fails error for {duplicate entry}
It prints something else is wrong error for {SQL syntax}
Ok, if I write my code like following (without chaining), then expected output happens:
$stm = $dbh->prepare("INSERT INTO mytable(id,token) values(NULL,$token)");
$stm->execute();
Well I want to know, when can I chain those PDO statements?
An exception can only be thrown in either the prepare or execute methods. Either of those is going to happen before $stm =. In other words, if an exception is going to be thrown, the assignment to $stm is always going to be skipped, meaning the variable doesn't exist at all in your catch block. Therefore it can only evaluate to false, and will in fact produce a notice about being undefined.
Read the PDO documentation http://php.net/manual/en/book.pdo.php and look at the return values. You can only chain when an object is returned such as a statement or resultset.
Execute (http://php.net/manual/en/pdostatement.execute.php) returns a boolean, not an object so we know it cannot be chained.
Prepare (http://php.net/manual/en/pdo.prepare.php) returns a statement object, so we can use the return statement to chain on another method call.
Think of it like this:
$stmt = $dbh->prepare("..sql..");
$bool = $stmt->execute();
This can translate into:
$bool = $dbh->prepare("..sql..")->execute();
As the return from ->prepare() is the the $stmt.
The reason you aren't getting your expected output is that the way you have it written, any time you get a PDOException, $stm can never be true. If either the prepare or the execute fails, then $stm will be undefined.
I originally thought that you could fix this by removing the check for execute success from the catch block, but I was mistaken. You cannot get your expected output while still chaining the methods.
try {
$success = $dbh->prepare("INSERT INTO mytable(id,token) values(NULL,$token)")->execute();
if (!$success) {
// This can never be reached. If your have set PDO::ERRMODE_EXCEPTION, then either
// the query is successful and $success === true, or the prepare or the execute
// failed, and an exception will be thrown
echo 'inserting fails';
}
} catch(PDOException $e){
echo 'something else is wrong';
}
Just for the record. To answer the question the guy tried to ask.
A code from my article on PDO (also fixing an SQL injection):
try {
$dbh->prepare("INSERT INTO mytable(token) values(?)")->execute([$token]);
} catch (PDOException $e) {
if ($e->getCode() == 1062) {
// insert failed due to duplicate key error
echo "duplicate token";
} else {
// insert failed due to any other error
throw $e;
}
}

How to determine if a MySQL update query succeeded when the data passed in the query is the same as what is already in the database?

Let's say you have a form with pre-populated data from your database, and you allow your users to make changes and save the form. If the user clicks the save button without making any changes, MySQL will not actually perform a write operation, and therefore the affected_rows will return 0.
I understand the behavior, but what is the best practice for determining if an update failed, other than checking for the number of affected_rows?
What is the best practice for differentiating between an update that actually failed, and one that "succeeded" but resulted in 0 affected_rows so that I can provide feedback to the user?
Just check if no errors occurred after execution of query.
If you use mysql, check mysql_error():
if (!mysql_error()) print 'all is fine';
Same for mysqli.
Variation 1:
mysql_query() or die('error');
Variation 2:
$conn = mysql_query();
if(!$conn) {//Error code here}
Variation 3:
try {
$conn = mysql_query();
if (!$conn) throw new Exception("mysql Error");
} catch(Exception $e) {
echo $e->getMessage();
}
[affected_rows()][1] is -1 if a query fails, not zero.
[1]: http://www.php.net/manual/en/function.mysql-affected-rows.php
It may return 0 if no changes were made to the row (same values), or if mysql didnt find a row to update. It will only return -1 due syntax erro
if the update "fails" due to syntax error, or other mysql will return an error code on the actual mysql query and affected_rows will return with yet another error.
Php for example:
$qry = mysql_query("update blah where IamaSyntaxerror,33");
if ($qry === FALSE) { echo "an error has occured"; }
else { mysql_affected_rows() == 0 means no updates occured

PDO query problem

I am updating some code from the old mysql_* functions to PDO. It connects without a problem, runs the query without a problem, but the resultset is empty. PDO::query() is supposed to return a PDOStatement object, yet I am getting true in return. No errors are reported.
Here is my code:
try
{
$DB = new PDO("mysql:host=localhost;dbname=dbname", "user", "pass");
$stmt = $DB->prepare("SELECT * FROM report_clientinfo");
$stmt->execute();
}catch(PDOException $e)
{
echo $e->getMessage() . "\n";
}
echo gettype($stmt) . "\n";
if ($stmt) echo "true\n";
else echo "false\n";
$resultset = $stmt->fetchAll();
if(empty($resultset))
{
exit("ERROR: getClientInfo query failed.");
}
$DB = null;
print_r($resultset);
The output I am seeing is:
object
true
ERROR: getClientInfo query failed.
Any ideas why it is not returning any results?
object
true
ERROR: getClientInfo query failed.
It looks to me like your PDOStatement $stmt variable is in fact reported to be an object, not "true". The code then prints "true" when it sees that $stmt is non-null, which it is, because it's an object.
I recommend that you check the return value from $stmt->execute(). You might have an SQL error. For example, if you misspelled the table name, or the table doesn't exist in the database "dbname" that you connected to, or the user you login as doesn't have privilege to query that table.
Also check $stmt->errorInfo() to get more details on any error that occurred.
I'm a little embarrassed to report back that I was pointing to the wrong DSN. I guess that's what I get for trying to learn something new on just a few hours of sleep after going out for New Year's Eve. Thanks for the tip on the PDOStatement::errorInfo() method, I had not noticed it before.

Categories