Author: timbo
Date: Sun Jul 23 14:39:31 2006
New Revision: 6693

Added:
   dbi/trunk/t/43prof_env.t
Modified:
   dbi/trunk/Changes
   dbi/trunk/DBI.xs
   dbi/trunk/MANIFEST
   dbi/trunk/dumpmethods.pl
   dbi/trunk/lib/DBI/Profile.pm
   dbi/trunk/lib/DBI/PurePerl.pm
   dbi/trunk/t/40profile.t
   dbi/trunk/t/41prof_dump.t
   dbi/trunk/t/72childhandles.t
   dbi/trunk/test.pl

Log:
Fixed memory leak (per handle) thanks to Nicholas Clark.
*** A small leak remains, probably since DBI 1.49.
Changed parsing of non-numeric DBI_PROFILE env var values.
Changed DBI::Profile docs extensively - still a work-in-progress


Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes   (original)
+++ dbi/trunk/Changes   Sun Jul 23 14:39:31 2006
@@ -10,9 +10,13 @@
 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 memory leak (per handle) thanks to Nicholas Clark.
+    A small leak remains, probably since DBI 1.49.
   Fixed execute_for_fetch/execute_array to RaiseError thanks to Martin J. 
Evans.
 
+  Changed parsing of non-numeric DBI_PROFILE env var values.
+  Changed DBI::Profile docs extensively.
+
   Added ability for DBI::Profile Path to contain code refs - cool!
   Added $dbh->statistics_info specification thanks to Brandon Black.
 

Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs    (original)
+++ dbi/trunk/DBI.xs    Sun Jul 23 14:39:31 2006
@@ -1068,6 +1068,7 @@
        DBIc_ATTR(imp, Errstr)   = COPY_PARENT("Errstr",1,0);   /* scalar ref   
*/
        DBIc_ATTR(imp, TraceLevel)=COPY_PARENT("TraceLevel",0,0);/* scalar 
(int)*/
        DBIc_ATTR(imp, FetchHashKeyName) = COPY_PARENT("FetchHashKeyName",0,0); 
/* scalar ref */
+
        if (parent) {
            dbih_setup_attrib(h,imp,"HandleSetErr",parent,0,1);
            dbih_setup_attrib(h,imp,"HandleError",parent,0,1);
@@ -1080,10 +1081,13 @@
                AV *av;
                /* add weakref to new (outer) handle into parents ChildHandles 
array */
                tmp_svp = hv_fetch((HV*)SvRV(parent), "ChildHandles", 12, 1);
-               if (!SvROK(*tmp_svp))
-                   sv_setsv(*tmp_svp, (SV*)newRV_noinc((SV*)newAV()));
+               if (!SvROK(*tmp_svp)) {
+                   SV *ChildHandles_rvav = newRV((SV*)newAV());
+                   sv_setsv(*tmp_svp, ChildHandles_rvav);
+                   sv_free(ChildHandles_rvav);
+                }
                av = (AV*)SvRV(*tmp_svp);
-               av_push(av, (SV*)sv_rvweaken(newRV((SV*)SvRV(orv))));
+                av_push(av, (SV*)sv_rvweaken(newRV((SV*)SvRV(orv))));
                if (av_len(av) % 120 == 0) {
                    /* time to do some housekeeping to remove dead handles */
                    I32 i = av_len(av); /* 0 = 1 element */
@@ -1564,7 +1568,7 @@
         cacheit = 1; /* just save it in the hash */
     }
     else if (strEQ(key, "Profile")) {
-       static const char dbi_class[] = "DBI::Profile";
+       static const char profile_class[] = "DBI::Profile";
        if (on && (!SvROK(valuesv) || (SvTYPE(SvRV(valuesv)) != SVt_PVHV)) ) {
            /* not a hash ref so use DBI::Profile to work out what to do */
            dTHR;
@@ -1574,17 +1578,17 @@
            perl_require_pv("DBI/Profile.pm");
            if (SvTRUE(ERRSV)) {
                STRLEN lna;
-               warn("Can't load %s: %s", dbi_class, SvPV(ERRSV,lna));
+               warn("Can't load %s: %s", profile_class, SvPV(ERRSV,lna));
                valuesv = &sv_undef;
            }
            else {
                PUSHMARK(SP);
-               XPUSHs(sv_2mortal(newSVpv(dbi_class,0)));
+               XPUSHs(sv_2mortal(newSVpv(profile_class,0)));
                XPUSHs(valuesv);
                PUTBACK;
                returns = perl_call_method("_auto_new", G_SCALAR);
                if (returns != 1)
-                   croak("%s _auto_new", dbi_class);
+                   croak("%s _auto_new", profile_class);
                SPAGAIN;
                valuesv = POPs;
                PUTBACK;
@@ -1594,8 +1598,8 @@
        if (on && !sv_isobject(valuesv)) {
            /* not blessed already - so default to DBI::Profile */
            HV *stash;
-           perl_require_pv(dbi_class);
-           stash = gv_stashpv(dbi_class, GV_ADDWARN);
+           perl_require_pv(profile_class);
+           stash = gv_stashpv(profile_class, GV_ADDWARN);
            sv_bless(valuesv, stash);
        }
        DBIc_set(imp_xxh,DBIcf_Profile, on);
@@ -2562,6 +2566,7 @@
     I32 trace_level = (trace_flags & DBIc_TRACE_LEVEL_MASK);
     int is_DESTROY;
     int is_FETCH;
+    int is_unrelated_to_Statement = 0;
     int keep_error = FALSE;
     UV  ErrCount = UV_MAX;
     int i, outitems;
@@ -2802,6 +2807,10 @@
        }
     }
 
+    is_unrelated_to_Statement = ( (DBIc_TYPE(imp_xxh) == DBIt_ST) ? 0
+                                : (DBIc_TYPE(imp_xxh) == DBIt_DR) ? 1
+                                : (ima_flags & IMA_UNRELATED_TO_STMT) );
+
     if (tainting && items > 1                /* method call has args   */
        && DBIc_is(imp_xxh, DBIcf_TaintIn)    /* taint checks requested */
        && !(ima_flags & IMA_NO_TAINT_IN)
@@ -3200,8 +3209,8 @@
                neatsvpv(DBIc_ERR(imp_xxh),0), neatsvpv(DBIc_STATE(imp_xxh),0) 
);
 
        if (    DBIc_has(imp_xxh, DBIcf_ShowErrorStatement)
+           && !is_unrelated_to_Statement
            && (DBIc_TYPE(imp_xxh) == DBIt_ST || ima_flags & IMA_SHOW_ERR_STMT)
-           && !(ima_flags & IMA_UNRELATED_TO_STMT) /* error unrelated to 
Statement */
            && (statement_svp = hv_fetch((HV*)SvRV(h), "Statement", 9, 0))
            &&  statement_svp && SvOK(*statement_svp)
        ) {
@@ -3283,7 +3292,7 @@
        }
 
        if (profile_t1) { /* see also dbi_profile() call a few lines below */
-           SV *statement_sv = (ima_flags & IMA_UNRELATED_TO_STMT) ? &sv_no : 
&sv_undef;
+           SV *statement_sv = (is_unrelated_to_Statement) ? &sv_no : &sv_undef;
            dbi_profile(h, imp_xxh, statement_sv, imp_msv ? imp_msv : (SV*)cv,
                profile_t1, dbi_time());
        }
@@ -3299,7 +3308,7 @@
        }
     }
     else if (profile_t1) { /* see also dbi_profile() call a few lines above */
-        SV *statement_sv = (ima_flags & IMA_UNRELATED_TO_STMT) ? &sv_no : 
&sv_undef;
+        SV *statement_sv = (is_unrelated_to_Statement) ? &sv_no : &sv_undef;
        dbi_profile(h, imp_xxh, statement_sv, imp_msv ? imp_msv : (SV*)cv,
                profile_t1, dbi_time());
     }

Modified: dbi/trunk/MANIFEST
==============================================================================
--- dbi/trunk/MANIFEST  (original)
+++ dbi/trunk/MANIFEST  Sun Jul 23 14:39:31 2006
@@ -59,7 +59,7 @@
 t/40profile.t
 t/41prof_dump.t
 t/42prof_data.t
-t/43profenv.t
+t/43prof_env.t
 t/50dbm.t
 t/60preparse.t
 t/70callbacks.t

Modified: dbi/trunk/dumpmethods.pl
==============================================================================
--- dbi/trunk/dumpmethods.pl    (original)
+++ dbi/trunk/dumpmethods.pl    Sun Jul 23 14:39:31 2006
@@ -1,5 +1,9 @@
 package DBI;
 
+use warnings;
+
+use Data::Dumper;
+
 BEGIN { $ENV{DBI_PUREPERL} = 2 }
 use DBI;
 
@@ -7,6 +11,7 @@
 my @ima_n   = grep { /^IMA_.*/ } keys %{"DBI::"};
 warn "@ima_n";
 my %ima_n2v = map { /^(IMA_.*)/ ? ($1=>&$_) : () } @ima_n;
+warn Dumper \%ima_n2v;
 my %ima_v2n = reverse %ima_n2v;    
 my @ima_a   = map { $ima_v2a{1<<$_} || "b".($_+1) } 0..31;
 
@@ -15,8 +20,7 @@
 my %bit2hex; @bit2hex{ @bit2hex_bitkeys } = @bit2hex_bitvals;
 my @bit2hex_values = ("0x00000000", @bit2hex_bitvals);
 
-use Data::Dumper;
-warn Dumper \%DBI::DBI_methods;
+#warn Dumper \%DBI::DBI_methods;
 
 while ( my ($class, $meths) = each %DBI::DBI_methods ) {
 

Modified: dbi/trunk/lib/DBI/Profile.pm
==============================================================================
--- dbi/trunk/lib/DBI/Profile.pm        (original)
+++ dbi/trunk/lib/DBI/Profile.pm        Sun Jul 23 14:39:31 2006
@@ -12,16 +12,16 @@
   DBI_PROFILE=2 prog.pl
 
 This will profile your program and then output a textual summary
-grouped by query.  You can also enable profiling by setting the
-Profile attribute of any DBI handle:
+grouped by query when the program exits.  You can also enable profiling by
+setting the Profile attribute of any DBI handle:
 
   $dbh->{Profile} = 2;
 
-Other values are possible - see L<"ENABLING A PROFILE"> below.
+Then the summary will be printed when the handle is destroyed.
 
-=head1 DESCRIPTION
+Many other values apart from are possible - see L<"ENABLING A PROFILE"> below.
 
-DBI::Profile is fairly new and subject to change.
+=head1 DESCRIPTION
 
 The DBI::Profile module provides a simple interface to collect and
 report performance and benchmarking data from the DBI.
@@ -52,14 +52,13 @@
 another high-resolution timestamp and calls a function to record
 the information.  That function is passed the two timestamps
 plus the DBI handle and the name of the method that was called.
-That information about a single DBI method call is called the
-I<profile sample> data.
+That data about a single DBI method call is called a I<profile sample>.
 
 =item Data Filtering
 
-If the method call was invoked by the DBI or by a driver then the
-call is currently ignored for profiling because the time spent will
-be accounted for by the original 'outermost' call.
+If the method call was invoked by the DBI or by a driver then the call is
+ignored for profiling because the time spent will be accounted for by the
+original 'outermost' call for your code.
 
 For example, the calls that the selectrow_arrayref() method makes
 to prepare() and execute() etc. are not counted individually
@@ -68,14 +67,14 @@
 done then it would be very easy to double count time spent inside
 the DBI.
 
-In future releases it may be possible to alter this behaviour.
-
 =item Data Storage Tree
 
-The profile data is stored as 'leaves on a tree'. The 'path' through
-the branches of the tree to the particular leaf that will store the
-profile sample data for a profiled call is determined dynamically.
-This is a powerful feature.
+The profile data is accumulated as 'leaves on a tree'. The 'path' through the
+branches of the tree to a particular leaf is determined dynamically for each 
sample.
+This is a key feature of DBI profiliing.
+
+For each profiled method call the DBI walks along the Path and uses each value
+in the Path to step into and grow the Data tree.
 
 For example, if the Path is
 
@@ -85,54 +84,43 @@
 
   $h->{Profile}->{Data}->{foo}->{bar}->{baz}
 
-It wouldn't be very useful to merge all the call data into one leaf
-node (except to get an overall 'time spent inside the DBI' total).
-It's more common to want the Path to include the current statement
-text and/or the name of the method called to show what the time
-spent inside the DBI was for.
-
-The Path can contain some 'magic cookie' values that are automatically
-replaced by corresponding dynamic values when they're used.
-For example DBIprofile_Statement (exported by DBI::profile) is
-automatically replaced by value of the C<Statement> attribute of
-the handle. For example, is the Path was:
-
-  [ 'foo', DBIprofile_Statement, 'bar' ]
+But it's not very useful to merge all the call data into one leaf node (except
+to get an overall 'time spent inside the DBI' total).  It's more common to want
+the Path to include dynamic values such as the current statement text and/or
+the name of the method called to show what the time spent inside the DBI was 
for.
 
-and the value of $h->{Statement} was:
+The Path can contain some 'magic cookie' values that are automatically replaced
+by corresponding dynamic values when they're used. These magic cookies always
+start with a punctuation character.
 
-  SELECT * FROM tablename
+For example a value of 'C<!MethodName>' in the Path causes the corresponding
+entry in the Data to be the name of the method that was called.
+For example, if the Path was:
 
-then the profile data will be merged into the tree at:
+  [ 'foo', '!MethodName', 'selectall_arrayref' ]
 
-  $h->{Profile}->{Data}->{foo}->{SELECT * FROM tablename}->{bar}
+and the selectall_arrayref() method was called, then the profile sample data
+for that call will be merged into the tree at:
 
-The default Path is just C<[ DBIprofile_Statement ]> and so by
-default the profile data is aggregated per distinct Statement string.
-
-For statement handles this is always simply the string that was
-given to prepare() when the handle was created.  For database handles
-this is the statement that was last prepared or executed on that
-database handle. That can lead to a little 'fuzzyness' because, for
-example, calls to the quote() method to build a new statement will
-typically be associated with the previous statement. In practice
-this isn't a significant issue and the dynamic Path mechanism can
-be used to setup your own rules.
+  $h->{Profile}->{Data}->{foo}->{selectall_arrayref}->{bar}
 
 =item Profile Data
 
 Profile data is stored at the 'leaves' of the tree as references
 to an array of numeric values. For example:
 
-    [
-      106,                    # count
-      0.0312958955764771,     # total duration
-      0.000490069389343262,   # first duration
-      0.000176072120666504,   # shortest duration
-      0.00140702724456787,    # longest duration
-      1023115819.83019,       # time of first event
-      1023115819.86576,       # time of last event
-    ]
+  [
+    106,                  # 0: count of samples at this node
+    0.0312958955764771,   # 1: total duration
+    0.000490069389343262, # 2: first duration
+    0.000176072120666504, # 3: shortest duration
+    0.00140702724456787,  # 4: longest duration
+    1023115819.83019,     # 5: time of first sample
+    1023115819.86576,     # 6: time of last sample
+  ]
+
+After the first sample, later samples always update elements 0, 1, and 6, and
+may update 3 or 4 depending on the duration of the sampled call.
 
 =back
 
@@ -145,55 +133,61 @@
 
 The Profile attribute holds a blessed reference to a hash object
 that contains the profile data and attributes relating to it.
+
 The class the Profile object is blessed into is expected to
 provide at least a DESTROY method which will dump the profile data
 to the DBI trace file handle (STDERR by default).
 
-All these examples have the same effect as the first:
+All these examples have the same effect as each other:
 
+  $h->{Profile} = 0;
+  $h->{Profile} = "/DBI::Profile";
+  $h->{Profile} = DBI::Profile->new();
   $h->{Profile} = {};
-  $h->{Profile} = "DBI::Profile";
-  $h->{Profile} = "2/DBI::Profile";
-  $h->{Profile} = 2;
+  $h->{Profile} = { Path => [] };
+
+Similarly, these examples have the same effect as each other:
+
+  $h->{Profile} = 6;
+  $h->{Profile} = "6/DBI::Profile";
+  $h->{Profile} = "!Statement:!MethodName/DBI::Profile";
+  $h->{Profile} = { Path => [ '!Statement', '!MethodName' ] };
 
 If a non-blessed hash reference is given then the DBI::Profile
 module is automatically C<require>'d and the reference is blessed
 into that class.
 
-If a string is given then it is split on 'C</>' characters and the
-first value is used to select the Path to be used (see below).
+If a string is given then it is processed like this:
+
+    ($path, $module, $args) = split /\//, $string, 3
+
+    @path = split /:/, $path
+    @args = split /:/, $args
+
+    eval "require $module" if $module
+    $module ||= "DBI::Profile"
+
+    $module->new( Path => [EMAIL PROTECTED], @args )
+
+So the first value is used to select the Path to be used (see below).
 The second value, if present, is used as the name of a module which
 will be loaded and it's C<new> method called. If not present it
 defaults to DBI::Profile. Any other values are passed as arguments
-to the C<new> method. For example: "C<2/DBIx::OtherProfile/Foo/42>".
+to the C<new> method. For example: "C<2/DBIx::OtherProfile/Foo:42>".
+
+Numbers can be used as a shorthand way to enable common Path values.
+The simplest way to explain how the values are interpreted is to show the code:
 
-Various common sequences for Path can be selected by simply assigning
-an integer value to Profile. The simplest way to explain how the
-values are interpreted is to show the code:
-
-    push @Path, "DBI"                       if $path & 0x01;
-    push @Path, DBIprofile_Statement        if $path & 0x02;
-    push @Path, DBIprofile_MethodName       if $path & 0x04;
-    push @Path, DBIprofile_MethodClass      if $path & 0x08;
-    push @Path, DBIprofile_Caller           if $path & 0x10;
-
-(The order here is subject to change and shouldn't be relied upon.)
-
-So using the value "C<1>" causes all profile data to be merged into
-a single leaf of the tree. That's useful when you just want a total.
-
-Using "C<2>" causes profile sample data to be merged grouped by
-the corresponding Statement text. This is the most frequently used.
-
-Using "C<4>" causes profile sample data to be merged grouped by
-the method name ('FETCH', 'prepare' etc.). Using "C<8>" is similar
-but gives the fully qualified 'glob name' of the method called. For
-example: '*DBD::Driver::db::prepare', '*DBD::_::st::fetchrow_hashref'.
-
-The values can be added together to create deeper paths. The most
-useful being 6 (statement then method name) or 10 (statement then
-method name with class).  Using a negative number will reverse the
-path. Thus -6 will group by method name then statement.
+    push @Path, "DBI"           if $path_elem & 0x01;
+    push @Path, "!Statement"    if $path_elem & 0x02;
+    push @Path, "!MethodName"   if $path_elem & 0x04;
+    push @Path, "!MethodClass"  if $path_elem & 0x08;
+    push @Path, "!Caller"       if $path_elem & 0x10;
+
+So "2" is the same as "!Statement" and "6" (2+4) is the same as
+"!Statement:!Method".  Those are the two most commonly used values.  Using a
+negative number will reverse the path. Thus "-6" will group by method name then
+statement.
 
 The spliting and parsing of string values assigned to the Profile
 attribute may seem a little odd, but there's a good reason for it.
@@ -201,7 +195,8 @@
 string which can be passed in to a script as a parameter. For
 example:
 
-    dbi:DriverName(RaiseError=>1,Profile=>2):dbname
+    dbi:DriverName(Profile=>2):dbname
+    dbi:DriverName(Profile=>{Username}:!Statement/MyProfiler/Foo:42):dbname
 
 And also, if the C<DBI_PROFILE> environment variable is set then
 The DBI arranges for every driver handle to share the same profile
@@ -231,18 +226,27 @@
 
 =item Special Constant
 
-B<DBIprofile_Statement>
+B<!Statement>
 
 Use the current Statement text. Typically that's the value of the Statement
 attribute for the handle the method was called with. Some methods, like
 commit() and rollback(), are unrelated to a particular statement. For those
-methods DBIprofile_Statement records an empty string.
+methods !Statement records an empty string.
+
+For statement handles this is always simply the string that was
+given to prepare() when the handle was created.  For database handles
+this is the statement that was last prepared or executed on that
+database handle. That can lead to a little 'fuzzyness' because, for
+example, calls to the quote() method to build a new statement will
+typically be associated with the previous statement. In practice
+this isn't a significant issue and the dynamic Path mechanism can
+be used to setup your own rules.
 
-B<DBIprofile_MethodName>
+B<!MethodName>
 
 Use the name of the DBI method that the profile sample relates to.
 
-B<DBIprofile_MethodClass>
+B<!MethodClass>
 
 Use the fully qualified name of the DBI method, including
 the package, that the profile sample relates to. This shows you
@@ -261,7 +265,7 @@
 DBD::mysql::db::selectrow_arrayref. Currently the first
 call Pern't record the true location. That may change.
 
-B<DBIprofile_Caller>
+B<!Caller>
 
 Use a string showing the filename and line number of the code calling the
 method, and the filename and line number of the code that called that.
@@ -294,7 +298,7 @@
 If the value of Path is anything other than an array reference,
 it is treated as if it was:
 
-       [ DBI::Profile::DBIprofile_Statement ]
+       [ DBI::Profile::!Statement ]
 
 
 =head1 REPORTING
@@ -467,9 +471,9 @@
 
 The $h parameter is the handle the extra profile sample should be
 associated with. The $statement parameter is the string to use where
-the Path specifies DBIprofile_Statement. If $statement is undef
+the Path specifies !Statement. If $statement is undef
 then $h->{Statement} will be used. Similarly $method is the string
-to use if the Path specifies DBIprofile_MethodName. There is no
+to use if the Path specifies !MethodName. There is no
 default value for $method.
 
 The $h->{Profile}{Path} attribute is processed by dbi_profile() in
@@ -491,7 +495,7 @@
 
 Applications which generate many different statement strings
 (typically because they don't use placeholders) and profile with
-DBIprofile_Statement in the Path (the default) will consume memory
+!Statement in the Path (the default) will consume memory
 in the Profile Data structure for each statement.
 
 If a method throws an exception itself (not via RaiseError) then
@@ -577,47 +581,42 @@
 
     # This sub is called by DBI internals when a non-hash-ref is
     # assigned to the Profile attribute. For example
-    #  dbi:mysql(RaiseError=>1,Profile=>4/DBIx::MyProfile):dbname
+    #  
dbi:mysql(RaiseError=>1,Profile=>!Statement:!MethodName/DBIx::MyProfile/arg1:arg2):dbname
     # This sub works out what to do and returns a suitable hash ref.
     
-    my ($path, $module, @args);
-
-    # parse args
-    if ($arg =~ m!/!) {
-        # it's a path/module/arg/arg/arg list
-        ($path, $module, @args) = split /\s*\/\s*/, $arg, -1;
-    } elsif ($arg =~ /^\d+$/) {
-        # it's a numeric path selector
-        $path = $arg;
-    } else {
-        # it's a module name
-        $module = $arg;
-    }
+    $arg =~ s/^DBI::/2\/DBI::/
+        and carp "Automatically changed old-style DBI::Profile specification 
to $arg";
 
+    # it's a path/module/arg/arg/arg list
+    my ($path, $package, $args) = split /\//, $arg, 3;
+    my @args = (defined $args) ? split(/:/, $args, -1) : ();
     my @Path;
-    if ($path) {
-       my $reverse = ($path < 0) ? ($path=-$path, 1) : 0;
-       push @Path, "DBI"                       if $path & 0x01;
-       push @Path, DBIprofile_Statement        if $path & 0x02;
-       push @Path, DBIprofile_MethodName       if $path & 0x04;
-       push @Path, DBIprofile_MethodClass      if $path & 0x08;
-       push @Path, DBIprofile_Caller           if $path & 0x10;
-       @Path = reverse @Path if $reverse;
-    } else {
-        # default Path
-        push @Path, DBIprofile_Statement;
-    }
 
-    if ($module) {
-       if (eval "require $module") {
-         $class = $module;
-       }
-       else {
-           carp "Can't use $module for DBI profile: $@";
-       }
+    for my $element (split /:/, $path) {
+        if (DBI::looks_like_number($element)) {
+            my $reverse = ($element < 0) ? ($element=-$element, 1) : 0;
+            my @p;
+            push @p, "DBI"                     if $element & 0x01;
+            push @p, DBIprofile_Statement      if $element & 0x02;
+            push @p, DBIprofile_MethodName     if $element & 0x04;
+            push @p, DBIprofile_MethodClass    if $element & 0x08;
+            push @p, DBIprofile_Caller         if $element & 0x10;
+            push @Path, ($reverse ? reverse @p : @p);
+        }
+        elsif ($element =~ /^&(\w.*)/) {
+            # XXX need to work out what package to map names into
+            warn "$element style elements not yet supported in Path";
+            push @Path, $element;
+        }
+        else {
+            push @Path, $element;
+        }
     }
 
-    return $class->new(Path => [EMAIL PROTECTED], @args);
+    eval "require $package" if $package; # sliently ignores errors
+    $package ||= $class;
+
+    return $package->new(Path => [EMAIL PROTECTED], @args);
 }
 
 

Modified: dbi/trunk/lib/DBI/PurePerl.pm
==============================================================================
--- dbi/trunk/lib/DBI/PurePerl.pm       (original)
+++ dbi/trunk/lib/DBI/PurePerl.pm       Sun Jul 23 14:39:31 2006
@@ -119,11 +119,11 @@
 use constant IMA_KEEP_ERR_SUB  => 0x0008; #/*  '' if in nested call */
 use constant IMA_NO_TAINT_IN           => 0x0010; #/* don't check for tainted 
args*/
 use constant IMA_NO_TAINT_OUT   => 0x0020; #/* don't taint results     */
-use constant IMA_COPY_STMT     => 0x0040; #/* copy sth Statement to dbh */
+use constant IMA_COPY_UP_STMT   => 0x0040; #/* copy sth Statement to dbh */
 use constant IMA_END_WORK      => 0x0080; #/* set on commit & rollback */
 use constant IMA_STUB          => 0x0100; #/* donothing eg $dbh->connected */
 use constant IMA_CLEAR_STMT     => 0x0200; #/* clear Statement before call  */
-use constant IMA_PROF_EMPTY_STMT=> 0x0400; #/* profile as empty Statement   */
+use constant IMA_UNRELATED_TO_STMT=> 0x0400; #/* profile as empty Statement   
*/
 use constant IMA_NOT_FOUND_OKAY        => 0x0800; #/* not error if not found */
 use constant IMA_EXECUTE       => 0x1000; #/* do/execute: DBIcf_Executed   */
 use constant IMA_SHOW_ERR_STMT  => 0x2000; #/* dbh meth relates to Statement*/
@@ -237,12 +237,12 @@
 
     push @pre_call_frag, q{
        my $parent_dbh = $h->{Database};
-    } if (IMA_COPY_STMT|IMA_EXECUTE) & $bitmask;
+    } if (IMA_COPY_UP_STMT|IMA_EXECUTE) & $bitmask;
 
     push @pre_call_frag, q{
        warn "No Database set for $h on $method_name!" unless $parent_dbh; # eg 
proxy problems
        $parent_dbh->{Statement} = $h->{Statement} if $parent_dbh;
-    } if IMA_COPY_STMT & $bitmask;
+    } if IMA_COPY_UP_STMT & $bitmask;
 
     push @pre_call_frag, q{
        $h->{Executed} = 1;

Modified: dbi/trunk/t/40profile.t
==============================================================================
--- dbi/trunk/t/40profile.t     (original)
+++ dbi/trunk/t/40profile.t     Sun Jul 23 14:39:31 2006
@@ -52,9 +52,9 @@
 
 # using a package name
 $dbh = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1 });
-$dbh->{Profile} = "DBI::Profile";
+$dbh->{Profile} = "/DBI::Profile";
 is_deeply sanitize_tree($dbh->{Profile}), bless {
-       'Path' => [ DBIprofile_Statement ],
+       'Path' => [ ],
 } => 'DBI::Profile';
 
 # using a combined path and name

Modified: dbi/trunk/t/41prof_dump.t
==============================================================================
--- dbi/trunk/t/41prof_dump.t   (original)
+++ dbi/trunk/t/41prof_dump.t   Sun Jul 23 14:39:31 2006
@@ -25,7 +25,7 @@
 }
 
 my $dbh = DBI->connect("dbi:ExampleP:", '', '', 
-                       { RaiseError=>1, Profile=>"DBI::ProfileDumper" });
+                       { RaiseError=>1, Profile=>"2/DBI::ProfileDumper" });
 isa_ok( $dbh, 'DBI::db' );
 isa_ok( $dbh->{Profile}, "DBI::ProfileDumper" );
 isa_ok( $dbh->{Profile}{Data}, 'HASH' );

Added: dbi/trunk/t/43prof_env.t
==============================================================================
--- (empty file)
+++ dbi/trunk/t/43prof_env.t    Sun Jul 23 14:39:31 2006
@@ -0,0 +1,52 @@
+#!perl -w
+
+use strict;
+
+#
+# test script for using DBI_PROFILE env var to enable DBI::Profile
+# and testing non-ref assignments to $h->{Profile}
+#
+
+BEGIN { $ENV{DBI_PROFILE} = 6 }        # prior to use DBI
+
+use DBI;
+use DBI::Profile;
+use File::Spec;
+use Config;
+use Data::Dumper;
+
+BEGIN {
+    if ($DBI::PurePerl) {
+       print "1..0 # Skipped: profiling not supported for DBI::PurePerl\n";
+       exit 0;
+    }
+}
+
+use Test::More tests => 11;
+
+DBI->trace(0, "STDOUT");
+
+my $dbh1 = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1 });
+is(ref $dbh1->{Profile}, "DBI::Profile");
+is(ref $dbh1->{Profile}{Data}, 'HASH');
+is(ref $dbh1->{Profile}{Path}, 'ARRAY');
+
+my $dbh2 = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1 });
+is(ref $dbh2->{Profile}, "DBI::Profile");
+is(ref $dbh2->{Profile}{Data}, 'HASH');
+is(ref $dbh2->{Profile}{Path}, 'ARRAY');
+
+is $dbh1->{Profile}, $dbh2->{Profile}, '$h->{Profile} should be shared';
+
+$dbh1->do("set dummy=1");
+$dbh1->do("set dummy=2");
+
+my $profile = $dbh1->{Profile};
+
+my $p_data = $profile->{Data};
+is keys %$p_data, 3; # '', $sql1, $sql2
+ok $p_data->{''};
+ok $p_data->{"set dummy=1"};
+ok $p_data->{"set dummy=2"};
+
+__END__

Modified: dbi/trunk/t/72childhandles.t
==============================================================================
--- dbi/trunk/t/72childhandles.t        (original)
+++ dbi/trunk/t/72childhandles.t        Sun Jul 23 14:39:31 2006
@@ -22,7 +22,7 @@
     exit 0;
 }
 
-plan tests => 15;
+plan tests => 14;
 
 {
     # make 10 connections
@@ -62,10 +62,8 @@
 
 my $dbh = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1 });
 
-
 my $empty = $dbh->{ChildHandles};
-is ref $empty, 'ARRAY', "ChildHandles should be an array-ref if wekref is 
available";
-is scalar @$empty, 0, "ChildHandles should start with an empty array-ref";
+is_deeply $empty, [], "ChildHandles should be an array-ref if wekref is 
available";
 
 # test child handles for statement handles
 {

Modified: dbi/trunk/test.pl
==============================================================================
--- dbi/trunk/test.pl   (original)
+++ dbi/trunk/test.pl   Sun Jul 23 14:39:31 2006
@@ -37,7 +37,7 @@
 $::opt_t = 0;          # thread test
 $::opt_n = 0;          # counter for other options
 
-GetOptions(qw(d=i h=i l=s m t=i n=i))
+GetOptions(qw(d=i h=i l=s m=i t=i n=i))
     or die "Usage: $0 [-d n] [-h n] [-m] [-t n] [-n n] [drivername]\n";
 
 my $count = 0;
@@ -73,23 +73,22 @@
 
 if ($::opt_m) {
     #$dbh->trace(9);
-    my $level = 4;
+    my $level = $::opt_m;
     my $cnt = 10000;
     print "Using $driver, same dbh...\n";
-    #for (my $i=0; $i<$cnt; ++$i) { mem_test($dbh, undef, $level, undef, 
undef, undef) }
+    for (my $i=0; $i<$cnt; ++$i) { mem_test($dbh, undef, $level, undef, undef, 
undef) }
     print "Using NullP, reconnecting each time...\n";
-    #for (my $i=0; $i<$cnt; ++$i) { mem_test(undef, ['dbi:NullP:'], $level, 
undef, undef, undef) }
+    for (my $i=0; $i<$cnt; ++$i) { mem_test(undef, ['dbi:NullP:'], $level, 
undef, undef, undef) }
     print "Using ExampleP, reconnecting each time...\n";
     my $r_develleak = 0;
-    mem_test(undef, ['dbi:ExampleP:'], $level, undef, undef, \$r_develleak)
-        while 1;
+    mem_test(undef, ['dbi:NullP:'], $level, undef, undef, \$r_develleak) while 
1;
     #mem_test(undef, ['dbi:mysql:VC'], $level, "select * from campaigns where 
length(?)>0", 0, undef) while 1;
 }
 elsif ($::opt_t) {
        thread_test();
 }
 else {
-
+    
     # new experimental connect_test_perf method
     DBI->connect_test_perf("dbi:$driver:", '', '', {
        dbi_loops=>5, dbi_par=>20, dbi_verb=>1
@@ -142,9 +141,14 @@
     system("echo $count; $ps$$") if (($count++ % 500) == 0);
 
     my $dbh = $orig_dbh || DBI->connect(@$connect);
+    $dbh->{RaiseError} = 1;
     my $cursor_a;
 
-    my $dl_count = ($$r_develleak++) ? Devel::Leak::NoteSV(my $dl_handle) : 0;
+    my ($dl_count, $dl_handle);
+    if ($$r_develleak++) {
+        $dbh->trace(2);
+        $dl_count = Devel::Leak::NoteSV($dl_handle);
+    }
 
     $cursor_a = $dbh->prepare($select)         if $level >= 2;
     $cursor_a->execute(@$params)               if $level >= 3;
@@ -153,7 +157,10 @@
     $cursor_a->finish if $cursor_a && $cursor_a->{Active};
     undef $cursor_a;
 
-    die Devel::Leak::CheckSV($dl_handle) if $dl_handle;
+    @{$dbh->{ChildHandles}} = ();
+
+    die Devel::Leak::CheckSV($dl_handle)-$dl_count
+        if $dl_handle;
 
     $dbh->disconnect unless $orig_dbh;
     undef $dbh;

Reply via email to