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}) : ();