Perl Checking for Errors

2.3.2 Solution

Everybody has problem s get t ing program s t o work correct ly. But if you dont ant icipat e difficult ies by checking for errors, you m ake t he j ob a lot harder. Add som e error-checking code so your program s can help you figure out what went wrong.

2.3.3 Discussion

You now know how t o connect t o t he MySQL server. I t s also a good idea t o know how t o check for errors and how t o ret rieve MySQL- relat ed error inform at ion from t he API , so t hat s what well cover next . When errors occur, MySQL provides a num eric error code and a corresponding descript ive t ext error m essage. The recipes in t his sect ion show how t o access t his inform at ion. Youre probably anxious t o see how t o do m ore int erest ing t hings such as issue queries and get back t he result s , but error checking is fundam ent ally im port ant . Program s som et im es fail, especially during developm ent , and if you dont know how t o det erm ine why failures occur, youll be flying blind. The exam ple program s in t his sect ion show how t o check for errors, but will in fact execut e w it hout any problem s if your MySQL account is set up properly. Thus, you m ay have t o m odify t he exam ples slight ly t o force errors t o occur so t hat t he error- handling st at em ent s are t riggered. For exam ple, you can change a connect ion- est ablishm ent call t o supply a bad password. This will give you a feel for how t he code act s when errors do occur. A general debugging aid t hat is not specific t o any API is t o check t he MySQL query log t o see what queries t he server act ually is receiving. This requires t hat you have query logging t urned on and t hat you have access t o t he log on t he MySQL server host . The log oft en will show you t hat a query is m alform ed in a part icular way and give you a clue about why your program is not const ruct ing t he proper query st ring. I f youre running a script under a w eb server and it fails, check t he servers error log.

2.3.4 Perl

The DBI m odule provides t wo at t ribut es t hat cont rol what happens when DBI m et hod invocat ions fail: • PrintError , if enabled, causes DBI t o print an error m essage using warn . • RaiseError , if enabled, causes DBI t o print an error m essage using die ; t his t erm inat es your script . By default , PrintError is enabled and RaiseError is disabled, so a script cont inues execut ing aft er print ing a m essage if errors occur. Eit her or bot h at t ribut es can be specified in t he connect call. Set t ing an at t ribut e t o 1 or 0 enables or disables it , respect ively. To specify eit her or bot h at t ribut es, pass t hem in a hash reference as t he fourt h argum ent t o t he connect call. The synt ax is dem onst rat ed short ly. The following code uses t he default set t ings for t he error- handling at t ribut es. This result s in a warning m essage if t he connect call fails, but t he script will cont inue execut ing: my dbh = DBI-connect dsn, cbuser, cbpass; However, because you really cant do m uch if t he connect ion at t em pt fails, it s oft en prudent t o exit inst ead aft er DBI print s a m essage: my dbh = DBI-connect dsn, cbuser, cbpass or exit; To print your own error m essages, leave RaiseError disabled and disable PrintError as well. Then t est t he result s of DBI m et hod calls yourself. When a m et hod fails, t he DBI::err and DBI::errstr variables will cont ain t he MySQL num eric error code and descript ive error st ring, respect ively: my dbh = DBI-connect dsn, cbuser, cbpass, {PrintError = 0} or die Connection error: DBI::errstr DBI::err\n; I f no error occurs, DBI::err w ill be 0 or undef , and DBI::errstr will be t he em pt y st r ing or undef . When checking for errors, you should access t hese variables im m ediat ely aft er invoking t he DBI m et hod t hat set s t hem . I f you invoke anot her m et hod before using t hem , t heir values will be reset . The default set t ings PrintError enabled, RaiseError disabled are not so useful if youre print ing your own m essages. I n t his case, DBI print s a m essage aut om at ically, t hen your script print s it s own m essage. This is at best redundant , and at worst confusing t o t he person using t he script . I f you enable RaiseError , you can call DBI m et hods wit hout checking for ret urn values t hat indicat e errors. I f a m et hod fails, DBI print s an error and t erm inat es your script . I f t he m et hod ret urns, you can assum e it succeeded. This is t he easiest approach for script writ ers: let DBI do all t he error checking However, if PrintError and RaiseError bot h are enabled, DBI m ay call warn and die in succession, result ing in error m essages being print ed t wice. To avoid t his problem , it s best t o disable PrintError whenever you enable RaiseError . That s t he approach generally used in t his book, as illust rat ed here: my dbh = DBI-connect dsn, cbuser, cbpass, {PrintError = 0, RaiseError = 1}; I f you dont want t he all- or- not hing behavior of enabling RaiseError for aut om at ic error checking versus having t o do all your own checking, you can adopt a m ixed approach. I ndividual handles have PrintError and RaiseError at t ribut es t hat can be enabled or disabled select ively. For exam ple, you can enable RaiseError globally by t urning it on when you call connect , t hen disable it select ively on a per-handle basis. Suppose you have a script t hat reads t he usernam e and password from t he com m and- line argum ent s, t hen loops while t he user ent ers queries t o be execut ed. I n t his case youd probably want DBI t o die and print t he error m essage aut om at ically if t he connect ion fails t heres not m uch you can do if t he user doesnt provide a valid nam e and password . Aft er connect ing, on t he ot her hand, you wouldnt want t he script t o exit j ust because t he user ent ers a synt act ically invalid query. I t would be bet t er for t he script t o t rap t he error, print a m essage, t hen loop t o get t he next query. The following code shows how t his can be done t he do m et hod used in t he exam ple execut es a query and ret urns undef t o indicat e an error : my user_name = shift ARGV; my password = shift ARGV; my dbh = DBI-connect dsn, user_name, password, {PrintError = 0, RaiseError = 1}; dbh-{RaiseError} = 0; disable automatic termination on error print Enter queries to be executed, one per line; terminate with Control- D\n; while read and execute queries { dbh-do _ or warn Query failed: DBI::errstr DBI::err\en; } dbh-{RaiseError} = 1; re-enable automatic termination on error I f RaiseError is enabled, you can t rap errors wit hout t erm inat ing your program by execut ing code w it hin an eval block. I f an error occurs wit hin t he block, eval fails and ret urns a m essage in t he variable. Typically, eval is used som et hing like t his: eval { statements that might fail go here... }; if { print An error occurred: \n; } This t echnique is com m only used, for exam ple, t o im plem ent t ransact ions. See Chapt er 15 . Using RaiseError in com binat ion wit h eval differs from using RaiseError alone in t he following ways: • Errors t erm inat e only t he eval block, not t he ent ire script . • Any error t erm inat es t he eval block, whereas RaiseError applies only t o DBI - relat ed errors. When you use eval w it h RaiseError enabled, be sure t o disable PrintError . Ot herwise, in som e versions of DBI , an error m ay sim ply cause warn t o be called wit hout t erm inat ing t he eval block as you expect . I n addit ion t o using t he error-handling at t ribut es PrintError and RaiseError , you can get lot s of useful inform at ion about your script s execut ion by t urning on DBI s t racing m echanism . I nvoke t he trace m et hod wit h an argum ent indicat ing t he t race level. Levels 1 t o 9 enable t racing wit h increasingly m ore verbose out put , and level 0 disables t racing: DBI-trace 1; enable tracing, minimal output DBI-trace 3; elevate trace level DBI-trace 0; disable tracing I ndividual dat abase and st at em ent handles have trace m et hods, t oo. That m eans you can localize t racing t o a single handle if you want . Trace out put norm ally goes t o your t erm inal or, in t he case of a web script , t o t he web servers error log . You can writ e t race out put t o a specific file by providing a second argum ent indicat ing a filenam e: DBI-trace 1, tmptrace.out; I f t he t race file already exist s, t race out put is appended t o t he end; t he files cont ent s are not cleared first . Beware of t urning on a file t race while developing a script , t hen forget t ing t o disable t he t race when you put t he script int o product ion. Youll event ually find t o your chagrin t hat t he t race file has becom e quit e large. Or worse, a filesyst em will fill up and youll have no idea why

2.3.5 PHP