problem with subclassing DBI and " is not a DBI handle (has no magic)"error

problem with subclassing DBI and " is not a DBI handle (has no magic)"error

am 19.06.2006 20:51:38 von Martin.Evans

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

Re: problem with subclassing DBI and " is not a DBI handle (has no magic)" error

am 20.06.2006 11:06:02 von Tim.Bunce

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
>
>

Re: problem with subclassing DBI and " is not a DBI handle (has no magic)" error

am 20.06.2006 19:04:19 von Martin.Evans

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
>>
>>

Re: problem with subclassing DBI and " is not a DBI handle (has no magic)" error

am 20.06.2006 20:50:34 von Tim.Bunce

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.


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
> >>
> >>
>

Re: problem with subclassing DBI and " is not a DBI handle (has nomagic)" error

am 20.06.2006 21:11:42 von Martin.Evans

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
>>>>
>>>>
>>
>
>
>
>

Re: problem with subclassing DBI and " is not a DBI handle (has nomagic)" error

am 21.06.2006 09:01:17 von Martin.Evans

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
>>>>>
>>>>>
>>>
>>>
>>>
>>
>>
>>
>>
>
>
>
>

RE: problem with subclassing DBI and " is not a DBI handle (has no magic)" error

am 21.06.2006 12:03:48 von Martin.Evans

Appologies to this list and especially Tim. The problem was in my subclass
DBIx::Log4perl which suffered from an autovivification problem in the
prepare method accurately described in the the DBI docs as:

"When calling a SUPER::method that returns a handle, be careful to check the
return value before trying to do other things with it in your overridden
method. This is especially important if you want to set a hash attribute on the
handle, as Perl's autovivification will bite you by (in)conveniently creating
an unblessed hashref, which your method will then return with usually baffling
results later on."

The main reasons I went down the wrong route was that the error I was
getting was listed in the DBI Changelog but suggesting a different
problem to the one I had and that DBD::DB2 and DBD::mysql worked whilst Oracle
didn't. The reason for this is that Oracle errors on a prepare if a column does
not exist and mysql/db2 don't so when you call selectrow_array with DBD::Oracle
you get:

subclass: selectrow_array
calls selectrow_array
subclass: prepare
calls prepare which fails where upon my subclass failed to check the
returned handle before using it.

and with DB2 you get:

subclass: selectrow_arrary
calls selectrow_array
DBI: selectrow_array
_do_selectrow_array
subclass: prepare
calls prepare which succeeds and hence returned statement handle is
good.

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


On 19-Jun-2006 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