package newDBI;
use strict;
use warnings;

use DBI;

@newDBI::ISA = qw(DBI);

# List of names of classes with hooks into us; the user is expected to
# change this.  Best do it before the first significant event.
our @ANALYSE = qw( newDBI::default_analyser );

BEGIN {
    warn "****************************\n";
    warn "* SQL BENCHMARKING ENABLED *\n";
    warn "****************************\n\n";

}

###  Globals for newDBI::default_analyser
#    Left up here to conserve interface of newDBI::setSortRoutine etc.
#
#

our $debug = defined $ENV{DEBUG_WRAPDBI};
our (%counts, %times);

my $start = time();

#
#
###

sub connect {
    my ($self, @args) = @_;

    foreach my $a (@newDBI::ANALYSE) {
	$a->preConnect($self, @args);
    }

    #
    # rebless the DBI::db handle into our own subclass so we can overide its prepare() method
    #
    my $dbh = bless(DBI->connect(@args), "newDBI::db");

    foreach my $a (@newDBI::ANALYSE) {
	$a->postConnect($self, \$dbh, @args);
    }

    return $dbh;
}

END {
    foreach my $a (@ANALYSE) {
	$a->showResults;
    }
}


package newDBI::db;
use strict;
use warnings;

@newDBI::db::ISA = qw(DBI::db);

use Time::HiRes qw( gettimeofday tv_interval );

sub prepare {
    my ($self, @args) = @_;

    foreach my $a (@newDBI::ANALYSE) {
	$a->prePrepare($self, @args);
    }

    #
    # rebless the DBI::st handle into our own subclass so we can overide its execute() method
    #
    my $t0 = [gettimeofday];
    my $sth = bless($self->SUPER::prepare(@args), 'newDBI::st');
    my $time = tv_interval( $t0, [gettimeofday]);

    foreach my $a (@newDBI::ANALYSE) {
	$a->postPrepare($self, $sth, $t0, $time, @args);
    }

    return $sth;
}


package newDBI::st;
use strict;
use warnings;

@newDBI::st::ISA = qw(DBI::st);

use Time::HiRes qw( gettimeofday tv_interval );

my ($preExecHook, $postExecHook);

sub setPreExecHook {
    my $ref = shift;

    $preExecHook = $ref unless defined $ref and ref $ref ne 'CODE';
}

sub setPostExecHook {
    my $ref = shift;

    $postExecHook = $ref unless defined $ref and ref $ref ne 'CODE';
}

sub execute {
    my ($self, @args) = @_;

    my $statement = $self->{Statement};

    foreach my $a (@newDBI::ANALYSE) {
	$a->preExecute($self, $statement, @args);
    }

    my $t0 = [gettimeofday];
    my $rv = $self->SUPER::execute(@args);
    my $time = tv_interval( $t0, [gettimeofday]);

    foreach my $a (@newDBI::ANALYSE) {
	$a->postExecute($self, $statement, \$rv, $t0, $time, @args);
    }

    return $rv;
}


### Analysis methods for benchmarking

package newDBI::default_analyser;

sub preConnect{
    my ($called, $dbi_called, @args) = @_;
}

sub postConnect{
    my ($called, $dbi_called, $dbh_ref, @args) = @_;
}

sub prePrepare {
    my ($called, $dbh, @args) = @_;
}

sub postPrepare {
    my ($called, $dbh, $sth, $t0, $time, @args) = @_;
}

sub preExecute {
    my ($called, $sth, $statement, @args) = @_;
    &$preExecHook($sth) if defined $preExecHook;
    $newDBI::counts{$statement}++;
}

sub postExecute {
    my ($called, $sth, $statement, $rv_ref, $t0, $time, @args) = @_;
    $newDBI::times{$statement} += $time;
    &$postExecHook($sth, $$rv_ref) if defined $postExecHook;
}


sub showResults {
    my $called = shift; # ignored (class method)
    my ($total, $totalTime) = (0, 0);
    warn "\n****************************\n";
    warn "* SQL BENCHMARKING RESULTS *\n";
    warn "****************************\n\n";
	foreach (sort { $times{$b} <=> $times{$a} } keys %counts) {
		warn sprintf("%10d %10.6f %s\n\n", $counts{$_}, $times{$_}, $_);
		$total += $counts{$_};
		$totalTime += $times{$_};
	}
    my $elapsed = time() - $start;
    warn sprintf("\n\nWallclock time elapsed for %d queries in %.6f secs was %d secs\n",
		 $total, $totalTime, $elapsed);
}


1;
