Re: Connected Callback Errors Ignored

2013-11-15 Thread Tim Bunce
On Thu, Nov 14, 2013 at 02:07:41PM -0800, David E. Wheeler wrote:
 On Nov 14, 2013, at 9:17 AM, David E. Wheeler da...@justatheory.com wrote:
 
  The error about foo not existing is gone, overridden by the second
  error about bar missing. This can lead to hard-to-find bugs. What
  if the second query depended on a condition set up by the first, and
  the first failed? As a user, I would only see the second error, and
  think that the first statement had succeeded. It would take me quite
  a while to figure out that it had not, in fact, succeeded.

You'd need to write the callback code in the way you'd naturally write
it when not using RaiseError. Which typically means something like:

$dbh-do('SET search_path = ?', undef, 'foo')
or return;

 I’m also finding it means I can’t do error handling in the callback.

I'm not sure what you mean there.

 I have to do this to get it to ignore errors:
 
 $dbh-do('SET search_path = ?', undef, 'foo');
 $dbh-set_err(undef) if $dbh-err;
 
 I feel a little dirty.

Ignoring errors isn't a common action.

  All of which is to say, your fix in 1.630 certainly improves the
  situation, but since callbacks are really userland code, I think it
  would be beneficial to change callbacks to run in an outer context,
  with the outer DBI handles passed to them. Possible?
 
 This would eliminate all of these problems, no?

Probably. There are three main down-sides to consider:
1. compatibility - the risk of breaking some existing callback code
2. performance - the cost of getting the outer handle
3. pure-perl compatibility

There is an undocumented DBI::_handles function that returns the inner
and outer handles for a given (inner or outer) handle. Perl code within
DBI.pm, such as the connect_cached callback logic, would need to call
that (or something like it) to get the outer handle to pass to the callback.

The DBI::_handles implementation in lib/DBI/PurePerl.pm doesn't support
getting the outer handle from an inner one. That's probably fixable by
adding a weakref.

I don't have a strong opinion on this.

Tim.


Re: Connected Callback Errors Ignored

2013-11-15 Thread David E. Wheeler
On Nov 15, 2013, at 3:12 AM, Tim Bunce tim.bu...@pobox.com wrote:

 You'd need to write the callback code in the way you'd naturally write
 it when not using RaiseError. Which typically means something like:
 
$dbh-do('SET search_path = ?', undef, 'foo')
or return;

That will prevent any further code from executing, but does not give me a 
chance to handle the error.

 I’m also finding it means I can’t do error handling in the callback.
 
 I'm not sure what you mean there.

I *always* use RaiseError = 1 (or HandleError). Never ever check return values.

 I have to do this to get it to ignore errors:
 
$dbh-do('SET search_path = ?', undef, 'foo');
$dbh-set_err(undef) if $dbh-err;
 
 I feel a little dirty.
 
 Ignoring errors isn't a common action.

Oh, I agree, this is an edge case in that sense. The issue for me is more the 
inconsistency. I have RaiseError set, but I cannot actually catch the error and 
handle it where it happens, even though I have RaiseError set to true. I have 
to know that callbacks are different, and either check return values to handle 
errors (and call set_err(undef) if I have handled them in the callback), or 
check return values and return (on 1.630 or later) to let an external error 
handler catch the error. IOW, It’s a userland function but I have to write it 
like an internal DBI function. It’s inconsistent UE.

 Probably. There are three main down-sides to consider:
 1. compatibility - the risk of breaking some existing callback code

Seems unlikely, I think, since it’s not documented that it’s not the user’s 
handle that is passed to callbacks. I think I have been using callbacks longer 
than anyone and never noticed. Frankly I’m amazed at how few people know 
they’re there (I regularly get requests to add them to DBIx::Connector).

 2. performance - the cost of getting the outer handle
 3. pure-perl compatibility
 
 There is an undocumented DBI::_handles function that returns the inner
 and outer handles for a given (inner or outer) handle. Perl code within
 DBI.pm, such as the connect_cached callback logic, would need to call
 that (or something like it) to get the outer handle to pass to the callback.

Yeah. That’s not a ton over overhead, is it? Is an additional method call 
really likely to have much of a performance impact?

 The DBI::_handles implementation in lib/DBI/PurePerl.pm doesn't support
 getting the outer handle from an inner one. That's probably fixable by
 adding a weakref.
 
 I don't have a strong opinion on this.

I am strongly in favor of providing a consistent interface to users. Currently, 
error-handling in callbacks is inconsistent when using RaiseError or 
HandleError.

I can carve out a little time today to write some test cases if it’ll help.

Thanks,

David




Re: Connected Callback Errors Ignored

2013-11-15 Thread Tim Bunce
On Fri, Nov 15, 2013 at 09:14:58AM -0800, David E. Wheeler wrote:
 On Nov 15, 2013, at 3:12 AM, Tim Bunce tim.bu...@pobox.com wrote:
 
  You'd need to write the callback code in the way you'd naturally write
  it when not using RaiseError. Which typically means something like:
  
 $dbh-do('SET search_path = ?', undef, 'foo')
 or return;
 
 That will prevent any further code from executing, but does not give me a 
 chance to handle the error.

When the method call (which fired the callback) returns, the error
recorded on the handle will trigger RaiseError etc.

 I *always* use RaiseError = 1 (or HandleError). Never ever check return 
 values.

Ah, your use of HandleError adds an extra perspective on this.

  Probably. There are three main down-sides to consider:
  1. compatibility - the risk of breaking some existing callback code
 
 Seems unlikely, I think, since it’s not documented that it’s not the
 user’s handle that is passed to callbacks. I think I have been using
 callbacks longer than anyone and never noticed. Frankly I’m amazed at
 how few people know they’re there (I regularly get requests to add
 them to DBIx::Connector).

I agree (on all points :).

  2. performance - the cost of getting the outer handle
  3. pure-perl compatibility
  
  There is an undocumented DBI::_handles function that returns the inner
  and outer handles for a given (inner or outer) handle. Perl code within
  DBI.pm, such as the connect_cached callback logic, would need to call
  that (or something like it) to get the outer handle to pass to the callback.
 
 Yeah. That’s not a ton over overhead, is it? Is an additional method
 call really likely to have much of a performance impact?

Probably not. I think connect_cached is the only use of Callbacks in
DBI.pm (not DBI.xs), and the very small added cost would only be paid by
those using Callbacks anyway.

  The DBI::_handles implementation in lib/DBI/PurePerl.pm doesn't support
  getting the outer handle from an inner one. That's probably fixable by
  adding a weakref.
  
  I don't have a strong opinion on this.
 
 I am strongly in favor of providing a consistent interface to users.
 Currently, error-handling in callbacks is inconsistent when using
 RaiseError or HandleError.

I agree.

 I can carve out a little time today to write some test cases if it’ll help.

No need, I've just hacked t/70callbacks.t to include those tests :)

Tim.


Re: Connected Callback Errors Ignored

2013-11-15 Thread David E. Wheeler
On Nov 15, 2013, at 10:24 AM, Tim Bunce tim.bu...@pobox.com wrote:

 When the method call (which fired the callback) returns, the error
 recorded on the handle will trigger RaiseError etc.

Only on 1.630 and higher.

 I *always* use RaiseError = 1 (or HandleError). Never ever check return 
 values.
 
 Ah, your use of HandleError adds an extra perspective on this.

Yeah, I have been defaulting to it for so long that I forget that some folks 
might not.

 Probably not. I think connect_cached is the only use of Callbacks in
 DBI.pm (not DBI.xs), and the very small added cost would only be paid by
 those using Callbacks anyway.

And only on connection or reconnection, yes? Not like for every call to do() or 
fetch()!

 I can carve out a little time today to write some test cases if it’ll help.
 
 No need, I've just hacked t/70callbacks.t to include those tests :)

Cool. Not pushed yet, I guess. Will watch for it!

Thanks!

David




Re: Connected Callback Errors Ignored

2013-11-14 Thread Tim Bunce
On Wed, Nov 13, 2013 at 11:22:03AM -0800, David E. Wheeler wrote:
 DBIers,
 
 Given this script:
 
 use v5.18;
 use warnings;
 use utf8;
 use DBI;
 
 my $dbh = DBI-connect('dbi:SQLite:', '', '', {
 PrintError = 0,
 RaiseError = 1,
 AutoCommit = 1,
 Callbacks = {
 connected = sub {
 say 'connected';
 $_[0]-do('SELECT 1 from foo');
 return;
 },
 }
 });
 
 say 'go';
 $dbh-do('SELECT 1 from foo');
 
 The output is:
 
 connected
 go
 DBD::SQLite::db do failed: no such table: foo at /Users/david/bin/try 
 line 22.
 
 That doesn't seem right to me. Shouldn't the connected callback throw an 
 exception? IOW, I would not expect go to be output.

This turned out to be Quite Interesting

Firstly, the RaiseError/PrintError logic only applies to 'top level
calls', i.e. calls made by the application.  That's why code executed
'within' the DBI and drivers has to do it's own error checking.

Another issue is that the callback is passed the 'inner' handle not the
outer (tied) one. That's not documented but ought to be as it's bound to
lead to surprises.

So in this example the do() call isn't passing through the DBI's method
dispatcher. It's calling the drivers own do() method directly. That's
not a big deal in this case as the dispatcher wouldn't have applied the
RaiseError logic anyway as the call was made from 'within' the DBI.

However, the do() does record an error on the handle which should be
noticed by the DBI dispatcher when the connect() method is returning.
The problem here seems to be that the callback is on the connected()
method and that's marked as 'keep err'. [...later...]

It turns out that this is fixed by the changes I made in 1.630 to
improve the handling of errors recorded in methods that are marked
'keep err'. That change was originally for STORE but also fixes this.

So I'll take a guess that you're not using DBI 1.630. (And hope I'm right.)

Tim.


Re: Connected Callback Errors Ignored

2013-11-14 Thread David E. Wheeler
On Nov 14, 2013, at 3:47 AM, Tim Bunce tim.bu...@pobox.com wrote:

 So I'll take a guess that you're not using DBI 1.630. (And hope I'm right.)

Yep, I am using 1.628.

I'm wondering, though, if callbacks should not be considered internal calls, 
and the outer handle passed to them?

D



Re: Connected Callback Errors Ignored

2013-11-14 Thread David E. Wheeler
On Nov 14, 2013, at 9:17 AM, David E. Wheeler da...@justatheory.com wrote:

 The error about foo not existing is gone, overridden by the second error 
 about bar missing. This can lead to hard-to-find bugs. What if the second 
 query depended on a condition set up by the first, and the first failed? As a 
 user, I would only see the second error, and think that the first statement 
 had succeeded. It would take me quite a while to figure out that it had not, 
 in fact, succeeded.

I’m also finding it means I can’t do error handling in the callback. I have to 
do this to get it to ignore errors:

$dbh-do('SET search_path = ?', undef, 'foo');
$dbh-set_err(undef) if $dbh-err;

I feel a little dirty.

 All of which is to say, your fix in 1.630 certainly improves the situation, 
 but since callbacks are really userland code, I think it would be beneficial 
 to change callbacks to run in an outer context, with the outer DBI handles 
 passed to them. Possible?

This would eliminate all of these problems, no?

Thanks,

David



Connected Callback Errors Ignored

2013-11-13 Thread David E. Wheeler
DBIers,

Given this script:

use v5.18;
use warnings;
use utf8;
use DBI;

my $dbh = DBI-connect('dbi:SQLite:', '', '', {
PrintError = 0,
RaiseError = 1,
AutoCommit = 1,
Callbacks = {
connected = sub {
say 'connected';
$_[0]-do('SELECT 1 from foo');
return;
},
}
});

say 'go';
$dbh-do('SELECT 1 from foo');

The output is:

connected
go
DBD::SQLite::db do failed: no such table: foo at /Users/david/bin/try line 
22.

That doesn't seem right to me. Shouldn't the connected callback throw an 
exception? IOW, I would not expect go to be output.

Best,

David