roman has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

hope there's someone skilled enough in DBI.

Can I subclass DBI (DBI::db) and have the errors (I use RaiseError = 1) reported in user not in subclassing code?

Better with example:

#!/usr/bin/perl use strict; use warnings; { package MyDBI; use base qw(DBI); package MyDBI::db; use base qw(DBI::db); sub selectrow_array { my ($this) = shift; # only for ilustration return $this->SUPER::selectrow_array(@_); } package MyDBI::st; use base qw(DBI::st); } my $db = ...; # any connection, user, pwd my $user = ...; my $auth = ...; my $dbh = DBI->connect( "dbi:mysql:$db", $user, $auth, { 'RaiseError' => 1, 'RootClass' => 'MyDBI', 'PrintError' => 0, } ); warn ref($dbh); warn join ';', $dbh->selectrow_array("SELECT 'dog', 'cat', 'rat' "); warn join ';', $dbh->selectrow_array("SELECT 'dog', 'cat', 'rat' FROM +no_such_table");

running this code I give

MyDBI::db at simple.pl line 35. dog;cat;rat at simple.pl line 36. DBD::mysql::db selectrow_array failed: Table 'devel.no_such_table' doe +sn't exist at simple.pl line 15.

As you see the error is reported to occur on line 15 (inside MyDBI::db::selectrow_array), but I want it to be reported on line 38 (where selectrow_array is called).

Can I make DBI to report it this way?

Thanks for any help

Replies are listed 'Best First'.
Re: How to subclass DBI and still have the errors reported in user code
by moritz (Cardinal) on Sep 11, 2007 at 12:06 UTC
    use Carp qw(croak) # in your overrided method: eval { # call SUPER method here } if ($@){ croak($@); }

      Thanks for idea, it is the solution, but:

      With catch and croak you have to take care of context (AFAIK), something like this:

      sub selectrow_array { my $this = shift(); my ( @ret, $ret ); eval { if (wantarray) { # list @ret = $this->SUPER::selectrow_array(@_); } elsif ( defined(wantarray) ) { # scalar $ret = $this->SUPER::selectrow_array(@_); } else { # void - nonsense $this->SUPER::selectrow_array(@_); } }; croak $@ if $@; return if !defined wantarray; return wantarray ? @ret : $ret; }

      The original error line is still being reported. Of course you can strip it with RE. But with $dbh->{'PrintError'} set the line still appear.

      DBD::mysql::db selectrow_array failed: Table 'devel.no_such_table' doe +sn't exist at simple.pl line 17. DBD::mysql::db selectrow_array failed: Table 'devel.no_such_table' doe +sn't exist at simple.pl line 17. at simple.pl line 43

      DBI actually "croaks" for itself. The error is reported on the boundary of user and DBI code. What I want is to tell DBI: this code is safe, don't report errors from here, go up in stack. I dont' know whether DBI has some rules for telling whether a call shouldn't generate errors as Carp have (inheritance is one the rules).

      Maybe that the elegant, DBI specific solution, doesn't exist.

Re: How to subclass DBI and still have the errors reported in user code
by pilcrow (Sexton) on Sep 11, 2007 at 14:40 UTC
    As you see the error is reported to occur on line 15 (inside MyDBI::db::selectrow_array), but I want it to be reported on line 38 (where selectrow_array is called).
    Adapting DBI.pm's discussion of error handling, you might try:
    use Carp; DBI->connect(..., { RootClass => ..., HandleError => sub { local $Carp::CarpLevel = 1; $_[0] = Carp::shortmess($_[0]); 0; }, }

    Your subclass' connected method might install something like this by default, taking care to remember and summon a user-supplied HandleError, too.

    -pilcrow
Re: How to subclass DBI and still have the errors reported in user code
by thospel (Hermit) on Sep 12, 2007 at 13:31 UTC
    You could try to use the @CARP_NOT variable (see the Carp documentation) like
    @CARP_NOT = qw(DBI);
    That will take out the DBI level and all modules that DBI trusts. Most likely you will have to add other modules in the list too since few people bother to set up Carp trust relationships. In your case you'll likely have to add some DBD::mysql specific module names like DBD::mysql::db (but maybe that DBD::mysql will work too)