"The Perl DBI Database Driver Writer's Guide" introduces attribute handling in the following way:
Note the use of the STORE method for setting the dbh attributes. That's because within the driver code, the handle object you have is the 'inner' handle of a tied hash, not the outer handle that the users of your driver have.
Because you have the inner handle, tie magic doesn't get invoked when you get or set values in the hash. This is often very handy for speed when you want to get or set simple non-special driver-specific attributes.
All well, nothing to criticise. However, it's placed below the connect() method:
<http://search.cpan.org/~timb/DBI-1.46/lib/DBI/DBD.pm#The_database_handle_constructor>
where we have an outer handle by way of an exception! Instead of changing the nice description, I'd rather change the example code (see attached patch). Advantages that accrued:
- Now, we have the inner handle the text speaks about. - The example is shorter. - The example shows both, the STORE method and direct hash access. - The example shows a realistic DBI attribute ('Active') instead of whatever gets extracted from DSN (and may cause troubles in STORE). - The example shows a realistic driver specific attribute. - The examples are more consistent ($dbh is always the inner handle).
Steffen
Index: lib/DBI/DBD.pm =================================================================== --- lib/DBI/DBD.pm (revision 629) +++ lib/DBI/DBD.pm (working copy) @@ -696,21 +696,18 @@ # environment variables to be set; this could be where you # validate that they are set, or default them if they are not set.
+ # Assume you can attach to your database via drv_connect: + my $connection = drv_connect($dbname, $user, $auth); + return $drh->set_err(1,'Connection refused') unless $connection; + # create a 'blank' dbh (call superclass constructor) - my $dbh = DBI::_new_dbh($drh, { - 'Name' => $dbname, - }) - or return undef; + my ($outer, $dbh) = DBI::_new_dbh($drh, { Name => $dbname }); - # Process attributes from the DSN; we assume ODBC syntax - # here, that is, the DSN looks like var1=val1;...;varN=valN - foreach my $var (split(/;/, $dbname)) { - if ($var =~ m/(.*?)=(,*)/) { - # Not !!! $dbh->{$var} = $val; - $dbh->STORE($var, $val); - } - } - $dbh; + $dbh->STORE('Active', 1 ); + + $dbh->{drv_connection} = $connection; + + return $outer; } The Name attribute is a standard DBI attribute. @@ -721,11 +718,13 @@ The constructor _new_dbh is called, returning a database handle. The constructor's prototype is: - $dbh = DBI::_new_dbh($drh, $public_attr, $private_attr); + ($outer, $inner) = DBI::_new_dbh($drh, $public_attr, $private_attr); with similar arguments to those in the I<driver handle constructor>, except that the C<$class> is replaced by C<$drh>. +In scalar context, only the outer handle is returned. + Note the use of the I<STORE> method for setting the dbh attributes. That's because within the driver code, the handle object you have is the 'inner' handle of a tied hash, not the outer handle that the @@ -807,19 +806,18 @@ my ($dbh, $statement, @attribs) = @_; # create a 'blank' sth - my $sth = DBI::_new_sth($dbh, { - 'Statement' => $statement, - }); + my ($outer, $sth) = DBI::_new_sth($dbh, { Statement => $statement }); - # Setup module specific data - $sth->STORE('drv_params', []); $sth->STORE('NUM_OF_PARAMS', ($statement =~ tr/?//)); - $sth; + $sth->{drv_params} = []; + + return $outer; } This is still the same: check the arguments and call the super class constructor I<DBI::_new_sth>. +Again, in scalar context, only the outer handle is returned. The C<Statement> attribute should be cached as shown. Note the prefix I<drv_> in the attribute names: it is required that your private attributes are lowercased and use such a prefix.