diff -u DBI-1.14/Changes DBI-1.30/Changes
--- DBI-1.14/Changes	Wed Jun 14 21:04:11 2000
+++ DBI-1.30/Changes	Thu Jul 18 15:24:02 2002
@@ -1,4 +1,413 @@
-Changes in DBI 1.14,	14th June 1999
+=head1 NAME
+
+DBI::Changes - List of significant changes to the DBI
+
+=cut
+
+Need to add docs for DbTypeSubclass (ala DBIx::AnyDBD)
+Move FIRSTKEY/NEXTKEY/EXISTS/DELETE? to XS (and pureperl)
+
+  Changes for driver authors, not required but strongly recommended:
+  Change DBIS to DBIc_DBISTATE(imp_xxh)   [or imp_dbh, imp_sth etc]
+  Change DBILOGFP to DBIc_LOGPIO(imp_xxh) [or imp_dbh, imp_sth etc]
+  Any function from which all instances of DBIS and DBILOGFP are
+  removed can also probably have dPERLINTERP removed (a good thing).
+
+$dbh->{Statement} can be wrong because fetch doesn't update value
+maybe imp_dbh holds imp_sth (or inner handle) of last sth method
+called (if not DESTROY) and sth outer DESTROY clears it (to reduce ref count)
+Then $dbh->{LastSth} would work (returning outer handle if valid).
+Then $dbh->{Statement} would be the same as $dbh->{LastSth}->{Statement}
+Also $dbh->{ParamValues} would be the same as $dbh->{LastSth}->{ParamValues}.
+
+=head1 CHANGES
+
+=head2 Changes in DBI 1.30,    18th July 2002
+
+  Fixed problems with selectrow_array, selectrow_arrayref, and
+    selectall_arrayref introduced in DBI 1.29.
+  Fixed FETCHing a handle attribute to not clear $DBI::err etc (broken in 1.29).
+  Fixed core dump at trace level 9 or above.
+  Fixed compilation with perl 5.6.1 + ithreads (i.e. Windows).
+  Changed definition of behaviour of selectrow_array when called in a scalar
+    context to match fetchrow_array.
+  Corrected selectrow_arrayref docs which showed selectrow_array thanks to Paul DuBois.
+
+=head2 Changes in DBI 1.29,    15th July 2002
+
+  NOTE: This release changes the specified behaviour for the
+  : fetchrow_array method when called in a scalar context:
+  : The DBI spec used to say that it would return the FIRST field.
+  : Which field it returns (i.e., the first or the last) is now undefined.
+  : This does not affect statements that only select one column, which is
+  : usually the case when fetchrow_array is called in a scalar context.
+  : FYI, this change was triggered by discovering that the fetchrow_array
+  : implementation in Driver.xst (used by most compiled drivers) 
+  : didn't match the DBI specification. Rather than change the code
+  : to match, and risk breaking existing applications, I've changed the
+  : specification (that part was always of dubious value anyway).
+
+  NOTE: Future versions of the DBI may not support for perl 5.5 much longer.
+  : If you are still using perl 5.005_03 you should be making plans to
+  : upgrade to at least perl 5.6.1, or 5.8.0. Perl 5.8.0 is due to be
+  : released in the next week or so.  (Although it's a "point 0" release,
+  : it is the most throughly tested release ever.)
+
+  Added XS/C implementations of selectrow_array, selectrow_arrayref, and
+    selectall_arrayref to Driver.xst. See DBI 1.26 Changes for more info.
+  Removed support for the old (fatally flawed) "5005" threading model.
+  Added support for new perl 5.8 iThreads thanks to Gerald Richter.
+    (Threading support and safety should still be regarded as beta
+    quality until further notice. But it's much better than it was.)
+  Updated the "Threads and Thread Safety" section of the docs.
+  The trace output can be sent to STDOUT instead of STDERR by using
+    "STDOUT" as the name of the file, i.e., $h->trace(..., "STDOUT")
+  Added pointer to perlreftut, perldsc, perllol, and perlboot manuals
+    into the intro section of the docs, suggested by Brian McCain.
+  Fixed DBI::Const::GetInfo::* pod docs thanks to Zack Weinberg.
+  Some changes to how $dbh method calls are treated by DBI::Profile:
+    Meta-data methods now clear $dbh->{Statement} on entry.
+    Some $dbh methods are now profiled as if $dbh->{Statement} was empty
+    (because thet're unlikely to actually relate to its contents).
+  Updated dbiport.h to ppport.h from perl 5.8.0.
+  Tested with perl 5.5.3 (vanilla, Solaris), 5.6.1 (vanilla, Solaris), and
+    perl 5.8.0 (RC3@17527 with iThreads & Multiplicity on Solaris and FreeBSD).
+
+=head2 Changes in DBI 1.28,    14th June 2002
+
+  Added $sth->{ParamValues} to return a hash of the most recent
+    values bound to placeholders via bind_param() or execute().
+    Individual drivers need to be updated to support it.
+  Enhanced ShowErrorStatement to include ParamValues if available:
+    "DBD::foo::st execute failed: errstr [for statement ``...'' with params: 1='foo']"
+  Further enhancements to DBD::PurePerl accuracy.
+
+=head2 Changes in DBI 1.27,    13th June 2002
+
+  Fixed missing column in C implementation of fetchall_arrayref()
+    thanks to Philip Molter for the prompt reporting of the problem.
+
+=head2 Changes in DBI 1.26,    13th June 2002
+
+  Fixed t/40profile.t to work on Windows thanks to Smejkal Petr.
+  Fixed $h->{Profile} to return undef, not error, if not set.
+  Fixed DBI->available_drivers in scalar context thanks to Michael Schwern.
+
+  Added C implementations of selectrow_arrayref() and fetchall_arrayref()
+    in Driver.xst.  All compiled drivers using Driver.xst will now be
+    faster making those calls. Most noticable with fetchall_arrayref for
+    many rows or selectrow_arrayref with a fast query. For example, using
+    DBD::mysql a selectrow_arrayref for a single row using a primary key
+    is ~20% faster, and fetchall_arrayref for 20000 rows is twice as fast!
+    Drivers just need to be recompiled and reinstalled to enable it.
+    The fetchall_arrayref speed up only applies if $slice parameter is not used.
+  Added $max_rows parameter to fetchall_arrayref() to optionally limit
+    the number of rows returned. Can now fetch batches of rows.
+  Added MaxRows attribute to selectall_arrayref()
+    which then passes it to fetchall_arrayref().
+  Changed selectrow_array to make use of selectrow_arrayref.
+  Trace level 1 now shows first two parameters of all methods
+    (used to only for that for some, like prepare,execute,do etc)
+  Trace indicator for recursive calls (first char on trace lines)
+    now starts at 1 not 2.
+
+  Documented that $h->func() does not trigger RaiseError etc
+    so applications must explicitly check for errors.
+  DBI::Profile with DBI_PROFILE now shows percentage time inside DBI.
+  HandleError docs updated to show that handler can edit error message.
+  HandleError subroutine interface is now regarded as stable.
+
+=head2 Changes in DBI 1.25,    5th June 2002
+
+  Fixed build problem on Windows and some compiler warnings.
+  Fixed $dbh->{Driver} and $sth->{Statement} for driver internals
+    These are 'inner' handles as per behaviour prior to DBI 1.16.
+  Further minor improvements to DBI::PurePerl accuracy.
+
+=head2 Changes in DBI 1.24,    4th June 2002
+
+  Fixed reference loop causing a handle/memory leak
+    that was introduced in DBI 1.16.
+  Fixed DBI::Format to work with 'filehandles' from IO::Scalar
+    and similar modules thanks to report by Jeff Boes.
+  Fixed $h->func for DBI::PurePerl thanks to Jeff Zucker.
+  Fixed $dbh->{Name} for DBI::PurePerl thanks to Dean Arnold.
+
+  Added DBI method call profiling and benchmarking.
+    This is a major new addition to the DBI.
+    See $h->{Profile} attribute and DBI::Profile module.
+    For a quick trial, set the DBI_PROFILE environment variable and
+    run your favourite DBI script. Try it with DBI_PROFILE set to 1,
+    then try 2, 4, 8, 10, and -10. Have fun!
+
+  Added execute_array() and bind_param_array() documentation
+    with thanks to Dean Arnold.
+  Added notes about the DBI having not yet been tested with iThreads
+    (testing and patches for SvLOCK etc welcome).
+  Removed undocumented Handlers attribute (replaced by HandleError).
+  Tested with 5.5.3 and 5.8.0 RC1.
+
+=head2 Changes in DBI 1.23,    25th May 2002
+
+  Greatly improved DBI::PurePerl in performance and accuracy.
+  Added more detail to DBI::PurePerl docs about what's not supported.
+  Fixed undef warnings from t/15array.t and DBD::Sponge.
+
+=head2 Changes in DBI 1.22,    22nd May 2002
+
+  Added execute_array() and bind_param_array() with special thanks
+    to Dean Arnold. Not yet documented. See t/15array.t for examples.
+    All drivers now automatically support these methods.
+  Added DBI::PurePerl, a transparent DBI emulation for pure-perl drivers
+    with special thanks to Jeff Zucker. Perldoc DBI::PurePerl for details.
+  Added DBI::Const::GetInfo* modules thanks to Steffen Goeldner.
+  Added write_getinfo_pm utility to DBI::DBD thanks to Steffen Goeldner.
+  Added $allow_active==2 mode for prepare_cached() thanks to Stephen Clouse.
+
+  Updated DBI::Format to Revision 11.4 thanks to Tom Lowery.
+  Use File::Spec in Makefile.PL (helps VMS etc) thanks to Craig Berry.
+  Extend $h->{Warn} to commit/rollback ineffective warning thanks to Jeff Baker.
+  Extended t/preparse.t and removed "use Devel::Peek" thanks to Scott Hildreth.
+  Only copy Changes to blib/lib/Changes.pm once thanks to Jonathan Leffler.
+  Updated internals for modern perls thanks to Jonathan Leffler and Jeff Urlwin.
+  Tested with perl 5.7.3 (just using default perl config).
+
+  Documentation changes:
+
+  Added 'Catalog Methods' section to docs thanks to Steffen Goeldner.
+  Updated README thanks to Michael Schwern.
+  Clarified that driver may choose not to start new transaction until
+    next use of $dbh after commit/rollback.
+  Clarified docs for finish method.
+  Clarified potentials problems with prepare_cached() thanks to Stephen Clouse.
+
+
+=head2 Changes in DBI 1.21,    7th February 2002
+
+  The minimum supported perl version is now 5.005_03.
+
+  Fixed DBD::Proxy support for AutoCommit thanks to Jochen Wiedmann.
+  Fixed DBI::ProxyServer bind_param(_inout) handing thanks to Oleg Mechtcheriakov.
+  Fixed DBI::ProxyServer fetch loop thanks to nobull@mail.com.
+  Fixed install_driver do-the-right-thing with $@ on error. It, and connect(),
+    will leave $@ empty on success and holding the error message on error.
+    Thanks to Jay Lawrence, Gavin Sherlock and others for the bug report.
+  Fixed fetchrow_hashref to assign columns to the hash left-to-right
+    so later fields with the same name overwrite earlier ones
+    as per DBI < 1.15, thanks to Kay Roepke.
+
+  Changed tables() to use quote_indentifier() if the driver returns a
+    true value for $dbh->get_info(29) # SQL_IDENTIFIER_QUOTE_CHAR
+  Changed ping() so it no longer triggers RaiseError/PrintError.
+  Changed connect() to not call $class->install_driver unless needed.
+  Changed DESTROY to catch fatal exceptions and append to $@.
+
+  Added ISO SQL/CLI & ODBCv3 data type definitions thanks to Steffen Goeldner.
+  Removed the definition of SQL_BIGINT data type constant as the value is
+    inconsistent between standards (ODBC=-5, SQL/CLI=25).
+  Added $dbh->column_info(...) thanks to Steffen Goeldner.
+  Added $dbh->foreign_key_info(...) thanks to Steffen Goeldner.
+  Added $dbh->quote_identifier(...) insipred by Simon Oliver.
+  Added $dbh->set_err(...) for DBD authors and DBI subclasses
+    (actually been there for a while, now expanded and documented).
+  Added $h->{HandleError} = sub { ... } addition and/or alternative
+    to RaiseError/PrintError. See the docs for more info.
+  Added $h->{TraceLevel} = N attribute to set/get trace level of handle
+    thus can set trace level via an (eg externally specified) DSN
+    using the embedded attribute syntax:
+      $dsn = 'dbi:DB2(PrintError=1,TraceLevel=2):dbname';
+    Plus, you can also now do: local($h->{TraceLevel}) = N;
+    (but that leaks a little memory in some versions of perl).
+  Added some call tree information to trace output if trace level >= 3
+    With thanks to Graham Barr for the stack walking code.
+  Added experimental undocumented $dbh->preparse(), see t/preparse.t
+    With thanks to Scott T. Hildreth for much of the work.
+  Added Fowler/Noll/Vo hash type as an option to DBI::hash().
+
+  Documentation changes:
+
+  Added DBI::Changes so now you can "perldoc DBI::Changes", yeah!
+  Added selectrow_arrayref & selectrow_hashref docs thanks to Doug Wilson.
+  Added 'Standards Reference Information' section to docs to gather
+    together all references to relevant on-line standards.
+  Added link to poop.sourceforge.net into the docs thanks to Dave Rolsky.
+  Added link to hyperlinked BNF for SQL92 thanks to Jeff Zucker.
+  Added 'Subclassing the DBI' docs thanks to Stephen Clouse, and
+    then changed some of them to reflect the new approach to subclassing.
+  Added stronger wording to description of $h->{private_*} attributes.
+  Added docs for DBI::hash.
+
+  Driver API changes:
+
+  Now a COPY of the DBI->connect() attributes is passed to the driver
+    connect() method, so it can process and delete any elements it wants.
+    Deleting elements reduces/avoids the explicit
+      $dbh->{$_} = $attr->{$_} foreach keys %$attr;
+    that DBI->connect does after the driver connect() method returns.
+
+
+=head2 Changes in DBI 1.20,    24th August 2001
+
+  WARNING: This release contains two changes that may affect your code.
+  : Any code using selectall_hashref(), which was added in March 2001, WILL
+  : need to be changed. Any code using fetchall_arrayref() with a non-empty
+  : hash slice parameter may, in a few rare cases, need to be changed.
+  : See the change list below for more information about the changes.
+  : See the DBI documentation for a description of current behaviour.
+
+  Fixed memory leak thanks to Toni Andjelkovic.
+  Changed fetchall_arrayref({ foo=>1, ...}) specification again (sorry):
+    The key names of the returned hashes is identical to the letter case of
+    the names in the parameter hash, regardless of the L</FetchHashKeyName>
+    attribute. The letter case is ignored for matching.
+  Changed fetchall_arrayref([...]) array slice syntax specification to
+    clarify that the numbers in the array slice are perl index numbers
+    (which start at 0) and not column numbers (which start at 1).
+  Added { Columns=>... } and { Slice =>... } attributes to selectall_arrayref()
+    which is passed to fetchall_arrayref() so it can fetch hashes now.
+  Added a { Columns => [...] } attribute to selectcol_arrayref() so that
+    the list it returns can be built from more than one column per row.
+    Why? Consider my %hash = @{$dbh->selectcol_arrayref($sql,{ Columns=>[1,2]})}
+    to return id-value pairs which can be used directly to build a hash.
+  Added $hash_ref = $sth->fetchall_hashref( $key_field )
+    which returns a ref to a hash with, typically, one element per row.
+    $key_field is the name of the field to get the key for each row from.
+    The value of the hash for each row is a hash returned by fetchrow_hashref.
+  Changed selectall_hashref to return a hash ref (from fetchall_hashref)
+    and not an array of hashes as it has since DBI 1.15 (end March 2001).
+    WARNING: THIS CHANGE WILL BREAK ANY CODE USING selectall_hashref()!
+    Sorry, but I think this is an important regularization of the API.
+    To get previous selectall_hashref() behaviour (an array of hash refs)
+    change $ary_ref = $dbh->selectall_hashref( $statement, undef, @bind);
+	to $ary_ref = $dbh->selectall_arrayref($statement, { Columns=>{} }, @bind);
+  Added NAME_lc_hash, NAME_uc_hash, NAME_hash statement handle attributes.
+    which return a ref to a hash of field_name => field_index (0..n-1) pairs.
+  Fixed select_hash() example thanks to Doug Wilson.
+  Removed (unbundled) DBD::ADO and DBD::Multiplex from the DBI distribution.
+    The latest versions of those modules are available from CPAN sites.
+  Added $dbh->begin_work. This method causes AutoCommit to be turned
+    off just until the next commit() or rollback().
+    Driver authors: if the DBIcf_BegunWork flag is set when your commit or
+    rollback method is called then please turn AutoCommit on and clear the
+    DBIcf_BegunWork flag. If you don't then the DBI will but it'll be much
+    less efficient and won't handle error conditions very cleanly.
+  Retested on perl 5.4.4, but the DBI won't support 5.4.x much longer.
+  Added text to SUPPORT section of the docs:
+    For direct DBI and DBD::Oracle support, enhancement, and related work
+    I am available for consultancy on standard commercial terms.
+  Added text to ACKNOWLEDGEMENTS section of the docs:
+    Much of the DBI and DBD::Oracle was developed while I was Technical
+    Director (CTO) of the Paul Ingram Group (www.ig.co.uk).  So I'd
+    especially like to thank Paul for his generosity and vision in
+    supporting this work for many years.
+
+=head2 Changes in DBI 1.19,    20th July 2001
+
+  Made fetchall_arrayref({ foo=>1, ...}) be more strict to the specification
+    in relation to wanting hash slice keys to be lowercase names.
+    WARNING: If you've used fetchall_arrayref({...}) with a hash slice
+    that contains keys with uppercase letters then your code will break.
+    (As far as I recall the spec has always said don't do that.)
+  Fixed $sth->execute() to update $dbh->{Statement} to $sth->{Statement}.
+  Added row number to trace output for fetch method calls.
+  Trace level 1 no longer shows fetches with row>1 (to reduce output volume).
+  Added $h->{FetchHashKeyName} = 'NAME_lc' or 'NAME_uc' to alter
+    behaviour of fetchrow_hashref() method. See docs.
+  Added type_info quote caching to quote() method thanks to Dean Kopesky.
+    Makes using quote() with second data type param much much faster.
+  Added type_into_all() caching to type_info(), spotted by Dean Kopesky.
+  Added new API definition for table_info() and tables(),
+    driver authors please note!
+  Added primary_key_info() to DBI API thanks to Steffen Goeldner.
+  Added primary_key() to DBI API as simpler interface to primary_key_info().
+  Indent and other fixes for DBI::DBD doc thanks to H.Merijn Brand.
+  Added prepare_cached() insert_hash() example thanks to Doug Wilson.
+  Removed false docs for fetchall_hashref(), use fetchall_arrayref({}).
+
+=head2 Changes in DBI 1.18,    4th June 2001
+
+  Fixed that altering ShowErrorStatement also altered AutoCommit!
+    Thanks to Jeff Boes for spotting that clanger.
+  Fixed DBD::Proxy to handle commit() and rollback(). Long overdue, sorry.
+  Fixed incompatibility with perl 5.004 (but no one's using that right? :)
+  Fixed connect_cached and prepare_cached to not be affected by the order
+    of elements in the attribute hash. Spotted by Mitch Helle-Morrissey.
+  Fixed version number of DBI::Shell
+    reported by Stuhlpfarrer Gerhard and others.
+  Defined and documented table_info() attribute semantics (ODBC compatible)
+    thanks to Olga Voronova, who also implemented then in DBD::Oracle.
+  Updated Win32::DBIODBC (Win32::ODBC emulation) thanks to Roy Lee.
+
+=head2 Changes in DBI 1.16,    30th May 2001
+
+  Reimplemented fetchrow_hashref in C, now fetches about 25% faster!
+  Changed behaviour if both PrintError and RaiseError are enabled
+    to simply do both (in that order, obviously :)
+  Slight reduction in DBI handle creation overhead.
+  Fixed $dbh->{Driver} & $sth->{Database} to return 'outer' handles.
+  Fixed execute param count check to honour RaiseError spotted by Belinda Giardie.
+  Fixed build for perl5.6.1 with PERLIO thanks to H.Merijn Brand.
+  Fixed client sql restrictions in ProxyServer.pm thanks to Jochen Wiedmann.
+  Fixed batch mode command parsing in Shell thanks to Christian Lemburg.
+  Fixed typo in selectcol_arrayref docs thanks to Jonathan Leffler.
+  Fixed selectrow_hashref to be available to callers thanks to T.J.Mather.
+  Fixed core dump if statement handle didn't define Statement attribute.
+  Added bind_param_inout docs to DBI::DBD thanks to Jonathan Leffler.
+  Added note to data_sources() method docs that some drivers may
+    require a connected database handle to be supplied as an attribute.
+  Trace of install_driver method now shows path of driver file loaded.
+  Changed many '||' to 'or' in the docs thanks to H.Merijn Brand.
+  Updated DBD::ADO again (improvements in error handling) from Tom Lowery.
+  Updated Win32::DBIODBC (Win32::ODBC emulation) thanks to Roy Lee.
+  Updated email and web addresses in DBI::FAQ thanks to Michael A Chase.
+
+=head2 Changes in DBI 1.15,    28th March 2001
+
+  Added selectrow_arrayref
+  Added selectrow_hashref
+  Added selectall_hashref thanks to Leon Brocard.
+  Added DBI->connect(..., { dbi_connect_method => 'method' })
+  Added $dbh->{Statement} aliased to most recent child $sth->{Statement}.
+  Added $h->{ShowErrorStatement}=1 to cause the appending of the
+    relevant Statement text to the RaiseError/PrintError text.
+  Modified type_info to always return hash keys in uppercase and
+    to not require uppercase 'DATA_TYPE' key from type_info_all.
+    Thanks to Jennifer Tong and Rob Douglas.
+  Added \%attr param to tables() and table_info() methods.
+  Trace method uses warn() if it can't open the new file.
+  Trace shows source line and filename during global destruction.
+  Updated packages:
+    Updated Win32::DBIODBC (Win32::ODBC emulation) thanks to Roy Lee.
+    Updated DBD::ADO to much improved version 0.4 from Tom Lowery.
+    Updated DBD::Sponge to include $sth->{PRECISION} thanks to Tom Lowery.
+    Changed DBD::ExampleP to use lstat() instead of stat().
+  Documentation:
+    Documented $DBI::lasth (which has been there since day 1).
+    Documented SQL_* names.
+    Clarified and extended docs for $h->state thanks to Masaaki Hirose.
+    Clarified fetchall_arrayref({}) docs (thanks to, er, someone!).
+    Clarified type_info_all re lettercase and index values.
+    Updated DBI::FAQ to 0.38 thanks to Alligator Descartes.
+    Added cute bind_columns example thanks to H.Merijn Brand.
+    Extended docs on \%attr arg to data_sources method.
+  Makefile.PL
+    Removed obscure potential 'rm -rf /' (thanks to Ulrich Pfeifer).
+    Removed use of glob and find (thanks to Michael A. Chase).
+  Proxy:
+    Removed debug messages from DBD::Proxy AUTOLOAD thanks to Brian McCauley.
+    Added fix for problem using table_info thanks to Tom Lowery.
+    Added better determination of where to put the pid file, and...
+    Added KNOWN ISSUES section to DBD::Proxy docs thanks to Jochen Wiedmann.
+  Shell:
+    Updated DBI::Format to include DBI::Format::String thanks to Tom Lowery.
+    Added describe command thanks to Tom Lowery.
+    Added columnseparator option thanks to Tom Lowery (I think).
+    Added 'raw' format thanks to, er, someone, maybe Tom again.
+  Known issues:
+    Perl 5.005 and 5.006 both leak memory doing local($handle->{Foo}).
+    Perl 5.004 doesn't. The leak is not a DBI or driver bug.
+
+=head2 Changes in DBI 1.14,	14th June 2000
 
   NOTE: This version is the one the DBI book is based on.
   NOTE: This version requires at least Perl 5.004.
@@ -15,7 +424,7 @@
   Added prepare_cached() example to the docs.
   Added further DBD::ADO enhancements from Thomas Lowery.
 
-Changes in DBI 1.13,	11th July 1999
+=head2 Changes in DBI 1.13,	11th July 1999
 
   Fixed Win32 PerlEx IIS concurrency bugs thanks to Murray Nesbitt.
   Fixed problems with DBD::ExampleP long_list test mode.
@@ -26,7 +435,7 @@
   Actually documented connect_cached thanks to Michael Schwern.
   Fixed user/key/cipher bug in ProxyServer thanks to Joshua Pincus.
 
-Changes in DBI 1.12,	29th June 1999
+=head2 Changes in DBI 1.12,	29th June 1999
 
   Fixed significant DBD::ADO bug (fetch skipped first row).
   Fixed ProxyServer bug handling non-select statements.
@@ -34,14 +443,14 @@
   Trace only shows calls to trace_msg and _set_fbav at high levels.
   Modified t/examp.t to workaround Cygwin buffering bug.
 
-Changes in DBI 1.11,	17th June 1999
+=head2 Changes in DBI 1.11,	17th June 1999
 
   Fixed bind_columns argument checking to allow a single arg.
   Fixed problems with internal default_user method.
   Fixed broken DBD::ADO.
   Made default $DBI::rows more robust for some obscure cases.
 
-Changes in DBI 1.10,	14th June 1999
+=head2 Changes in DBI 1.10,	14th June 1999
 
   Fixed trace_msg.al error when using Apache.
   Fixed dbd_st_finish enhancement in Driver.xst (internals).
@@ -49,7 +458,7 @@
     and temporarily disabled warning added in 1.09.
   Thread safety optimised for single thread case.
 
-Changes in DBI 1.09,	9th June 1999
+=head2 Changes in DBI 1.09,	9th June 1999
 
   Added optional minimum trace level parameter to trace_msg().
   Added warning in Makefile.PL that DBI will require 5.004 soon.
@@ -68,7 +477,7 @@
   Added portability fixes for MacOS from Chris Nandor.
   Updated mailing list address from fugue.com to isc.org.
 
-Changes in DBI 1.08,	12th May 1999
+=head2 Changes in DBI 1.08,	12th May 1999
 
   Much improved DBD::ADO driver thanks to Phlip Plumlee and others.
   Connect now allows you to specify attribute settings within the DSN
@@ -82,7 +491,7 @@
   Workaround for Cygwin32 build problem with help from Jong-Pork Park.
   bind_columns no longer needs undef or hash ref as first parameter.
 
-Changes in DBI 1.07,	6th May 1999
+=head2 Changes in DBI 1.07,	6th May 1999
 
   Trace output now shows contents of array refs returned by DBI.
   Changed names of some result columns from type_info, type_info_all,
@@ -94,7 +503,7 @@
   Added code for MULTIPLICITY/PERL_OBJECT contributed by ActiveState.
   Added $sth->more_results (undocumented for now).
 
-Changes in DBI 1.06,	6th January 1999
+=head2 Changes in DBI 1.06,	6th January 1999
 
   Fixed Win32 Makefile.PL problem in 1.04 and 1.05.
   Significant DBD::Proxy enhancements and fixes
@@ -103,13 +512,13 @@
   Added $sth->{NAME_uc} and $sth->{NAME_lc} attributes.
   Enhanced fetchrow_hashref to take an attribute name arg.
 
-Changes in DBI 1.05,	4th January 1999
+=head2 Changes in DBI 1.05,	4th January 1999
 
   Improved DBD::ADO connect (thanks to Phlip Plumlee).
   Improved thread safety (thanks to Jochen Wiedmann).
   [Quick release prompted by truncation of copies on CPAN]
 
-Changes in DBI 1.04,	3rd January 1999
+=head2 Changes in DBI 1.04,	3rd January 1999
 
   Fixed error in Driver.xst. DBI build now tests Driver.xst.
   Removed unused variable compiler warnings in Driver.xst.
@@ -117,7 +526,7 @@
   Further clarification in the DBI::DBD driver writers manual.
   Added optional name parameter to $sth->fetchrow_hashref.
 
-Changes in DBI 1.03,	1st January 1999
+=head2 Changes in DBI 1.03,	1st January 1999
 
   Now builds with Perl>=5.005_54 (PERL_POLLUTE in DBIXS.h)
   DBI trace trims path from "at yourfile.pl line nnn".
@@ -145,13 +554,13 @@
   Fixed DBD::Sponge to work with empty result sets.
   Complete rewrite of DBI::ProxyServer and DBD::Proxy.
 
-Changes in DBI 1.02,	2nd September 1998
+=head2 Changes in DBI 1.02,	2nd September 1998
 
   Fixed DBI::Shell including @ARGV and /current.
   Added basic DBI::Shell test.
   Renamed DBI::Shell /display to /format.
 
-Changes in DBI 1.01,	2nd September 1998
+=head2 Changes in DBI 1.01,	2nd September 1998
 
   Many enhancements to Shell (with many contributions from
   Jochen Wiedmann, Tom Lowery and Adam Marks).
@@ -163,7 +572,7 @@
   DBI->connect now honours PrintError=1 if connect fails.
   Assorted clarifications to the docs.
 
-Changes in DBI 1.00,	14th August 1998
+=head2 Changes in DBI 1.00,	14th August 1998
 
   The DBI is no longer 'alpha' software!
   Added $dbh->tables and $dbh->table_info.
@@ -173,7 +582,7 @@
   DBI::Shell now uses neat_list to print results
   It also escapes "'" chars and converts newlines to spaces.
 
-Changes in DBI 0.95,	10th August 1998
+=head2 Changes in DBI 0.95,	10th August 1998
 
   WARNING: THIS IS AN EXPERIMENTAL RELEASE!
 
@@ -183,7 +592,7 @@
   Improved connect error reporting by DBD::Proxy.
   All trace/debug messages from DBI now go to trace file.
 
-Changes in DBI 0.94,	9th August 1998
+=head2 Changes in DBI 0.94,	9th August 1998
 
   WARNING: THIS IS AN EXPERIMENTAL RELEASE!
 
@@ -207,14 +616,14 @@
   Added @bool = DBI::looks_like_number(@ary).
   Many assorted improvements to the DBI docs.
 
-Changes in DBI 0.93,	13th February 1998
+=head2 Changes in DBI 0.93,	13th February 1998
 
   Fixed DBI::DBD::dbd_postamble bug causing 'Driver.xsi not found' errors.
   Changes to handling of 'magic' values in neatsvpv (used by trace).
   execute (in Driver.xst) stops binding after first bind error.
   This release requires drivers to be rebuilt.
 
-Changes in DBI 0.92,	3rd February 1998
+=head2 Changes in DBI 0.92,	3rd February 1998
 
   Fixed per-handle memory leak (with many thanks to Irving Reid).
   Added $dbh->prepare_cached() caching variant of $dbh->prepare.
@@ -232,7 +641,7 @@
   Fixed small 'once per interpreter' leak.
   Assorted minor documentation fixes.
 
-Changes in DBI 0.91,	10th December 1997
+=head2 Changes in DBI 0.91,	10th December 1997
 
   NOTE: This fix may break some existing scripts:
   DBI->connect("dbi:...",$user,$pass) was not setting AutoCommit and PrintError!
@@ -248,7 +657,7 @@
   Fixed to build okay with 5.004_54 with or without USE_THREADS.
   Note that the DBI has not been tested for thread safety yet.
 
-Changes in DBI 0.90,	6th September 1997
+=head2 Changes in DBI 0.90,	6th September 1997
 
   Can once again be built with Perl 5.003.
   The DBI class can be subclassed more easily now.
@@ -258,13 +667,13 @@
   Note: 0.90, 0.89 and possibly some other recent versions have
   a small memory leak. This will be fixed in the next release.
 
-Changes in DBI 0.89,	25th July 1997
+=head2 Changes in DBI 0.89,	25th July 1997
 
   Minor fix to neatsvpv (mainly used for debug trace) to workaround
   bug in perl where SvPV removes IOK flag from an SV.
   Minor updates to the docs.
 
-Changes in DBI 0.88,	22nd July 1997
+=head2 Changes in DBI 0.88,	22nd July 1997
 
   Fixed build for perl5.003 and Win32 with Borland.
   Fixed documentation formatting.
@@ -274,12 +683,12 @@
   The DBI can now export SQL type values: use DBI ':sql_types';
   Modified Driver.xst and renamed DBDI.h to dbd_xsh.h
 
-Changes in DBI 0.87,	18th July 1997
+=head2 Changes in DBI 0.87,	18th July 1997
 
   Fixed minor type clashes.
   Added more docs about placeholders and bind values.
 
-Changes in DBI 0.86,	16th July 1997
+=head2 Changes in DBI 0.86,	16th July 1997
 
   Fixed failed connect causing 'unblessed ref' and other errors.
   Drivers must handle AutoCommit FETCH and STORE else DBI croaks.
@@ -302,7 +711,7 @@
   that won't appear in drivers for some time. Driver writers might like
   to read perldoc DBI::DBD and comment on or apply the information given.
 
-Changes in DBI 0.85,	25th June 1997
+=head2 Changes in DBI 0.85,	25th June 1997
 
   NOTE: New-style connect now defaults to AutoCommit mode unless
   { AutoCommit => 0 } specified in connect attributes. See the docs.
@@ -315,7 +724,7 @@
   Fixed bug which set some attributes true when there value was fetched.
   Added new internal DBIc_set() macro for drivers to use.
 
-Changes in DBI 0.84,	20th June 1997
+=head2 Changes in DBI 0.84,	20th June 1997
 
   Added $h->{PrintError} attribute which, if set true, causes all errors to
   trigger a warn().
@@ -326,7 +735,7 @@
   Renamed $h->debug to $h->trace() and added a trace filename arg.
   Assorted other minor tidy-ups.
 
-Changes in DBI 0.83,	11th June 1997
+=head2 Changes in DBI 0.83,	11th June 1997
 
   Added driver specification syntax to DBI->connect data_source
   parameter: DBI->connect('dbi:driver:...', $user, $passwd);
@@ -348,7 +757,7 @@
   Unknown driver specific attribute names no longer croak.
   Fixed problem with internal neatsvpv macro.
 
-Changes in DBI 0.82,	23rd May 1997
+=head2 Changes in DBI 0.82,	23rd May 1997
 
   Added $h->{RaiseError} attribute which, if set true, causes all errors to
   trigger a die(). This makes it much easier to implement robust applications
@@ -364,12 +773,12 @@
   changed (DBIXS_VERSION == 8 and DBISTATE_VERSION == 8) so drivers will have
   to be recompiled.
 
-Changes in DBI 0.81,	7th May 1997
+=head2 Changes in DBI 0.81,	7th May 1997
 
   Minor fix to let DBI build using less modern perls.
   Fixed a suprious typo warning.
 
-Changes in DBI 0.80,	6th May 1997
+=head2 Changes in DBI 0.80,	6th May 1997
 
   Builds with no changes on NT using perl5.003_99 (with thanks to Jeffrey Urlwin).
   Automatically supports Apache::DBI (with thanks to Edmund Mergl).
@@ -383,13 +792,13 @@
   Added more pod text.  Fixed assorted bugs.
   
 
-Changes in DBI 0.79,	7th Apr 1997
+=head2 Changes in DBI 0.79,	7th Apr 1997
 
   Minor release. Tidied up pod text and added some more descriptions
   (especially disconnect). Minor changes to DBI.xs to remove compiler
   warnings.
 
-Changes in DBI 0.78,	28th Mar 1997
+=head2 Changes in DBI 0.78,	28th Mar 1997
 
   Greatly extended the pod documentation in DBI.pm, including the under
   used bind_columns method. Use 'perldoc DBI' to read after installing.
@@ -401,7 +810,7 @@
   DBI emulation layer for Win32::ODBC. Note that it's unsupported, your
   mileage will vary, and bug reports without fixes will probably be ignored.
 
-Changes in DBI 0.77,	21st Feb 1997
+=head2 Changes in DBI 0.77,	21st Feb 1997
 
   Removed erroneous $h->errstate and $h->errmsg methods from DBI.pm.
   Added $h->err, $h->errstr and $h->state default methods in DBI.xs.
@@ -411,16 +820,16 @@
   -I$(INSTALLSITEARCH)/auto/DBI -I$(INSTALLSITEARCH)/DBI)
 
 
-Changes in DBI 0.76,	3rd Feb 1997
+=head2 Changes in DBI 0.76,	3rd Feb 1997
 
   Fixed a compiler type warnings (pedantic IRIX again).
 
-Changes in DBI 0.75,	27th Jan 1997
+=head2 Changes in DBI 0.75,	27th Jan 1997
 
   Fix problem introduced by a change in Perl5.003_XX.
   Updated README and DBI.pm docs.
 
-Changes in DBI 0.74,	14th Jan 1997
+=head2 Changes in DBI 0.74,	14th Jan 1997
 
   Dispatch now sets dbi_debug to the level of the current handle
   (this makes tracing/debugging individual handles much easier).
@@ -431,7 +840,7 @@
   No longer sets perl_destruct_level unless debug set >= 4.
   Make compatible with PerlIO and sfio.
 
-Changes in DBI 0.73,	10th Oct 1996
+=head2 Changes in DBI 0.73,	10th Oct 1996
 
   Fixed some compiler type warnings (IRIX).
   Fixed DBI->internal->{DebugLog} = $filename.
@@ -440,24 +849,24 @@
   Usage: $dbh->bind_param_inout($param, \$value, $maxlen [, \%attribs ])
   (only currently used by DBD::Oracle at this time.)
 
-Changes in DBI 0.72,	23 Sep 1996
+=head2 Changes in DBI 0.72,	23 Sep 1996
 
   Using an undefined value as a handle now gives a better
   error message (mainly useful for emulators like Oraperl).
   $dbh->do($sql, @params) now works for binding placeholders.
 
-Changes in DBI 0.71,	10 July 1996
+=head2 Changes in DBI 0.71,	10 July 1996
 
   Removed spurious abort() from invalid handle check.
   Added quote method to DBI interface and added test.
 
-Changes in DBI 0.70,	16 June 1996
+=head2 Changes in DBI 0.70,	16 June 1996
 
   Added extra invalid handle check (dbih_getcom)
   Fixed broken $dbh->quote method.
   Added check for old GCC in Makefile.PL
 
-Changes in DBI 0.69
+=head2 Changes in DBI 0.69
 
   Fixed small memory leak.
   Clarified the behaviour of DBI->connect.
@@ -465,19 +874,19 @@
   Fixed "Can't read $DBI::errstr, lost last handle" problem.
 
 
-Changes in DBI 0.68,	2 Mar 1996
+=head2 Changes in DBI 0.68,	2 Mar 1996
 
   Changes to suit perl5.002 and site_lib directories.
   Detects old versions ahead of new in @INC.
 
 
-Changes in DBI 0.67,	15 Feb 1996
+=head2 Changes in DBI 0.67,	15 Feb 1996
 
   Trivial change to test suite to fix a problem shown up by the
   Perl5.002gamma release Test::Harness.
 
 
-Changes in DBI 0.66,	29 Jan 1996
+=head2 Changes in DBI 0.66,	29 Jan 1996
 
   Minor changes to bring the DBI into line with 5.002 mechanisms,
   specifically the xs/pm VERSION checking mechanism.
@@ -485,7 +894,7 @@
   Requires 5.002 (beta2 or later).
 
 
-Changes in DBI 0.65,	23 Oct 1995
+=head2 Changes in DBI 0.65,	23 Oct 1995
 
   Added $DBI::state to hold SQL CLI / ODBC SQLSTATE value.
   SQLSTATE "00000" (success) is returned as "" (false), all else is true.
@@ -517,7 +926,7 @@
   should be ignored for now.
 
 
-Changes in DBI 0.64,	23 Oct 1995
+=head2 Changes in DBI 0.64,	23 Oct 1995
 
   Fixed 'disconnect invalidates 1 associated cursor(s)' problem.
   Drivers using DBIc_ACTIVE_on/off() macros should not need any changes
@@ -525,14 +934,14 @@
   Fixed possible core dump in dbih_clearcom during global destruction.
 
 
-Changes in DBI 0.63,	1 Sep 1995
+=head2 Changes in DBI 0.63,	1 Sep 1995
 
   Minor update. Fixed uninitialised memory bug in method
   attribute handling and streamlined processing and debugging.
   Revised usage definitions for bind_* methods and readblob.
 
 
-Changes in DBI 0.62,	26 Aug 1995
+=head2 Changes in DBI 0.62,	26 Aug 1995
 
   Added method redirection method $h->func(..., $method_name).
   This is now the official way to call private driver methods
@@ -555,7 +964,7 @@
   Faster startup via install_method/_add_dispatch changes.
 
 
-Changes in DBI 0.61,	22 Aug 1995
+=head2 Changes in DBI 0.61,	22 Aug 1995
 
   Added $sth->bind_col($column, \$var [, \%attribs ]);
 
@@ -590,7 +999,7 @@
   test.pl includes a trivial test of bind_param and bind_columns.
 
 
-Changes in DBI 0.60,	17 Aug 1995
+=head2 Changes in DBI 0.60,	17 Aug 1995
 
   This release has significant code changes but much less
   dramatic than the previous release. The new implementors data
@@ -621,7 +1030,7 @@
   as dramatic as these last two releases! (I hope :-)
 
 
-Changes in DBI 0.59	15 Aug 1995
+=head2 Changes in DBI 0.59	15 Aug 1995
 
   NOTE: THIS IS AN UNSTABLE RELEASE!
 
@@ -633,7 +1042,7 @@
   (Comparing parts of v0.21 with v0.20 may be useful.)
 
 
-Changes in DBI 0.58	21 June 1995
+=head2 Changes in DBI 0.58	21 June 1995
 
   Added DBI->internal->{DebugLog} = $filename;
   Reworked internal logging.
@@ -641,11 +1050,13 @@
   Made disconnect_all a compulsary method for drivers.
 
 
-Some Key Dates in Ancient History
+=head1 ANCIENT HISTORY
 
-29th Sep 1992: DBperl project started.
-19th Sep 1994: DBperl project renamed to DBI.
 12th Oct 1994: First public release of the DBI module.
                (for Perl 5.000-beta-3h)
 
-End.
+19th Sep 1994: DBperl project renamed to DBI.
+
+29th Sep 1992: DBperl project started.
+
+=cut
diff -u DBI-1.14/DBI.pm DBI-1.30/DBI.pm
--- DBI-1.14/DBI.pm	Wed Jun 14 21:04:14 2000
+++ DBI-1.30/DBI.pm	Thu Jul 18 15:24:04 2002
@@ -1,14 +1,14 @@
-# $Id: DBI.pm,v 10.32 2000/06/14 20:04:03 timbo Exp $
+# $Id: DBI.pm,v 11.18 2002/07/18 14:23:44 timbo Exp $
 #
-# Copyright (c) 1994-2000  Tim Bunce  England
+# Copyright (c) 1994-2002  Tim Bunce  Ireland
 #
 # See COPYRIGHT section in pod text below for usage and distribution rights.
 #
 
-require 5.004;
+require 5.005_03;
 
 BEGIN {
-$DBI::VERSION = "1.14"; # ==> ALSO update the version in the pod text below!
+$DBI::VERSION = "1.30"; # ==> ALSO update the version in the pod text below!
 }
 
 =head1 NAME
@@ -20,7 +20,7 @@
   use DBI;
 
   @driver_names = DBI->available_drivers;
-  @data_sources = DBI->data_sources($driver_name);
+  @data_sources = DBI->data_sources($driver_name, \%attr);
 
   $dbh = DBI->connect($data_source, $username, $auth, \%attr);
 
@@ -28,20 +28,30 @@
   $rv  = $dbh->do($statement, \%attr);
   $rv  = $dbh->do($statement, \%attr, @bind_values);
 
-  $ary_ref = $dbh->selectall_arrayref($statement);
-  @row_ary = $dbh->selectrow_array($statement);
+  $ary_ref  = $dbh->selectall_arrayref($statement);
+  $hash_ref = $dbh->selectall_hashref($statement, $key_field);
+
   $ary_ref = $dbh->selectcol_arrayref($statement);
+  $ary_ref = $dbh->selectcol_arrayref($statement, \%attr);
+
+  @row_ary  = $dbh->selectrow_array($statement);
+  $ary_ref  = $dbh->selectrow_arrayref($statement);
+  $hash_ref = $dbh->selectrow_hashref($statement);
 
   $sth = $dbh->prepare($statement);
   $sth = $dbh->prepare_cached($statement);
 
-  $rv = $sth->bind_param($p_num, $bind_value);
-  $rv = $sth->bind_param($p_num, $bind_value, $bind_type);
-  $rv = $sth->bind_param($p_num, $bind_value, \%attr);
+  $rc = $sth->bind_param($p_num, $bind_value);
+  $rc = $sth->bind_param($p_num, $bind_value, $bind_type);
+  $rc = $sth->bind_param($p_num, $bind_value, \%attr);
 
   $rv = $sth->execute;
   $rv = $sth->execute(@bind_values);
 
+  $rc = $sth->bind_param_array($p_num, $bind_values, \%attr);
+  $rv = $sth->execute_array(\%attr);
+  $rv = $sth->execute_array(\%attr, @bind_values);
+
   $rc = $sth->bind_col($col_num, \$col_variable);
   $rc = $sth->bind_columns(@list_of_refs_to_vars_to_bind);
 
@@ -50,13 +60,17 @@
   $hash_ref = $sth->fetchrow_hashref;
 
   $ary_ref  = $sth->fetchall_arrayref;
+  $ary_ref  = $sth->fetchall_arrayref( $slice, $max_rows );
+
+  $hash_ref = $sth->fetchall_hashref( $key_field );
 
   $rv  = $sth->rows;
 
+  $rc  = $dbh->begin_work;
   $rc  = $dbh->commit;
   $rc  = $dbh->rollback;
 
-  $sql = $dbh->quote($string);
+  $quoted_string = $dbh->quote($string);
 
   $rc  = $h->err;
   $str = $h->errstr;
@@ -64,86 +78,71 @@
 
   $rc  = $dbh->disconnect;
 
-I<The synopsis above only lists the major methods.>
+I<This synopsis above only lists the major methods.>
 
 
 =head2 GETTING HELP
 
 If you have questions about DBI, you can get help from
-the I<dbi-users@isc.org> mailing list.
-You can subscribe to the list by visiting:
+the I<dbi-users@perl.org> mailing list.
+You can get help on subscribing and using the list by emailing:
 
-  http://www.isc.org/dbi-lists.html
+  dbi-users-help@perl.org
 
 Also worth a visit is the DBI home page at:
 
-  http://www.symbolstone.org/technology/perl/DBI
+  http://dbi.perl.org/
 
 Before asking any questions, reread this document, consult the
 archives and read the DBI FAQ. The archives are listed
-at the end of this document.
-The FAQ is installed as a DBI::FAQ module so
+at the end of this document and on the DBI home page.
+The FAQ is installed as a L<DBI::FAQ> module so
 you can read it by executing C<perldoc DBI::FAQ>.
 
+This document often uses terms like I<references>, I<objects>,
+I<methods>.  If you're not familar with those terms then it would
+be a good idea to read at least the following perl manuals first:
+L<perlreftut>, L<perldsc>, L<perllol>, and L<perlboot>.
+
 Please note that Tim Bunce does not maintain the mailing lists or the
 web page (generous volunteers do that).  So please don't send mail
 directly to him; he just doesn't have the time to answer questions
 personally. The I<dbi-users> mailing list has lots of experienced
-people who should be able to help you if you need it.
-
-=head2 NOTE
-
-This is the DBI specification that corresponds to the DBI version 1.14
-(C<$Date: 2000/06/14 20:04:03 $>).
-
-The DBI specification is evolving at a steady pace, so it's
-important to check that you have the latest copy. The RECENT CHANGES
-section below has a summary of user-visible changes. The F<Changes>
-file supplied with the DBI holds more detailed change information.
-
-Note also that whenever the DBI changes, the drivers take some time to
-catch up. Recent versions of the DBI have added new features
-(marked I<NEW> in the text) that may not yet be supported by the drivers
-you use. Talk to the authors of those drivers if you need the features.
-
-Extensions to the DBI and other DBI related modules use the C<DBIx::*>
-namespace. See L</Naming Conventions and Name Space> and:
-
-  http://www.perl.com/CPAN/modules/by-module/DBIx/
-
-=head2 RECENT CHANGES 
+people who should be able to help you if you need it. If you do email
+Tim he's very likely to just forward it to the mailing list.
 
-Here is a brief summary of significant user-visible changes in recent versions.
-(If a recent version isn't mentioned, it simply means that there were no
-significant user-visible changes in that version.)
+=head2 NOTES
 
-=over 4 
+This is the DBI specification that corresponds to the DBI version 1.30
+(C<$Date: 2002/07/18 14:23:44 $>).
 
-=item Between DBI 1.00 and DBI 1.09:
+The DBI is evolving at a steady pace, so it's good to check that
+you have the latest copy.
 
-  Added $dbh->selectcol_arrayref($statement) method.
+The significant user-visible changes in each release are documented
+in the L<DBI::Changes> module so you can read them by executing
+C<perldoc DBI::Changes>.
 
-  Connect now allows you to specify attribute settings within the DSN.
-  For example: 
-	dbi:Driver(RaiseError=>1,Taint=>1,AutoCommit=>0):dbname"
+Some DBI changes require changes in the drivers, but the drivers
+can take some time to catch up. Recent versions of the DBI have
+added new features (generally marked I<NEW> in the text) that may
+not yet be supported by the drivers you use. Talk to the authors
+of those drivers if you need the new features.
 
-  Added $h->{Taint}, $sth->{NAME_uc}, and $sth->{NAME_lc} attributes.
+Extensions to the DBI API often use the C<DBIx::*> namespace.
+See L</Naming Conventions and Name Space> and:
 
-=back 
+  http://search.cpan.org/search?mode=module&query=DBIx%3A%3A
 
 =cut
 
 # The POD text continues at the end of the file.
 
-# DBI file-private variables
-my %installed_rootclass;
-
 
 {
 package DBI;
 
-my $Revision = substr(q$Revision: 10.32 $, 10);
-
+my $Revision = substr(q$Revision: 11.18 $, 10);
 
 use Carp;
 use DynaLoader ();
@@ -153,34 +152,98 @@
 @ISA = qw(Exporter DynaLoader);
 
 # Make some utility functions available if asked for
-@EXPORT    = ();		# we export nothing by default
-@EXPORT_OK = ('%DBI');	# populated by export_ok_tags:
+@EXPORT    = ();		    # we export nothing by default
+@EXPORT_OK = qw(%DBI %DBI_methods hash); # also populated by export_ok_tags:
 %EXPORT_TAGS = (
    sql_types => [ qw(
-    SQL_ALL_TYPES
-	SQL_CHAR SQL_NUMERIC SQL_DECIMAL SQL_INTEGER SQL_SMALLINT
-	SQL_FLOAT SQL_REAL SQL_DOUBLE SQL_VARCHAR
-	SQL_DATE SQL_TIME SQL_TIMESTAMP
-	SQL_LONGVARCHAR SQL_BINARY SQL_VARBINARY SQL_LONGVARBINARY
-	SQL_BIGINT SQL_TINYINT
-	SQL_WCHAR SQL_WVARCHAR SQL_WLONGVARCHAR
+	SQL_GUID
+	SQL_WLONGVARCHAR
+	SQL_WVARCHAR
+	SQL_WCHAR
 	SQL_BIT
+	SQL_TINYINT
+	SQL_LONGVARBINARY
+	SQL_VARBINARY
+	SQL_BINARY
+	SQL_LONGVARCHAR
+	SQL_UNKNOWN_TYPE
+	SQL_ALL_TYPES
+	SQL_CHAR
+	SQL_NUMERIC
+	SQL_DECIMAL
+	SQL_INTEGER
+	SQL_SMALLINT
+	SQL_FLOAT
+	SQL_REAL
+	SQL_DOUBLE
+	SQL_DATETIME
+	SQL_DATE
+	SQL_INTERVAL
+	SQL_TIME
+	SQL_TIMESTAMP
+	SQL_VARCHAR
+	SQL_BOOLEAN
+	SQL_UDT
+	SQL_UDT_LOCATOR
+	SQL_ROW
+	SQL_REF
+	SQL_BLOB
+	SQL_BLOB_LOCATOR
+	SQL_CLOB
+	SQL_CLOB_LOCATOR
+	SQL_ARRAY
+	SQL_ARRAY_LOCATOR
+	SQL_MULTISET
+	SQL_MULTISET_LOCATOR
+	SQL_TYPE_DATE
+	SQL_TYPE_TIME
+	SQL_TYPE_TIMESTAMP
+	SQL_TYPE_TIME_WITH_TIMEZONE
+	SQL_TYPE_TIMESTAMP_WITH_TIMEZONE
+	SQL_INTERVAL_YEAR
+	SQL_INTERVAL_MONTH
+	SQL_INTERVAL_DAY
+	SQL_INTERVAL_HOUR
+	SQL_INTERVAL_MINUTE
+	SQL_INTERVAL_SECOND
+	SQL_INTERVAL_YEAR_TO_MONTH
+	SQL_INTERVAL_DAY_TO_HOUR
+	SQL_INTERVAL_DAY_TO_MINUTE
+	SQL_INTERVAL_DAY_TO_SECOND
+	SQL_INTERVAL_HOUR_TO_MINUTE
+	SQL_INTERVAL_HOUR_TO_SECOND
+	SQL_INTERVAL_MINUTE_TO_SECOND
    ) ],
    utils     => [ qw(
 	neat neat_list dump_results looks_like_number
    ) ],
+   profile   => [ qw(
+	dbi_profile dbi_profile_merge dbi_time
+   ) ],
 );
-Exporter::export_ok_tags('sql_types', 'utils');
 
 $DBI::dbi_debug = $ENV{DBI_TRACE} || $ENV{PERL_DBI_DEBUG} || 0;
 
 # If you get an error here like "Can't find loadable object ..."
 # then you haven't installed the DBI correctly. Read the README
 # then install it again.
-bootstrap DBI;
+if ( $ENV{DBI_PUREPERL} ) {
+    eval { bootstrap DBI } if       $ENV{DBI_PUREPERL} == 1;
+    require DBI::PurePerl  if $@ or $ENV{DBI_PUREPERL} >= 2;
+    $DBI::PurePerl ||= 0; # just to silence "only used once" warnings
+}
+else {
+    bootstrap DBI;
+}
+
+$EXPORT_TAGS{preparse_flags} = [ grep { /^DBIpp_\w\w_/ } keys %{__PACKAGE__."::"} ];
+
+Exporter::export_ok_tags(keys %EXPORT_TAGS);
 
 }
+
 *trace_msg = \&DBD::_::common::trace_msg;
+*set_err   = \&DBD::_::common::set_err;
 
 use strict;
 
@@ -202,6 +265,7 @@
 	# digits (and equals) are stripped off and used as the level
 	unshift @DBI::dbi_debug, 2;
 	@DBI::dbi_debug = ($1,$2) if $DBI::dbi_debug =~ m/^(\d+)=(.*)/;
+	$DBI::dbi_debug = $DBI::dbi_debug[0];
     }
     DBI->trace(@DBI::dbi_debug);
 }
@@ -230,12 +294,11 @@
 
 # --- Dynamically create the DBI Standard Interface
 
-my $std = undef;
-my $keeperr = { O=>0x04 };
+my $keeperr = { O=>0x0004 };
 
 my @TieHash_IF = (	# Generic Tied Hash Interface
-	'STORE'   => { O=>0x10 },
-	'FETCH'   => $keeperr,
+	'STORE'   => { O=>0x0410 },
+	'FETCH'   => { O=>0x0404 },
 	'FIRSTKEY'=> $keeperr,
 	'NEXTKEY' => $keeperr,
 	'EXISTS'  => $keeperr,
@@ -243,19 +306,20 @@
 	'DESTROY' => undef,	# hardwired internally
 );
 my @Common_IF = (	# Interface functions common to all DBI classes
-	func    =>	{				O=>0x06	},
-	event   =>	{ U =>[2,0,'$type, @args'],	O=>0x04 },
-	'trace' =>	{ U =>[1,3,'[$trace_level, [$filename]]'],	O=>0x04 },
-	trace_msg =>	{ U =>[2,3,'$message_text [, $min_level ]' ],	O=>0x04, T=>8 },
-	debug   =>	{ U =>[1,2,'[$debug_level]'],	O=>0x04 }, # old name for trace
-	private_data =>	{ U =>[1,1],			O=>0x04 },
+	func    =>	{				O=>0x0006	},
+	event   =>	{ U =>[2,0,'$type, @args'],	O=>0x0004 },
+	'trace' =>	{ U =>[1,3,'[$trace_level, [$filename]]'],	O=>0x0004 },
+	trace_msg =>	{ U =>[2,3,'$message_text [, $min_level ]' ],	O=>0x0004, T=>8 },
+	debug   =>	{ U =>[1,2,'[$debug_level]'],	O=>0x0004 }, # old name for trace
+	private_data =>	{ U =>[1,1],			O=>0x0004 },
 	err     =>	$keeperr,
 	errstr  =>	$keeperr,
-	state   =>	{ U =>[1,1], O=>0x04 },
-	_not_impl => $std,
+	state   =>	{ U =>[1,1], O=>0x0004 },
+	set_err =>	{ },
+	_not_impl =>	undef,
 );
 
-my %DBI_IF = (	# Define the DBI Interface:
+%DBI::DBI_methods = ( # Define the DBI interface methods per class:
 
     dr => {		# Database Driver Interface
 	@Common_IF,
@@ -269,25 +333,35 @@
     db => {		# Database Session Class Interface
 	@Common_IF,
 	@TieHash_IF,
-	commit     	=> { U =>[1,1] },
-	rollback   	=> { U =>[1,1] },
+	connected   	=> { O=>0x0100 },
+	begin_work   	=> { U =>[1,2,'[ \%attr ]'], O=>0x0400 },
+	commit     	=> { U =>[1,1], O=>0x0480 },
+	rollback   	=> { U =>[1,1], O=>0x0480 },
 	'do'       	=> { U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'] },
+	preparse    	=> {  }, # XXX
 	prepare    	=> { U =>[2,3,'$statement [, \%attr]'] },
 	prepare_cached	=> { U =>[2,4,'$statement [, \%attr [, $allow_active ] ]'] },
 	selectrow_array	=> { U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'] },
+	selectrow_arrayref=>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'] },
+	selectrow_hashref=>{ U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'] },
 	selectall_arrayref=>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'] },
+	selectall_hashref=>{ U =>[3,0,'$statement, $keyfield [, \%attr [, @bind_params ] ]'] },
 	selectcol_arrayref=>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'] },
-	handler    	=> { U =>[2,2,'\&handler'] },
-	ping       	=> { U =>[1,1] },
-	disconnect 	=> { U =>[1,1] },
-	quote      	=> { U =>[2,3, '$string [, $data_type ]' ], O=>0x30 },
+	ping       	=> { U =>[1,1], O=>0x0404 },
+	disconnect 	=> { U =>[1,1], O=>0x0400 },
+	quote      	=> { U =>[2,3, '$string [, $data_type ]' ], O=>0x0430 },
+	quote_identifier=> { U =>[2,5, '$name [, ...]' ],	    O=>0x0430 },
 	rows       	=> $keeperr,
 
-	tables          => { U =>[1,2,'[ \%attr ]' ] },
-	table_info      => { U =>[1,2,'[ \%attr ]' ] },
-	type_info_all	=> { U =>[1,1] },
-	type_info	=> { U =>[1,2] },
-	get_info	=> { U =>[2,2] },
+	tables          => { U =>[1,6,'$catalog, $schema, $table, $type [, \%attr ]' ], O=>0x0200 },
+	table_info      => { U =>[1,6,'$catalog, $schema, $table, $type [, \%attr ]' ],	O=>0x0200 },
+	column_info     => { U =>[1,6,'$catalog, $schema, $table, $column [, \%attr ]' ], O=>0x0200 },
+	primary_key_info=> { U =>[4,5,'$catalog, $schema, $table [, \%attr ]' ],	O=>0x0200 },
+	primary_key     => { U =>[4,5,'$catalog, $schema, $table [, \%attr ]' ],	O=>0x0200 },
+	foreign_key_info=> { U =>[1,7,'$pk_catalog, $pk_schema, $pk_table, $fk_catalog, $fk_schema, $fk_table' ], O=>0x0200 },
+	type_info_all	=> { U =>[1,1], O=>0x0200 },
+	type_info	=> { U =>[1,2,'$data_type'], O=>0x0200 },
+	get_info	=> { U =>[2,2,'$info_type'], O=>0x0200 },
     },
     st => {		# Statement Class Interface
 	@Common_IF,
@@ -296,7 +370,11 @@
 	bind_columns	=> { U =>[2,0,'\\$var1 [, \\$var2, ...]'] },
 	bind_param	=> { U =>[3,4,'$parameter, $var [, \%attr]'] },
 	bind_param_inout=> { U =>[4,5,'$parameter, \\$var, $maxlen, [, \%attr]'] },
-	execute		=> { U =>[1,0,'[@args]'] },
+	execute		=> { U =>[1,0,'[@args]'], O=>0x40 },
+
+	bind_param_array  => { U =>[3,4,'$parameter, $var [, \%attr]'] },
+	bind_param_inout_array => { U =>[4,5,'$parameter, \\@var, $maxlen, [, \%attr]'] },
+	execute_array     => { U =>[2,0,'\\%attribs [, @args]'] },
 
 	fetch    	  => undef, # alias for fetchrow_arrayref
 	fetchrow_arrayref => undef,
@@ -304,7 +382,8 @@
 	fetchrow_array    => undef,
 	fetchrow   	  => undef, # old alias for fetchrow_array
 
-	fetchall_arrayref => { U =>[1,2] },
+	fetchall_arrayref => { U =>[1,3] },
+	fetchall_hashref  => { U =>[2,2] },
 
 	blob_read  =>	{ U =>[4,5,'$field, $offset, $len [, \\$buf [, $bufoffset]]'] },
 	blob_copy_to_file => { U =>[3,3,'$field, $filename_or_handleref'] },
@@ -320,8 +399,8 @@
 );
 
 my($class, $method);
-foreach $class (keys %DBI_IF){
-    my %pkgif = %{$DBI_IF{$class}};
+foreach $class (keys %DBI::DBI_methods){
+    my %pkgif = %{ $DBI::DBI_methods{$class} };
     foreach $method (keys %pkgif){
 	DBI->_install_method("DBI::${class}::$method", 'DBI.pm',
 			$pkgif{$method});
@@ -341,24 +420,36 @@
 }
 
 
+sub CLONE {
+    my $olddbis = $DBI::_dbistate;
+    _clone_dbis() unless $DBI::PurePerl; # clone the DBIS structure
+    %DBI::installed_drh = ();	# clear loaded drivers so they have a chance to reinitialize
+    DBI->trace_msg(sprintf "CONE DBI for new thread %s\n",
+	$DBI::PurePerl ? "" : sprintf("(dbis %x -> %x)",$olddbis, $DBI::_dbistate));
+}
+	
 
 # --- The DBI->connect Front Door methods
 
 sub connect_cached {
     # XXX we expect Apache::DBI users to still call connect()
-    return shift->connect(@_[0..4], 'connect_cached');
+    my ($class, $dsn, $user, $pass, $attr) = @_;
+    ($attr ||= {})->{dbi_connect_method} = 'connect_cached';
+    return $class->connect($dsn, $user, $pass, $attr);
 }
 
 sub connect {
     my $class = shift;
-    my($dsn, $user, $pass, $attr, $old_driver, $connect_meth) = @_;
-    $connect_meth ||= $connect_via; # $connect_meth not user visible
+    my($dsn, $user, $pass, $attr, $old_driver) = @_;
     my $driver;
     my $dbh;
 
     # switch $old_driver<->$attr if called in old style
     ($old_driver, $attr) = ($attr, $old_driver) if $attr and !ref($attr);
 
+    my $connect_meth = (ref $attr) ? $attr->{dbi_connect_method} : undef;
+    $connect_meth ||= $connect_via;	# fallback to default
+
     $dsn ||= $ENV{DBI_DSN} || $ENV{DBI_DBNAME} || '' unless $old_driver;
 
     if ($DBI::dbi_debug) {
@@ -386,28 +477,33 @@
 	DBI->trace_msg("       DBI_AUTOPROXY: dbi:$driver:$dsn\n");
     }
 
-    unless ($old_driver) { # new-style connect so new default semantics
-	$driver_attrib_spec = { split /\s*=>?\s*|\s*,\s*/, $driver_attrib_spec }
-	    if $driver_attrib_spec;
-	$attr = {
-	    PrintError=>1, AutoCommit=>1,
-	    ref $attr ? %$attr : (),
-	    ref $driver_attrib_spec ? %$driver_attrib_spec : (),
-	};
-	# XXX to be enabled for DBI v2.0
-	#Carp::carp("AutoCommit attribute not specified in DBI->connect")
+    my %attr;	# take a copy we can delete from
+    if ($old_driver) {
+	%attr = %$attr if $attr;
+    }
+    else {		# new-style connect so new default semantics
+	%attr = (
+	    PrintError => 1,
+	    AutoCommit => 1,
+	    ref $attr           ? %$attr : (),
+	    $driver_attrib_spec ? (split /\s*=>?\s*|\s*,\s*/, $driver_attrib_spec) : (),
+	);
+	# XXX to be enabled for DBI v2.0?
+	#Carp::carp("AutoCommit attribute not specified in $class->connect")
 	#    if $^W && !defined($attr->{AutoCommit});
     }
+    $attr = \%attr;	# now set $attr at our local copy
 
-    my $drh = $class->install_driver($driver)
-	|| die "panic: install_driver($driver) failed";
+    my $drh = $DBI::installed_drh{$driver} || $class->install_driver($driver)
+	or die "panic: $class->install_driver($driver) failed";
 
     ($user, $pass) = $drh->default_user($user, $pass, $attr)
 	if !(defined $user && defined $pass);
 
     unless ($dbh = $drh->$connect_meth($dsn, $user, $pass, $attr)) {
 	my $msg = "$class->connect($dsn) failed: ".$drh->errstr;
-	if (ref $attr) {
+	if (%attr) {
+	    # XXX add $attr{HandleError} logic here?
 	    Carp::croak($msg) if $attr->{RaiseError};
 	    Carp::carp ($msg) if $attr->{PrintError};
 	}
@@ -416,30 +512,49 @@
 	return undef;
     }
 
-    # XXX this is inelegant but practical in the short term, sigh.
-    if ($installed_rootclass{$class}) {
-	$dbh->{RootClass} = $class;
-	bless $dbh => $class.'::db';
-	my ($outer, $inner) = DBI::_handles($dbh);
-	bless $inner => $class.'::db';
+
+    # handle basic RootClass subclassing:
+    my $rebless_class = $attr->{RootClass} || ($class ne 'DBI' ? $class : '');
+    if ($rebless_class) {
+	if ($attr->{RootClass}) {	# explicit attribute (rather than static call)
+	    delete $attr->{RootClass};
+	    DBI::_load_module($rebless_class);
+	}
+        no strict 'refs';
+        unless (@{"$rebless_class\::db::ISA"}) {
+            Carp::carp("DBI subclass '$rebless_class\::db' isn't setup, ignored");
+            $rebless_class = undef;
+            $class = 'DBI';
+        }
+        else {
+            $dbh->{RootClass} = $rebless_class; # $dbh->STORE called via plain DBI::db
+            DBI::_set_isa([$rebless_class], 'DBI');     # sets up both '::db' and '::st'
+            DBI::_rebless($dbh, $rebless_class);        # appends '::db'
+        }
     }
 
-    if (ref $attr) {
-	my %a = %$attr;
+
+    if (%$attr) {
+
+	DBI::_rebless_dbtype_subclass($dbh, $rebless_class||$class, delete $attr->{DbTypeSubclass}, $attr)
+	    if $attr->{DbTypeSubclass};
+
 	my $a;
-	# handle these attributes first
-	foreach $a (qw(RaiseError PrintError AutoCommit)) {
-	    next unless exists $a{$a};
-	    $dbh->{$a} = $a{$a};
-	    delete $a{$a};
+	foreach $a (qw(RaiseError PrintError AutoCommit)) { # do these first
+	    next unless  exists $attr->{$a};
+	    $dbh->{$a} = delete $attr->{$a};
 	}
-	foreach $a (keys %a) {
-	    $dbh->{$a} = $a{$a};
+	foreach $a (keys %$attr) {
+	    $dbh->{$a} = $attr->{$a};
 	}
     }
+
+    # if we've been subclassed then let the subclass know that we're connected
+    $dbh->connected($dsn, $user, $pass, \%attr) if ref $dbh ne 'DBI::db';
+
     DBI->trace_msg("    <- connect= $dbh\n") if $DBI::dbi_debug;
 
-    $dbh;
+    return $dbh;
 }
 
 
@@ -452,11 +567,15 @@
 }
 
 
+sub disconnect {	# a regular beginners bug
+    Carp::croak("DBI->disconnect is not a DBI method. Read the DBI manual.");
+}
+
+
 sub install_driver {		# croaks on failure
     my $class = shift;
     my($driver, $attr) = @_;
     my $drh;
-    local $@;
 
     $driver ||= $ENV{DBI_DRIVER} || '';
 
@@ -470,7 +589,7 @@
     return $drh if $drh = $DBI::installed_drh{$driver};
 
     DBI->trace_msg("    -> $class->install_driver($driver"
-			.") for perl=$] pid=$$ ruid=$< euid=$>\n")
+			.") for $^O perl=$] pid=$$ ruid=$< euid=$>\n")
 	if $DBI::dbi_debug;
 
     # --- load the code
@@ -500,8 +619,10 @@
     }
     if ($DBI::dbi_debug) {
 	no strict 'refs';
+	(my $driver_file = $driver_class) =~ s/::/\//g;
 	my $dbd_ver = ${"$driver_class\::VERSION"} || "undef";
-	DBI->trace_msg("       install_driver: $driver_class loaded (version $dbd_ver)\n")
+	DBI->trace_msg("       install_driver: $driver_class version $dbd_ver"
+		." loaded from $INC{qq($driver_file.pm)}\n");
     }
 
     # --- do some behind-the-scenes checks and setups on the driver
@@ -532,25 +653,140 @@
 	my $class = $driver_class."::$type";
 	no strict 'refs';
 	push @{"${class}::ISA"},     "DBD::_::$type";
-	push @{"${class}_mem::ISA"}, "DBD::_mem::$type";
+	push @{"${class}_mem::ISA"}, "DBD::_mem::$type" unless $DBI::PurePerl;
     }
 }
 
 
-sub init_rootclass {
-    my $rootclass = shift;
-    no strict 'refs';
-    croak("Can't init '$rootclass' without '$rootclass\::db' class.")
-	unless defined ${"$rootclass\::db::"}{ISA};
+sub _rebless {
+    my $dbh = shift;
+    my ($outer, $inner) = DBI::_handles($dbh);
+    my $class = shift(@_).'::db';
+    bless $inner => $class;
+    bless $outer => $class; # outer last for return
+}
+
+
+sub _set_isa {
+    my ($classes, $topclass) = @_;
+    my $trace = DBI->trace_msg("       _set_isa([@$classes])\n");
+    foreach my $suffix ('::db','::st') {
+	my $previous = $topclass || 'DBI'; # trees are rooted here
+	foreach my $class (@$classes) {
+	    my $base_class = $previous.$suffix;
+	    my $sub_class  = $class.$suffix;
+	    my $sub_class_isa  = "${sub_class}::ISA";
+	    no strict 'refs';
+	    if (@$sub_class_isa) {
+		DBI->trace_msg("       $sub_class_isa skipped (already set to @$sub_class_isa)\n")
+		    if $trace;
+	    }
+	    else {
+		@$sub_class_isa = ($base_class) unless @$sub_class_isa;
+		DBI->trace_msg("       $sub_class_isa = $base_class\n")
+		    if $trace;
+	    }
+	    $previous = $class;
+	}
+    }
+}
+
+
+sub _rebless_dbtype_subclass {
+    my ($dbh, $rootclass, $DbTypeSubclass, $attr) = @_;
+    # determine the db type names for class hierarchy
+    my @hierarchy = DBI::_dbtype_names($dbh, $DbTypeSubclass, $attr);
+    # add the rootclass prefix to each ('DBI::' or 'MyDBI::' etc)
+    $_ = $rootclass.'::'.$_ foreach (@hierarchy);
+    # load the modules from the 'top down'
+    DBI::_load_module($_) foreach (reverse @hierarchy);
+    # setup class hierarchy if needed, does both '::db' and '::st'
+    DBI::_set_isa(\@hierarchy, $rootclass);
+    # finally bless the handle into the subclass
+    DBI::_rebless($dbh, $hierarchy[0]);
+}
+
+
+sub _dbtype_names { # list dbtypes for hierarchy, ie Informix=>ADO=>ODBC
+    my ($dbh, $DbTypeSubclass, $attr) = @_;
+
+    if ($DbTypeSubclass && $DbTypeSubclass ne '1' && ref $DbTypeSubclass ne 'CODE') {
+	# treat $DbTypeSubclass as a comma separated list of names
+	my @dbtypes = split /\s*,\s*/, $DbTypeSubclass;
+	$dbh->trace_msg("    DbTypeSubclass($DbTypeSubclass)=@dbtypes (explicit)\n");
+	return @dbtypes;
+    }
+
+    # XXX will call $dbh->get_info(17) (=SQL_DBMS_NAME) in future?
+
+    my $driver = $dbh->{Driver}->{Name};
+    if ( $driver eq 'Proxy' ) {
+        # XXX Looking into the internals of DBD::Proxy is questionable!
+        ($driver) = $dbh->{proxy_client}->{application} =~ /^DBI:(.+?):/i
+		or die "Can't determine driver name from proxy";
+    }
+
+    my @dbtypes = (ucfirst($driver));
+    if ($driver eq 'ODBC' || $driver eq 'ADO') {
+	# XXX will move these out and make extensible later:
+	my $_dbtype_name_regexp = 'Oracle'; # eg 'Oracle|Foo|Bar'
+	my %_dbtype_name_map = (
+	     'Microsoft SQL Server'	=> 'MSSQL',
+	     'SQL Server'		=> 'Sybase',
+	     'Adaptive Server Anywhere'	=> 'ASAny',
+	     'ADABAS D'			=> 'AdabasD',
+	);
+
+        my $name;
+	$name = $dbh->func(17, 'GetInfo') # SQL_DBMS_NAME
+		if $driver eq 'ODBC';
+	$name = $dbh->{ado_conn}->Properties->Item('DBMS Name')->Value
+		if $driver eq 'ADO';
+	die "Can't determine driver name! ($DBI::errstr)\n"
+		unless $name;
+
+	my $dbtype;
+        if ($_dbtype_name_map{$name}) {
+            $dbtype = $_dbtype_name_map{$name};
+        }
+	else {
+	    if ($name =~ /($_dbtype_name_regexp)/) {
+		$dbtype = lc($1);
+	    }
+	    else { # generic mangling for other names:
+		$dbtype = lc($name);
+	    }
+	    $dbtype =~ s/\b(\w)/\U$1/g;
+	    $dbtype =~ s/\W+/_/g;
+	}
+	# add ODBC 'behind' ADO
+	push    @dbtypes, 'ODBC' if $driver eq 'ADO';
+	# add discovered dbtype in front of ADO/ODBC
+	unshift @dbtypes, $dbtype;
+    }
+    @dbtypes = &$DbTypeSubclass($dbh, \@dbtypes)
+	if (ref $DbTypeSubclass eq 'CODE');
+    $dbh->trace_msg("    DbTypeSubclass($DbTypeSubclass)=@dbtypes\n");
+    return @dbtypes;
+}
+
+sub _load_module {
+    (my $module = shift) =~ s!::!/!g;
+    eval {
+        require $module.'.pm';
+    };
+    return 1 unless $@;
+    return 0 if $@ =~ /^\b\@INC\b/;
+    die; # propagate $@;
+}
 
-    $installed_rootclass{$rootclass} = 1;
-    # may do checks on ::db and ::st classes later
+
+sub init_rootclass {	# deprecated
     return 1;
 }
 
 
 *internal = \&DBD::Switch::dr::driver;
-#sub internal { return DBD::Switch::dr::driver(@_); }
 
 
 sub available_drivers {
@@ -582,7 +818,9 @@
 	}
 	closedir(DBI::DIR);
     }
-    return sort @drivers;
+
+    # "return sort @drivers" will not DWIM in scalar context.
+    return wantarray ? sort @drivers : @drivers;
 }
 
 sub data_sources {
@@ -664,22 +902,23 @@
 # --- Private Internal Function for Creating New DBI Handles
 
 sub _new_handle {
-    my($class, $parent, $attr, $imp_data) = @_;
+    my ($class, $parent, $attr, $imp_data, $imp_class) = @_;
 
     Carp::croak('Usage: DBI::_new_handle'
 	    .'($class_name, parent_handle, \%attr, $imp_data)'."\n"
 	    .'got: ('.join(", ",$class, $parent, $attr, $imp_data).")\n")
-	unless(@_ == 4	and (!$parent or ref $parent)
-			and ref $attr eq 'HASH');
+	unless (@_ == 5	and (!$parent or ref $parent)
+			and ref $attr eq 'HASH'
+			and $imp_class);
 
-    my $imp_class = $attr->{ImplementorClass} or
-	Carp::croak("_new_handle($class): 'ImplementorClass' attribute not given");
+    $attr->{ImplementorClass} = $imp_class
+	or Carp::croak("_new_handle($class): 'ImplementorClass' attribute not given");
 
     DBI->trace_msg("    New $class (for $imp_class, parent=$parent, id=".($imp_data||'').")\n")
 	if $DBI::dbi_debug >= 3;
 
     # This is how we create a DBI style Object:
-    my(%hash, $i, $h);
+    my (%hash, $i, $h);
     $i = tie    %hash, $class, $attr;  # ref to inner hash (for driver)
     $h = bless \%hash, $class;         # ref to outer hash (for application)
     # The above tie and bless may migrate down into _setup_handle()...
@@ -689,14 +928,16 @@
     return $h unless wantarray;
     ($h, $i);
 }
-# minimum constructors for the tie's (alias to XS version)
+# XXX minimum constructors for the tie's (alias to XS version)
 sub DBI::st::TIEHASH { bless $_[1] => $_[0] };
 *DBI::dr::TIEHASH = \&DBI::st::TIEHASH;
 *DBI::db::TIEHASH = \&DBI::st::TIEHASH;
 
 
 # These three special constructors are called by the drivers
-# The way they are called is likey to change.
+# The way they are called is likely to change.
+
+my $profile;
 
 sub _new_drh {	# called by DBD::<drivername>::driver()
     my ($class, $initial_attr, $imp_data) = @_;
@@ -705,49 +946,53 @@
     # State must be undef to get automatic faking in DBI::var::FETCH
     my ($h_state_store, $h_err_store, $h_errstr_store) = (undef, 0, '');
     my $attr = {
-	'ImplementorClass' => $class,
 	# these attributes get copied down to child handles by default
 	'Handlers'	=> [],
 	'State'		=> \$h_state_store,  # Holder for DBI::state
 	'Err'		=> \$h_err_store,    # Holder for DBI::err
 	'Errstr'	=> \$h_errstr_store, # Holder for DBI::errstr
 	'Debug' 	=> 0,
+	FetchHashKeyName=> 'NAME',
 	%$initial_attr,
-	'Type'=>'dr',
     };
-    _new_handle('DBI::dr', '', $attr, $imp_data);
+    my ($h, $i) = _new_handle('DBI::dr', '', $attr, $imp_data, $class);
+
+    # XXX DBI_PROFILE unless DBI::PurePerl because for some reason
+    # it kills the t/zz_*_pp.t tests (they silently exit early)
+    if ($ENV{DBI_PROFILE} && !$DBI::PurePerl) {
+	# The profile object created here when the first driver is loaded
+	# is shared by all drivers so we end up with just one set of profile
+	# data and thus the 'total time in DBI' is really the true total.
+	if (!$profile) {	# first time
+	    $h->{Profile} = $ENV{DBI_PROFILE};
+	    $profile = $h->{Profile};
+	}
+	else {
+	    $h->{Profile} = $profile;
+	}
+    }
+    return $h unless wantarray;
+    ($h, $i);
 }
 
 sub _new_dbh {	# called by DBD::<drivername>::dr::connect()
-    my ($drh, $initial_attr, $imp_data) = @_;
+    my ($drh, $attr, $imp_data) = @_;
     my $imp_class = $drh->{ImplementorClass}
-	|| Carp::croak("DBI _new_dbh: $drh has no ImplementorClass");
+	or Carp::croak("DBI _new_dbh: $drh has no ImplementorClass");
     substr($imp_class,-4,4) = '::db';
-    my $app_class  = ref $drh;
+    my $app_class = ref $drh;
     substr($app_class,-4,4) = '::db';
-    my $attr = {
-	'ImplementorClass' => $imp_class,
-	%$initial_attr,
-	'Type'   => 'db',
-	'Driver' => (DBI::_handles($drh))[0],
-    };
-    _new_handle($app_class, $drh, $attr, $imp_data);
+    _new_handle($app_class, $drh, $attr||{}, $imp_data, $imp_class);
 }
 
 sub _new_sth {	# called by DBD::<drivername>::db::prepare)
-    my ($dbh, $initial_attr, $imp_data) = @_;
+    my ($dbh, $attr, $imp_data) = @_;
     my $imp_class = $dbh->{ImplementorClass}
-	|| Carp::croak("DBI _new_sth: $dbh has no ImplementorClass");
+	or Carp::croak("DBI _new_sth: $dbh has no ImplementorClass");
     substr($imp_class,-4,4) = '::st';
-    my $app_class  = ref $dbh;
+    my $app_class = ref $dbh;
     substr($app_class,-4,4) = '::st';
-    my $attr = {
-	'ImplementorClass' => $imp_class,
-	%$initial_attr,
-	'Type'     => 'st',
-	'Database' => $dbh,
-    };
-    _new_handle($app_class, $dbh, $attr, $imp_data);
+    _new_handle($app_class, $dbh, $attr, $imp_data, $imp_class);
 }
 
 
@@ -773,8 +1018,7 @@
 	($drh, $inner) = DBI::_new_drh('DBD::Switch::dr', {
 		'Name'    => 'Switch',
 		'Version' => $DBI::VERSION,
-		# the Attribution is defined as a sub as an example
-		'Attribution' => sub { "DBI $DBI::VERSION by Tim Bunce" },
+		'Attribution' => "DBI $DBI::VERSION by Tim Bunce",
 	    }, \$err);
 	Carp::croak("DBD::Switch init failed!") unless ($drh && $inner);
 	return $drh;
@@ -822,6 +1066,8 @@
     sub NEXTKEY  { }
     sub EXISTS   { defined($_[0]->FETCH($_[1])) } # XXX undef?
     sub CLEAR    { Carp::carp "Can't CLEAR $_[0] (DBI)" }
+
+    *dump_handle = \&DBI::dump_handle;
 }
 
 
@@ -864,11 +1110,13 @@
 	my $cache = $drh->FETCH('CachedKids');
 	$drh->STORE('CachedKids', $cache = {}) unless $cache;
 
-	my $key = join "~", $dsn, $user||'', $auth||'', $attr ? %$attr : ();
+	my @attr_keys = $attr ? sort keys %$attr : ();
+	my $key = join "~~", $dsn, $user||'', $auth||'',
+		$attr ? (@attr_keys,@{$attr}{@attr_keys}) : ();
 	my $dbh = $cache->{$key};
-	return $dbh if $dbh && $dbh->FETCH('Active') && $dbh->ping;
+	return $dbh if $dbh && $dbh->FETCH('Active') && eval { $dbh->ping };
 	$dbh = $drh->connect(@_);
-	$cache->{$key} = $dbh;	# replace, even if it failed
+	$cache->{$key} = $dbh;	# replace prev entry, even if connect failed
 	return $dbh;
     }
 
@@ -896,25 +1144,59 @@
     # Drivers are required to implement *::db::DESTROY to encourage tidy-up
     sub DESTROY  { Carp::croak("Driver has not implemented DESTROY for @_") }
 
+    sub quote_identifier {
+	my ($dbh, @id) = @_;
+	my $attr = (@id > 3) ? pop @id : undef;
+
+	my $info = $dbh->{dbi_quote_identifier_cache} ||= [
+	    $dbh->get_info(29)  || '"',	# SQL_IDENTIFIER_QUOTE_CHAR
+	    $dbh->get_info(41)  || '.',	# SQL_CATALOG_NAME_SEPARATOR
+	    $dbh->get_info(114) ||   1,	# SQL_CATALOG_LOCATION
+	];
+
+	my $quote = $info->[0];
+	foreach (@id) {			# quote the elements
+	    next unless defined;
+	    s/$quote/$quote$quote/g;	# escape embedded quotes
+	    $_ = qq{$quote$_$quote};
+	}
+
+	# strip out catalog if present for special handling
+	my $catalog = (@id >= 3) ? shift @id : undef;
+
+	# join the dots, ignoring any null/undef elements (ie schema)
+	my $quoted_id = join '.', grep { defined } @id;
+
+	if ($catalog) {			# add catalog correctly
+	    $quoted_id = ($info->[2] == 2)	# SQL_CL_END
+		    ? $quoted_id . $info->[1] . $catalog
+		    : $catalog   . $info->[1] . $quoted_id;
+	}
+	return $quoted_id;
+    }
+
     sub quote {
 	my ($dbh, $str, $data_type) = @_;
+
 	return "NULL" unless defined $str;
 	unless ($data_type) {
 	    $str =~ s/'/''/g;		# ISO SQL2
 	    return "'$str'";
 	}
-	# Optimise for standard numerics which need no quotes
-	return $str if $data_type == DBI::SQL_INTEGER
-		    || $data_type == DBI::SQL_SMALLINT
-		    || $data_type == DBI::SQL_DECIMAL
-		    || $data_type == DBI::SQL_FLOAT
-		    || $data_type == DBI::SQL_REAL
-		    || $data_type == DBI::SQL_DOUBLE
-		    || $data_type == DBI::SQL_NUMERIC;
-	my $ti = $dbh->type_info($data_type);
-	# XXX needs checking
-	my $lp = $ti ? $ti->{LITERAL_PREFIX} || "" : "'";
-	my $ls = $ti ? $ti->{LITERAL_SUFFIX} || "" : "'";
+
+	my $dbi_literal_quote_cache = $dbh->{'dbi_literal_quote_cache'} ||= [ {} , {} ];
+	my ($prefixes, $suffixes) = @$dbi_literal_quote_cache;
+
+	my $lp = $prefixes->{$data_type};
+	my $ls = $suffixes->{$data_type};
+
+	if ( ! defined $lp || ! defined $ls ) {
+	    my $ti = $dbh->type_info($data_type);
+	    $lp = $prefixes->{$data_type} = $ti ? $ti->{LITERAL_PREFIX} || "" : "'";
+	    $ls = $suffixes->{$data_type} = $ti ? $ti->{LITERAL_SUFFIX} || "" : "'";
+	}
+	return $str unless $lp || $ls; # no quoting required
+
 	# XXX don't know what the standard says about escaping
 	# in the 'general case' (where $lp != "'").
 	# So we just do this and hope:
@@ -933,39 +1215,65 @@
 	($rows == 0) ? "0E0" : $rows;
     }
 
+    sub _do_selectrow {
+	my ($method, $dbh, $stmt, $attr, @bind) = @_;
+	my $sth = ((ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr))
+	    or return;
+	$sth->execute(@bind)
+	    or return;
+	my $row = $sth->$method()
+	    and $sth->finish;
+	return $row;
+    }
+
+    sub selectrow_hashref {  return _do_selectrow('fetchrow_hashref',  @_); }
+
+    # XXX selectrow_array/ref also have C implementations in Driver.xst
+    sub selectrow_arrayref { return _do_selectrow('fetchrow_arrayref', @_); }
     sub selectrow_array {
-	my ($dbh, $stmt, $attr, @bind) = @_;
-	my $sth = (ref $stmt) ? $stmt
-			      : $dbh->prepare($stmt, $attr);
-	return unless $sth;
-	$sth->execute(@bind) || return;
-	my @row = $sth->fetchrow_array;
-	$sth->finish;
-	return $row[0] unless wantarray;
-	return @row;
+	my $row = _do_selectrow('fetchrow_arrayref', @_) or return;
+	return $row->[0] unless wantarray;
+	return @$row;
     }
 
+    # XXX selectall_arrayref also has C implementation in Driver.xst
+    # which fallsback to this if a slice is given
     sub selectall_arrayref {
 	my ($dbh, $stmt, $attr, @bind) = @_;
-	my $sth = (ref $stmt) ? $stmt
-			      : $dbh->prepare($stmt, $attr);
+	my $sth = (ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr)
+	    or return;
+	$sth->execute(@bind) || return;
+	my $slice = $attr->{Slice}; # typically undef, else hash or array ref
+	if (!$slice and $slice=$attr->{Columns}) {
+	    if (ref $slice eq 'ARRAY') { # map col idx to perl array idx
+		$slice = [ @{$attr->{Columns}} ];	# take a copy
+		for (@$slice) { $_-- }
+	    }
+	}
+	return $sth->fetchall_arrayref($slice, $attr->{MaxRows});
+    }
+
+    sub selectall_hashref {
+	my ($dbh, $stmt, $key_field, $attr, @bind) = @_;
+	my $sth = (ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr);
 	return unless $sth;
 	$sth->execute(@bind) || return;
-	my $slice = $attr->{dbi_fetchall_arrayref_attr}; # typically undef
-	return $sth->fetchall_arrayref($slice);
+	return $sth->fetchall_hashref($key_field);
     }
 
     sub selectcol_arrayref {
 	my ($dbh, $stmt, $attr, @bind) = @_;
-	my $sth = (ref $stmt) ? $stmt
-			      : $dbh->prepare($stmt, $attr);
+	my $sth = (ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr);
 	return unless $sth;
 	$sth->execute(@bind) || return;
-	my $column = 1;
-	my $value;
-	$sth->bind_col($column, \$value) || return;
+	my @columns = ($attr->{Columns}) ? @{$attr->{Columns}} : (1);
+	my @values  = (undef) x @columns;
+	my $idx = 0;
+	for (@columns) {
+	    $sth->bind_col($_, \$values[$idx++]) || return;
+	}
 	my @col;
-	push @col, $value while $sth->fetch;
+	push @col, @values while $sth->fetch;
 	return \@col;
     }
 
@@ -976,10 +1284,11 @@
 	# the template must handle clearing the cache themselves.
 	my $cache = $dbh->FETCH('CachedKids');
 	$dbh->STORE('CachedKids', $cache = {}) unless $cache;
-	my $key = ($attr) ? join(" | ", $statement, %$attr) : $statement;
+	my @attr_keys = ($attr) ? sort keys %$attr : ();
+	my $key = ($attr) ? join("~~", $statement, @attr_keys, @{$attr}{@attr_keys}) : $statement;
 	my $sth = $cache->{$key};
 	if ($sth) {
-	    if ($sth->FETCH('Active')) {
+	    if ($sth->FETCH('Active') && ($allow_active||0) != 2) {
 		Carp::carp("prepare_cached($statement) statement handle $sth was still active")
 		    if !$allow_active;
 		$sth->finish;
@@ -996,6 +1305,13 @@
 	"0 but true";	# special kind of true 0
     }
 
+    sub begin_work {
+	my $dbh = shift;
+	return $dbh->DBI::set_err(1, "Already in a transaction")
+		unless $dbh->FETCH('AutoCommit');
+	$dbh->STORE('AutoCommit', 0); # will croak if driver doesn't support it
+	$dbh->STORE('BegunWork',  1); # trigger post commit/rollback action
+    }
     sub commit {
 	shift->_not_impl('commit');
     }
@@ -1011,20 +1327,47 @@
 	shift->_not_impl('table_info');
     }
 
+    sub column_info {
+	shift->_not_impl('column_info');
+    }
+
+    sub primary_key_info {
+	shift->_not_impl('primary_key_info');
+    }
+
+    sub primary_key {
+	my ($dbh, @args) = @_;
+	my $sth = $dbh->primary_key_info(@args) or return;
+	my ($row, @col);
+	push @col, $row->[3] while ($row = $sth->fetch);
+	croak("primary_key method not called in list context")
+		unless wantarray; # leave us some elbow room
+	return @col;
+    }
+
+    sub foreign_key_info {
+	shift->_not_impl('foreign_key_info');
+    }
+
     sub tables {
 	my ($dbh, @args) = @_;
-	my $sth = $dbh->table_info(@args);
-	return unless $sth;
-	my ($row, @tables);
-	while($row = $sth->fetch) {
-	    my $name = $row->[2];
-	    if ($row->[1]) {
-		my $schema = $row->[1];
-		# a little hack
-		my $quote = ($schema eq uc($schema)) ? '' : '"';
-		$name = "$quote$schema$quote.$name"
-	    }
-	    push @tables, $name;
+	my $sth    = $dbh->table_info(@args) or return;
+	my $tables = $sth->fetchall_arrayref or return;
+	my @tables;
+	if ($dbh->get_info(29)) { # SQL_IDENTIFIER_QUOTE_CHAR
+	    @tables = map { $dbh->quote_identifier( @{$_}[0,1,2] ) } @$tables;
+	}
+	else {		# temporary old style hack (yeach)
+	    @tables = map {
+		my $name = $_->[2];
+		if ($_->[1]) {
+		    my $schema = $_->[1];
+		    # a sad hack (mostly for Informix I recall)
+		    my $quote = ($schema eq uc($schema)) ? '' : '"';
+		    $name = "$quote$schema$quote.$name"
+		}
+		$name;
+	    } @$tables;
 	}
 	return @tables;
     }
@@ -1038,11 +1381,21 @@
 
     sub type_info {
 	my ($dbh, $data_type) = @_;
-	my $tia = $dbh->type_info_all;
-	return unless @$tia;
-	my $idx_hash = shift @$tia;
-	my $dt_idx   = $idx_hash->{DATA_TYPE}
-		or Carp::croak("No DATA_TYPE field in type_info_all result");
+	my $idx_hash;
+	my $tia = $dbh->{dbi_type_info_row_cache};
+	if ($tia) {
+	    $idx_hash = $dbh->{dbi_type_info_idx_cache};
+	}
+	else {
+	    my $temp = $dbh->type_info_all;
+	    return unless $temp && @$temp;
+	    $tia      = $dbh->{dbi_type_info_row_cache} = $temp;
+	    $idx_hash = $dbh->{dbi_type_info_idx_cache} = shift @$tia;
+	}
+
+	my $dt_idx   = $idx_hash->{DATA_TYPE} || $idx_hash->{data_type};
+	Carp::croak("type_info_all returned non-standard DATA_TYPE index value ($dt_idx != 1)")
+	    if $dt_idx && $dt_idx != 1;
 
 	# --- simple DATA_TYPE match filter
 	my @ti;
@@ -1058,13 +1411,12 @@
 	}
 
 	# --- format results into list of hash refs
-	my $idx_fields =   keys %$idx_hash;
-	my @idx_names  =   keys %$idx_hash;
+	my $idx_fields = keys %$idx_hash;
+	my @idx_names  = map { uc($_) } keys %$idx_hash;
 	my @idx_values = values %$idx_hash;
+	Carp::croak "type_info_all result has $idx_fields keys but ".(@{$ti[0]})." fields"
+		if @ti && @{$ti[0]} != $idx_fields;
 	my @out = map {
-	    Carp::croak
-		"type_info_all result has $idx_fields keys but ".(@$_)." fields"
-		if @$_ != $idx_fields;
 	    my %h; @h{@idx_names} = @{$_}[ @idx_values ]; \%h;
 	} @ti;
 	return $out[0] unless wantarray;
@@ -1080,44 +1432,153 @@
     sub cancel  { undef }
     sub bind_param { Carp::croak("Can't bind_param, not implement by driver") }
 
-    sub fetchrow_hashref {
+#
+# ********************************************************
+#
+#	BEGIN ARRAY BINDING
+#
+#	Array binding support for drivers which don't support
+#	array binding, but have sufficient interfaces to fake it.
+#	NOTE: mixing scalars and arrayrefs requires using bind_param_array
+#	for *all* params...unless we modify bind_param for the default
+#	case...
+#
+#	2002-Apr-10	D. Arnold
+
+    sub bind_param_array {
+	my $sth = shift;
+	my ($p_id, $value_array, $attr) = @_;
+
+	return $sth->DBI::set_err(1, "Value for parameter $p_id must be a scalar or an arrayref, not a ".ref($value_array))
+	    if defined $value_array and ref $value_array and ref $value_array ne 'ARRAY';
+
+	return $sth->DBI::set_err(1, "Can't use named placeholders for non-driver supported bind_param_array")
+	    unless DBI::looks_like_number($p_id); # because we rely on execute(@ary) here
+
+	# get/create arrayref to hold params
+	my $hash_of_arrays = $sth->{ParamArrays} ||= { };
+
+	if (ref $value_array eq 'ARRAY') {
+	    # check that input has same length as existing
+	    # find first arrayref entry (if any)
+	    foreach (keys %$hash_of_arrays) {
+		my $v = $$hash_of_arrays{$_};
+		next unless ref $v eq 'ARRAY';
+		return $sth->DBI::set_err(1,
+			"Arrayref for parameter $p_id has ".@$value_array." elements"
+			." but parameter $_ has ".@$v)
+		    if @$value_array != @$v;
+	    }
+	}
+
+	# If the bind has attribs then we rely on the driver conforming to
+	# the DBI spec in that a single bind_param() call with those attribs
+	# makes them 'sticky' and apply to all later execute(@values) calls.
+	# Since we only call bind_param() if we're given attribs then
+	# applications using drivers that don't support bind_param can still
+	# use bind_param_array() so long as they don't pass any attribs.
+
+	$$hash_of_arrays{$p_id} = $value_array;
+	return $sth->bind_param($p_id, undef, $attr) 
+		if $attr;
+	1;
+    }
+
+    sub bind_param_inout_array { 
 	my $sth = shift;
-	my $name = shift || 'NAME';
-	# This may be recoded in XS. It could work with fb_av and bind_col.
-	# Probably best to add an AV*fields_hvav to dbih_stc_t and set it up
-	# on the first call to fetchhash which alternate name/value pairs.
-	# This implementation is just rather simple and not very optimised.
-	# Notes for XS implementation: since apps may add entries to the hash
-	# before the next fetch we need to check the key count and, if changed,
-	# scan the hash and delete inappropriate keys.
-	my $row = $sth->fetch or return undef;
-	my %hash;
-	@hash{ @{ $sth->FETCH($name) } } = @$row;
-	return \%hash;
+	# XXX not supported so we just call bind_param_array instead
+	# and then return an error
+	my ($p_num, $value_array, $attr) = @_;
+	$sth->bind_param_array($p_num, $value_array, $attr);
+	return $sth->DBI::set_err(1, "bind_param_inout_array not supported");
     }
 
-    sub fetchall_arrayref {
+    sub execute_array {
 	my $sth = shift;
-	my $slice= shift || [];
-	my $mode = ref $slice;
+	my ($attribs, @array_of_arrays) = @_;
+
+	# get tuple status array or hash attribute (if any)
+	my $tuple_sts = $attribs->{ArrayTupleStatus};
+	return $sth->DBI::set_err(1, "ArrayTupleStatus attribute must be an arrayref")
+		if $tuple_sts and ref $tuple_sts ne 'ARRAY';
+
+	# bind all supplied arrays
+	if (@array_of_arrays) {
+	    $sth->{ParamArrays} = { };	# clear out old params
+	    my $NUM_OF_PARAMS = $sth->FETCH('NUM_OF_PARAMS');
+	    return $sth->DBI::set_err(1, @array_of_arrays." bind values supplied but $NUM_OF_PARAMS expected")
+		    if @array_of_arrays != $NUM_OF_PARAMS;
+	    $sth->bind_param_array($_, $array_of_arrays[$_-1]) or return
+		    foreach (1..@array_of_arrays);
+	}
+
+	# no binds, no args, why did they call us ? just toss it to execute()
+	return $sth->execute
+		unless $sth->{ParamArrays};
+
+	# get the length of a bound array
+	my $len = 1; # in case all are scalars
+	my %hash_of_arrays = %{$sth->{ParamArrays}};
+	foreach (keys(%hash_of_arrays)) {
+	    my $ary = $hash_of_arrays{$_};
+	    $len = @$ary if ref $ary eq 'ARRAY';
+	}
+	my @bind_ids = 1..keys(%hash_of_arrays);
+
+	my ($errcount, $rowcount);
+	my %errstr_cache;
+	@$tuple_sts = () if $tuple_sts; # reset the status array
+	$tuple_sts->[$len-1] = undef;	# presize array
+	for (my $i=0; $i < $len; ++$i) {	# for each tuple
+
+	    my @tuple = map {
+		my $a = $hash_of_arrays{$_};
+		ref($a) ? $a->[$i] : $a
+	    } @bind_ids;
+	    my $rc = $sth->execute(@tuple);
+	    if ($rc) {
+		$rowcount += $tuple_sts->[$i] = $rc;
+		next;
+	    }
+
+	    return unless $tuple_sts;	# return error if no status provided
+	    $errcount++;
+	    my $err = $sth->err;
+	    $tuple_sts->[$i] = [ $err, $errstr_cache{$err} ||= $sth->errstr ];
+	}
+	return ($errcount) ? undef : $rowcount;
+    }
+
+
+    sub fetchall_arrayref {	# ALSO IN Driver.xst
+	my ($sth, $slice, $max_rows) = @_;
+	$max_rows = -1 unless defined $max_rows;
+	my $mode = ref($slice) || 'ARRAY';
 	my @rows;
 	my $row;
 	if ($mode eq 'ARRAY') {
 	    # we copy the array here because fetch (currently) always
 	    # returns the same array ref. XXX
-	    if (@$slice) {
-		push @rows, [ @{$row}[ @$slice] ] while($row = $sth->fetch);
+	    if ($slice && @$slice) {
+		$max_rows = -1 unless defined $max_rows;
+		push @rows, [ @{$row}[ @$slice] ]
+		    while($max_rows-- and $row = $sth->fetch);
+	    }
+	    elsif (defined $max_rows) {
+		$max_rows = -1 unless defined $max_rows;
+		push @rows, [ @$row ]
+		    while($max_rows-- and $row = $sth->fetch);
 	    }
 	    else {
-		push @rows, [ @$row ] while($row = $sth->fetch);
+		push @rows, [ @$row ]          while($row = $sth->fetch);
 	    }
 	}
 	elsif ($mode eq 'HASH') {
-	    my @o_keys = keys %$slice;
-	    if (@o_keys) {
-		my %i_names = map {  (lc($_)=>$_) } @{ $sth->FETCH('NAME') };
-		my @i_keys  = map { $i_names{lc($_)} } @o_keys;
-		while ($row = $sth->fetchrow_hashref) {
+	    $max_rows = -1 unless defined $max_rows;
+	    if (keys %$slice) {
+		my @o_keys = keys %$slice;
+		my @i_keys = map { lc } keys %$slice;
+		while ($max_rows-- and $row = $sth->fetchrow_hashref('NAME_lc')) {
 		    my %hash;
 		    @hash{@o_keys} = @{$row}{@i_keys};
 		    push @rows, \%hash;
@@ -1125,13 +1586,33 @@
 	    }
 	    else {
 		# XXX assumes new ref each fetchhash
-		push @rows, $row while ($row = $sth->fetchrow_hashref);
+		push @rows, $row
+		    while ($max_rows-- and $row = $sth->fetchrow_hashref());
 	    }
 	}
 	else { Carp::croak("fetchall_arrayref($mode) invalid") }
 	return \@rows;
     }
 
+    sub fetchall_hashref {	# XXX may be better to fetchall_arrayref then convert to hashes
+	my ($sth, $key_field) = @_;
+
+	my $hash_key_name = $sth->{FetchHashKeyName};
+	my $names_hash = $sth->FETCH("${hash_key_name}_hash");
+	my $index = $names_hash->{$key_field};	# perl index not column number
+	++$index if defined $index;		# convert to column number
+	$index ||= $key_field if DBI::looks_like_number($key_field) && $key_field>=1;
+	return $sth->DBI::set_err(1, "Field '$key_field' does not exist (not one of @{[keys %$names_hash]})")
+		unless defined $index;
+	my $key_value;
+	$sth->bind_col($index, \$key_value) or return;
+	my %rows;
+	while (my $row = $sth->fetchrow_hashref($hash_key_name)) {
+	    $rows{ $key_value } = $row;
+	}
+	return \%rows;
+    }
+
     *dump_results = \&DBI::dump_results;
 
     sub blob_copy_to_file {	# returns length or undef on error
@@ -1160,7 +1641,7 @@
     sub DESTROY  { Carp::croak("Driver has not implemented DESTROY for @_") }
 }
 
-{   # See install_driver
+unless ($DBI::PurePerl) {   # See install_driver
     { package DBD::_mem::dr; @ISA = qw(DBD::_mem::common);	}
     { package DBD::_mem::db; @ISA = qw(DBD::_mem::common);	}
     { package DBD::_mem::st; @ISA = qw(DBD::_mem::common);	}
@@ -1176,7 +1657,7 @@
 a set of methods, variables, and conventions that provide a consistent
 database interface, independent of the actual database being used.
 
-It is important to remember that the DBI is just an interface. 
+It is important to remember that the DBI is just an interface.
 The DBI is a layer
 of "glue" between an application and one or more database I<driver>
 modules.  It is the driver modules which do most of the real work. The DBI
@@ -1198,7 +1679,7 @@
   `-------'       | |...
                   `-'
 
-The API, or Application Programming Interface, defines the 
+The API, or Application Programming Interface, defines the
 call interface and variables for Perl scripts to use. The API
 is implemented by the Perl DBI extension.
 
@@ -1207,7 +1688,7 @@
 of drivers, error checking and handling, providing default
 implementations for methods, and many other non-database specific duties.
 
-Each driver 
+Each driver
 contains implementations of the DBI methods using the
 private interface functions of the corresponding database engine.  Only authors
 of sophisticated/multi-database applications or generic library
@@ -1235,7 +1716,7 @@
 
 =head2 Outline Usage
 
-To use DBI, 
+To use DBI,
 first you need to load the DBI module:
 
   use DBI;
@@ -1291,7 +1772,7 @@
   $sth = $dbh->prepare("INSERT INTO table(foo,bar,baz) VALUES (?,?,?)");
 
   while(<CSV>) {
-    chop;
+    chomp;
     my ($foo,$bar,$baz) = split /,/;
 	$sth->execute( $foo, $bar, $baz );
   }
@@ -1322,9 +1803,9 @@
 handled without loss of accuracy.  Beware that Perl may not preserve
 the same accuracy when the string is used as a number.
 
-Dates and times are returned as character strings in the native format
-of the corresponding database engine.  Time zone effects are
-database/driver dependent.
+Dates and times are returned as character strings in the current
+default format of the corresponding database engine.  Time zone effects
+are database/driver dependent.
 
 Perl supports binary data in Perl strings, and the DBI will pass binary
 data to and from the driver without change. It is up to the driver
@@ -1389,6 +1870,7 @@
 
 Driver Specific Prefix Registry:
 
+  ad_      DBD::AnyData
   ado_     DBD::ADO
   best_    DBD::BestWins
   csv_     DBD::CSV
@@ -1404,15 +1886,20 @@
   ora_     DBD::Oracle
   pg_      DBD::Pg
   proxy_   DBD::Proxy
+  rdb_     DBD::RDB
+  sapdb_   DBD::SAP_DB
   solid_   DBD::Solid
   syb_     DBD::Sybase
+  sql_     SQL::Statement (used by some drivers)
+  tdat_    DBD::Teradata
   tuber_   DBD::Tuber
+  uni_     DBD::Unify
   xbase_   DBD::XBase
 
 
 =head2 SQL - A Query Language
 
-Most DBI drivers require applications to use a dialect of SQL 
+Most DBI drivers require applications to use a dialect of SQL
 (Structured Query Language) to interact with the database engine.
 The following links provide useful information and further links about
 SQL:
@@ -1438,7 +1925,7 @@
 
 =head2 Placeholders and Bind Values
 
-Some drivers support placeholders and bind values. 
+Some drivers support placeholders and bind values.
 I<Placeholders>, also called parameter markers, are used to indicate
 values in a database statement that will be supplied later,
 before the prepared statement is executed.  For example, an application
@@ -1489,13 +1976,13 @@
 
   my $sth = $dbh->prepare(q{
     INSERT INTO sales (product_code, qty, price) VALUES (?, ?, ?)
-  }) || die $dbh->errstr;
+  }) or die $dbh->errstr;
   while (<>) {
-      chop;
+      chomp;
       my ($product_code, $qty, $price) = split /,/;
-      $sth->execute($product_code, $qty, $price) || die $dbh->errstr;
+      $sth->execute($product_code, $qty, $price) or die $dbh->errstr;
   }
-  $dbh->commit || die $dbh->errstr;
+  $dbh->commit or die $dbh->errstr;
 
 See L</execute> and L</bind_param> for more details.
 
@@ -1507,11 +1994,35 @@
 See also the L</bind_column> method, which is used to associate Perl
 variables with the output columns of a C<SELECT> statement.
 
-=head1 THE DBI CLASS
+=head1 THE DBI PACKAGE AND CLASS
 
 In this section, we cover the DBI class methods, utility functions,
 and the dynamic attributes associated with generic DBI handles.
 
+=head2 DBI Constants
+
+Constants representing the values of the SQL standard types can be
+imported individually by name, or all together by importing the
+special C<:sql_types> tag.
+
+The names and values of all the defined SQL standard types can be
+produced like this:
+
+  foreach (@{ $DBI::EXPORT_TAGS{sql_types} }) {
+    printf "%s=%d\n", $_, &{"DBI::$_"};
+  }
+
+These constants are defined by SQL/CLI, ODBC or both.
+C<SQL_BIGINT> is (currently) omitted, because SQL/CLI and ODBC provide
+conflicting codes.
+
+See the L</type_info>, L</type_info_all>, and L</bind_param> methods
+for possible uses.
+
+Note that just because the DBI defines a named constant for a given
+data type doesn't mean that drivers will support that data type.
+
+
 =head2 DBI Class Methods
 
 The following methods are provided by the DBI class:
@@ -1521,9 +2032,9 @@
 =item C<connect>
 
   $dbh = DBI->connect($data_source, $username, $password)
-            || die $DBI::errstr;
+            or die $DBI::errstr;
   $dbh = DBI->connect($data_source, $username, $password, \%attr)
-            || die $DBI::errstr;
+            or die $DBI::errstr;
 
 Establishes a database connection, or session, to the requested C<$data_source>.
 Returns a database handle object if the connection succeeds. Use
@@ -1573,15 +2084,15 @@
 If C<$username> or C<$password> are undefined (rather than just empty),
 then the DBI will substitute the values of the C<DBI_USER> and C<DBI_PASS>
 environment variables, respectively.  The DBI will warn if the
-environment variables are not defined.  However, the everyday use of 
-these environment 
+environment variables are not defined.  However, the everyday use of
+these environment
 variables is not recommended for security reasons. The mechanism is
 primarily intended to simplify testing.
 
 C<DBI->E<gt>C<connect> automatically installs the driver if it has not been
 installed yet. Driver installation either returns a valid driver
 handle, or it I<dies> with an error message that includes the string
-"C<install_driver>" and the underlying problem. So C<DBI->E<gt>C<connect> 
+"C<install_driver>" and the underlying problem. So C<DBI->E<gt>C<connect>
 will die
 on a driver installation failure and will only return C<undef> on a
 connect failure, in which case C<$DBI::errstr> will hold the error message.
@@ -1592,7 +2103,7 @@
 contents of these fields.  The driver is free to interpret the
 C<$data_source>, C<$username>, and C<$password> fields in any way, and supply
 whatever defaults are appropriate for the engine being accessed.
-(Oracle, for example, uses the ORACLE_SID and TWO_TASK environment 
+(Oracle, for example, uses the ORACLE_SID and TWO_TASK environment
 variables if no C<$data_source> is specified.)
 
 The C<AutoCommit> and C<PrintError> attributes for each connection default to
@@ -1601,7 +2112,7 @@
 rather than rely on the default. Future versions of
 the DBI may issue a warning if C<AutoCommit> is not explicitly defined.
 
-The C<\%attr> parameter can be used to alter the default settings of 
+The C<\%attr> parameter can be used to alter the default settings of
 C<PrintError>, C<RaiseError>, C<AutoCommit>, and other attributes. For example:
 
   $dbh = DBI->connect($data_source, $user, $pass, {
@@ -1617,6 +2128,12 @@
 Individual attributes values specified in this way take precedence over
 any conflicting values specified via the C<\%attr> parameter to C<connect>.
 
+The C<dbi_connect_method> attribute can be used to specify which driver
+method should be called to establish the connection. The only useful
+values are 'connect', 'connect_cached', or some specialized case like
+'Apache::DBI::connect' (which is automatically the default when running
+within Apache).
+
 Where possible, each session (C<$dbh>) is independent from the transactions
 in other sessions. This is useful when you need to hold cursors open
 across transactions--for example, if you use one session for your long lifespan
@@ -1630,19 +2147,19 @@
 
 In this "old-style" form of C<connect>, the C<$data_source> should not start
 with "C<dbi:driver_name:>". (If it does, the embedded driver_name
-will be ignored). Also note that in this older form of C<connect>, 
+will be ignored). Also note that in this older form of C<connect>,
 the C<$dbh->E<gt>C<{AutoCommit}> attribute is I<undefined>, the
 C<$dbh->E<gt>C<{PrintError}> attribute is off, and the old C<DBI_DBNAME>
 environment variable is
-checked if C<DBI_DSN> is not defined. Beware that this "old-style" 
+checked if C<DBI_DSN> is not defined. Beware that this "old-style"
 C<connect> will be withdrawn in a future version of DBI.
 
 =item C<connect_cached> I<NEW>
 
   $dbh = DBI->connect_cached($data_source, $username, $password)
-            || die $DBI::errstr;
+            or die $DBI::errstr;
   $dbh = DBI->connect_cached($data_source, $username, $password, \%attr)
-            || die $DBI::errstr;
+            or die $DBI::errstr;
 
 C<connect_cached> is like L</connect>, except that the database handle
 returned is also
@@ -1680,15 +2197,20 @@
   @ary = DBI->data_sources($driver, \%attr);
 
 Returns a list of all data sources (databases) available via the named
-driver. The driver will be loaded if it hasn't been already. If C<$driver> is empty
-or C<undef>, then the value of the C<DBI_DRIVER> environment variable is
-used.
+driver.  If C<$driver> is empty or C<undef>, then the value of the
+C<DBI_DRIVER> environment variable is used.
+
+The driver will be loaded if it hasn't been already. Note that if the
+driver loading fails then it I<dies> with an error message that
+includes the string "C<install_driver>" and the underlying problem.
 
 Data sources are returned in a form suitable for passing to the
 L</connect> method (that is, they will include the "C<dbi:$driver:>" prefix).
 
 Note that many drivers have no way of knowing what data sources might
-be available for it. These drivers return an empty or incomplete list.
+be available for it. These drivers return an empty or incomplete list
+or may require driver-specific attributes, such as a connected database
+handle, to be supplied.
 
 
 =item C<trace>
@@ -1708,7 +2230,6 @@
   3 - As above, adding some high-level information from the driver
       and some internal information from the DBI.
   4 - As above, adding more detailed information from the driver.
-      Also includes DBI mutex information when using threaded Perl.
   5 and above - As above but with more and more obscure information.
 
 Trace level 1 is best for a simple overview of what's happening.
@@ -1721,11 +2242,13 @@
 in the trace output may be edited and truncated.
 
 Initially trace output is written to C<STDERR>.  If C<$trace_filename> is
-specified, then the file is opened in append mode and all trace
+specified and can be opened in append mode then all trace
 output (including that from other handles) is redirected to that file.
+A warning is generated is the file can't be opened.
 Further calls to C<trace> without a C<$trace_filename> do not alter where
 the trace output is sent. If C<$trace_filename> is undefined, then
 trace output is sent to C<STDERR> and the previous trace file is closed.
+The C<trace> method returns the I<previous> tracelevel.
 
 See also the C<$h->E<gt>C<trace> and C<$h->E<gt>C<trace_msg> methods and the
 L</DEBUGGING> section
@@ -1779,6 +2302,23 @@
 Returns false for each element that does not look like a number.
 Returns C<undef> for each element that is undefined or empty.
 
+=item C<hash>
+
+  $hash_value = DBI::hash($buffer, $type);
+
+Return a 32-bit integer 'hash' value corresponding to the contents of $buffer.
+The $type parameter selects which kind of hash algorithm should be used.
+
+For the technically curious, type 0 (which is the default if $type
+isn't specified) is based on the Perl 5.1 hash except that the value
+is forced to be negative (for obscure historical reasons).
+Type 1 is the better "Fowler / Noll / Vo" (FNV) hash. See
+http://www.isthe.com/chongo/tech/comp/fnv/ for more information.
+Both types are implemented in C and are very fast.
+
+This function doesn't have much to do with databases, except that
+it can be handy to store hash values in a database.
+
 =back
 
 
@@ -1813,9 +2353,15 @@
 
 =item C<$DBI::rows>
 
-Equivalent to C<$h->E<gt>C<rows>. Please refer to the documentation 
+Equivalent to C<$h->E<gt>C<rows>. Please refer to the documentation
 for the L</rows> method.
 
+=item C<$DBI::lasth>
+
+Returns the DBI object handle used for the most recent DBI method call.
+If the last DBI method call was a DESTROY then $DBI::lasth will return
+the handle of the parent of the destroyed handle, if there is one.
+
 =back
 
 
@@ -1857,10 +2403,34 @@
   $str = $h->state;
 
 Returns an error code in the standard SQLSTATE five character format.
-Note that the specific success code C<00000> is translated to C<0>
+Note that the specific success code C<00000> is translated to 'C<>'
 (false). If the driver does not support SQLSTATE (and most don't),
 then state will return C<S1000> (General Error) for all errors.
 
+The driver is free to return any value via C<state>, e.g., warning
+codes, even if it has not declared an error by returning a true value
+via the L</err> method described above.
+
+=item C<set_err> I<NEW>
+
+  $rv = $h->set_err($err, $errstr);
+  $rv = $h->set_err($err, $errstr, $state, $method);
+  $rv = $h->set_err($err, $errstr, $state, $method, $rv);
+
+Set the C<err>, C<errstr>, and C<state> values for the handle.
+This will trigger the normal DBI error handling mechanisms,
+such as C<RaiseError> and C<HandleError>, if they are enabled.
+This method is typically only used by DBI drivers and DBI subclasses.
+
+The $method parameter provides an alternate method name, instead
+of the fairly unhelpful 'C<set_err>', for the
+C<RaiseError>/C<PrintError> error string.
+
+The C<set_err> method normally returns undef.  The $rv parameter
+provides an alternate return value. The C<HandleError> subroutine
+can access and alter this value.
+
+
 =item C<trace>
 
   $h->trace($trace_level);
@@ -1887,8 +2457,8 @@
 the trace output is sent. If C<$trace_filename> is undefined, then
 trace output is sent to C<STDERR> and the previous trace file is closed.
 
-See also the C<DBI->E<gt>C<trace> method and L</DEBUGGING> for information
-about the C<DBI_TRACE> environment variable.
+See also the C<DBI->E<gt>C<trace> method, the C<$h->E<gt>C<{TraceLevel}> attribute,
+and L</DEBUGGING> for information about the C<DBI_TRACE> environment variable.
 
 
 =item C<trace_msg>
@@ -1906,16 +2476,21 @@
 
 =item C<func>
 
-  $h->func(@func_arguments, $func_name);
+  $h->func(@func_arguments, $func_name) or die ...;
 
 The C<func> method can be used to call private non-standard and
 non-portable methods implemented by the driver. Note that the function
 name is given as the last argument.
 
-This method is not directly related to calling stored procedures.
+It's also important to note that the func() method does not clear
+a previous error ($DBI::err etc.) and it does not trigger automatic
+error detection (RaiseError etc.) so you must check the return
+status and/or $h->err to detect errors.
+
+(This method is not directly related to calling stored procedures.
 Calling stored procedures is currently not defined by the DBI.
 Some drivers, such as DBD::Oracle, support it in non-portable ways.
-See driver documentation for more details.
+See driver documentation for more details.)
 
 =back
 
@@ -1992,6 +2567,10 @@
 For a database handle, this attribute does not disable an I<explicit>
 call to the disconnect method, only the implicit call from DESTROY.
 
+The default value, false, means that a handle will be automatically
+destroyed when it passes out of scope.  A true value disables automatic
+destruction. (Think of the name as meaning 'inactive the DESTROY method'.)
+
 This attribute is specifically designed for use in Unix applications
 that "fork" child processes. Either the parent or the child process,
 but not both, should set C<InactiveDestroy> on all their shared handles.
@@ -2024,9 +2603,14 @@
 
   DBD::Oracle::db prepare failed: ... error text here ...
 
-If C<PrintError> is also on, then the C<PrintError> is done before the
-C<RaiseError> unless no C<__DIE__> handler has been defined, in which case
-C<PrintError> is skipped since the C<die> will print the message.
+If you turn C<RaiseError> on then you'd normally turn C<PrintError> off.
+If C<PrintError> is also on, then the C<PrintError> is done first (naturally).
+
+Typically C<RaiseError> is used in conjunction with C<eval { ... }>
+to catch the exception that's been thrown and followed by an
+C<if ($@) { ... }> block to handle the caught exception. In that eval
+block the $DBI::lasth variable can be useful for diagnosis and reporting.
+For example, $DBI::lasth->{Type} and $DBI::lasth->{Statement}.
 
 If you want to temporarily turn C<RaiseError> off (inside a library function
 that is likely to fail, for example), the recommended way is like this:
@@ -2041,9 +2625,105 @@
 The same logic applies to other attributes, including C<PrintError>.
 
 Sadly, this doesn't work for Perl versions up to and including 5.004_04.
+Even more sadly, for Perl 5.5 and 5.6.0 it does work but leaks memory!
 For backwards compatibility, you could just use C<eval { ... }> instead.
 
 
+=item C<HandleError> (code ref, inherited)
+
+This attribute can be used to provide your own alternative behaviour
+in case of errors. If set to a reference to a subroutine then that
+subroutine is called when an error is detected (at the same point that
+C<RaiseError> and C<PrintError> are handled).
+
+The subroutine is called with three parameters: the error message
+string that C<RaiseError> and C<PrintError> would use,
+the DBI handle being used, and the first value being returned by
+the method that failed (typically undef).
+
+If the subroutine returns a false value then the C<RaiseError>
+and/or C<PrintError> attributes are checked and acted upon as normal.
+
+For example, to C<die> with a full stack trace for any error:
+
+  use Carp;
+  $h->{HandleError} = sub { confess(shift) };
+
+Or to turn errors into exceptions:
+
+  use Exception; # or your own favourite exception module
+  $h->{HandleError} = sub { Exception->new('DBI')->raise($_[0]) };
+
+It is possible to 'stack' multiple HandleError handlers by using
+closures:
+
+  sub your_subroutine {
+    my $previous_handler = $h->{HandleError};
+    $h->{HandleError} = sub {
+      return 1 if $previous_handler and &$previous_handler(@_);
+      ... your code here ...
+    }
+  }
+
+Using a C<my> inside a subroutine to store the previous C<HandleError>
+value is important.  See L<perlsub> and L<perlref> for more information
+about I<closures>.
+
+It is possible for C<HandleError> to alter the error message that
+will be used by C<RaiseError> and C<PrintError> if it returns false.
+It can do that by altering the value of $_[0]. This example appends
+a stack trace to all errors and, unlike the previous example using
+Carp::confess, this will work C<PrintError> as well as C<RaiseError>:
+
+  $h->{HandleError} = sub { $_[0]=Carp::longmess($_[0]); 0; };
+
+It is also possible for C<HandleError> to hide an error, to a limited
+degree, by using L</set_err> to reset $DBI::err and $DBI::errstr,
+and altering the return value of the failed method. For example:
+
+  $h->{HandleError} = sub {
+    return 0 unless $_[0] =~ /^\S+ fetchrow_arrayref failed:/;
+    return 0 unless $_[1]->err == 1234; # the error to 'hide'
+    $h->set_err(0,"");	# turn off the error
+    $_[2] = [ ... ];	# supply alternative return value
+    return 1;
+  };
+
+This only works for methods which return a single value and is hard
+to make reliable (avoiding infinite loops, for example) and so isn't
+recommended for general use!  If you find a I<good> use for it then
+please let me know.
+
+
+=item C<ShowErrorStatement> (boolean, inherited) I<NEW>
+
+This attribute can be used to cause the relevant Statement text to be
+appended to the error messages generated by the C<RaiseError> and
+C<PrintError> attributes. Only applies to errors on statement handles
+plus the prepare(), do(), and the various C<select*()> database handle methods.
+(The exact format of the appended text is subject to change.)
+
+If C<$h-E<gt>{ParamValues}> returns a hash reference of parameter
+(placeholder) values then those are formatted and appened to the
+end of the Statement text in the error message.
+
+
+=item C<TraceLevel> (integer, inherited) I<NEW>
+
+This attribute can be used as an alternative to the L</trace> method
+to set the DBI trace level for a specific handle.
+
+
+=item C<FetchHashKeyName> (string, inherited)
+
+This attribute is used to specify which attribute name the
+fetchrow_hashref() method should use to get the field names for the
+hash keys. For historical reasons it defaults to 'C<NAME>' but it
+is recommended to set it to 'C<NAME_lc>' or 'C<NAME_uc>' according
+to your preference. It can only be set for driver and database tables.
+For statement handles the value is frozen when prepare() is called.
+
+
 =item C<ChopBlanks> (boolean, inherited)
 
 This attribute can be used to control the trimming of trailing space
@@ -2125,16 +2805,32 @@
 applications unless you take great care now. If you use DBI Taint mode,
 please report your experience and any suggestions for changes.
 
+=item C<Profile> (inherited)
 
-=item C<private_*>
+Enable collection and reporting of method call timing statistics.
+See the L<DBI::Profile> module documentation for I<much> more detail.
+
+=item C<private_your_module_name_*>
 
 The DBI provides a way to store extra information in a DBI handle as
 "private" attributes. The DBI will allow you to store and retreive any
-attribute which has a name starting with "C<private_>". It is strongly
-recommended that you use just I<one> private attribute (e.g., use a
-hash ref) and give it a long and unambiguous name that includes the
-module or application name that the attribute relates to (e.g.,
-"C<private_YourFullModuleName_thingy>").
+attribute which has a name starting with "C<private_>".
+
+It is I<strongly> recommended that you use just I<one> private
+attribute (e.g., use a hash ref) I<and> give it a long and unambiguous
+name that includes the module or application name that the attribute
+relates to (e.g., "C<private_YourFullModuleName_thingy>").
+
+Because of the way the Perl tie mechanism works you cannot reliably
+use the C<||=> operator directly to initialise the attribute, like this:
+
+  my $foo = $dbh->{private_yourmodname_foo} ||= { ... }; # WRONG
+
+you should use a two step approach like this:
+
+  my $foo = $dbh->{private_yourmodname_foo};
+  $foo ||= $dbh->{private_yourmodname_foo} = { ... };
+
 
 =back
 
@@ -2152,13 +2848,13 @@
 
 =item C<do>
 
-  $rc  = $dbh->do($statement)           || die $dbh->errstr;
-  $rc  = $dbh->do($statement, \%attr)   || die $dbh->errstr;
-  $rv  = $dbh->do($statement, \%attr, @bind_values) || ...
+  $rows = $dbh->do($statement)           or die $dbh->errstr;
+  $rows = $dbh->do($statement, \%attr)   or die $dbh->errstr;
+  $rows = $dbh->do($statement, \%attr, @bind_values) or die ...
 
 Prepare and execute a single statement. Returns the number of rows
-affected or C<undef> on error. A return value of C<-1> means the 
-number of rows is not known or is not available.
+affected or C<undef> on error. A return value of C<-1> means the
+number of rows is not known, not applicable, or not available.
 
 This method is typically most useful for I<non>-C<SELECT> statements that
 either cannot be prepared in advance (due to a limitation of the
@@ -2181,7 +2877,7 @@
   my $rows_deleted = $dbh->do(q{
       DELETE FROM table
       WHERE status = ?
-  }, undef, 'DONE') || die $dbh->errstr;
+  }, undef, 'DONE') or die $dbh->errstr;
 
 Using placeholders and C<@bind_values> with the C<do> method can be
 useful because it avoids the need to correctly quote any variables
@@ -2202,19 +2898,51 @@
 
 This utility method combines L</prepare>, L</execute> and
 L</fetchrow_array> into a single call. If called in a list context, it
-returns the first row of data from the statement. If called in a scalar
-context, it returns the first field of the first row. The C<$statement>
+returns the first row of data from the statement.  The C<$statement>
 parameter can be a previously prepared statement handle, in which case
 the C<prepare> is skipped.
 
 If any method fails, and L</RaiseError> is not set, C<selectrow_array>
 will return an empty list.
 
-In a scalar context, C<selectrow_array> returns the value of the first
-field. An C<undef> is returned if there are no matching rows or an error
-occurred. Since that C<undef> can't be distinguished from an C<undef> returned
-because the first field value was NULL, calling C<selectrow_array> in
-a scalar context should be used with caution.
+If called in a scalar context for a statement handle that has more
+than one column, it is undefined whether the driver will return
+the value of the first column or the last. So don't do that.
+Also, in a scalar context, an C<undef> is returned if there are no
+more rows or if an error occurred. That C<undef> can't be distinguished
+from an C<undef> returned because the first field value was NULL.
+For these reasons you should exercise some caution if you use
+C<selectrow_array> in a scalar context.
+
+
+=item C<selectrow_arrayref>
+
+  $ary_ref = $dbh->selectrow_arrayref($statement);
+  $ary_ref = $dbh->selectrow_arrayref($statement, \%attr);
+  $ary_ref = $dbh->selectrow_arrayref($statement, \%attr, @bind_values);
+
+This utility method combines L</prepare>, L</execute> and
+L</fetchrow_arrayref> into a single call. It returns the first row of
+data from the statement.  The C<$statement> parameter can be a previously
+prepared statement handle, in which case the C<prepare> is skipped.
+
+If any method fails, and L</RaiseError> is not set, C<selectrow_array>
+will return undef.
+
+
+=item C<selectrow_hashref>
+
+  $hash_ref = $dbh->selectrow_hashref($statement);
+  $hash_ref = $dbh->selectrow_hashref($statement, \%attr);
+  $hash_ref = $dbh->selectrow_hashref($statement, \%attr, @bind_values);
+
+This utility method combines L</prepare>, L</execute> and
+L</fetchrow_hashref> into a single call. It returns the first row of
+data from the statement.  The C<$statement> parameter can be a previously
+prepared statement handle, in which case the C<prepare> is skipped.
+
+If any method fails, and L</RaiseError> is not set, C<selectrow_hashref>
+will return undef.
 
 
 =item C<selectall_arrayref>
@@ -2231,10 +2959,47 @@
 in which case the C<prepare> is skipped. This is recommended if the
 statement is going to be executed many times.
 
-If any method except C<fetch> fails, and L</RaiseError> is not set,
-C<selectall_arrayref> will return C<undef>.  If C<fetch> fails and
+If L</RaiseError> is not set and any method except C<fetchall_arrayref>
+fails then C<selectall_arrayref> will return C<undef>; if
+C<fetchall_arrayref> fails then it will return with whatever data
+has been fetched thus far. You should check C<$sth->E<gt>C<err>
+afterwards (or use the C<RaiseError> attribute) to discover if the data is
+complete or was truncated due to an error.
+
+The L</fetchall_arrayref> method called by C<selectall_arrayref>
+supports a $max_rows parameter. You can specify a value for $max_rows
+by including a 'C<MaxRows>' attribute in \%attr.
+
+The L</fetchall_arrayref> method called by C<selectall_arrayref>
+also supports a $slice parameter. You can specify a value for $slice by
+including a 'C<Slice>' or 'C<Columns>' attribute in \%attr. The only
+difference between the two is that if C<Slice> is not defined and
+C<Columns> is an array ref, then the array is assumed to contain column
+index values (which count from 1), rather than perl array index values.
+In which case the array is copied and each value decremented before
+passing to C</fetchall_arrayref>.
+
+
+=item C<selectall_hashref>
+
+  $hash_ref = $dbh->selectall_hashref($statement, $key_field);
+  $hash_ref = $dbh->selectall_hashref($statement, $key_field, \%attr);
+  $hash_ref = $dbh->selectall_hashref($statement, $key_field, \%attr, @bind_values);
+
+This utility method combines L</prepare>, L</execute> and
+L</fetchall_hashref> into a single call. It returns a reference to a
+hash containing one entry for each row. The key for each row entry is
+specified by $key_field. The value is a reference to a hash returned by
+C<fetchrow_hashref>.
+
+The C<$statement> parameter can be a previously prepared statement handle,
+in which case the C<prepare> is skipped. This is recommended if the
+statement is going to be executed many times.
+
+If any method except C<fetchrow_hashref> fails, and L</RaiseError> is not set,
+C<selectall_hashref> will return C<undef>.  If C<fetchrow_hashref> fails and
 L</RaiseError> is not set, then it will return with whatever data it
-has fetched thus far.
+has fetched thus far. $DBI::err should be checked to catch that.
 
 
 =item C<selectcol_arrayref>
@@ -2252,15 +3017,26 @@
 statement is going to be executed many times.
 
 If any method except C<fetch> fails, and L</RaiseError> is not set,
-C<selectall_arrayref> will return C<undef>.  If C<fetch> fails and
+C<selectcol_arrayref> will return C<undef>.  If C<fetch> fails and
 L</RaiseError> is not set, then it will return with whatever data it
-has fetched thus far.
+has fetched thus far. $DBI::err should be checked to catch that.
+
+The C<selectcol_arrayref> method defaults to pushing a single column
+value (the first) from each row into the result array. However, it can
+also push another column, or even multiple columns per row, into the
+result array. This behaviour can be specified via a 'C<Columns>'
+attribute which must be a ref to an array containing the column number
+or numbers to use. For example:
+
+  # get array of id and name pairs:
+  my $ary_ref = $dbh->selectcol_arrayref("select id, name from table", { Columns=>[1,2] });
+  my %hash = @$ary_ref; # build hash from key-value pairs so $hash{$id} => name
 
 
 =item C<prepare>
 
-  $sth = $dbh->prepare($statement)          || die $dbh->errstr;
-  $sth = $dbh->prepare($statement, \%attr)  || die $dbh->errstr;
+  $sth = $dbh->prepare($statement)          or die $dbh->errstr;
+  $sth = $dbh->prepare($statement, \%attr)  or die $dbh->errstr;
 
 Prepares a single statement for later execution by the database
 engine and returns a reference to a statement handle object.
@@ -2304,29 +3080,81 @@
 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's an example of one possible use of C<prepare_cached>:
-
-  while ( ($field, $value) = each %search_fields ) {
-      push @sql,   "$field = ?";
-      push @values, $value;
+Here are some examples of C<prepare_cached>:
+
+  sub insert_hash {
+    my ($table, $field_values) = @_;
+    my @fields = sort keys %$field_values; # sort required
+    my @values = @{$field_values}{@fields};
+    my $sql = sprintf "insert into %s (%s) values (%s)",
+	$table, join(",", @fields), join(",", ("?")x@fields);
+    my $sth = $dbh->prepare_cached($sql);
+    return $sth->execute(@values);
+  }
+
+  sub search_hash {
+    my ($table, $field_values) = @_;
+    my @fields = sort keys %$field_values; # sort required
+    my @values = @{$field_values}{@fields};
+    my $qualifier = "";
+    $qualifier = "where ".join(" and ", map { "$_=?" } @fields) if @fields;
+    $sth = $dbh->prepare_cached("SELECT * FROM $table $qualifier");
+    return $dbh->selectall_arrayref($sth, {}, @values);
+  }
+
+I<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 WHERE bar=?');
+  $sth->execute($bar);
+  while (my $data = $sth->fetchrow_hashref) {
+    my $sth2 = $dbh->prepare_cached('SELECT * FROM foo WHERE bar=?');
+    $sth2->execute($data->{bar});
+    while (my $data2 = $sth2->fetchrow_arrayref) {
+      do_stuff(...);
+    }
   }
-  $qualifier = "";
-  $qualifier = "where ".join(" and ", @sql) if @sql;
-  $sth = $dbh->prepare_cached("SELECT * FROM table $qualifier");
-  $sth->execute(@values);
+
+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.
+Typically the the inner fetch loop will work normally, fetching all
+the records and terminating when there are no more, but now $sth
+is the same as $sth2 the outer fetch loop will also terminate.
+
+The C<$allow_active> parameter lets you adjust DBI's behavior when
+prepare_cached is returning a statement handle that is still active.
+There are three settings:
+
+=over 4
+
+B<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.
+
+B<1>: C<finish> will be called on the statement handle, but the
+warning is suppressed.
+
+B<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
+
+Because the cache used by prepare_cached() is keyed by all the
+parameters, including any attributes passed, you can also avoid
+this issue by doing something like:
+
+  my $sth = $dbh->prepare_cached("...", { dbi_dummy => __FILE__.__LINE__ });
+
+which will ensure that prepare_cached only returns statements cached
+by that line of code in that source file. 
 
 
 =item C<commit>
 
-  $rc  = $dbh->commit     || die $dbh->errstr;
+  $rc  = $dbh->commit     or die $dbh->errstr;
 
 Commit (make permanent) the most recent series of database changes
 if the database supports transactions and AutoCommit is off.
@@ -2338,7 +3166,7 @@
 
 =item C<rollback>
 
-  $rc  = $dbh->rollback   || die $dbh->errstr;
+  $rc  = $dbh->rollback   or die $dbh->errstr;
 
 Rollback (undo) the most recent series of uncommitted database
 changes if the database supports transactions and AutoCommit is off.
@@ -2348,10 +3176,25 @@
 
 See also L</Transactions> in the L</FURTHER INFORMATION> section below.
 
+=item C<begin_work>
+
+  $rc  = $dbh->begin_work   or die $dbh->errstr;
+
+Enable transactions (by turning C<AutoCommit> off) until the next call
+to C<commit> or C<rollback>. After the next C<commit> or C<rollback>,
+C<AutoCommit> will automatically be turned on again.
+
+If C<AutoCommit> is already off when C<begin_work> is called then
+it does nothing except return an error. If the driver does not support
+transactions then when C<begin_work> attempts to set C<AutoCommit> off
+the driver will trigger a fatal error.
+
+See also L</Transactions> in the L</FURTHER INFORMATION> section below.
+
 
 =item C<disconnect>
 
-  $rc = $dbh->disconnect  || warn $dbh->errstr;
+  $rc = $dbh->disconnect  or warn $dbh->errstr;
 
 Disconnects the database from the database handle. C<disconnect> is typically only used
 before exiting the program. The handle is of little use after disconnecting.
@@ -2375,11 +3218,11 @@
 you disconnect, then you should explicitly call L</commit> or L</rollback>
 before disconnecting.
 
-If you disconnect from a database while you still have active statement
-handles, you will get a warning. The statement handles should either be
-cleared (destroyed) before disconnecting, or the C<finish> method 
-should be called on
-each one.
+If you disconnect from a database while you still have active
+statement handles (e.g., SELECT statement handles that may have
+more data to fetch), you will get a warning. The warning may indicate
+that a fetch loop terminated early, perhaps due to an uncaught error.
+To avoid the warning call the C<finish> method on the active handles.
 
 
 =item C<ping>
@@ -2397,21 +3240,107 @@
 just the default. Drivers should override this method with one that
 does the right thing for their type of database.
 
-Few applications would have use for this method. See the specialized
+Few applications would have direct use for this method. See the specialized
 Apache::DBI module for one example usage.
 
 
+=item C<get_info> I<NEW>
+
+B<Warning:> This method is experimental and may change.
+
+  $value = $dbh->get_info( $info_type );
+
+Returns information about the implementation, i.e. driver and data
+source capabilities, restrictions etc. It returns C<undef> for
+unknown or unimplemented information types. For example:
+
+  $database_version  = $dbh->get_info(  18 ); # SQL_DBMS_VER
+  $max_select_tables = $dbh->get_info( 106 ); # SQL_MAXIMUM_TABLES_IN_SELECT
+
+See L</"Standards Reference Information"> for more detailed information
+about the information types and their meanings and possible return values.
+
+The DBI curently doesn't provide a name to number mapping for the
+information type codes or the results. Applications are expected to use
+the integer values directly, with the name in a comment, or define
+their own named values using something like the L<constant> pragma.
+
+Because some DBI methods make use of get_info(), drivers are strongly
+encouraged to support I<at least> the following very minimal set
+of information types to ensure the DBI itself works properly:
+
+ Type  Name                        Example A     Example B
+ ----  --------------------------  ------------  ------------
+   17  SQL_DBMS_NAME               'ACCESS'      'Oracle'
+   18  SQL_DBMS_VER                '03.50.0000'  '08.01.0721'
+   29  SQL_IDENTIFIER_QUOTE_CHAR   '`'           '"'
+   41  SQL_CATALOG_NAME_SEPARATOR  '.'           '@'
+  114  SQL_CATALOG_LOCATION        1             2
+
+
 =item C<table_info> I<NEW>
 
 B<Warning:> This method is experimental and may change.
 
-  $sth = $dbh->table_info;
+  $sth = $dbh->table_info( $catalog, $schema, $table, $type );
+  $sth = $dbh->table_info( $catalog, $schema, $table, $type, \%attr );
+  $sth = $dbh->table_info( \%attr ); # old style
 
 Returns an active statement handle that can be used to fetch
 information about tables and views that exist in the database.
 
-The handle has at least the following fields in the order show
-below. Other fields, after these, may also be present.
+The old style interface passes all the parameters as a reference
+to an attribute hash with some or all of the following attributes:
+
+  %attr = (
+       TABLE_CAT   => $catalog  # String value of the catalog name
+     , TABLE_SCHEM => $schema   # String value of the schema name
+     , TABLE_NAME  => $table    # String value of the table name
+     , TABLE_TYPE  => $type     # String value of the table type(s)
+  );
+
+The old style interface is deprecated and will be removed in a future version.
+
+The support for the selection criteria is driver specific. If the
+driver doesn't support one or more of them then you may get back more
+than you asked for and can do the filtering yourself.
+
+The arguments $catalog, $schema and $table may accept search patterns
+according to the database/driver, for example: $table = '%FOO%';
+Remember that the underscore character ('C<_>') is a search pattern
+that means match any character, so 'FOO_%' is the same as 'FOO%'
+and 'FOO_BAR%' will match names like 'FOO1BAR'.
+
+The value of $type is a comma-separated list of one or more types of
+tables to be returned in the result set. Each value may optionally be
+quoted, e.g.:
+
+  $type = "TABLE";
+  $type = "'TABLE','VIEW'";
+
+In addition the following special cases may also be supported by some drivers:
+
+=over 4
+
+=item *
+If the value of $catalog is '%' and $schema and $table name
+are empty strings, the result set contains a list of catalog names.
+For example:
+
+  $sth = $dbh->table_info('%', '', '');
+
+=item *
+If the value of $schema is '%' and $catalog and $table are empty
+strings, the result set contains a list of schema names.
+
+=item *
+If the value of $type is '%' and $catalog, $schema, and $table are all
+empty strings, the result set contains a list of table types.
+
+=back
+
+The statement handle returned has at least the following fields in the
+order show below. Other fields, after these, may also be present.
 
 B<TABLE_CAT>: Table catalog identifier. This field is NULL (C<undef>) if not
 applicable to the data source, which is usually the case. This field
@@ -2432,38 +3361,315 @@
 
 Note that C<table_info> might not return records for all tables.
 Applications can use any valid table regardless of whether it's
-returned by C<table_info>.  See also L</tables>.
+returned by C<table_info>.
 
-For more detailed information about the fields and their meanings,
-you can refer to:
+See also L</tables>, L</"Catalog Methods"> and
+L</"Standards Reference Information">.
 
-  http://msdn.microsoft.com/library/psdk/dasdk/odch6wqb.htm
+=item C<column_info> I<NEW>
 
-If that URL ceases to work then use the MSDN search facility at:
+B<Warning:> This method is experimental and may change.
 
-  http://search.microsoft.com/us/dev/
+  $sth = $dbh->column_info( $catalog, $schema, $table, $column );
+
+Returns an active statement handle that can be used to fetch
+information about columns in specified tables.
+
+The arguments $schema, $table and $column may accept search patterns
+according to the database/driver, for example: $table = '%FOO%';
+
+Note: The support for the selection criteria is driver specific. If the
+driver doesn't support one or more of them then you may get back more
+than you asked for and can do the filtering yourself.
+
+The statement handle returned has at least the following fields in the
+order shown below. Other fields, after these, may also be present.
+
+B<TABLE_CAT>: The catalog identifier.
+This field is NULL (C<undef>) if not applicable to the data source,
+which is often the case.  This field is empty if not applicable to the
+table.
+
+B<TABLE_SCHEM>: The schema identifier.
+This field is NULL (C<undef>) if not applicable to the data source,
+and empty if not applicable to the table.
+
+B<TABLE_NAME>: The table identifier.
+Note: A driver may provide column metadata not only for base tables, but
+also for derived objects like SYNONYMS etc.
+
+B<COLUMN_NAME>: The column identifier.
+
+B<DATA_TYPE>: The concise data type code.
+
+B<TYPE_NAME>: A data source dependent data type name.
+
+B<COLUMN_SIZE>: The column size.
+This is the maximum length in characters for character data types,
+the number of digits or bits for numeric data types or the length
+in the representation of temporal types.
+See the relevant specifications for detailed information.
+
+B<BUFFER_LENGTH>: The length in bytes of transferred data.
+
+B<DECIMAL_DIGITS>: The total number of significant digits to the right of
+the decimal point.
+
+B<NUM_PREC_RADIX>: The radix for numeric precision.
+The value is 10 or 2 for numeric data types and NULL (C<undef>) if not
+applicable.
+
+B<NULLABLE>: Indicates if a column can accept NULLs.
+The following values are defined:
+
+  SQL_NO_NULLS          0
+  SQL_NULLABLE          1
+  SQL_NULLABLE_UNKNOWN  2
+
+B<REMARKS>: A description of the column.
+
+B<COLUMN_DEF>: The default value of the column.
+
+B<SQL_DATA_TYPE>: The SQL data type.
+
+B<SQL_DATETIME_SUB>: The subtype code for datetime and interval data types.
+
+B<CHAR_OCTET_LENGTH>: The maximum length in bytes of a character or binary
+data type column.
+
+B<ORDINAL_POSITION>: The column sequence number (starting with 1).
+
+B<IS_NULLABLE>: Indicates if the column can accept NULLs.
+Possible values are: 'NO', 'YES' and ''.
+
+SQL/CLI defines the following additional columns:
+
+  CHAR_SET_CAT
+  CHAR_SET_SCHEM
+  CHAR_SET_NAME
+  COLLATION_CAT
+  COLLATION_SCHEM
+  COLLATION_NAME
+  UDT_CAT
+  UDT_SCHEM
+  UDT_NAME
+  DOMAIN_CAT
+  DOMAIN_SCHEM
+  DOMAIN_NAME
+  SCOPE_CAT
+  SCOPE_SCHEM
+  SCOPE_NAME
+  MAX_CARDINALITY
+  DTD_IDENTIFIER
+  IS_SELF_REF
+
+Drivers capable of supplying any of those values should do so in
+the corresponding column and supply undef values for the others.
+
+Drivers wishing to provide extra database/driver specific information
+should do so in extra columns beyond all those listed above, and
+use lowercase field names with the driver-specific prefix (i.e.,
+'ora_...'). Applications accessing such fields should do so by name
+and not by column number.
+
+The result set is ordered by TABLE_CAT, TABLE_SCHEM, TABLE_NAME
+and ORDINAL_POSITION.
+
+Note: There is some overlap with statement attributes (in perl) and
+SQLDescribeCol (in ODBC). However, SQLColumns provides more metadata.
+
+See also L</"Catalog Methods"> and L</"Standards Reference Information">.
+
+=item C<primary_key_info> I<NEW>
+
+B<Warning:> This method is experimental and may change.
+
+  $sth = $dbh->primary_key_info( $catalog, $schema, $table );
+
+Returns an active statement handle that can be used to fetch information
+about columns that make up the primary key for a table.
+The arguments don't accept search patterns (unlike table_info()).
+
+For example:
+
+  $sth = $dbh->primary_key_info( undef, $user, 'foo' );
+  $data = $sth->fetchall_arrayref;
+
+Note: The support for the selection criteria, such as $catalog, is
+driver specific.  If the driver doesn't support catalogs and/or
+schemas, it may ignore these criteria.
+
+The statement handle returned has at least the following fields in the
+order shown below. Other fields, after these, may also be present.
+
+B<TABLE_CAT>: The catalog identifier.
+This field is NULL (C<undef>) if not applicable to the data source,
+which is often the case.  This field is empty if not applicable to the
+table.
+
+B<TABLE_SCHEM>: The schema identifier.
+This field is NULL (C<undef>) if not applicable to the data source,
+and empty if not applicable to the table.
+
+B<TABLE_NAME>: The table identifier.
+
+B<COLUMN_NAME>: The column identifier.
+
+B<KEY_SEQ>: The column sequence number (starting with 1).
+Note: This field is named B<ORDINAL_POSITION> in SQL/CLI.
+
+B<PK_NAME>: The primary key constraint identifier.
+This field is NULL (C<undef>) if not applicable to the data source.
+
+See also L</"Catalog Methods"> and L</"Standards Reference Information">.
+
+=item C<primary_key> I<NEW>
+
+B<Warning:> This method is experimental and may change.
+
+  @key_column_names = $dbh->primary_key( $catalog, $schema, $table );
+
+Simple interface to the primary_key_info() method. Returns a list of
+the column names that comprise the primary key of the specified table.
+The list is in primary key column sequence order.
+
+
+=item C<foreign_key_info> I<NEW>
+
+B<Warning:> This method is experimental and may change.
+
+  $sth = $dbh->foreign_key_info( $pk_catalog, $pk_schema, $pk_table
+                               , $fk_catalog, $fk_schema, $fk_table );
+
+Returns an active statement handle that can be used to fetch information
+about foreign keys in and/or referencing the specified table(s).
+The arguments don't accept search patterns (unlike table_info()).
+
+C<$pk_catalog>, C<$pk_schema>, C<$pk_table>
+identify the primary (unique) key table (B<PKT>).
+
+C<$fk_catalog>, C<$fk_schema>, C<$fk_table>
+identify the foreign key table (B<FKT>).
+
+If both B<PKT> and B<FKT> are given, the function returns the foreign key, if
+any, in table B<FKT> that refers to the primary (unique) key of table B<PKT>.
+(Note: In SQL/CLI, the result is implementation-defined.)
+
+If only B<PKT> is given, then the result set contains the primary key
+of that table and all foreign keys that refer to it.
+
+If only B<FKT> is given, then the result set contains all foreign keys
+in that table and the primary keys to which they refer.
+(Note: In SQL/CLI, the result includes unique keys too.)
+
+For example:
+
+  $sth = $dbh->foreign_key_info( undef, $user, 'master');
+  $sth = $dbh->foreign_key_info( undef, undef,   undef , undef, $user, 'detail');
+  $sth = $dbh->foreign_key_info( undef, $user, 'master', undef, $user, 'detail');
+
+Note: The support for the selection criteria, such as C<$catalog>, is
+driver specific.  If the driver doesn't support catalogs and/or
+schemas, it may ignore these criteria.
+
+The statement handle returned has the following fields in the order shown below.
+Because ODBC never includes unique keys, they define different columns in the
+result set than SQL/CLI. SQL/CLI column names are shown in parentheses.
+
+B<PKTABLE_CAT    ( UK_TABLE_CAT      )>:
+The primary (unique) key table catalog identifier.
+This field is NULL (C<undef>) if not applicable to the data source,
+which is often the case.  This field is empty if not applicable to the
+table.
+
+B<PKTABLE_SCHEM  ( UK_TABLE_SCHEM    )>:
+The primary (unique) key table schema identifier.
+This field is NULL (C<undef>) if not applicable to the data source,
+and empty if not applicable to the table.
+
+B<PKTABLE_NAME   ( UK_TABLE_NAME     )>:
+The primary (unique) key table identifier.
+
+B<PKCOLUMN_NAME  (UK_COLUMN_NAME    )>:
+The primary (unique) key column identifier.
+
+B<FKTABLE_CAT    ( FK_TABLE_CAT      )>:
+The foreign key table catalog identifier.
+This field is NULL (C<undef>) if not applicable to the data source,
+which is often the case.  This field is empty if not applicable to the
+table.
+
+B<FKTABLE_SCHEM  ( FK_TABLE_SCHEM    )>:
+The foreign key table schema identifier.
+This field is NULL (C<undef>) if not applicable to the data source,
+and empty if not applicable to the table.
+
+B<FKTABLE_NAME   ( FK_TABLE_NAME     )>:
+The foreign key table identifier.
+
+B<FKCOLUMN_NAME  ( FK_COLUMN_NAME    )>:
+The foreign key column identifier.
+
+B<KEY_SEQ        ( ORDINAL_POSITION  )>:
+The column sequence number (starting with 1).
+
+B<UPDATE_RULE    ( UPDATE_RULE       )>:
+The referential action for the UPDATE rule.
+The following codes are defined:
+
+  CASCADE              0
+  RESTRICT             1
+  SET NULL             2
+  NO ACTION            3
+  SET DEFAULT          4
+
+B<DELETE_RULE    ( DELETE_RULE       )>:
+The referential action for the DELETE rule.
+The codes are the same as for UPDATE_RULE.
+
+B<FK_NAME        ( FK_NAME           )>:
+The foreign key name.
+
+B<PK_NAME        ( UK_NAME           )>:
+The primary (unique) key name.
+
+B<DEFERRABILITY  ( DEFERABILITY      )>:
+The deferrability of the foreign key constraint.
+The following codes are defined:
+
+  INITIALLY DEFERRED   5
+  INITIALLY IMMEDIATE  6
+  NOT DEFERRABLE       7
+
+B<               ( UNIQUE_OR_PRIMARY )>:
+This column is necessary if a driver includes all candidate (i.e. primary and
+alternate) keys in the result set (as specified by SQL/CLI).
+The value of this column is UNIQUE if the foreign key references an alternate
+key and PRIMARY if the foreign key references a primary key, or it
+may be undefined if the driver doesn't have access to the information.
+
+See also L</"Catalog Methods"> and L</"Standards Reference Information">.
 
-and search for C<SQLTables returns> using the exact phrase option.
-The link you want will probably just be called C<SQLTables> and will
-be part of the Data Access SDK.
 
 =item C<tables> I<NEW>
 
 B<Warning:> This method is experimental and may change.
 
-  @names = $dbh->tables;
+  @names = $dbh->tables( $catalog, $schema, $table, $type );
+  @names = $dbh->tables;	# deprecated
 
-Returns a list of table and view names, possibly including a schema prefix.
-This list should include all
-tables that can be used in a C<SELECT> statement without further
-qualification.
+Simple interface to table_info(). Returns a list of matching
+table names, possibly including a catalog/schema prefix.
 
-Note that C<table_info> might not return records for all tables.
-Applications can use any valid table regardless of whether it's
-returned by tables.  See also L</table_info>.
+See L</table_info> for a description of the parameters.
 
+If C<$dbh->E<gt>C<get_info(29)> returns true (29 is SQL_IDENTIFIER_QUOTE_CHAR)
+then the table names are constructed and quoted by L</quote_identifier>
+to ensure they are usable even if they contain whitespace or reserved
+words etc.
 
-=item C<type_info_all> I<NEW>
+
+=item C<type_info_all>
 
 B<Warning:> This method is experimental and may change.
 
@@ -2473,10 +3679,11 @@
 type variant supported by the database and driver. The array and its
 contents should be treated as read-only.
 
-The first item is a reference to a hash of C<Name =>E<gt> C<Index> pairs. The
-following items are references to arrays, one per supported data type
-variant. The leading hash defines the names and order of the fields
-within the following list of arrays. For example:
+The first item is a reference to an 'index' hash of C<Name =>E<gt> C<Index> pairs.
+The items following that are references to arrays, one per supported data
+type variant. The leading index hash defines the names and order of the
+fields within the arrays that follow it.
+For example:
 
   $type_info_all = [
     {   TYPE_NAME         => 0,
@@ -2520,22 +3727,33 @@
 This method is not normally used directly. The L</type_info> method
 provides a more useful interface to the data.
 
-=item C<type_info> I<NEW>
+Even though an 'index' hash is provided, all the field names in the
+index hash defined above will always have the index values defined
+above.  This is defined behaviour so that you don't need to rely on the
+index hash, which is handy because the lettercase of the keys is not
+defined. It is usually uppercase, as show here, but drivers are free to
+return names with any lettercase. Drivers are also free to return extra
+driver-specific columns of information - though it's recommended that
+they start at column index 50 to leave room for expansion of the
+DBI/ODBC specification.
+
+
+=item C<type_info>
 
 B<Warning:> This method is experimental and may change.
 
   @type_info = $dbh->type_info($data_type);
 
 Returns a list of hash references holding information about one or more
-variants of C<$data_type>. The list is ordered by C<DATA_TYPE> first and
+variants of $data_type. The list is ordered by C<DATA_TYPE> first and
 then by how closely each type maps to the corresponding ODBC SQL data
 type, closest first.  If called in a scalar context then only the first
 (best) element is returned.
 
-If C<$data_type> is undefined or C<SQL_ALL_TYPES>, then the list will
+If $data_type is undefined or C<SQL_ALL_TYPES>, then the list will
 contain hashes for all data type variants supported by the database and driver.
 
-If C<$data_type> is an array reference then C<type_info> returns the
+If $data_type is an array reference then C<type_info> returns the
 information for the I<first> type in the array that has any matches.
 
 The keys of the hash follow the same letter case conventions as the
@@ -2611,7 +3829,7 @@
 =item FIXED_PREC_SCALE (boolean)
 
 Indicates whether the data type always has the same precision and scale
-(such as a money type).  NULL (C<undef>) is returned for data types 
+(such as a money type).  NULL (C<undef>) is returned for data types
 for which
 this is not applicable.
 
@@ -2657,7 +3875,7 @@
 
 =item NUM_PREC_RADIX (integer)
 
-The radix value of the data type. For approximate numeric types, 
+The radix value of the data type. For approximate numeric types,
 C<NUM_PREC_RADIX>
 contains the value 2 and C<COLUMN_SIZE> holds the number of bits. For
 exact numeric types, C<NUM_PREC_RADIX> contains the value 10 and C<COLUMN_SIZE> holds
@@ -2672,6 +3890,11 @@
 
 =back
 
+For example, to find the type name for the fields in a select statement
+you can do:
+
+  @names = map { scalar $dbh->type_info($_)->{TYPE_NAME} } @{ $sth->{TYPE} }
+
 Since DBI and ODBC drivers vary in how they map their types into the
 ISO standard types you may need to search for more than one type.
 Here's an example looking for a usable type to store a date:
@@ -2681,25 +3904,8 @@
 Similarly, to more reliably find a type to store small integers, you could
 use a list starting with C<SQL_SMALLINT>, C<SQL_INTEGER>, C<SQL_DECIMAL>, etc.
 
-For more detailed information about these fields and their meanings, you
-can refer to:
-
-  http://msdn.microsoft.com/library/psdk/dasdk/odch6yy7.htm
-
-If that URL ceases to work then use the MSDN search facility at 
- 
-    http://search.microsoft.com/us/dev/
- 
-and search the MSDN Library for C<SQLGetTypeInfo returns> using the exact phrase option.
-The link you want will probably just be called C<SQLGetTypeInfo> (there
-may be more than one). 
-
-The individual data types are currently described here:
+See also L</"Standards Reference Information">.
 
-  http://msdn.microsoft.com/library/psdk/dasdk/odap8fcj.htm
-
-If that URL ceases to work, or to get more general information, use the
-MSDN search facility as described above and search for C<SQL Data Types>.
 
 =item C<quote>
 
@@ -2718,7 +3924,7 @@
 outer quotation marks).
 
 An undefined C<$value> value will be returned as the string C<NULL> (without
-quotation marks) to match how NULLs are represented in SQL.
+single quotation marks) to match how NULLs are represented in SQL.
 
 If C<$data_type> is supplied, it is used to try to determine the required
 quoting behavior by using the information returned by L</type_info>.
@@ -2730,6 +3936,32 @@
 any way with escaping or quoting shell meta-characters. There is no
 need to quote values being used with L</"Placeholders and Bind Values">.
 
+=item C<quote_identifier>
+
+  $sql = $dbh->quote_identifier( $name );
+  $sql = $dbh->quote_identifier( $name1, $name2, $name3, \%attr );
+
+Quote an identifier (table name etc.) for use in an SQL statement,
+by escaping any special characters (such as double quotation marks)
+it contains and adding the required type of outer quotation marks.
+
+Undefined names are ignored and the remainder are quoted and then
+joined together, typically with a dot (C<.>) character. For example:
+
+  $id = $dbh->quote_identifier( undef, 'Her schema', 'My table' );
+
+would, for most database types, return C<"Her schema"."My table">
+(including all the double quotation marks).
+
+If three names are supplied then the first is assumed to be a catalog
+name and special rules may be applied based on what L</get_info>
+returns for SQL_CATALOG_NAME_SEPARATOR (41) and SQL_CATALOG_LOCATION (114).
+For example, for Oracle:
+
+  $id = $dbh->quote_identifier( 'link', 'schema', 'table' );
+
+would return C<"schema"."table"@"link">.
+
 =back
 
 
@@ -2764,7 +3996,7 @@
 Attempting to set C<AutoCommit> to an unsupported value is a fatal error.
 This is an important feature of the DBI. Applications that need
 full transaction behavior can set C<$dbh->E<gt>C<{AutoCommit} = 0> (or
-set C<AutoCommit> to 0 via L</connect>) 
+set C<AutoCommit> to 0 via L</connect>)
 without having to check that the value was assigned successfully.
 
 For the purposes of this description, we can divide databases into three
@@ -2789,15 +4021,13 @@
 L</disconnect>). If L</rollback> is called then any changes since the
 last commit are undone.
 
-If C<AutoCommit> is on, then the effect is the same as if the DBI 
+If C<AutoCommit> is on, then the effect is the same as if the DBI
 called C<commit> automatically after every successful database
-operation. In other words, calling C<commit> or C<rollback> explicitly while
+operation. So calling C<commit> or C<rollback> explicitly while
 C<AutoCommit> is on would be ineffective because the changes would
 have already been commited.
 
-Changing C<AutoCommit> from off to on should issue a L</commit> in most drivers.
-
-Changing C<AutoCommit> from on to off should have no immediate effect.
+Changing C<AutoCommit> from off to on will trigger a L</commit>.
 
 For databases which don't support a specific auto-commit mode, the
 driver has to commit each statement automatically using an explicit
@@ -2811,13 +4041,16 @@
 For these databases, the intention is to have them act like databases in
 which a transaction is always active (as described above).
 
-To do this, the DBI driver will automatically begin a transaction when
-C<AutoCommit> is turned off (from the default "on" state) and will
-automatically begin another transaction after a L</commit> or L</rollback>.
-In this way, the application does not have to treat these databases as a
-special case.
+To do this, the driver will automatically begin an explicit transaction
+when C<AutoCommit> is turned off, or after a L</commit> or
+L</rollback> (or when the application issues the next database
+operation after one of those events).
 
-See L</disconnect> for other important notes about transactions.
+In this way, the application does not have to treat these databases
+as a special case.
+
+See L</commit>, L</disconnect> and L</Transactions> for other important
+notes about transactions.
 
 
 =item C<Driver>  (handle)
@@ -2835,7 +4068,15 @@
 but with the leading "C<dbi:DriverName:>" removed.
 
 
-=item C<RowCacheSize>  (integer) I<NEW>
+=item C<Statement>  (string, read-only)
+
+Returns the statement string passed to the most recent L</prepare> method
+called in this database handle, even if that method failed. This is especially
+useful where C<RaiseError> is enabled and the exception handler checks $@
+and sees that a 'prepare' method call failed.
+
+
+=item C<RowCacheSize>  (integer)
 
 A hint to the driver indicating the size of the local row cache that the
 application would like the driver to use for future C<SELECT> statements.
@@ -2851,7 +4092,7 @@
 
 Note that large cache sizes may require a very large amount of memory
 (I<cached rows * maximum size of row>). Also, a large cache will cause
-a longer delay not only for the first fetch, but also whenever the 
+a longer delay not only for the first fetch, but also whenever the
 cache needs refilling.
 
 See also the L</RowsInCache> statement handle attribute.
@@ -2872,9 +4113,9 @@
 
 =item C<bind_param>
 
-  $rc = $sth->bind_param($p_num, $bind_value)  || die $sth->errstr;
-  $rv = $sth->bind_param($p_num, $bind_value, \%attr)     || ...
-  $rv = $sth->bind_param($p_num, $bind_value, $bind_type) || ...
+  $rc = $sth->bind_param($p_num, $bind_value)  or die $sth->errstr;
+  $rv = $sth->bind_param($p_num, $bind_value, \%attr)     or ...
+  $rv = $sth->bind_param($p_num, $bind_value, $bind_type) or ...
 
 The C<bind_param> method can be used to bind a value
 with a placeholder embedded in the prepared statement. Placeholders
@@ -2929,6 +4170,12 @@
 C<bind_param> call. However, it can be left unspecified, in which case it
 defaults to the previous value.
 
+The SQL_INTEGER and other related constants can be imported using
+
+  use DBI qw(:sql_types);
+
+See L</"DBI Constants"> for more information.
+
 Perl only has string and number scalar data types. All database types
 that aren't numbers are bound as strings and must be in a format the
 database will understand.
@@ -2948,9 +4195,9 @@
 
 =item C<bind_param_inout>
 
-  $rc = $sth->bind_param_inout($p_num, \$bind_value, $max_len)  || die $sth->errstr;
-  $rv = $sth->bind_param_inout($p_num, \$bind_value, $max_len, \%attr)     || ...
-  $rv = $sth->bind_param_inout($p_num, \$bind_value, $max_len, $bind_type) || ...
+  $rc = $sth->bind_param_inout($p_num, \$bind_value, $max_len)  or die $sth->errstr;
+  $rv = $sth->bind_param_inout($p_num, \$bind_value, $max_len, \%attr)     or ...
+  $rv = $sth->bind_param_inout($p_num, \$bind_value, $max_len, $bind_type) or ...
 
 This method acts like L</bind_param>, but also enables values to be
 updated by the statement. The statement is typically
@@ -2977,10 +4224,55 @@
 See also L</"Placeholders and Bind Values"> for more information.
 
 
+=item C<bind_param_array>
+
+  $rc = $sth->bind_param_array($p_num, $array_ref_or_value)
+  $rc = $sth->bind_param_array($p_num, $array_ref_or_value, \%attr)
+  $rc = $sth->bind_param_array($p_num, $array_ref_or_value, $bind_type)
+
+The C<bind_param_array> method is used to bind an array of values
+to a placeholder embedded in the prepared statement which is to be executed
+with L</execute_array>. For example:
+
+  $dbh->{RaiseError} = 1;        # save having to check each method call
+  $sth = $dbh->prepare("INSERT INTO people(first_name, last_name) VALUES(?, ?)");
+  $sth->bind_param_array(1, [ 'John', 'Mary', 'Tim' ]);
+  $sth->bind_param_array(2, [ 'Booth', 'Todd', 'Robinson' ]);
+  my @tuple_status;
+  $sth->execute_array(\@tuple_status);
+
+The C<%attr> argument is the same as defined for L</bind_param>.
+Refer to L</bind_param> for general details on using placeholders.
+
+Each array bound to the statement must have the same number of
+elements.  Some drivers may define a method attribute to relax this
+safety check.
+
+Scalar values, including C<undef>, may also be bound by
+C<bind_param_array>. In which case the same value will be used for each
+L</execute> call. Driver-specific implementations may behave
+differently, e.g., when binding to a stored procedure call, some
+databases permit mixing scalars and arrays as arguments.
+
+The default implementation provided by DBI (for drivers that have
+not implemented array binding) is to iteratively L</execute> for
+each parameter tuple provided in the bound arrays.  Drivers may
+provide more optimized implementations using whatever bulk operation
+support the database API provides. The default driver behaviour should 
+match the default DBI behaviour, but always consult your driver
+documentation as there may be driver specific issues to consider.
+
+Note that the default implementation currently only supports non-data
+returning statements. Also, C<bind_param_array> and L</bind_param>
+cannot be mixed in the same statement execution, and C<bind_param_array>
+must be used with L</execute_array>; using C<bind_param_array> will
+have no effect for L</execute>.
+
+
 =item C<execute>
 
-  $rv = $sth->execute                || die $sth->errstr;
-  $rv = $sth->execute(@bind_values)  || die $sth->errstr;
+  $rv = $sth->execute                or die $sth->errstr;
+  $rv = $sth->execute(@bind_values)  or die $sth->errstr;
 
 Perform whatever processing is necessary to execute the prepared
 statement.  An C<undef> is returned if an error occurs.  A successful
@@ -3009,6 +4301,80 @@
 type.
 
 
+=item C<execute_array>
+
+  $rv = $sth->execute_array(\%attr) or die $sth->errstr;
+  $rv = $sth->execute_array(\%attr, @bind_values)  or die $sth->errstr;
+
+Execute the prepared statement for each parameter tuple provided
+either in the @bind_values, or by prior calls to L</bind_param_array>.
+
+An C<undef> is returned if an error occurs.  A successful
+C<execute_array> always returns true regardless of the number of
+rows affected, even if it's zero (see below). It is always important
+to check the return status of C<execute_array> (and most other DBI
+methods) for errors.
+
+Parameters may be supplied either by prior calls to L</bind_param_array>,
+or in the C<@bind_values> argument. The values supplied may be either
+scalars, or arrayrefs. See L</bind_param_array> for details.
+
+The supplied C<\%attr> hashref currently supports only the C<ArrayTupleStatus>
+attribute, which should specify an arrayref to receive the status of each
+parameter tuple bound to the statement. For parameter tuples which
+are successfully executed, the element at the same ordinal position in the 
+status array will return the resulting rowcount.
+
+If a parameter tuple causes an error, the associated status array
+element will be set to an arrayref of [ $sth->err, $sth->errstr ]
+returned by the failed execution.  If B<any> tuple returns an error,
+C<execute_array> will return C<undef> B<after> it has executed all
+the parameter tuples. In that case, the application should inspect
+the status array to determine which parameter tuples failed.
+
+If no C<ArrayTupleStatus> is provided, C<execute_array> will return
+C<undef> on the first occurance of a parameter tuple causing an error.
+[XXX This may change as it doesn't match the behaviour of drivers
+which use bulk operation API to ship the data to the server.]
+
+If all parameter tuples are successfully executed, C<execute_array> returns 
+the sum of the number of rows affected by all the parameter tuples,
+if known. If no rows were affected, then C<execute> returns
+"C<0E0>", which Perl will treat as 0 but will regard as true. Note that it
+is I<not> an error for no rows to be affected by a statement. If the
+number of rows affected is not known, then C<execute_array> may return a 
+negative number. Applications should not rely on the returned value to
+indicate actual total rowcounts, but use the C<ArrayTupleStatus> and explicitly
+inspect each returned element of the status array.
+
+Support for data returning statements is driver-specific and subject
+to change. At present, the default implementation provided by DBI
+only supports non-data returning statements.
+
+If any C<@bind_values> are given, then C<execute_array> will effectively call
+L</bind_param_array> for each value before executing the statement.
+Values bound in this way are usually treated as C<SQL_VARCHAR> types
+unless the driver can determine the correct type (which is rare), or
+unless C<bind_param>, C<bind_param_inout>, C<bind_param_array>, 
+or C<bind_param_inout_array> has already been used to specify the type.
+
+Transaction semantics using array binding are driver and database specific.
+If C<AutoCommit> is on, the default DBI implementation will cause each 
+parameter tuple to be inidividually committed (or rolled back in the event
+of an error). If C<AutoCommit> is off, the application is responsible
+for explicitly committing the entire set of bound parameter tuples.
+Note that different drivers and databases may have different behaviors
+when some parameter tuples cause failures. In some cases, the driver or
+database may automatically rollback the effect of all prior parameter 
+tuples that succeeded in the transaction; other drivers or databases may 
+retain the effect of prior successfully executed parameter tuples. Be
+sure to check your driver and database for its specific behavior.
+
+Note that, in general, performance will usually be better with C<AutoCommit>
+turned off, and using explicit C<commit> after each C<execute_array>
+call.
+
+
 =item C<fetchrow_arrayref>
 
   $ary_ref = $sth->fetchrow_arrayref;
@@ -3025,8 +4391,10 @@
 C<RaiseError> attribute) to discover if the C<undef> returned was due to an
 error.
 
-Note that the same array reference will currently be returned for each
-fetch, so don't store the reference and then use it after a later fetch.
+Note that the same array reference is returned for each fetch, so don't
+store the reference and then use it after a later fetch.  Also, the
+elements of the array are also reused for each row, so take care if you
+want to take a reference to an element. See also L</bind_columns>.
 
 =item C<fetchrow_array>
 
@@ -3041,11 +4409,14 @@
 the C<RaiseError> attribute) to discover if the empty list returned was
 due to an error.
 
-In a scalar context, C<fetchrow_array> returns the value of the first
-field. An C<undef> is returned if there are no more rows or if an error
-occurred. Since that C<undef> can't be distinguished from an C<undef> returned
-because the first field value was NULL, you should exercise some
-caution if you use C<fetchrow_array> in a scalar context.
+If called in a scalar context for a statement handle that has more
+than one column, it is undefined whether the driver will return
+the value of the first column or the last. So don't do that.
+Also, in a scalar context, an C<undef> is returned if there are no
+more rows or if an error occurred. That C<undef> can't be distinguished
+from an C<undef> returned because the first field value was NULL.
+For these reasons you should exercise some caution if you use
+C<fetchrow_array> in a scalar context.
 
 =item C<fetchrow_hashref>
 
@@ -3080,8 +4451,8 @@
 =item C<fetchall_arrayref>
 
   $tbl_ary_ref = $sth->fetchall_arrayref;
-  $tbl_ary_ref = $sth->fetchall_arrayref( $slice_array_ref );
-  $tbl_ary_ref = $sth->fetchall_arrayref( $slice_hash_ref  );
+  $tbl_ary_ref = $sth->fetchall_arrayref( $slice );
+  $tbl_ary_ref = $sth->fetchall_arrayref( $slice, $max_rows  );
 
 The C<fetchall_arrayref> method can be used to fetch all the data to be
 returned from a prepared and executed statement handle. It returns a
@@ -3093,17 +4464,24 @@
 afterwards (or use the C<RaiseError> attribute) to discover if the data is
 complete or was truncated due to an error.
 
-When passed an array reference, C<fetchall_arrayref> uses L</fetchrow_arrayref>
+If $slice is an array reference, C<fetchall_arrayref> uses L</fetchrow_arrayref>
 to fetch each row as an array ref. If the parameter array is not empty
-then it is used as a slice to select individual columns by index number.
-
-With no parameters, C<fetchall_arrayref> acts as if passed an empty array ref.
-
-When passed a hash reference, C<fetchall_arrayref> uses L</fetchrow_hashref>
-to fetch each row as a hash reference. If the parameter hash is not empty,
-then it is used as a slice to select individual columns by name. The
-names should be lower case regardless of the letter case in C<$sth->E<gt>C<{NAME}>.
-The values of the hash should be set to 1.
+then it is used as a slice to select individual columns by perl array
+index number (starting at 0, unlike column and parameter numbers which
+start at 1).
+
+With no parameters, or if $slice is undefined, C<fetchall_arrayref>
+acts as if passed an empty array ref.
+
+If $slice is a hash reference, C<fetchall_arrayref> uses L</fetchrow_hashref>
+to fetch each row as a hash reference. If the $slice hash is empty then
+fetchrow_hashref() is simply called in a tight loop and the keys in the hashes
+have whatever name lettercase is returned by default from fetchrow_hashref.
+(See L</FetchHashKeyName> attribute.) If the $slice hash is not
+empty, then it is used as a slice to select individual columns by
+name.  The values of the hash should be set to 1.  The key names
+of the returned hashes match the letter case of the names in the
+parameter hash, regardless of the L</FetchHashKeyName> attribute.
 
 For example, to fetch just the first column of every row:
 
@@ -3117,12 +4495,52 @@
 
   $tbl_ary_ref = $sth->fetchall_arrayref({});
 
-To fetch only the fields called "foo" and "bar" of every row as a hash ref:
+To fetch only the fields called "foo" and "bar" of every row as a hash ref
+(with keys named "foo" and "BAR"):
+
+  $tbl_ary_ref = $sth->fetchall_arrayref({ foo=>1, BAR=>1 });
+
+The first two examples return a reference to an array of array refs.
+The third and forth return a reference to an array of hash refs.
+
+If $max_rows is defined and greater than or equal to zero then it
+is used to limit the number of rows fetched before returning.
+fetchall_arrayref() can then be called again to fetch more rows.
+This is especially useful when you need the better performance of
+fetchall_arrayref() but don't have enough memory to fetch and return
+all the rows in one go.
 
-  $tbl_ary_ref = $sth->fetchall_arrayref({ foo=>1, bar=>1 });
 
-The first two examples return a reference to an array of array refs. The last
-returns a reference to an array of hash refs.
+=item C<fetchall_hashref>
+
+  $hash_ref = $dbh->fetchall_hashref($key_field);
+
+The C<fetchall_hashref> method can be used to fetch all the data to be
+returned from a prepared and executed statement handle. It returns a
+reference to a hash that contains, at most, one entry per row.
+
+If there are no rows to return, C<fetchall_hashref> returns a reference
+to an empty hash. If an error occurs, C<fetchall_hashref> returns the
+data fetched thus far, which may be none.  You should check
+C<$sth->E<gt>C<err> afterwards (or use the C<RaiseError> attribute) to
+discover if the data is complete or was truncated due to an error.
+
+The $key_field parameter provides the name of the field that holds the
+value to be used for the key for the returned hash.  For example:
+
+  $dbh->{FetchHashKeyName} = 'NAME_lc';
+  $sth = $dbh->prepare("SELECT FOO, BAR, ID, NAME, BAZ FROM TABLE");
+  $hash_ref = $sth->fetchall_hashref('id');
+  print "Name for id 42 is $hash_ref->{42}->{name}\n";
+
+The $key_field parameter can also be specified as an integer column
+number (counting from 1).  If $key_field doesn't match any column in
+the statement, as a name first then as a number, then an error is
+returned.
+
+This method is normally used only where the key field value for each
+row is unique.  If multiple rows are returned with the same value for
+the key field then later rows overwrite earlier ones.
 
 
 =item C<finish>
@@ -3131,14 +4549,18 @@
 
 Indicates that no more data will be fetched from this statement handle
 before it is either executed again or destroyed.  The C<finish> method
-is rarely needed,
-but can sometimes be helpful in very specific situations to
-allow the server to free up resources (such as
-sort buffers).
-
-When all the data has been fetched from a C<SELECT> statement, the driver
-should automatically call C<finish> for you. So you should not normally
-need to call it explicitly.
+is rarely needed, but can sometimes be helpful in very specific
+situations to allow the server to free up resources (such as sort
+buffers).
+
+When all the data has been fetched from a C<SELECT> statement, the
+driver should automatically call C<finish> for you. So you should
+I<not> normally need to call it explicitly I<except> when you know
+that you've not fetched all the data from a statement handle.
+The most common example is when you only want to fetch one row,
+but in that case the C<selectrow_*> methods may be better anyway.
+Adding calls to C<finish> after each fetch loop is a common mistake,
+don't do it, it can mask genuine problems like uncaught fetch errors.
 
 Consider a query like:
 
@@ -3157,9 +4579,10 @@
 
 The C<finish> method does not affect the transaction status of the
 database connection.  It has nothing to do with transactions. It's mostly an
-internal "housekeeping" method that is rarely needed. There's no need
-to call C<finish> if you're about to destroy or re-execute the statement
-handle.  See also L</disconnect> and the L</Active> attribute.
+internal "housekeeping" method that is rarely needed.
+See also L</disconnect> and the L</Active> attribute.
+
+The C<finish> method should have been called C<cancel_select>.
 
 
 =item C<rows>
@@ -3237,8 +4660,17 @@
   }
 
 For compatibility with old scripts, the first parameter will be
-ignored if it is C<undef> or
-a hash reference.
+ignored if it is C<undef> or a hash reference.
+
+Here's a more fancy example that binds columns to the values I<inside>
+a hash (thanks to H.Merijn Brand):
+
+  $sth->execute;
+  my %row;
+  $sth->bind_columns( \( @row{ @{$sth->{NAME_lc} } } ));
+  while ($sth->fetch) {
+      print "$row{region}: $row{sales}\n";
+  }
 
 
 =item C<dump_results>
@@ -3276,7 +4708,7 @@
 Note that some drivers cannot provide valid values for some or all of
 these attributes until after C<$sth->E<gt>C<execute> has been called.
 
-See also L</finish> to learn more about the effect it 
+See also L</finish> to learn more about the effect it
 may have on some attributes.
 
 =over 4
@@ -3311,7 +4743,29 @@
 
 Like L</NAME> but always returns uppercase names.
 
-=item C<TYPE>  (array-ref, read-only) I<NEW>
+=item C<NAME_hash>  (hash-ref, read-only)
+
+=item C<NAME_lc_hash>  (hash-ref, read-only)
+
+=item C<NAME_uc_hash>  (hash-ref, read-only)
+
+The C<NAME_hash>, C<NAME_lc_hash>, and C<NAME_uc_hash> attributes
+return column name information as a reference to a hash.
+
+The keys of the hash are the names of the columns.  The letter case of
+the keys corresponds to the letter case returned by the C<NAME>,
+C<NAME_lc>, and C<NAME_uc> attributes respectively (as described above).
+
+The value of each hash entry is the perl index number of the
+corresponding column (counting from 0). For example:
+
+  $sth = $dbh->prepare("select Id, Name from table");
+  $sth->execute;
+  @row = $sth->fetchrow_array;
+  print "Name $row[ $sth->{NAME_lc_hash}{name} ]\n";
+
+
+=item C<TYPE>  (array-ref, read-only)
 
 Returns a reference to an array of integer values for each
 column. The value indicates the data type of the corresponding column.
@@ -3321,18 +4775,18 @@
 types that don't exactly match standard types should generally return
 the same values as an ODBC driver supplied by the makers of the
 database. That might include private type numbers in ranges the vendor
-has officially registered. See:
+has officially registered with the ISO working group:
 
-  ftp://jerry.ece.umassd.edu/isowg3/dbl/SQL_Registry
+  ftp://sqlstandards.org/SC32/SQL_Registry/
 
-Where there's no vendor-supplied ODBC driver to be compatible with, the
-DBI driver can use type numbers in the range that is now officially reserved
-for use by the DBI: -9999 to -9000.
+Where there's no vendor-supplied ODBC driver to be compatible with,
+the DBI driver can use type numbers in the range that is now
+officially reserved for use by the DBI: -9999 to -9000.
 
 All possible values for C<TYPE> should have at least one entry in the
 output of the C<type_info_all> method (see L</type_info_all>).
 
-=item C<PRECISION>  (array-ref, read-only) I<NEW>
+=item C<PRECISION>  (array-ref, read-only)
 
 Returns a reference to an array of integer values for each
 column.  For non-numeric columns, the value generally refers to either
@@ -3343,7 +4797,7 @@
 "display size" can be up to 7 characters greater than the precision.
 (for the sign + decimal point + the letter E + a sign + 2 or 3 digits).
 
-=item C<SCALE>  (array-ref, read-only) I<NEW>
+=item C<SCALE>  (array-ref, read-only)
 
 Returns a reference to an array of integer values for each column.
 NULL (C<undef>) values indicate columns where scale is not applicable.
@@ -3351,7 +4805,8 @@
 =item C<NULLABLE>  (array-ref, read-only)
 
 Returns a reference to an array indicating the possibility of each
-column returning a null.  Possible values are C<0> = no, C<1> = yes, C<2> = unknown.
+column returning a null.  Possible values are C<0>
+(or an empty string) = no, C<1> = yes, C<2> = unknown.
 
   print "First column may return NULL\n" if $sth->{NULLABLE}->[0];
 
@@ -3363,7 +4818,22 @@
 C<"where current of ..."> SQL syntax, then it returns C<undef>.
 
 
-=item C<Statement>  (string, read-only) I<NEW>
+=item C<ParamValues>  (hash ref, read-only)
+
+Returns a reference to a hash containing the values currently bound
+to placeholders.  Returns undef if not supported by the driver.
+If the driver does support C<ParamValues> but no values have been
+bound yet then either undef or an empty hash may be returned.
+
+See L</ShowErrorStatement> for an example of how this is used.
+
+It is possible that the values in the hash returned by C<ParamValues>
+are not exactly the same as those passed to bind_param() or execute().
+The driver may have modified the values in some way based on the
+TYPE the value was bound with. For example a floating point value
+bound as an SQL_INTEGER type may be returned as an integer.
+
+=item C<Statement>  (string, read-only)
 
 Returns the statement string passed to the L</prepare> method.
 
@@ -3382,6 +4852,59 @@
 
 =head1 FURTHER INFORMATION
 
+=head2 Catalog Methods
+
+An application can retrieve metadata information from the DBMS by issuing
+appropriate queries on the views of the Information Schema. Unfortunately,
+C<INFORMATION_SCHEMA> views are seldom supported by the DBMS.
+Special methods (catalog methods) are available to return result sets
+for a small but important portion of that metadata:
+
+  column_info
+  foreign_key_info
+  primary_key_info
+  table_info
+
+All catalog methods accept arguments in order to restrict the result sets.
+Passing C<undef> to an optional argument does not constrain the search for
+that argument.
+However, an empty string ('') is treated as a regular search criteria
+and will only match an empty value.
+
+B<Note>: SQL/CLI and ODBC differ in the handling of empty strings. An
+empty string will not restrict the result set in SQL/CLI.
+
+Most arguments in the catalog methods accept only I<ordinary values>, e.g.
+the arguments of C<primary_key_info()>.
+Such arguments are treated as a literal string, i.e. the case is significant
+and quote characters are taken literally.
+
+Some arguments in the catalog methods accept I<search patterns> (strings
+containing '_' and/or '%'), e.g. the C<$table> argument of C<column_info()>.
+Passing '%' is equivalent to leaving the argument C<undef>.
+
+B<Caveat>: The underscore ('_') is valid and often used in SQL identifiers.
+Passing such a value to a search pattern argument may return more rows than
+expected!
+To include pattern characters as literals, they must be preceded by an
+escape character which can be achieved with
+
+  $esc = $dbh->get_info( 14 );  # SQL_SEARCH_PATTERN_ESCAPE
+  $search_pattern =~ s/([_%])/$esc$1/g;
+
+The ODBC and SQL/CLI specifications define a way to change the default
+behavior described above: All arguments (except I<list value arguments>)
+are treated as I<identifier> if the C<SQL_ATTR_METADATA_ID> attribute is
+set to C<SQL_TRUE>.
+I<Quoted identifiers> are very similar to I<ordinary values>, i.e. their
+body (the string within the quotes) is interpreted literally.
+I<Unquoted identifiers> are compared in UPPERCASE.
+
+The DBI (currently) does not support the C<SQL_ATTR_METADATA_ID> attribute,
+i.e. it behaves like an ODBC driver where C<SQL_ATTR_METADATA_ID> is set to
+C<SQL_FALSE>.
+
+
 =head2 Transactions
 
 Transactions are a fundamental part of any robust database system. They
@@ -3414,7 +4937,7 @@
 If the C<RaiseError> attribute is not set, then DBI calls would need to be
 manually checked for errors, typically like this:
 
-  $h->method(@args) || die $h->errstr;
+  $h->method(@args) or die $h->errstr;
 
 With C<RaiseError> set, the DBI will automatically C<die> if any DBI method
 call on that handle (or a child handle) fails, so you don't have to
@@ -3427,6 +4950,14 @@
 C<$h->E<gt>C<{RaiseError}> attribute is that all DBI calls will be checked
 automatically. Both techniques are strongly recommended.
 
+After calling C<commit> or C<rollback> many drivers will not let you
+fetch from a previously active C<SELECT> statement handle that's a child
+of the same database handle. A typical way round this is to connect the
+the database twice and use one connection for C<SELECT> statements.
+
+See L</AutoCommit> and L</disconnect> for other important information
+about transactions.
+
 
 =head2 Handling BLOB / LONG / Memo Fields
 
@@ -3456,15 +4987,15 @@
 Here's a complete example program to select and fetch some data:
 
   my $dbh = DBI->connect("dbi:DriverName:db_name", $user, $password)
-      || die "Can't connect to $data_source: $DBI::errstr";
+      or die "Can't connect to $data_source: $DBI::errstr";
 
   my $sth = $dbh->prepare( q{
           SELECT name, phone
           FROM mytelbook
-  }) || die "Can't prepare statement: $DBI::errstr";
+  }) or die "Can't prepare statement: $DBI::errstr";
 
   my $rc = $sth->execute
-      || die "Can't execute statement: $DBI::errstr";
+      or die "Can't execute statement: $DBI::errstr";
 
   print "Query will return $sth->{NUM_OF_FIELDS} fields.\n\n";
   print "Field names: @{ $sth->{NAME} }\n";
@@ -3490,7 +5021,7 @@
 
   open FH, "<phone.csv" or die "Unable to open phone.csv: $!";
   while (<FH>) {
-      chop;
+      chomp;
       my ($name, $phone) = split /,/;
       $sth->execute($name, $phone);
   }
@@ -3515,12 +5046,38 @@
 
 =head2 Threads and Thread Safety
 
-Perl versions 5.004_50 and later include optional experimental support
-for multiple threads on many platforms.  If the DBI is built using a
-Perl that has threads enabled then it will use a per-driver mutex to
-ensure that only one thread is with a driver at any one time.
-Please note that support for threads in Perl is still experimental and
-is known to have some significant problems. It's use is not recommended.
+Perl 5.7 and later support a new threading model called iThreads.
+(The old and fatally flawed "5.005 style" threads are not supported
+by the DBI.)
+
+In the iThreads model each thread has it's own copy of the perl
+interpreter.  When a new thread is created the original perl
+interpreter is 'cloned' to create a new copy for the new thread.
+
+If the DBI and drivers are loaded and handles created before the
+thread is created then it will get a cloned copy of the DBI, the
+drivers and the handles.
+
+However, the internal pointer data within the handles will refer
+to the DBI and drivers in the original interpreter. Using those
+handles in the new interpreter thread is not safe, so the DBI detects
+this and croaks on any method call using handles that don't belong
+to the current thread (except for DESTROY).
+
+Because of this (possibly temporary) restriction, newly created
+threads must make their own connctions to the database. Handles
+can't be shared across threads.
+
+But BEWARE, some underlying database APIs (the code the DBD driver
+uses to talk to the database, often supplied by the database vendor)
+are not thread safe. If it's not thread safe, then allowing more
+than one thread to enter the code at the same time may cause
+subtle/serious problems. In some cases allowing more than
+one thread to enter the code, even if I<not> at the same time,
+can cause problems. You have been warned.
+
+Using DBI with perl threads is not yet recommended for production
+environments.
 
 
 =head2 Signal Handling and Canceling Operations
@@ -3550,6 +5107,114 @@
 engine does not have cancel implemented.
 
 
+=head2 Subclassing the DBI
+
+DBI can be subclassed and extended just like any other object
+oriented module.  Before we talk about how to do that, it's important
+to be clear about how the DBI classes and how they work together.
+
+By default C<$dbh = DBI->E<gt>C<connect(...)> returns a $dbh blessed
+into the C<DBI::db> class.  And the C<$dbh->E<gt>C<prepare> method
+returns an $sth blessed into the C<DBI::st> class (actually it
+simply changes the last four characters of the calling handle class
+to be C<::st>).
+
+The leading 'C<DBI>' is known as the 'root class' and the extra
+'C<::db>' or 'C<::st>' are the 'handle type suffixes'. If you want
+to subclass the DBI you'll need to put your overriding methods into
+the appropriate classes.  For example, if you want to use a root class
+of C<MySubDBI> and override the do(), prepare() and execute() methods,
+then your do() and prepare() methods should be in the C<MySubDBI::db>
+class and the execute() method should be in the C<MySubDBI::st> class.
+
+To setup the inheritance hierarchy the @ISA variable in C<MySubDBI::db>
+should include C<DBI::db> and the @ISA variable in C<MySubDBI::st>
+should include C<DBI::st>.  The C<MySubDBI> root class itself isn't
+currently used for anything visible and so, apart from setting @ISA
+to include C<DBI>, it should be left empty.
+
+So, having put your overriding methods into the right classes, and
+setup the inheritance hierarchy, how do you get the DBI to use them?
+You have two choices, either a static method call using the name
+of your subclass:
+
+  $dbh = MySubDBI->connect(...);
+
+or specifying a C<RootClass> attribute:
+
+  $dbh = DBI->connect(..., { RootClass => 'MySubDBI' });
+
+The only difference between the two is that using an explicit
+RootClass attribute will make the DBI automatically attempt to load
+a module by that name (and not complain if such a module can't be
+found). If both forms are used then the attribute takes precedence.
+
+Here's a brief example of a DBI subclass.  A more thorough example
+can be found in t/subclass.t in the DBI distribution.
+
+  package MySubDBI;
+
+  use strict;
+
+  use DBI;
+  use vars qw(@ISA);
+  @ISA = qw(DBI);
+
+  package MySubDBI::db;
+  use vars qw(@ISA);
+  @ISA = qw(DBI::db);
+
+  sub prepare {
+    my ($dbh, @args) = @_;
+    my $sth = $dbh->SUPER::prepare(@args)
+        or return;
+    $sth->{private_mysubdbi_info} = { foo => 'bar' };
+    return $sth;
+  }
+
+  package MySubDBI::st;
+  use vars qw(@ISA);
+  @ISA = qw(DBI::st);
+
+  sub fetch {
+    my ($sth, @args) = @_;
+    my $row = $sth->SUPER::fetch(@args)
+        or return;
+    do_something_magical_with_row_data($row)
+        or return $sth->set_err(1234, "The magic failed", undef, "fetch");
+    return $row;
+  }
+
+When calling a SUPER::method that returns a handle, be careful to
+check the return value before trying to do other things with it in
+your overridden method. This is especially important if you want
+to set a hash attribute on the handle, as Perl's autovivification
+will bite you by (in)conveniently creating an unblessed hashref,
+which your method will then return with usually baffling results
+later on.  It's best to check right after the call and return undef
+immediately on error, just like DBI would and just like the example
+above.
+
+If your method needs to record an error it should call the set_err()
+method with the error code and error string, as shown in the example
+above. The error code and error string will be recorded in the
+handle and available via C<$h->E<gt>C<err> and C<$DBI::errstr> etc.
+The set_err() method always returns an undef or empty list as
+approriate. Since your method should nearly always return an undef
+or empty list as soon as an error is detected it's handy to simply
+return what set_err() returns, as shown in the example above.
+
+If the handle has C<RaiseError>, C<PrintError>, or C<HandleError>
+etc. set then the set_err() method will honour them. This means
+that if C<RaiseError> is set then set_err() won't return in the
+normal way but will 'throw an exception' that can be caught with
+an C<eval> block.
+
+You can stash private data into DBI handles
+via C<$h->E<gt>C<{private_..._*}>.  See the entry under L</ATTRIBUTES
+COMMON TO ALL HANDLES> for info and important caveats.
+
+
 =head1 DEBUGGING
 
 In addition to the L</trace> method, you can enable the same trace
@@ -3572,6 +5237,13 @@
 
 See also the L</trace> method.
 
+It can sometimes be handy to compare trace files from two different
+runs of the same script. However using a tool like C<diff> doesn't work
+well because the trace file is full of object addresses that may
+differ each run. Here's a handy little command to strip those out:
+
+ perl -pe 's/\b0x[\da-f]{6,}/0xNNNN/gi; s/\b[\da-f]{6,}/<long number>/gi'
+
 
 =head1 WARNING AND ERROR MESSAGES
 
@@ -3613,43 +5285,63 @@
 
 =back
 
-=head2 Warnings
+=head1 Pure-Perl DBI
 
-=over 4
+A pure-perl emulation of the DBI is included in the distribution
+for people using pure-perl drivers who, for whatever reason, can't
+install the compiled DBI. See L<DBI::PurePerl>.
 
-=item Database handle destroyed without explicit disconnect
+=head1 SEE ALSO
 
-A C<$dbh> handle went out of scope or the program ended before the handle
-was disconnected from the database.  You should always explicitly call
-C<disconnect> when you are finished using a database handle. If using
-transactions then you should also explicitly call C<commit> or C<rollback>
-before C<disconnect>.
+=head2 Driver and Database Documentation
 
-=item DBI Handle cleared whilst still holding %d cached kids!
+Refer to the documentation for the DBD driver that you are using.
 
-Most probably due to a DBI bug. Possibly a DBD driver bug. Please report it.
+Refer to the SQL Language Reference Manual for the database engine that you are using.
+
+=head2 Standards Reference Information
 
-=item DBI Handle cleared whilst still active!
+More detailed information about the semantics of certain DBI methods
+that are based on ODBC and SQL/CLI standards is available on-line
+via microsoft.com, for ODBC, and www.jtc1sc32.org for the SQL/CLI
+standard:
+
+ DBI method        ODBC function     SQL/CLI Working Draft
+ ----------        -------------     ---------------------
+ column_info       SQLColumns        Page 124
+ foreign_key_info  SQLForeignKeys    Page 163
+ get_info          SQLGetInfo        Page 214
+ primary_key_info  SQLPrimaryKeys    Page 254
+ table_info        SQLTables         Page 294
+ type_info         SQLGetTypeInfo    Page 239
 
-Most probably due to a DBI bug. Possibly a DBD driver bug. Please report it.
+For example, for ODBC information on SQLColumns you'd visit:
 
-=item DBI Handle has uncleared implementors data
+  http://msdn.microsoft.com/library/en-us/odbc/htm/odbcsqlcolumns.asp
 
-Most probably a DBD driver bug. Please report it.
+If that URL ceases to work then use the MSDN search facility at:
 
-=item DBI Handle has %d uncleared child handles
+  http://search.microsoft.com/us/dev/
 
-Most probably due to a DBI bug. Possibly a DBD driver bug. Please report it.
+and search for C<SQLColumns returns> using the exact phrase option.
+The link you want will probably just be called C<SQLColumns> and will
+be part of the Data Access SDK.
 
-=back
+And for SQL/CLI standard information on SQLColumns you'd read page 124 of
+the (very large) SQL/CLI Working Draft available from:
 
-=head1 SEE ALSO
+  http://www.jtc1sc32.org/sc32/jtc1sc32.nsf/Attachments/7E3B41486BD99C3488256B410064C877/$FILE/32N0744T.PDF
 
-=head2 Driver and Database Documentation
 
-Refer to the documentation for the DBD driver that you are using.
+A hyperlinked, browsable version of the BNF syntax for SQL92 (plus
+Oracle 7 SQL and PL/SQL) is available here:
+
+  http://cui.unige.ch/db-research/Enseignement/analyseinfo/SQL92/BNFindex.html
+
+A BNF syntax for SQL3 is available here:
+
+  http://www.sqlstandards.org/SC32/WG3/Progression_Documents/Informal_working_drafts/iso-9075-2-1999.bnf
 
-Refer to the SQL Language Reference Manual for the database engine that you are using.
 
 =head2 Books and Journals
 
@@ -3663,6 +5355,20 @@
 
  The Perl Journal, April 1997.
 
+=head2 Perl Modules
+
+Index of DBI related modules available from CPAN:
+
+ http://search.cpan.org/search?mode=module&query=DBIx%3A%3A
+ http://search.cpan.org/search?mode=doc&query=DBI
+
+For a good comparison of RDBMS-OO mappers and some OO-RDBMS mappers
+(including Class::DBI, Alzabo, and DBIx::RecordSet in the former
+category and Tangram and SPOPS in the latter) see the Perl
+Object-Oriented Persistence project pages at:
+
+ http://poop.sourceforge.net
+
 =head2 Manual Pages
 
 L<perl(1)>, L<perlmod(1)>, L<perlbook(1)>
@@ -3670,25 +5376,27 @@
 =head2 Mailing List
 
 The I<dbi-users> mailing list is the primary means of communication among
-users of the DBI and its related modules. Subscribe and unsubscribe via:
+users of the DBI and its related modules. For details send email to:
 
- http://www.isc.org/dbi-lists.html
+ dbi-users-help@perl.org
 
 There are typically between 700 and 900 messages per month.  You have
 to subscribe in order to be able to post. However you can opt for a
 'post-only' subscription.
 
-Mailing list archives are held at:
+Mailing list archives (of variable quality) are held at:
 
  http://www.xray.mpe.mpg.de/mailing-lists/dbi/
- http://www.egroups.com/list/dbi-users/info.html
+ http://groups.yahoo.com/group/dbi-users
  http://www.bitmechanic.com/mail-archives/dbi-users/
+ http://marc.theaimsgroup.com/?l=perl-dbi&r=1&w=2
+ http://www.mail-archive.com/dbi-users%40perl.org/
 
 =head2 Assorted Related WWW Links
 
 The DBI "Home Page":
 
- http://www.symbolstone.org/technology/perl/DBI
+ http://dbi.perl.org/
 
 Other DBI related links:
 
@@ -3716,9 +5424,10 @@
 
  http://language.perl.com/style/
 
+
 =head2 FAQ
 
-Please also read the DBI FAQ which is installed as a DBI::FAQ module. 
+Please also read the DBI FAQ which is installed as a DBI::FAQ module.
 You can use I<perldoc> to read it by executing the C<perldoc DBI::FAQ> command.
 
 =head1 AUTHORS
@@ -3729,7 +5438,7 @@
 
 =head1 COPYRIGHT
 
-The DBI module is Copyright (c) 1994-2000 Tim Bunce. England.
+The DBI module is Copyright (c) 1994-2002 Tim Bunce. Ireland.
 All rights reserved.
 
 You may distribute under the terms of either the GNU General Public
@@ -3756,12 +5465,17 @@
 to Alligator Descartes for starting work on the "Programming the Perl
 DBI" book and letting me jump on board.
 
+Much of the DBI and DBD::Oracle was developed while I was Technical
+Director (CTO) of the Paul Ingram Group (www.ig.co.uk).  So I'd
+especially like to thank Paul for his generosity and vision in
+supporting this work for many years.
+
 =head1 TRANSLATIONS
 
-A German translation of this manual and other Perl module docs (all
-probably slightly out of date) is available, thanks to O'Reilly, at:
+A German translation of this manual (possibly slightly out of date) is
+available, thanks to O'Reilly, at:
 
-  http://www.oreilly.de/catalog/perlmodger/
+  http://www.oreilly.de/catalog/perldbiger/
 
 Some other translations:
 
@@ -3779,6 +5493,10 @@
 
   http://www.perlclinic.com
 
+For direct DBI and DBD::Oracle support, enhancement, and related work
+I am available for consultancy on standard commercial terms.
+
+
 =head1 TRAINING
 
 References to DBI related training resources. No recommendation implied.
@@ -3902,8 +5620,8 @@
 
 =item JDBC Server by Stuart 'Zen' Bishop zen@bf.rmit.edu.au
 
-The server is written in Perl. The client classes that talk to it are 
-of course in Java. Thus, a Java applet or application will be able to 
+The server is written in Perl. The client classes that talk to it are
+of course in Java. Thus, a Java applet or application will be able to
 comunicate via the JDBC API with any database that has a DBI driver installed.
 The URL used is in the form C<jdbc:dbi://host.domain.etc:999/Driver/DBName>.
 It seems to be very similar to some commercial products, such as jdbcKona.
@@ -3914,11 +5632,6 @@
 DBI::ProxyServer are part of the DBI distribution.
 
 =item SQL Parser
-
-	Hugo van der Sanden <hv@crypt.compulink.co.uk>
-	Stephen Zander <stephen.zander@mckesson.com>
-
-Based on the O'Reilly lex/yacc book examples and C<byacc>.
 
 See also the SQL::Statement module, a very simple SQL parser and engine,
 base of the DBD::CSV driver.
diff -u DBI-1.14/DBI.xs DBI-1.30/DBI.xs
--- DBI-1.14/DBI.xs	Sun Jun 11 03:27:43 2000
+++ DBI-1.30/DBI.xs	Thu Jul 18 15:23:51 2002
@@ -1,6 +1,6 @@
-/* $Id: DBI.xs,v 10.28 2000/06/11 02:27:27 timbo Exp $
+/* $Id: DBI.xs,v 11.14 2002/07/18 14:23:44 timbo Exp $
  *
- * Copyright (c) 1994, 1995, 1996, 1997  Tim Bunce  England.
+ * Copyright (c) 1994-2002  Tim Bunce  Ireland.
  *
  * See COPYRIGHT section in DBI.pm for usage and distribution rights.
  */
@@ -30,32 +30,33 @@
 #ifdef HAS_SETLINEBUF
 #define PerlIO_setlinebuf(f)        setlinebuf(f)
 #else
+#ifndef USE_PERLIO
 #define PerlIO_setlinebuf(f)        setvbuf(f, Nullch, _IOLBF, 0)
 #endif
 #endif
-
-#ifndef PerlIO
-#  define PerlIO FILE
-#  define PerlIO_printf fprintf
-#  define PerlIO_stderr() stderr
-#  define PerlIO_close(f) fclose(f)
-#  define PerlIO_open(f,m) fopen(f,m)
-#  define PerlIO_flush(f) Fflush(f)
-#  define PerlIO_puts(f,s) fputs(s,f)
 #endif
 
-#ifndef aTHX_
-#  define aTHX_
-#  define aTHXo_
+#ifndef CopFILEGV
 #  define CopFILEGV(cop) cop->cop_filegv
 #  define CopLINE(cop)   cop->cop_line
-#  define get_sv perl_get_sv
+#  define CopSTASH(cop)           cop->cop_stash
+#  define CopSTASHPV(cop)           (CopSTASH(cop) ? HvNAME(CopSTASH(cop)) : Nullch)
+#endif
+#ifndef PERL_GET_THX
+#define PERL_GET_THX ((void*)0)
+#endif
+#ifndef PerlProc_getpid
+#define PerlProc_getpid() getpid()
+extern Pid_t getpid (void);
+#endif
+#ifndef aTHXo_
+#define aTHXo_
 #endif
 
 
 static imp_xxh_t *dbih_getcom	   _((SV *h));
 static void       dbih_clearcom	   _((imp_xxh_t *imp_xxh));
-static int	  dbih_logmsg	   _((SV *h, imp_xxh_t *imp_xxh, char *fmt, ...));
+static int	  dbih_logmsg	   _((imp_xxh_t *imp_xxh, char *fmt, ...));
 static SV	 *dbih_make_com	   _((SV *parent_h, char *imp_class, STRLEN imp_size, STRLEN extra));
 static SV	 *dbih_make_fdsv   _((SV *sth, char *imp_class, STRLEN imp_size, char *col_name));
 static AV        *dbih_get_fbav	   _((imp_sth_t *imp_sth));
@@ -63,8 +64,11 @@
 static int        dbih_set_attr_k  _((SV *h, SV *keysv, int dbikey, SV *valuesv));
 static SV        *dbih_get_attr_k  _((SV *h, SV *keysv, int dbikey));
 
+static int	set_err _((SV *h, imp_xxh_t *imp_xxh, int errval, char *errstr, char *state));
 static int	quote_type _((int sql_type, int p, int s, int *base_type, void *v));
 static int	dbi_hash _((char *string, long i));
+static void	dbih_dumphandle _((SV *h, char *msg, int level));
+static void	dbih_dumpcom _((imp_xxh_t *imp_xxh, char *msg, int level));
 char *neatsvpv _((SV *sv, STRLEN maxlen));
 
 DBISTATE_DECLARE;
@@ -86,12 +90,18 @@
     U16   trace_level;
 } dbi_ima_t;
 
+/* These values are embedded in the data passed to install_method	*/
 #define IMA_HAS_USAGE		0x0001	/* check parameter usage	*/
 #define IMA_FUNC_REDIRECT	0x0002	/* is $h->func(..., "method")	*/
 #define IMA_KEEP_ERR		0x0004	/* don't reset err & errstr	*/
-#define IMA_spare	 	0x0008	/* */
+#define IMA_spare		0x0008	/* */
 #define IMA_NO_TAINT_IN   	0x0010	/* don't check for tainted args	*/
 #define IMA_NO_TAINT_OUT   	0x0020	/* don't taint results		*/
+#define IMA_COPY_STMT   	0x0040	/* copy sth Statement to dbh	*/
+#define IMA_END_WORK	   	0x0080	/* set on commit & rollback	*/
+#define IMA_STUB		0x0100	/* donothing eg $dbh->connected */
+#define IMA_CLEAR_STMT   	0x0200	/* clear Statement before call	*/
+#define IMA_PROF_EMPTY_STMT   	0x0400	/* profile as empty Statement	*/
 
 #define DBIc_STATE_adjust(imp_xxh, state)				 \
     (SvOK(state)	/* SQLSTATE is implemented by driver   */	 \
@@ -101,12 +111,11 @@
 	    : &sv_no)			/* Success ("00000")	*/	 \
     )
 
-#define DBI_LAST_HANDLE		g_dbi_last_h /* special fake inner handle	*/
-#define DBI_LAST_HANDLE_PARENT	(DBIc_PARENT_H(DBIh_COM(DBI_LAST_HANDLE)))
-#define DBI_IS_LAST_HANDLE(h)	(SvRVx(DBI_LAST_HANDLE) == SvRV(h))
-#define DBI_SET_LAST_HANDLE(h)	(SvRVx(DBI_LAST_HANDLE) =  SvRV(h))
-#define DBI_UNSET_LAST_HANDLE	(SvRVx(DBI_LAST_HANDLE) =  &sv_undef)
-#define DBI_LAST_HANDLE_OK	(SvRVx(DBI_LAST_HANDLE) != &sv_undef )
+#define DBI_LAST_HANDLE		g_dbi_last_h /* special fake inner handle */
+#define DBI_IS_LAST_HANDLE(h)	((DBI_LAST_HANDLE) == SvRV(h))
+#define DBI_SET_LAST_HANDLE(h)	((DBI_LAST_HANDLE) =  SvRV(h))
+#define DBI_UNSET_LAST_HANDLE	((DBI_LAST_HANDLE) =  &sv_undef)
+#define DBI_LAST_HANDLE_OK	((DBI_LAST_HANDLE) != &sv_undef )
 
 #ifdef PERL_LONG_MAX
 #define MAX_LongReadLen PERL_LONG_MAX
@@ -115,11 +124,13 @@
 #endif
 
 #ifdef DBI_USE_THREADS
-static char *dbi_build_opt = "-thread";
+static char *dbi_build_opt = "-ithread";
 #else
 static char *dbi_build_opt = "-nothread";
 #endif
 
+/* 32 bit magic FNV-0 and FNV-1 prime */
+#define FNV_32_PRIME ((UV)0x01000193)
 
 /* --- make DBI safe for multiple perl interpreters --- */
 /*     Contributed by Murray Nesbitt of ActiveState     */
@@ -190,32 +201,36 @@
 }
 
 static void
-dbi_bootinit()
+dbi_bootinit(dbistate_t * parent_dbis)
 {
 INIT_PERINTERP;
 
     Newz(dummy, DBIS, 1, dbistate_t);
 
-#ifdef DBI_USE_THREADS
-    Newz(1, DBIS->mutex, 1, dbi_mutex);
-    MUTEX_INIT(DBIS->mutex);
-#endif
-
     /* store version and size so we can spot DBI/DBD version mismatch	*/
     DBIS->check_version = check_version;
     DBIS->version = DBISTATE_VERSION;
     DBIS->size    = sizeof(*DBIS);
     DBIS->xs_version = DBIXS_VERSION;
 
-	/* store some other critical values */
-    DBIS->debug	 = 0;
+    DBIS->debug	 = parent_dbis ? parent_dbis->debug : 0;
     DBIS->logmsg = dbih_logmsg;
     DBIS->logfp	 = PerlIO_stderr();
-    DBIS->neatsvpvlen = get_sv("DBI::neat_maxlen", GV_ADDMULTI);
-    sv_setiv(DBIS->neatsvpvlen, 400);
+#ifdef DBI_USE_THREADS
+#if (PERL_VERSION >= 8) /* fp_dup prototype changed in 5.8.0 */
+    /* XXX the fp_dup type param is a fudge, but fp_dup doesn't use it anyway */
+    DBIS->logfp	 = parent_dbis ? fp_dup(parent_dbis->logfp,'>',0) : PerlIO_stderr();
+#endif
+#endif
+    DBIS->neatsvpvlen = perl_get_sv("DBI::neat_maxlen", GV_ADDMULTI);
+    if (!parent_dbis)
+	sv_setiv(DBIS->neatsvpvlen, 400);
+#ifdef DBI_USE_THREADS
+    DBIS->thr_owner = PERL_GET_THX;
+#endif
 
     /* publish address of dbistate so dynaloaded DBD's can find it	*/
-    sv_setiv(get_sv(DBISTATE_PERLNAME,1), (IV)DBIS);
+    sv_setiv(perl_get_sv(DBISTATE_PERLNAME,1), (IV)DBIS);
 
     DBISTATE_INIT; /* check DBD code to set DBIS from DBISTATE_PERLNAME	*/
 
@@ -235,8 +250,6 @@
     /* We want a handle reference but we don't want to increment	*/
     /* the handle's reference count and we don't want perl to try	*/
     /* to destroy it during global destruction. Take care!		*/
-    g_dbi_last_h  = newRV(&sv_undef);
-    SvROK_off(g_dbi_last_h);	/* so sv_clean_objs() won't destroy it	*/
     DBI_UNSET_LAST_HANDLE;	/* ensure setup the correct way		*/
 
     /* trick to avoid 'possible typo' warnings	*/
@@ -252,6 +265,19 @@
 /* Utility functions                                                 */
 
 
+static char *
+dbih_htype_name(int htype)
+{
+    switch(htype) {
+    case DBIt_DR: return "dr";
+    case DBIt_DB: return "db";
+    case DBIt_ST: return "st";
+    case DBIt_FD: return "fd";
+    default:      return "??";
+    }
+}
+
+
 char *
 neatsvpv(sv, maxlen) /* return a tidy ascii value, for debugging only */
     SV * sv;
@@ -344,12 +370,12 @@
 	maxlen = 6;
     maxlen -= 2;			/* account for quotes	*/
     if (len > maxlen) {
-	SvGROW(nsv, (int)(1+maxlen+4+1));
+	SvGROW(nsv, (1+maxlen+4+1));
 	sv_setpvn(nsv, "'", 1);
 	sv_catpvn(nsv, v, maxlen-3);	/* account for three dots */
 	sv_catpvn(nsv, "...'", 4);
     } else {
-	SvGROW(nsv, (int)(1+len+1+1));
+	SvGROW(nsv, (1+len+1+1));
 	sv_setpvn(nsv, "'", 1);
 	sv_catpvn(nsv, v, len);
 	sv_catpvn(nsv, "'", 1);
@@ -366,6 +392,26 @@
 }
 
 
+static int
+set_err(SV *h, imp_xxh_t *imp_xxh, int errval, char *errstr, char *state)
+{
+    STRLEN lna;
+    sv_setiv(DBIc_ERR(imp_xxh),    errval);
+    if (!errstr || !*errstr)
+	errstr = SvPV(DBIc_ERR(imp_xxh), lna);
+    sv_setpv(DBIc_ERRSTR(imp_xxh), errstr);
+    if (state && *state) {
+	if (strlen(state) != 5)
+	    croak("set_err: state must be 5 character string");
+	sv_setpv(DBIc_STATE(imp_xxh), state);
+    }
+    else {
+	(void)SvOK_off(DBIc_STATE(imp_xxh));
+    }
+    return 0;
+}
+
+
 static char *
 mkvname(stash, item, uplevel)	/* construct a variable name	*/
     HV *stash;
@@ -388,25 +434,39 @@
 
 
 static int
-dbi_hash(key, i)
-    char *key;
-    long i; /* spare */
-{
-    STRLEN klen = strlen(key);
-    U32 hash = 0;
-    while (klen--)
-        hash = hash * 33 + *key++;
-    hash &= 0x7FFFFFFF;	/* limit to 31 bits		*/
-    hash |= 0x40000000;	/* set bit 31			*/
-    return -(int)hash;	/* return negative int	*/
+dbi_hash(char *key, long type)
+{
+    if (type == 0) {
+	STRLEN klen = strlen(key);
+	U32 hash = 0;
+	while (klen--)
+	    hash = hash * 33 + *key++;
+	hash &= 0x7FFFFFFF;	/* limit to 31 bits		*/
+	hash |= 0x40000000;	/* set bit 31			*/
+	return -(int)hash;	/* return negative int	*/
+    }
+    else if (type == 1) {	/* Fowler/Noll/Vo hash	*/
+	/* see http://www.isthe.com/chongo/tech/comp/fnv/ */
+	U32 hash = 0x811c9dc5;
+	unsigned char *s = (unsigned char *)key;    /* unsigned string */
+	while (*s) {
+	    /* multiply by the 32 bit FNV magic prime mod 2^64 */
+	    hash *= FNV_32_PRIME;
+	    /* xor the bottom with the current octet */
+	    hash ^= (U32)*s++;
+	}
+	return hash;
+    }
+    croak("DBI::hash(%d): invalid type", type);
+    return 0; /* NOT REACHED */
 }
 
 
 static int
 #ifdef I_STDARG
-dbih_logmsg(SV *h, imp_xxh_t *imp_xxh, char *fmt, ...)
+dbih_logmsg(imp_xxh_t *imp_xxh, char *fmt, ...)
 #else
-dbih_logmsg(h, imp_xxh, fmt, va_alist)
+dbih_logmsg(imp_xxh, fmt, va_alist)
 SV *h;
 imp_xxh_t *imp_xxh;
 char *fmt;
@@ -426,7 +486,7 @@
 }
 
 
-static void
+static int
 set_trace_file(file)
     SV *file;
 {
@@ -434,24 +494,63 @@
     STRLEN lna;
     char *filename;
     PerlIO *fp;
-    if (!file)			/* no arg == no change			*/
-	return;
-    if (!SvOK(file)) {		/* undef arg == reset back to stderr	*/
-	if (DBILOGFP != PerlIO_stderr())
+    if (!file)		/* no arg == no change */
+	return 0;
+    /* XXX need to support file being a filehandle object */
+    filename = (SvOK(file)) ? SvPV(file, lna) : Nullch;
+    /* undef arg == reset back to stderr */
+    if (!filename || strEQ(filename,"STDERR")) {
+	if (DBILOGFP != PerlIO_stderr() && DBILOGFP != PerlIO_stdout())
 	    PerlIO_close(DBILOGFP);
 	DBILOGFP = PerlIO_stderr();
-	return;
+	return 1;
+    }
+    if (strEQ(filename,"STDOUT")) {
+	if (DBILOGFP != PerlIO_stderr() && DBILOGFP != PerlIO_stdout())
+	    PerlIO_close(DBILOGFP);
+	DBILOGFP = PerlIO_stdout();
+	return 1;
     }
-    filename = SvPV(file, lna);
     fp = PerlIO_open(filename, "a+");
-    if (fp == Nullfp)
-	PerlIO_printf(DBILOGFP,"Can't open trace file %s: %s", filename, Strerror(errno));
+    if (fp == Nullfp) {
+	warn("Can't open trace file %s: %s", filename, Strerror(errno));
+	return 0;
+    }
     else {
 	if (DBILOGFP != PerlIO_stderr())
 	    PerlIO_close(DBILOGFP);
 	DBILOGFP = fp;
+	/* if this line causes your compiler or linker to choke	*/
+	/* then just comment it out, it's not essential.	*/
 	PerlIO_setlinebuf(fp);	/* force line buffered output */
+	return 1;
+    }
+}
+
+
+static int
+set_trace(h, level, file)
+    SV *h;
+    int	level;
+    SV *file;
+{
+    dPERINTERP;
+    D_imp_xxh(h);
+    SV *dsv = DBIc_DEBUG(imp_xxh);
+    /* Return trace level in effect now. No change if new value not given */
+    int RETVAL = (DBIS->debug > SvIV(dsv)) ? DBIS->debug : SvIV(dsv);
+    set_trace_file(file);
+    if (level != RETVAL) {	 /* set value */
+	if (level > 0) {
+	    PerlIO_printf(DBILOGFP,"    %s trace level set to %d in DBI %s%s\n",
+		neatsvpv(h,0), level, XS_VERSION, dbi_build_opt);
+	    if (!dowarn && level>0)
+		PerlIO_printf(DBILOGFP,"    Note: perl is running without the recommended perl -w option\n");
+	    PerlIO_flush(DBILOGFP);
+	}
+	sv_setiv(dsv, level);
     }
+    return RETVAL;
 }
 
 
@@ -462,33 +561,40 @@
 {
     dPERINTERP;
     MAGIC *mg;
-    SV *hrv;
-    STRLEN lna;
+    SV *ohv;		/* outer HV after derefing the RV	*/
+    SV *hrv;		/* dni inner handle RV-to-HV		*/
+
+    /* enable a raw HV (not ref-to-HV) to be passed in, eg DBIc_MY_H */
+    ohv = SvROK(orv) ? SvRV(orv) : orv;
 
-    if (!SvROK(orv) || SvTYPE(SvRV(orv)) != SVt_PVHV) {
+    if (!ohv || SvTYPE(ohv) != SVt_PVHV) {
 	if (!what)
 	    return NULL;
+	if (DBIS->debug)
+	    sv_dump(orv);
 	if (!SvOK(orv))
 	    croak("%s given an undefined handle %s",
 		what, "(perhaps returned from a previous call which failed)");
-	croak("%s handle '%s' is not a DBI handle", what, SvPV(orv,lna));
+	croak("%s handle %s is not a DBI handle", what, neatsvpv(orv,0));
     }
-    if (!SvMAGICAL(SvRV(orv))) {
+    if (!SvMAGICAL(ohv)) {
 	sv_dump(orv);
-	croak("%s handle '%s' is not a DBI handle (has no magic)",
-		what, SvPV(orv,lna));
+	croak("%s handle %s is not a DBI handle (has no magic)",
+		what, neatsvpv(orv,0));
     }
 
-    if ( (mg=mg_find(SvRV(orv),'P')) == NULL) {	/* hash tie magic	*/
+    if ( (mg=mg_find(ohv,'P')) == NULL) {	/* hash tie magic	*/
 	/* maybe it's already an inner handle... */
-	if (mg_find(SvRV(orv), DBI_MAGIC) == NULL) {
+	if (mg_find(ohv, DBI_MAGIC) == NULL) {
 	    if (!what)
 		return NULL;
-	    croak("%s handle '%s' is not a valid DBI handle",
-		    what, SvPV(orv,lna));
+	    sv_dump(orv);
+	    croak("%s handle %s is not a valid DBI handle",
+		what, neatsvpv(orv,0));
 	}
 	hrv = orv; /* was already a DBI handle inner hash */
-    }else{
+    }
+    else {
 	hrv = mg->mg_obj;  /* inner hash of tie */
     }
 
@@ -496,32 +602,14 @@
     if (DBIS->debug && (!SvROK(hrv) || SvTYPE(SvRV(hrv)) != SVt_PVHV)) {
 	if (!what)
 	    return NULL;
-	croak("panic: %s inner handle '%s' is not a hash ref",
-		what, SvPV(hrv,lna));
+	sv_dump(orv);
+	croak("panic: %s inner handle %s is not a hash ref",
+		what, neatsvpv(hrv,0));
     }
     return hrv;
 }
 
 
-#ifdef DBI_USE_THREADS
-static void
-dbi_cond_signal(imp_xxh)
-    imp_xxh_t *imp_xxh;
-{
-    if (!imp_xxh || !DBIc_THR_COND(imp_xxh))
-	return;
-    DBI_LOCK;
-    if (PL_threadnum && DBIS->debug >= 4)
-	PerlIO_printf(DBILOGFP,"    .. thread %lu leaving %s\n",
-		DBIc_THR_USER(imp_xxh), HvNAME(DBIc_IMP_STASH(imp_xxh)));
-    DBIc_THR_USER(imp_xxh) = DBIc_THR_USER_NONE; /* free for other thread to enter */
-    COND_SIGNAL(DBIc_THR_COND(imp_xxh));
-    DBI_UNLOCK;
-}
-#endif
-
-
-
 
 /* --------------------------------------------------------------------	*/
 /* Functions to manage a DBI handle (magic and attributes etc).     	*/
@@ -533,16 +621,17 @@
     dPERINTERP;
     MAGIC *mg;
     SV *sv;
-    STRLEN lna;
 
     /* important and quick sanity check (esp non-'safe' Oraperl)	*/
-    if (!SvROK(hrv)			/* must at least be a ref */
-	&& hrv != DBI_LAST_HANDLE	/* special for var::FETCH */) {
+    if (SvROK(hrv)) 			/* must at least be a ref */
+	sv = SvRV(hrv);
+    else if (hrv == DBI_LAST_HANDLE)	/* special for var::FETCH */
+	sv = DBI_LAST_HANDLE;
+    else {
 	sv_dump(hrv);
-	croak("Invalid DBI handle %s", SvPV(hrv,lna));
+	croak("Invalid DBI handle %s", neatsvpv(hrv,0));
     }
 
-    sv = SvRV(hrv);
 
     /* Short cut for common case. We assume that a magic var always	*/
     /* has magic and that DBI_MAGIC, if present, will be the first.	*/
@@ -561,40 +650,51 @@
 
 
 static SV *
-dbih_setup_attrib(h, attrib, parent, read_only)
+dbih_setup_attrib(h, attrib, parent, read_only, optional)
     SV *h;
     char *attrib;
     SV *parent;
     int read_only;
+    int optional;
 {
     dPERINTERP;
     STRLEN len = strlen(attrib);
-    STRLEN lna;
+    SV **asvp;
 
-    SV *asv = *hv_fetch((HV*)SvRV(h), attrib, len, 1);
-    /* we assume that we won't have any existing 'undef' attribures here */
+    asvp = hv_fetch((HV*)SvRV(h), attrib, len, !optional);
+    /* we assume that we won't have any existing 'undef' attributes here */
     /* (or, alternately, we take undef to mean 'copy from parent')	 */
-    if (!SvOK(asv)) {	/* attribute doesn't already exists (the common case) */
-	SV **psv;
-	if (!parent || !SvROK(parent))
-	    croak("dbih_setup_attrib(%s): '%s' not set and no parent supplied",
-		    SvPV(h,lna), attrib);
-	psv = hv_fetch((HV*)SvRV(parent), attrib, len, 0);
-	if (!psv)
-	    croak("dbih_setup_attrib(%s): '%s' not set and not in parent",
-		    SvPV(h,lna), attrib);
-	sv_setsv(asv, *psv); /* copy attribute from parent to handle */
+    if (!(asvp && SvOK(*asvp))) { /* attribute doesn't already exists (the common case) */
+	SV **psvp;
+	if ((!parent || !SvROK(parent)) && !optional) {
+	    croak("dbih_setup_attrib(%s): %s not set and no parent supplied",
+		    neatsvpv(h,0), attrib);
+	}
+	psvp = hv_fetch((HV*)SvRV(parent), attrib, len, 0);
+	if (psvp) {
+	    if (!asvp)
+		asvp = hv_fetch((HV*)SvRV(h), attrib, len, 1);
+	    sv_setsv(*asvp, *psvp); /* copy attribute from parent to handle */
+	}
+	else {
+	    if (!optional)
+		croak("dbih_setup_attrib(%s): %s not set and not in parent",
+		    neatsvpv(h,0), attrib);
+	}
     }
     if (DBIS->debug >= 5) {
 	PerlIO_printf(DBILOGFP,"    dbih_setup_attrib(%s, %s, %s)",
 	    neatsvpv(h,0), attrib, neatsvpv(parent,0));
-	if (SvOK(asv))
-	     PerlIO_printf(DBILOGFP," %s (already defined)\n", neatsvpv(asv,0));
-	else PerlIO_printf(DBILOGFP," %s (copied from parent)\n", neatsvpv(asv,0));
-    }
-    if (read_only)
-	SvREADONLY_on(asv);
-    return asv;
+	if (!asvp)
+	     PerlIO_printf(DBILOGFP," undef (not defined)\n");
+	else
+	if (SvOK(*asvp))
+	     PerlIO_printf(DBILOGFP," %s (already defined)\n", neatsvpv(*asvp,0));
+	else PerlIO_printf(DBILOGFP," %s (copied from parent)\n", neatsvpv(*asvp,0));
+    }
+    if (read_only && asvp)
+	SvREADONLY_on(*asvp);
+    return asvp ? *asvp : &sv_undef;
 }
 
 
@@ -642,7 +742,7 @@
     if (imp_size == 0) {
 	/* get size of structure to allocate for common and imp specific data   */
 	char *imp_size_name = mkvname(imp_stash, "imp_data_size", 0);
-	imp_size = SvIV(get_sv(imp_size_name, 0x05));
+	imp_size = SvIV(perl_get_sv(imp_size_name, 0x05));
 	if (imp_size == 0) {
 	    imp_size = sizeof(imp_sth_t);
 	    if (sizeof(imp_dbh_t) > imp_size)
@@ -654,14 +754,15 @@
     }
 
     if (DBIS->debug >= 3)
-	PerlIO_printf(DBILOGFP,"    dbih_make_com(%s, %s, %d)\n",
-		neatsvpv(parent_h,0), imp_class, imp_size);
+	PerlIO_printf(DBILOGFP,"    dbih_make_com(%s, %s, %d) thr#%p\n",
+		neatsvpv(parent_h,0), imp_class, imp_size, PERL_GET_THX);
 
     dbih_imp_sv = newSV(imp_size);
 
     imp = (imp_xxh_t*)(void*)SvPVX(dbih_imp_sv);
     memzero((char*)imp, imp_size);
 
+    DBIc_DBISTATE(imp)  = DBIS;
     DBIc_IMP_STASH(imp) = imp_stash;
 
     if (!parent_h) {		/* only a driver (drh) has no parent	*/
@@ -672,28 +773,20 @@
 		   |DBIcf_ACTIVE	/* drivers are 'Active' by default	*/
 		   |DBIcf_AutoCommit	/* advisory, driver must manage this	*/
 	);
-#ifdef DBI_USE_THREADS
-	Newz(1, DBIc_THR_COND(imp), 1, dbi_cond);
-	COND_INIT(DBIc_THR_COND(imp));
-#endif
     } else {		
 	imp_xxh_t *parent_com = DBIh_COM(parent_h);
-	DBIc_PARENT_H(imp)    = SvREFCNT_inc(parent_h);	/* ensure it lives	*/
+	DBIc_PARENT_H(imp)    = (SV*)SvREFCNT_inc(parent_h); /* ensure it lives	*/
 	DBIc_PARENT_COM(imp)  = parent_com;	 	/* shortcut for speed	*/
 	DBIc_TYPE(imp)	      = DBIc_TYPE(parent_com) + 1;
 	DBIc_FLAGS(imp)       = DBIc_FLAGS(parent_com) & ~DBIcf_INHERITMASK;
-	DBIc_THR_COND(imp)    = DBIc_THR_COND(parent_com);
 	++DBIc_KIDS(parent_com);
     }
-    /* handles come into life with DBIc_THR_USER set to DBIc_THR_USER_NONE	*/
-    /* because DBIc_THR_USER indicates which thread has 'entered the DBI'	*/
-    /* on this handle and, at this point, none has.				*/
-    DBIc_THR_USER(imp) = DBIc_THR_USER_NONE;
+#ifdef DBI_USE_THREADS
+    DBIc_THR_USER(imp) = PERL_GET_THX ;
+#endif
 
     if (DBIc_TYPE(imp) == DBIt_ST) {
 	imp_sth_t *imp_sth = (imp_sth_t*)imp;
-	DBIc_NUM_FIELDS(imp_sth) = 0;	/* num of fields not known yet	*/
-	DBIc_FIELDS_AV(imp_sth)  = Nullav;
 	DBIc_ROW_COUNT(imp_sth)  = -1;
     }
 
@@ -721,22 +814,22 @@
     char imp_mem_name[300];
     HV  *imp_mem_stash;
     imp_xxh_t *imp;
-    STRLEN lna;
+    imp_xxh_t *parent_imp;
 
     h      = dbih_inner(orv, "dbih_setup_handle");
     parent = dbih_inner(parent, NULL);	/* check parent valid (& inner)	*/
 
     if (DBIS->debug >= 3)
 	PerlIO_printf(DBILOGFP,"    dbih_setup_handle(%s=>%s, %s, %lx, %s)\n",
-	    SvPV(orv,lna), SvPV(h,lna), imp_class, (long)parent, neatsvpv(imp_datasv,0));
+	    neatsvpv(orv,0), neatsvpv(h,0), imp_class, (long)parent, neatsvpv(imp_datasv,0));
 
     if (mg_find(SvRV(h), DBI_MAGIC) != NULL)
-	croak(errmsg, SvPV(orv,lna), imp_class, "already a DBI (or ~magic) handle");
+	croak(errmsg, neatsvpv(orv,0), imp_class, "already a DBI (or ~magic) handle");
 
     strcpy(imp_mem_name, imp_class);
     strcat(imp_mem_name, "_mem");
     if ( (imp_mem_stash = gv_stashpv(imp_mem_name, FALSE)) == NULL) 
-        croak(errmsg, SvPV(orv,lna), imp_mem_name, "unknown _mem package");
+        croak(errmsg, neatsvpv(orv,0), imp_mem_name, "unknown _mem package");
 
     DBI_LOCK;
 
@@ -747,24 +840,48 @@
     sv_bless(dbih_imp_rv, imp_mem_stash);
     sv_free(dbih_imp_rv);
 
-    DBIc_MY_H(imp) = SvRV(orv);	/* take _copy_ of pointer, not new ref	*/
+    DBIc_MY_H(imp) = (HV*)SvRV(orv);	/* take _copy_ of pointer, not new ref	*/
     DBIc_IMP_DATA(imp) = (imp_datasv) ? newSVsv(imp_datasv) : &sv_undef;
 
     if (DBIc_TYPE(imp) <= DBIt_ST) {
+	SV **tmp_svp;
+	parent_imp = (parent) ? DBIh_COM(parent) : NULL;
 	/* Copy some attributes from parent if not defined locally and	*/
 	/* also take address of attributes for speed of direct access.	*/
 	/* parent is null for drh, in which case h must hold the values	*/
-#define COPY_PARENT(name,ro) SvREFCNT_inc(dbih_setup_attrib(h, (name), parent, ro))
+#define COPY_PARENT(name,ro,opt) SvREFCNT_inc(dbih_setup_attrib(h,(name),parent,ro,opt))
 #define DBIc_ATTR(imp, f) _imp2com(imp, attr.f)
 	/* XXX we should validate that these are the right type (refs etc)	*/
-	DBIc_ATTR(imp, Err)      = COPY_PARENT("Err",1);	/* scalar ref	*/
-	DBIc_ATTR(imp, State)    = COPY_PARENT("State",1);	/* scalar ref	*/
-	DBIc_ATTR(imp, Errstr)   = COPY_PARENT("Errstr",1);	/* scalar ref	*/
-	DBIc_ATTR(imp, Handlers) = COPY_PARENT("Handlers",1);	/* array ref	*/
-	DBIc_ATTR(imp, Debug)    = COPY_PARENT("Debug",0);	/* scalar (int)	*/
-	if (parent)
-	     DBIc_LongReadLen(imp) = DBIc_LongReadLen(DBIh_COM(parent));
-	else DBIc_LongReadLen(imp) = DBIc_LongReadLen_init;
+	DBIc_ATTR(imp, Err)      = COPY_PARENT("Err",1,0);	/* scalar ref	*/
+	DBIc_ATTR(imp, State)    = COPY_PARENT("State",1,0);	/* scalar ref	*/
+	DBIc_ATTR(imp, Errstr)   = COPY_PARENT("Errstr",1,0);	/* scalar ref	*/
+	DBIc_ATTR(imp, Debug)    = COPY_PARENT("Debug",0,0);	/* scalar (int)	*/
+	DBIc_ATTR(imp, FetchHashKeyName) = COPY_PARENT("FetchHashKeyName",0,0);	/* scalar ref */
+	if (parent) {
+	    dbih_setup_attrib(h,"HandleError",parent,0,1);
+	    if (DBIc_has(parent_imp,DBIcf_Profile)) {
+		dbih_setup_attrib(h,"Profile",parent,0,1);
+	    }
+	    DBIc_LongReadLen(imp) = DBIc_LongReadLen(parent_imp);
+	}
+	else {
+	    DBIc_LongReadLen(imp) = DBIc_LongReadLen_init;
+	}
+
+	switch (DBIc_TYPE(imp)) {
+	case DBIt_DB:
+	    /* cache _inner_ handle, but also see quick_FETCH() */
+	    hv_store((HV*)SvRV(h), "Driver", 6, newRV(SvRV(parent)), 0);
+	    break;
+	case DBIt_ST:
+	    /* cache _inner_ handle, but also see quick_FETCH() */
+	    hv_store((HV*)SvRV(h), "Database", 8, newRV(SvRV(parent)), 0);
+	    /* copy (alias) Statement from the sth up into the dbh	*/
+	    tmp_svp = hv_fetch((HV*)SvRV(h), "Statement", 9, 1);
+	    if (tmp_svp)
+		hv_store((HV*)SvRV(parent), "Statement", 9, SvREFCNT_inc(*tmp_svp), 0);
+	    break;
+	}
     }
 
     /* Use DBI magic on inner handle to carry handle attributes 	*/
@@ -774,23 +891,33 @@
 
     DBI_SET_LAST_HANDLE(h);
 
-	DBI_UNLOCK;
+    DBI_UNLOCK;
 }
 
 
 static void
-dbih_dumpcom(imp_xxh, msg)
+dbih_dumphandle(SV *h, char *msg, int level)
+{
+    D_imp_xxh(h);
+    dbih_dumpcom(imp_xxh, msg, level);
+}
+
+static void
+dbih_dumpcom(imp_xxh, msg, level)
     imp_xxh_t *imp_xxh;
     char *msg;
+    int level;
 {
     dPERINTERP;
-    SV *flags = newSVpv("",0);
+    SV *flags = sv_2mortal(newSVpv("",0));
     STRLEN lna;
     char *pad = "      ";
     if (!msg)
 	msg = "dbih_dumpcom";
-    PerlIO_printf(DBILOGFP,"    %s (h 0x%lx, com 0x%lx):\n", msg,
-	    (IV)DBIc_MY_H(imp_xxh), (IV)imp_xxh);
+    PerlIO_printf(DBILOGFP,"    %s (%sh 0x%lx 0x%lx, com 0x%lx, imp %s):\n",
+	msg, dbih_htype_name(DBIc_TYPE(imp_xxh)),
+	(IV)DBIc_MY_H(imp_xxh), (IV)SvRVx(DBIc_MY_H(imp_xxh)),
+	(IV)imp_xxh, HvNAME(DBIc_IMP_STASH(imp_xxh)));
     if (DBIc_COMSET(imp_xxh))			sv_catpv(flags,"COMSET ");
     if (DBIc_IMPSET(imp_xxh))			sv_catpv(flags,"IMPSET ");
     if (DBIc_ACTIVE(imp_xxh))			sv_catpv(flags,"Active ");
@@ -799,25 +926,21 @@
     if (DBIc_is(imp_xxh, DBIcf_ChopBlanks))	sv_catpv(flags,"ChopBlanks ");
     if (DBIc_is(imp_xxh, DBIcf_RaiseError))	sv_catpv(flags,"RaiseError ");
     if (DBIc_is(imp_xxh, DBIcf_PrintError))	sv_catpv(flags,"PrintError ");
+    if (DBIc_is(imp_xxh, DBIcf_HandleError))	sv_catpv(flags,"HandleError ");
+    if (DBIc_is(imp_xxh, DBIcf_ShowErrorStatement))	sv_catpv(flags,"ShowErrorStatement ");
     if (DBIc_is(imp_xxh, DBIcf_AutoCommit))	sv_catpv(flags,"AutoCommit ");
+    if (DBIc_is(imp_xxh, DBIcf_BegunWork))	sv_catpv(flags,"BegunWork ");
     if (DBIc_is(imp_xxh, DBIcf_LongTruncOk))	sv_catpv(flags,"LongTruncOk ");
     if (DBIc_is(imp_xxh, DBIcf_MultiThread))	sv_catpv(flags,"MultiThread ");
     if (DBIc_is(imp_xxh, DBIcf_Taint))		sv_catpv(flags,"Taint ");
+    if (DBIc_is(imp_xxh, DBIcf_Profile))	sv_catpv(flags,"Profile ");
     PerlIO_printf(DBILOGFP,"%s FLAGS 0x%lx: %s\n", pad, (long)DBIc_FLAGS(imp_xxh), SvPV(flags,lna));
-    PerlIO_printf(DBILOGFP,"%s TYPE %d\n",	pad, DBIc_TYPE(imp_xxh));
-    PerlIO_printf(DBILOGFP,"%s PARENT %s\n",	pad, neatsvpv(DBIc_PARENT_H(imp_xxh),0));
-    PerlIO_printf(DBILOGFP,"%s KIDS %ld (%ld active)\n", pad,
+    PerlIO_printf(DBILOGFP,"%s PARENT %s\n",	pad, neatsvpv((SV*)DBIc_PARENT_H(imp_xxh),0));
+    PerlIO_printf(DBILOGFP,"%s KIDS %ld (%ld Active)\n", pad,
 		    (long)DBIc_KIDS(imp_xxh), (long)DBIc_ACTIVE_KIDS(imp_xxh));
-    PerlIO_printf(DBILOGFP,"%s IMP_DATA %s in '%s'\n", pad,
-	    neatsvpv(DBIc_IMP_DATA(imp_xxh),0), HvNAME(DBIc_IMP_STASH(imp_xxh)));
+    PerlIO_printf(DBILOGFP,"%s IMP_DATA %s\n", pad, neatsvpv(DBIc_IMP_DATA(imp_xxh),0));
     if (DBIc_LongReadLen(imp_xxh) != DBIc_LongReadLen_init)
 	PerlIO_printf(DBILOGFP,"%s LongReadLen %ld\n", pad, (long)DBIc_LongReadLen(imp_xxh));
-#ifdef DBI_USE_THREADS
-    if (DBIc_THR_COND(imp_xxh)) {
-	PerlIO_printf(DBILOGFP,"%s thread cond var 0x%lx", pad, (long)DBIc_THR_COND(imp_xxh));
-	PerlIO_printf(DBILOGFP,", tid %lu\n", (unsigned long)DBIc_THR_USER(imp_xxh));
-    }
-#endif
 
     if (DBIc_TYPE(imp_xxh) <= DBIt_DB) {
 	imp_dbh_t *imp_dbh = (imp_dbh_t*)imp_xxh;
@@ -829,6 +952,16 @@
 	PerlIO_printf(DBILOGFP,"%s NUM_OF_FIELDS %d\n", pad, DBIc_NUM_FIELDS(imp_sth));
 	PerlIO_printf(DBILOGFP,"%s NUM_OF_PARAMS %d\n", pad, DBIc_NUM_PARAMS(imp_sth));
     }
+    if (level > 0) {
+        SV* value;
+	char *key;
+	I32   keylen;
+	SV *inner = dbih_inner(DBIc_MY_H(imp_xxh), msg);
+	PerlIO_printf(DBILOGFP,"%s cached attributes:\n", pad);
+	while ( (value = hv_iternextsv((HV*)SvRV(inner), &key, &keylen)) ) {
+	    PerlIO_printf(DBILOGFP,"%s   '%s' => %s\n", pad, key, neatsvpv(value,0));
+	}
+    }
 }
 
 
@@ -838,6 +971,7 @@
 {
     dPERINTERP;
     dTHR;
+    dTHX;
     int dump = FALSE;
     int auto_dump = (DBIS->debug >= 6);
 
@@ -846,13 +980,24 @@
 
     /* --- pre-clearing sanity checks --- */
 
+#ifdef DBI_USE_THREADS
+    if (DBIc_THR_USER(imp_xxh) != my_perl) { /* don't clear handle that belongs to another thread */
+	if (DBIS->debug >= 3) {
+	    PerlIO_printf(DBILOGFP,"    skipped dbih_clearcom: DBI handle (type=%d, %s) is owned by thread %p not current thread %p\n", 
+		  DBIc_TYPE(imp_xxh), HvNAME(DBIc_IMP_STASH(imp_xxh)), DBIc_THR_USER(imp_xxh), my_perl) ;
+	    PerlIO_flush(DBILOGFP);
+	}
+	return;
+    }
+#endif
+
     if (!DBIc_COMSET(imp_xxh)) {	/* should never happen	*/
-	dbih_dumpcom(imp_xxh, "dbih_clearcom: DBI handle already cleared");
+	dbih_dumpcom(imp_xxh, "dbih_clearcom: DBI handle already cleared", 0);
 	return;
     }
 
     if (auto_dump)
-	dbih_dumpcom(imp_xxh,"dbih_clearcom");
+	dbih_dumpcom(imp_xxh,"dbih_clearcom", 0);
 
     if (!dirty) {
 	if (DBIc_TYPE(imp_xxh) <= DBIt_DB) {
@@ -885,7 +1030,7 @@
     }
 
     if (dump && !auto_dump) /* else was already dumped above */
-	dbih_dumpcom(imp_xxh, "dbih_clearcom");
+	dbih_dumpcom(imp_xxh, "dbih_clearcom", 0);
 
     /* --- pre-clearing adjustments --- */
 
@@ -903,21 +1048,15 @@
 
     sv_free(DBIc_IMP_DATA(imp_xxh));	/* do this first	*/
     if (DBIc_TYPE(imp_xxh) <= DBIt_ST) {	/* DBIt_FD doesn't have attr */
-	sv_free(_imp2com(imp_xxh, attr.Handlers));
 	sv_free(_imp2com(imp_xxh, attr.Debug));
 	sv_free(_imp2com(imp_xxh, attr.State));
 	sv_free(_imp2com(imp_xxh, attr.Err));
 	sv_free(_imp2com(imp_xxh, attr.Errstr));
+	sv_free(_imp2com(imp_xxh, attr.FetchHashKeyName));
     }
 
-#ifdef DBI_USE_THREADS
-    if (DBIc_TYPE(imp_xxh) == DBIt_DR && DBIc_THR_COND(imp_xxh)) {
-	COND_DESTROY(DBIc_THR_COND(imp_xxh));
-	Safefree(DBIc_THR_COND(imp_xxh));
-    }
-#endif
 
-    sv_free(DBIc_PARENT_H(imp_xxh));	/* do this last		*/
+    sv_free((SV*)DBIc_PARENT_H(imp_xxh));	/* do this last		*/
 
     DBIc_COMSET_off(imp_xxh);
 
@@ -967,9 +1106,6 @@
 {
     AV *av;
 
-    if (DBIc_TYPE(imp_sth) != DBIt_ST)
-	croak("dbih_get_fbav: bad handle type: %d", DBIc_TYPE(imp_sth));
-
     if ( (av = DBIc_FIELDS_AV(imp_sth)) == Nullav)
 	av = dbih_setup_fbav(imp_sth);
 
@@ -1068,7 +1204,6 @@
     dPERINTERP;
     dTHR;
     D_imp_xxh(h);
-    STRLEN lna;
     STRLEN keylen;
     char  *key = SvPV(keysv, keylen);
     int    htype = DBIc_TYPE(imp_xxh);
@@ -1078,7 +1213,7 @@
 
     if (DBIS->debug >= 3)
 	PerlIO_printf(DBILOGFP,"    STORE %s %s => %s\n",
-		SvPV(h,lna), neatsvpv(keysv,0), neatsvpv(valuesv,0));
+		neatsvpv(h,0), neatsvpv(keysv,0), neatsvpv(valuesv,0));
 
     if (strEQ(key, "CompatMode")) {
 	(on) ? DBIc_COMPAT_on(imp_xxh) : DBIc_COMPAT_off(imp_xxh);
@@ -1102,6 +1237,11 @@
     else if (strEQ(key, "InactiveDestroy")) {
 	(on) ? DBIc_IADESTROY_on(imp_xxh) : DBIc_IADESTROY_off(imp_xxh);
     }
+    else if (strEQ(key, "FetchHashKeyName")) {
+	if (htype >= DBIt_ST)
+	    croak("Can't set FetchHashKeyName for a statement handle, set in parent before prepare()");
+	cacheit = 1;	/* just save it */
+    }
     else if (strEQ(key, "RootClass")) {
 	cacheit = 1;	/* just save it */
     }
@@ -1125,6 +1265,54 @@
     else if (strEQ(key, "PrintError")) {
 	DBIc_set(imp_xxh,DBIcf_PrintError, on);
     }
+    else if (strEQ(key, "HandleError")) {
+	if ( on && (!SvROK(valuesv) || (SvTYPE(SvRV(valuesv)) != SVt_PVCV)) ) {
+	    croak("Can't set HandleError to '%s'",neatsvpv(valuesv,0));
+	}
+	DBIc_set(imp_xxh,DBIcf_HandleError, on);
+	cacheit = 1; /* child copy setup by dbih_setup_handle() */
+    }
+    else if (strEQ(key, "Profile")) {
+	char *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;
+	    dSP;
+	    I32 returns;
+	    TAINT_NOT; /* the require is presumed innocent till proven guilty */
+	    perl_require_pv("DBI/Profile.pm");
+	    if (SvTRUE(ERRSV)) {
+		STRLEN lna;
+		warn("Can't load %s: %s", class, SvPV(ERRSV,lna));
+		valuesv = &sv_undef;
+	    }
+	    else {
+		PUSHMARK(SP);
+		XPUSHs(sv_2mortal(newSVpv(class,0)));
+		XPUSHs(valuesv);
+		PUTBACK;
+		returns = perl_call_method("_auto_new", G_SCALAR);
+		if (returns != 1)
+		    croak("_auto_new");
+		SPAGAIN;
+		valuesv = POPs;
+		PUTBACK;
+	    }
+	    on = SvTRUE(valuesv); /* in case it returns undef */
+	}
+	if (on && !sv_isobject(valuesv)) {
+	    /* not blessed already - so default to DBI::Profile */
+	    HV *stash;
+	    perl_require_pv(class);
+	    stash = gv_stashpv(class, GV_ADDWARN);
+	    sv_bless(valuesv, stash);
+	}
+	DBIc_set(imp_xxh,DBIcf_Profile, on);
+	cacheit = 1; /* child copy setup by dbih_setup_handle() */
+    }
+    else if (strEQ(key, "ShowErrorStatement")) {
+	DBIc_set(imp_xxh,DBIcf_ShowErrorStatement, on);
+    }
     else if (strEQ(key, "MultiThread") && internal) {
 	/* here to allow pure-perl drivers to set MultiThread */
 	DBIc_set(imp_xxh,DBIcf_MultiThread, on);
@@ -1145,10 +1333,21 @@
 	    DBIc_CACHED_KIDS(imp_dbh) = (HV*)SvREFCNT_inc(SvRV(valuesv));
 	}
     }
-    else if (htype<=DBIt_DB && strEQ(key, "AutoCommit")) {
-	/* The driver should have intercepted this and handled it.	*/
-	croak("DBD driver has not implemented the AutoCommit attribute");
-	/* DBIc_set(imp_xxh,DBIcf_AutoCommit, on); */
+    else if (htype<=DBIt_DB && keylen==10 && strEQ(key, "AutoCommit")) {
+	/* driver should have intercepted this and either handled it	*/
+	/* or set valuesv to either the 'magic' on or off value.	*/
+	if (SvIV(valuesv) != -900 && SvIV(valuesv) != -901)
+	    croak("DBD driver has not implemented the AutoCommit attribute");
+	DBIc_set(imp_xxh,DBIcf_AutoCommit, (SvIV(valuesv)==-901));
+    }
+    else if (htype==DBIt_DB && keylen==9 && strEQ(key, "BegunWork")) {
+	DBIc_set(imp_xxh,DBIcf_BegunWork, on);
+    }
+    else if (keylen==10  && strEQ(key, "TraceLevel")) {
+	set_trace(h, SvIV(valuesv), Nullch);
+    }
+    else if (keylen==9  && strEQ(key, "TraceFile")) { /* XXX undocumented and readonly */
+	set_trace_file(valuesv);
     }
     else if (htype==DBIt_ST && strEQ(key, "NUM_OF_FIELDS")) {
 	D_imp_sth(h);
@@ -1167,22 +1366,17 @@
 	    char *hint = "";
 	    if (strEQ(key, "NUM_FIELDS"))
 		hint = " (perhaps you meant NUM_OF_FIELDS)";
-	    croak("Can't set %s->{%s}: unrecognised attribute%s",
-		    SvPV(h,lna), key, hint);
+	    croak("Can't set %s->{%s}: unrecognised attribute or invalid value%s",
+		    neatsvpv(h,0), key, hint);
 	}
 	/* Allow private_* attributes to be stored in the cache.	*/
 	/* This is designed to make life easier for people subclassing	*/
 	/* the DBI classes and may be of use to simple perl DBD's.	*/
-	if (strnNE(key,"private_",8) && strnNE(key,"dbd_",4))
+	if (strnNE(key,"private_",8) && strnNE(key,"dbd_",4) && strnNE(key,"dbi_",4))
 	    return FALSE;
 	cacheit = 1;
     }
     if (cacheit) {
-	/* old code
-	SV **svp = hv_fetch((HV*)SvRV(h), key, keylen, 1);
-	SvREFCNT_dec(*svp);
-	*svp = SvREFCNT_inc(valuesv);
-	*/
 	hv_store((HV*)SvRV(h), key, keylen, newSVsv(valuesv), 0);
     }
     return TRUE;
@@ -1214,36 +1408,51 @@
     /* XXX needs to be split into separate dr/db/st funcs	*/
     /* XXX probably needs some form of hashing->switch lookup	*/
 
-    if (htype==DBIt_ST && keylen==7 && strnEQ(key, "NAME_", 5)
-	&& (key[5]=='u' || key[5]=='l') && key[6] == 'c'
+    if (htype==DBIt_ST && *key=='N' && (keylen==7 || keylen==9 || keylen==12)
+	&& strnEQ(key, "NAME_", 5)
+	&& (	(keylen==9 && strEQ(key, "NAME_hash"))
+	    ||	((key[5]=='u' || key[5]=='l') && key[6] == 'c'
+		    && (!key[7] || strnEQ(&key[7], "_hash", 5)))
+	)
     ) {
 	D_imp_sth(h);
-	AV *name = NULL;
+	AV *name_av = NULL;
 
+	/* fetch from tied outer handle to trigger FETCH magic	*/
 	svp = hv_fetch((HV*)DBIc_MY_H(imp_sth), "NAME",4, FALSE);
 	sv = (svp) ? *svp : &sv_undef;
-
-	if (SvGMAGICAL(sv))
-	    mg_get(sv);	/* can core dump in 5.004 */
-	name = (AV*)SvRV(sv);
-
-	if (sv && name) {
-	    int up = (key[5] == 'u');
-	    AV *av = newAV();
+	if (SvGMAGICAL(sv))	/* resolve the magic		*/
+	    mg_get(sv);		/* can core dump in 5.004	*/
+	name_av = (AV*)SvRV(sv);
+
+	if (sv && name_av) {
+	    char *name;
+	    int upcase = (key[5] == 'u');
+	    AV *av = Nullav;
+	    HV *hv = Nullhv;
+	    if (strEQ(&key[strlen(key)-5], "_hash"))
+		 hv = newHV();
+	    else av = newAV();
 	    i = DBIc_NUM_FIELDS(imp_sth);
-	    assert(i == AvFILL(name)+1);
+	    assert(i == AvFILL(name_av)+1);
 	    while(--i >= 0) {
-		sv = newSVsv(AvARRAY(name)[i]);
-		for (p = SvPV(sv,lna); p && *p; ++p) {
+		sv = newSVsv(AvARRAY(name_av)[i]);
+		name = SvPV(sv,lna);
+		if (key[5] != 'h') {	/* "NAME_hash" */
+		    for (p = name; p && *p; ++p) {
 #ifdef toUPPER_LC
-		    *p = (up) ? toUPPER_LC(*p) : toLOWER_LC(*p);
+			*p = (upcase) ? toUPPER_LC(*p) : toLOWER_LC(*p);
 #else
-		    *p = (up) ? toUPPER(*p) : toLOWER(*p);
+			*p = (upcase) ? toUPPER(*p) : toLOWER(*p);
 #endif
+		    }
 		}
-		av_store(av, i, sv);
+		if (av)
+		    av_store(av, i, sv);
+		else
+		    hv_store(hv, name, SvCUR(sv), newSViv(i), 0);
 	    }
-	    valuesv = newRV(sv_2mortal((SV*)av));
+	    valuesv = newRV(sv_2mortal( (av ? (SV*)av : (SV*)hv) ));
 	    cacheit = TRUE;	/* can't change */
 	}
     }
@@ -1261,6 +1470,21 @@
     else if (keylen==12 && strEQ(key, "RowCacheSize")) {
 	valuesv = &sv_undef;
     }
+    else if (keylen==4 && strEQ(key, "Type")) {
+	char *type = dbih_htype_name(htype);
+	valuesv = newSVpv(type,0);
+	cacheit = TRUE;	/* can't change */
+    }
+    /* these are here but are, sadly, not called because not-preloading	*/
+    /* them into the handle attrib cache caused wierdness in t/proxy.t	*/
+    /* that I never got to the bottom of. One day maybe.		*/
+    else if (  (htype==DBIt_ST && keylen==8 && strEQ(key, "Database"))
+	    || (htype==DBIt_DB && keylen==6 && strEQ(key, "Driver"))
+    ) {
+	D_imp_from_child(imp_dbh, imp_dbh_t, imp_xxh);
+	valuesv = newRV((SV*)DBIc_MY_H(imp_dbh));
+	cacheit = FALSE;  /* else create ref loop */
+    }
     else if (htype==DBIt_ST && keylen==11 && strEQ(key, "RowsInCache")) {
 	valuesv = &sv_undef;
     }
@@ -1270,6 +1494,9 @@
     else if (keylen==4  && strEQ(key, "Kids")) {
 	valuesv = newSViv(DBIc_KIDS(imp_xxh));
     }
+    else if (keylen==10  && strEQ(key, "TraceLevel")) {
+	valuesv = newSViv(DBIc_DEBUGIV(imp_xxh));
+    }
     else if (keylen==10  && strEQ(key, "ActiveKids")) {
 	valuesv = newSViv(DBIc_ACTIVE_KIDS(imp_xxh));
     }
@@ -1302,6 +1529,9 @@
     else if (keylen==10 && strEQ(key, "PrintError")) {
 	valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_PrintError));
     }
+    else if (keylen==18 && strEQ(key, "ShowErrorStatement")) {
+	valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_ShowErrorStatement));
+    }
     else if (keylen==10 && strEQ(key, "MultiThread")) {
 	valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_MultiThread));
     }
@@ -1309,18 +1539,23 @@
 	valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_Taint));
     }
     else if (htype<=DBIt_DB && keylen==10 && strEQ(key, "AutoCommit")) {
-	/* The driver should have intercepted this and handled it.	*/
-	croak("DBD driver has not implemented the AutoCommit attribute");
-	/* valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_AutoCommit)); */
+	valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_AutoCommit));
+    }
+    else if (keylen==9  && strEQ(key, "BegunWork")) {
+	valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_BegunWork));
     }
     else {	/* finally check the actual hash just in case	*/
 	svp = hv_fetch((HV*)SvRV(h), key, keylen, FALSE);
 	if (svp)
 	    valuesv = *svp;
-	else if (isUPPER(*key))
-	    croak("Can't get %s->{%s}: unrecognised attribute",SvPV(h,lna),key);
+	else if (!isUPPER(*key))	/* dbd_*, private_* etc */
+	    valuesv = &sv_undef;
+	else if (	(*key=='H' && strEQ(key, "HandleError"))
+		||	(*key=='P' && strEQ(key, "ParamValues"))
+		||	(*key=='P' && strEQ(key, "Profile"))	)
+	    valuesv = &sv_undef;
 	else
-	    valuesv = &sv_undef;	/* dbd_*, private_* etc	*/
+	    croak("Can't get %s->{%s}: unrecognised attribute",neatsvpv(h,0),key);
     }
     if (cacheit) {
 	svp = hv_fetch((HV*)SvRV(h), key, keylen, TRUE);
@@ -1329,7 +1564,7 @@
 	sv_free(sv);
     }
     if (DBIS->debug >= 3)
-	PerlIO_printf(DBILOGFP,"    FETCH %s %s = %s%s\n", SvPV(h,lna),
+	PerlIO_printf(DBILOGFP,"    .. FETCH %s %s = %s%s\n", neatsvpv(h,0),
 	    neatsvpv(keysv,0), neatsvpv(valuesv,0), cacheit?" (cached)":"");
     if (valuesv == &sv_yes || valuesv == &sv_no || valuesv == &sv_undef)
 	return valuesv;	/* no need to mortalize yes or no */
@@ -1353,7 +1588,7 @@
     if (svp)
 	valuesv = *svp;
     else
-    if (!SvOK(ph=DBIc_PARENT_H(imp_xxh)))
+    if (!SvOK(ph=(SV*)DBIc_PARENT_H(imp_xxh)))
 	valuesv = Nullsv;
     else /* recurse up */
 	valuesv = dbih_find_attr(ph, keysv, copydown, spare);
@@ -1369,62 +1604,15 @@
 
 static SV *
 dbih_event(hrv, evtype, a1, a2)
-    SV *hrv;    /* ref to inner hash */
+    SV *hrv;
     char *evtype;
     SV *a1, *a2;
 {
-    dPERINTERP;
-    dSP;
-    D_imp_xxh(hrv);
     /* We arrive here via DBIh_EVENT* macros (see DBIXS.h) called from	*/
     /* DBD driver C code OR $h->event() method (in DBD::_::common)	*/
-    /* If an array of handlers is defined then call them in reverse	*/
-    /* order until one returns true */
-
-    STRLEN lna;
-    AV *handlers_av = (AV*)DBIc_HANDLERS(imp_xxh);
-    SV *status = &sv_undef;
-    SV *evtype_sv = Nullsv;
-    int i;
-
-    if (DBIS->debug >= 3)
-	PerlIO_printf(DBILOGFP,"    %s EVENT %s %s on %s\n",
-	    evtype, neatsvpv(a1,0), neatsvpv(a2,0), SvPV(hrv,lna));
-
-    if (SvTYPE(handlers_av) != SVt_PVAV) {	/* must be \@ or undef	*/
-	if (SvOK(handlers_av))
-	    warn("%s->{Handlers} (%s) is not an array reference or undef",
-		neatsvpv(hrv,0), neatsvpv((SV*)handlers_av,0));
-	return &sv_undef;
-    }
-
-    i = av_len(handlers_av) + 1;
-    while(--i >= 0) {	/* Call each handler in turn	*/
-	SV *sv = *av_fetch(handlers_av, i, 1);
-	if (!evtype_sv)
-	    evtype_sv = sv_2mortal(newSVpv(evtype,0));
-	/* call handler */
-/* XXX probably need a better way. Note that DBD::ExampleP uses this! */
-	PUSHMARK(sp);
-	EXTEND(sp, 4);
-	PUSHs(hrv);
-	PUSHs(evtype_sv);
-	if (SvOK(a2) || SvOK(a1)) { PUSHs(a1); }
-	if (SvOK(a2))             { PUSHs(a2); }
-	PUTBACK;
-	perl_call_sv(sv, G_SCALAR);	/* NOTE: May longjmp (die)	*/
-	SPAGAIN;
-	status = POPs;
-	PUTBACK;
-	if (DBIS->debug >= 3)
-	    PerlIO_printf(DBILOGFP, "    %s handler%d %s returned %s\n",
-		    neatsvpv(hrv,0), i,
-		    neatsvpv(sv,0), neatsvpv(status,0));
-	if (SvTRUE(status))	/* event was handled so		*/
-	    break;		/* don't call any more handlers	*/
-    }
-
-    return status;	/* true if event was 'handled' (not defined)	*/
+    /* XXX VERY OLD INTERFACE/CONCEPT MAY GO SOON */
+    /* OR MAY EVOLVE INTO A WAY TO HANDLE 'SUCCESS_WITH_INFO'/'WARNINGS' from db */
+    return &sv_undef;
 }
 
 
@@ -1442,7 +1630,7 @@
      (actually it sets a flag so dispatch will run code for us).
  - If it's a ref to a CODE ref then return the CODE ref
      (an escape mechanism to allow real code refs to be stored).
- - Else return NULL (it's some other form of ref, let driver do it).
+ - Else return the ref.
 */
 
 static SV * 
@@ -1466,35 +1654,109 @@
     if ( (type=SvTYPE(SvRV(sv))) == SVt_RV
 	&& SvTYPE(SvRV(SvRV(sv))) == SVt_PVCV)
 	return SvRV(sv); /* return deref if ref to CODE ref */
-    if (type != SVt_PVCV)
-	return sv;	 /* return non-code refs */
-    *imp_msv = (SV*)SvRV(sv); /* tell dispatch() to execute this code instead */
-    return NULL;
+    return sv;
 }
 
 
-static void
-log_where(logfp, trace_level)
-    PerlIO *logfp;
-    int trace_level;
+STATIC I32
+dbi_dopoptosub_at(PERL_CONTEXT *cxstk, I32 startingblock)
 {
-    dTHR;
-    char *file, *sep;
-    STRLEN len;
-    if (dirty) {
-	PerlIO_printf(logfp," during global destruction.");
-	return;
+    I32 i;
+    register PERL_CONTEXT *cx;
+    for (i = startingblock; i >= 0; i--) {
+	cx = &cxstk[i];
+	switch (CxTYPE(cx)) {
+	default:
+	    continue;
+	case CXt_EVAL:
+	case CXt_SUB:
+#ifdef CXt_FORMAT
+	case CXt_FORMAT:
+#endif
+	    DEBUG_l( Perl_deb(aTHX_ "(Found sub #%ld)\n", (long)i));
+	    return i;
+	}
     }
-    if (!CopLINE(curcop)) {
-	PerlIO_printf(logfp," at unknown location!");
-	return;
+    return i;
+}
+
+
+static char *
+dbi_caller(long *line)
+{
+    register I32 cxix;
+    register PERL_CONTEXT *cx;
+    register PERL_CONTEXT *ccstack = cxstack;
+    PERL_SI *top_si = PL_curstackinfo;
+    char *stashname;
+
+    *line = -1;
+    for ( cxix = dbi_dopoptosub_at(ccstack, cxstack_ix) ;; cxix = dbi_dopoptosub_at(ccstack, cxix - 1)) {
+	/* we may be in a higher stacklevel, so dig down deeper */
+	while (cxix < 0 && top_si->si_type != PERLSI_MAIN) {
+	    top_si = top_si->si_prev;
+	    ccstack = top_si->si_cxstack;
+	    cxix = dbi_dopoptosub_at(ccstack, top_si->si_cxix);
+	}
+	if (cxix < 0) {
+	    return NULL;
+	}
+	if (PL_DBsub && cxix >= 0 && ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub))
+	    continue;
+	cx = &ccstack[cxix];
+	stashname = CopSTASHPV(cx->blk_oldcop);
+	if (!stashname)
+	    continue;
+	if (!(stashname[0] == 'D'
+	    && stashname[1] == 'B'
+	    && strchr("DI", stashname[2])
+	    && (!stashname[3] || (stashname[3] == ':' && stashname[4] == ':')))) 
+	{
+	    STRLEN len;
+	    *line = (I32)CopLINE(cx->blk_oldcop);
+	    return SvPV(GvSV(CopFILEGV(cx->blk_oldcop)), len);
+	}
+	cxix = dbi_dopoptosub_at(ccstack, cxix - 1);
     }
-    file = SvPV(GvSV(CopFILEGV(curcop)), len);
-    if (trace_level<=4) {
-	if ( (sep=strrchr(file,'/')) || (sep=strrchr(file,'\\')))
-	    file = sep+1;
+    /* NOT REACHED */
+    return NULL;
+}
+
+static char *
+log_where(int trace_level, SV *buf, int append, char *suffix)
+{
+    dTHR;
+    if (!buf) {
+	buf = sv_2mortal(newSV(80));
+	sv_setpv(buf,"");
     }
-    PerlIO_printf(logfp," at %s line %ld.", file, (long)CopLINE(curcop));
+    else
+    if (!append)
+	sv_setpv(buf,"");
+    if (CopLINE(curcop)) {
+	STRLEN len;
+	long  near_line = CopLINE(curcop);
+	char *near_file = SvPV(GvSV(CopFILEGV(curcop)), len);
+	char *file = near_file;
+	if (trace_level <= 4) {
+	    char *sep;
+	    if ( (sep=strrchr(file,'/')) || (sep=strrchr(file,'\\')))
+		file = sep+1;
+	}
+	sv_catpvf(buf, " at %s line %ld", file, near_line);
+
+	if (trace_level >= 3) {
+	    long far_line;
+	    char *far_file = dbi_caller(&far_line);
+	    if (far_file && !(far_line==near_line && strEQ(far_file,near_file)) )
+		sv_catpvf(buf, " via %s line %ld", far_file, far_line);
+	}
+    }
+    if (dirty)
+	sv_catpvf(buf, " during global destruction");
+    if (suffix)
+	sv_catpv(buf, suffix);
+    return SvPVX(buf);
 }
 
 
@@ -1519,6 +1781,216 @@
     }
 }
 
+static double
+dbi_time() {
+# ifdef HAS_GETTIMEOFDAY
+    struct timeval when;
+    gettimeofday(&when, (struct timezone *) 0);
+    return when.tv_sec + (when.tv_usec / 1000000.0);
+# else	/* per-second is almost useless */
+    return time(NULL);
+# endif
+}
+
+static void
+dbi_profile(SV *h, imp_xxh_t *imp_xxh, char *statement, SV *method, double t1, double t2)
+{
+#define DBIprof_MAX_PATH_ELEM	9	/* STATEMENT->$Statement->$method */
+#define DBIprof_COUNT		0
+#define DBIprof_TOTAL_TIME	1
+#define DBIprof_FIRST_TIME	2
+#define DBIprof_MIN_TIME	3
+#define DBIprof_MAX_TIME	4
+#define DBIprof_FIRST_CALLED	5
+#define DBIprof_LAST_CALLED	6
+#define DBIprof_max_index	6
+    double ti = t2 - t1;
+    char *path[DBIprof_MAX_PATH_ELEM+1];
+    int idx = -1;
+    STRLEN lna;
+    SV *profile;
+    SV *tmp;
+    AV *av;
+
+    int call_depth = DBIc_CALL_DEPTH(imp_xxh);
+    int parent_call_depth = DBIc_PARENT_COM(imp_xxh) ? DBIc_CALL_DEPTH(DBIc_PARENT_COM(imp_xxh)) : 0;
+    /* Only count calls originating from the application code	*/
+    /* *MAY* be made configurable later				*/
+    /* XXX BEWARE that if nested call profile data is merged	*/
+    /* with the non-nested data then we'll get invalid results	*/
+    if (call_depth > 1 || parent_call_depth > 0)
+	return;
+
+    if (!DBIc_has(imp_xxh, DBIcf_Profile))
+	return;
+
+    profile = *hv_fetch((HV*)SvRV(h), "Profile", 7, 1);
+    if (!( profile && SvROK(profile) && SvTYPE(SvRV(profile))==SVt_PVHV )) {
+	DBIc_set(imp_xxh, DBIcf_Profile, 0); /* disable */
+	if (!dirty)
+	    warn("Profile attribute isn't a hash ref (%s,%d)", neatsvpv(profile,0), SvTYPE(profile));
+	return;
+    }
+
+    if (!statement) {
+	SV **psv = hv_fetch((HV*)SvRV(h), "Statement", 9, 0);
+	statement = (psv && SvOK(*psv)) ? SvPV(*psv, lna) : "";
+    }
+    if (DBIc_DBISTATE(imp_xxh)->debug >= 4)
+	PerlIO_printf(DBIc_LOGPIO(imp_xxh), "dbi_profile %s %f %d %d q{%s}\n",
+		neatsvpv((SvTYPE(method)==SVt_PVCV) ? (SV*)CvGV(method) : method, 0),
+		ti, call_depth, parent_call_depth, statement);
+
+    idx = 0;
+    path[idx++] = "Data";
+    tmp = *hv_fetch((HV*)SvRV(profile), "Path", 4, 1);
+    if (SvROK(tmp) && SvTYPE(SvRV(tmp))==SVt_PVCV) {
+	/* call sub, use returned list of values as path */
+	/* if no values returned then don't save data	*/
+	path[idx++] = Nullch;
+    }
+    else if (SvROK(tmp) && SvTYPE(SvRV(tmp))==SVt_PVAV) {
+	int len;
+	av = (AV*)SvRV(tmp);
+	len = av_len(av); /* -1=empty, 0=one element */
+	for ( ;(idx-1) <= len && idx < DBIprof_MAX_PATH_ELEM; ++idx) {
+	    SV *pathsv = AvARRAY(av)[idx-1];
+	    char *p;
+	    switch(SvIOK(pathsv) ? SvIV(pathsv) : 0) {
+	    case -2100000001:
+		p = statement;
+		break;
+	    case -2100000002:
+		p = (SvTYPE(method)==SVt_PVCV)
+			? GvNAME(CvGV(method))
+			: (isGV(method) ? GvNAME(method) : SvPV(method,lna));
+		break;
+	    case -2100000003:
+		if (SvTYPE(method) == SVt_PVCV) {
+		    p = SvPV((SV*)CvGV(method), lna);
+		}
+		else if (isGV(method)) {
+		    /* just using SvPV(method,lna) sometimes causes an error:	*/
+		    /* "Can't coerce GLOB to string" so we use gv_efullname()	*/
+		    SV *tmpsv = sv_2mortal(newSVpv("",0));
+		    gv_efullname(tmpsv, (GV*)method);
+		    p = SvPV(tmpsv,lna);
+		}
+		else {
+		    p = SvPV(method,lna);
+		}
+		break;
+	    default:
+		p = SvPV(pathsv,lna);
+		break;
+	    }
+	    path[idx] = p;
+	}
+    }
+    else if (SvOK(tmp)) {
+	DBIc_set(imp_xxh, DBIcf_Profile, 0); /* disable */
+	warn("Profile Path attribute isn't valid (%s)", neatsvpv(tmp,0));
+	return;
+    }
+    else {
+	path[idx++] = statement;
+    }
+    path[idx++] = Nullch;
+
+    tmp = profile;
+    for (idx=0; path[idx]; ++idx) {
+	if (SvROK(tmp))
+	    tmp = SvRV(tmp);
+	else if (SvTYPE(tmp) != SVt_PVHV) {
+	    HV *hv = newHV();
+	    if (SvOK(tmp))
+		warn("Profile data element %s replaced with new hash ref", neatsvpv(tmp,0));
+	    sv_setsv(tmp, newRV_noinc((SV*)hv));
+	    tmp = (SV*)hv;
+	}
+	if (SvTYPE(tmp) != SVt_PVHV)
+	    break;
+	tmp = *hv_fetch((HV*)tmp, path[idx], strlen(path[idx]), 1);
+	/* warn("%d hv_fetch %s = %s", idx, path[idx], neatsvpv(tmp,0)); */
+    }
+    if (!SvOK(tmp)) {
+	av = newAV();
+	sv_setsv(tmp, newRV_noinc((SV*)av));
+	av_store(av, DBIprof_COUNT,		newSViv(1));
+	av_store(av, DBIprof_TOTAL_TIME,	newSVnv(ti));
+	av_store(av, DBIprof_FIRST_TIME,	newSVnv(ti));
+	av_store(av, DBIprof_MIN_TIME,		newSVnv(ti));
+	av_store(av, DBIprof_MAX_TIME,		newSVnv(ti));
+	av_store(av, DBIprof_FIRST_CALLED,	newSVnv(t1));
+	av_store(av, DBIprof_LAST_CALLED,	newSVnv(t1));
+        return;
+    }
+    if (SvROK(tmp))
+	tmp = SvRV(tmp);
+    if (SvTYPE(tmp) != SVt_PVAV)
+	croak("Invalid Profile data leaf element at depth %d: %s (type %d)",
+		idx, neatsvpv(tmp,0), SvTYPE(tmp));
+    av = (AV*)tmp;
+    sv_inc( *av_fetch(av, DBIprof_COUNT, 1));
+    tmp = *av_fetch(av, DBIprof_TOTAL_TIME, 1);
+    sv_setnv(tmp, SvNV(tmp) + ti);
+    tmp = *av_fetch(av, DBIprof_MIN_TIME, 1);
+    if (ti < SvNV(tmp)) sv_setnv(tmp, ti);
+    tmp = *av_fetch(av, DBIprof_MAX_TIME, 1);
+    if (ti > SvNV(tmp)) sv_setnv(tmp, ti);
+    sv_setnv( *av_fetch(av, DBIprof_LAST_CALLED, 1), t1);
+    return;
+}
+
+static void
+dbi_profile_merge(SV *dest, SV *increment)
+{
+    AV *d_av, *i_av;
+    SV *tmp;
+    double i_nv;
+    if (!SvROK(dest)      || SvTYPE(SvRV(dest))      != SVt_PVAV
+    ||  !SvROK(increment) || SvTYPE(SvRV(increment)) != SVt_PVAV)
+	croak("dbi_profile_merge(%s, %s) requires array refs",
+		neatsvpv(dest,0), neatsvpv(dest,0));
+    i_av = (AV*)SvRV(increment);
+    d_av = (AV*)SvRV(dest);
+
+    if (av_len(d_av) < DBIprof_max_index) {
+	int idx;
+	av_extend(d_av, DBIprof_max_index);
+	for(idx=0; idx<=DBIprof_max_index; ++idx) {
+	    tmp = *av_fetch(d_av, idx, 1);
+	    if (!SvOK(tmp))
+		sv_setiv(tmp, 0);
+	}
+    }
+
+    tmp = *av_fetch(d_av, DBIprof_COUNT, 1);
+    sv_setiv( tmp, SvIV(tmp) + SvIV( *av_fetch(i_av, DBIprof_COUNT, 1)) );
+
+    tmp = *av_fetch(d_av, DBIprof_TOTAL_TIME, 1);
+    sv_setnv( tmp, SvNV(tmp) + SvNV( *av_fetch(i_av, DBIprof_TOTAL_TIME, 1)) );
+
+    tmp = *av_fetch(d_av, DBIprof_FIRST_TIME, 1);
+    sv_setnv( tmp, SvNV(tmp) + SvNV( *av_fetch(i_av, DBIprof_FIRST_TIME, 1)) );
+
+    i_nv = SvNV(*av_fetch(i_av, DBIprof_MIN_TIME, 1));
+    tmp  =      *av_fetch(d_av, DBIprof_MIN_TIME, 1);
+    if (i_nv < SvNV(tmp)) sv_setnv(tmp, i_nv);
+
+    i_nv = SvNV(*av_fetch(i_av, DBIprof_MAX_TIME, 1));
+    tmp  =      *av_fetch(d_av, DBIprof_MAX_TIME, 1);
+    if (i_nv > SvNV(tmp)) sv_setnv(tmp, i_nv);
+
+    i_nv = SvNV(*av_fetch(i_av, DBIprof_FIRST_CALLED, 1));
+    tmp  =      *av_fetch(d_av, DBIprof_FIRST_CALLED, 1);
+    if (i_nv < SvNV(tmp)) sv_setnv(tmp, i_nv);
+
+    i_nv = SvNV(*av_fetch(i_av, DBIprof_LAST_CALLED, 1));
+    tmp  =      *av_fetch(d_av, DBIprof_LAST_CALLED, 1);
+    if (i_nv > SvNV(tmp)) sv_setnv(tmp, i_nv);
+}
+
 
 /* ----------------------------------------------------------------- */
 /* ---   The DBI dispatcher. The heart of the perl DBI.          --- */
@@ -1540,6 +2012,7 @@
     int keep_error = FALSE;
     int i, outitems;
     int call_depth;
+    double profile_t1 = 0.0;
 
     char	*meth_name = GvNAME(CvGV(cv));
     dbi_ima_t	*ima       = (dbi_ima_t*)CvXSUBANY(cv).any_ptr;
@@ -1548,70 +2021,33 @@
     SV		*qsv       = NULL; /* quick result from a shortcut method   */
 
 
-    if (!SvROK(h) || SvTYPE(SvRV(h)) != SVt_PVHV) {
-        croak("%s: handle %s is not a hash reference",meth_name,SvPV(h,lna));
-    }
-
     if (debug >= 9) {
 	PerlIO *logfp = DBILOGFP;
-        PerlIO_printf(logfp,"    >> %-11s DISPATCH (%s rc%ld/%ld @%ld g%x a%lx)",
+        PerlIO_printf(logfp,"    >> %-11s DISPATCH (%s rc%ld/%ld @%ld g%x ima%lx pid#%ld)",
 	    meth_name, neatsvpv(h,0),
 	    (long)SvREFCNT(h), (SvROK(h) ? (long)SvREFCNT(SvRV(h)) : (long)-1),
-	    (long)items, (int)gimme, (long)ima);
-	log_where(logfp, debug);
-	PerlIO_puts(logfp, "\n");	/* end of the line */
+	    (long)items, (int)gimme, (long)(ima?ima->flags:0), (long)PerlProc_getpid());
+	PerlIO_puts(logfp, log_where(debug, 0, 0, "\n"));
 	PerlIO_flush(logfp);
     }
 
+    if (!SvROK(h) || SvTYPE(SvRV(h)) != SVt_PVHV) {
+        croak("%s: handle %s is not a hash reference",meth_name,neatsvpv(h,0));
+    }
+
     if (*meth_name=='D' && strEQ(meth_name,"DESTROY")) {
 	/* note that croak()'s won't propagate, only append to $@ */
 	is_DESTROY = TRUE;
 	keep_error = TRUE;
     }
 
-    /* Check method call against Internal Method Attributes */
-    if (ima) {
-
-	if (ima->flags & IMA_FUNC_REDIRECT) {
-	    SV *meth_name_sv = POPs;
-	    PUTBACK;
-	    --items;
-	    if (!SvPOK(meth_name_sv) || SvNIOK(meth_name_sv))
-		croak("%s->%s() invalid redirect method name '%s'",
-			SvPV(h,lna), meth_name, SvPV(meth_name_sv,lna));
-	    meth_name = SvPV(meth_name_sv, na);
-	}
-    	if (ima->flags & IMA_KEEP_ERR)
-	    keep_error = TRUE;
-
-	if (ima->flags & IMA_HAS_USAGE) {
-	    char *err = NULL;
-	    char msg[200];
-
-	    if (ima->minargs && (items < ima->minargs
-				|| (ima->maxargs>0 && items > ima->maxargs))) {
-		/* the error reporting is a little tacky here */
-		sprintf(msg,
-		    "DBI %s: invalid number of parameters: handle + %ld\n",
-		    meth_name, (long)items-1);
-		err = msg;
-	    }
-	    /* arg type checking could be added here later */
-	    if (err) {
-		croak("%sUsage: %s->%s(%s)", err, "$h", meth_name,
-		    (ima->usage_msg) ? ima->usage_msg : "...?");
-	    }
-	}
-
-    }
-
-    /* If h is a tied hash ref, switch to the inner ref 'behind' the tie.
-       This means *all* DBI methods work with the inner (non-tied) ref.
-       This makes it much easier for methods to access the real hash
-       data (without having to go through FETCH and STORE methods) and
-       for tie and non-tie methods to call each other.
-    */
-    if (SvRMAGICAL(SvRV(h)) && (mg=mg_find(SvRV(h),'P'))!=NULL) {
+    /* If h is a tied hash ref, switch to the inner ref 'behind' the tie.
+       This means *all* DBI methods work with the inner (non-tied) ref.
+       This makes it much easier for methods to access the real hash
+       data (without having to go through FETCH and STORE methods) and
+       for tie and non-tie methods to call each other.
+    */
+    if (SvRMAGICAL(SvRV(h)) && (mg=mg_find(SvRV(h),'P'))!=NULL) {
 
         if (SvPVX(mg->mg_obj)==NULL) {  /* maybe global destruction */
             if (debug >= 3)
@@ -1640,6 +2076,73 @@
 
     imp_xxh = DBIh_COM(h); /* get common Internal Handle Attributes	*/
 
+    if (DBIc_has(imp_xxh,DBIcf_Profile)) {
+	profile_t1 = dbi_time(); /* just get start time here */
+    }
+
+#ifdef DBI_USE_THREADS
+{
+    PerlInterpreter * h_perl = DBIc_THR_USER(imp_xxh) ;
+    if (h_perl != my_perl) {
+	/* XXX could call a 'handle clone' method here, for dbh's at least */
+	if (is_DESTROY) {
+	    if (debug >= 2) {
+		PerlIO_printf(DBILOGFP,"    DESTROY ignored because DBI %sh handle (%s) is owned by thread %p not current thread %p\n",
+		      dbih_htype_name(DBIc_TYPE(imp_xxh)), HvNAME(DBIc_IMP_STASH(imp_xxh)), h_perl, my_perl) ;
+		PerlIO_flush(DBILOGFP);
+	    }
+	    XSRETURN(0); /* don't DESTROY handle, if it is not our's !*/
+	}
+	croak("%s %s failed: handle %d is owned by thread %x not current thread %x (%s)",
+	    HvNAME(DBIc_IMP_STASH(imp_xxh)), meth_name, DBIc_TYPE(imp_xxh), h_perl, my_perl,
+	    "handles can't be shared between threads and your driver may need a CLONE method added");
+    }
+}
+#endif
+
+    /* Check method call against Internal Method Attributes */
+    if (ima) {
+
+	if (ima->flags & (IMA_STUB|IMA_FUNC_REDIRECT|IMA_KEEP_ERR|IMA_CLEAR_STMT)) {
+
+	    if (ima->flags & IMA_STUB) {
+		XSRETURN(0);
+	    }
+	    if (ima->flags & IMA_FUNC_REDIRECT) {
+		SV *meth_name_sv = POPs;
+		PUTBACK;
+		--items;
+		if (!SvPOK(meth_name_sv) || SvNIOK(meth_name_sv))
+		    croak("%s->%s() invalid redirect method name %s",
+			    neatsvpv(h,0), meth_name, neatsvpv(meth_name_sv,0));
+		meth_name = SvPV(meth_name_sv, lna);
+	    }
+	    if (ima->flags & IMA_KEEP_ERR)
+		keep_error = TRUE;
+	    if (ima->flags & IMA_CLEAR_STMT)
+		hv_store((HV*)SvRV(h), "Statement", 9, &sv_undef, 0);
+	}
+
+	if (ima->flags & IMA_HAS_USAGE) {
+	    char *err = NULL;
+	    char msg[200];
+
+	    if (ima->minargs && (items < ima->minargs
+				|| (ima->maxargs>0 && items > ima->maxargs))) {
+		/* the error reporting is a little tacky here */
+		sprintf(msg,
+		    "DBI %s: invalid number of parameters: handle + %ld\n",
+		    meth_name, (long)items-1);
+		err = msg;
+	    }
+	    /* arg type checking could be added here later */
+	    if (err) {
+		croak("%sUsage: %s->%s(%s)", err, "$h", meth_name,
+		    (ima->usage_msg) ? ima->usage_msg : "...?");
+	    }
+	}
+    }
+
     if (tainting && items > 1		      /* method call has args	*/
 	&& DBIc_is(imp_xxh, DBIcf_Taint)      /* taint checks requested	*/
 	&& !(ima && ima->flags & IMA_NO_TAINT_IN)
@@ -1667,8 +2170,14 @@
 	    clear_cached_kids(h, imp_xxh, meth_name, debug);
 
 	if (DBI_IS_LAST_HANDLE(h)) {	/* if destroying _this_ handle */
-	    SV *lhp = DBI_LAST_HANDLE_PARENT;
-	    (SvROK(lhp)) ? DBI_SET_LAST_HANDLE(lhp) : DBI_UNSET_LAST_HANDLE;
+	    SV *lhp = DBIc_PARENT_H(imp_xxh);
+	    if (lhp && SvROK(lhp)) {
+		DBI_SET_LAST_HANDLE(lhp);
+	    }
+	    else {
+		DBI_UNSET_LAST_HANDLE;
+	    }
+	
 	} /* otherwise don't alter last handle */
 
 	if (DBIc_IADESTROY(imp_xxh)) { /* want's ineffective destroy	*/
@@ -1680,6 +2189,15 @@
 	DBI_SET_LAST_HANDLE(h);
 	SAVEINT(DBIc_CALL_DEPTH(imp_xxh));
 	call_depth = ++DBIc_CALL_DEPTH(imp_xxh);
+
+	if (ima) {
+	    if (ima->flags & IMA_COPY_STMT) { /* execute() */
+		SV *parent = DBIc_PARENT_H(imp_xxh);
+		SV **tmp_svp = hv_fetch((HV*)SvRV(h), "Statement", 9, 1);
+		/* XXX sv_copy() if Profiling? */
+		hv_store((HV*)SvRV(parent), "Statement", 9, SvREFCNT_inc(*tmp_svp), 0);
+	    }
+	}
     }
 
     /* --- dispatch --- */
@@ -1687,10 +2205,22 @@
     if (!keep_error)
 	DBIh_CLEAR_ERROR(imp_xxh);
 
-    /* Now check if we can provide a shortcut implementation here. */
-    /* At the moment we only offer a quick fetch mechanism.        */
+    /* Shortcut for fetching attributes to bypass method call overheads */
     if (*meth_name=='F' && strEQ(meth_name,"FETCH")) {
+
 	qsv = quick_FETCH(h, ST(1), &imp_msv);
+
+	/* disable FETCH from cache for special attributes */
+	if (qsv && SvROK(qsv) && SvTYPE(SvRV(qsv))==SVt_PVHV) {
+	    STRLEN kl;
+	    char *key = SvPV(ST(1), kl);
+	    if (*key=='D' &&
+		(  (kl==6 && DBIc_TYPE(imp_xxh)==DBIt_DB && strEQ(key,"Driver"))
+		|| (kl==8 && DBIc_TYPE(imp_xxh)==DBIt_ST && strEQ(key,"Database")) )
+	    ) {
+		qsv = Nullsv;
+	    }
+	}
     }
 
     if (qsv) { /* skip real method call if we already have a 'quick' value */
@@ -1709,28 +2239,6 @@
 	    }
 	}
 
-#ifdef DBI_USE_THREADS		/* only pay the cost with threaded perl	*/
-	if (PL_threadnum) {	/* and only if > one thread exists	*/
-	    DBI_LOCK;
-	    while(DBIc_THR_USER(imp_xxh) != DBIc_THR_USER_NONE && DBIc_THR_USER(imp_xxh) != thr->tid) {
-		if (debug >= 4) {
-		    PerlIO_printf(DBILOGFP,"    .. %s: thread %lu waiting for thread %lu to leave %s\n",
-			    meth_name, thr->tid, DBIc_THR_USER(imp_xxh), HvNAME(DBIc_IMP_STASH(imp_xxh)));
-		    PerlIO_flush(DBILOGFP);
-		}
-		COND_WAIT(DBIc_THR_COND(imp_xxh), DBIS->mutex);
-	    }
-	    DBIc_THR_USER(imp_xxh) = thr->tid;
-	    if (debug >= 4) {
-		PerlIO_printf(DBILOGFP,"    .. %s: thread %lu entering %s\n",
-			meth_name, thr->tid, HvNAME(DBIc_IMP_STASH(imp_xxh)));
-		PerlIO_flush(DBILOGFP);
-	    }
-	    SAVEDESTRUCTOR(dbi_cond_signal, imp_xxh);  /* arrange later cond signal	*/
-	    DBI_UNLOCK;
-	}
-#endif
-
 	if (!imp_msv) {
 	    imp_msv = (SV*)gv_fetchmethod(DBIc_IMP_STASH(imp_xxh), meth_name);
 	    if (!imp_msv) {
@@ -1748,7 +2256,7 @@
 	    char *imp_meth_name = (isGV(imp_msv)) ? GvNAME(imp_msv) : meth_name;
 	    HV *imp_stash = DBIc_IMP_STASH(imp_xxh);
 	    PerlIO_printf(logfp, "%c   -> %s ",
-			call_depth>1 ? '0'+call_depth : ' ', imp_meth_name);
+			call_depth>1 ? '0'+call_depth-1 : ' ', imp_meth_name);
 	    if (imp_meth_name[0] == 'A' && strEQ(imp_meth_name,"AUTOLOAD"))
 		    PerlIO_printf(logfp, "\"%s\" ", meth_name);
 	    if (isGV(imp_msv) && GvSTASH(imp_msv) != imp_stash)
@@ -1763,7 +2271,7 @@
 		    (ima && i==ima->hidearg) ? "****" : neatsvpv(ST(i),0));
 	    }
 #ifdef DBI_USE_THREADS
-	    PerlIO_printf(logfp, ") thr%lu\n", thr->tid);
+	    PerlIO_printf(logfp, ") thr#%p\n", DBIc_THR_USER(imp_xxh));
 #else
 	    PerlIO_printf(logfp, ")\n");
 #endif
@@ -1803,39 +2311,40 @@
 
 	}
 	else {
-	    outitems = perl_call_sv(isGV(imp_msv) ? (SV*)GvCV(imp_msv) : imp_msv, gimme);
+	    outitems = perl_call_sv(isGV(imp_msv) ? (SV*)GvCV(imp_msv) : imp_msv,
+		(is_DESTROY ? gimme | G_EVAL | G_KEEPERR : gimme) );
 	}
+	SPAGAIN;
 
 	if (debug) { /* XXX restore local vars so ST(n) works below	*/
-	    SPAGAIN; sp -= outitems; ax = (sp - stack_base) + 1; 
+	    sp -= outitems; ax = (sp - stack_base) + 1; 
 	}
 
-	/* We might perform some fancy error handling here one day	*/
     }
 
     if (debug >= 1) {
 	PerlIO *logfp = DBILOGFP;
-	if (!keep_error && SvTRUE(DBIc_ERR(imp_xxh)))
-	    PerlIO_printf(logfp,"    !! ERROR: %s %s\n",
-		neatsvpv(DBIc_ERR(imp_xxh),0), neatsvpv(DBIc_ERRSTR(imp_xxh),0));
-	PerlIO_printf(logfp,"%c   <- %s",
-		    call_depth>1 ? '0'+call_depth :
-			DBIc_is(imp_xxh, DBIcf_Taint) ? 'T':' ',
+	int is_fetch  = (*meth_name=='f' && DBIc_TYPE(imp_xxh)==DBIt_ST && strnEQ(meth_name,"fetch",5));
+	int row_count = (is_fetch) ? DBIc_ROW_COUNT((imp_sth_t*)imp_xxh) : 0;
+	if (is_fetch && row_count>=2 && debug<=1 && SvOK(ST(0))) {
+	    /* skip the 'middle' rows to reduce output */
+	    goto skip_meth_return_trace;
+	}
+	if (!keep_error) {
+	    if (SvTRUE(DBIc_ERR(imp_xxh)))
+		PerlIO_printf(logfp,"    !! ERROR: %s %s\n",
+		    neatsvpv(DBIc_ERR(imp_xxh),0), neatsvpv(DBIc_ERRSTR(imp_xxh),0));
+	}
+	PerlIO_printf(logfp,"%c%c  <- %s",
+		    (call_depth > 1)                ? '0'+call_depth-1 : ' ',
+		    (DBIc_is(imp_xxh, DBIcf_Taint)) ? 'T'              : ' ',
 		    meth_name);
-	if (debug==1) { /* make debug level 1 for some methods more useful */
-	    if (	(*meth_name=='S' && strEQ(meth_name,"STORE"))
-	    	||	(*meth_name=='p' && strEQ(meth_name,"prepare"))
-	    	||	(*meth_name=='e' && strEQ(meth_name,"execute"))
-	    	||	(*meth_name=='d' && strEQ(meth_name,"do"))
-	    ) {
-		/* we only have the first two parameters available here */
-		PerlIO_printf(logfp,"(");
-		if (items >= 1)
-		    PerlIO_printf(logfp,"%s", neatsvpv(st1,0));
-		if (items >= 2)
-		    PerlIO_printf(logfp," %s", neatsvpv(st2,0));
-		PerlIO_printf(logfp,"%s)", (items > 2) ? " ..." : "");
-	    }
+	if (debug==1 && items>=2) { /* make level 1 more useful */
+	    /* we only have the first two parameters available here */
+	    PerlIO_printf(logfp,"(%s", neatsvpv(st1,0));
+	    if (items >= 3)
+		PerlIO_printf(logfp," %s", neatsvpv(st2,0));
+	    PerlIO_printf(logfp,"%s)", (items > 3) ? " ..." : "");
 	}
 
 	if (gimme & G_ARRAY)
@@ -1857,50 +2366,46 @@
 		    PerlIO_printf(logfp, "%ldkeys", HvKEYS(SvRV(s)));
 	    }
 	}
-	if (gimme & G_ARRAY)
+	if (gimme & G_ARRAY) {
 	    PerlIO_printf(logfp," ) [%d items]", outitems);
-	else if (SvROK(ST(0)) && outitems == 1) {
-	    /* XXX to do: AvFILL+1 or HvKEYS
-	    long elems = -1;
-	    PerlIO_printf(logfp," of %ld elements", elems);
-	    */
+	}
+	if (is_fetch && row_count) {
+	    PerlIO_printf(logfp," row%d", row_count);
 	}
 	if (qsv) /* flag as quick and peek at the first arg (still on the stack) */
 	    PerlIO_printf(logfp," (%s from cache)", neatsvpv(st1,0));
-	log_where(logfp, debug); /* add file and line number information */
-	PerlIO_puts(logfp, "\n");	/* end of the line */
+	/* XXX add flag to show pid here? */
+	PerlIO_puts(logfp, log_where(debug, 0, 0, "\n")); /* add file and line number information */
+    skip_meth_return_trace:
 	PerlIO_flush(logfp);
     }
 
-    if (   !keep_error				/* so would be a new error	*/
-	&& SvTRUE(DBIc_ERR(imp_xxh))		/* and an error exists		*/
-	&& call_depth <= 1			/* skip nested (internal) calls	*/
-	&& DBIc_has(imp_xxh, DBIcf_RaiseError|DBIcf_PrintError)
-	/* check that we're not nested inside a call to our parent */
-	&& (!DBIc_PARENT_COM(imp_xxh) || DBIc_CALL_DEPTH(DBIc_PARENT_COM(imp_xxh)) < 1)
-    ) {
-	int raise_error = DBIc_has(imp_xxh, DBIcf_RaiseError);
-	SV *msg;
-	char intro[200];
-	sprintf(intro,"%s %s failed: ", HvNAME(DBIc_IMP_STASH(imp_xxh)), meth_name);
-	msg = sv_2mortal(newSVpv(intro,0));
-	sv_catsv(msg, DBIc_ERRSTR(imp_xxh));
-
-	/* PrintError = report errors via warn()	*/
-	if (DBIc_has(imp_xxh, DBIcf_PrintError)) {
-	    /* if both PrintError and RaiseError are true	*/
-	    /* we do both unless there's no __DIE__ hook	*/
-	    if (!raise_error || (diehook && SvOK(diehook)))
-		warn("%s", SvPV(msg,lna));
+    if (ima && ima->flags & IMA_END_WORK) { /* commit() or rollback() */
+	if (DBIc_has(imp_xxh, DBIcf_BegunWork)) {
+	    DBIc_off(imp_xxh, DBIcf_BegunWork);
+	    if (!DBIc_has(imp_xxh, DBIcf_AutoCommit)) {
+		/* We only get here if the driver hasn't implemented their own code	*/
+		/* for begin_work, or has but hasn't correctly turned AutoCommit	*/
+		/* back on in their commit or rollback code. So we have to do it.	*/
+		/* This is bad because it'll probably trigger a spurious commit()	*/
+		/* and may mess up the error handling below for the commit/rollback	*/
+		PUSHMARK(SP);
+		XPUSHs(h);
+		XPUSHs(sv_2mortal(newSVpv("AutoCommit",0)));
+		XPUSHs(&sv_yes);
+		PUTBACK;
+		perl_call_method("STORE", G_DISCARD);
+		SPAGAIN;
+	    }
 	}
-	/* RaiseError = report errors via croak()	*/
-	if (raise_error)
-	    croak("%s", SvPV(msg,lna));
     }
 
     if (tainting
 	&& DBIc_is(imp_xxh, DBIcf_Taint)      /* taint checks requested	*/
-	&& 0/*disabled*/ /* !(ima && ima->flags & IMA_NO_TAINT_OUT) */
+	/* XXX this would taint *everything* being returned from *any*	*/
+	/* method that doesn't have IMA_NO_TAINT_OUT set.		*/
+	/* DISABLED: just tainting fetched data in get_fbav seems ok	*/
+	&& 0/* XXX disabled*/ /* !(ima && ima->flags & IMA_NO_TAINT_OUT) */
     ) {
 	dTHR;
 	TAINT; /* affects sv_setsv()'s within same perl statement */
@@ -1937,12 +2442,454 @@
 	}
     }
 
+
+    if (   !keep_error				/* so would be a new error	*/
+	&& SvTRUE(DBIc_ERR(imp_xxh))		/* and an error exists		*/
+	&& call_depth <= 1			/* skip nested (internal) calls	*/
+	&& DBIc_has(imp_xxh, DBIcf_RaiseError|DBIcf_PrintError|DBIcf_HandleError)
+	/* check that we're not nested inside a call to our parent */
+	&& (!DBIc_PARENT_COM(imp_xxh) || DBIc_CALL_DEPTH(DBIc_PARENT_COM(imp_xxh)) < 1)
+    ) {
+	SV *msg;
+	SV **hook_svp = 0;
+	SV **statement_svp = NULL;
+	char *err_meth_name = meth_name;
+	char intro[200];
+
+	if (*meth_name=='s' && strEQ(meth_name,"set_err")) {
+	    SV **sem_svp = hv_fetch((HV*)SvRV(h), "dbi_set_err_method", 18, GV_ADDWARN);
+	    if (SvOK(*sem_svp))
+		err_meth_name = SvPV(*sem_svp,lna);
+	}
+
+	sprintf(intro,"%s %s failed: ", HvNAME(DBIc_IMP_STASH(imp_xxh)), err_meth_name);
+	msg = sv_2mortal(newSVpv(intro,0));
+	sv_catsv(msg, DBIc_ERRSTR(imp_xxh));
+
+	if (    DBIc_has(imp_xxh, DBIcf_ShowErrorStatement)
+	    && (DBIc_TYPE(imp_xxh) == DBIt_ST
+		|| strEQ(err_meth_name,"prepare")	/* XXX use IMA flag for this */
+		|| strEQ(err_meth_name,"do")
+		|| strnEQ(err_meth_name,"select",6)
+		)
+	    && (statement_svp = hv_fetch((HV*)SvRV(h), "Statement", 9, 0))
+	    &&  statement_svp && SvOK(*statement_svp)
+	) {
+	    SV **svp;
+	    sv_catpv(msg, " [for statement ``");
+	    sv_catsv(msg, *statement_svp);
+
+	    /* fetch from tied outer handle to trigger FETCH magic  */
+	    /* could add DBIcf_ShowErrorParams (default to on?)		*/
+	    svp = hv_fetch((HV*)DBIc_MY_H(imp_xxh),"ParamValues",11,FALSE);
+	    if (svp && SvMAGICAL(*svp))
+		mg_get(*svp);
+	    if (svp && SvRV(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV ) {
+		HV *bvhv = (HV*)SvRV(*svp);
+		SV *sv;
+		char *key;
+		I32 keylen;
+		I32 param_idx = 0;
+		hv_iterinit(bvhv);
+		sv_catpv(msg, "'' with params: ");
+		while ( (sv = hv_iternextsv(bvhv, &key, &keylen)) ) {
+		    sv_catpvf(msg, "%s%s=%s",
+			(param_idx++==0 ? "" : ", "),
+			key, neatsvpv(sv,0));
+		}
+		sv_catpv(msg, "])");
+	    }
+	    else {
+		sv_catpv(msg, "''])");
+	    }
+	}
+
+	if (DBIc_has(imp_xxh, DBIcf_HandleError)
+		&& (hook_svp=hv_fetch((HV*)SvRV(h),"HandleError",11,0))
+		&&  hook_svp && SvOK(*hook_svp)
+	) {
+	    dSP;
+	    SV *result = *(sp-outitems+1);
+	    PerlIO *logfp = DBILOGFP;
+	    IV items;
+	    SV *status;
+	    int debug = (DBIS->debug >= 1);
+	    if (debug)
+		PerlIO_printf(logfp,"    -> HandleError on %s via %s%s%s%s\n",
+		    neatsvpv(h,0), neatsvpv(*hook_svp,0),
+		    (result ? " (" : ""),
+		    (result ? neatsvpv(result,0) : ""),
+		    (result ? ")" : "")
+		);
+	    PUSHMARK(SP);
+	    XPUSHs(msg);
+	    XPUSHs(sv_2mortal(newRV((SV*)DBIc_MY_H(imp_xxh))));
+	    XPUSHs( result ? result : sv_newmortal() );
+	    PUTBACK;
+	    items = perl_call_sv(*hook_svp, G_SCALAR);
+	    SPAGAIN;
+	    status = POPs;
+	    PUTBACK;
+	    if (!SvTRUE(status)) /* handler says it didn't handle it, so... */
+		hook_svp = 0;  /* pretend we didn't have a handler...     */
+	    if (debug)
+		PerlIO_printf(logfp,"    <- HandleError= %s%s%s%s\n",
+		    neatsvpv(status,0),
+		    (result ? " (" : ""),
+		    (result ? neatsvpv(result,0) : ""),
+		    (result ? ")" : "")
+		);
+	}
+
+	if (profile_t1 && !is_DESTROY) { /* see also dbi_profile() call a few lines below */
+	    char *Statement = (ima && ima->flags & IMA_PROF_EMPTY_STMT) ? "" : Nullch;
+	    dbi_profile(h, imp_xxh, Statement, imp_msv ? imp_msv : (SV*)cv,
+		profile_t1, dbi_time());
+	}
+	if (!hook_svp) {
+	    if (DBIc_has(imp_xxh, DBIcf_PrintError))
+		warn("%s", SvPV(msg,lna));
+	    if (DBIc_has(imp_xxh, DBIcf_RaiseError))
+		croak("%s", SvPV(msg,lna));
+	}
+    }
+    else if (profile_t1 && !is_DESTROY) { /* see also dbi_profile() call a few lines above */
+	char *Statement = (ima && ima->flags & IMA_PROF_EMPTY_STMT) ? "" : Nullch;
+	dbi_profile(h, imp_xxh, Statement, imp_msv ? imp_msv : (SV*)cv,
+		profile_t1, dbi_time());
+    }
+
     XSRETURN(outitems);
 }
 
 
 
 /* --------------------------------------------------------------------	*/
+
+/* comment and placeholder styles to accept and return */
+
+#define DBIpp_cm_cs 0x000001   /* C style */
+#define DBIpp_cm_hs 0x000002   /* #       */
+#define DBIpp_cm_dd 0x000004   /* --      */
+#define DBIpp_cm_br 0x000008   /* {}      */
+#define DBIpp_cm_dw 0x000010   /* '-- ' dash dash whitespace */
+#define DBIpp_cm_XX 0x00001F   /* any of the above */
+     
+#define DBIpp_ph_qm 0x000100   /* ?       */
+#define DBIpp_ph_cn 0x000200   /* :1      */
+#define DBIpp_ph_cs 0x000400   /* :name   */
+#define DBIpp_ph_sp 0x000800   /* %s (as return only, not accept)    */
+#define DBIpp_ph_XX 0x000F00   /* any of the above */
+
+#define DBIpp_st_qq 0x010000   /* '' char escape */
+#define DBIpp_st_bs 0x020000   /* \  char escape */
+#define DBIpp_st_XX 0x030000   /* any of the above */
+
+#define DBIpp_L_BRACE '{'
+#define DBIpp_R_BRACE '}'
+#define PS_accept(flag)  DBIbf_has(ps_accept,(flag))
+#define PS_return(flag)  DBIbf_has(ps_return,(flag))
+
+SV *
+preparse(SV *dbh, char *statement, IV ps_return, IV ps_accept, void *foo)
+{
+	D_imp_xxh(dbh);
+/*
+	The idea here is that ps_accept defines which constructs to
+	recognize (accept) as valid in the source string (other
+	constructs are ignored), and ps_return defines which
+	constructs are valid to return in the result string.
+
+	If a construct that is valid in the input is also valid in the
+	output then it's simply copied. If it's not valid in the output
+	then it's editied into one of the valid forms (ideally the most
+	'standard' and/or information preserving one).
+
+	For example, if ps_accept includes '--' style comments but
+	ps_return doesn't, but ps_return does include '#' style
+	comments then any '--' style comments would be rewritten as '#'
+	style comments.
+
+	Similarly for placeholders. DBD::Oracle, for example, would say
+	'?', ':1' and ':name' are all acceptable input, but only
+	':name' should be returned.
+
+	(There's a tricky issue with the '--' comment style because it can
+	clash with valid syntax, i.e., "... set foo=foo--1 ..." so it
+	would be *bad* to misinterpret that as the start of a comment.
+	Perhaps we need a DBIpp_cm_dw (for dash-dash-whitespace) style
+	to allow for that.)
+
+	Also, we'll only support DBIpp_cm_br as an input style. And
+	even then, only with reluctance. We may (need to) drop it when
+	we add support for odbc escape sequences.
+*/
+
+    int idx = 1;
+
+    char in_quote = '\0';
+    char in_comment = '\0';
+    char rt_comment = '\0';
+    char *src, *start, *dest;
+    char *style = "", *laststyle = '\0';
+    SV *new_stmt_sv;
+
+    if (!(ps_return | DBIpp_ph_XX)) { /* no return ph type specified */
+	ps_return |= ps_accept | DBIpp_ph_XX;	/* so copy from ps_accept */
+    }
+
+    /* XXX this allocation strategy won't work when we get to more advanced stuff */
+    new_stmt_sv = newSV(strlen(statement) * 3);
+    sv_setpv(new_stmt_sv,"");
+    src  = statement;
+    dest = SvPVX(new_stmt_sv);
+
+    while( *src ) 
+    {
+	if (*src == '%' && PS_return(DBIpp_ph_sp))
+	    *dest++ = '%';
+
+	if (in_comment)
+	{
+	     if (	(in_comment == '-' && (*src == '\n' || *(src+1) == '\0')) 
+		||	(in_comment == '#' && (*src == '\n' || *(src+1) == '\0'))
+		||	(in_comment == DBIpp_L_BRACE && *src == DBIpp_R_BRACE) /* XXX nesting? */
+		||	(in_comment == '/' && *src == '*' && *(src+1) == '/')
+	     ) {
+		switch (rt_comment) {
+		case '/':	*dest++ = '*'; *dest++ = '/';	break;
+		case '-':	*dest++ = '\n';			break;
+		case '#':	*dest++ = '\n';			break;
+		case DBIpp_L_BRACE: *dest++ = DBIpp_R_BRACE;	break;
+		case '\0':	/* ensure deleting a comment doesn't join two tokens */
+			if (in_comment=='/' || in_comment==DBIpp_L_BRACE)
+			    *dest++ = ' '; /* ('-' and '#' styles use the newline) */
+			break;
+		}
+		if (in_comment == '/')
+		    src++;
+		src += (*src != '\n' || *(dest-1)=='\n') ? 1 : 0;
+		in_comment = '\0';
+		rt_comment = '\0';
+	     }
+             else 
+	     if (rt_comment)
+                *dest++ = *src++;
+	     else
+		src++;	/* delete (don't copy) the comment */
+	     continue;
+	}
+
+	if (in_quote)
+	{
+	    if (*src == in_quote) {
+		in_quote = 0;
+	    }
+	    *dest++ = *src++;
+	    continue;
+	}
+
+	/* Look for comments */
+        if (*src == '-' && *(src+1) == '-' &&
+		(PS_accept(DBIpp_cm_dd) || (*(src+2) == ' ' && PS_accept(DBIpp_cm_dw)))
+	)
+        {
+	    in_comment = *src;
+	    src += 2;	/* skip past 2nd char of double char delimiters */
+	    if (PS_return(DBIpp_cm_dd) || PS_return(DBIpp_cm_dw)) {
+                *dest++ = rt_comment = '-';
+                *dest++ = '-';
+                if (PS_return(DBIpp_cm_dw) && *src!=' ')
+		    *dest++ = ' '; /* insert needed white space */
+            }
+	    else if (PS_return(DBIpp_cm_cs)) {
+                *dest++ = rt_comment = '/';
+                *dest++ = '*';
+            }
+	    else if (PS_return(DBIpp_cm_hs)) {
+                *dest++ = rt_comment = '#';
+            }
+	    else if (PS_return(DBIpp_cm_br)) {
+                *dest++ = rt_comment = DBIpp_L_BRACE;
+            }
+	    continue;
+        }
+        else if (*src == '/' && *(src+1) == '*' && PS_accept(DBIpp_cm_cs))
+        {
+	    in_comment = *src;
+	    src += 2;	/* skip past 2nd char of double char delimiters */
+	    if (PS_return(DBIpp_cm_cs)) {
+                *dest++ = rt_comment = '/';
+                *dest++ = '*';
+            }
+	    else if (PS_return(DBIpp_cm_dd) || PS_return(DBIpp_cm_dw)) {
+                *dest++ = rt_comment = '-';
+                *dest++ = '-';
+                if (PS_return(DBIpp_cm_dw)) *dest++ = ' '; 
+            }
+	    else if (PS_return(DBIpp_cm_hs)) {
+                *dest++ = rt_comment = '#';
+            }
+	    else if (PS_return(DBIpp_cm_br)) {
+                *dest++ = rt_comment = DBIpp_L_BRACE;
+            }
+	    continue;
+        }
+        else if (*src == '#' && PS_accept(DBIpp_cm_hs))
+        {
+	    in_comment = *src;
+	    src++;
+	    if (PS_return(DBIpp_cm_hs)) {
+                *dest++ = rt_comment = '#';
+            }
+	    else if (PS_return(DBIpp_cm_dd) || PS_return(DBIpp_cm_dw)) {
+                *dest++ = rt_comment = '-';
+                *dest++ = '-';
+                if (PS_return(DBIpp_cm_dw)) *dest++ = ' '; 
+            }
+	    else if (PS_return(DBIpp_cm_cs)) {
+                *dest++ = rt_comment = '/';
+                *dest++ = '*';
+            }
+	    else if (PS_return(DBIpp_cm_br)) {
+                *dest++ = rt_comment = DBIpp_L_BRACE;
+            }
+	    continue;
+        }
+        else if (*src == DBIpp_L_BRACE && PS_accept(DBIpp_cm_br))
+        {
+	    in_comment = *src;
+	    src++;
+	    if (PS_return(DBIpp_cm_br)) {
+                *dest++ = rt_comment = DBIpp_L_BRACE;
+            }
+	    else if (PS_return(DBIpp_cm_dd) || PS_return(DBIpp_cm_dw)) {
+                *dest++ = rt_comment = '-';
+                *dest++ = '-';
+                if (PS_return(DBIpp_cm_dw)) *dest++ = ' '; 
+            }
+	    else if (PS_return(DBIpp_cm_cs)) {
+                *dest++ = rt_comment = '/';
+                *dest++ = '*';
+            }
+	    else if (PS_return(DBIpp_cm_hs)) {
+                *dest++ = rt_comment = '#';
+            }
+	    continue;
+        }
+
+       if (    !(*src==':' && (PS_accept(DBIpp_ph_cn) || PS_accept(DBIpp_ph_cs)))
+           &&  !(*src=='?' &&  PS_accept(DBIpp_ph_qm))
+       ){
+	    if (*src == '\'' || *src == '"')
+		in_quote = *src;
+	    *dest++ = *src++;
+	    continue;
+	}
+
+	/* only here for : or ? outside of a comment or literal	*/
+
+	start = dest;			/* save name inc colon	*/ 
+	*dest++ = *src++;		/* copy and move past first char */
+
+	if (*start == '?')		/* X/Open Standard */
+        {
+	    style = "?";
+
+            if (PS_return(DBIpp_ph_qm))
+		;
+            else if (PS_return(DBIpp_ph_cn)) { /* '?' -> ':p1' (etc) */
+                sprintf(start,":p%d", idx++);
+                dest = start+strlen(start);
+            }
+            else if (PS_return(DBIpp_ph_sp)) { /* '?' -> '%s' */
+		   *start  = '%';
+		   *dest++ = 's';
+            }
+	} 
+        else if (isDIGIT(*src)) {   /* :1 */ 
+	    int pln = atoi(src);
+	    style = ":1";
+
+	    if (PS_return(DBIpp_ph_cn)) { /* ':1'->':p1'  */
+		   idx = pln;
+		   *dest++ = 'p';
+		   while(isDIGIT(*src))
+		       *dest++ = *src++;
+            }
+	    else if (PS_return(DBIpp_ph_qm) /* ':1' -> '?'  */
+	    	 ||  PS_return(DBIpp_ph_sp) /* ':1' -> '%s' */
+	    ) {
+		   PS_return(DBIpp_ph_qm) ? sprintf(start,"?") : sprintf(start,"%%s");
+		   dest = start + strlen(start);
+                   if (pln != idx) {
+			char buf[99];
+			sprintf(buf, "preparse found placeholder :%d out of sequence, expected :%d", pln, idx);
+			set_err(dbh, imp_xxh, 1, buf, 0);
+			return &sv_undef;
+                   }
+		   while(isDIGIT(*src)) src++;
+                   idx++;
+            }
+	} 
+        else if (isALNUM(*src))         /* :name */ 
+        {
+	    style = ":name";
+
+	    if (PS_return(DBIpp_ph_cs)) {
+		;
+            }
+	    else if (PS_return(DBIpp_ph_qm) /* ':name' -> '?'  */
+	    	 ||  PS_return(DBIpp_ph_sp) /* ':name' -> '%s' */
+	    ) {
+		PS_return(DBIpp_ph_qm) ? sprintf(start,"?") : sprintf(start,"%%s");
+		dest = start + strlen(start);
+		while (isALNUM(*src))	/* consume name, includes '_'	*/
+		    src++;
+	    }
+	}
+        /* perhaps ':=' PL/SQL construct */
+	else { continue; }
+
+	*dest = '\0';			/* handy for debugging	*/
+
+	if (laststyle && style != laststyle) {
+	    char buf[99];
+	    sprintf(buf, "preparse found mixed placeholder styles (%s / %s)", style, laststyle);
+	    set_err(dbh, imp_xxh, 1, buf, 0);
+            return &sv_undef;
+        }
+	laststyle = style;
+    }
+    *dest = '\0';
+
+    /* warn about probable parsing errors, but continue anyway (returning processed string) */
+    switch (in_quote)
+    {
+    case '\'':
+	    set_err(dbh, imp_xxh, 1, "preparse found unterminated single-quoted string", 0);
+	    break;
+    case '\"':
+	    set_err(dbh, imp_xxh, 1, "preparse found unterminated double-quoted string", 0);
+	    break;
+    }
+    switch (in_comment)
+    {
+    case DBIpp_L_BRACE:
+	    set_err(dbh, imp_xxh, 1, "preparse found unterminated bracketed {...} comment", 0);
+	    break;
+    case '/':
+	    set_err(dbh, imp_xxh, 1, "preparse found unterminated bracketed C-style comment", 0);
+	    break;
+    }
+
+    SvCUR_set(new_stmt_sv, strlen(SvPVX(new_stmt_sv)));
+    *SvEND(new_stmt_sv) = '\0';
+    return new_stmt_sv;
+}
+
+
+/* --------------------------------------------------------------------	*/
 /* The DBI Perl interface (via XS) starts here. Currently these are 	*/
 /* all internal support functions. Note install_method and see DBI.pm	*/
 
@@ -1954,36 +2901,84 @@
 
 BOOT:
     items = items;		/* avoid 'unused variable' warning	*/
-    dbi_bootinit();
+    dbi_bootinit(NULL);
 
 
 I32
 constant()
 	PROTOTYPE:
     ALIAS:
-	SQL_ALL_TYPES	= SQL_ALL_TYPES
-	SQL_CHAR	= SQL_CHAR
-	SQL_NUMERIC	= SQL_NUMERIC
-	SQL_DECIMAL	= SQL_DECIMAL
-	SQL_INTEGER	= SQL_INTEGER
-	SQL_SMALLINT	= SQL_SMALLINT
-	SQL_FLOAT	= SQL_FLOAT
-	SQL_REAL	= SQL_REAL
-	SQL_DOUBLE	= SQL_DOUBLE
-	SQL_DATE	= SQL_DATE
-	SQL_TIME	= SQL_TIME
-	SQL_TIMESTAMP	= SQL_TIMESTAMP
-	SQL_VARCHAR	= SQL_VARCHAR
-	SQL_LONGVARCHAR = SQL_LONGVARCHAR
-	SQL_BINARY	= SQL_BINARY
-	SQL_VARBINARY	= SQL_VARBINARY
-	SQL_LONGVARBINARY = SQL_LONGVARBINARY
-	SQL_TINYINT	= SQL_TINYINT
-	SQL_BIGINT	= SQL_BIGINT
-	SQL_WCHAR       = SQL_WCHAR
-	SQL_WVARCHAR    = SQL_WVARCHAR
-	SQL_WLONGVARCHAR = SQL_WLONGVARCHAR
-	SQL_BIT         = SQL_BIT
+	SQL_ALL_TYPES                    = SQL_ALL_TYPES
+	SQL_ARRAY                        = SQL_ARRAY
+	SQL_ARRAY_LOCATOR                = SQL_ARRAY_LOCATOR
+	SQL_BINARY                       = SQL_BINARY
+	SQL_BIT                          = SQL_BIT
+	SQL_BLOB                         = SQL_BLOB
+	SQL_BLOB_LOCATOR                 = SQL_BLOB_LOCATOR
+	SQL_BOOLEAN                      = SQL_BOOLEAN
+	SQL_CHAR                         = SQL_CHAR
+	SQL_CLOB                         = SQL_CLOB
+	SQL_CLOB_LOCATOR                 = SQL_CLOB_LOCATOR
+	SQL_DATE                         = SQL_DATE
+	SQL_DATETIME                     = SQL_DATETIME
+	SQL_DECIMAL                      = SQL_DECIMAL
+	SQL_DOUBLE                       = SQL_DOUBLE
+	SQL_FLOAT                        = SQL_FLOAT
+	SQL_GUID                         = SQL_GUID
+	SQL_INTEGER                      = SQL_INTEGER
+	SQL_INTERVAL                     = SQL_INTERVAL
+	SQL_INTERVAL_DAY                 = SQL_INTERVAL_DAY
+	SQL_INTERVAL_DAY_TO_HOUR         = SQL_INTERVAL_DAY_TO_HOUR
+	SQL_INTERVAL_DAY_TO_MINUTE       = SQL_INTERVAL_DAY_TO_MINUTE
+	SQL_INTERVAL_DAY_TO_SECOND       = SQL_INTERVAL_DAY_TO_SECOND
+	SQL_INTERVAL_HOUR                = SQL_INTERVAL_HOUR
+	SQL_INTERVAL_HOUR_TO_MINUTE      = SQL_INTERVAL_HOUR_TO_MINUTE
+	SQL_INTERVAL_HOUR_TO_SECOND      = SQL_INTERVAL_HOUR_TO_SECOND
+	SQL_INTERVAL_MINUTE              = SQL_INTERVAL_MINUTE
+	SQL_INTERVAL_MINUTE_TO_SECOND    = SQL_INTERVAL_MINUTE_TO_SECOND
+	SQL_INTERVAL_MONTH               = SQL_INTERVAL_MONTH
+	SQL_INTERVAL_SECOND              = SQL_INTERVAL_SECOND
+	SQL_INTERVAL_YEAR                = SQL_INTERVAL_YEAR
+	SQL_INTERVAL_YEAR_TO_MONTH       = SQL_INTERVAL_YEAR_TO_MONTH
+	SQL_LONGVARBINARY                = SQL_LONGVARBINARY
+	SQL_LONGVARCHAR                  = SQL_LONGVARCHAR
+	SQL_MULTISET                     = SQL_MULTISET
+	SQL_MULTISET_LOCATOR             = SQL_MULTISET_LOCATOR
+	SQL_NUMERIC                      = SQL_NUMERIC
+	SQL_REAL                         = SQL_REAL
+	SQL_REF                          = SQL_REF
+	SQL_ROW                          = SQL_ROW
+	SQL_SMALLINT                     = SQL_SMALLINT
+	SQL_TIME                         = SQL_TIME
+	SQL_TIMESTAMP                    = SQL_TIMESTAMP
+	SQL_TINYINT                      = SQL_TINYINT
+	SQL_TYPE_DATE                    = SQL_TYPE_DATE
+	SQL_TYPE_TIME                    = SQL_TYPE_TIME
+	SQL_TYPE_TIMESTAMP               = SQL_TYPE_TIMESTAMP
+	SQL_TYPE_TIMESTAMP_WITH_TIMEZONE = SQL_TYPE_TIMESTAMP_WITH_TIMEZONE
+	SQL_TYPE_TIME_WITH_TIMEZONE      = SQL_TYPE_TIME_WITH_TIMEZONE
+	SQL_UDT                          = SQL_UDT
+	SQL_UDT_LOCATOR                  = SQL_UDT_LOCATOR
+	SQL_UNKNOWN_TYPE                 = SQL_UNKNOWN_TYPE
+	SQL_VARBINARY                    = SQL_VARBINARY
+	SQL_VARCHAR                      = SQL_VARCHAR
+	SQL_WCHAR                        = SQL_WCHAR
+	SQL_WLONGVARCHAR                 = SQL_WLONGVARCHAR
+	SQL_WVARCHAR                     = SQL_WVARCHAR
+	DBIpp_cm_cs	= DBIpp_cm_cs
+	DBIpp_cm_hs	= DBIpp_cm_hs
+	DBIpp_cm_dd	= DBIpp_cm_dd
+	DBIpp_cm_dw	= DBIpp_cm_dw
+	DBIpp_cm_br	= DBIpp_cm_br
+	DBIpp_cm_XX	= DBIpp_cm_XX
+	DBIpp_ph_qm	= DBIpp_ph_qm
+	DBIpp_ph_cn	= DBIpp_ph_cn
+	DBIpp_ph_cs	= DBIpp_ph_cs
+	DBIpp_ph_sp	= DBIpp_ph_sp
+	DBIpp_ph_XX	= DBIpp_ph_XX
+	DBIpp_st_qq	= DBIpp_st_qq
+	DBIpp_st_bs	= DBIpp_st_bs
+	DBIpp_st_XX	= DBIpp_st_XX
     CODE:
     RETVAL = ix;
     OUTPUT:
@@ -1991,6 +2986,13 @@
 
 
 void
+_clone_dbis()
+    CODE:
+    dPERINTERP;
+    dbi_bootinit(DBIS);
+
+
+void
 _setup_handle(sv, imp_class, parent, imp_datasv)
     SV *	sv
     char *	imp_class
@@ -2016,42 +3018,13 @@
     /* return the outer and inner handle for any given handle */
     D_imp_xxh(sv);
     SV *ih = sv_mortalcopy( dbih_inner(sv, "_handles") );
-    SV *oh = sv_2mortal(newRV(DBIc_MY_H(imp_xxh))); /* XXX dangerous */
+    SV *oh = sv_2mortal(newRV((SV*)DBIc_MY_H(imp_xxh))); /* XXX dangerous */
     EXTEND(SP, 2);
     PUSHs(oh);	/* returns outer handle then inner */
     PUSHs(ih);
 
 
 void
-set_err(sv, errval, errstr=&sv_no, state=&sv_undef)
-    SV *	sv
-    SV *	errval
-    SV *	errstr
-    SV *	state
-    CODE:
-    {
-    dPERINTERP;
-    D_imp_xxh(sv);
-    sv_setsv(DBIc_ERR(imp_xxh),    errval);
-    if (errstr==&sv_no || !SvOK(errstr))
-	errstr = errval;
-    sv_setsv(DBIc_ERRSTR(imp_xxh), errstr);
-    if (SvOK(state)) {
-	STRLEN len;
-	if (SvPV(state, len) && len != 5)
-	    croak("set_err: state must be 5 character string");
-	sv_setsv(DBIc_STATE(imp_xxh), state);
-    }
-    else {
-	(void)SvOK_off(DBIc_STATE(imp_xxh));
-    }
-    sv = dbih_inner(sv,"set_err");
-    DBI_SET_LAST_HANDLE(sv);
-    ST(0) = &sv_undef;
-    }
-
-
-void
 neat(sv, maxlen=0)
     SV *	sv
     U32	maxlen
@@ -2060,11 +3033,11 @@
 
 
 int
-hash(key, i=0)
+hash(key, type=0)
     char *key
-    int i
+    long type
     CODE:
-    RETVAL = dbi_hash(key, i);
+    RETVAL = dbi_hash(key, type);
     OUTPUT:
     RETVAL
 
@@ -2172,7 +3145,7 @@
 	    PerlIO_flush(DBILOGFP);
 	}
 	DBIS->debug = level;
-	sv_setiv(get_sv("DBI::dbi_debug",0x5), level);
+	sv_setiv(perl_get_sv("DBI::dbi_debug",0x5), level);
     }
     }
     OUTPUT:
@@ -2181,14 +3154,13 @@
 
 
 void
-dump_handle(sv, msg="DBI::dump_handle")
+dump_handle(sv, msg="DBI::dump_handle", level=0)
     SV *	sv
     char *	msg
+    int 	level
     CODE:
-    {
-    D_imp_xxh(sv);
-    dbih_dumpcom(imp_xxh, msg);
-    }
+    dbih_dumphandle(sv, msg, level);
+
 
 
 void
@@ -2197,13 +3169,55 @@
     CODE:
     {
     dPERINTERP;
-    PerlIO_printf(DBILOGFP, "DBI::_svdump(%s)", SvPV(sv,na));
+    PerlIO_printf(DBILOGFP, "DBI::_svdump(%s)", neatsvpv(sv,0));
 #ifdef DEBUGGING
     sv_dump(sv);
 #endif
     }
 
 
+double
+dbi_time()
+
+
+SV *
+dbi_profile(h, statement, method, t1, t2)
+    SV *h
+    SV *statement
+    SV *method
+    double t1
+    double t2
+    CODE:
+    D_imp_xxh(h);
+    STRLEN lna = 0;
+    dbi_profile(h, imp_xxh,
+	SvOK(statement) ? SvPV(statement,lna) : Nullch,
+	SvROK(method)   ? SvRV(method)        : method,
+	t1, t2
+    );
+    RETVAL = &sv_undef;
+    OUTPUT:
+    RETVAL
+
+
+SV *
+dbi_profile_merge(dest, ...)
+    SV * dest
+    CODE:
+    {
+	if (!SvROK(dest) || SvTYPE(SvRV(dest)) != SVt_PVAV)
+	    croak("dbi_profile_merge(%s,...) not an array reference", neatsvpv(dest,0));
+	/* items==2 for dest + 1 arg, ST(0) is dest, ST(1) is first arg */
+	while (--items >= 1) {
+	    SV *thingy = ST(items); /* currently has to be an array ref */
+	    dbi_profile_merge(dest, thingy);
+	}
+	RETVAL = newSVsv(*av_fetch((AV*)SvRV(dest), DBIprof_TOTAL_TIME, 1));
+    }
+    OUTPUT:
+    RETVAL
+
+
 MODULE = DBI   PACKAGE = DBI::var
 
 void
@@ -2212,7 +3226,8 @@
     CODE:
     dPERINTERP;
     /* Note that we do not come through the dispatcher to get here.	*/
-    char *meth = SvPV(SvRV(sv),na);	/* what should this tie do ?	*/
+    STRLEN lna;
+    char *meth = SvPV(SvRV(sv),lna);	/* what should this tie do ?	*/
     char type = *meth++;		/* is this a $ or & style	*/
     HV *imp_stash;
     GV *imp_gv;
@@ -2222,14 +3237,8 @@
 
     if (DBIS->debug >= 2 || (ok && DBIc_DEBUGIV(imp_xxh) >= 2)) {
 	trace = 2;
-	PerlIO_printf(DBILOGFP,"    -> $DBI::%s (%c) FETCH from lasth=", meth, type);
-	if (ok) {
-	    SvROK_on(DBI_LAST_HANDLE);
-	    PerlIO_printf(DBILOGFP,"%s\n", SvPV(DBI_LAST_HANDLE,na));
-	    SvROK_off(DBI_LAST_HANDLE);
-	} else {
-	    PerlIO_printf(DBILOGFP,"none\n");
-	}
+	PerlIO_printf(DBILOGFP,"    -> $DBI::%s (%c) FETCH from lasth=%s\n", meth, type,
+		(ok) ? neatsvpv(DBI_LAST_HANDLE,0): "none");
     }
 
     if (type == '!') {	/* special case for $DBI::lasth */
@@ -2238,12 +3247,10 @@
 	}
 	/* Currently we can only return the INNER handle.	*/
 	/* This handle should only be used for true/false tests	*/
-	SvROK_on(DBI_LAST_HANDLE);
-	ST(0) = sv_mortalcopy(DBI_LAST_HANDLE);
+	ST(0) = sv_2mortal(newRV(DBI_LAST_HANDLE));
 	if (trace)
 	    PerlIO_printf(DBILOGFP,"    <- $DBI::%s= %s (inner)\n",
-				meth, SvPV(DBI_LAST_HANDLE,na));
-	SvROK_off(DBI_LAST_HANDLE);
+				meth, neatsvpv(DBI_LAST_HANDLE,0));
 	XSRETURN(1);
     }
     if ( !ok ) {		/* warn() may be changed to a debug later */
@@ -2259,7 +3266,6 @@
 	XSRETURN(1);
     }
     if (type == '"') {	/* special case for $DBI::state	*/
-	STRLEN lna;
 	SV *state = DBIc_STATE(imp_xxh);
 	ST(0) = DBIc_STATE_adjust(imp_xxh, state);
 	if (trace)
@@ -2268,7 +3274,7 @@
     }
     if (type == '$') { /* lookup scalar variable in implementors stash */
 	char *vname = mkvname(DBIc_IMP_STASH(imp_xxh), meth, 0);
-	SV *vsv = get_sv(vname, 1);
+	SV *vsv = perl_get_sv(vname, 1);
 	if (trace)
 	    PerlIO_printf(DBILOGFP,"    <- %s = %s\n", vname, neatsvpv(vsv,0));
 	ST(0) = sv_mortalcopy(vsv);
@@ -2277,8 +3283,8 @@
     /* default to method call via stash of implementor of DBI_LAST_HANDLE */
     imp_stash = DBIc_IMP_STASH(imp_xxh);
     if (DBIS->debug >= 2)
-        fprintf(DBILOGFP,"    >> %s::%s\n", HvNAME(imp_stash), meth);
-    ST(0) = DBI_LAST_HANDLE;
+        PerlIO_printf(DBILOGFP,"    >> %s::%s\n", HvNAME(imp_stash), meth);
+    ST(0) = sv_2mortal(newRV(DBI_LAST_HANDLE));
     if ((imp_gv = gv_fetchmethod(imp_stash,meth)) == NULL) {
         croak("Can't locate $DBI::%s object method \"%s\" via package \"%s\"",
             meth, meth, HvNAME(imp_stash));
@@ -2287,6 +3293,17 @@
     perl_call_sv((SV*)GvCV(imp_gv), GIMME);
 
 
+MODULE = DBI   PACKAGE = DBD::_::db
+
+SV *
+preparse(dbh, statement, ps_accept, ps_return, foo=Nullch)
+    SV *	dbh
+    char *	statement
+    IV		ps_accept
+    IV		ps_return
+    void	*foo
+
+
 
 MODULE = DBI   PACKAGE = DBD::_::st
 
@@ -2412,6 +3429,60 @@
 	}
     }
 
+
+SV *
+fetchrow_hashref(sth, keyattrib=Nullch)
+    SV *	sth
+    char *	keyattrib
+    PREINIT:
+    SV *rowavr;
+    D_imp_sth(sth);
+    CODE:
+    PUSHMARK(sp);
+    XPUSHs(sth);
+    PUTBACK;
+    if (perl_call_method("fetch", G_SCALAR) != 1)
+	croak("panic: DBI fetch");	/* should never happen */
+    if (!keyattrib || !*keyattrib) {
+	SV *kn = DBIc_FetchHashKeyName(imp_sth);
+	if (kn && SvOK(kn))
+	    keyattrib = SvPVX(kn);
+	else
+	    keyattrib = "NAME";
+    }
+    SPAGAIN;
+    rowavr = POPs;
+    PUTBACK;
+    /* have we got an array ref in rowavr */
+    if (SvROK(rowavr) && SvTYPE(SvRV(rowavr)) == SVt_PVAV) {
+	/* get field names. copy to invoke magic FETCH and for safety */
+        SV *ka_rv = newSVsv(*hv_fetch((HV*)DBIc_MY_H(imp_sth), keyattrib,strlen(keyattrib), TRUE));
+	AV *ka_av = (AV*)SvRV(ka_rv);
+	AV *rowav = (AV*)SvRV(rowavr);
+	int num_fields = AvFILL(rowav)+1;
+	HV *hv = newHV();
+
+	int i;
+	for (i=0; i < num_fields; ++i) {	/* honor the original order as sent by the database */
+	    STRLEN len;
+	    SV  **field_name_svp = av_fetch(ka_av, i, 1);
+	    char *field_name     = SvPV(*field_name_svp, len);
+	    hv_store(hv, field_name, len, newSVsv((SV*)(AvARRAY(rowav)[i])), 0);
+	}
+	RETVAL = newRV((SV*)hv);
+	SvREFCNT_dec(hv);  	/* since newRV incremented it	*/
+	SvREFCNT_dec(ka_rv);	/* since we created it		*/
+    }
+    else {
+	RETVAL = &sv_undef;
+#if (PERL_VERSION < 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION <= 4))
+	RETVAL = newSV(0); /* mutable undef for 5.004_04 */
+#endif
+    }
+    OUTPUT:
+    RETVAL
+
+
 void
 fetch(sth)
     SV *	sth
@@ -2542,34 +3613,55 @@
     ST(0) = sv_mortalcopy(errstr);
 
 
+void
+set_err(h, errval, errstr=&sv_no, state=&sv_undef, method=&sv_undef, result=Nullsv)
+    SV *	h
+    SV *	errval
+    SV *	errstr
+    SV *	state
+    SV *	method
+    SV *	result
+    CODE:
+    {
+    D_imp_xxh(h);
+    STRLEN lna;
+    SV **sem_svp;
+    sv_setsv(DBIc_ERR(imp_xxh),    errval);
+    if (errstr==&sv_no || !SvOK(errstr))
+	errstr = errval;
+    sv_setsv(DBIc_ERRSTR(imp_xxh), errstr);
+    if (SvOK(state)) {
+	STRLEN len;
+	if (SvPV(state, len) && len != 5)
+	    croak("set_err: state must be 5 character string");
+	sv_setsv(DBIc_STATE(imp_xxh), state);
+    }
+    else {
+	(void)SvOK_off(DBIc_STATE(imp_xxh));
+    }
+    /* store provided method name so handler code can find it */
+    sem_svp = hv_fetch((HV*)SvRV(h), "dbi_set_err_method", 18, 1);
+    if (SvOK(method))
+	sv_setpv(*sem_svp, SvPV(method,lna));
+    else
+	(void)SvOK_off(*sem_svp);
+
+    /* We don't check RaiseError and call die here because that must be	*/
+    /* done by returning theough dispatch and letting the DBI handle it	*/
+    ST(0) = (result ? result : &sv_undef);
+    }
+
+
 int
-trace(sv, level=0, file=Nullsv)
-    SV *	sv
+trace(h, level=0, file=Nullsv)
+    SV *h
     int	level
     SV *file
     ALIAS:
     debug = 1
     CODE:
-    {
-    dPERINTERP;
-    D_imp_xxh(sv);
-    SV *dsv = DBIc_DEBUG(imp_xxh);
-    STRLEN lna;
-    /* Return trace level in effect now. No change if new value not given */
-    RETVAL = (DBIS->debug > SvIV(dsv)) ? DBIS->debug : SvIV(dsv);
-    set_trace_file(file);
-    if (level != RETVAL) {	 /* set value */
-	if (level > 0) {
-	    PerlIO_printf(DBILOGFP,"    %s trace level set to %d in DBI %s%s\n",
-		SvPV(sv,lna), level, XS_VERSION, dbi_build_opt);
-	    if (!dowarn && level>0)
-		PerlIO_printf(DBILOGFP,"    Note: perl is running without the recommended perl -w option\n");
-	    PerlIO_flush(DBILOGFP);
-	    ix = ix;		/* avoid 'unused variable' warning	*/
-	}
-	sv_setiv(dsv, level);
-    }
-    }
+    ix = ix;	/* avoid 'unused variable' warning	*/
+    RETVAL = set_trace(h, level, file);
     OUTPUT:
     RETVAL
 
diff -u DBI-1.14/DBIXS.h DBI-1.30/DBIXS.h
--- DBI-1.14/DBIXS.h	Sun Jun 11 03:27:52 2000
+++ DBI-1.30/DBIXS.h	Mon Jul 15 12:19:04 2002
@@ -1,6 +1,6 @@
-/* $Id: DBIXS.h,v 10.15 2000/06/11 02:27:27 timbo Exp $
+/* $Id: DBIXS.h,v 11.8 2002/07/15 11:18:57 timbo Exp $
  *
- * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999  Tim Bunce  England
+ * Copyright (c) 1994-2002  Tim Bunce  Ireland
  *
  * See COPYRIGHT section in DBI.pm for usage and distribution rights.
  */
@@ -49,22 +49,19 @@
 #endif
 
 
-#if defined(USE_THREADS) && !defined(DBI_NO_THREADS)
-#define DBI_USE_THREADS
-#define DBI_LOCK	MUTEX_LOCK(DBIS->mutex)
-#define DBI_UNLOCK	MUTEX_UNLOCK(DBIS->mutex)
-#define dbi_mutex perl_mutex
-#define dbi_cond  perl_cond
-#else
 #define DBI_LOCK
 #define DBI_UNLOCK
-#define dbi_mutex void
-#define dbi_cond  void
-#endif
+
+#ifndef DBI_NO_THREADS
+#ifdef USE_ITHREADS
+#define DBI_USE_THREADS
+#endif /* USE_ITHREADS */
+#endif /* DBI_NO_THREADS */
 
 
-/* forward declaration of 'DBI Handle Common Data', see below		*/
+/* forward struct declarations						*/
 
+typedef struct dbistate_st dbistate_t;
 /* implementor needs to define actual struct { dbih_??c_t com; ... }*/
 typedef struct imp_drh_st imp_drh_t;	/* driver			*/
 typedef struct imp_dbh_st imp_dbh_t;	/* database			*/
@@ -88,18 +85,18 @@
     U32  flags;
     int  call_depth;	/* used by DBI to track nested calls (int)	*/
     U16  type;		/* DBIt_DR, DBIt_DB, DBIt_ST			*/
-    SV   *my_h;		/* copy of outer handle HV (not refcounted)	*/
-    SV   *parent_h;	/* parent inner handle (RV(HV)) (r.c.inc)	*/
+    HV   *my_h;		/* copy of outer handle HV (not refcounted)	*/
+    SV   *parent_h;	/* parent inner handle (ref to hv) (r.c.inc)	*/
     imp_xxh_t *parent_com;	/* parent com struct shortcut		*/
-    dbi_cond  *thr_cond;/* condition for thread access (see dispatch)	*/
+    PerlInterpreter * thr_user;  /* thread that owns the handle         */
 
     HV   *imp_stash;	/* who is the implementor for this handle	*/
     SV   *imp_data;	/* optional implementors data (for perl imp's)	*/
 
     I32  kids;		/* count of db's for dr's, st's for db's etc	*/
     I32  active_kids;	/* kids which are currently DBIc_ACTIVE		*/
-    U32  thr_user;	/* thread id currently using the handle		*/
-    void *spare2;
+    U32 pad;		/* keep binary compat */
+    dbistate_t *dbistate;
 } dbih_com_std_t;
 
 typedef struct dbih_com_attr_st {
@@ -109,9 +106,10 @@
     SV *State;		/* Standard SQLSTATE, 5 char string	*/
     SV *Err;		/* Native engine error code		*/
     SV *Errstr;		/* Native engine error message		*/
-    SV *Handlers;
+    SV *spare;
     U32  LongReadLen;	/* auto read length for long/blob types	*/
-    I32  spare1;
+    SV *FetchHashKeyName;	/* for fetchrow_hashref		*/
+    /* (NEW FIELDS?... DON'T FORGET TO UPDATE dbih_clearcom()!)	*/
 } dbih_com_attr_t;
 
 
@@ -186,20 +184,23 @@
 #define DBIc_PARENT_COM(imp)  	_imp2com(imp, std.parent_com)
 #define DBIc_THR_COND(imp)  	_imp2com(imp, std.thr_cond)
 #define DBIc_THR_USER(imp)  	_imp2com(imp, std.thr_user)
-#define DBIc_THR_USER_NONE  	(U32_MAX)
+#define DBIc_THR_USER_NONE  	(0xFFFF)
 #define DBIc_IMP_STASH(imp)  	_imp2com(imp, std.imp_stash)
 #define DBIc_IMP_DATA(imp)  	_imp2com(imp, std.imp_data)
+#define DBIc_DBISTATE(imp)  	_imp2com(imp, std.dbistate)
+#define DBIc_LOGPIO(imp)  	DBIc_DBISTATE(imp)->logfp
 #define DBIc_KIDS(imp)  	_imp2com(imp, std.kids)
 #define DBIc_ACTIVE_KIDS(imp)  	_imp2com(imp, std.active_kids)
+#define DBIc_LAST_METHOD(imp)  	_imp2com(imp, std.last_method)
 
 #define DBIc_DEBUG(imp)		(_imp2com(imp, attr.Debug))
 #define DBIc_DEBUGIV(imp)	SvIV(DBIc_DEBUG(imp))
 #define DBIc_STATE(imp)		SvRV(_imp2com(imp, attr.State))
 #define DBIc_ERR(imp)		SvRV(_imp2com(imp, attr.Err))
 #define DBIc_ERRSTR(imp)	SvRV(_imp2com(imp, attr.Errstr))
-#define DBIc_HANDLERS(imp)	SvRV(_imp2com(imp, attr.Handlers))
 #define DBIc_LongReadLen(imp)  	_imp2com(imp, attr.LongReadLen)
 #define DBIc_LongReadLen_init	80	/* may change */
+#define DBIc_FetchHashKeyName(imp) (_imp2com(imp, attr.FetchHashKeyName))
 
 /* handle sub-type specific fields						*/
 /*	dbh	*/
@@ -213,32 +214,43 @@
 #define DBIc_FDESC_AV(imp)  	_imp2com(imp, fields_fdav)
 #define DBIc_FDESC(imp, i)  	((imp_fdh_t*)(void*)SvPVX(AvARRAY(DBIc_FDESC_AV(imp))[i]))
 
-#define DBIcf_COMSET	  0x0001	/* needs to be clear'd before free'd	*/
-#define DBIcf_IMPSET	  0x0002	/* has implementor data to be clear'd	*/
-#define DBIcf_ACTIVE	  0x0004	/* needs finish/disconnect before clear	*/
-#define DBIcf_IADESTROY	  0x0008	/* do DBIc_ACTIVE_off before DESTROY	*/
-#define DBIcf_WARN  	  0x0010	/* warn about poor practice etc  	*/
-#define DBIcf_COMPAT  	  0x0020	/* compat/emulation mode (eg oraperl)	*/
-
-#define DBIcf_ChopBlanks  0x0040	/* rtrim spaces from fetch char columns	*/
-#define DBIcf_RaiseError  0x0080	/* throw exception (croak) on error	*/
-#define DBIcf_PrintError  0x0100	/* warn() on error			*/
-#define DBIcf_AutoCommit  0x0200	/* dbh only. used by drivers		*/
-#define DBIcf_LongTruncOk 0x0400	/* truncation to LongReadLen is okay	*/
-#define DBIcf_MultiThread 0x0800	/* allow multiple threads to enter	*/
-#define DBIcf_Taint       0x1000	/* taint fetched data			*/
-
-#define DBIcf_INHERITMASK			/* what NOT to pass on to children */	\
-	(U32)( DBIcf_COMSET | DBIcf_IMPSET | DBIcf_ACTIVE | DBIcf_IADESTROY		\
-	/* These are for dbh only:	*/						\
-	| DBIcf_AutoCommit	)
-
-/* general purpose flag setting and testing macros (except ACTIVE)	*/
-#define DBIc_is(imp, flag)	(DBIc_FLAGS(imp) &   (flag))
-#define DBIc_has(imp,flag)	DBIc_is(imp, flag) /* alias for _is */
-#define DBIc_on(imp, flag)	(DBIc_FLAGS(imp) |=  (flag))
-#define DBIc_off(imp,flag)	(DBIc_FLAGS(imp) &= ~(flag))
-#define DBIc_set(imp,flag,on)	((on) ? DBIc_on(imp, flag) : DBIc_off(imp,flag))
+#define DBIcf_COMSET	  0x000001	/* needs to be clear'd before free'd	*/
+#define DBIcf_IMPSET	  0x000002	/* has implementor data to be clear'd	*/
+#define DBIcf_ACTIVE	  0x000004	/* needs finish/disconnect before clear	*/
+#define DBIcf_IADESTROY	  0x000008	/* do DBIc_ACTIVE_off before DESTROY	*/
+#define DBIcf_WARN  	  0x000010	/* warn about poor practice etc  	*/
+#define DBIcf_COMPAT  	  0x000020	/* compat/emulation mode (eg oraperl)	*/
+
+#define DBIcf_ChopBlanks  0x000040	/* rtrim spaces from fetch char columns	*/
+#define DBIcf_RaiseError  0x000080	/* throw exception (croak) on error	*/
+#define DBIcf_PrintError  0x000100	/* warn() on error			*/
+#define DBIcf_AutoCommit  0x000200	/* dbh only. used by drivers		*/
+#define DBIcf_LongTruncOk 0x000400	/* truncation to LongReadLen is okay	*/
+#define DBIcf_MultiThread 0x000800	/* allow multiple threads to enter	*/
+#define DBIcf_Taint       0x001000	/* taint fetched data			*/
+#define DBIcf_ShowErrorStatement  0x002000	/* include Statement in error	*/
+#define DBIcf_BegunWork   0x004000	/* between begin_work & commit/rollback */
+#define DBIcf_HandleError 0x008000	/* has coderef in HandleError attribute	*/
+#define DBIcf_Profile     0x010000	/* profile activity on this handle	*/
+
+#define DBIcf_INHERITMASK		/* what NOT to pass on to children */	\
+  (U32)( DBIcf_COMSET | DBIcf_IMPSET | DBIcf_ACTIVE | DBIcf_IADESTROY		\
+  /* These are for dbh only:	*/						\
+  | DBIcf_AutoCommit | DBIcf_BegunWork	)
+
+/* general purpose bit setting and testing macros			*/
+#define DBIbf_is( bitset,flag)		((bitset) &   (flag))
+#define DBIbf_has(bitset,flag)		DBIbf_is(bitset, flag) /* alias for _is */
+#define DBIbf_on( bitset,flag)		((bitset) |=  (flag))
+#define DBIbf_off(bitset,flag)		((bitset) &= ~(flag))
+#define DBIbf_set(bitset,flag,on)	((on) ? DBIbf_on(bitset, flag) : DBIbf_off(bitset,flag))
+
+/* as above, but specifically for DBIc_FLAGS imp flags (except ACTIVE)	*/
+#define DBIc_is(imp, flag)	DBIbf_is( DBIc_FLAGS(imp), flag)
+#define DBIc_has(imp,flag)	DBIc_is(imp, flag) /* alias for DBIc_is */
+#define DBIc_on(imp, flag)	DBIbf_on( DBIc_FLAGS(imp), flag)
+#define DBIc_off(imp,flag)	DBIbf_off(DBIc_FLAGS(imp), flag)
+#define DBIc_set(imp,flag,on)	DBIbf_set(DBIc_FLAGS(imp), flag, on)
 
 #define DBIc_COMSET(imp)	DBIc_is(imp, DBIcf_COMSET)
 #define DBIc_COMSET_on(imp)	DBIc_on(imp, DBIcf_COMSET)
@@ -334,7 +346,7 @@
 
 /* --- DBI State Structure --- */
 
-typedef struct {
+struct dbistate_st {
 
 #define DBISTATE_VERSION  94	/* Must change whenever dbistate_t does	*/
 
@@ -363,15 +375,17 @@
     SV        * (*make_fdsv)	_((SV *sth, char *imp_class, STRLEN imp_size, char *col_name));
     int         (*bind_as_num)	_((int sql_type, int p, int s, int *t, void *v));
     int         (*hash)		_((char *string, long i));
-    AV        * (*preparse)	_((SV *sth, char *statement, U32 flags, U32 spare));
+    SV        * (*preparse)	_((SV *sth, char *statement, IV ps_return, IV ps_accept, void *foo));
+
+    SV *neatsvpvlen;		/* only show dbgpvlen chars when debugging pv's	*/
 
-    SV *neatsvpvlen;	/* only show dbgpvlen chars when debugging pv's	*/
+    PerlInterpreter * thr_owner;	/* thread that owns this dbistate	*/
 
-    dbi_mutex	*mutex;
+    int         (*logmsg)	_((imp_xxh_t *imp_xxh, char *fmt, ...));
+    int         (*set_err)	_((imp_xxh_t *imp_xxh, char *fmt, ...));
 
-    int         (*logmsg)	_((SV *h, imp_xxh_t *imp_xxh, char *fmt, ...));
-    void *pad[8];
-} dbistate_t;
+    void *pad2[7];
+};
 
 /* macros for backwards compatibility */
 #define set_attr(h, k, v)	set_attr_k(h, k, 0, v)
@@ -391,7 +405,7 @@
 /* note that USE_ITHREADS implies MULTIPLICITY                      */
 #if defined(MULTIPLICITY) || defined(PERL_OBJECT) || defined(PERL_CAPI)
 
-# define DBISTATE_DECLARE
+# define DBISTATE_DECLARE typedef int dummy_dbistate /* keep semicolon from feeling lonely */
 # define DBISTATE_ASSIGN(st)
 # define DBISTATE_INIT
 static dbistate_t **get_dbistate() {
@@ -399,9 +413,7 @@
 }
 # undef DBIS
 # define DBIS (*get_dbistate())
-
-/* temporary for drivers that mistakenly use 'dbis' instead of 'DBIS' */
-# define dbis (*get_dbistate())
+# define dbis (*get_dbistate()) /* temp for bad drivers using 'dbis' instead of 'DBIS' */
 
 #else	/* plain and simple non perl object / multiplicity case */
 
@@ -429,9 +441,10 @@
 /* attribs value. One day we may add some extra magic in here.		*/
 #define DBD_ATTRIBS_CHECK(func, h, attribs)	\
     if ((attribs) && SvOK(attribs)) {		\
+	STRLEN lna=0;				\
 	if (!SvROK(attribs) || SvTYPE(SvRV(attribs))!=SVt_PVHV)		\
 	    croak("%s->%s(...): attribute parameter '%s' is not a hash ref",	\
-		    SvPV(h,na), func, SvPV(attribs,na));		\
+		    SvPV(h,lna), func, SvPV(attribs,lna));		\
     } else (attribs) = Nullsv
 
 #define DBD_ATTRIB_GET_SVP(attribs, key,klen)			\
@@ -442,6 +455,10 @@
 #define DBD_ATTRIB_GET_BOOL(attribs, key,klen, svp, var)		\
 	if ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)	\
 	    var = SvTRUE(*svp)
+
+#define DBD_ATTRIB_TRUE(attribs, key,klen, svp)				\
+	(  ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)	\
+	    ? SvTRUE(*svp) : 0 )
 
 #define DBD_ATTRIB_GET_IV(attribs, key,klen, svp, var)			\
 	if ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)	\
diff -u DBI-1.14/Driver.xst DBI-1.30/Driver.xst
--- DBI-1.14/Driver.xst	Sun Jun 11 03:27:50 2000
+++ DBI-1.30/Driver.xst	Thu Jul 18 15:24:03 2002
@@ -1,10 +1,13 @@
-#  $Id: Driver.xst,v 10.18 2000/06/02 16:31:37 timbo Exp $
-#  Copyright (c) 1994,1995,1996,1997,1998  Tim Bunce
+#  $Id: Driver.xst,v 11.7 2002/07/15 11:18:57 timbo Exp $
+#  Copyright (c) 1997-2002  Tim Bunce  Ireland
 #
 #  You may distribute under the terms of either the GNU General Public
 #  License or the Artistic License, as specified in the Perl README file.
 
 
+#include "Driver_xst.h"
+
+
 MODULE = DBD::~DRIVER~	PACKAGE = DBD::~DRIVER~
 
 REQUIRE:    1.929
@@ -66,11 +69,114 @@
 
 
 void
+selectall_arrayref(...)
+    PREINIT:
+    SV *sth;
+    SV **maxrows_svp;
+    SV **tmp_svp;
+    SV *attr = &sv_undef;
+    imp_sth_t *imp_sth;
+    CODE:
+    if (items > 2) {
+	attr = ST(2);
+	if (SvROK(attr) &&
+		(DBD_ATTRIB_TRUE(attr,"Slice",5,tmp_svp) || DBD_ATTRIB_TRUE(attr,"Columns",7,tmp_svp))
+	) {
+	    /* fallback to perl implementation */
+	    ST(0) = dbixst_bounce_method("DBD::~DRIVER~::db::SUPER::selectall_arrayref", items);
+	    XSRETURN(1);
+	}
+    }
+    /* --- prepare --- */
+    if (SvROK(ST(1))) {
+	sth = ST(1);
+    }
+    else {
+	sth = dbixst_bounce_method("prepare", 3);
+	if (!SvROK(sth))
+	    XSRETURN_UNDEF;
+    }
+    imp_sth = (imp_sth_t*)(DBIh_COM(sth));
+    /* --- bind_param --- */
+    if (items > 3) {    /* need to bind params before execute */
+        if (!dbdxst_bind_params(sth, imp_sth, items-2, ax+2) ) {
+            XSRETURN_UNDEF;
+        }
+    }
+    /* --- execute --- */
+    DBIc_ROW_COUNT(imp_sth) = 0;
+    if ( dbd_st_execute(sth, imp_sth) <= -2 ) { /* -2 == error */
+        XSRETURN_UNDEF;
+    }
+    /* --- fetchall --- */
+    maxrows_svp = DBD_ATTRIB_GET_SVP(attr, "MaxRows", 7);
+    ST(0) = dbdxst_fetchall_arrayref(sth, &sv_undef, (maxrows_svp) ? *maxrows_svp : &sv_undef);
+
+
+void
+selectrow_arrayref(...)
+    ALIAS:
+	selectrow_array = 1
+    PREINIT:
+    imp_sth_t *imp_sth;
+    SV *sth;
+    AV *row_av;
+    PPCODE:
+    if (SvROK(ST(1))) {
+	sth = ST(1);
+    }
+    else {
+	/* --- prepare --- */
+	sth = dbixst_bounce_method("prepare", 3);
+	if (!SvROK(sth))
+	    XSRETURN_UNDEF;
+    }
+    imp_sth = (imp_sth_t*)(DBIh_COM(sth));
+    /* --- bind_param --- */
+    if (items > 3) {	/* need to bind params before execute */
+	if (!dbdxst_bind_params(sth, imp_sth, items-2, ax+2) ) {
+	    XSRETURN_UNDEF;
+	}
+    }
+    /* --- execute --- */
+    DBIc_ROW_COUNT(imp_sth) = 0;
+    if ( dbd_st_execute(sth, imp_sth) <= -2 ) {	/* -2 == error */
+        XSRETURN_UNDEF;
+    }
+    /* --- fetchrow_arrayref --- */
+    row_av = dbd_st_fetch(sth, imp_sth);
+    if (!row_av) {
+	if (GIMME == G_SCALAR)
+	    PUSHs(&sv_undef);
+    }
+    else if (ix == 1) { /* selectrow_array */
+	int i;
+	int num_fields = AvFILL(row_av)+1;
+	if (GIMME == G_SCALAR)
+	    num_fields = 1; /* return just first field */
+	EXTEND(sp, num_fields);
+	for(i=0; i < num_fields; ++i) {
+	    PUSHs(AvARRAY(row_av)[i]);
+	}
+    }
+    else {
+	PUSHs( sv_2mortal(newRV((SV *)row_av)) );
+    }
+    /* --- finish --- */
+#ifdef dbd_db_finish3
+    dbd_st_finish3(sth, imp_sth, 0);
+#else
+    dbd_st_finish(sth, imp_sth);
+#endif
+
+
+
+void
 commit(dbh)
     SV *	dbh
     CODE:
     D_imp_dbh(dbh);
-    if (DBIc_has(imp_dbh,DBIcf_AutoCommit))
+    if (DBIc_has(imp_dbh,DBIcf_AutoCommit) && DBIc_WARN(imp_dbh))
 	warn("commit ineffective with AutoCommit enabled");
     ST(0) = dbd_db_commit(dbh, imp_dbh) ? &sv_yes : &sv_no;
 
@@ -79,7 +185,7 @@
     SV *	dbh
     CODE:
     D_imp_dbh(dbh);
-    if (DBIc_has(imp_dbh,DBIcf_AutoCommit))
+    if (DBIc_has(imp_dbh,DBIcf_AutoCommit) && DBIc_WARN(imp_dbh))
 	warn("rollback ineffective with AutoCommit enabled");
     ST(0) = dbd_db_rollback(dbh, imp_dbh) ? &sv_yes : &sv_no;
 
@@ -281,28 +387,12 @@
     CODE:
     D_imp_sth(sth);
     int retval;
-    if (items > 1) {
-	/* Handle binding supplied values to placeholders	*/
-	int i;
-        SV *idx;
-	if (items-1 != DBIc_NUM_PARAMS(imp_sth)
-	    && DBIc_NUM_PARAMS(imp_sth) != DBIc_NUM_PARAMS_AT_EXECUTE
-	) {
-	    croak("execute called with %ld bind variables when %d are needed",
-		    items-1, DBIc_NUM_PARAMS(imp_sth));
+    if (items > 1) {	/* need to bind params */
+	if (!dbdxst_bind_params(sth, imp_sth, items, ax) ) {
 	    XSRETURN_UNDEF;
 	}
-        idx = sv_2mortal(newSViv(0));
-	for(i=1; i < items ; ++i) {
-	    SV* value = ST(i);
-            if (SvGMAGICAL(value))
-                mg_get(value);	/* trigger magic to FETCH the value     */
-	    sv_setiv(idx, i);
-	    if (!dbd_bind_ph(sth, imp_sth, idx, value, 0, Nullsv, FALSE, 0)) {
-		XSRETURN_UNDEF;	/* dbd_bind_ph already registered error	*/
-	    }
-	}
     }
+    /* XXX this code is duplicated in selectrow_arrayref above	*/
     if (DBIc_ROW_COUNT(imp_sth) > 0) /* reset for re-execute */
 	DBIc_ROW_COUNT(imp_sth) = 0;
     retval = dbd_st_execute(sth, imp_sth);
@@ -336,16 +426,31 @@
     PPCODE:
     D_imp_sth(sth);
     AV *av;
-    if (0) ix = ix;	/* avoid unused variable warning */
     av = dbd_st_fetch(sth, imp_sth);
     if (av) {
-	int num_fields = AvFILL(av)+1;
 	int i;
+	int num_fields = AvFILL(av)+1;
 	EXTEND(sp, num_fields);
 	for(i=0; i < num_fields; ++i) {
 	    PUSHs(AvARRAY(av)[i]);
 	}
+	if (0) ix = ix;	/* avoid unused variable warning */
     }
+
+
+void
+fetchall_arrayref(sth, slice=&sv_undef, batch_row_count=&sv_undef)
+    SV *	sth
+    SV *	slice
+    SV *	batch_row_count
+    CODE:
+    if (SvOK(slice)) {  /* fallback to perl implementation */
+        ST(0) = dbixst_bounce_method("DBD::~DRIVER~::st::SUPER::fetchall_arrayref", 3);
+    }
+    else {
+	ST(0) = dbdxst_fetchall_arrayref(sth, slice, batch_row_count);
+    }
+
 
 void
 finish(sth)
Only in DBI-1.30: Driver_xst.h
diff -u DBI-1.14/MANIFEST DBI-1.30/MANIFEST
--- DBI-1.14/MANIFEST	Sun Jun 11 03:27:44 2000
+++ DBI-1.30/MANIFEST	Mon Jul 15 12:23:12 2002
@@ -3,6 +3,7 @@
 DBI.xs	The Database Interface Module XS code
 DBIXS.h	The DBI XS public interface for Drivers (DBD::...)
 Driver.xst	Template driver xs file
+Driver_xst.h	Template driver xs support code
 MANIFEST
 Makefile.PL	The Makefile generator
 Perl.xs	Test harness (currently) for Driver.xst
@@ -14,24 +15,34 @@
 dbiproxy.PL	Frontend for DBI::ProxyServer
 dbish.PL	Frontend for DBI::Shell
 lib/Bundle/DBI.pm	A bundle for automatic installation via CPAN.
-lib/DBD/ADO.pm		An experimental driver for Win32 ADO
 lib/DBD/ExampleP.pm	A very simple example Driver module
-lib/DBD/Multiplex.pm	A driver for multiplexing other drivers
 lib/DBD/NullP.pm	An empty example Driver module
 lib/DBD/Proxy.pm	Proxy driver
 lib/DBD/Sponge.pm	A driver for fake cursors (precached data)
+lib/DBI/Const/GetInfoType.pm	GetInfo type code data based on standards
+lib/DBI/Const/GetInfoReturn.pm	GetInfo return values plus tools based on standards
+lib/DBI/Const/GetInfo/ANSI.pm	GetInfo data based on ANSI standard
+lib/DBI/Const/GetInfo/ODBC.pm	GetInfo data based on ODBC standard
 lib/DBI/DBD.pm		Some basic help for people writing DBI drivers
 lib/DBI/FAQ.pm		The DBI FAQ in module form for perldoc
 lib/DBI/Format.pm		Class for formatting rows of data
+lib/DBI/Profile.pm	Manage DBI usage profile data
 lib/DBI/ProxyServer.pm	The proxy drivers server
+lib/DBI/PurePerl.pm	A DBI.xs emulation in Perl
 lib/DBI/Shell.pm	The DBI interactive shell
 lib/DBI/W32ODBC.pm	An experimental DBI emulation layer for Win32::ODBC
 lib/Win32/DBIODBC.pm	An experimental Win32::ODBC emulation layer for DBI
-t/basics.t	Test script for very basic DBI functionality
-t/dbidrv.t	Test script for loading drivers and using DBI vars
-t/examp.t	Test script for the DBI using the ExampleP driver
-t/meta.t	Test script for metadata methods
-t/proxy.t	Test script for the proxy driver
-t/shell.t	Test script for DBI::Shell
-t/subclass.t	Test script for subclassing the DBI
+t/01basics.t
+t/02dbidrv.t
+t/03hleak.t
+t/04mods.t
+t/05thrclone.t
+t/10examp.t
+t/15array.t
+t/20meta.t
+t/30subclass.t
+t/40profile.t
+t/60preparse.t
+t/70shell.t
+t/80proxy.t
 test.pl	A very simple test harness using ExampleP.pm
diff -u DBI-1.14/Makefile.PL DBI-1.30/Makefile.PL
--- DBI-1.14/Makefile.PL	Sun Jun 11 03:27:53 2000
+++ DBI-1.30/Makefile.PL	Mon Jul 15 12:19:23 2002
@@ -1,47 +1,74 @@
 # -*- perl -*-
 #
-# $Id: Makefile.PL,v 10.14 2000/06/11 02:27:27 timbo Exp $
+# $Id: Makefile.PL,v 11.8 2002/07/15 11:18:57 timbo Exp $
 #
-#  Copyright (c) 1994,1995,1996,1997  Tim Bunce  England
+#  Copyright (c) 1994-2002  Tim Bunce  England
 #
 # See COPYRIGHT section in DBI.pm for usage and distribution rights.
 
 BEGIN {
-    require 5.004;
+    require 5.005_03;
     unshift @INC, "lib";
 }
 
 use ExtUtils::MakeMaker 5.16, qw(WriteMakefile $Verbose);
 use Getopt::Long;
 use Config;
+use File::Find;
+use File::Spec;
+use strict;
 
 use Test::Harness;	# tests use it so check it's loadable early
 
+use DBI::DBD;
 
+$| = 1;
+$^W = 1;
 my $os = $^O;
 my $osvers = $Config{osvers};
 $osvers =~ s/^\s*(\d+\.\d+).*/$1/;  # drop sub-sub-version: 2.5.1 -> 2.5
 my $ext_pl = $^O eq 'VMS' ? '.pl' : '';
 
 $::opt_v = 0;
+$::opt_thread = 1;	# thread if we can, use "-nothread" to disable
 $::opt_g = 0;
-$::opt_thread = 1;	# unset later if not a threaded perl
+$::opt_g = 1 if -d 'RCS' && $ENV{LOGNAME} eq 'timbo'; # it's me!
 
 GetOptions(qw(v! g! thread!))
 	or die "Invalid arguments\n";
 
 $::opt_g &&= '-g';  # convert to actual string
 
-if ($Config{archname} =~ /-thread\b/i) {
+if ($Config{useithreads}) {
     print "\n";
-    print "*** You are using a perl with experimental threading enabled!\a\n";
-    print "*** You should be aware that using multiple threads is unstable\n";
-    print "*** are should NOT be done in production environments.\n";
-    print "DBI thread mutex protection is ", $::opt_thread ? "enabled\n" : "disabled!\n";
+    print "*** You are using a perl configured with threading enabled.\a\n";
+    print "*** You should be aware that using multiple threads is\n";
+    print "*** not recommended for production environments.\n";
     print "\n";
-    sleep 5;
+    sleep 4;
+    $::opt_thread = 1;
 }
 else {
+
+    if ($Config{archname} =~ /thread/ && $::opt_thread) {
+	# oh dear... tell it like it is:
+	print "\n";
+	print "DBI versions from 1.29 onwards no longer support the old style\n";
+	print "of perl threading (now known as '5005 threads'). It is badly flawed\n";
+	print "and could never be safe to use in production environments.\n\n";
+	print "If you are using multiple threads you are *strongly* encouraged to\n";
+	print "upgrade to perl 5.8.x or later.\n";
+	print "If you are not using multiple threads you are *strongly* encouraged to\n";
+	print "upgrade to at least 5.6.x (preferably perl 5.8.x or later.)\n";
+	print "or at the very least rebuild this version with threading disabled.\n";
+	print "If you have stick with your current build of perl...\n";
+	print "then you also have to stick with DBI 1.28 for safety.\n";
+	print "Or if *desparate* you may be able to build this DBI using 'perl Makefile.PL -nothread' but\n";
+	print "but but, that will have *no* logic to handle threads because the logic\n";
+	print "that was there for 5005 threads has now been removed! You have been warned.\n";
+	die "*** ABORTED.\n";
+    }
+
     $::opt_thread = 0;
 }
 
@@ -76,13 +103,13 @@
 }
 
  
-%opts = (
+my %opts = (
     NAME=> 'DBI',
     VERSION_FROM=> 'DBI.pm',
     EXE_FILES => [ "dbish$ext_pl", "dbiproxy$ext_pl" ],
     DIR => [],
     dynamic_lib => { OTHERLDFLAGS => "$::opt_g" },
-    clean => { FILES=> "\$(DISTVNAME)/ Perl.xsi "
+    clean => { FILES=> "\$(DISTVNAME) Perl.xsi "
 			."dbish$ext_pl dbiproxy$ext_pl ndtest.prt" },
     dist  => {
 	DIST_DEFAULT=> 'clean distcheck disttest ci tardist',
@@ -91,14 +118,16 @@
     },
 );
 
-if ($ExtUtils::MakeMaker::VERSION >= 5.43) {
+if ($] >= 5.6 ) {
     $opts{ABSTRACT_FROM} = 'DBI.pm';
-    $opts{AUTHOR} = 'Tim Bunce (dbi-users@isc.org)';
+    $opts{AUTHOR} = 'Tim Bunce (dbi-users@perl.org)';
     $opts{CAPI} = 'TRUE' if $Config{archname} =~ /-object\b/i;
 }
 
-$opts{DEFINE} = '-Wall -Wno-comment'
-    if $Config{cc} eq 'gcc';	# ask gcc to be mildly pedantic
+if ($Config{gccversion}) {	# ask gcc to be mildly pedantic
+    $opts{DEFINE} .= ' -Wall -Wno-comment';
+    $opts{DEFINE} .= ' -Wpointer-arith' if $::opt_g;
+}
 
 $opts{DEFINE} .= ' -DDBI_NO_THREADS' unless $::opt_thread;
 
@@ -119,16 +148,20 @@
     $opts{LIBS} = "-L$Config{archlib}/CORE";
 }
 
-# Set some private WriteMakefile options if this is 'me' :-)
-if ($ENV{S_ARCH_SW} && $ENV{LOGNAME} eq 'timbo'){	# a reasonable guess
-    $opts{DEFINE} .= ' -g -Wpointer-arith -Wconversion';
-}
-
 warn "WARNING: Your GNU C compiler is very old. Please upgrade.\n"
 	if ($Config{gccversion} and $Config{gccversion} =~ m/^(1|2\.[1-5])/);
 
+# Set aside some values for post_initialize() in package MY
+my ( $cfg_privlibexp, $cfg_archlibexp, $cfg_sitelibexp, $cfg_sitearchexp,
+     $cfg_man3direxp ) =
+     @Config{qw( privlibexp archlibexp sitelibexp sitearchexp man3direxp ) };
+
 $Verbose = $::opt_v;
-WriteMakefile( %opts );
+WriteMakefile(
+    dbd_edit_mm_attribs(\%opts, {
+	create_pp_tests => 1,
+    })
+);
 
 warn <<EOT;
 
@@ -146,8 +179,6 @@
     use the ppm utility to fetch and install a prebuilt DBI instead.
 EOT
 
-exit 0;
-
 
 # =====================================================================
 
@@ -173,57 +204,63 @@
 sub post_initialize {
     my($self) = shift;
 
-    if ($Config{privlibexp} ne $Config{sitelibexp}) {
+    if ($cfg_privlibexp ne $cfg_sitelibexp) {
+	# this block could probably be removed now
+	my %old;
+	File::Find::find( sub {
+	    local $_ = $File::Find::name;
+	    s:\\:/:g if $os eq 'MSWin32';
+	    $File::Find::prune = 1, return
+		if -d $_ && ( $_ eq $cfg_sitelibexp ||
+			      $_ eq $cfg_sitearchexp ||
+			      $_ eq $cfg_man3direxp );
+	    ++$old{$_} if m:\bDB(I|D$):; # DBI files, but just DBD dirs
+	}, $cfg_privlibexp, $cfg_archlibexp );
+	if ( %old ) {
 	warn "
 Warning: By default new modules are installed into your 'site_lib'
-directories. Since site_lib directories come after the normal library
-directories you must delete old DBI files and directories from your
-'privlib' and 'archlib' directories and their auto subdirectories.
-If you don't have an old version of the DBI installed you can ignore this.
-";
-	my $find = "find $Config{privlibexp} $Config{archlibexp} ";
-	$find .= "-name 'DB*' -print | sort | uniq";
-	if (open(FIND, "$find |")) {
-	    my @old;
-	    while(<FIND>) {
-		next unless m:\bDB(I|D$):;
-		next if m:^\Q$Config{sitelibexp}/:;
-		next if m:^\Q$Config{sitearchexp}/:;
-		next if m:^\Q$Config{man3direxp}/:;
-		chop;
-		push @old, $_;
-	    }
-	    close(FIND);
-	    warn "Here's a list of probable old files and directories:\n ",
-		    join("\n ",@old),"\n" if @old;
-	    warn "Reinstall DBD::* drivers after deleting the listed DBD directories.\n"
-		if "@old" =~ /\bDBD$/m;
-	    warn "\n";
+         directories. Since site_lib directories come after the normal library
+         directories you must delete old DBI files and directories from your
+         'privlib' and 'archlib' directories and their auto subdirectories.
+
+Reinstall DBI and your DBD::* drivers after deleting the old directories.
+
+Here's a list of probable old files and directories:
+
+ " . join( "\n ", ( sort keys %old ), "\n" );
 	}
     }
 
     # install files that DBD's may need
-    my $file;
-    foreach $file (qw(DBIXS.h dbi_sql.h dbd_xsh.h dbipport.h), <*.xst>) {
-	$self->{PM}->{$file} = '$(INST_ARCHAUTODIR)/'.$file;
-    }
+    File::Find::find( sub {
+	$File::Find::prune = 1, return if -d $_ && '.' ne $_;
+	$self->{PM}->{$_} = File::Spec->catfile($self->{INST_ARCHAUTODIR}, $_)
+	    if '.h' eq substr( $_, -2 ) || '.xst' eq substr( $_, -4 );
+        }, '.' );
 
     return '';
 }
 
 
 sub post_constants {
+    my($self) = shift;
 
     # ensure that Driver.xst and related code gets tested
-
-    BEGIN {
-	require 'lib/DBI/DBD.pm';
-	import DBI::DBD;
-    }
-    my $xst = dbd_postamble();
+    my $xst = main::dbd_postamble();
     $xst =~ s/\$\(BASEEXT\)/Perl/g;
     $xst .= '
 DBI.c: Perl$(OBJ_EXT)
+
+# make Changes file available as installed pod docs "perldoc DBI::Changes"
+inst_libdbi = ' . File::Spec->catdir($self->{INST_LIB}, 'DBI') . '
+changes_pm = '  . File::Spec->catfile($self->{INST_LIB}, 'DBI', 'Changes.pm') . '
+
+config :: $(changes_pm)
+
+$(changes_pm): Changes
+	$(NOECHO) $(MKPATH) $(inst_libdbi)
+	$(NOECHO) $(RM_F) $(changes_pm)
+	$(CP) Changes $(changes_pm)
 ';
 
     return $xst;
diff -u DBI-1.14/README DBI-1.30/README
--- DBI-1.14/README	Tue May 30 21:57:58 2000
+++ DBI-1.30/README	Wed Jun  5 04:27:19 2002
@@ -1,12 +1,12 @@
 DBI - The Perl Database Interface by Tim Bunce.
 
-Copyright (c) 1994-2000  Tim Bunce  England.
+Copyright (c) 1994-2002  Tim Bunce  Ireland.
 
 See COPYRIGHT section in DBI.pm for usage and distribution rights.
 
 RECENT NEWS:
 
-    The DBI comes bundled with DBI::Shell, DBD::ADO, DBD::Proxy and
+    The DBI comes bundled with DBI::Shell, DBD::Proxy and
     DBI::ProxyServer modules. Those are still alpha releases.
 
     Note that recent additions to the DBI spec (marked *NEW*) may
@@ -34,19 +34,17 @@
 which you will be told are already answered in the documentation.
 
 For more information and to keep informed about progress you can join
-the a mailing list via http://www.isc.org/dbi-lists.html (if you are
-unable to use the web you can subscribe by sending a message to
-dbi-users-request@isc.org, it may take a few days to be processed).
+the a mailing list via mailto:dbi-users-help@perl.org
 
 Much useful information and online archives of the mailing lists can be
-found at http://www.symbolstone.org/technology/perl/DBI
+found at http://dbi.perl.org/
 
 See also http://www.perl.com/CPAN/
 
 
 BEFORE BUILDING, TESTING AND INSTALLING this you will need to:
 
-    Build, test and install Perl 5 (preferably 5.004_04 or later).
+    Build, test and install Perl 5 (5.005_03 or later).
     It is very important to test it and actually install it!
     (You can use "Configure -Dprefix=..." to build a private copy.)
 
@@ -63,7 +61,7 @@
 
 First, carefully read the notes at the bottom of this file.
 
-If you can't fix it your self please post details to dbi-users@isc.org.
+If you can't fix it your self please post details to dbi-users@perl.org.
 You can CC them to me at Tim.Bunce@ig.co.uk BUT please DON'T send them
 just to me (I'm likely to ignore it).  Please include:
 
@@ -90,10 +88,8 @@
 Please don't post problems to comp.lang.perl.* or perl5-porters.
 This software is supported via the dbi-users mailing list.  For more
 information and to keep informed about progress you can join the
-mailing list via http://www.isc.org/dbi-lists.html (if you are unable
-to use the web you can subscribe by sending a message to
-dbi-request@isc.org, it may take a few days to be processed by hand.
-Please note that I do not run or manage the mailing list).
+mailing list via mailto:dbi-users-help@perl.org
+(please note that I do not run or manage the mailing list).
 
 It is important to check that you are using the latest version before
 posting. If you're not then I'm *very* likely to simply say "upgrade to
@@ -111,19 +107,11 @@
 
 ---
 If you get an error like "fatal: relocation error: symbol not found: ..."
-do not use GNU as or GNU ld on Solaris. Delete or rename them, they are
-just bad news.  In the words of John D Groenveld <groenvel@cse.psu.edu>:
-Run, dont walk, to your console and 'mv /opt/gnu/bin/as /opt/gnu/bin/gas;
-mv /opt/gnu/bin/ld /opt/gnu/bin/gld'. You can add -v to the gcc command
-in the Makefile to see what GCC is using.
+using gcc <3.x then try not using GNU as or GNU ld on Solaris.
 
 ---
 If you get compiler warnings like "value computed is not used" and
 "unused variable" you can ignore them.
-
----
-For perl5.005 onwards: If you get "`thr' undeclared" compiler error,
-you'll need to add "dTHR;" to the top of the function that has the error.
 
 ---
 If you get compiler errors refering to Perl's own header files
diff -u DBI-1.14/ToDo DBI-1.30/ToDo
--- DBI-1.14/ToDo	Wed Jun 14 21:04:13 2000
+++ DBI-1.30/ToDo	Mon Jul 15 12:19:17 2002
@@ -1,15 +1,63 @@
 
-*** Assorted to-do items and random thoughts *** in no particular order ***
+*** Assorted to-do items and random thoughts *** IN NO PARTICULAR ORDER ***
+
+The DBI needs some standard way to handle the 'success with info' concept:
+	$h->{HandleEvent}
+
+Maybe have fetchrow_arrayref return not a new reference to the same
+array each time, but the same reference to the same array each time.
+That would save a new/mortal/free for each row. Need to consider
+safety in case application assigns to the ref etc. Could sanity
+check in get_fbav that SvRV still points to the row array.
+But maybe it's not worth it if perl's "tmp stealing" works for refs.
+
+Define install_method so apps using things like DBD::AnyData can
+avoid using func() so much. Especially as func is marked as 'keep error'
+so RaiseError doesn't work.
+
+DBI::Profile: add simple way to normalise the sql (convert constants
+to placeholders) so profiling is more effective for drivers/applications
+which don't use placeholders.
+
+Sort out DBIcDBISTATE() and DBIS mess
+
+fetchall_hashref: may be better to call fetchall_arrayref then convert to hashes
+
+Sponge behave_like - generalize?
+
+Update DBI::DBD with overview (at least) recommend Driver.xst strongly.
+Find XS drivers that don't use it and talk to authors.
+Ship separate DBD::TemplateP and DBD::TemplateXS drivers as examples.
+
+Check: local($h->{PrintError})=0; resets $DBI::errstr at end of scope?
+
+ShowTracePid attrib	( or just add to trace level change message)
+Add UnimpMethodHook (in XS)
+Test and integrate with DProf?
+FetchHashReuse attrib (=1 or ={}) copy from dbh to sth.
+Record attrib STOREs so can be replayed (or use subclass?)
+
+Merge/document  DBIx::AnyDBD functionality into the DBI
+
+make lasth return outer handle
+
+Add $h->swap_internal_handle($other_h)
+
+Add a C call to return boolean for is a number' for a given SV.
+Needs to do the right thing for a non-numeric string SV that's been
+tested in a numeric context (eg $a='S23'; foo() if $a==-1; $sth->execute($a))
+So if SvNVOK is true but the value is 0 then should also do looks_like_number()
+to be sure.
+
+Rename finish to discard_pending_rows :-) [or just add an alias and docs :-]
+Or maybe to 'close' (the ODBC equiv)
 
-begin_work
 add details of the DBI book to the docs
 test shell /connect user etc
 
 document dbi_fetchall_arrayref_attr attr of selectall_arrayref().
 
-passing \$sql or \"...sql..." to $dbh->select methods to mean use prepare_cached?
-
-Count execute()s (and do's?) (in ima struct?).
+Count execute()s (and do's?) (in ima struct? not thread safe).
 Reset count in commit/rollback.
 In $dbh->DESTROY don't issue warning if count == 0.
 
@@ -17,6 +65,17 @@
 change *.xst to treat eaither that or -2 as an error. (The -2 is
 a transition or old drivers.)
 
+ODBC 3.5 date and intervals types and subtypes (from unixODBC?)
+http://www.vpservices.com/jeff/programs/SQL/docs/odbc-getinfo-msdn.html
+
+Proxy: allow config to speficy SQL to allow/deny via regexen
+Docs for connect_cached and test with proxy.
+
+BINDING:
+
+Add to docs & tutorial re wrong bind type on a param may cause
+index to not be used! (Find real examples first)
+
 > And note that if you are using bind_param_inout as 'bind_param_by_ref',
 > then the $maxlen parameter is redundant.  I suspect all drivers could
 > implement bind_param_by_ref; most drivers, and specifically the Informix
@@ -27,50 +86,29 @@
 Okay. I'll take that as a vote for bind_param_by_ref as an alias for
 bind_param_inout. >>todo.
 
-bind_params($value1,\%attr1, $value2,\%attr2, ...); # _ref?
-
-selectall_hashref($sql, \%attr, [ $join, @fields ]);
-	# leave as key=>[@row] or key=>$value if only one left?
-	# include the keys in the value?
-
-Use PerlIO? Change fprintf's to PerlIO_printf's etc.
-
-Added refcount to dump method output (to trace ref leakage etc).
-
-ODBC 3.5 date and intervals types and subtypes (from unixODBC?)
-
-Docs for connect_cached and test with proxy.
-
-Proxy: allow config to speficy SQL to allow/deny via regexen
-
-Primary Key metadata
+LOGGING:
 
 Seperate log for sql statements?
 
-Get bind values from statement handle via an attribute.
-Useful for diagnostics. Also probably useful for pure-perl drivers.
-Also some people want to try to log sql statements with the placeholders
-'filled in'.
+Add per-handle debug file pointer (or at least a macro that _looks_ like that).
+Or, better yet, a function pointer so we can do more clever things.
+Maybe also need to add locking to prevent file contents being mangled
+by multiple threads/interpreters (esp mod_perl v2 and ActivePerl).
+
+OTHERS:
 
 Enable warnings in default_user().
-Add prompt for user/pass in default_user().
+Add prompt for user/pass in default_user()?
 
 Change bind_column to save the info for get_fbav to use when
-first called. Thus making bind before execute reliable.
+first called. Thus making bind before execute work for all drivers.
 
 Add a handle flag to say that the driver has a hash that maps error
 codes into SQLSTATE values. The error event mechanism could check for
 the flag and lookup the SQLSTATE value for the error from the hash.
 
 ODBC attribute defining if transactions are supported
-
-> I had to override
-> tables from DBD::_::db because it doesn't handle the quoting
-> conventions used by Informix databases.  Basically, table owners
-> are case sensitive in some databases, and therefore need to be
-> enclosed in double quotes (delimited identifier).
-: Needs attribute to say that a) double-quotes can be used
-: and b) that names are case sensitive.
+http://www.vpservices.com/jeff/programs/SQL/docs/odbc-getinfo-msdn.html
 
 $dbh-level __WARN__ and __DIE__. Initially just provide DBIXS hooks
 for DBD's to use (and pass h and imp_xxh to). Thus $dbh can redirect
@@ -78,18 +116,13 @@
 
 Informix inspired changes.
 
-Detect it in eval block for when PrintError and RaiseError are both set
-to avoid duplicated error msg.
-
 Add hook to DBI::DBD to write a myconfig.txt file into the
 source directory containing key driver and config info.
 
-attribute for 'last inserted id'
+attribute for 'last inserted id'?
 
 test suite
-
-$sth->{Database} and $dbh->{Driver} should hold the
-outer handle not the inner one.
+http://www.mipt.sw.ru/en/products/ots/
 
 $sth->more_results, now and $sth->become($sth2) later
 
@@ -100,73 +133,45 @@
 to &DBI::connect and &DBI::connect_cached
 in xs dbi_init code.
 
-Complete and test connect_cached, inc proxy.
-
 fetch_scroll handling via get_fbav.
 Also add:
 	getrow_array       (or getdata_?)
-	getrow_arrayref
-	getrow_hashref
+	getrow_arrayref?
+	getrow_hashref?
 
 test that $dbh->do will RaiseError
 RaiseError in %attr - doc it not working - hints.
 
-SQL_ACTIVE_STATEMENTS
-
 Consider making all SQL_ known to the DBI. Want to check for
 valid spelling. Also would like C<exists $h->{Attrib}> to
 work. Maybe auto load a module that defines a private hash
 that just serves to list known attributes.
 Or DBI->SQL_FOO and/or DBI::SQL_FOO.
 
-t/shell uninit var at 5.00502/Term/Cap.pm line 284.
-
 check out http://tegan.deltanet.com/~phlip/DBUIframe.html
 
-FAQ check 80 col max
-
-If trace log_where() says DBI.pm then use caller(1)?
-
 Auto proxying for individual drivers: DBI_PROXY_foo=...
 Else more generic dsn-rewrite via env vars
 
 Check DBD::Proxy connect&fetch latency (e.g. CGI use).
 
-Add TYPE, SCALE, PRECISION to DBD::Sponge
-
-$dbh->get_info?
-
-Bundle DBD::Xbase?
-Bundle Tie::DBI?
-Bundle others
-
-Add $dbh->{BusyCallback} = sub { ... } hook so drivers which support
-polling can allow other processing without complicating the logic.
-(But that won't allow callbacks into DBI code.)
-
-Add per-handle debug file pointer (or at least a macro that _looks_ like that).
-Or, better yet, a function pointer so we can do more clever things.
+Add SCALE to DBD::Sponge
 
 Rework handle creation for
 $h->clone ?, and maybe
 $dbh->connect([$user, $passwd])	# to reconnect, possibly as a different user
 
-generic dbd_preparse (for both C and perl)
 Add (ODBC) attribute to indicate if placeholders are supported.
+http://www.vpservices.com/jeff/programs/SQL/docs/odbc-getinfo-msdn.html
 
-SQL Parser
-  CPAN/authors/id/JAKE/perl5-byacc-patches-0.5.tar.gz
-
-Fix shared error codes between (dbh) handles
+Fix shared error codes between (dbh) handles, maybe.
 
 Add problem reporting info to DBI.pm
 
-Metadata! (DBI::Metadata?)
-
 Add 'break handling' when field values change? Use two fbav's so 'previous
-record' is available. Defind break fields and handlers. Call them via
+record' is available. Define break fields and handlers. Call them via
 an alternate fetch_with_break method.
-
+Jan 2002: Also now see DBIx::FetchLoop (Brendan Fagan)
 
 ---
 > > Is there any way to know whether a particular DBD needs a separate
diff -u DBI-1.14/dbi_sql.h DBI-1.30/dbi_sql.h
--- DBI-1.14/dbi_sql.h	Sun Jun 11 01:06:15 2000
+++ DBI-1.30/dbi_sql.h	Thu Feb  7 03:15:47 2002
@@ -1,4 +1,4 @@
-/* $Id: dbi_sql.h,v 10.3 2000/06/02 16:31:37 timbo Exp $
+/* $Id: dbi_sql.h,v 11.4 2002/02/07 03:00:53 timbo Exp $
  *
  * Copyright (c) 1997,1998,1999  Tim Bunce  England
  *
@@ -9,34 +9,65 @@
 /* Some core SQL CLI standard (ODBC) declarations		*/
 #ifndef SQL_SUCCESS	/* don't clash with ODBC based drivers	*/
 
-/* Standard SQL datatypes (ANSI/ODBC type numbering)		*/
-#define	SQL_ALL_TYPES		0
-#define	SQL_CHAR		1
-#define	SQL_NUMERIC		2
-#define	SQL_DECIMAL		3
-#define	SQL_INTEGER		4
-#define	SQL_SMALLINT		5
-#define	SQL_FLOAT		6
-#define	SQL_REAL		7
-#define	SQL_DOUBLE		8
-#define SQL_DATE		9	/* SQL_DATETIME in CLI!	*/
-#define SQL_TIME		10
-#define SQL_TIMESTAMP		11
-#define	SQL_VARCHAR		12
-
-/* Other SQL datatypes (ODBC type numbering)			*/
-#define SQL_LONGVARCHAR		(-1)
-#define SQL_BINARY		(-2)
-#define SQL_VARBINARY		(-3)
-#define SQL_LONGVARBINARY	(-4)
-#define SQL_BIGINT		(-5)	/* too big for IV	*/
-#define SQL_TINYINT		(-6)
-
-/* Support for Unicode and SQL92 */
-#define SQL_BIT                 (-7)
-#define SQL_WCHAR               (-8)
-#define SQL_WVARCHAR            (-9)
-#define SQL_WLONGVARCHAR        (-10)
+/* SQL datatype codes */
+#define SQL_GUID                         (-11)
+#define SQL_WLONGVARCHAR                 (-10)
+#define SQL_WVARCHAR                      (-9)
+#define SQL_WCHAR                         (-8)
+#define SQL_BIT                           (-7)
+#define SQL_TINYINT                       (-6)
+#define SQL_BIGINT                        (-5)
+#define SQL_LONGVARBINARY                 (-4)
+#define SQL_VARBINARY                     (-3)
+#define SQL_BINARY                        (-2)
+#define SQL_LONGVARCHAR                   (-1)
+#define SQL_UNKNOWN_TYPE                    0
+#define SQL_ALL_TYPES                       0
+#define SQL_CHAR                            1
+#define SQL_NUMERIC                         2
+#define SQL_DECIMAL                         3
+#define SQL_INTEGER                         4
+#define SQL_SMALLINT                        5
+#define SQL_FLOAT                           6
+#define SQL_REAL                            7
+#define SQL_DOUBLE                          8
+#define SQL_DATETIME                        9
+#define SQL_DATE                            9
+#define SQL_INTERVAL                       10
+#define SQL_TIME                           10
+#define SQL_TIMESTAMP                      11
+#define SQL_VARCHAR                        12
+#define SQL_BOOLEAN                        16
+#define SQL_UDT                            17
+#define SQL_UDT_LOCATOR                    18
+#define SQL_ROW                            19
+#define SQL_REF                            20
+#define SQL_BLOB                           30
+#define SQL_BLOB_LOCATOR                   31
+#define SQL_CLOB                           40
+#define SQL_CLOB_LOCATOR                   41
+#define SQL_ARRAY                          50
+#define SQL_ARRAY_LOCATOR                  51
+#define SQL_MULTISET                       55
+#define SQL_MULTISET_LOCATOR               56
+#define SQL_TYPE_DATE                      91
+#define SQL_TYPE_TIME                      92
+#define SQL_TYPE_TIMESTAMP                 93
+#define SQL_TYPE_TIME_WITH_TIMEZONE        94
+#define SQL_TYPE_TIMESTAMP_WITH_TIMEZONE   95
+#define SQL_INTERVAL_YEAR                 101
+#define SQL_INTERVAL_MONTH                102
+#define SQL_INTERVAL_DAY                  103
+#define SQL_INTERVAL_HOUR                 104
+#define SQL_INTERVAL_MINUTE               105
+#define SQL_INTERVAL_SECOND               106
+#define SQL_INTERVAL_YEAR_TO_MONTH        107
+#define SQL_INTERVAL_DAY_TO_HOUR          108
+#define SQL_INTERVAL_DAY_TO_MINUTE        109
+#define SQL_INTERVAL_DAY_TO_SECOND        110
+#define SQL_INTERVAL_HOUR_TO_MINUTE       111
+#define SQL_INTERVAL_HOUR_TO_SECOND       112
+#define SQL_INTERVAL_MINUTE_TO_SECOND     113
 
 
 /* Main return codes						*/
diff -u DBI-1.14/dbipport.h DBI-1.30/dbipport.h
--- DBI-1.14/dbipport.h	Sun Jun 11 03:27:45 2000
+++ DBI-1.30/dbipport.h	Mon Jul 15 12:19:17 2002
@@ -1,57 +1,81 @@
 
-#ifndef _P_P_PORTABILITY_H_
-#define _P_P_PORTABILITY_H_
-
-/* Perl/Pollution/Portability Version 1.0007 */
-
-/* Copyright (C) 1999, Kenneth Albanowski. This code may be used and
-   distributed under the same license as any version of Perl. */
-   
-/* For the latest version of this code, please retreive the Devel::PPPort
-   module from CPAN, contact the author at <kjahds@kjahds.com>, or check
-   with the Perl maintainers. */
-   
-/* If you needed to customize this file for your project, please mention
-   your changes, and visible alter the version number. */
-
+/* ppport.h -- Perl/Pollution/Portability Version 2.0002 
+ *
+ * Automatically Created by Devel::PPPort on Fri Jul 12 11:55:34 2002 
+ *
+ * Do NOT edit this file directly! -- Edit PPPort.pm instead.
+ *
+ * Version 2.x, Copyright (C) 2001, Paul Marquess.
+ * Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
+ * This code may be used and distributed under the same license as any
+ * version of Perl.
+ * 
+ * This version of ppport.h is designed to support operation with Perl
+ * installations back to 5.004, and has been tested up to 5.8.0.
+ *
+ * If this version of ppport.h is failing during the compilation of this
+ * module, please check if a newer version of Devel::PPPort is available
+ * on CPAN before sending a bug report.
+ *
+ * If you are using the latest version of Devel::PPPort and it is failing
+ * during compilation of this module, please send a report to perlbug@perl.com
+ *
+ * Include all following information:
+ *
+ *  1. The complete output from running "perl -V"
+ *
+ *  2. This file.
+ *
+ *  3. The name & version of the module you were trying to build.
+ *
+ *  4. A full log of the build that failed.
+ *
+ *  5. Any other information that you think could be relevant.
+ *
+ *
+ * For the latest version of this code, please retreive the Devel::PPPort
+ * module from CPAN.
+ * 
+ */
 
 /*
-   In order for a Perl extension module to be as portable as possible
-   across differing versions of Perl itself, certain steps need to be taken.
-   Including this header is the first major one, then using dTHR is all the
-   appropriate places and using a PL_ prefix to refer to global Perl
-   variables is the second.
-*/
+ * In order for a Perl extension module to be as portable as possible
+ * across differing versions of Perl itself, certain steps need to be taken.
+ * Including this header is the first major one, then using dTHR is all the
+ * appropriate places and using a PL_ prefix to refer to global Perl
+ * variables is the second.
+ *
+ */
 
 
 /* If you use one of a few functions that were not present in earlier
-   versions of Perl, please add a define before the inclusion of ppport.h
-   for a static include, or use the GLOBAL request in a single module to
-   produce a global definition that can be referenced from the other
-   modules.
-   
-   Function:            Static define:           Extern define:
-   newCONSTSUB()        NEED_newCONSTSUB         NEED_newCONSTSUB_GLOBAL
-
-*/
+ * versions of Perl, please add a define before the inclusion of ppport.h
+ * for a static include, or use the GLOBAL request in a single module to
+ * produce a global definition that can be referenced from the other
+ * modules.
+ * 
+ * Function:            Static define:           Extern define:
+ * newCONSTSUB()        NEED_newCONSTSUB         NEED_newCONSTSUB_GLOBAL
+ *
+ */
  
 
 /* To verify whether ppport.h is needed for your module, and whether any
-   special defines should be used, ppport.h can be run through Perl to check
-   your source code. Simply say:
-   
-   	perl -x ppport.h *.c *.h *.xs foo/*.c [etc]
-   
-   The result will be a list of patches suggesting changes that should at
-   least be acceptable, if not necessarily the most efficient solution, or a
-   fix for all possible problems. It won't catch where dTHR is needed, and
-   doesn't attempt to account for global macro or function definitions,
-   nested includes, typemaps, etc.
-   
-   In order to test for the need of dTHR, please try your module under a
-   recent version of Perl that has threading compiled-in.
- 
-*/ 
+ * special defines should be used, ppport.h can be run through Perl to check
+ * your source code. Simply say:
+ * 
+ * 	perl -x ppport.h *.c *.h *.xs foo/bar*.c [etc]
+ * 
+ * The result will be a list of patches suggesting changes that should at
+ * least be acceptable, if not necessarily the most efficient solution, or a
+ * fix for all possible problems. It won't catch where dTHR is needed, and
+ * doesn't attempt to account for global macro or function definitions,
+ * nested includes, typemaps, etc.
+ * 
+ * In order to test for the need of dTHR, please try your module under a
+ * recent version of Perl that has threading compiled-in.
+ *
+ */ 
 
 
 /*
@@ -141,6 +165,9 @@
 __DATA__
 */
 
+#ifndef _P_P_PORTABILITY_H_
+#define _P_P_PORTABILITY_H_
+
 #ifndef PERL_REVISION
 #   ifndef __PATCHLEVEL_H_INCLUDED__
 #       include "patchlevel.h"
@@ -157,33 +184,109 @@
 
 #define PERL_BCDVERSION ((PERL_REVISION * 0x1000000L) + (PERL_VERSION * 0x1000L) + PERL_SUBVERSION)
 
+/* It is very unlikely that anyone will try to use this with Perl 6 
+   (or greater), but who knows.
+ */
+#if PERL_REVISION != 5
+#	error ppport.h only works with Perl version 5
+#endif /* PERL_REVISION != 5 */
+
 #ifndef ERRSV
 #	define ERRSV perl_get_sv("@",FALSE)
 #endif
 
 #if (PERL_VERSION < 4) || ((PERL_VERSION == 4) && (PERL_SUBVERSION <= 5))
 /* Replace: 1 */
-#	define PL_sv_undef	sv_undef
-#	define PL_sv_yes	sv_yes
-#	define PL_sv_no		sv_no
-#	define PL_na		na
-#	define PL_stdingv	stdingv
-#	define PL_hints		hints
+#	define PL_Sv		Sv
+#	define PL_compiling	compiling
+#	define PL_copline	copline
 #	define PL_curcop	curcop
 #	define PL_curstash	curstash
-#	define PL_copline	copline
-#	define PL_Sv		Sv
+#	define PL_defgv		defgv
+#	define PL_dirty		dirty
+#	define PL_dowarn	dowarn
+#	define PL_hints		hints
+#	define PL_na		na
+#	define PL_perldb	perldb
+#	define PL_rsfp_filters	rsfp_filters
+#	define PL_rsfpv		rsfp
+#	define PL_stdingv	stdingv
+#	define PL_sv_no		sv_no
+#	define PL_sv_undef	sv_undef
+#	define PL_sv_yes	sv_yes
 /* Replace: 0 */
 #endif
 
+#ifdef HASATTRIBUTE
+#  if defined(__GNUC__) && defined(__cplusplus)
+#    define PERL_UNUSED_DECL
+#  else
+#    define PERL_UNUSED_DECL __attribute__((unused))
+#  endif
+#else
+#  define PERL_UNUSED_DECL
+#endif
+
+#ifndef dNOOP
+#  define NOOP (void)0
+#  define dNOOP extern int Perl___notused PERL_UNUSED_DECL
+#endif
+
 #ifndef dTHR
-#  ifdef WIN32
-#	define dTHR extern int Perl___notused
+#  define dTHR          dNOOP
+#endif
+
+#ifndef dTHX
+#  define dTHX          dNOOP
+#  define dTHXa(x)      dNOOP
+#  define dTHXoa(x)     dNOOP
+#endif
+
+#ifndef pTHX
+#    define pTHX	void
+#    define pTHX_
+#    define aTHX
+#    define aTHX_
+#endif         
+
+#ifndef UVSIZE
+#   define UVSIZE IVSIZE
+#endif
+
+#ifndef NVTYPE
+#   if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE)
+#       define NVTYPE long double
+#   else
+#       define NVTYPE double
+#   endif
+typedef NVTYPE NV;
+#endif
+
+#ifndef INT2PTR
+
+#if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE)
+#  define PTRV                  UV
+#  define INT2PTR(any,d)        (any)(d)
+#else
+#  if PTRSIZE == LONGSIZE
+#    define PTRV                unsigned long
 #  else
-#	define dTHR extern int errno
+#    define PTRV                unsigned
 #  endif
+#  define INT2PTR(any,d)        (any)(PTRV)(d)
+#endif
+#define NUM2PTR(any,d)  (any)(PTRV)(d)
+#define PTR2IV(p)       INT2PTR(IV,p)
+#define PTR2UV(p)       INT2PTR(UV,p)
+#define PTR2NV(p)       NUM2PTR(NV,p)
+#if PTRSIZE == LONGSIZE
+#  define PTR2ul(p)     (unsigned long)(p)
+#else
+#  define PTR2ul(p)     INT2PTR(unsigned long,p)        
 #endif
 
+#endif /* !INT2PTR */
+
 #ifndef boolSV
 #	define boolSV(b) ((b) ? &PL_sv_yes : &PL_sv_no)
 #endif
@@ -202,6 +305,15 @@
 /* Replace: 0 */
 #endif
 
+/* DEFSV appears first in 5.004_56 */
+#ifndef DEFSV
+#  define DEFSV	GvSV(PL_defgv)
+#endif
+
+#ifndef SAVE_DEFSV
+#    define SAVE_DEFSV SAVESPTR(GvSV(PL_defgv))
+#endif
+
 #ifndef newRV_noinc
 #  ifdef __GNUC__
 #    define newRV_noinc(sv)               \
@@ -211,7 +323,7 @@
           nsv;                            \
       })
 #  else
-#    if defined(CRIPPLED_CC) || defined(USE_THREADS)
+#    if defined(USE_THREADS)
 static SV * newRV_noinc (SV * sv)
 {
           SV *nsv = (SV*)newRV(sv);       
@@ -220,7 +332,7 @@
 }
 #    else
 #      define newRV_noinc(sv)    \
-        ((PL_Sv=(SV*)newRV(sv), SvREFCNT_dec(sv), (SV*)PL_Sv)
+        (PL_Sv=(SV*)newRV(sv), SvREFCNT_dec(sv), (SV*)PL_Sv)
 #    endif
 #  endif
 #endif
@@ -233,7 +345,7 @@
 #if defined(NEED_newCONSTSUB)
 static
 #else
-extern void newCONSTSUB _((HV * stash, char * name, SV *sv));
+extern void newCONSTSUB(HV * stash, char * name, SV *sv);
 #endif
 
 #if defined(NEED_newCONSTSUB) || defined(NEED_newCONSTSUB_GLOBAL)
@@ -282,5 +394,147 @@
 
 #endif /* newCONSTSUB */
 
+#ifndef START_MY_CXT
+
+/*
+ * Boilerplate macros for initializing and accessing interpreter-local
+ * data from C.  All statics in extensions should be reworked to use
+ * this, if you want to make the extension thread-safe.  See ext/re/re.xs
+ * for an example of the use of these macros.
+ *
+ * Code that uses these macros is responsible for the following:
+ * 1. #define MY_CXT_KEY to a unique string, e.g. "DynaLoader_guts"
+ * 2. Declare a typedef named my_cxt_t that is a structure that contains
+ *    all the data that needs to be interpreter-local.
+ * 3. Use the START_MY_CXT macro after the declaration of my_cxt_t.
+ * 4. Use the MY_CXT_INIT macro such that it is called exactly once
+ *    (typically put in the BOOT: section).
+ * 5. Use the members of the my_cxt_t structure everywhere as
+ *    MY_CXT.member.
+ * 6. Use the dMY_CXT macro (a declaration) in all the functions that
+ *    access MY_CXT.
+ */
+
+#if defined(MULTIPLICITY) || defined(PERL_OBJECT) || \
+    defined(PERL_CAPI)    || defined(PERL_IMPLICIT_CONTEXT)
+
+/* This must appear in all extensions that define a my_cxt_t structure,
+ * right after the definition (i.e. at file scope).  The non-threads
+ * case below uses it to declare the data as static. */
+#define START_MY_CXT
+
+#if (PERL_VERSION < 4 || (PERL_VERSION == 4 && PERL_SUBVERSION < 68 ))
+/* Fetches the SV that keeps the per-interpreter data. */
+#define dMY_CXT_SV \
+	SV *my_cxt_sv = perl_get_sv(MY_CXT_KEY, FALSE)
+#else /* >= perl5.004_68 */
+#define dMY_CXT_SV \
+	SV *my_cxt_sv = *hv_fetch(PL_modglobal, MY_CXT_KEY,		\
+				  sizeof(MY_CXT_KEY)-1, TRUE)
+#endif /* < perl5.004_68 */
+
+/* This declaration should be used within all functions that use the
+ * interpreter-local data. */
+#define dMY_CXT	\
+	dMY_CXT_SV;							\
+	my_cxt_t *my_cxtp = INT2PTR(my_cxt_t*,SvUV(my_cxt_sv))
+
+/* Creates and zeroes the per-interpreter data.
+ * (We allocate my_cxtp in a Perl SV so that it will be released when
+ * the interpreter goes away.) */
+#define MY_CXT_INIT \
+	dMY_CXT_SV;							\
+	/* newSV() allocates one more than needed */			\
+	my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\
+	Zero(my_cxtp, 1, my_cxt_t);					\
+	sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
+
+/* This macro must be used to access members of the my_cxt_t structure.
+ * e.g. MYCXT.some_data */
+#define MY_CXT		(*my_cxtp)
+
+/* Judicious use of these macros can reduce the number of times dMY_CXT
+ * is used.  Use is similar to pTHX, aTHX etc. */
+#define pMY_CXT		my_cxt_t *my_cxtp
+#define pMY_CXT_	pMY_CXT,
+#define _pMY_CXT	,pMY_CXT
+#define aMY_CXT		my_cxtp
+#define aMY_CXT_	aMY_CXT,
+#define _aMY_CXT	,aMY_CXT
+
+#else /* single interpreter */
+
+
+#define START_MY_CXT	static my_cxt_t my_cxt;
+#define dMY_CXT_SV	dNOOP
+#define dMY_CXT		dNOOP
+#define MY_CXT_INIT	NOOP
+#define MY_CXT		my_cxt
+
+#define pMY_CXT		void
+#define pMY_CXT_
+#define _pMY_CXT
+#define aMY_CXT
+#define aMY_CXT_
+#define _aMY_CXT
+
+#endif 
+
+#endif /* START_MY_CXT */
+
+#ifndef IVdf
+#  if IVSIZE == LONGSIZE
+#       define	IVdf		"ld"
+#       define	UVuf		"lu"
+#       define	UVof		"lo"
+#       define	UVxf		"lx"
+#       define	UVXf		"lX"
+#   else
+#       if IVSIZE == INTSIZE
+#           define	IVdf	"d"
+#           define	UVuf	"u"
+#           define	UVof	"o"
+#           define	UVxf	"x"
+#           define	UVXf	"X"
+#       endif
+#   endif
+#endif
+
+#ifndef NVef
+#   if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) && \
+	defined(PERL_PRIfldbl) /* Not very likely, but let's try anyway. */ 
+#       define NVef		PERL_PRIeldbl
+#       define NVff		PERL_PRIfldbl
+#       define NVgf		PERL_PRIgldbl
+#   else
+#       define NVef		"e"
+#       define NVff		"f"
+#       define NVgf		"g"
+#   endif
+#endif
+
+#ifndef AvFILLp			/* Older perls (<=5.003) lack AvFILLp */
+#   define AvFILLp AvFILL
+#endif
+
+#ifdef SvPVbyte
+#   if PERL_REVISION == 5 && PERL_VERSION < 7
+       /* SvPVbyte does not work in perl-5.6.1, borrowed version for 5.7.3 */
+#       undef SvPVbyte
+#       define SvPVbyte(sv, lp) \
+          ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \
+           ? ((lp = SvCUR(sv)), SvPVX(sv)) : my_sv_2pvbyte(aTHX_ sv, &lp))
+       static char *
+       my_sv_2pvbyte(pTHX_ register SV *sv, STRLEN *lp)
+       {   
+           sv_utf8_downgrade(sv,0);
+           return SvPV(sv,*lp);
+       }
+#   endif
+#else
+#   define SvPVbyte SvPV
+#endif
 
 #endif /* _P_P_PORTABILITY_H_ */
+
+/* End of File ppport.h */
diff -u DBI-1.14/dbiproxy.PL DBI-1.30/dbiproxy.PL
--- DBI-1.14/dbiproxy.PL	Sat May 15 01:43:59 1999
+++ DBI-1.30/dbiproxy.PL	Sat May 25 18:36:26 2002
@@ -7,6 +7,9 @@
 
 use strict;
 
+my $arg_test    = shift(@ARGV)		if $ARGV[0] eq '--test';
+$ENV{DBI_TRACE} = shift(@ARGV) || 2	if $ARGV[0] =~ s/^--dbitrace=?//;
+
 require DBI::ProxyServer;
 
 # XXX these should probably be moved into DBI::ProxyServer
@@ -15,13 +18,13 @@
 delete $ENV{ENV};
 delete $ENV{BASH_ENV};
 
-if ($ARGV[0] eq '--test') {
+if ($arg_test) {
     require RPC::PlServer::Test;
-    shift @ARGV;
     @DBI::ProxyServer::ISA = qw(RPC::PlServer::Test DBI);
 }
 
 DBI::ProxyServer::main(@ARGV);
+
 exit(0);
 
 
Common subdirectories: DBI-1.14/lib and DBI-1.30/lib
Common subdirectories: DBI-1.14/t and DBI-1.30/t
diff -u DBI-1.14/test.pl DBI-1.30/test.pl
--- DBI-1.14/test.pl	Sun Jun 11 01:06:14 2000
+++ DBI-1.30/test.pl	Wed Jun  5 04:27:17 2002
@@ -1,6 +1,6 @@
 #!/usr/local/bin/perl -w
 
-# $Id: test.pl,v 10.4 2000/06/02 16:31:37 timbo Exp $
+# $Id: test.pl,v 11.5 2002/06/05 03:26:39 timbo Exp $
 #
 # Copyright (c) 1994-1998 Tim Bunce
 #
@@ -14,12 +14,12 @@
 
 BEGIN {
     print "$0 @ARGV\n";
-    print q{DBI test application $Revision: 10.4 $}."\n";
+    print q{DBI test application $Revision: 11.5 $}."\n";
     $| = 1;
-    eval "require blib; import blib;";	# wasn't in 5.003, hence the eval
-	warn $@ if $@;
 }
 
+use blib;
+
 use DBI;
 
 use DBI::DBD;	# simple test to make sure it's okay
@@ -53,22 +53,33 @@
 print "Available Drivers: ",join(", ",DBI->available_drivers(1)),"\n";
 
 
-my $dbh = DBI->connect('', '', '', $driver);
+my $dbh = DBI->connect('', '', '', $driver); # old-style connect syntax
 $dbh->debug($::opt_h);
 
-if (0) {	# only works after 5.004_04
-    my $h = DBI->connect('dbi:NullP:','','');
-    $h->trace(3);
-    {
+if (0) {
+    DBI->trace(3);
+    my $h = DBI->connect('dbi:NullP:','','', { RootClass=>'MyTestDBI', DbTypeSubclass=>'foo, bar' });
+    DBI->trace(0);
+    { # only works after 5.004_04:
 	warn "RaiseError= '$h->{RaiseError}' (pre local)\n";
 	local($h->{RaiseError});# = undef;
 	warn "RaiseError= '$h->{RaiseError}' (post local)\n";
     }
     warn "RaiseError= '$h->{RaiseError}' (post local block)\n";
+    exit 1;
 }
 
 if ($::opt_m) {
-    mem_test($dbh) while 1;
+    #$dbh->trace(9);
+    my $level = 4;
+    my $cnt = 10000;
+    print "Using $driver, same dbh...\n";
+    #for (my $i=0; $i<$cnt; ++$i) { mem_test($dbh, undef, $level) }
+    print "Using NullP, reconnecting each time...\n";
+    #for (my $i=0; $i<$cnt; ++$i) { mem_test(undef, ['dbi:NullP:'], $level) }
+    print "Using ExampleP, reconnecting each time...\n";
+    mem_test(undef, ['dbi:ExampleP:'], $level) while 1;
+    #mem_test(undef, ['dbi:mysql:VC'], $level, "select * from campaigns where length(?)>0") while 1;
 }
 elsif ($::opt_t) {
 	thread_test();
@@ -84,16 +95,28 @@
     print "Testing handle creation speed...\n";
     my $null_dbh = DBI->connect('dbi:NullP:','','');
     my $null_sth = $null_dbh->prepare('');	# create one to warm up
-    $count = 5000;
+    $count = 10_000;
     my $i = $count;
     my $t1 = new Benchmark;
     $null_dbh->prepare('') while $i--;
     my $td = Benchmark::timediff(Benchmark->new, $t1);
     my $tds= Benchmark::timestr($td);
-    my $dur = $td->cpu_a;
+    my $dur = $td->cpu_a || (1/$count); # fudge if cpu_a==0
     printf "$count NullP statement handles cycled in %.1f cpu+sys seconds (%d per sec)\n\n",
 	    $dur, $count / $dur;
 
+  if (0) {
+    $null_dbh = DBI->connect('dbi:mysql:VC_log','','',{RaiseError=>1});
+    $null_sth = $null_dbh->prepare('select * from big');
+    $null_sth->execute();
+    $t1 = new Benchmark;
+    1 while ($null_sth->fetchrow_hashref());
+    #1 while ($null_sth->fetchrow_arrayref());
+    $td = Benchmark::timediff(Benchmark->new, $t1);
+    $tds= Benchmark::timestr($td);
+    $dur = $td->cpu_a;
+    printf "$DBI::rows in $tds\n";
+  }
 }
 
 #DBI->trace(4);
@@ -102,40 +125,45 @@
 
 
 sub mem_test {	# harness to help find basic leaks
-    my($dbh) = @_;
-	system("echo $count; $ps$$") if (($count++ % 1000) == 0);
-    my $cursor_a = $dbh->prepare("select mode,ino,name from ?");
-    $cursor_a->execute('/usr');
-    my @row_a = $cursor_a->fetchrow;
-    $cursor_a->finish;
+    my ($orig_dbh, $connect, $level, $select, $params) = @_;
+    $select ||= "select mode,ino,name from ?";
+    $params ||= [ '.' ];
+    my $dbh = $orig_dbh || DBI->connect(@$connect);
+    system("echo $count; $ps$$") if (($count++ % 500) == 0);
+    my $cursor_a;
+    $cursor_a = $dbh->prepare($select)		if $level >= 2;
+    $cursor_a->execute(@$params)		if $level >= 3;
+    my $rows = $cursor_a->fetchall_arrayref({})	if $level >= 4;
+    $cursor_a->finish if $cursor_a && $cursor_a->{Active};
+    $dbh->disconnect unless $orig_dbh;
 }
 
 
 sub thread_test {
-	require Thread;
-	my $dbh = DBI->connect("dbi:ExampleP:.", "", "") || die $DBI::err;
-	#$dbh->trace(4);
-	my @t;
-	print "Starting $::opt_t threads:\n";
-	foreach(1..$::opt_t) {
-		print "$_\n";
-		push @t, Thread->new(\&thread_test_loop, $dbh, $::opt_n||99);
-	}
-	print "Small sleep to allow threads to progress\n";
-	sleep 2;
-	print "Joining threads:\n";
-	foreach(@t) {
-		print "$_\n";
-		$_->join
-	}
+    require Thread;
+    my $dbh = DBI->connect("dbi:ExampleP:.", "", "") || die $DBI::err;
+    #$dbh->trace(4);
+    my @t;
+    print "Starting $::opt_t threads:\n";
+    foreach(1..$::opt_t) {
+	print "$_\n";
+	push @t, Thread->new(\&thread_test_loop, $dbh, $::opt_n||99);
+    }
+    print "Small sleep to allow threads to progress\n";
+    sleep 2;
+    print "Joining threads:\n";
+    foreach(@t) {
+	print "$_\n";
+	$_->join
+    }
 }
 
 sub thread_test_loop {
-	my $dbh = shift;
-	my $i = shift || 10;
+    my $dbh = shift;
+    my $i = shift || 10;
     while($i-- > 0) {
-		$dbh->selectall_arrayref("select * from ?", undef, ".");
-	}
+	$dbh->selectall_arrayref("select * from ?", undef, ".");
+    }
 }
 
 # end.
