Author: timbo
Date: Sat Mar  6 15:45:45 2010
New Revision: 13836

Modified:
   dbi/trunk/Changes
   dbi/trunk/DBI.pm

Log:
Edited new docs and extended them slightly.


Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes   (original)
+++ dbi/trunk/Changes   Sat Mar  6 15:45:45 2010
@@ -35,7 +35,7 @@
   Updated DBI::Profile and DBD::File docs to fix pod nits
     thanks to Frank Wiegand.
   Corrected typos in Gopher documentation reported by Jan Krynicky.
-  Documented the Callbacks attribute.
+  Documented the Callbacks attribute thanks to David E. Wheeler.
 
   Updated dbipport.h to Devel::PPPort 3.19 (H.Merijn Brand)
 

Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm    (original)
+++ dbi/trunk/DBI.pm    Sat Mar  6 15:45:45 2010
@@ -2749,7 +2749,7 @@
 The cached database handle is replaced with a new connection if it
 has been disconnected or if the C<ping> method fails.
 
-That the behaviour of this method differs in several respects from the
+Note that the behaviour of this method differs in several respects from the
 behaviour of persistent connections implemented by Apache::DBI.
 However, if Apache::DBI is loaded then C<connect_cached> will use it.
 
@@ -2757,7 +2757,9 @@
 also cause problems, such as too many connections, and so should
 be used with care. In particular, avoid changing the attributes of
 a database handle created via connect_cached() because it will affect
-other code that may be using the same handle.
+other code that may be using the same handle. When connect_cached()
+returns a handle the attributes will be reset to their initial values.
+This can cause problems, especially with the C<AutoCommit> attribute.
 
 Where multiple separate parts of a program are using connect_cached()
 to connect to the same database with the same (initial) attributes
@@ -4020,43 +4022,44 @@
 
   Preparing q{SELECT 1}
 
-The cool thing is that, because callbacks are executed before the methods
+Because callbacks are executed I<before> the methods
 they're associated with, you can modify the arguments before they're passed on
 to the method call. For example, to make sure that all calls to C<prepare()>
 are immediately prepared by L<DBD::Pg>, add a callback that makes sure that
-C<pg_prepare_now> is always set:
+the C<pg_prepare_now> attribute is always set:
 
   my $dbh = DBI->connect($dsn, $username, $auth, {
       Callbacks => {
           prepare => sub {
-              if ($_[2]) {
-                  $_[2]->{pg_prepare_now} = 1;
-              } else {
-                  $_[2] = { pg_prepare_now => 1 };
-              }
-              return;
+              $_[2] ||= {};
+              $_[2]->{pg_prepare_now} = 1;
+              return; # must return nothing
           },
       }
   });
 
-Note that we've created the attributes hash if it's not passed to the
-C<prepare>.
+Note that we are editing the contents of C<@_> directly. In this case we've
+created the attributes hash if it's not passed to the C<prepare> call.
 
 You can also prevent the associated method from ever executing. While a
-callback executes, C<$_> holds the method name. To prevent the method from
+callback executes, C<$_> holds the method name. (This allows multiple callbacks
+to share the same code reference and still know what method was called.)
+To prevent the method from 
 executing, simply C<undef $_>. For example, if you wanted to disable calls to
 C<ping()>, you could do this:
 
   $dbh->{Callbacks} = {
       ping => sub {
-          undef $_;   # tell dispatch to not call the method
+          # tell dispatch to not call the method:
+          undef $_;
+          # return this value instead:
           return "42 bells";
       }
   };
 
 As with other attributes, Callbacks can be specified on a handle or via the
 attributes to C<connect()>. Callbacks can also be applied to a statement
-methods on a statement handle:
+methods on a statement handle. For example:
 
   $sth->{Callbacks} = {
       execute => sub {
@@ -4064,14 +4067,26 @@
       }
   };
 
-In addition to the all of the DBI methods, the C<Callbacks> hash reference
-supports three additional keys. The first is the C<ChildCallbacks> keys, which
-allows one to define callbacks for all statement handles created from a
+The C<Callbacks> attribute of a database handle isn't copied to any statement
+handles it creates. So setting callbacks for a statement handle requires you to
+set the C<Callbacks> attribute on the statement handle yourself, as in the
+example above, or use the special C<ChildCallbacks> key described below.
+
+B<Special Keys in Callbacks Attribute>
+
+In addition to DBI handle method names, the C<Callbacks> hash reference
+supports three additional keys.
+
+The first is the C<ChildCallbacks> key. When a statement handle is created from
+a database handle the C<ChildCallbacks> key of the database handle's
+C<Callbacks> attribute, if any, becomes the new C<Callbacks> attribute of the
+statement handle.
+This allows you to define callbacks for all statement handles created from a
 database handle. For example, if you wanted to count how many times C<execute>
 was called in your application, you could write:
 
   my $exec_count = 0;
-  my $dbh = DBI->connect('dbi:Pg:dbname=bric', '', '', {
+  my $dbh = DBI->connect( $dsn, $username, $auth, {
       Callbacks => {
           ChildCallbacks => {
               execute => sub { $exec_count++; return; }
@@ -4080,17 +4095,28 @@
   });
 
   END {
-      print "Execute was called $exec_count times\n";
+      print "The execute method was called $exec_count times\n";
   }
 
 The other two special keys are C<connect_cached.new> and
 C<connect_cached.reused>. These keys define callbacks that are called when
 C<connect_cached()> is called, but allow different behaviors depending on
-whether a new handle is created or a handle is returned. For example, if your
-app uses C<connect_cached()> in a utility method, it might be prudent to
-prevent it from interfering with open transactions. To prevent a cached handle
-from having its transactions committed before it's returned, simply eliminate
-the C<AutoCommit> attribute in a C<connect_cached.reused> callback, like so:
+whether a new handle is created or a handle is returned. The callback is
+invoked with these arguments: C<$dbh, $dsn, $user, $auth, $attr>.
+
+For example, some applications uses C<connect_cached()> to connect with
+C<AutoCommit> enabled and then disable C<AutoCommit> temporarily for
+transactions. If C<connect_cached()> is called during a transaction, perhaps in
+a utility method, then it might select the same cached handle and then force
+C<AutoCommit> on, forcing a commit of the transaction. See the 
L</connect_cached>
+documentation for one way to deal with that. Here we'll describe an alternative
+approach using a callback.
+
+Because the C<connect_cached.*> callbacks are invoked before connect_cached()
+has applied the connect attributes you can use a callback to edit the 
attributes
+that will be applied.  To prevent a cached handle from having its transactions
+committed before it's returned, you can eliminate the C<AutoCommit> attribute
+in a C<connect_cached.reused> callback, like so:
 
   my $cb = {
       ‘connect_cached.reused’ => sub { delete $_[4]->{AutoCommit} },
@@ -4098,7 +4124,7 @@
 
   sub dbh {
       my $self = shift;
-      DBI->connect_cached( $dsn $username $auth, {
+      DBI->connect_cached( $dsn, $username, $auth, {
           PrintError => 0,
           RaiseError => 1,
           AutoCommit => 1,
@@ -4110,8 +4136,12 @@
 enabled, while cached database handles are left in whatever transaction state
 they happened to be in when retrieved from the cache.
 
-One more useful hint: applying callbacks to the C<connected()> method. This
-method is a no-op (unless you subclass the DBI and change it), but you can
+A more common application for callbacks is setting connection state only when a
+new connection is made (by connect() or connect_cached()). Adding a callback to
+the L</connected> method makes this easy.
+This method is a no-op by default (unless you subclass the DBI and change it).
+The DBI calls it to indicate that a new connection has been made and the 
connection
+attributes have all been set.  You can
 give it a bit of added functionality by applying a callback to it. For
 example, to make sure that MySQL understands your application's ANSI-compliant
 SQL, set it up like so:
@@ -4119,18 +4149,32 @@
   my $dbh = DBI->connect($dsn, $username, $auth, {
       Callbacks => {
           connected => sub {
-              my $dbh = shift;
-              $dbh->do(q{
+              shift->do(q{
                   SET SESSION 
sql_mode='ansi,strict_trans_tables,no_auto_value_on_zero';
-              }) unless exists $dbh->{private_myapp_sql_mode};
-              $dbh->{private_myapp_sql_mode} = 1;
+              });
               return;
           },
       }
   });
 
-The setting of C<< $dbh->{private_myapp_sql_mode} >> ensures that the
-configuration code is called only once.
+One significant limitation with callbacks is that there can only be one per
+method per handle. This means it's easy for one use of callbacks to interfere
+with, or typically simply overwrite, another use of callbacks. For this reason
+modules using callbacks should document the fact clearly so application authors
+can tell if use of callbacks by the module will clash with use of callbacks by
+the application.
+
+You might be able to work around this issue by taking a copy of the original
+callback and calling it within your own. For example:
+
+  my $prev_cb = $h->{Callbacks}{method_name};
+  $h->{Callbacks}{method_name} = sub {
+    if ($prev_cb) {
+       my @result = $prev_cb->(@_);
+       return @result if not $_; # $prev_cb vetoed call
+    }
+    ... your callback logic here ...
+  };
 
 =head3 C<private_your_module_name_*>
 

Reply via email to