How to debug PDO script? - php

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

Related

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

I am iterating rows of a csv file.
On row 1, I call stored procedure (import_extended_data_sp) and it succeeds.
On row 2, the call fails with :
Strict Standards mysqli::next_result(): There is no next result set.
However, with the call being exactly the same as the first, I am struggling to see why ?
I have now hard coded test values as parameters, and checked that the Sproc has no issue with the same values being given twice.
It still fails on the second call !?
Am wondering if there is some nuance of mysqli, where I need to clear or reset something before making the second call ?
<?php include("cnn.php");?>
<?php include("fn_db.php");?>
# ... get csv file (skipped for brevity) #
while($row = fgetcsv($file_data))
{
$line = array_combine($head, $row);
# This call works on every loop - no issues
$id = placemark_to_db($mysqli,$v_header,$line['id_placemark'],$line['name'],$line['swim_type'],$line['latitude'],$line['longitude'],$line['description']);
# This next line only succeeds on first call, but fails on next while loop
$x = xtended_to_db($mysqli,'99','[{"xtra":"oo"}]');
}
** fn_db.php >> xtended_to_db**
function xtended_to_db($cn,$id,$jsonarray){
# procedure returns a rowcount in output parameter
$cn->multi_query( "CALL import_extended_data_sp($id,'$jsonarray',#out);select #out as _out");
$cn->next_result();
$rs=$cn->store_result();
$ret = $rs->fetch_object()->_out;
$rs->free();
return $ret;
}
cnn.php
<?php
$mysqli = new mysqli("xxx.xxx.xxx.xxx","mydb","pass","user");
// Check connection
if ($mysqli -> connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli -> connect_error;
exit();
}
?>
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.

Particular Query not running in PHP(using PDO)

The below php code runs a query for which i get $res = true which means that the query should have run.
I checked the health of the PDO object also to make sure and found out that other queries get run correctly.
Also no exception were caught. Have run out of ways to solve this issue.
function insertPaypalSavedSanity($udi, $isPaypalSaved){
global $DBH;
try {
$sql = "INSERT INTO sanity(user_id, saved_paypal_used_previously) VALUES(:user_id,:sv) ON DUPLICATE KEY UPDATE saved_paypal_used_previously=:sv";
$sqlcon = $DBH->prepare($sql);
$res = $sqlcon->execute(array(
'user_id'=>$_SESSION['userID'],
'sv'=>$isPaypalSaved));//$res is true after executing the query
$x = 1;// I put the debugger at this point and run other queries with my PDO object($DBH)
} catch(Exception $e)
{
logger ("sanity queries",$e->getMessage());
}
}
This got solved by adding start transaction to beginning of statement and commit to the end of statement. I still have no clue as to why this worked.

Writing a Select Where statement in PHP and MySQL

Would someone please me with the code below, I am inexperienced in this area and my class in SQL was "A long time ago in a galaxy far, far away..." I know the connection string works because I have used it in other functions with this app. I have even used the code below for retrieving *rows from another table in another function, for the most part, except that I didn't use the WHERE clause.
First, I am able to store IP addresses in the table using a function and it is working well. Now I want to check to see if a given one exist in this table. Partial code is given below.
What seems to always return is 0 rows. I have put in test data into the table and hard-coded the $ipA, but I still get 0 rows return. Please help if possible and thanks for the effort spent.
function checkDB($ipA) {
require_once('connection.inc.php');
$resultAns = "";
//create db connection
$conn = dbConnect();
//init prepared stmt
$stmt = $conn->stmt_init();
//Set sql query for ipAddress search
//prepare the SQL query
$sql = 'SELECT * FROM ipAddress WHERE ipA = ?';
//submit the query and capture the result
if ($stmt->prepare($sql)) {
$stmt->bind_param('s', $ipA);
$stmt = $stmt->execute();
//if qry triggers error affeted_rows value becomes -1 &
//php treats -1 as true; so test for greater than 0
$numRows = $stmt->num_rows; //not to sure about the syntax here
}
// I want to know if the query brought back something or not, I don't what
// to know exactly what, only that it found a match or did not find a match.
// echos are for testing purposes to show me where I am landing.
if ($numRows == 0) {
echo '<script type="text/javascript">window.alert("numRows = 0")</script>';
$resultAns = 0;
} elseif ($numRows == 1) {
echo '<script type="text/javascript">window.alert("numRows = 1")</script>';
$resultAns = 1;
}
return $resultAns;
}
Try storing the result after you execute
$stmt->store_result();
Use $stmt->store_result(); before you call num_rows.
While the others caught one reason that $numRows would never receive a value other than 0, the other piece of code that was flawed and caused problems was...
$stmt = $stmt->execute(); which should have been just $stmt->execute();
I must have mixed it up with other code I wrote from somewhere else.
Thanks for the answers, they did help.

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

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";
}

record updated? when $stmt->execute() $stmt->affected_rows

considering the following for my question:
$success = false;
$err_msg = '';
$sql = 'UPDATE task SET title = ? WHERE task_id = ?';
$conn = connect('w'); // create database connection: r= read, w= write
$stmt = $conn->stmt_init(); // initialize a prepared statement
$stmt->prepare($sql);
$stmt->bind_param('si', $_POST['title'], $_POST['id']);
$stmt->execute();
If i want to check if an insert or a deletion was succesfull, i could easily check for the affected_rows, like this:
if ($stmt->affected_rows > 0) {
$success = true;
} else {
$err_msg = $stmt->error;
}
If $stmt->affected_rows equals -1, it means that $stmt->execute() executed correctly but did not insert the record or did not delete the record successfully.
But, what about an update ? What is the correct way to deal with an update?
The way i do it is by checking for the return value :
$isRecordUpdated = $stmt->execute();
if (!$isRecordUpdated) {
// execute failed, therefore NO record updated!
} else {
//execute success, record updated!
}
Is that the correct way you guys are doing it?
It seems to me that there are really two equivalent and correct ways of doing this: either by checking the return value of execute as you do, or by checking the affected_rows value. -1 means the query errored out; 0 means that it did not affect (delete or update) any rows because there were none matching the query.
Since it seems there is no "better" way, you should pick what would be most convenient for your code. If e.g. using one approach over the other means that you can then share code among all types of queries, you might want to pick that one.
Why not store the value from mysql-affected-rows into a property of that object when you call execute()?
http://php.net/manual/en/function.mysql-affected-rows.php

Categories