www.developer.com/lang/article.php/2184681
|
By April 10, 2003 Error HandlingThe handling of errors within programs, or the lack thereof, is one of the more common causes of questions concerning programming with DBI. Someone will ask "Why doesn't my program work?" and the answer generally runs along the lines of "Why aren't you performing error checking?" Sure enough, nine out of ten times when error checking is added, the exact error message appears and the cause for error is obvious. Automatic Versus Manual Error CheckingEarly versions of the DBI required programmers to perform their own error checking, in a traditional way similar to the examples listed earlier for connecting to a database. Each method that returned some sort of status indicator as to its success or failure should have been followed by an error condition checking statement. This is an excellent, slightly C-esque way of programming, but it quickly gets to be tiresome, and the temptation to skip the error checking grows. The DBI now has a far more straightforward error-handling capability in the style of exceptions. That is, when DBI internally detects that an error has occurred after a DBI method call, it can automatically either Manual error checking still has a place in some applications where failures are expected and common. For example, should a database connection attempt fail, your program can detect the error, sleep for five minutes, and automatically re-attempt a connection. With automatic error checking, your program will exit, telling you only that the connection attempt failed. DBI allows mixing and matching of error-checking styles by allowing you to selectively enable and disable automatic error checking on a per-handle basis. Manual error checkingOf course, the DBI still allows you to manually error check your programs and the execution of DBI methods. This form of error checking is more akin to classic C and Perl programming, where each important statement is checked to ensure that it has executed successfully, allowing the program to take evasive action upon failure. DBI, by default, performs basic automatic error reporting for you by enabling the For example:
### Attributes to pass to DBI->connect( )
%attr = (
PrintError => 0,
RaiseError => 0
);
### Connect...
my $dbh = DBI->connect( "dbi:Oracle:archaeo", "username",
"password" , \%attr );
### Re-enable warning-level automatic error reporting...
$dbh->{PrintError} = 1;
Most DBI methods will return a false status value, usually
### Try connecting to a database
my $dbh = DBI->connect( ... )
or die "Can't connect to database: $DBI::errstr!\";
The following program disables automatic error handling, with our own tests to check for errors. This example also moves the attributes into the
#!/usr/bin/perl -w
#
# ch04/error/ex1: Small example using manual error checking.
use DBI; # Load the DBI module
### Perform the connection using the Oracle driver
my $dbh = DBI->connect( undef, "stones", "stones", {
PrintError => 0,
RaiseError => 0
} ) or die "Can't connect to the database: $DBI::errstr\n";
### Prepare a SQL statement for execution
my $sth = $dbh->prepare( "SELECT * FROM megaliths" )
or die "Can't prepare SQL statement: $DBI::errstr\n";
### Execute the statement in the database
$sth->execute
or die "Can't execute SQL statement: $DBI::errstr\n";
### Retrieve the returned rows of data
my @row;
while ( @row = $sth->fetchrow_array( ) ) {
print "Row: @row\n";
}
warn "Data fetching terminated early by error: $DBI::errstr\n"
if $DBI::err;
### Disconnect from the database
$dbh->disconnect
or warn "Error disconnecting: $DBI::errstr\n";
exit;
As can be seen from the example, the code to check the errors that may have arisen in a DBI method is actually longer than the code to perform the operations themselves. Similarly, it is entirely possible that you may just genuinely forget to add a check after a statement, which may result in extremely bizarre program execution and error reporting, not to mention hours of wasted debugging time! Automatic error checkingThe automatic error checking capabilities of the DBI operates on two levels. The Because the standard Perl functions of These different levels of automatic error checking can be turned on for any handle, although database handles are usually the most common and useful. To enable the style of automatic error checking you want, you may set the value of either of the following two attributes:
$h->{PrintError} = 1;
$h->{RaiseError} = 1;
Similarly, to disable automatic error checking, simply set the value of these attributes to If both A more common way in which these attributes are used is to specify them in the optional attribute hash supplied to The following short example illustrates the use of
#!/usr/bin/perl -w
#
# ch04/error/ex2: Small example using automatic error handling
# with RaiseError, i.e., the program will
# abort upon detection of any errors.
use DBI; # Load the DBI module
my ($dbh, $sth, @row);
### Perform the connection using the Oracle driver
$dbh = DBI->connect( "dbi:Oracle:archaeo", "username",
"password" , {
PrintError => 0, ### Don't report errors via warn( )
RaiseError => 1 ### Do report errors via die( )
} );
### Prepare a SQL statement for execution
$sth = $dbh->prepare( "SELECT * FROM megaliths" );
### Execute the statement in the database
$sth->execute( );
### Retrieve the returned rows of data
while ( @row = $sth->fetchrow_array( ) ) {
print "Row: @row\n";
}
### Disconnect from the database
$dbh->disconnect( );
exit;
This example is both shorter and more readable than the manual error checking shown in a following example. The actual program logic is clearer. The most obvious additional benefit is that we can forget to handle error checking manually after a DBI operation, since the DBI will check for errors for us. Mixed error checkingYou can mix error checking styles within a single program, since automatic error checking can be easily enabled and disabled on a per-handle basis. There are plenty of occasions where mixed error checking is useful. For example, you might have a program that runs continuously, such as one that polls a database for recently added stock market quotes every couple of minutes. Disaster occurs! The database crashes! The ideal situation here is that the next time the program tries connecting to the database and fails, it'll wait a few minutes before retrying rather than aborting the program altogether. Once we've connected to the database, the error checking should now simply warn when a statement fails and not die. This mixed style of error checking can be broken down into two areas: manual error checking for the
#!/usr/bin/perl -w
#
# ch04/error/mixed1: Example showing mixed error checking modes.
use DBI; # Load the DBI module
### Attributes to pass to DBI->connect( ) to disable
### automatic error checking
my %attr = (
PrintError => 0,
RaiseError => 0,
);
### The program runs forever and ever and ever and ever ...
while ( 1 ) {
my $dbh;
### Attempt to connect to the database. If the connection
### fails, sleep and retry until it succeeds ...
until (
$dbh = DBI->connect( "dbi:Oracle:archaeo",
"username", "password" , %attr )
) {
warn "Can't connect: $DBI::errstr. Pausing before
retrying.\n";
sleep( 5 * 60 );
}
eval { ### Catch _any_ kind of failures from the
### code within
### Enable auto-error checking on the database handle
$dbh->{RaiseError} = 1;
### Prepare a SQL statement for execution
my $sth = $dbh->prepare(
"SELECT stock, value FROM current_values" );
while (1) {
### Execute the statement in the database
$sth->execute( );
### Retrieve the returned rows of data
while ( my @row = $sth->fetchrow_array( ) ) {
print "Row: @row\n";
}
### Pause for the stock market values to move
sleep 60;
}
};
warn "Monitoring aborted by error: $@\n" if $@;
### Short sleep here to avoid thrashing the database
sleep 5;
}
exit;
This program demonstrates that with DBI, you can easily write explicit error checking and recovery code alongside automatic error checking. |
| Go to page: Prev 1 2 3 4 5 6 7 8 Next |