Database insert isn't executed in nested transaction - php

I want to check whether the user has changed the Document image in the edit form. If the user changed the image, I have to delete the old one from database and filesystem, then I have to add the new one (in both db and filesystem).
Problem: if I edit a Document which has already got an image on the DB (so if $oldImage = $this->getImageByDocumentId($docId) actually returns $oldImage), everything works. But if the Document hasn't got any $oldImage, something goes wrong and it doesn't insert the new Image on DB (but It saves it on filesystem!)
This is part of my MySQLDocumentService:
public function editDocument($document) {
try {
$conn = $this->getAdapter();
$conn->beginTransaction();
$sql = "UPDATE Documents d
SET d.name=:name, d.description=:description, d.content_id=:contentId, d.category_id=:categoryId, d.sharer_id=:sharerId, d.rating_id=:ratingId, d.price=:price
WHERE d.document_id=:id";
$prepStatement = $conn->prepare($sql);
$prepStatement->execute(array(':id' => $document->getId(),
':name' => $document->getName(),
':description' => $document->getDescription(),
':contentId' => rand(1,2000),
':categoryId' => $document->getCategory()->getId(),
':sharerId' => 1,
':ratingId' => 1,
':price' => $document->getPrice()));
// If image has been changed, take the old image name
if (!is_null($document->getImage())) {
$image = $document->getImage();
$docId = $document->getId();
$oldImage = $this->getImageByDocumentId($docId); // Here's the problem: if it doesn't find the oldImage, it doesn't insert the new one
if (!is_null($oldImage)) {
// If previous method succeeded, delete oldImage from DB and filesystem
$oldImageName = $oldImage->getName();
$this->deleteImageByName($oldImageName);
}
// Save new image (chosen on form) on db and filesystem
if (file_exists("uploads/img/" . $image->getName())) {
echo $image->getName() . " already exists. ";
return false;
} else {
move_uploaded_file($image->getTempName(), "uploads/img/" . $image->getName());
}
// Saves image path on DB
// If I edit a Document which has already got an image on the DB, everything works. But if the Document hasn't got any oldImage, something goes wrong and it doesn't insert the new Image on DB (but It saves it on filesystem!)
$sqlImage = 'INSERT INTO Images (name, alt_name, position, description, type, size, document_id)
VALUES ("name", "altName", 2, "description", "type", "size", 2)';
$prepStatementImg = $conn->prepare($sqlImage);
$prepStatementImg->execute();
}
$conn->commit();
return true;
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
}
public function getImageByDocumentId($docId) {
try {
$conn = $this->getAdapter();
$conn->beginTransaction();
$sql = 'SELECT i.image_id, i.document_id, i.name, i.alt_name, i.position, i.description, i.type, i.size
FROM Images i
WHERE i.document_id=:id';
$prepStatement = $conn->prepare($sql);
$prepStatement->execute(array(':id' => $docId));
$result = $prepStatement->fetch();
if ($result) {
$image = new Image();
$image->setName($result['name']);
$image->setId($result['image_id']);
$image->setAltName($result['alt_name']);
$image->setDescription($result['description']);
$image->setPosition($result['position']);
$image->setType($result['type']);
$image->setSize($result['size']);
// Manca la costruzione del relativo documento, ma non penso serva
$conn->commit();
return $image;
} else {
return null;
}
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
}
public function deleteImageByName($imgName) {
try {
$imgName = str_replace( array( '..', '/', '\\', ':' ), '', $imgName );
unlink( "uploads/img/" . $imgName );
} catch (Exception $fsEx) {
echo "Failed: " . $fsEx->getMessage();
}
try {
$conn = $this->getAdapter();
$conn->beginTransaction();
$sql = 'DELETE FROM Images
WHERE name=:name';
$prepStatement = $conn->prepare($sql);
$prepStatement->execute(array(':name' => $imgName));
$conn->commit();
return true;
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
}
If I comment $oldImage = $this->getImageByDocumentId($docId) it commits the INSERT of the new Image on DB and everything works.
I thought it could have been an issue with nested transactions, but it's strange since everything works when $oldImage is correctly found on db. (I also created a class that extends PDO class, as written on this guide).
What can I do?
EDIT: In one of the kind answers below (by Soyale), arose doubts about nested methods and multiple transitions. I thereby paste my MyPDO class, which should avoid multiple transitions (I hope so, at least). It's taken by bitluni's comment on PDO::beginTransaction manual page.
class MyPDO extends PDO {
protected $transactionCounter = 0;
function beginTransaction()
{
if(!$this->transactionCounter++)
return parent::beginTransaction();
return $this->transactionCounter >= 0;
}
function commit()
{
if(!--$this->transactionCounter)
return parent::commit();
return $this->transactionCounter >= 0;
}
function rollback()
{
if($this->transactionCounter >= 0)
{
$this->transactionCounter = 0;
return parent::rollback();
}
$this->transactionCounter = 0;
return false;
}
}
As Soyale stated,
parent::openTransaction [I think it's a typo for beginTransaction()] isn't good idea too. Or maybe if you have some flag that one of transaction was opened already it could pass the exam.
I think that transactionCounter could be the flag you're talking about. In my mind, this will let me commit and rollback properly. Am I wrong?

in my opinion You have mistake in this method: getImageByDocumentId.
You don't commit transaction in this fragmenty:
if ($result) {
$image = new Image();
$image->setName($result['name']);
$image->setId($result['image_id']);
$image->setAltName($result['alt_name']);
$image->setDescription($result['description']);
$image->setPosition($result['position']);
$image->setType($result['type']);
$image->setSize($result['size']);
// Manca la costruzione del relativo documento, ma non penso serva
$conn->commit();
return $image;
} else {
$conn->commit(); //Add this line :)
return null;
}
I'm wondering why are so many transactions there? It should be in one transaction so if one query fails then you can rollback all of them.
Some more words about transactions:
Lets look at your code:
public function editDocument($document) {
$conn = $this->getAdapter();
$conn->beginTransaction(); // 1-st open transaction
$this->getImageByDocumentId(...); // 2-nd opened transaction
$this->deleteImageByName(...); // And the third one
}
public function getImageByDocumentId($docId) {
$conn = $this->getAdapter();
$conn->beginTransaction();
//This method mainly select some data from DB so do you need transaction here ?
//Query in this method does not affect any data
//Data remains unchanged
//So you can use sth like this
$conn = $this->getAdapter();
//$conn->beginTransaction(); //-> tyhis line is useless
$sql = 'SELECT i.image_id, i.document_id, i.name, i.alt_name, i.position,
i.description, i.type, i.size
FROM Images i
WHERE i.document_id=:id';
$prepStatement = $conn->prepare($sql);
$prepStatement->execute(array(':id' => $docId));
$result = $prepStatement->fetch();
//(...) rest of code
}
public function deleteImageByName($imgName) {
$conn = $this->getAdapter();
$conn->beginTransaction();
}
as You can see every of yours method contains beginTransaction() It's a bit messy and lead to nested transactions and commits. I'm working mainly with Firebird DB and there if new transaction opened is the old one is moved down (we receive new resource handler).
Most interesting is deleteImageByName() method. As you can see there is already open one transaction (from editDocument()). Now You are opening the second one. Now you have delete your image deleteImageByName() has return true and commit transaction.
public function deleteImageByName($imgName) {
//In my opinion this fragment should go after successfully deleted from database
//and insert new image (prevent data loss)
try {
$imgName = str_replace( array( '..', '/', '\\', ':' ), '', $imgName );
unlink( "uploads/img/" . $imgName );
} catch (Exception $fsEx) {
echo "Failed: " . $fsEx->getMessage();
}
//here you are deleting db record
try {
$conn = $this->getAdapter();
$conn->beginTransaction();
$sql = 'DELETE FROM Images
WHERE name=:name';
$prepStatement = $conn->prepare($sql);
$prepStatement->execute(array(':name' => $imgName));
//And you are commiting this (bad idea if there is more than only delete task)
$conn->commit();
return true;
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
}
Now if from some reason the insert fails, then you don't have new image nor the old one.
If there will be only one transaction (in main method editDocument()) you can rollback transaction and don't delete old image.
parent::openTransaction isn't good idea too. Or maybe if you have some flag that one of transaction was opened allready it could pass the exam.
Generally you should open transaction for one task. Lets suppose that your task is: editDocument. editDocument isn't simple action. It consist of bunch of other actions so the transaction (only one transaction from master method) should include all of these actions. (in your case delete old image and insert new one). Stomething like this:
public function editDocument() {
$conn = $this->getAdapter();
$conn->beginTransaction();
//1. $this->deleteOldImage();
//2. $this->insertNewOne();
//3. $this->deleteFileWithOldImage();
//Of every method should consist fail statement: $conn->rollback(); and throw exception
$conn->commit();
}
Sorry for my english :)
Edit: -> Why your class isn't so good
#KurtBourbaki This extension looks nice but it isn't. Please note that if you forgot to commit opened transaction then you continued with mess. In your question there was one mistake. Missing line. Please try to use your class with that bug. How it works ? Lets analize it:
class MyPDO extends PDO {
protected $transactionCounter = 0;
//1. Increment counter regardless of whether it was set
//2. PDO::beginTransaction() only if counter was 0
function beginTransaction()
{
if(!$this->transactionCounter++)
return parent::beginTransaction();
return $this->transactionCounter >= 0;
}
//This is interesting
//1. decrement counter
//2. PDO::commit() but only when decrement counter == 0
//So there is a core place because even with that class your primary bug will occur
//because You have omitted exactly this one command.
function commit()
{
if(!--$this->transactionCounter)
return parent::commit();
return $this->transactionCounter >= 0;
}
//rollback transaction looks good
}
I don't know why this answer was voted so high on php.net.
I saw there better solution than that. There is solution with simply private boolean flag posted by drm at melp dot nl on http://pl1.php.net/manual/en/pdo.begintransaction.php.
I prefer this one because it really doesn't allow to open multiple transaction.
EDIT:
As Kurt pointed out my choice isn't good either.
So as I have written in my last comment. My preferred solution is to never open nested transaction. There are some informations about transactions in documentation for DBMS. Most popular DB MySQL

try replacing, inside getImageByDocumentId function,
if($result)
by
if($prepStatement->rowCount()>0)
I think you're entering into the if when $prepStatement is executed, whether it has results or not, so your image is being empty, causing your script to break

If you insert data directly in your query you must have a $pdo->query not $pdo->prepare, well that's what I think
I believe your query should be
// Saves image path on DB
// If I edit a Document which has already got an image on the DB, everything works. But if the Document hasn't got any oldImage, something goes wrong and it doesn't insert the new Image on DB (but It saves it on filesystem!)
$sqlImage = 'INSERT INTO Images (name, alt_name, position, description, type, size, document_id)
VALUES (:name, :altName, :position, :description, :type, :size, :id)';
$prepStatementImg = $conn->prepare($sqlImage);

Related

Delete / Truncate table in Symfony 3.4 not working as it should

I am trying to import data with Symfony 3.4. And I have successfully finished the task. In the end, I run into some problems, and I don't know how to solve it. So, what am I doing:
taking data from an Excel file and store it in the database.
on refresh, I delete the table and again go through the excel file and store values in the database
I just saw that primary keys are getting updated. It is not a problem, but it bothers me. So if I add 130 rows, delete them, store them again, the primary key will start from 260, and back from 390 and so on.
So I decided not to delete a table, but to truncate it. And the first time it creates 130 rows. On refresh, it truncates the table but stores only five rows instead of 130, and I cannot find the bug. So on delete everything works fine, but on truncate, the second time it stores only five rows instead of 130. Here is my code, and if someone sees the bug, please let me know:
public function importMappedAttributes($mappedAttributesValues)
{
if (!$this->checkIfTableIsEmpty()) {
$this->truncateTable();
}
try{
foreach ($mappedAttributesValues as $value) {
$mappedAttributes = new MappedAttribute();
$mappedAttributes->setAkeneoAttribute($value['result_one']);
$mappedAttributes->setXmlAttribute($value['result_two']);
$mappedAttributes->setXmlPath($value['result_three']);
$mappedAttributes->setMetadata($value['result_four']);
$this->getEntityManager()->persist($mappedAttributes);
$this->getEntityManager()->flush();
}
} catch (\Exception $e){
throw new \Exception('Something went wrong!');
}
}
The first part checks if the table is empty and if is not truncate it:
private function checkIfTableIsEmpty() {
$numberOfRows = count($this->getEntityManager()->getRepository('ImportAttributesBundle:MappedAttribute')->findAll());
if ($numberOfRows > 1) {
return false;
}
return true;
}
public function truncateTable() {
$em = $this->getEntityManager();
$classMetaData = $em->getClassMetadata('ImportAttributesBundle:MappedAttribute');
$connection = $em->getConnection();
$dbPlatform = $connection->getDatabasePlatform();
try {
$connection->query('SET FOREIGN_KEY_CHECKS=0');
$q = $dbPlatform->getTruncateTableSql($classMetaData->getTableName());
$connection->executeUpdate($q);
$connection->query('SET FOREIGN_KEY_CHECKS=1');
$connection->commit();
}
catch (\Exception $e) {
$connection->rollback();
}
}
The problem was that $this->getEntityManager()->flush(); should go outside the foreach. That was causing unexpected behavior and when I put it after the foreach everything worked fine.

Fallback Overkill?

I have had no formal teaching in coding and was hoping if anyone could tell me if I was being to cautious with my code?
// Insert info to the db
if ($stmt = $db_connect->prepare("INSERT INTO db (col1, col2) VALUES (?, ?)")) {
if(!$stmt->execute([$val1, $val2])) {
exit("1:Faild to create deal");
}
// Get last id
$id = (int)$db_connect->lastInsertId();
$stmt->closeCursor();
} else { exit("0:Faild to create deal"); }
// Create the folder
if(!mkdir("folder/folder".$id)) {
if($stmt = $db_connect->prepare("DELETE FROM db WHERE id=?")) {
if(!$stmt->execute([$id])) {
exit("1:Faild to create the directory -> Faild to remove the row from the database");
}
exit("Faild to create the directory");
}
exit("0:Faild to create the directory -> Faild to remove the row from the database");
}
I repeat the create folder statement 2 more times with the same layout. It's just repeatable code that looks to be overkill.
Note: The package I have with my host only has MyISAM tables so I can't use Rollback.
If something fails I want to undo everything that has passed.
Could someone please give me some guidance to best practices or am I doing it right?
i re-structured and extended your code plus added a bit of simple error handling by using exceptions.
first you should set your PDO error handling to exception mode:
$db_connect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
then i have capsulated your actions into functions, which you can put into a separate file and include it, or even nest them into classes:
/*** FUNCTIONS ***/
// insert info to the db
function dbInsertInfo($db_connect, $val1, $val2)
{
$stmt = $db_connect->prepare("INSERT INTO db (col1, col2) VALUES (?, ?)");
$stmt->execute([$val1, $val2]));
}
//-------------------------------
// get the last insert id
function dbGetId($db_connect)
{
$id = (int)$db_connect->lastInsertId();
$stmt->closeCursor();
return $id;
}
//-------------------------------
// delete db-entry
function dbDeleteId($db_connect, $id)
{
$stmt = $db_connect->prepare("DELETE FROM db WHERE id=?");
$stmt->execute([$id]);
}
//-------------------------------
// create the folder
function createFolder($id)
{
if(!mkdir("folder/folder".$id)) throw new Exception("Failed to create the directory");
}
//-------------------------------
then here is your procedure with all the try{ } catch{ } sections for the error handling by exceptions:
/* PROCEDURE */
// 01 | try to insert into db
try
{
dbInsertInfo($db_connect, $val1, $val2);
}
catch(PDOException $e)
{
//if exception thrown, do not continue the script:
echo "Unable to insert into DB: ".$e->getMessage();
exit();
}
//-------------------------------
// 02 | try to get last insert id
$id = false;
try
{
$id = dbGetId($db_connect);
}
catch(PDOException $e)
{
//if exception thrown, do not continue the script:
echo "Unable to get last insert id from DB: ".$e->getMessage();
exit();
}
//-------------------------------
// 03 | try to create folder // if it fails -> try to delete db entry
try
{
createFolder($id);
}
catch(Exception $e)
{
// if exception caught, try to remove the corresponding DB entry:
echo $e->getMessage();
echo "<br />";
echo "trying to remove DB entry now";
// try to delete db entry
try
{
dbDeleteId($db_connect, $id);
}
catch(PDOException $e)
{
//if exception thrown, do not continue the script:
echo "Unable to delete from DB: ".$e->getMessage();
exit();
}
}
//-------------------------------
/* Everything worked fine if you get to this point of the code*/
just might seem like an technical overkill for you now, but i think it's much more structured and better to read it, once you got into it. plus, it's only divided into 3 steps.

PDO Active Record prepared statement in separate classes

I need to do continuous parsing of several external stomp data streams, inserts of relevant fields into a MySql db, and regular queries from the db. All of this is in a protected environment - ie I'm not dealing with web forms or user inputs
Because I'm implementing a range of inserts into + queries from different tables, I've decided to set up a PDO active record model - following the advice of Nicholas Huot and many SO contributors.
I've got a simple repeated insert working OK, but after several days of grief can't get a prepared insert to fly. I want to use prepared inserts given there are going to be a lot of these (ie for performance).
Relevant bits of the code are :
=========
Database class :
private function __construct()
{
try {
// Some extra bad whitespace removed around =
$this->dbo = new PDO('mysql:host=' . DBHOST . ';dbname=' . DBNAME, DBUSER, DBPSW, $options);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
}
public static function getInstance()
{
if(!self::$instance)
{
self::$instance = new self();
}
return self::$instance;
}
public function prepQuery($sql) // prepared statements
{
try {
$dbo = database::getInstance();
$stmt = new PDOStatement();
$dbo->stmt = $this->$dbo->prepare($sql);
var_dump($dbo);
}
catch (PDOException $e) {
echo "PDO prepare failed : ".$e->getMessage();
}
}
public function execQuery($sql) // nb uses PDO execute
{
try {
$this->results = $this->dbo->execute($sql);
}
catch (PDOException $e) {
echo "PDO prepared Execute failed : \n";
var_dump(PDOException);
}
}
=========
Table class :
function storeprep() // prepares write to db. NB prep returns PDOstatement
{
$dbo = database::getInstance();
$sql = $this->buildQuery('storeprep');
$dbo->prepQuery($sql);
return $sql;
}
function storexecute($paramstring) // finalises write to db :
{
echo "\nExecuting with string : " . $paramstring . "\n";
$dbo = database::getInstance(); // Second getInstance needed ?
$dbo->execQuery(array($paramstring));
}
//table class also includes buildQuery function which returns $sql string - tested ok
=======
Controller :
$dbo = database::getInstance();
$movements = new trainmovts();
$stmt = $movements->storeprep(); // set up prepared query
After these initial steps, the Controller runs through a continuous loop, selects the fields needed for storage into a parameter array $exec, then calls $movements->storexecute($exec);
My immediate problem is that I get the error message "Catchable fatal error: Object of class database could not be converted to string " at the Database prepquery function (which is called by the Table storeprep fn)
Can anyone advise on this immediate prob, whether the subsequent repeated executes should work in this way, and more widely should I change anything with the structure ?
I think your problem in this line $dbo->stmt = $this->$dbo->prepare($sql);, php want to translate $dbo to string and call function with this name from this. Actually you need to use $this->dbo.
And actually your functions not static, so i think you don't need to call getInstance each time, you can use $this.

PHP: my page takes a very long time to load but does not time out. Are there too many queries at once or can my code be optimized?

This is the function I use to access the DB, theoretically maybe up 10-20 times in a do while loop, though right now I removed the loop so it only can do one db query at a time. Can this be optimized more or is this how transactions and commits are properly done? Also, I don't know what $db->rollBack() actually does, I just saw it on a stackoverflow
<?php
function util_db_access($statement, $array) {
$db = new PDO('mysql:host=localhost;dbname=db;charset=UTF8', 'user', 'pass');
try {
//echo "1";
$db->beginTransaction();
//echo "2";
$stmt = $db->prepare($statement);
//echo "3";
if($stmt->execute($array)) {
$db->commit();
//echo "4";
if($rows = $stmt->fetchAll(PDO::FETCH_ASSOC)) {
//echo "5";
if($stmt->rowCount() < 2) {
$rows = $rows[0];
}
return $rows;
} else {
//echo "6.1";
//$db->commit();
return true;
}
} else {
//echo "6.2";
//$db->commit();
return false;
}
} catch(PDOException $e) {
$db->rollBack();
//log, we are gonna keep a log eventually.. right?
return -1;
}
}
?>
This thing can be optimized very quickly.
For starters you are creating a new connection to the database with every function call. I don't know for sure if the connection is closed when the PDO object goes out of scope but nevertheless it's bad design.
UPDATE
PHP will close the connection to the database when the PDO object is destroyed.
Reference http://php.net/manual/en/pdo.connections.php
Try using a profiler to determine where your bottleneck is - there's one included in xdebug. Given the simplicity of this code, it may be the query itself - try running the query standalone, either using the mysql cli client or MySQL Workbench, and see what timings you get back. If it is the query that's slow, you can use EXPLAIN and the bountiful optimization sections in the MySQL manual to improve it.
$db->rollBack() make a roll back for the transaction. If you don't know what transactions are there is no point for you to use them, because it creates an unnecessary overhead for the server.
commit() permanently writes the data from the query, rollback() undo everything to the state where you called beginTransaction()
Transactions are to be used when its crucial that changes in more then one place happens simultaneously, imagine a transaction of money between two accounts. On simple selects there is absolutely no need for transactions.
(I'm also sceptic to how you use try/catch)
However... If you run one query directly on the db server, is it also slow? Do you open/close the db connection between each call? (if so -(yes)- don't). What is the network relationship between the http/db server?
I can't help rewriting your function, removing useless parts and adding essential ones
function util_db_access($type, $statement, $array) {
static $db;
if (empty($db)) {
$dsn = 'mysql:host=localhost;dbname=db;charset=UTF8';
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$db = new PDO($dsn, 'user', 'pass', $opt);
}
$stmt = $db->prepare($statement);
$stmt->execute($array);
if($type == 'all') {
return $stmt->fetchAll();
}
if($type == 'row') {
return $stmt->fetch();
}
if($type == 'one') {
return $stmt->fetchColumn();
}
if($type == 'id') {
return $db->lastInsertId();
}
if($type == 'numrows') {
return $stmt->rowCount();
}
}
most likely this new version won't suffer any delays too
now you can use this great function without any delay or inconvenience:
$user = util_db_access("row", "SELECT * FROM users WHERE id = ?", array($someId));
$id = util_db_access("id", "INSERT INTO users SET name=?", array($name));
and so on
However, having just a function for this purpose is quite inconvenient, and I hope you will grow it into class soon. You can take my Safemysql class as a source of some ideas.

PHP Try and Catch for SQL Insert

I have a page on my website (high traffic) that does an insert on every page load.
I am curious of the fastest and safest way to (catch an error) and continue if the system is not able to do the insert into MySQL. Should I use try/catch or die or something else. I want to make sure the insert happens but if for some reason it can't I want the page to continue to load anyway.
...
$db = mysql_select_db('mobile', $conn);
mysql_query("INSERT INTO redirects SET ua_string = '$ua_string'") or die('Error #10');
mysql_close($conn);
...
Checking the documentation shows that its returns false on an error. So use the return status rather than or die(). It will return false if it fails, which you can log (or whatever you want to do) and then continue.
$rv = mysql_query("INSERT INTO redirects SET ua_string = '$ua_string'");
if ( $rv === false ){
//handle the error here
}
//page continues loading
This can do the trick,
function createLog($data){
$file = "Your path/incompletejobs.txt";
$fh = fopen($file, 'a') or die("can't open file");
fwrite($fh,$data);
fclose($fh);
}
$qry="INSERT INTO redirects SET ua_string = '$ua_string'"
$result=mysql_query($qry);
if(!$result){
createLog(mysql_error());
}
You can implement throwing exceptions on mysql query fail on your own. What you need is to write a wrapper for mysql_query function, e.g.:
// user defined. corresponding MySQL errno for duplicate key entry
const MYSQL_DUPLICATE_KEY_ENTRY = 1022;
// user defined MySQL exceptions
class MySQLException extends Exception {}
class MySQLDuplicateKeyException extends MySQLException {}
function my_mysql_query($query, $conn=false) {
$res = mysql_query($query, $conn);
if (!$res) {
$errno = mysql_errno($conn);
$error = mysql_error($conn);
switch ($errno) {
case MYSQL_DUPLICATE_KEY_ENTRY:
throw new MySQLDuplicateKeyException($error, $errno);
break;
default:
throw MySQLException($error, $errno);
break;
}
}
// ...
// doing something
// ...
if ($something_is_wrong) {
throw new Exception("Logic exception while performing query result processing");
}
}
try {
mysql_query("INSERT INTO redirects SET ua_string = '$ua_string'")
}
catch (MySQLDuplicateKeyException $e) {
// duplicate entry exception
$e->getMessage();
}
catch (MySQLException $e) {
// other mysql exception (not duplicate key entry)
$e->getMessage();
}
catch (Exception $e) {
// not a MySQL exception
$e->getMessage();
}
if you want to log the error etc you should use try/catch, if you dont; just put # before mysql_query
edit :
you can use try catch like this; so you can log the error and let the page continue to load
function throw_ex($er){
throw new Exception($er);
}
try {
mysql_connect(localhost,'user','pass');
mysql_select_db('test');
$q = mysql_query('select * from asdasda') or throw_ex(mysql_error());
}
catch(exception $e) {
echo "ex: ".$e;
}
Elaborating on yasaluyari's answer I would stick with something like this:
We can just modify our mysql_query as follows:
function mysql_catchquery($query,$emsg='Error submitting the query'){
if ($result=mysql_query($query)) return $result;
else throw new Exception($emsg);
}
Now we can simply use it like this, some good example:
try {
mysql_catchquery('CREATE TEMPORARY TABLE a (ID int(6))');
mysql_catchquery('insert into a values(666),(418),(93)');
mysql_catchquery('insert into b(ID, name) select a.ID, c.name from a join c on a.ID=c.ID');
$result=mysql_catchquery('select * from d where ID=7777777');
while ($tmp=mysql_fetch_assoc($result)) { ... }
} catch (Exception $e) {
echo $e->getMessage();
}
Note how beautiful it is. Whenever any of the qq fails we gtfo with our errors. And you can also note that we don't need now to store the state of the writing queries into a $result variable for verification, because our function now handles it by itself. And the same way it handles the selects, it just assigns the result to a variable as does the normal function, yet handles the errors within itself.
Also note, we don't need to show the actual errors since they bear huge security risk, especially so with this outdated extension. That is why our default will be just fine most of the time. Yet, if we do want to notify the user for some particular query error, we can always pass the second parameter to display our custom error message.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
I am not sure if there is a mysql version of this but adding this line of code allows throwing mysqli_sql_exception.
I know, passed a lot of time and the question is already checked answered but I got a different answer and it may be helpful.
$sql = "INSERT INTO customer(FIELDS)VALUES(VALUES)";
mysql_query($sql);
if (mysql_errno())
{
echo "<script>alert('License already registered');location.replace('customerform.html');</script>";
}
To catch specific error in Mysqli
$conn = ...;
$q = "INSERT INTO redirects (ua_string) VALUES ('$ua_string')";
if (mysqli_query($conn, $q)) {
// Successful
}
else {
die('Mysqli Error: '.$conn->error); // Show Error Complete Description
}
mysqli_close($conn);
Use any method described in the previous post to somehow catch the mysql error.
Most common is:
$res = mysql_query('bla');
if ($res===false) {
//error
die();
}
//normal page
This would also work:
function error() {
//error
die()
}
$res = mysql_query('bla') or error();
//normal page
try { ... } catch {Exception $e) { .... } will not work!
Note: Not directly related to you question but I think it would much more better if you display something usefull to the user. I would never revisit a website that just displays a blank screen or any mysterious error message.
$new_user = new User($user);
$mapper = $this->spot->mapper("App\User");
try{
$id = $mapper->save($new_user);
}catch(Exception $exception){
$data["error"] = true;
$data["message"] = "Error while insertion. Erron in the query";
$data["data"] = $exception->getMessage();
return $response->withStatus(409)
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
if error occurs, you will get something like this->
{
"error": true,
"message": "Error while insertion. Erron in the query",
"data": "An exception occurred while executing 'INSERT INTO \"user\" (...) VALUES (...)' with params [...]:\n\nSQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: \"default\"" }
with status code:409.

Categories