Umm, here's another thought...

How about changing prepare_cached so it includes the caller() info
in the hash key by default.  That way prepare_cached won't return
the same statement handle if called from two different places even
if the $statement is the same.

Tim.

On Wed, Feb 27, 2002 at 10:38:16PM -0600, Stephen Clouse wrote:
> Well, we finally discovered how prepare_cached can "cause problems", as the docs 
> so eloquently state.  I'm amazed we evaded it this long.  We use prepare_cached 
> literally everywhere for performance reasons, so it was only a matter of time 
> before somewhere, 10 or 15 levels deep in method calls, someone tried to reuse a 
> statement while it was active somewhere farther up the call stack.
> 
> I had solved this problem in our DBI subclass, but this seems a useful (and 
> benign) enough change that I went ahead and integrated it into our local DBI 
> sources.  Tiny code and large doc patch attached.  Do with it what you will.
> 
> -- 
> Stephen Clouse <[EMAIL PROTECTED]>
> Senior Programmer, IQ Coordinator Project Lead
> The IQ Group, Inc. <http://www.theiqgroup.com/>

Content-Description: dbi-prepare_cached_hack.patch
> Index: DBI.pm
> ===================================================================
> RCS file: /home/cvs/patched-sources/DBI/DBI.pm,v
> retrieving revision 1.1.1.1
> diff -u -r1.1.1.1 DBI.pm
> --- DBI.pm    28 Feb 2002 01:05:12 -0000      1.1.1.1
> +++ DBI.pm    28 Feb 2002 04:31:46 -0000
> @@ -1239,7 +1239,8 @@
>       my $key = ($attr) ? join("~~", $statement, @attr_keys, @{$attr}{@attr_keys}) : 
>$statement;
>       my $sth = $cache->{$key};
>       if ($sth) {
> -         if ($sth->FETCH('Active')) {
> +         $allow_active = 0 if !defined $allow_active;
> +         if ($sth->FETCH('Active') && $allow_active != 2) {
>               Carp::carp("prepare_cached($statement) statement handle $sth was still 
>active")
>                   if !$allow_active;
>               $sth->finish;
> @@ -2879,14 +2880,6 @@
>  corresponding cached C<$sth> will be returned without contacting the
>  database server.
>  
> -This caching can be useful in some applications, but it can also cause
> -problems and should be used with care. If the cached C<$sth> being
> -returned is active (i.e., is a C<SELECT> that may still have data to be
> -fetched) then a warning will be generated and C<finish> will be called
> -for you.  The warning can be suppressed by setting C<$allow_active> to
> -true.  The cache can be accessed (and cleared) via the L</CachedKids>
> -attribute.
> -
>  Here are some examples of C<prepare_cached>:
>  
>    sub insert_hash {
> @@ -2908,6 +2901,44 @@
>      $sth = $dbh->prepare_cached("SELECT * FROM $table $qualifier");
>      return $dbh->selectall_arrayref($sth, {}, @values);
>    }
> +
> +Caveat emptor: This caching can be useful in some applications, but it can also
> +cause problems and should be used with care. Here is a contrived case where
> +caching would cause a significant problem:
> +
> +  my $sth = $dbh->prepare_cached('SELECT * FROM foo');
> +  $sth->execute(@values);
> +  while (my $data = $sth->fetch) {
> +    my $sth2 = $dbh->prepare_cached('SELECT * FROM foo');
> +    $sth2->execute(@more_values);
> +    while (my $data2 = $sth2->fetch) {
> +      do_stuff($data,$data2);
> +    }
> +  }
> +
> +In this example, since both handles are preparing the exact same statement,
> +C<$sth2> will not be its own statement handle, but a duplicate of C<$sth>
> +returned from the cache. The results will certainly not be what you expect,
> +and, depending on the pickiness of the SQL engine, may throw an error when it
> +returns to the outer loop expecting to fetch more data when the inner loop has
> +already exhausted it. The C<$allow_active> parameter lets you adjust DBI's
> +behavior when prepare_cached is returning a statement handle it sees as active.
> +There are three settings:
> +
> +=over 4
> +
> +0: A warning will be generated, and C<finish> will be called on the statement
> +handle before it is returned.  This is the default behavior if C<$allow_active>
> +is not passed.
> +
> +1: C<finish> will be called on the statement handle, but the warning is
> +suppressed.
> +
> +2: DBI will not touch the statement handle before returning it.  You will need
> +to check C<$sth->E<gt>C<{Active}> on the returned statement handle and deal
> +with it in your own code.
> +
> +=back
>  
>  
>  =item C<commit>



Reply via email to