How can a handle errors from db2_fetch_assoc in PHP? - php

I have a query to a program that I am running on my iSeries. I understand that sometimes this query will fill occasionally because there are no records to select. Every time the query fails, it logs to php.log as:
PHP Warning: db2_fetch_assoc(): Fetch Failure in /www/zendsvr6/htdocs/views/hp1110.php on line 82
I am looking for a solution to prevent this error from logging. I know that I can disable PHP error logging already. I do not want to disable the logging.
Here is a sample of the code:
$stmt = db2_prepare($conn, $sql);
if (!$stmt) {
$code = db2_stmt_error();
$msg = db2_stmt_errormsg();
// TODO in production do not display SQL. Log it instead.
die("Failed to prepare $sql. Reason: $code $msg");
} //(if (!$result))
$bound1 = db2_bind_param($stmt, 1, "item", DB2_PARAM_IN);
$bound2 = db2_bind_param($stmt, 2, "house", DB2_PARAM_IN);
$bound3 = db2_bind_param($stmt, 3, "counter", DB2_PARAM_INOUT);
if (!$bound1 || !$bound2 || !$bound3) {
die("Failed to bind one or more parameters.");
} //(if (!$bound1 || !$bound2 || !$bound3) )
// execute with bound parameters.
$result = db2_execute($stmt);
if (!$result) {
$code = db2_stmt_error();
$msg = db2_stmt_errormsg();
// TODO in production do not display SQL. Log it instead.
die("Failed to execute $sql. Reason: $code $msg");
} //(if (!$result))
if (!$counter) {
return array();//
} //(if (!$counter))
// success. create array to return to caller.
$rows = array();
while (($row = db2_fetch_assoc($stmt)) != false) {
$rows[] = $row;
} //(while...)
// this returns it to the caller.
return $rows;
} //(getParts)
Thanks!

If the db2 driver logs that every time you try to fetch from a result which has no(more) rows available, then you can avoid it by fetching only exactly the number of rows returned:
$total_rows = db2_num_rows($stmt);
for ($i = 0; $i < $total_rows; $i++) {
$row = db2_fetch_assoc($stmt);
... do stuff ...
}

Related

Prepared Statement Return Get Results & Count

I am trying to get both the results of my query and the row count wihtout having to make two trips the the DB if possible. I am using prepared statements in a procedural way. My code is as follows:
$dbd = mysqli_stmt_init($dbconnection);
if (mysqli_stmt_prepare($dbd, "SELECT * FROM Contacts WHERE First_Name = ?" )) {
mysqli_stmt_bind_param($dbd, "s", $val1);
if (!mysqli_stmt_execute($dbd)) {
echo "Execute Error: " . mysqli_error($dbconnection);
} else {
//do nothing
}
} else {
echo "Prep Error: " . mysqli_error($dbconnection);
}
$result = mysqli_stmt_get_result($dbd);
So the above code works just fine and returns my results. What I want to do now is get the row count using this same statement but i don't want to have to write a brand new prepared statement. If I writer a separate prepared statement and use store_results and num_rows I get the row count but that would force me to have to write an entire new block of code and trip to db. I am trying to do something as follows but It throws and error:
$dbd = mysqli_stmt_init($dbconnection);
if (mysqli_stmt_prepare($dbd, "SELECT * FROM Contacts WHERE First_Name = ?" )) {
mysqli_stmt_bind_param($dbd, "s", $val1);
if (!mysqli_stmt_execute($dbd)) {
echo "Execute Error: " . mysqli_error($dbconnection);
} else {
//do nothing
}
} else {
echo "Prep Error: " . mysqli_error($dbconnection);
}
$result = mysqli_stmt_get_result($dbd);
mysqli_stmt_store_result($dbd);
$rows = mysqli_stmt_num_rows($dbd);
The throws and error as if i can't run both get results and store results using the same prepared statement. Im simply trying to keep my code compact and reuse as much as possible. If i break the above out into two separat prepared statements it works fine, Im just wondering there is a way to just add a line or two to my existing statement and get the row count. Or do i have to write an entire new block of code with new stmt_init, stmt_prepare, bind_param, execute, etc...
I tried your code (and reformatted it a bit), but I can't get it to work when I use both store_result() and get_result(). I can only use store_result then bind_result().
So the alternative is to fetch all the rows and then count them:
Example:
$sql = "SELECT * FROM Contacts WHERE First_Name = ?";
$stmt = mysqli_stmt_init($dbconnection);
if (mysqli_stmt_prepare($stmt, $sql) === false) {
trigger_error("Prep Error: " . mysqli_error($dbconnection));
return 1;
}
if (mysqli_stmt_bind_param($stmt, "s", $val1) === false) {
trigger_error("Bind Error: " . mysqli_stmt_error($stmt));
return 1;
}
if (mysqli_stmt_execute($stmt) === false) {
trigger_error("Execute Error: " . mysqli_stmt_error($stmt));
return 1;
}
$result = mysqli_stmt_get_result($stmt);
$rows = mysqli_fetch_all($result, MYSQLI_ASSOC);
$num_rows = count($rows);
print "$num_rows rows\n";
foreach ($rows as $row) {
print_r($row);
}
In my opinion, PDO is much easier:
$pdo = new PDO("mysql:host=127.0.0.1;dbname=test", "xxxx", "xxxxxxxx");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Contacts WHERE First_Name = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$val1]);
$num_rows = $stmt->rowCount();
print "$num_rows rows\n";
while ($row = $stmt->fetch()) {
print_r($row);
}

PHP mysqli_fetch_assoc not doing returning correct value

I have an old PHP code that has mysql in it.
It gets an array from a SELECT statement, adds it to a JSON object, as a property and echoes the encoded JSON.
I changed it around to use mysqli, but when I try to get the rows, and create an array out of them, it just returns nothing.
Here's the old mysql code:
$con = mysql_connect('host','account','password');
if (!$con)
{
//log my error
};
mysql_select_db("database_name", $con);
mysql_set_charset('utf8');
$sql = "SELECT field1 as Field1, field2 as Field2 from table where ID = '".$parameter."'";
$query = mysql_query($sql);
$results = array();
while($row = mysql_fetch_assoc( $query ) )
{
$results[] = $row;
}
return $results;
Version1: Here's the new one that I tried writing:
$con = mysqli_connect('host','account','password','database_name');
$sql = "SELECT field1 as Field1, field2 as Field2 from table where ID = '".$parameter."'";
$results = array();
if($result=mysqli_query($con, $sql))
{
while ($row=mysqli_fetch_assoc($result))
{
$results[] = $row;
}
return $results;
}
else
{
//error
}
Version2: Second thing I tried, which only returns 1 ROW:
...same as above until $sql
if($result=mysqli_query($con,$sql))
{
$row=mysqli_fetch_assoc($result);
return $row;
}
Version3: Or I tried to completely mirror the mysql structure like this:
$sql = "SELECT ...";
$query = mysqli_query($con, $sql);
$results = array();
while($row = mysqli_fetch_assoc( $query ) )
{
$results[] = $row;
}
return $results;
Wrapping the resulting array into the JSON:
$obj = new stdClass();
$obj->Data = $results;
$obj->ErrorMessage = '';
die(json_encode($obj)); //or echo json_encode($obj);
None of the mysqli version are working, so I was thinking there might be an important change in the way these arrays are created.
Any tips on what could be wrong on the first mysqli example?
With Version2 I can tell that the SQL connection is there, and I can at least select a row. But it's obviously only one row, than it returns it. It makes me think, that building up the array is the source of the problem, or it's regarding the JSON object...
LATER EDIT:
OK! Found a working solution.
ALSO, I played around with the data, selected a smaller chunk, and it suddenly worked. Lesson from this: the function is not responding the same way for 40 rows or for 5 rows. Does it have something to do with a php.ini setting? Or could there be illegal characters in the selection? Could it be that the length of a 'Note' column (from the db) is too long for the array to handle?
Here's the working chunk of code, that selects some rows from the database, puts them into an array, and then puts that array into an object that is encoded into JSON at the end, with a statusmessage next to it. Could be improved, but this is just for demo.
$con = mysqli_connect('host','username','password','database_name');
if (!$con)
{
$errorMessage = 'SQL connection error: '.$con->connect_error;
//log or do whatever.
};
$sql = "SELECT Field1 as FieldA, field2 as FieldB, ... from Table where ID='something'";
$results = array();
if($result = mysqli_query($con, $sql))
{
while($row = mysqli_fetch_assoc($result))
{
$results[] = $row;
}
}
else
{
//log if it failed for some reason
die();
}
$obj->Data = $results;
$obj->Error = '';
die(json_encode($obj));
Question is: how can I overcome the issue regarding the size of the array / illegal characters (if that's the case)?
Your "Version 1" seems to be correct from a PHP perspective, but you need to actually handle the errors - both when connecting and when performing the query. Doing so would have told you that you don't actually query a table, you're missing FROM tablename in the query.
Use mysqli_connect_error() when connecting, and mysqli_error($con) when querying to get back the actual errors. General PHP error-reporting might also help you.
The code below assumes that $parameter is defined prior to this code.
$con = mysqli_connect('host','account','password','database_name');
if (mysqli_connect_errno())
die("An error occurred while connecting: ".mysqli_connect_error());
$sql = "SELECT field1 as Field1, field2 as Field2
FROM table
WHERE ID = '".$parameter."'";
$results = array();
if ($result = mysqli_query($con, $sql)) {
while ($row = mysqli_fetch_assoc($result)) {
$results[] = $row;
}
return $results;
} else {
return mysqli_error($con);
}
Error-reporing
Adding
error_reporting(E_ALL);
ini_set("display_errors", 1);
at the top of your file, directly after <?php would enable you to get the PHP errors.
NOTE: Errors should never be displayed in a live environment, as it might be exploited by others. While developing, it's handy and eases troubleshooting - but it should never be displayed otherwise.
Security
You should also note that this code is vulnerable to SQL-injection, and that you should use parameterized queries with placeholders to protect yourself against that. Your code would look like this with using prepared statements:
$con = mysqli_connect('host','account','password','database_name');
if (mysqli_connect_errno())
die("An error occurred while connecting: ".mysqli_connect_error())
$results = array();
if ($stmt = mysqli_prepare("SELECT field1 as Field1, field2 as Field2
FROM table
WHERE ID = ?")) {
if (mysqli_stmt_bind_param($stmt, "s", $parameter)) {
/* "s" indicates that the first placeholder and $parameter is a string */
/* If it's an integer, use "i" instead */
if (mysqli_stmt_execute($stmt)) {
if (mysqli_stmt_bind_result($stmt, $field1, $field2) {
while (mysqli_stmt_fetch($stmt)) {
/* Use $field1 and $field2 here */
}
/* Done getting the data, you can now return */
return true;
} else {
error_log("bind_result failed: ".mysqli_stmt_error($stmt));
return false;
}
} else {
error_log("execute failed: ".mysqli_stmt_error($stmt));
return false;
}
} else {
error_log("bind_param failed: ".mysqli_stmt_error($stmt));
return false;
}
} else {
error_log("prepare failed: ".mysqli_stmt_error($stmt));
return false;
}
References
http://php.net/mysqli.prepare
How can I prevent SQL injection in PHP?

Warning: db2_fetch_assoc(): Fetch Failure

This is probably very simple (I'm a novice), but I haven't been able to find an answer.
I'm using Linux, DB2 and PHP.
My simple DB2 query from PHP only returns rows with integer values, but fails with "Fetch Failure" for anything else (varchar ...).
The query works with db2cli for all values:
echo "select COLUMN from TEST.TABLE"" | ./db2cli execsql -dsn SCHEMA
But fails in PHP:
$conn_string = "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=$database;" .
"HOSTNAME=$hostname;PORT=$port;PROTOCOL=TCPIP;UID=$user;PWD=$passwd;";
$conn = db2_pconnect($conn_string, '', '');
if ($conn) {
$sql = 'select COLUMN from TEST.TABLE';
$options = array('cursor' => DB2_SCROLLABLE, 'binmode' => DB2_BINARY);
$stmt = db2_prepare($conn, $sql, $options);
if ($stmt) {
$result = db2_execute($stmt);
if (!$result) {
echo "exec errormsg: " .db2_stmt_errormsg($stmt);
}
$total_rows = db2_num_rows($stmt);
print "<br>Total rows: $total_rows";
for ($i = 0; $i < $total_rows; $i++) {
$row = db2_fetch_array($stmt);
print "<br>$row[0]";
}
} else {
echo "exec erromsg: " . db2_stmt_erromsg($stmt);
}
db2_close($conn);
} else {
echo "failed #2 ".db2_conn_errormsg();
}
}
It will display any rows with integer values, but empty strings for everything else with the error "Fetch Failure" in the log. I tried db2_fetch_array, db2_fetch_assoc and db2_fetch_both.
Note: I've added the superfluous stuff like db2_scrollable and db2_num_rows later on in attempts to solve the problem. Unsuccessfully.
EDIT: I can even FILTER by the values that won't display ( SELECT column WHERE column = 'value') and it'll return the correct number of rows.

MySql affected_rows always 0 but UPDATE works

The following code in question always logs that the affected_rows was 0. I check my DB, the row updates fine everytime. Why is it 0?
public static function updateNextRunTime($nextRunTime)
{
$mysqli = new mysqli(GDB_HOST, GDB_USERNAME, GDB_PASSWORD, GDB_NAME);
if ($mysqli->connect_errno)
{
throw new Exception('DB Connection Failed. Error Code: ' . $mysqli->connect_errno);
}
$cmd = $mysqli->prepare("UPDATE balanceagent SET NextRunTime = ? WHERE Id = 1");
if (!$cmd)
{
throw new Exception($mysqli->error);
}
$cmd->bind_param('s', $nextRunTime);
$cmd->execute();
$rows = $mysqli->affected_rows;
$cmd->close();
$mysqli->close();
if ($rows != 1)
{
logToFile("Balance Agent Error - Failed to update the next run time! - Affected rows: " . $rows);
}
}
For prepared statements you should be using the mysqli_stmt::affected_rows form :
$cmd->bind_param('s', $nextRunTime);
$cmd->execute();
$rows = $cmd->affected_rows;
Your should check
$cmd->affected_rows;
instead of
$mysqli->affected_rows;

How to Process more than 100 mysql query with rollback feature?

Currently I am developing a system in which at a point I have to fire 100 inter dependent queries like,
In my script (Currently doing),
$query1 = mysql_query("INSERT_QUERY_STRING");
if(!$query) {
die('error'.mysql_error());
}
//this loop can be turn more than 100 times
while(CONDITION) {
//query generated here
$query2 = mysql_query("UPDATE_QUERY_STRING");
$query3 = mysql_query("UPDATE_QUERY_STRING");
$query4 = mysql_query("UPDATE_QUERY_STRING");
$query5 = mysql_query("UPDATE_QUERY_STRING");
if(!$query2 || !$query3 || !$query4 || !$query5) {
die('error '.mysql_error());
}
}
$query6 = mysql_query("UPDATE_QUERY_STRING");
if(!$query6) {
die('error '.mysql_error());
}
Ignore syntax error, it's just a logic
The problem is, since all the queries are depended on each other, if one fails then all the changes occurred before should be revoked. I know one solution of using MYSQL TRANSACTION, COMMIT AND ROLLBACK. Here's what I figured out (planning to do this),
$processes = array();
$processes[] = "INSERT_QUERY_STRING"; //query1
//this loop can be turn more than 100 times
while(CONDITION) {
//query generated here
$processes[] = "UPDATE_QUERY_STRING"; //looping queries
}
$processes[] = "UPDATE_QUERY_STRING" //last query
mysql_query("SET AUTOCOMMIT=0");
mysql_query("START TRANSACTION");
$error = false;
for($i=0;$i < count($processes) ; $i++)
{
$q = mysql_query($processes[$i]);
if(!$q) {
$error = true;
break;
}
}
if($error == false) {
mysql_query("COMMIT");
}
else {
mysql_query("ROLLBACK");
}
Is my solution good / best ? Is there any other possible solution do same stuff more quickly and effectively ? Thanks.
I think that will be better use try/catch in tour code:
$processes = array();
$processes[] = "INSERT_QUERY_STRING"; //query1
//this loop can be turn more than 100 times
while(CONDITION) {
//query generated here
$processes[] = "UPDATE_QUERY_STRING"; //looping queries
}
$processes[] = "UPDATE_QUERY_STRING"; //last query
mysql_query("SET AUTOCOMMIT=0");
mysql_query("START TRANSACTION");
$count_processes = count($processes);
for($i=0; $i < $count_processes; $i++)
{
try {
$q = mysql_query($processes[$i]);
if (!$q) throw new Exception(mysql_error());
}
catch (Exception $e) {
mysql_query("ROLLBACK");
break;
}
}
mysql_query("COMMIT");
mysql_query("SET AUTOCOMMIT=1");
Use try/catch is better because you have control over the code and error :)
Transactions are definitely the best way to go here. I'd put insert queries in try-catch statement to cover the unpredictable cases and use foreach for traversing $processes

Categories