Reputation: 119
I need to intercept all queries (in order to, say, do some structered logging).
The app already uses a sub-classed DBI so I was hoping I could just inject the code I need in to the execute
overload in MyDB::st
class.
Apparently, this is not sufficient.
In the example below, MyDB::st::execute
executes only in example 2). It does not execute in 1) where I call do
nor in 3) where I call selectrow_array
(even though the documentation says selectrow_array
combines prepare
, execute
, and fetchrow_hashref
).
Do I need to overload statements in MyDB::db
class as well? There is quite a few (do
, selectrow_array
, selectrow_arrayref
, selectrow_hashref
, selectall_arrayref
, selectall_array
, selectall_hashref
, selectcol_arrayref
, etc.) I was hoping I could avoid that.
The example uses SQLite for simplicity but other drivers I checked worked the same.
#!/usr/bin/perl
package MyDB;
use strict;
use warnings;
use base 'DBI';
sub connect {
return DBI::connect( 'MyDB', 'dbi:SQLite:dbname=so.sqlite', '', '',);
}
package MyDB::db;
use strict;
use warnings;
use base 'DBI::db';
sub prepare {
my $self = shift;
warn " DB: PREPARE\n";
return $self->SUPER::prepare( @_ );
}
package MyDB::st;
use strict;
use warnings;
use base 'DBI::st';
sub execute {
my $self = shift;
warn " ST: EXECUTE\n";
return $self->SUPER::execute( @_ );
}
sub fetchrow_hashref {
my $self = shift;
warn " ST: FETCHROW_HASHREF\n";
return $self->SUPER::fetchrow_hashref( @_ );
}
package main;
use strict;
use warnings;
my $dbh = MyDB::connect();
warn "1) --- DO ---\n";
$dbh->do("CREATE TABLE IF NOT EXISTS ttt (a int)");
warn "2) --- PREPARE/EXECUTE/FETCHROW_HASHREF ---\n";
my $sth = $dbh->prepare( "SELECT * FROM ttt" );
$sth->execute();
$sth->fetchrow_hashref();
warn "3) --- SELECTROW_ARRAY ---\n";
$dbh->selectrow_array( "SELECT * FROM ttt" );
Upvotes: 1
Views: 128
Reputation: 386206
While the documentation describes certain methods in terms of other methods, the documentation doesn't claim these method call those other methods.
Each driver provides their own implementation of many of the DBI methods (e.g. selectrow_array
) through the use of a template provided by DBI. Written in C, the implementation of the methods in the template frequently call the driver's functions directly rather than calling them indirectly through DBI method calls.
This makes the calls faster, but as you've discovered, it makes it harder to extend DBI.
You can find a Perl implementation of all the methods you listed inside of DBI.pm
. Copy these. These implementations are all defined in terms of the following methods, limiting what you have to change:
prepare
execute
fetch
fetchrow_hashref
fetchrow_arrayref
bind_col
Upvotes: 4
Reputation: 5279
If all you want to do is log queries, you don't need to intercept anything. Try setting the DBI_TRACE
environment variable.
DBI_TRACE=1=dbitrace.log
where the 1
is the log level and the second argument is a path to your log file. See https://metacpan.org/pod/DBI#DBI_TRACE and https://metacpan.org/pod/DBI#TRACING
Upvotes: 2