PHP MySQL create duplicate record on unique index strange fail
Action: creating table record that is a duplicate on a unique key.
Good: The unique key is working because the duplicate record is not created.
Bad: The mysqli functions that I am using in PHP are not failing in the way that I expect.
Meta: I have some PHP (running 5.3.6) that handles creating new records in a Mysql (5.5.9) table from AJAX POST requests. Authentication and input scrubbing and validation is handled outside of the scope of the code that I am sharing. The respond function also handles printing out the response in a valid format. The table I am inserting into has an auto incrementing id column and a unique name column.
// connect to server
$mysqli = new mysqli("localhost", "dbuser", 'dbpassword', "database");
if(mysqli_connect_errno()) {
$response["message"] = "unable to connect to the MySQL server at 'localhost' as 'dbuser' using a password or select the database 'database' because ".mysqli_connect_errno();
respond($response);
}
// build and run query
if ($stmt = $mysqli -> prepare("INSERT INTO `database`.`table` (`id`, `name`) VALUES (NULL, ?);")) {
$stmt -> bind_param("s", $inputs["name"]);
$success = $stmt -> execute();
if ($success) {
$response["status"] = "success";
$response["id"] = $mysqli -> insert_id;
respond($response);
} else {
$response["message"] = "failed to insert because ".$stmt -> error;
}
$stmt -> close();
} else {
$response["message"] = "unable to build query because ".$开发者_运维知识库mysqli -> error;
respond($response);
}
$mysqli -> close();
This works as expected when inserting a new unique name but doesn't return anything when attempting to insert a new duplicate name. I expected the
$stmt -> execute();
to fail so I would return a response explaining the error. This code doesn't do that. The only way I have found to return that the create failed because of a duplicate is to add 2 lines of code before the close statement like this:
// connect to server
$mysqli = new mysqli("localhost", "dbuser", 'dbpassword', "database");
if(mysqli_connect_errno()) {
$response["message"] = "unable to connect to the MySQL server at 'localhost' as 'dbuser' using a password or select the database 'database' because ".mysqli_connect_errno();
respond($response);
}
// build and run query
if ($stmt = $mysqli -> prepare("INSERT INTO `database`.`table` (`id`, `name`) VALUES (NULL, ?);")) {
$stmt -> bind_param("s", $inputs["name"]);
$success = $stmt -> execute();
if ($success) {
$response["status"] = "success";
$response["id"] = $mysqli -> insert_id;
respond($response);
} else {
$response["message"] = "failed to insert because ".$stmt -> error;
}
$stmt -> close();
} else {
$response["message"] = "unable to build query because ".$mysqli -> error;
respond($response);
}
$response["message"] = "Duplicate";
respond($response);
$mysqli -> close();
The thing I don't understand is when the query runs as expected, the duplicate response is not returned. When the query fails because it is trying to insert a duplicate on the unique key, this returns the duplicate response.
Question: Why does a successful insert not run the respond function at the end of the code block?
Question: Why does a failed insert because of attempting to insert a duplicate on a unique key not throw an exception or fail as I expected?
I have also tried wrapping the entire thing in a try catch block but it didn't help any because it's not throwing any exceptions. If you could shed any light on this matter I would greatly appreciate it.
Question: Why does a successful insert not run the respond function at the end of the code block?
I assume your respond
method has an exit
or die
in it somewhere that halts execution.
Question: Why does a failed insert because of attempting to insert a duplicate on a unique key not throw an exception or fail as I expected?
I think it's to do with the logic in your if
statements.
If your procedure ever gets into the else statement
$response["message"] = "failed to insert because ".$stmt -> error;
no response is ever sent (the respond
function is never called). The reason your second example works is because the code reaches that point every time (because its outside the last else
statement. If you move a call to respond
after the line above you might find it returns the message as expected. Full code might look like the following:
// connect to server
$mysqli = new mysqli("localhost", "dbuser", 'dbpassword', "database");
if(mysqli_connect_errno()) {
$response["message"] = "unable to connect to the MySQL server at 'localhost' as 'dbuser' using a password or select the database 'database' because ".mysqli_connect_errno();
respond($response);
}
// build and run query
if ($stmt = $mysqli -> prepare("INSERT INTO `database`.`table` (`id`, `name`) VALUES (NULL, ?);")) {
$stmt -> bind_param("s", $inputs["name"]);
$success = $stmt -> execute();
if ($success) {
$response["status"] = "success";
$response["id"] = $mysqli -> insert_id;
respond($response);
} else {
$response["message"] = "failed to insert because ".$stmt -> error;
respond($response); // <-- this is the bit you need
}
$stmt -> close();
} else {
$response["message"] = "unable to build query because ".$mysqli -> error;
respond($response);
}
$mysqli -> close();
Try to get error information from $stmt->errorCode()
and $stmt->errorInfo()
精彩评论