1

I'm trying to use transactions with PDO, with the following code

try
{
    $bdd = new PDO('mysql:host=HOSTNAME;dbname=DATABASENAME', 'USERNAME', 'PASSWORD');
}
catch(Exception $e)
{
    die('Error : '.$e->getMessage());
}    

$qry = 'UPDATE table SET field = CASE';
foreach($elements as $el){
  $qry .= ' WHEN id = '. $el['id'] .' THEN '. $el['value'];
}
$qry .= ' ELSE field END, SET update_date = CASE';
foreach($elements as $el){
  $qry .= ' WHEN id = '. $el['id'] .' THEN NOW()';
}
$qry .= ' ELSE update_date END';

$update = $bdd->prepare($qry);

$bdd->beginTransaction();
try {
    $update->execute();  
    $bdd->commit();
}
catch(Exception $e) {
    $bdd->rollback();
    echo 'Error : '.$e->getMessage().'<br />';
    echo 'N° : '.$e->getCode();
    exit();
}

But I get the following error (and the table is not updated) :

Fatal error: Uncaught exception 'PDOException' with message 'There is no active transaction' in script.php:106 
Stack trace: 
#0 script.php(106): PDO->rollBack() #1 {main} thrown in script.php on line 106

(Line 106 is $bdd->rollback();)

I understand that it's telling me there is no transaction going on, but... there should be, with $bdd->rollback();...

Thanks for any help you can give !


EDIT : The database engine is MyISAM.

I have additional info after further testing :

I simplified the $qry in my question because I thought it was irrelevant, but it seems it isn't. The query I'm actually using is a much longer one, updating multiple elements with the following code (I also modified the code up there ^ in the original question) :

$qry = 'UPDATE table SET field = CASE';
foreach($elements as $el){
  $qry .= ' WHEN id = '. $el['id'] .' THEN '. $el['value'];
}
$qry .= ' ELSE field END, SET update_date = CASE';
foreach($elements as $el){
  $qry .= ' WHEN id = '. $el['id'] .' THEN NOW()';
}
$qry .= ' ELSE update_date END';

It seems that the length of the query and/or the number of updated lines is causing my issue :

  • when updating 72 lines at most (the query is 3463 characters long, all spaces included), the table is updated, and vardump($bdd->beginTransaction();) returns bool(true) (regardless of wether it's before or after the $update = $bdd->prepare($qry) line.
  • when updating 73 lines or more (the query is 3509 characters long, all spaces included, when it updates 73 lines), the table is not updated, and vardump($bdd->beginTransaction();) returns bool(false) (once again, regardless of wether it is before or after the $update = $bdd->prepare($qry) line.

Hints on why, and how I can fix it are appreciated.

Thanks again


EDIT 2 : I switched the engine to InnoDB. The issue still stands.

François M.
  • 4,027
  • 11
  • 30
  • 81
  • It returns `bool(false) ` – François M. Oct 14 '15 at 10:38
  • Ensure that PDO will throw exceptions on errors: `$bdd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);`. I suspect that it isn't the size of the query but the contents. – Ryan Vincent Oct 14 '15 at 12:00
  • I now get a new error : `Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2006 MySQL server has gone away' in script.php:75 Stack trace: #0 script.php(75): PDO->beginTransaction() #1 {main} thrown in script.php on line 75 `, line 75 being `$bdd->beginTransaction();`. I have no idea what it means... – François M. Oct 14 '15 at 12:19
  • Will you save the generated SQL to a file then try and run the same query in a `SQL Manager` such as `phpmyadmin`? I want to find out if this is a PDO issue or a server issue. [B.5.2.9 MySQL server has gone away](https://dev.mysql.com/doc/refman/5.5/en/gone-away.html). One possibility is 'query too large'. ;-/ But it would have to be very large. Hence storing it in a file so you can check it. What is the size of the data that you are sending to be updated? – Ryan Vincent Oct 14 '15 at 12:38
  • Putting the generated query in `phpmyadmin` worked and updated the table. I'm not sure of what you mean by `size of the data` ? I optimized the length of the query (by turning the second part into `date_update = CASE WHEN id IN (1, 2, 3) THEN NOW() ELSE date_update`), so I guess the length is not the problem here, because even though the query is much shorter, the issue remains : it still works when I update 72 lines or less, and give an error when I update 73 or more. – François M. Oct 14 '15 at 12:53
  • I have just realized you are not using `placeholders` in your SQL prepare statement. Hmm. I suspect that there are data values on line 73 that are confusing PHP. In the mean time you need to generate the SQL a little bit differently. May a suggest you use what is provided in [here](http://stackoverflow.com/a/33069595/3184785). Your PDO database life will get a lot easier. – Ryan Vincent Oct 14 '15 at 13:14
  • More info : if I remove the transaction-related lines (`beginTransaction()`, `commit()` and `rollback()` in the script, the updating works fine. – François M. Oct 14 '15 at 13:17
  • 1
    I have no idea why that should have an effect. I am having difficulty making sense of the effects you are seeing. I cannot see how I can be of further assistance. sorry. – Ryan Vincent Oct 14 '15 at 13:21

1 Answers1

1

Move

$bdd->beginTransaction();

to above your query:

$qry = 'UPDATE table SET field = value'
$update = $bdd->prepare($qry);

EDIT:

You are using MyISAM engine which does not support transactions. Update your tables to use InnoDB.

Springie
  • 718
  • 5
  • 8
  • Can you update your post to show your PDO connection code? – Springie Oct 14 '15 at 10:24
  • I'm not sure if I added what you're asking, but it's updated – François M. Oct 14 '15 at 10:34
  • 1
    The code work for me, What database engine are you using? You should be using InnoDB as that supports transactions. – Springie Oct 14 '15 at 10:41
  • Thnaks for this insight. The database engine is MyISAM, but I don't think that's the problem : I found out a case when `var_dump($bdd->beginTransaction());` returns `true`. It seems the problem might be with the length of the query. I edited the original question to add what I found out, can you have another look at it, please ? – François M. Oct 14 '15 at 11:31
  • 1
    Change to InnoDB. MyISAM does not support transaction. The query is fine. All your code worked on my test with InnoDB tables, – Springie Oct 14 '15 at 11:36
  • I changed the engine to InnoDB. I still get the same issue described in my edit of the original question : 72 updated lines works, 73 doesn't. (error message is still `no active transaction`) – François M. Oct 14 '15 at 11:46