Author: timbo
Date: Mon Jul 10 05:15:49 2006
New Revision: 6629

Modified:
   dbi/trunk/Changes
   dbi/trunk/DBI.xs
   dbi/trunk/lib/DBI/Profile.pm
   dbi/trunk/t/40profile.t

Log:
Added ability for DBI::Profile Path to contain code refs - cool.


Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes   (original)
+++ dbi/trunk/Changes   Mon Jul 10 05:15:49 2006
@@ -7,12 +7,14 @@
 =head2 Changes in DBI 1.52 (svn rev XXX),   XXX
 
 XXX update DBD::File (as sub-module?) to match latest.
-XXX update docs for Profile !Foo magic vars
+XXX update docs for Profile !Foo magic vars and code refs.
 
   Fixed memory leak (16 bytes per sth) thanks to Doru Theodor Petrescu.
   Fixed small memory leak (per interpreter/thread) thanks to Ephraim Dan.
   Fixed execute_for_fetch/execute_array to RaiseError thanks to Martin J. 
Evans.
-  Added $dbh->statistics_info thanks to Brandon Black.
+
+  Added ability for DBI::Profile Path to contain code refs - cool.
+  Added $dbh->statistics_info specification thanks to Brandon Black.
 
 =head2 Changes in DBI 1.51 (svn rev 6475),   6th June 2006
 

Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs    (original)
+++ dbi/trunk/DBI.xs    Mon Jul 10 05:15:49 2006
@@ -2235,7 +2235,8 @@
     if (SvTYPE(node) != SVt_PVHV) {
         HV *hv = newHV();
         if (SvOK(node))
-            warn("Profile data element %s replaced with new hash ref", 
neatsvpv(orig_node,0));
+            warn("Profile data element %s replaced with new hash ref (for %s)",
+                neatsvpv(orig_node,0), name);
         sv_setsv(node, newRV_noinc((SV*)hv));
         node = (SV*)hv;
     }
@@ -2245,7 +2246,7 @@
 
 
 static void
-dbi_profile(SV *h, imp_xxh_t *imp_xxh, const char *statement, SV *method, 
double t1, double t2)
+dbi_profile(SV *h, imp_xxh_t *imp_xxh, SV *statement_sv, SV *method, double 
t1, double t2)
 {
 #define DBIprof_MAX_PATH_ELEM  100
 #define DBIprof_COUNT          0
@@ -2261,6 +2262,7 @@
     int src_idx = 0;
     HV *dbh_outer_hv = NULL;
     HV *dbh_inner_hv = NULL;
+    char *statement_pv;
     SV *profile;
     SV *tmp;
     SV *dest_node;
@@ -2289,14 +2291,18 @@
        return;
     }
 
-    if (!statement) {
+    /* statement_sv: undef = use $h->{Statement}, "" (&sv_no) = use empty 
string */
+
+    if (!SvOK(statement_sv)) {
        SV **psv = hv_fetch(h_hv, "Statement", 9, 0);
-       statement = (psv && SvOK(*psv)) ? SvPV_nolen(*psv) : "";
+       statement_sv = (psv && SvOK(*psv)) ? *psv : &sv_no;
     }
+    statement_pv = SvPV_nolen(statement_sv);
+
     if (DBIc_DBISTATE(imp_xxh)->debug >= 4)
        PerlIO_printf(DBIc_LOGPIO(imp_xxh), "dbi_profile %s %f q{%s}\n",
                neatsvpv((SvTYPE(method)==SVt_PVCV) ? (SV*)CvGV(method) : 
method, 0),
-               ti, statement);
+               ti, neatsvpv(statement_sv,0));
 
     dest_node = _profile_next_node(profile, "Data");
 
@@ -2309,18 +2315,49 @@
        while ( src_idx <= len ) {
            SV *pathsv = AvARRAY(av)[src_idx++];
 
-           if (SvROK(tmp) && SvTYPE(SvRV(tmp))==SVt_PVCV) {
+           if (SvROK(pathsv) && SvTYPE(SvRV(pathsv))==SVt_PVCV) {
                /* call sub, use returned list of values as path */
-               /* if no values returned then don't save data   */
-               warn("code ref in Path");
-                dest_node = _profile_next_node(dest_node, "CODE");
+                /* returning a ref to undef vetos this profile data */
+                dSP;
+                I32 ax;
+                SV *code_sv = SvRV(pathsv);
+                I32 items;
+                I32 item_idx;
+                char *method_pv = (SvTYPE(method)==SVt_PVCV)
+                    ? GvNAME(CvGV(method))
+                    : (isGV(method) ? GvNAME(method) : SvPV_nolen(method));
+                EXTEND(SP, 4);
+                PUSHMARK(SP);
+                PUSHs(h);   /* push inner handle, then others params */
+               PUSHs( sv_2mortal(newSVpv(method_pv,0)));
+                PUTBACK;
+                SAVE_DEFSV; /* local($_) = $statement */
+                DEFSV = statement_sv;
+                items = call_sv(code_sv, G_ARRAY);
+                SPAGAIN;
+                SP -= items ;
+                ax = (SP - PL_stack_base) + 1 ;
+                for (item_idx=0; item_idx < items; ++item_idx) {
+                    SV *item_sv = ST(item_idx);
+                    if (SvROK(item_sv)) {
+                        if (!SvOK(SvRV(item_sv)))
+                            items = -2; /* flag that we're rejecting this 
profile data */
+                        else /* other refs reserved */
+                            warn("Ignored ref returned by code ref in Profile 
Path");
+                        break;
+                    }
+                    dest_node = _profile_next_node(dest_node, 
SvPV_nolen(item_sv));
+                }
+                PUTBACK;
+                if (items == -2) /* this profile data was vetoed */
+                    return;
            }
            else if (SvOK(pathsv)) {
                STRLEN len;
                 const char *p = SvPV(pathsv,len);
                if (p[0] == '!') { /* special cases */
                     if (p[1] == 'S' && strEQ(p, "!Statement")) {
-                        dest_node = _profile_next_node(dest_node, statement);
+                        dest_node = _profile_next_node(dest_node, 
statement_pv);
                     }
                     else if (p[1] == 'M' && strEQ(p, "!MethodName")) {
                         p = (SvTYPE(method)==SVt_PVCV)
@@ -2393,7 +2430,7 @@
        }
     }
     else { /* a bad Path value is treated as a Path of just Statement */
-        dest_node = _profile_next_node(dest_node, statement);
+        dest_node = _profile_next_node(dest_node, statement_pv);
     }
 
 
@@ -3246,8 +3283,8 @@
        }
 
        if (profile_t1) { /* see also dbi_profile() call a few lines below */
-           char *Statement = (ima_flags & IMA_UNRELATED_TO_STMT) ? "" : Nullch;
-           dbi_profile(h, imp_xxh, Statement, imp_msv ? imp_msv : (SV*)cv,
+           SV *statement_sv = (ima_flags & IMA_UNRELATED_TO_STMT) ? &sv_no : 
&sv_undef;
+           dbi_profile(h, imp_xxh, statement_sv, imp_msv ? imp_msv : (SV*)cv,
                profile_t1, dbi_time());
        }
        if (is_warning) {
@@ -3262,8 +3299,8 @@
        }
     }
     else if (profile_t1) { /* see also dbi_profile() call a few lines above */
-       char *Statement = (ima_flags & IMA_UNRELATED_TO_STMT) ? "" : Nullch;
-       dbi_profile(h, imp_xxh, Statement, imp_msv ? imp_msv : (SV*)cv,
+        SV *statement_sv = (ima_flags & IMA_UNRELATED_TO_STMT) ? &sv_no : 
&sv_undef;
+       dbi_profile(h, imp_xxh, statement_sv, imp_msv ? imp_msv : (SV*)cv,
                profile_t1, dbi_time());
     }
 
@@ -3923,9 +3960,8 @@
     D_imp_xxh(h);
     STRLEN lna = 0;
     (void)cv;
-    dbi_profile(h, imp_xxh,
-       SvOK(statement) ? SvPV(statement,lna) : Nullch,
-       SvROK(method)   ? SvRV(method)        : method,
+    dbi_profile(h, imp_xxh, statement,
+       SvROK(method) ? SvRV(method) : method,
        t1, t2
     );
     RETVAL = &sv_undef;
@@ -4028,7 +4064,7 @@
     if (trace)
        PerlIO_printf(DBILOGFP,"    <- $DBI::%s= %s\n", meth, 
neatsvpv(ST(0),0));
     if (profile_t1)
-       dbi_profile(DBI_LAST_HANDLE, imp_xxh, Nullch, (SV*)cv, profile_t1, 
dbi_time());
+       dbi_profile(DBI_LAST_HANDLE, imp_xxh, &sv_undef, (SV*)cv, profile_t1, 
dbi_time());
 
 
 MODULE = DBI   PACKAGE = DBD::_::db

Modified: dbi/trunk/lib/DBI/Profile.pm
==============================================================================
--- dbi/trunk/lib/DBI/Profile.pm        (original)
+++ dbi/trunk/lib/DBI/Profile.pm        Mon Jul 10 05:15:49 2006
@@ -638,7 +638,7 @@
        (my $progname = $0) =~ s:.*/::;
        if ($count) {
            $prologue .= sprintf "%fs ", $time_in_dbi;
-           my $perl_time = ($DBI::PERL_ENDING) ? time_in_dbi() - $^T : $t2-$t1;
+           my $perl_time = ($DBI::PERL_ENDING) ? time() - $^T : $t2-$t1;
            $prologue .= sprintf "%.2f%% ", $time_in_dbi/$perl_time*100 if 
$perl_time;
            my @lt = localtime(time);
            my $ts = sprintf "%d-%02d-%02d %02d:%02d:%02d",
@@ -694,6 +694,7 @@
 
 sub format_profile_thingy {
     my ($self, $thingy, $depth, $pad, $path, $leaves) = @_;
+    return "undef" if not defined $thingy;
     return $self->format_profile_leaf(  $thingy, $depth, $pad, $path, $leaves)
        if UNIVERSAL::isa($thingy,'ARRAY');
     return $self->format_profile_branch($thingy, $depth, $pad, $path, $leaves)

Modified: dbi/trunk/t/40profile.t
==============================================================================
--- dbi/trunk/t/40profile.t     (original)
+++ dbi/trunk/t/40profile.t     Mon Jul 10 05:15:49 2006
@@ -20,7 +20,7 @@
     }
 }
 
-use Test::More tests => 37;
+use Test::More tests => 43;
 
 $Data::Dumper::Indent = 1;
 $Data::Dumper::Terse = 1;
@@ -177,6 +177,7 @@
 ok($output =~ /\((\d+) calls\)/);
 ok($1 >= $count);
 
+# 
-----------------------------------------------------------------------------------
 
 # try statement and method name path
 $dbh = DBI->connect("dbi:ExampleP:", 'usrnam', '', {
@@ -186,12 +187,11 @@
 $sql = "select name from .";
 $sth = $dbh->prepare($sql);
 $sth->execute();
-while ( my $hash = $sth->fetchrow_hashref ) {}
+$sth->fetchrow_hashref;
 undef $sth; # DESTROY
 
 $tmp = sanitize_tree($dbh->{Profile});
 # make test insentitive to number of local files
-$tmp->{Data}{usrnam}{'select name from .'}{foo}{fetchrow_hashref}[0] = -1;
 is_deeply $tmp, bless {
     'Path' => [ '{Username}', DBIprofile_Statement, 'foo', 
DBIprofile_MethodName ],
     'Data' => {
@@ -205,15 +205,78 @@
            'select name from .' => {
                    'foo' => {
                        'execute' => [ 1, 0, 0, 0, 0, 0, 0 ],
-                       'fetchrow_hashref' => [ -1, 0, 0, 0, 0, 0, 0 ],
+                       'fetchrow_hashref' => [ 1, 0, 0, 0, 0, 0, 0 ],
                        'DESTROY' => [ 1, 0, 0, 0, 0, 0, 0 ],
-                       'prepare' => [ 1, 0, 0, 0, 0, 0, 0 ]
+                       'prepare' => [ 1, 0, 0, 0, 0, 0, 0 ],
+                        # XXX finish shouldn't be profiled as it's not called 
explicitly
+                        # but currently the finish triggered by DESTROY does 
get profiled
+                       'finish' => [ 1, 0, 0, 0, 0, 0, 0 ],
                    },
            },
        },
     },
 } => 'DBI::Profile';
 
+
+# 
-----------------------------------------------------------------------------------
+
+print "testing code ref in Path\n";
+
+sub run_test1 {
+    my ($profile) = @_;
+    $dbh = DBI->connect("dbi:ExampleP:", 'usrnam', '', {
+        RaiseError => 1,
+        Profile => $profile,
+    });
+    $sql = "select name from .";
+    $sth = $dbh->prepare($sql);
+    $sth->execute();
+    $sth->fetchrow_hashref;
+    undef $sth; # DESTROY
+    return sanitize_profile_data_nodes($dbh->{Profile}{Data});
+}
+
+$tmp = run_test1( { Path => [ 'foo', sub { 'bar' }, 'baz' ] });
+is_deeply $tmp, { 'foo' => { 'bar' => { 'baz' => [ 8, 0,0,0,0,0,0 ] } } };
+
+$tmp = run_test1( { Path => [ 'foo', sub { 'ping','pong' } ] });
+is_deeply $tmp, { 'foo' => { 'ping' => { 'pong' => [ 8, 0,0,0,0,0,0 ] } } };
+
+$tmp = run_test1( { Path => [ 'foo', sub { \undef } ] });
+is_deeply $tmp, { 'foo' => undef }, 'should be vetoed';
+
+# check what code ref sees in $_
+$tmp = run_test1( { Path => [ sub { $_ } ] });
+is_deeply $tmp, {
+  '' => [ 3, 0, 0, 0, 0, 0, 0 ],
+  'select name from .' => [ 5, 0, 0, 0, 0, 0, 0 ]
+}, '$_ should contain statement';
+
+# check what code ref sees in @_
+$tmp = run_test1( { Path => [ sub { my ($h,$method) = @_; return (ref $h, 
$method) } ] });
+is_deeply $tmp, {
+  'DBI::db' => {
+    'FETCH'   => [ 1, 0, 0, 0, 0, 0, 0 ],
+    'prepare' => [ 1, 0, 0, 0, 0, 0, 0 ],
+    'STORE'   => [ 2, 0, 0, 0, 0, 0, 0 ],
+  },
+  'DBI::st' => {
+    'fetchrow_hashref' => [ 1, 0, 0, 0, 0, 0, 0 ],
+    'execute' => [ 1, 0, 0, 0, 0, 0, 0 ],
+    'finish'  => [ 1, 0, 0, 0, 0, 0, 0 ],
+    'DESTROY' => [ 1, 0, 0, 0, 0, 0, 0 ],
+  },
+}, 'should have @_ as keys';
+
+# check we can filter by method
+$tmp = run_test1( { Path => [ sub { return \undef unless $_[1] =~ /^fetch/; 
return $_[1] } ] });
+#warn Dumper($tmp);
+is_deeply $tmp, {
+    'fetchrow_hashref' => [ 1, 0, 0, 0, 0, 0, 0 ],
+}, 'should be able to filter by method';
+
+# 
-----------------------------------------------------------------------------------
+
 print "dbi_profile_merge\n";
 my $total_time = dbi_profile_merge(
     my $totals=[],
@@ -244,18 +307,20 @@
     my $data = shift;
     return $data unless ref $data;
     $data = dclone($data);
-    _sanitize_node($data->{Data}) if $data->{Data};
+    sanitize_profile_data_nodes($data->{Data}) if $data->{Data};
     return $data;
 }
 
-sub _sanitize_node {
+sub sanitize_profile_data_nodes {
     my $node = shift;
     if (ref $node eq 'HASH') {
-        _sanitize_node($_) for values %$node;
+        sanitize_profile_data_nodes($_) for values %$node;
     }
     elsif (ref $node eq 'ARRAY') {
-       # sanitize the profile data node so tests
-       $_ = 0 for @[EMAIL PROTECTED]; # not 0
+        if (@$node == 7 and DBI::looks_like_number($node->[0])) {
+            # sanitize the profile data node to simplify tests
+            $_ = 0 for @[EMAIL PROTECTED]; # not 0
+        }
     }
-    return;
+    return $node;
 }

Reply via email to