On Wed, May 01, 2002 at 11:57:20AM +0800, Stas Bekman wrote:
> [ CC'ing Edmund on this thread in case he is not on the dbi-users list]
> 
> Tim Bunce wrote:
> > On Tue, Apr 30, 2002 at 03:34:26PM -0400, Geoffrey Young wrote:
> > 
> >>hi Tim and all - it's been a long time :)
> >>
> >>Stas and I just ran across something interesting concerning Apache::DBI and $dbh 
> >>attributes.  I think it involves a both a bug and a place for enhancements.
> >>
> >>we all know how Apache::DBI works - it stashes away $dbh based on connect string 
>info. 
> >>however, connect string info is limiting, so nice thing to do is strip down your 
>connect() 
> >>call to the bare minimum and add attributes to $dbh after the fact:
> >>
> >>my $dbh = DBI->connect($dbase, $user, $pass);
> >>$dbh->{AutoCommit} = 0;
> > 
> > 
> > Don't do that.
> > 
> > It (connect_cached) stashes away $dbh based on connect string info
> > *and* all the attributes. You should set all the atributes you
> > require in the connect() and then not change them later.
> > 
> > That way connections that require different attributes are sure to
> > get different handles.
> 
> That's actually a mod_perl advisory. Since if you have two scripts 
> wanting to reuse the same connection via Apache::DBI they *must* use the 
> exactly the same connect string. Now if one script wants LongReadLen = 
> 40 and the other LongReadLen = 80, they have to modify that after 
> connect(). If they supply different connect strings, the service will 
> need roughly a twice bigger number of connection, and so on.
> 
> Is it possible to deep copy $dbh, when handing it off 
> Apache::DBI::connect()? Then any changes will be local and won't affect 
> the cached copy.

No. Many attributes don't "exist" until/unless/only when you ask for them.
And some may be directly tied to server-side state information.

> >>to take this a step further, what would be really DWIMy is for DBI to clear all of 
>the 
> >>$dbh attributes not specified in the connect() call (including stuff like 
>LongReadLen) if 
> >>dbi_connect_method = 'Apache'.  that seems like the right thing, since with 
>Apache::DBI 
> >>wat we want a "new" $dbh, just without the connect overhead.  and DBI seems like a 
> >>reasonable place to do it, especially since DBI->connect is kinda 
>action-at-a-distance 
> >>anyway wrt Apache::DBI.
> >>
> >>maybe I'm missing something design-wise, though?
> > 
> > I think the Apache::DBI subclass can override the STORE method and
> > track attribute changes itself.
> 
> That sounds like a good solution. Edmund?
> 
> I raised this issue, since in the mod_perl book Eric and I are working 
> on, we say that the connect string shouldn't change in order to be able 
> to reuse connections.

s/connect string/connect string and username and password and attributes/ :)

> I guess we must warn users that any changes to 
> $dbh will persist, which renders the advisory useless and doesn't 
> provide any solution :(

The advisory is still valid - it just needs to be extended so say
that if they modify an attribute after the connect then they should
restore it... or wait for a version of Apache::DBI that'll do that for them :)

It ought to be as simple as:

sub STORE {
   my ($dbh, $key, $value) = @_;
   my $attrib_orig_values = $dbh->{private_apache_dbi_orig_attr};
   $attrib_orig_values->{$key} = $self->FETCH($key)
       unless exists $attrib_orig_values->{$key};
   return $self->SUPER::STORE($key, $value);
}

where $dbh->{private_apache_dbi_orig_attr} is set to {} after the
$drh->connect call.

Then your cleanup() handler can be extended to do:

    # restore any attributes changed after the connect
    my $attrib_orig_values = $dbh->{private_apache_dbi_orig_attr}
    $dbh->{$_} = $attrib_orig_values->{$_} for keys %$attrib_orig_values;
    %$attrib_orig_values = ();

Umm, DBI->connect will set RaiseError, PrintError and AutoCommit (plus
any extra attributes specified) after $drh->connect returns. So those
will trigger the STORE code. Very recent DBI's call
        $dbh->connected($dsn, $user, $pass, \%attr)
after setting the attributes and just before DBI->connect returns.
You could define your own connected sub and do
        $dbh->{private_apache_dbi_orig_attr} = {};
in there.

Have fun!

Tim.

p.s. This code:

       while (($key,$val) = each %{$args[3]}) {
           $Idx .= "$;$key=$val";
       }

may lead to extra connections because the order of entries in the hash may vary.
The DBI's connect_cached now uses:

  my @attr_keys = $attr ? sort keys %$attr : ();
  my $key = join "~~", $dsn, $user||'', $auth||'', $attr ? 
(@attr_keys,@{$attr}{@attr_keys}) : ();


Reply via email to