I'm trying to figure out a way to detect an occurrence of rollback in a MySQL stored procedure so I could handle the situation accordingly from a PHP script, but so far I can not find any solution.
My stored procedure looks like this:
delimiter |
create procedure multi_inserts(
IN var1 int(11),
.
.
.
IN string1 text
)
BEGIN
declare exit handler for sqlexception rollback;
declare exit handler for sqlwarning rollback;
START TRANSACTION;
insert into table1(a,b,c,d) values(var1,var2,var3,var4);
insert into table2(e,f,g) values(var5,var6,string1);
COMMIT;
END
delimiter ;
I did a rollback test on this procedure and it did rollback but I got no false.
I want my stored procedure to throw some kind of error message if the transaction failed, so I could handle it like this:
$result = mysql_query($procedure);
if(!$result)
{
//rollback occured do something
}
Is there a way to detect rollback in MySQL?
Am I missing something?
Any reply will be appreciated.
Thanks for reading.
Thanks to your advices I fixed this problem. Here's what I did:
Stored Procedure
delimiter |
create procedure multi_inserts(
IN var1 int(11),
.
.
.
IN string1 text
)
BEGIN
declare exit handler for sqlexception sqlwarning
BEGIN
rollback;
select -1;
END;
START TRANSACTION;
insert into table1(a,b,c,d) values(var1,var2,var3,var4);
insert into table2(e,f,g) values(var5,var6,string1);
COMMIT;
END
delimiter ;
If I use out variable instead of select -1, it gives me this error:
OUT or INOUT argument is not a
variable or NEW pseudo-variable in
BEFORE trigger
I don't know what did I wrong, but I couldn't fix this problem.
PHP script
$result=mysqli_query($con,$procedure);
if(is_object($result))
{
//rollback happened do something!
}
If the SP is successful it throws true.
You can add an output param and then set it to the value you want in your exit handlers.
Here's an example using your proc:
delimiter $$
create procedure multi_inserts(
IN var1 int(11),
.
.
.
IN string1 text,
OUT p_return_code tinyint unsigned
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
set p_return_code = 1;
rollback;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
set p_return_code = 2;
rollback;
END;
START TRANSACTION;
insert into table1(a,b,c,d) values(var1,var2,var3,var4);
insert into table2(e,f,g) values(var5,var6,string1);
COMMIT;
-- SUCCESS
set p_return_code = 0;
END $$
delimiter ;
You would usually do this PHP-side if you wanted to catch errors. Read http://php.net/manual/en/pdo.transactions.php for more information.
Hey do one thing, use OUTPUT variable and return 1 or 0 as result form SP and do what ever you want on this flag.
<?php
try {
$user='root';
$pass='';
$dbh = new PDO('mysql:host=localhost;dbname=dbname', $user, $pass,
array(PDO::ATTR_PERSISTENT => true));
echo "Connected\n";
} catch (Exception $e) {
die("Unable to connect: " . $e->getMessage());
}
try {
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
$dbh->exec("insert into salarychange (id, amount, changedate)
values (23, 50000, NOW())");
$dbh->commit();
}
catch (Exception $e) {
$dbh->rollBack();
echo "Failed: " . $e->getMessage();
}
?>
Related
Hi I have a stored procedure with a while inside which receives 2 parameters for the condition of the while. I make it to work like a for. If I executed it from SQL it works perfectly, but If I call it from PHP, the while doesn't respect the condition and suddenly ends.
The while insert an iterator number in each row like
1,a,b
2,a,b
3,a,b
etc. If I set the iterations from 1 to 1000 the loop stop without a reason in different interaction in every execution of the SP.
I have a try/catch and I can't detect the error.
I'm sure all the parameters are right.
Can someone help me to detect why the stored procedure can't end correctly?
I'm using PDO to access to SQL Server.
Store Procedure:
ALTER procedure [dbo].[sp_AddSealsFromPurchase]
#initialFolio int,
#finalFolio int,
#userDelivery_ID varchar(50),
#userReceives_ID varchar(50),
#color nvarchar(10),
#supplier_ID int,
#requisition_ID int,
#office_ID int,
#reference nvarchar(50)
as
begin
declare #now datetime;
set #now = getdate();
while #initialFolio<=#finalFolio Begin
begin try
INSERT into Seals (SealNo,SealType,Color,Supplier_ID,[Source],Office_ID,Requisition_ID,Reference)
Values (#initialFolio,1,#color,#supplier_ID,1,#office_ID,#requisition_ID,#reference)
INSERT INTO StatusSeal(Seal_ID,Status_ID,UserDelivery_ID,UserReceives_ID,StatusSeal_Date,UserDeliveryType,UserReceivesType)
Values(##Identity,1,#userDelivery_ID,#userReceives_ID,#now,1,1)
set #initialFolio = #initialFolio +1;
end try
begin catch
set #initialFolio = #initialFolio +1;
end catch
end
end
PHP:
function AddSealsFromPurchase($Data){
session_start();
$cnx=$this->connectSqlSrv();
$sth=$cnx->prepare("Exec sp_AddSealsFromPurchase ?,?,?,?,?,?,?,?,?");
$sth->bindParam(1, $Data["InitialFolio"]);
$sth->bindParam(2, $Data["FinalFolio"]);
$sth->bindParam(3, $Data["UserDelivery"]);
$sth->bindParam(4, $_SESSION['SESSINFOSEAL']['User_Usr']);
$sth->bindParam(5, $Data["Color"]);
$sth->bindParam(6, $Data["Supplier_ID"]);
$sth->bindParam(7, $Data["Requisition_ID"]);
$sth->bindParam(8, $_SESSION['SESSINFOSEAL']['Ofice_ID']);
$sth->bindParam(9, $Data["Reference"]);
session_write_close();
$retval=$this->ExecuteNoQuery($sth);
return json_encode($retval);
}
public function connectSqlSrv(){
try{
$dbCnx = new PDO("sqlsrv:Server=$this->server;Database=$this->db", $this->usr, $this->psw);
$dbCnx->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbCnx;
}
catch(PDOException $e){
echo $e;
die();
return null;
}
}
public function ExecuteNoQuery($sth)
{
$r=array('data'=>false,
'error'=>false,
'r'=>'');
try {
$sth->execute();
if ($r['r'] = $sth->rowCount()) {
$r['data'] = true;
}
} catch (PDOException $e) {
$r['error']=true;
$r['r'] = $e->getMessage();
}
return $r;
}
I am having a problem where a prepared MySQL stored procedure call runs fine in a transaction, and I see the expected results from the stored procedure, but the changes do not appear to be saving to the actual database.
The PHP side of things looks like this:
$options = array();
$db = new PDO("mysql:host=localhost;dbname=mydb", "myuser", "mypass", $options);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// ..... .... ... .. .
$response["error"] = true;
if ($db->beginTransaction() == true)
{
try
{
$stmt = $db->prepare("call Layout_Row_Add(:pageid, :position);");
// $jason->page_id
$stmt->bindValue(':pageid', (int)$jason->page_id, PDO::PARAM_INT);
// $jason->position
$stmt->bindValue(':position', (int)$jason->position, PDO::PARAM_INT);
$stmt->execute();
$response["dbg1"] = $jason->page_id;
$response["dbg2"] = $jason->position;
$response["intrans1"] = $db->inTransaction();
$row = $stmt->fetch();
$db->commit();
$response["intrans2"] = $db->inTransaction();
$response["new_row_id"] = $row["NewRowId"];
$response["error"] = false;
}
catch (PDOException $e)
{
$db->rollBack();
$response["errortext"] = "PDO exception: " . $e->getMessage();
}
catch (Exception $exc)
{
$db->rollBack();
$response["errortext"] = "Exception: " . $e->getMessage();
}
}
else
{
$response["errortext"] = "Couldn't start transaction";
}
The $response variable gets encoded into JSON and sent back to the browser, which gets this:
error false
dbg1 1
dbg2 3
intrans1 true
intrans2 false
new_row_id 21
Everything looks exactly like it should, new_row_id is at its expected value meaning the autoincrement field ticked up, and the debug fields and transaction info is as expected.
However, doing a select * in MySQL Workbench doesn't return any of these rows that were supposedly added by the procedure. Running the procedure itself in MySQL Workbench works fine, as in, the commit actually sticks. Here's the procedure itself:
CREATE DEFINER=`myuser`#`myhost` PROCEDURE `Layout_Row_Add`(PageId int, Position int)
BEGIN
declare NewRowId int unsigned default 0;
update pages_layout_rows set ordinal = ordinal + 1 where page_id = PageId and ordinal >= Position;
insert into pages_layout_rows (page_id, ordinal) values (PageId, Position);
set NewRowId = last_insert_id();
select NewRowId;
END
The table is set to InnoDB, so transaction support should be available. I don't really know what to try next.
Found it - it looks like if you don't consume all the resultsets, the transaction appears to get rolled back in the end anyway. A stored procedure call adds an empty resultset as the last resultset, so that's what's happening.
// ...
$row = $stmt->fetch();
// let's consume all resultsets
while($stmt->nextRowset() && $stmt->columnCount());
$sol->db->commit();
// ...
I have a trigger like this:
CREATE TRIGGER prevent_self_votes BEFORE INSERT ON votes
FOR EACH ROW
BEGIN
IF(new.user_id = (SELECT author_id FROM posts WHERE id=new.post_id)) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "you cannot vote for yourself";
END IF;
END;
Now I want to know, how can I use of that text you cannot vote for yourself in PHP?
Note: I use PDO.
You can do a try...catch and check for the result of statement execution to print error information like this:
Stub
create table votes (user_id int);
delimiter //
create trigger prevent_self_votes before insert on votes
for each row
begin
if (new.user_id = 12) then
signal sqlstate '45000' set message_text = 'You cannot vote for yourself, dude!';
end if;
end //
delimiter ;
PHP script
<?php
$db = new PDO('mysql:host=localhost;dbname=test', 'test', 'test');
$sql = 'insert into votes values (:user_id)';
$statement = $db->prepare($sql);
if ($statement === false) {
echo 'statement is false';
exit();
}
try {
$result = $statement->execute(array(':user_id'=>12));
if ($result === false) {
$error = $statement->errorInfo();
print_r($error);
echo "$error[2] ... is the error reported by trigger\n";
}
else {
print_r($result);
echo 'Inserted', "\n";
}
}
catch (PDOException $e) {
echo $e->getMessage();
}
?>
Result
$ php test.php
Array
(
[0] => 45000
[1] => 1644
[2] => You cannot vote for yourself, dude!
)
You cannot vote for yourself, dude! ... is the error reported by trigger
As you notice here, you could use the output of $statement->errorInfo()[2] to extract information provided by the trigger.
http://php.net/manual/en/pdo.errorinfo.php says that the first item in the array is SQLSTATE ANSI SQL error code, second item is driver specific error code and third item is driver specific error message.
If your query fails, check what caused the failure Look at the definition of PDOStatement::errorInfo.
I'm facing following error from my AJAX called php script. Fatal error: Call to a member function setFetchMode() on a non-object. Actually I have written a Stored procedure to create dynamic temporary table. Please find below the Stored procedure and php script for call stored procedure.
`CREATE DEFINER=`root`#`localhost` PROCEDURE `StoreValue`(IN `Clname` VARCHAR(55), IN `ip_a` VARCHAR(55), IN `port_n` INT, IN `table_1` VARCHAR(55), IN `variable1` VARCHAR(55), IN `moddate1` TIMESTAMP, IN `moddate2` TIMESTAMP)
BEGIN
DECLARE table_name VARCHAR(55);
SET #table_name := table_1;
set #sql_text:=concat('create temporary table ',#table_name,'(status varchar(100),moddate datetime,INDEX mod_index (moddate)) '," SELECT `status`,`moddate` FROM `globstats_graph` WHERE Cname=? and ip=? and port=? and Variable=? and status REGEXP '^[0-9]+$|^ON$' and moddate between ? and ?");
PREPARE stmt FROM #sql_text;
set #a=Clname;
set #b=ip_a;
set #c=port_n;
set #d=variable1;
set #e=moddate1;
set #f=moddate2;
EXECUTE stmt USING #a,#b,#c,#d,#e,#f;
END`
php code:
for connection
try {
$conn = new PDO("mysql:host=$hostname;dbname=$database",$username, $password);
}
catch (PDOException $pe) {
die("Error occurred:" . $pe->getMessage());
}
for call stored procedure
try {
// execute the stored procedure
$sql = "CALL StoreValue('$client','$ip','$port','$table_name','$var','$new_start','$new_end')";
$q = $conn->query($sql);
$q->setFetchMode(PDO::FETCH_ASSOC);
$total_search=$q->rowCount();
} catch (PDOException $pe) {
echo die("Error occurred:" . $pe->getMessage());
}
Please help me out from this problem...
Your $q variable is not an object, this causes the fatal error. Most likely the $conn->query() method call returned the problematic value, and you will need to dig a little bit to find out why the query() method failed.
One of the best places to start searching is the PDO documentation to see what support it has for error reporting. Particularly, I think you'll find this user comment very interesting, considering the fact that you wrapped the query code into a try/catch block: http://php.net/manual/en/pdo.query.php#74943.
The handling of errors by this function is controlled by the attribute PDO::ATTR_ERRMODE.
Use the following to make it throw an exception:
<?php
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>
My query is somewhat like this :
CREATE FUNCTION func ... ;
SELECT * FROM ....;
I'm using php's PDO which doesn't allow more than 1 statements in a $pdo->query() , so instead I decided that I'd split the query into two, so I execute the CREATE FUNCTION query using $pdo->exec() and then use $pdo->query() on the select statement.
However, I get the error on execution of select statement that FUNCTION database.func does not exist. How do I solve this? when I run it in phpmyadmin as a single query it works fine
Edit : PHP code :
class MyClass {
function connectPDO($user,$pass,$chartset="utf8"){
//simple function for making a new PDO object and mysql connection
$dbname = "my_db";
try {
//creating new pdo
$pdo = new PDO('mysql:host=localhost;dbname='.$dbname, $user,$pass);
//attributes set to throw errors and exceptions which can be caught, can be changed
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//fetch associative arrays
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
//default chartset
$pdo->exec('SET NAMES "'.$chartset.'"');
} catch (PDOException $e) {
//on some error
$output = "Ay caramba! there's something wrong here!.<br>";
echo $output.$e->getMessage();
exit();
}
$this->db = $pdo;
}
}
$object = new MyClass();
$object->connectPDO("user","pass");
$sqlPreliminary = "
DROP FUNCTION IF EXISTS myFunc;
DELIMITER //
CREATE FUNCTION myFunc (id INT)
RETURNS DECIMAL
BEGIN
RETURN id + 1;
END //
DELIMITER ;
";
$sqlFinal = "
SELECT id, myFunc(id) AS plusOne FROM table;
";
$object->db->exec($sqlPreliminary);
var_dump($object->db->query($sqlFinal)->fetchAll(PDO::FETCH_ASSOC));
Create a user defined function using PDO simply works by exec, no delimiter is required. Here $db is PDO instance.
$db->exec('CREATE FUNCTION ConvertHTMLToText(str LONGTEXT CHARSET utf8)
RETURNS LONGTEXT CHARSET utf8
BEGIN
DECLARE start, end INT DEFAULT 1;
LOOP
SET start = LOCATE("<", str, start);
IF (!start) THEN RETURN str; END IF;
SET end = LOCATE(">", str, start);
IF (!end) THEN SET end = start; END IF;
SET str = TRIM(INSERT(str, start, end - start + 1, ""));
END LOOP;
END');
EDIT:
This is multiple statement. The first part is DROP FUNCTION MyFunc IF EXISTS and the second is CREATE FUNCTION.
This was my previous answer, which was correct, but OP changed the code:
You don't say what $object->db is:
You call connectPDO("user","pass"); and then use $object->db->exec($sqlPreliminary);. $object should be global in function body. At the moment it is not, so it is a local variable, which is lost after end of the function. This $object in rest of the code is something different.
I have faced the same problem. When I removed " DELIMITER // " (also //
DELIMITER ; at the end), it worked. I think DELIMITER is no longer required with PHP PDO.
$sqlPreliminary = "
DROP FUNCTION IF EXISTS myFunc;
CREATE FUNCTION myFunc (id INT)
RETURNS DECIMAL
BEGIN
RETURN id + 1;
END
";
I tested this and worked.