{ # CBNP::DBI::ErrorHandler scope package CBNP::DBI::ErrorHandler; use strict; use vars qw($VERSION); use Carp; use DBI; use CBNP::Constants ":standard"; $VERSION = "0.01"; # POD-formatted documentation #------------------------------------------------------------------------------- =head1 NAME CBNP::DBI::ErrorHandler - Insulation for using DBI =head1 SYNOPSIS use CBNP::DBI::ErrorHandler; # You may want to use CBNP::MetaLite to get your database connection instead # of doing it yourself like this my $bufferedHandle = CBNP::DBI::ErrorHandler->connect(...DBI connection parameters...); # These DBI::db functions are currently insulated with ErrorHandler::db $bufferedHandle->do($sqlString); @answerList = $bufferedHandle->selectrow_array($sqlString); $answerListRef = $bufferedHandle->selectall_arrayref($sqlString); $buffStatement = $bufferedHandle->prepare($sqlString); $bufferedHandle->commit(); # These functions provided by ErrorHandler::db, but are not part of DBI @answerList = $bufferedHandle->selectrow_array_exists($sqlString); $answerListRef = $bufferedHandle->selectall_arrayref_exists($sqlString); # These DBI::st functions are currently insulated with ErrorHandler::st $buffStatement->execute(); $answerRowRef = $buffStatement->fetchrow_arrayref(); @answerRow = $buffStatement->fetchrow_array(); $answerListRef = $buffStatement->fetchall_arrayref(); # This function is provided by ErrorHandler::st, but is not part of DBI $answerListRef = $buffStatement->fetchall_arrayref_exists(); =head1 EXAMPLES See synopsis. =head1 DESCRIPTION CBNP::DBI::ErrorHandler takes care of some mundane error handling. If you use it directly, it will hopefully clean up your code some. The MORE IMPORTANT use of the error handler is through a subclass, like CBNP::DBI::ErrorHandler::Retry. CBNP::DBI::ErrorHandler::Retry implements error retries for a set of acceptable errors. For example, when database congestion is high and a statement encounters a deadlock situation, it will automatically retry. =head1 APPENDIX The following documentation describes the functions in this package =cut #------------------------------------------------------------------------------- =head2 connect Usage : $buffHandle = CBNP::DBI::ErrorHandler->connect( $dataSource, $userName, $password); $buffHandle = CBNP::DBI::ErrorHandler->connect( $dataSource, $userName, $password, \%attributes); Function : Execute's DBI's connect() function. Will throw an exception if there is a problem connecting to the data source. Arguments : String - data source string String - user name for the database login String - user password for the database login HashRef (optional) - attribute ref to be passed to DBI Example : See Usage Returns : CBNP::DBI::ErrorHandler::db - database handle ready for use =cut sub connect { my ($this, $dataSourceString, $userName, $password, $DBIParamRef) = @_; my $dbHandle = DBI->connect($dataSourceString, $userName, $password, $DBIParamRef); if(defined DBI::err()) { confess("failed to connect to data source \"$dataSourceString\"."); } # Re-bless the handle into this class type bless($dbHandle, "CBNP::DBI::ErrorHandler::db"); return $dbHandle; } } # End CBNP::DBI::ErrorHandler scope { # Begin CBNP::DBI::ErrorHandler::db scope package CBNP::DBI::ErrorHandler::db; use strict; use vars qw($VERSION @ISA); use Carp; use DBI; use CBNP::Constants ":standard"; $VERSION = "0.01"; @ISA = ("DBI::db"); #------------------------------------------------------------------------------- =head2 do Usage : $buffHandle->do($sqlString); $buffHandle->do($sqlString, \%attributes); $buffHandle->do($sqlString, \%attributes, @bindValues); Function : Executes DBI's do() on the database handle, will throw an exception if the statement returns abnormally Arguments : String - SQL string to be executed Hash ref (optional) - Attribute ref to be passed to DBI String array (optional) - Bind values for the SQL statement Example : See Usage Returns : =cut sub do { my ($this, $sqlString, $attributeRef, @bindValues) = @_; my $affectedRowCount = $this->SUPER::do($sqlString, $attributeRef, @bindValues); if(defined $this->err()) { confess("do() failed for SQL \"$sqlString\": " . DBI::errstr()); } return $affectedRowCount; } #------------------------------------------------------------------------------- =head2 selectrow_array Usage : @row = $buffHandle->selectrow_array($sqlString); @row = $buffHandle->selectrow_array($sqlString, \%attributes); @row = $buffHandle->selectrow_array($sqlString, \%attributes, @bindValues); Function : Executes DBI's selectrow_array() on the database handle, will throw an exception if the statement returns abnormally Arguments : String - SQL string to be executed Hash ref (optional) - Attribute ref to be passed to DBI String array (optional) - Bind values for the SQL statement Example : See Usage Returns : Array - the row of data requested =cut sub selectrow_array { my ($this, $sqlString, $attributeRef, @bindValues) = @_; # Orignially this copied the array an extra time so I could catch # an abnormal return from selectrow_array. This didn't work, since the # after-clause "OR" was changing the return value of the selectrow_array # (with the "OR" it would return only the first column of the first row). # I'm not sure why this was happening. # Now, abnormal return is assumed only if DBI::err is defined, which seems # to be the only reasonable way (using RaiseError, or checking for an # empty return list seem un-clean) to detect it. # I don't like that this will copy the array an extra time - sorry my @answer = $this->SUPER::selectrow_array($sqlString, $attributeRef, @bindValues); if(defined $this->err()) { confess("selectrow_array() failed for SQL \"$sqlString\": " . DBI::errstr()); } return wantarray ? @answer : $answer[0]; } #------------------------------------------------------------------------------- =head2 selectrow_array_exists Usage : @row = $buffHandle->selectrow_array_exists($sqlString); @row = $buffHandle->selectrow_array_exists($sqlString, \%attributes); @row = $buffHandle->selectrow_array_exists($sqlString, \%attributes, @bindValues); Function : Executes DBI's selectrow_array() on the database handle, will throw an exception if no data is returned, or if the statement returns abnormally. Use this when you NEED values returned. Arguments : String - SQL string to be executed Hash ref (optional) - Attribute ref to be passed to DBI String array (optional) - Bind values for the SQL statement Example : See Usage Returns : Array - the row of data requested =cut sub selectrow_array_exists { my ($this, $sqlString, $attributeRef, @bindValues) = @_; # I don't like that this will copy the array an extra time - sorry my @answer = $this->SUPER::selectrow_array($sqlString, $attributeRef, @bindValues); if(defined $this->err()) { confess("selectrow_array_exists() failed for SQL \"$sqlString\": " . DBI::errstr()); } # Additional check for existence since I'm not using the empty list # to detect an error, only the err() status. if(scalar(@answer) == 0 || !defined $answer[0]) { confess("selectrow_array_exists() resulted in no data for SQL " . "\"$sqlString\""); } return wantarray ? @answer : $answer[0]; } #------------------------------------------------------------------------------- =head2 selectall_arrayref Usage : $listRef = $buffHandle->selectall_arrayref($sqlString); $listRef = $buffHandle->selectall_arrayref($sqlString, \%attributes); $listRef = $buffHandle->selectall_arrayref($sqlString, \%attributes, @bindValues); Function : Executes DBI's selectall_arrayref() on the database handle, and will throw an exception if it returns abnormally Arguments : String - SQL string to be executed Hash ref (optional) - Attribute ref to be passed to DBI String array (optional) - Bind values for the SQL statement Example : See Usage Returns : Array ref - reference to the array of rows retrieved =cut sub selectall_arrayref { my ($this, $sqlString, $attributeRef, @bindValues) = @_; my $answerRef = $this->SUPER::selectall_arrayref($sqlString, $attributeRef, @bindValues); if(defined $this->err()) { confess("selectall_arrayref() failed for SQL \"$sqlString\": " . DBI::errstr()); } return $answerRef; } #------------------------------------------------------------------------------- =head2 selectall_arrayref_exists Usage : $listRef = $buffHandle->selectall_arrayref_exists($sqlString); $listRef = $buffHandle->selectall_arrayref_exists($sqlString, \%attributes); $listRef = $buffHandle->selectall_arrayref_exists($sqlString, \%attributes, @bindValues); Function : Executes DBI's selectall_arrayref() on the database handle. Will and will throw an exception if no data is returned, or if it returns abnormally. Use this when you NEED values returned. Arguments : String - SQL string to be executed Hash ref (optional) - Attribute ref to be passed to DBI String array (optional) - Bind values for the SQL statement Example : See Usage Returns : Array ref - reference to the array of rows retrieved =cut sub selectall_arrayref_exists { my ($this, $sqlString, $attributeRef, @bindValues) = @_; my $answerRef = $this->SUPER::selectall_arrayref($sqlString, $attributeRef, @bindValues); if(defined $this->err()) { confess("selectall_arrayref_exists() failed for SQL " . "\"$sqlString\": " . DBI::errstr()); } if(!defined $answerRef || !defined $answerRef->[0] || !defined $answerRef->[0]->[0]) { confess("selectall_arrayref_exists() resulted in no data for SQL " . "\"$sqlString\""); } return $answerRef; } #------------------------------------------------------------------------------- =head2 prepare Usage : $DBIStatementHandle = $buffHandle->prepare($sqlString); $DBIStatementHandle = $buffHandle->prepare($sqlString, \%attributes); Function : Executes DBI's prepare() on the database handle Arguments : String - SQL string to be executed Hash ref (optional) - Attribute ref to be passed to DBI Example : See Usage Returns : CBNP::DBI::ErrorHandler::Retry::st - statement handle that is retuned from the call to prepare() =cut sub prepare { my ($this, $sqlString, $attributeRef) = @_; my $statementHandle = $this->SUPER::prepare($sqlString, $attributeRef); if(defined $this->err()) { confess("prepare() failed for SQL \"$sqlString\": " . DBI::errstr()); } bless($statementHandle, "CBNP::DBI::ErrorHandler::st"); return $statementHandle; } #------------------------------------------------------------------------------- =head2 commit Usage : $buffHandle->commit(); Function : Executes DBI's commit() on the database handle Arguments : Example : See Usage Returns : =cut sub commit { my ($this) = @_; $this->SUPER::commit(); if(defined $this->err()) { confess("commit() failed: " . DBI::errstr()); } } } # End CBNP::DBI::ErrorHandler::db scope { # Begin CBNP::DBI::ErrorHandler::st scope package CBNP::DBI::ErrorHandler::st; use strict; use vars qw($VERSION @ISA); use Carp; use DBI; $VERSION = "0.01"; @ISA = ("DBI::st"); #------------------------------------------------------------------------------- =head2 execute Usage : $buffStatement->execute(); $buffStatement->execute(@bindValues); Function : Executes DBI's execute() on the statement handle. Will throw an exception if it returns abnormally. Arguments : Array (optional) - bind values for execution of the statement Example : See Usage Returns : =cut sub execute { my ($this, @bindValues) = @_; my $returnValue = $this->SUPER::execute(@bindValues); if(defined $this->err()) { confess("execute() failed for SQL \"" . $this->{"Statement"} . "\": " . DBI::errstr()); } return $returnValue; } #------------------------------------------------------------------------------- =head2 fetchrow_arrayref Usage : $rowRef = $buffStatement->fetchrow_arrayref(); Function : Executes DBI's fetchrow_arrayref() on the statement handle, will throw an exception if the statement returns abnormally. Note that when using fetchrow_arrayref(), once the rows have all been returned, undef is the next return value, and so a function fethrow_arrayref_exists is not possible. Arguments : Example : See Usage Returns : ArrayRef - the row of data requested, or undef if all rows have been returned already =cut sub fetchrow_arrayref { my ($this) = @_; # I don't like that this will copy the array an extra time - sorry my $answerRef = $this->SUPER::fetchrow_arrayref(); if(defined $this->err()) { confess("fetchrow_arrayref() failed for SQL \"" . $this->{"Statement"} . "\": " . DBI::errstr()); } return $answerRef; } #------------------------------------------------------------------------------- =head2 fetchrow_array Usage : @row = $buffStatement->fetchrow_array(); Function : Executes DBI's fetchrow_array() on the statement handle, will throw an exception if the statement returns abnormally. Note that when using fetchrow_array(), once the rows have all been returned, undef is the next return value, and so a function fetchrow_array_exists is not possible. Arguments : Example : See Usage Returns : Array - the row of data requested, or undef if all rows have been returned already =cut sub fetchrow_array { my ($this) = @_; # I don't like that this will copy the array an extra time - sorry my @answer = $this->SUPER::fetchrow_array(); if(defined $this->err()) { confess("fetchrow_array() failed for SQL \"" . $this->{"Statement"} . "\": " . DBI::errstr()); } return wantarray ? @answer : $answer[0]; } #------------------------------------------------------------------------------- =head2 fetchall_arrayref Usage : $answerListRef = $buffStatement->fetchall_arrayref(); $answerListRef = $buffStatement->fetchall_arrayref($sliceArrayRef); $answerListRef = $buffStatement->fetchall_arrayref($sliceHashRef); Function : Execute's DBI's fetchall_arrayref() on the statement handle. Will throw an exception if it returns abnormally(). Arguments : Array ref (optional) - slice to pass to DBI's fetchall_arrayref() Hash ref (optional) - slice to pass to DBI's fetchall_arrayref() Example : See Usage Returns : Array (or hash) ref - the reference returned by fetchall_arrayref() =cut sub fetchall_arrayref { my ($this, $sliceRef) = @_; my $answerRef = $this->SUPER::fetchall_arrayref($sliceRef); if(defined $this->err()) { confess("fetchall_arrayref() failed for SQL \"" . $this->{"Statement"} . "\": " . DBI::errstr()); } return $answerRef; } #------------------------------------------------------------------------------- =head2 fetchall_arrayref_exists Usage : $answerListRef = $buffStatement->fetchall_arrayref_exists(); $answerListRef = $buffStatement->fetchall_arrayref_exists($sliceArrayRef); $answerListRef = $buffStatement->fetchall_arrayref_exists($sliceHashRef); Function : Execute's DBI's fetchall_arrayref() on the statement handle. Will throw an exception if no data is returned, or if it returns abnormally. Use this when you NEED values returned. Arguments : Array ref (optional) - slice to pass to DBI's fetchall_arrayref() Hash ref (optional) - slice to pass to DBI's fetchall_arrayref() Example : See Usage Returns : Array or Hash ref - the reference returned by fetchall_arrayref() =cut sub fetchall_arrayref_exists { my ($this, $sliceRef) = @_; my $answerRef = $this->SUPER::fetchall_arrayref($sliceRef); if(defined $this->err()) { confess("fetchall_arrayref() failed for SQL \"" . $this->{"Statement"} . "\": " . DBI::errstr()); } if(!defined $answerRef || !defined $answerRef->[0] || !defined $answerRef->[0]->[0]) { confess("fetchall_arrayref_exists() resulted in no data for SQL \"" . $this->{"Statement"} . "\""); } return $answerRef; } } # End CBNP::DBI::ErrorHandler::st scope 1; # Got one? __END__ =head1 AUTHOR Clinton Torres (clinton.torres@llnl.gov) =head1 SEE ALSO =cut