Can I detect and handle MySQL Warnings with PHP? - php

I'm dealing with a MySQL table that defines the JobName column as UNIQUE. If somebody tries to save a new Job to the database using a JobName that is already in the database, MySQL throws a warning.
I would like to be able to detect this warning, just like an error, in my PHP script and deal with it appropriately. Ideally I would like to know what kind of warning MySQL has thrown so that I can branch the code to handle it.
Is this possible? If not, is it because MySQL doesn't have this ability, PHP doesn't have this ability, or both?

For warnings to be "flagged" to PHP natively would require changes to the mysql/mysqli driver, which is obviously beyond the scope of this question. Instead you're going to have to basically check every query you make on the database for warnings:
$warningCountResult = mysql_query("SELECT ##warning_count");
if ($warningCountResult) {
$warningCount = mysql_fetch_row($warningCountResult );
if ($warningCount[0] > 0) {
//Have warnings
$warningDetailResult = mysql_query("SHOW WARNINGS");
if ($warningDetailResult ) {
while ($warning = mysql_fetch_assoc($warningDetailResult) {
//Process it
}
}
}//Else no warnings
}
Obviously this is going to be hideously expensive to apply en-mass, so you might need to carefully think about when and how warnings may arise (which may lead you to refactor to eliminate them).
For reference, MySQL SHOW WARNINGS
Of course, you could dispense with the initial query for the SELECT ##warning_count, which would save you a query per execution, but I included it for pedantic completeness.

First, you should turn warnings off so that your visitors don't see your MySQL errors. Second, when you call mysql_query(), you should check to see if it returned false. If it did, call mysql_errno() to find out what went wrong. Match the number returned to the error codes on this page.
It looks like this is the error number you're looking for:
Error: 1169 SQLSTATE: 23000 (ER_DUP_UNIQUE)
Message: Can't write, because of unique constraint, to table '%s'

ini_set('mysql.trace_mode', 1)
may be what you are looking for.
The PHP errors can then be handled with a custom PHP error handler, but you can also just turn off displaying php errors as they are usually logged into a log file (depends on your php configuration).

depending on what (if any) framework you're using, I suggest you do a query to check for the jobname yourself and create the proper information to user in with the rest of the validations for the form.
Depending on the number of jobnames, you could send the names to the view that contains the form and use javascript to tell use which is taken.
If this doesnt make sense to you, then to sum my view it's this: dont design your program and / or user to try to do illegal things and catch the errors when they do and handle it then. It is much better, imho, to design your system to not create errors. Keep the errors to actual bugs :)

Updated to remove the stuff about errno functions which I now realize don't apply in your situation...
One thing in MySQL to be wary of for UPDATE statements: mysqli_affected_rows() will return zero even if the WHERE clause matched rows, but the SET clause didn't actually change the data values. I only mention this because that behaviour caused a bug in a system I once looked at--the programmer used that return value to check for errors after an update, assuming a zero meant that some error had occurred. It just meant that the user didn't change any existing values before clicking the update button.
So I guess using mysqli_affected_rows() can't be relied upon to find such warnings either, unless you have something like an update_time column in your table that will always be assigned a new timestamp value when updated. That sort of workaround seems kinda kludgey though.

You can detect Unique key violations using mysqli statement error no. The mysqli statement returns error 1062 , that is ER_DUP_ENTRY. You can look for error 1062 and print a suitable error message. If you want to print your column (jobName) also as part of your error message then you should parse the statement error string.
if($stmt = $mysqli->prepare($sql)){
$stmt->bind_param("sss",
$name,
$identKey,
$domain);
$stmt->execute();
if($mysqli->affected_rows != 1) {
//This will return errorno 1062
trigger_error('mysql error >> '.$stmt->errno .'::' .$stmt->error, E_USER_ERROR);
exit(1);
}
$stmt->close();
} else {
trigger_error('mysql error >> '. $mysqli->errno.'::'.$mysqli->error,E_USER_ERROR);
}

It is possible to get the warnings, and in a more efficient way with mysqli than with mysql.
Here is the code suggested on the manual page on php.net for the property mysqli->warning_count:
$mysqli->query($query);
if ($mysqli->warning_count) {
if ($result = $mysqli->query("SHOW WARNINGS")) {
$row = $result->fetch_row();
printf("%s (%d): %s\n", $row[0], $row[1], $row[2]);
$result->close();
}
}

Note on suppressing warnings: Generally, it is not a good idea to prevent warnings from being displayed since you might be missing something important. If you absolutely must hide warnings for some reason, you can do it on an individual basis by placing an # sign in front of the statement. That way you don't have to turn off all warning reporting and can limit it to a specific instance.
Example:
// this suppresses warnings that might result if there is no field titled "field" in the result
$field_value = #mysql_result($result, 0, "field");

Related

Insert multiple checkbox answers into database

I have a checkbox list and I want to insert each item checked into the database. Since it is a checkbox, I only have one value to pass to the database, but I am also passing a variable (primary key) grabbed from a previous database insertion.
My problem is that I can't get it to post to the database. I have tried multiple ways to do this through posts here and nothing seems to work. I am at a loss. I am also fairly new to PHP and mysqli, so I am sure there are better ways to do what I am trying to do, so please help me out.
The table consists of three columns (school_id, graduate_id, schoolName). The school_id is the AI primary key and graduate_id will be populated with the variable $graduateID grabbed from the previous query.
Here is what I have:
if (isset($_POST['school'])) {
$school=$_POST['school'];
$schoolQuery="";
foreach($school as $value) {
if(!$schoolQuery) {
$schoolQuery="INSERT INTO schoolReunion (graduate_id, schoolName) VALUES ($graduateID, '$value')";
} else {
$schoolQuery .= ", ($graduateID, '$value')";
mysqli_store_result($schoolQuery);
}
}
$schoolQuery .=";";
if (mysqli_multi_query ($schoolQuery)) {
echo "Files have been updated successfully.";
} else {
echo "Error: " . $schoolQuery . "<br/>" . mysqli_error($dbc);
}
}
When the error code prints out, it looks fine, but it just won't post anything and gives me no errors. Can you help me?
#Maximus & #Eko Junaidi Salam, while you may be right, I don't see any evidence that there are issues with the front-end/form.
#jeroen, you are totally correct.
CarR is dealing with a single concatenated query, so mysqli_query is suitable.
I recommend wrapping the user-supplied variable in mysqli_real_escape_string() to cover the query vulnerability.
mysqli_store_result() is to be used on queries that return a result set; mysqli_affected_rows() is the function that yields a measure of success with INSERT/UPDATE/DELETE type queries.
Lastly, you are right to correct MrTechie. I don't think he realized the query concatenation.
Assuming the form is delivering the necessary values, I'll suggest a new query build section:
$schoolQuery="";
foreach($school as $value){
if(!$schoolQuery) {
$schoolQuery="INSERT INTO schoolReunion (graduate_id,schoolName) VALUES ";
}else{
$schoolQuery.=",";
}
$schoolQuery.="('$graduateID','".mysqli_real_escape_string($dbc,$value)."')";
}
$schoolQuery.=";";
$graduateID IS NOT escaped because I am assuming it comes from a safe place; $value IS escaped because it comes from user input.
Now to deliver the built query. Your code has syntax and logic errors, so try this:
if($schoolResult=mysqli_query($dbc,$schoolQuery)){
$total_rows=mysqli_affected_rows($dbc);
echo $total_rows," file",($total_rows!=1?"s have":" has")," been added.";
// if($total_rows<1){echo "Query Logic Error, # $schoolQuery";}
}else{
// echo "Query Syntax Error # $schoolQuery<br>".mysqli_error($dbc);
}
Uncomment the error lines while you are testing, then re-comment or delete for production.
This should sufficiently fix all the issues that are apparent in your snippet of code. Beyond that are the assumed issues...
Since you mention that you are employing a previous query to declare $graduateID, I will assume you have acquired the value via a SELECT query. Be sure to check that you have used
mysqli_free_result($graduateResult); // I assumed this variable name
to avoid any conflicts with subsequent queries. This is sometimes overlooked.
mysqli_multi_query() is best used when you are dealing with queries that are dependent on a prerequisite query. It seems your $schoolQuery is dependent on the success of what I will call "$graduateQuery". If you wish, you could use mysqli_multi_query() to run $graduateQuery then $schoolQuery. To help you with this implementation, I would need to see more of your code. If you wish to go down that road, it might be best to message me directly or start a new post (after you've had a try at it yourself.) Here is half of your job done: Strict Standards: mysqli_next_result() error with mysqli_multi_query

When to provide error control for mysqli queries (PHP)

Okay, so I have some code here:
<?php
$rt = 'abc'; $imdb = 'defg';
if ($con = mysqli_connect($a,$b,$c,$d)) {
if (mysqli_query($con,"DELETE FROM blah WHERE a = '{$imdb}'")){
echo 'Deleted!';
if (mysqli_query($con, "INSERT INTO foo (c,d) VALUES ('{$rt}','{$imdb}')")){
echo 'Inserted after deletion!'
if (mysqli_query(...)) {
if (mysqli_query(....)) {
}
}
}
}
}
Some of my programs have many queries in a row, each of which is dependent on the result of a previous query. However, creating error handling for every single query and making sure that the logic of the code stays the same (not to mention staying readable) throughout can be a somewhat tedious and error prone process.
I would like to know a bit more about what is going on behind the scenes when a query is sent, because it would help me a lot with some of my questions. Namely, is it really necessary to write if (mysqli_query()) all the time for correct error handling, or is simply checking if the so-called 'master' connection exists enough?
That is, after I check mysqli_connect(), do I have to check every subsequent query within that connection to see if it went through (connection-wise, not logic-wise), or is it simply enough to check mysqli_connect() once, at the beginning of the program? It would sure make things a lot easier.
Also, while I'm looking at mysqli_multi_query() for more practical query management, I would prefer not to use it until I can fully understand the simpler query functions.
Thanks for your help!
Only few things for you to get it right
You have to distinguish an error from a query result. If your query depends on the result of the another one - it's all right to check the result. But if you want to check for the error - there are better ways.
In a properly written application a query error is a highly exceptional event, and there shouldn't be code written to handle it in place. It have to be done somewhere else.
mysqli can throw an exception in case of error, which is actually a Holy Grail you are looking for.
So, if you want to stop your code if one of queries failed, just set mysqli in exception mode and then pile your queries one after another.
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$con = mysqli_connect($a,$b,$c,$d);
$rt = 'abc'; $imdb = 'defg';
mysqli_query($con,"DELETE FROM ...");
mysqli_query($con, "INSERT INTO ...");
mysqli_query(...);
mysqli_query(...);
...
And two additional notes
if you want to undo a previous query if following one failed, then use transactions
you should NEVER write a query like you do, interpolating a variable directly into it. You ought to use prepared statements, substituting every variable with placeholder in the query.

Can I safely assume this INSERT succeeded?

I'm using MySQL with strict mode turned on. I am inserting data supplied by user through an HTML form. I've already done all my validation and this is the final step.
Here is an excerpt from my PHP code:
$sql = 'SET sql_mode = TRADITIONAL'; //last chance data validation
$mysqli->query($sql) or output_error('Database Error: Failed to set SQL mode');
$db_err_msg = 'Database Error: Failed to update profile summary';
$sql = "INSERT INTO {$tables['text']} (lngProfileId, memProfileText) VALUES(?, ?)
ON DUPLICATE KEY UPDATE memProfileText = VALUES(memProfileText)";
$stmt = $mysqli->prepare($sql) or output_error($db_err_msg);
$stmt->bind_param('is', $profile_id, $_POST['memProfileText'])
or output_error($db_err_msg);
$stmt->execute() or output_error($db_err_msg);
$stmt->close();
//if code reaches this point, can it assume insert/update succeeded?
Note: output_error is my error handler for fatal errors. It doesn't return; it exits.
Do I need to check for warnings? Or can I safely assume the insert/update succeeded? Is there some scenario that could cause a warning instead of an error even with strict mode turned on?
EDIT: I'm only concerned about things that should be considered fatal errors. If it was updated rather than inserted, that is of no concern to me. That's not an error.
Also, last_insert_id and affected_rows are meaningless in this case. If the user didn't make any changes, affected_rows will be 0 and the value of last_insert_id will not be updated.
My definition of succeeded in this case:
it inserted the record or updated the record or did nothing if no changes were made
it did not 'silently mangle my data'
Many newbie programmers make the mistake of assuming that a query succeeded without ever checking the return value or warnings. Their code works... under normal conditions.
My question is: "Considering my code, with strict mode turned on, and the error checking I've done, is it safe to assume the query succeeded? Is that a correct or incorrect assumption? Should I also check for warnings or would that be redundant?"
You should check the value of mysqli::info() function.
If it inserts data the string will look something like this,
Records: 1 Duplicates: 0 Warnings: 0
Another function is mysqli::affected_rows. it returns the number of rows affected for last insert/update/delete query.
Yes you can safely assume that, but just for a double check Fetch the ID of the latest record that is inserted. If there is an id, it was inserted then
While I think that it's probably a safe assumption, I figured better safe than sorry. I've added the following code directly after the execute statement before the close statement.
//I think this is probably redundant, but just in case
if ($mysqli->warning_count) {
$warnings = $stmt->get_warnings();
do {
trigger_error('Database Warning (' . $warnings->errno . '): '
. $warnings->message, E_USER_WARNING);
} while ( $warnings->next() );
}
I've added the comment so that I remember why I did that.

The way PDO parametrized query works

PLEASE READ THE QUESTION CAREFULLY. It is not usual silly "my code doesn't work!!!" question.
When I run this code with intended error
try {
$sth = $dbh->prepare("SELECT id FROM users WHERE name INN(?,?) ");
$sth->execute(array("I'm","d'Artagnan"));
} catch (PDOException $e) {
echo $e->getMessage();
}
I get this error message
You have an error in your SQL syntax ... near 'INN('I\'m','d\'Artagnan')' at line 1
But I thought for years that query and data being sent to the server separately and never interfere. Thus I have some questions (though I doubt anyone got an answer...)
Where does it get such a familiar string representation - quoted and escaped? Is it being made especially to report an error or is it a part of actual query?
How does it work in real? Does it substitute a placeholder with data or not?
Is there a way to get whole query, not only little bit of it, for debugging purposes?
Update
mysqli does it as expected: it throws an error says near 'INN(?,?)'
try adding
$dbh->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
;)
I'm not sure about all the details, but I will try to answer.
The quotation happens on the database side. The database escapes and sanitizes all values (see bullet 2) it receives so that it gets interpreted correctly.
The moment the error is thrown, the database (in this case MySQL) prints out the query it tried to run. This wouldn't be so helpful if it just showed the prepared part.
No, it doesn't. At preparation time the query gets compiled on the server side. When a query is executed with values, only the values are transmitted. This is pretty much the same as calling PREPARE and EXECUTE on the database directly.
This depends on the database you're using. MySQL for example can log all queries to a log file (check my.cnf settings for that). But you can also use debugDumpParams() on PHP side.
I hope this was a bit helpful.

mysql query occasionally returns nothing

We have a function used within our PHP/MySQL application which returns basic configuration information, it contains a simple select query and looks like this:
public function getConfigurationValue($field)
{
$res = mysql_query("SELECT `cfg_value` FROM `ls_config` WHERE `cfg_name` = '".mysql_real_escape_string($field)."'");
$cfg = htmlspecialchars(mysql_result($res,0));
return $cfg;
}
This problem we are having is that occasionally, seemingly at random, this query throws a mysql error on mysql_result saying that "supplied argument is not a valid mysql result resource". In our debugging we have determined though that this is not because $field is not being passed. Essentially, for a reason we cannot determine a perfectly valid query fails and returns no results causing an empty result set and the subsequent error. If the error was due to the mysql connection failing the script would have died well before this. Also, this function may be called 50-100 times on some page loads but it only tends to fail once on each load.
Please let me know if you need any other information to work this out.
Thanks.
searching for php "supplied argument is not a valid mysql result resource" reveals that to get the actual error, you'd need to call mysql_error, and the error that you get is because the result of the query is FALSE - this value not being a valid mysql result resource.
i.e. in short you have something like:
$res = FALSE; # should contain the mysql result but does not, due to error.
$cfg = htmlspecialchars(mysql_result($res,0)); # the attempt to call mysql_result on invalid argument errors out.
So you'd want to use something like this:
$query = "SELECT * FROM cats WHERE id=$id";
$qr1 = mysql_query ($query)
or die ("Query failed: " . mysql_error() . " Actual query: " . $query);
You might want to give this a shot and see what the underlying error message says.
Given that the error is "MySQL server has gone away", There can be multitude of reasons for it - this article would be a good start to investigate. Searching suggests also some php-related and stack-specific bugs, so it looks like you might need to debug it with a closer attention.
Maybe try to duplicate the setup on another box and then start experimenting with the versions/settings, and see if any of the already reported scenarios match your case. Unfortunately, seems there's no single simple answer to this.

Categories