When I said it works with DBD::mysql and DBD::DB2 I should have said
it worked. I've upgraded DBI and Log4perl since then so I need to
recheck. Will do so today.

Martin

Martin J. Evans wrote:
Tim Bunce wrote:

I'm saying that when you subclass the DBI (not a driver) then
the connect method in your dr subclass is not called.

So if you've used a subclass called MyDBI then your MyDBI::dr::connect
method is not called by MyDBI->connect.

(The drivers own ...::dr::connect method is still being called.)

You need to either do what you want in your MyDBI::connect method
or in your MyDBI::db::connected method.

Tim.


Tim,

Thanks.

I don't have a DBIx::Log4perl::dr::connect method, only a
DBIx::Log4perl::connect method (in fact I don't have any DBIx::Log4perl::dr methods). Also, I only saw the problem
described when using DBD::Oracle - no problem with DBD::mysql
or DBD::DB2.

All my connect code is in DBIx::Log4perl::connect and that basically
resolves to:

sub connect {
    my ($drh, $dsn, $user, $pass, $attr) = @_;
    my %h = ();
    my $log;
    if ($attr) {
# check we have not got DBIx_l4p_init without DBIx_l4p_log or vice versa
    my ($a, $b) = (exists($attr->{DBIx_l4p_init}),
               exists($attr->{DBIx_l4p_class}));
    die "DBIx_l4p_init specified without DBIx_l4p_class or vice versa"
      if (($a xor $b));
    # if passed a Log4perl log handle use that
    if (exists($attr->{DBIx_l4p_logger})) {
        $h{logger} = $attr->{DBIx_l4p_logger};
    } elsif ($a && $b) {
        Log::Log4perl->init($attr->{DBIx_l4p_init});
        $h{logger} = Log::Log4perl->get_logger($attr->{DBIx_l4p_class});
        $h{init} = $attr->{DBIx_l4p_init};
        $h{class} = $attr->{DBIx_l4p_class};
    } else {
        $h{logger} = Log::Log4perl->get_logger(); # "DBIx::Log4perl"
    }
    # save log mask
    $LogMask = $attr->{DBIx_l4p_logmask}
      if (exists($attr->{DBIx_l4p_logmask}));
    if ($LogMask & DBIX_L4P_LOG_ERRCAPTURE) {
        # save any error handler for DBI and replace with our own
        $h{HandleError} = $attr->{HandleError}
          if (exists($attr->{HandleError}));
        $attr->{HandleError} = \&_error_handler;
    }
    # remove our attrs from connection attrs
    delete $attr->{DBIx_l4p_init};
    delete $attr->{DBIx_l4p_class};
    delete $attr->{DBIx_l4p_logger};
    delete $attr->{DBIx_l4p_logmask};
    }
    $h{logger} = Log::Log4perl->get_logger() if (!exists($h{logger}));
    $glogger = $h{logger};
    my $dbh = $drh->SUPER::connect($dsn, $user, $pass, $attr);
    return $dbh if (!$dbh);

    $dbh->{private_DBIx_Log4perl} = \%h;
    if ($LogMask & DBIX_L4P_LOG_CONNECT) {
    $h{logger}->debug("connect: $dsn, $user");
    no strict 'refs';
    my $v = "DBD::" . $dbh->{Driver}->{Name} . "::VERSION";
    $h{logger}->info("DBI: " . $DBI::VERSION,
             ", DBIx::Log4perl: " . $DBIx::Log4perl::VERSION .
               ", Driver: " . $dbh->{Driver}->{Name} . "(" .
                 $$v . ")");
    }
    return $dbh;
}

Any further ideas?

Martin

On Tue, Jun 20, 2006 at 06:04:19PM +0100, Martin J. Evans wrote:

Tim,

I'm sorry but I obviously being a bit thick here. I'm using DBD::Oracle. Are
you saying DBD::Oracle::dr::connect is not being called and I
need to create a DBIx::Log4perl::db::connected method and call
DBD::Oracle::dr::connect in it and if so how?

Martin
--
Martin J. Evans
Easysoft Ltd, UK
http://www.easysoft.com


On 20-Jun-2006 Tim Bunce wrote:

I think a similar issue came up a few months ago.

It turns out that the *::dr::connect method is not called. There's a comment in t/30subclass.t that says:

   # the MyDBI::dr::connect method is NOT called!
   # you can either override MyDBI::connect()
   # or use MyDBI::db::connected()

but the DBI docs haven't been updated to match.
Sorry about that.

Patches welcome.

Tim.

On Mon, Jun 19, 2006 at 07:51:38PM +0100, Martin J. Evans wrote:

Hi,

This is a fairly complex setup to explain so I'll initially try and keep it simple and can expand it necessary. I'm getting the error

$ perl a.pl
SV = RV(0x9fa6df4) at 0xa4450e8
 REFCNT = 1
 FLAGS = (TEMP,ROK)
 RV = 0xa4451a8
dbih_getcom handle HASH(0xa4451a8) is not a DBI handle (has no magic) at /usr/lib/perl5/site_perl/5.8.8/DBIx/Log4perl/db.pm line 61.

I'm using DBI 1.51 and DBD::Oracle 1.17 and DBIx::Log4perl 0.05.
DBIx::log4perl is overriding most methods in DBI.

I'm running a script like this:

use XXX::DB;
use Log::Log4perl qw(get_logger :levels);
Log::Log4perl->init_and_watch("/etc/log4.conf", 60);
my $zzz = XXX::DB->new(
{DSN =>"dbi:Oracle:XE",User=>"xxx", Pass=>"yyy"});
my $dbh = $zzz->connect() or die "$DBD::errstr";
$dbh->selectrow_array("select x_entry_status_id from v_market_entries where entry_id = ? and market_id = ?", undef, 31, 11);

where XXX:DB just provides some extra methods not in DBI to
avoid differences in DBD::oracle, DBD::DB2 and DBD::mysql and
uses Log::Log4perl::get_logger but it only comes in to this in that it uses DBIx::Log4perl.

If the script is changed to omit the DBIx::Log4perl it becomes:

use DBI;
my $dbh = DBI->connect("dbi:Oracle:XE", "xxx", "yyy",{RaiseError => 1}) or die "$DBD::errstr"; $dbh->selectrow_array("select entry_status_id from v_market_entries where x_entry_id = ? and market_id = ?", undef, 31, 11);

The first thing to note is the second script works.
The second thing to note is that the column x_entry_id does NOT exist.
If the selectrow_array SQL is for a column that does exist it
works fine in both scripts.

I've read:

+One more thing to note: you must let the DBI do the handle creation.
+If you want to override the connect() method in your *::dr class then
+it must still call SUPER::connect to get a $dbh to work with.
+Similarly, an overridden prepare() method in *::db must still call
+SUPER::prepare to get a $sth. If you try to create your own handles
+using bless() then you'll find the DBI will reject them with an "is not
+a DBI handle (has no magic)" error.

from the DBI changelog and it would suggest I've done something wrong
when overriding the methods in DBI. However, I believe I've done the SUPER::connect and SUPER::selectrow_array correctly like this (this
example does omit some extra stuff on the private hash):

sub connect {
   my ($drh, $dsn, $user, $pass, $attr) = @_;
   my %h = ();
   my $dbh = $drh->SUPER::connect($dsn, $user, $pass, $attr);
   return $dbh if (!$dbh);
   # stuff setting $h{xxx}
   $dbh->{private_DBIx_Log4perl} = \%h;
   return $dbh;
}
sub selectrow_array {
   my ($dbh, @args) = @_;

   my $h = $dbh->{private_DBIx_Log4perl};

   if (wantarray) {
       my @ret = $dbh->SUPER::selectrow_array(@args);
       return @ret;

   } else {
       my $ret = $dbh->SUPER::selectrow_array(@args);
       return $ret;
   }
}

The comment in the DBI changelog suggests I've done something wrong.
Any ideas? If more info is required I can provide but I didn't want
to make this too convoluted initially.

Thanks.

Martin













Reply via email to