Problem Solution Discussion Using Transactions in Perl Programs

Checking How API Transaction Abstractions Map onto SQL Statements For API s t hat provide a t ransact ion abst ract ion, you can see how t he int erface m aps ont o t he underlying SQL by enabling logging in your MySQL server and t hen wat ching t he query log t o see what st at em ent s t he API execut es when you run a t ransact ional program .

15.5 Using Transactions in Perl Programs

15.5.1 Problem

You want t o perform a t ransact ion in a DBI script .

15.5.2 Solution

Use t he st andard DBI t ransact ion support m echanism .

15.5.3 Discussion

The DBI m echanism for perform ing t ransact ions is based on explicit m anipulat ion of aut o- com m it m ode. The procedure is as follows: 1. Turn on t he RaiseError at t ribut e if it s not enabled and disable PrintError if it s on. You want errors t o raise except ions wit hout print ing anyt hing; leaving PrintError enabled can int erfere wit h failure det ect ion in som e cases. 2. Disable t he AutoCommit at t ribut e so t hat a com m it will be done only when you say so. 3. Execut e t he st at em ent s t hat m ake up t he t ransact ion wit hin an eval block so t hat errors raise an except ion and t erm inat e t he block. The last t hing in t he block should be a call t o commit , which com m it s t he t ransact ion if all it s st at em ent s com plet ed successfully. 4. Aft er t he eval execut es, check t he variable. I f cont ains t he em pt y st ring, t he t ransact ion succeeded. Ot herwise, t he eval w ill have failed due t o t he occurrence of som e error and will cont ain an error m essage. I nvoke rollback t o cancel t he t ransact ion. I f you want t o display an error m essage, print before calling rollback . The following code shows how t o im plem ent t his procedure t o perform our exam ple t ransact ion. I t does so in such a way t hat t he current values of t he error- handling and aut o- com m it at t ribut es are saved before and rest ored aft er execut ing t he t ransact ion. That m ay be overkill for your own applicat ions. For exam ple, if you know t hat RaiseError and PrintError are set properly already, you need not save or rest ore t hem . save error-handling and auto-commit attributes, then make sure theyre set correctly. save_re = dbh-{RaiseError}; save_pe = dbh-{PrintError}; save_ac = dbh-{AutoCommit}; dbh-{RaiseError} = 1; raise exception if an error occurs dbh-{PrintError} = 0; dont print an error message dbh-{AutoCommit} = 0; disable auto-commit eval { move some money from one person to the other dbh-do UPDATE money SET amt = amt - 6 WHERE name = Eve; dbh-do UPDATE money SET amt = amt + 6 WHERE name = Ida; all statements succeeded; commit transaction dbh-commit ; }; if an error occurred { print Transaction failed, rolling back. Error was:\n\n; roll back within eval to prevent rollback failure from terminating the script eval { dbh-rollback ; }; } restore attributes to original state dbh-{AutoCommit} = save_ac; dbh-{PrintError} = save_pe; dbh-{RaiseError} = save_re; You can see t hat t he exam ple goes t o a lot of work j ust t o issue a couple of st at em ent s. To m ake t ransact ion processing easier, you m ight want t o creat e a couple of convenience funct ions t o handle t he processing t hat occurs before and aft er t he eval : sub transact_init { my dbh = shift; my attr_ref = {}; create hash in which to save attributes attr_ref-{RaiseError} = dbh-{RaiseError}; attr_ref-{PrintError} = dbh-{PrintError}; attr_ref-{AutoCommit} = dbh-{AutoCommit}; dbh-{RaiseError} = 1; raise exception if an error occurs dbh-{PrintError} = 0; dont print an error message dbh-{AutoCommit} = 0; disable auto-commit return attr_ref; return attributes to caller } sub transact_finish { my dbh, attr_ref, error = _; if error an error occurred { print Transaction failed, rolling back. Error was:\nerror\n; roll back within eval to prevent rollback failure from terminating the script eval { dbh-rollback ; }; } restore error-handling and auto-commit attributes dbh-{AutoCommit} = attr_ref-{AutoCommit}; dbh-{PrintError} = attr_ref-{PrintError}; dbh-{RaiseError} = attr_ref-{RaiseError}; } By using t hose t wo funct ions, our exam ple t ransact ion can be sim plified considerably: ref = transact_init dbh; eval { move some money from one person to the other dbh-do UPDATE money SET amt = amt - 6 WHERE name = Eve; dbh-do UPDATE money SET amt = amt + 6 WHERE name = Ida; all statements succeeded; commit transaction dbh-commit ; }; transact_finish dbh, ref, ; As of DBI 1.20, an alt ernat ive t o m anipulat ing t he AutoCommit at t ribut e m anually is t o begin a t ransact ion by invoking begin_work . This m et hod disables AutoCommit and causes it t o be enabled again aut om at ically when you invoke commit or rollback lat er . Transactions and Older Versions of DBD::mysql The DBI t ransact ion m echanism requires DBD: : m ysql 1.2216 or newer. For earlier versions, set t ing t he AutoCommit at t ribut e has no effect , so youll need t o issue t he t ransact ion- relat ed SQL st at em ent s yourself BEGIN , COMMIT , ROLLBACK .

15.6 Using Transactions in PHP Programs