On Wed, Feb 06, 2002 at 09:14:19AM +0100, Henrik Tougaard wrote:
> > Don't be surprised if you haven't heard of the Handlers attribute,
> > it's not documented.  And don't bother finding out about it because
> > it's probably going away.
> > 
> > But I think I have mentioned it a couple of times and wanted to
> > check if anyone was using it for anything? And if so, what (so
> > I can try to make sure there's a better way to do the same thing.)
> 
> Jup. We use it.
> 
> WE have an environment where we have DBI (and DBD::Ingres of course)
> and CORBA (using COPE - the perl CORBA ORB).
> They have different ways of handling exceptions.
> The DBI uses RaiseError, that calls a 'die', which is normally good.
> COPE uses Experimental::Exception (for historic reasons).
> CORBA has its own exception system and COPE models certain
> Exp::Exc exceptions to CORBA-exceptions.
> A 'normal' die, ie. with a string, is not a CORBA exception.
> 
> So we use a $dbh->{handler} to catch the DBI exception and turn it
> into a Experimental::Exception exception, that is a CORBA exception
> of a type that is valid for the method called.

Ah yes, that rings a bell.

> This could be done with a lot of eval- and try-blocks, but that
> is counter to the idea of RaiseError - ie. code as if there were
> no errors and if errors occur the 'do the rigth thing'.
> And $dbh->{handler} does this nicely. I do agree that it is not
> fit for public consumption, you do need to know a bit (a lot?)
> about the internals of DBI and DBD to be able to use it.
> 
> To sum it up: we do need a way of catching DBI errors and turning
> them into something else.

And now you have it...

> Something with a better interface would be nice. It would probably
> need to differ between $dbh errors and $sth erros (and $drh errors
> of course) as you often need to do things another way if you are
> in the middle of a select than if you are trying to update.

How does this sound:

    =item C<HandleError> (code ref, inherited) I<NEW>
     
    This attribute can be used to provide your own alternative behaviour
    in case of errors. If set to a reference to a subroutine then that
    subroutine is called when an error is detected (at the same point that
    C<RaiseError> and C<PrintError> are handled).
     
    The subroutine is called with three parameters: the error message
    string that C<RaiseError> and C<PrintError> would use,
    the DBI handle being used, and the first value being returned by
    the method that failed (typically undef).
     
    If the subroutine returns a false value then the C<RaiseError>
    and/or C<PrintError> attributes are checked and acted upon as normal.

    For example, to get a full stack trace for any error:
     
      use Carp;
      $h->{HandleError} = sub { confess(shift) };
     
    Or to turn errors into exceptions:
     
      use Exception; # or your own favourite exception module
      $h->{HandleError} = sub { Exception->new('DBI')->raise($_[0]) };
     
    It is possible to 'stack' multiple HandleError handlers by using
    closures:
     
      sub your_subroutine {
        my $previous_handler = $h->{HandleError};
        $h->{HandleError} = sub {
          return 1 if $previous_handler and &$previous_handler(@_);
          ... your code here ...
        }
      }

    Using a C<my> inside a subroutine to store the previous C<HandleError>
    value is important.  See L<perlsub> and L<perlref> for more information
    about I<closures>.
     
    It is possible for C<HandleError> to hide an error, to a limited
    degree, by using L</set_err> to reset $DBI::err and $DBI::errstr,
    and altering the return value of the failed method. For example:
     
      $h->{HandleError} = sub {
        return 0 unless $_[0] =~ /^\S+ fetchrow_arrayref failed:/;
        return 0 unless $_[1]->err == 1234; # the error to 'hide'
        $h->set_err(0,"");  # turn off the error
        $_[2] = [ ... ];    # supply alternative return value
        return 1;
      };
     
    This only works for methods which return a single value and is hard
    to make reliable (avoiding infinite loops, for example) and so isn't
    recommended for general use!  If you find a I<good> use for it then
    please let me know.

Hopefully that'll enable what you want (and more :)

Tim.

Reply via email to