Richard Clarke wrote: > Using Perrin's article on E-Toys is perhaps a good place to start. In the > Model object which performs various DB procedures, what actions were taken > if for some reason the connection to the database failed or if an SQL error > happened? Was the whole "execute()" block put in an eval procedure and then > a generic error page produced; Or were either per procedure evals or > Exception objects used to propogate specific errors up to the control object > so that it could display errors in the current page/view?
Well, naturally the answer is "it depends." Most database errors can't be gracefully recovered from, so we would let them propagate up. If it was possible for a database error to be caused by user input (say, a duplicate login name) that would need to be caught and handled. It would also be caught if any special cleanup of non-database resources was needed. Errors that propagate up are caught by an eval wrapping the whole handler method which issues a rollback to the database, logs the error, and prints either a pretty error page or a stack trace depending on the current debug settings. Here's an excerpt from the documentation I wrote for our exception base class. This uses the try/catch syntax from Error.pm. =head1 HANDLING DBI ERRORS Since DBI errors are the most common source of exceptions in our application, I'm giving them special treatment here. Because we are planning to use the RaiseError option of DBI, you can expect DBI to die whenever it hits a problem, as opposed to returning an undef value that you have to check for. This means that most of the time you don't need to do anything special for handling DBI errors. They will propagate up and be caught at the top level. DBI exceptions will be propagated as instances of the Error::Simple class. You can catch your own exceptions without catching the DBI exceptions by catching specific classes other than Error::Simple. try { # lookup password in Oracle my $sth = $dbh->prepare_cached($sql_query); $sth->execute($bind_value); if (!$sth->rows) { # should have matched something throw ESF::Error::User::BogusPassword -text $password; } $ary_ref = $sth->fetchrow_arrayref; # ... etc. ... } catch ESF::Error::User::BogusPassword with { # handle this error }; # <-- don't forget! It's okay to use transactions, and to put in your C<$dbh->commit> statement at the end assuming everything will work. If DBI throws an exception that propagates to the top without being caught, we will automatically issue a C<$dbh->rollback> command. Sometimes, you may want to catch a specific DBI error. Be careful when doing this, because you need to know the error number or text of the error message in order to trap the right exception. DBI's raise error setting will cause it to fill the text attribute of the Error::Simple objects it throws with the message given by Oracle, so if you want to trap errors of type ORA-172, you can do this: try { my $sth = $dbh->prepare_cached($sql_query); $sth->execute($bind_value); } catch Error::Simple with { my $err = shift; if ($err->text() =~ m/ORA-172/) { # handle the error here } else { # let this error propagate $err->throw(); } }; # <-- don't forget! - Perrin