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