On Sun, Oct 25, 2009 at 10:24:57PM -0700, David E. Wheeler wrote: > On Oct 24, 2009, at 2:50 PM, Tim Bunce wrote: > >> Callbacks are handled by the method dispatcher so all method names are >> valid (so don't bother trying to list them in the docs :) >> >> Plus the two special cases for connect_cached: 'connect_cached.new' and >> 'connect_cached.reused'. (There's also '*' but that's not really >> recommended.) > > Thanks. Following up on [this > post](http://markmail.org/message/fus3dfauxs6yz6sv), I wrote this code: > > my $dbh = DBI->connect('dbi:Pg:dbname=bric', '', '', { > Callbacks => { > execute => sub { print "Set in DBH\n"; return; } > } > }); > > my $sth = $dbh->prepare('SELECT id FROM pref WHERE name = ?'); > # $sth->{Callbacks}{execute} = sub { print "Set in STH\n"; return; }; > $sth->execute('Time Zone'); > > It output nothing. When I uncommented that second-to-last line, it output > "Set in STH". So it seems that a callback added to the dbh for a statement > method name does not end up getting passed on to the statement handle. So I > guess the Callbacks attribute is not passed on to statement handles created > for the database handle?
Yeap. > Seems a shame… I recall thinking about it but opting not to. I can't recall why though. Possibly just to err on the side of caution. I'm open to persuasion. > One other thing: It's nice that the callbacks execute before the method > call, so that you can disable it by undefing $_. But it'd be equally handle > to have callbacks after the method call. Yes, I'd always planned for that but never had a need to implement it myself. I'd figured the callback code ref could, optionally, be an array ref and if so, the second element would be the callback to call on method return. Note that a post-prepare() callback could be used to implement inheritance of Callbacks. (If the method attributes of prepare($sql, \%attr) were applied to the newly created sth, in the same way as connect(), then a pre-prepare callback could be used to pass down the $dbh->{Callbacks} to the $sth.) > For example, I'd love to be able > to create a callback on a statement handle to convert a timestamp column to > a DateTime object: > > $sth->{PostCallbacks}{fetch} = sub { > my ($sth, $row) = @_; > $row->[3] = DateTime::Format::Pg->parse_datetime($row->[3]); > }; That particular example is potentially risky - or at least unportable. What if the driver doesn't implement a higher-level method (like fetchrow_hashref or fetchall_* etc) in terms of fetch? I've always felt the best way to do that kind of thing would be to tie() the individual elements of the row buffer array - but I've never needed to do it myself so never explored it. Another issue to keep in mind is that there's only one callback per method. In other words there's no way for multiple callbacks for a single method to coexist on a handle. That limits the usefulness of Callbacks in general. Ideally I'd like to see an abstract interface for managing callbacks, rather than the current "stuff it in a hash". That way future support for post-method callbacks and multi-callbacks per method and handle could be added without exposing (and locking us into) particular implementation details. Tim. p.s. An interesting use-case for Callbacks can be see in DBI::Gofer::Execute. See http://search.cpan.org/~timb/DBI-1.609/lib/DBI/Gofer/Execute.pm#DBI_GOFER_RANDOM The corresponding code is in the _install_rand_callbacks method in http://cpansearch.perl.org/src/TIMB/DBI-1.609/lib/DBI/Gofer/Execute.pm