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.
Related
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();
// ...
The problem is that I'm using a stored procedure to handle validation of my accounts. (Please note that this is a dumbed down version of the procedure and method and is only used for demonstration purposes). Note: Account authentication is not the original purpose of this method, once again just for example.
Here's my Example MySQL procedure:
BEGIN
DECLARE rc INT(11);
DECLARE new VARCHAR(40);
SET new = uuid();
UPDATE accounts SET session_key = new WHERE account_id = id_in AND session_key = session_in;
SELECT ROW_COUNT() into rc;
IF(rc > 0) THEN
SELECT new AS `session_key`;
END IF;
END
Here's the PHP related code that goes with it:
private static function authenticate() {
$connection = Database::getConnection();
$account = 19;
$session = "cc73e13b-2983-11e5-8ade-00163e603fb4";
$statement = $connection->prepare("CALL AUTH_SESSION(:account, :session);");
$statement->bindParam(":account", $account);
$statement->bindParam(":session", $session);
if($statement->execute()) {
if($row = $statement->fetch()) {
echo 'valid session';
} else {
echo 'invalid session';
}
} else {
echo 'failed to execute query';
}
}
When the session_key and account_id are correct, the procedure prints out (or SELECTS) the new session_key so that PHP can obtain it. However when one of the two values are not correct the SELECT statement is never called.
I figured this would be fine. If a row is returned->get the session key (Success) and if the row isn't returned, the validation failed, but it apparently doesn't work that way.
The error returned is as follows:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error'
Just to re-iterate, this error is only thrown when (!(rc>0)).
You can only use fetch() if the query returns a result set, i.e. the stored procedure has to return the result of a SELECT.
You could have the procedure return an empty result set when rc > 0 is false.
BEGIN
DECLARE rc INT(11);
DECLARE new VARCHAR(40);
SET new = uuid();
UPDATE accounts SET session_key = new WHERE account_id = id_in AND session_key = session_in;
SELECT ROW_COUNT() into rc;
SELECT new AS session_key
FROM dual
WHERE rc > 0;
END
When you call fetch() it will return false if the result set is empty, otherwise it will return an array containing the session key.
What is a sure way to tell if an update query succeeded when using php pdo and mysql?
In my app, I update totals of items that are submitted but a user, and the table looks like this:
items
userId | itemsAdded | itemsChecked | itemsUnChecked | itemsTotal
1 | 5 | 2 | 3 | 5
So when I do update items set itemTotals = itemsChecked+itemUnChecked the itemsTotal column remains the same unless the itemsAdded changes and the itemsUnChecked increments (2 + 3 equals 5, 1 + 4 is also 5).
I used to use rowCount() to check if a query succeeded, but in this case, since the itemsTotal column stays the same, there's no way of telling if the sql succeeded or not.
$query = $conn->prepare($sql);
$query->execute(array(
":itemCount" => $itemCount
":itemId" => $itemId
));
$queryCount = $query->rowCount();
if($queryCount == 1) {
echo 'Updated succeeded';
} else {
echo 'Updated failed!';
}
I could also use:
$query = $conn->prepare($sql);
$result = $query->execute(array(
":itemCount" => $itemCount
":itemId" => $itemId
));
if($result) {
echo 'Updated succeeded';
} else {
echo 'Updated failed!';
}
But does that return true or false based on if the query succeed or based on the number of rows it updated?
I only need to check if the query succeeded or not. No need to tell the number of rows that were updated.
The execute() method will either throw an exception or return FALSE (depending on the error mode you have set for the database connection) when the execution of a SQL statement fails.
If we set the error mode to throw an exception, prior to executing a statement, (usually immediately after establishing a database connection), like this
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Then we can use a try/catch block to handle an exception thrown by a SQL statement. Something like this:
try {
$query->execute(...);
echo 'Update succeeded';
} catch(PDOException $e) {
echo 'Update failed!';
echo 'Error: ' . $e->getMessage();
}
Information about error mode settings and handling is available in the documentation here: http://php.net/manual/en/pdo.error-handling.php
Or, if PDO isn't set to throw an exception, we can use a simple if test. (The execute() method will return FALSE if the the statement fails.)
if ($query->execute(...)) {
echo 'Update succeeded';
} else {
echo 'Update failed!';
}
For more precise control with the different types of failure, we can use the errorCode() method to retrieve the SQLSTATE associated with the last operation on the statement handle, and we can perform conditional tests on the returned value. http://php.net/manual/en/pdostatement.errorcode.php
Even if no rows are affected that does not mean the update failed just, like you said, nothing was changed. It would only fail if there was an exception thrown. To handle this you would need to implement a try/catch block.
http://php.net/manual/en/language.exceptions.php
try{
$query = $conn->prepare($sql);
$result = $query->execute(array(
":itemCount" => $itemCount
":itemId" => $itemId
));
echo 'Updated succeeded';
} catch(Exception $e) {
echo 'Updated failed!';
}
Sorry for this beginners question and i'm not a PHP developer, but now i'm trying to learn it.
i want to add record in MySQL data base and i'm using transactions lock.
my code is as below.
$SqlQuery="INSERT INTO tab_photo VALUES('$PhotoID','$ProjectId','$Day','$barCode','$photoName','$PhotoXml')";
$waiting = true;
while($waiting) {
try {
// save border data
$stmt = $conn->prepare($SqlQuery);
$conn->beginTransaction();
$stmt->execute();
sleep(1);
$x=$conn->commit();
echo "x value-".$x;
echo "Success";
$waiting = false;
}
catch (PDOException $e){
echo "Failled :".$PhotoID."-".$PhotoID;
if(stripos($e->getMessage(), 'DATABASE IS LOCKED') !== false) {
// This should be specific to SQLite, sleep for 0.25 seconds
// and try again. We do have to commit the open transaction first though
$conn->commit();
usleep(250000);
} else {
$conn->rollBack();
throw $e;
}
}
}
in here as output it gives,
x value-1 Success
but actually this record doesn't add to the database.
My Questions:
Even the commit is successful(output 1) how does it not added to the database?
how can i check whether record is added to database? ( Is there any way to find it without write select statement?
As I understand, you expect that PDOException will be thrown when statement is failed to execute. But as I can see, exception is not thrown by default in such cases.
See how you can change that here
Suppose in your case you should have a code like this:
$conn = new PDO($connection_string);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // this will force PDO to throw exception when SQL statement fails instead of simply setting an error.
Suppose this will work fine for you.
Please note that you should not use
$SqlQuery="INSERT INTO tab_photo VALUES('$PhotoID','$ProjectId','$Day','$barCode','$photoName','$PhotoXml')";
Instead of that, you should use parameters binding:
$SqlQuery="INSERT INTO tab_photo VALUES(:PhotoID,:ProjectId,:Day,:barCode,:photoName,:PhotoXml)";
$stmt = $conn->prepare($SqlQuery);
$conn->beginTransaction();
$stmt->execute(array(':PhotoID' => $PhotoID, ':ProjectId' => $ProjectId, ....));
sleep(1);
See this for more details.
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();
}
?>