On Tue, 18 Jan 2011 07:40:25 -0500, John Scoles <sco...@pythian.com> wrote:
> On 17/01/2011 3:34 PM, Martin J. Evans wrote: > > There appear to be differences between DBDs which do not handle > > execute_array (so DBI does it for them) and DBDs which do handle > > execute_array (e.g., DBD::Oracle). The main ones discussed on > > #dbix-class which I investigated are whether the driver sets the err > > and errstr or even raises an error. Some of the guys writing > > DBIx::Class think execute_array should raise and error and fail on the > > first error but I explained since execute_array may send the entire > > batch to the server and it is server dependent when it stops this is > > beyond definition by DBI. Never the less the following script seems to > > show some large differences between DBI's execute_array and > > DBD::Oracle's: > > > > The first question is do any other DBIs utilize a native array_execute?? DBD::Unify and DBD::CSV do not > Anyway > > Well lets go back to DBI and see what it says > > When called in scalar context the execute_array() method returns > the number of tuples executed, or |undef| if an error occurred. Like > execute(), a successful execute_array() always returns true regardless > of the number of tuples executed, even if it's zero. If there were any > errors the ArrayTupleStatus array can be used to discover which tuples > failed and with what errors. > > In DBD::Oracle you will never get 'undef' returned as the execute will > always be successful even though all of your tuples may fail. > > So It agrees with the first para and works in scalar. > > To get the extra info that comes out in a non-DBD specific array_execute > we would have to build in an extra iteration over the results to give a > count of the Failed/Pass. As some of my customers use this with batch > loads of 5meg plus of inserts the iteration may take some time and sort > of defeat the purpose of a quick way to do bulk inserts. > > I think (you will have to ask Tim to verify) that the Idea behind > array_execute is a 'Batch' processor. ie send a Batch, to the server > then figure out what to with what is returned. > > so with > > a) even though RaiseError was set, no error was raised although a > warning was. > We know there was a problem so we have to fail the batch or at > least report on it is what the warning is telling us > > b) execute_array returned undef (correct) > Well at least that is a good thing > > c) errstr is set but err is not (0) > > d) the HandleError routine was not called - due to (a)? > Which is correct as is did do exactly what was expected. ie > 'execute a bath and report back' ^^^^ The batch will take long enough to have a comfortable bath? :) > e) the count of rows affected is -1 for all rows which worked - I > believe this is permissible > I will have to check on that. > > In the end I do not think this should ever error > > eval { > $inserted = $sth->execute_array( > { ArrayTupleStatus => \@tuple_status } ); > }; > > It is the wrong way to process a batch job. JMHO though > > > I guess the real sort of problem is that in the normal DBI array fetch > it is just iterating over array and doing the insert one at a time so > you get your good and error counts as you go. As well as each iteration > is a separate execute you will get a raise_error with it which is think > is suppressed but I would have to look at the code. > > I think you are right that the the chaps at DBIx have it wrong. It > should be a batch job and they would have to handle in that way. > > 1) bind > 2) exe > 3) commit if all successful or process if an error is returned. > > Anyway lets see what Tim has to say. > > We could add in the list context for DBD::Oracle and do some of this > processing with the caveat that it will take longer than the scalar context > > Cheers > John > > > use DBI; > > use strict; > > use Data::Dumper; > > > > sub fred > > { > > print "Error Handler called\n"; > > print Dumper(\@_); > > my ($msg, $handle, $val) = @_; > > > > print "handle_error: $msg\nhandle: $handle\nval=$val\n"; > > 0; > > } > > > > my $dbh = DBI->connect( > > 'DBI:Oracle:host=xxx;sid=devel', 'xxx', 'xxx', > > { RaiseError => 1, PrintError => 0, HandleError => \&fred > > }); > > do_it($dbh); > > > > my $dbh = DBI->connect( > > 'DBI:ODBC:DSN=xxx', 'xxx', 'xxx', > > { RaiseError => 1, PrintError => 0, HandleError => \&fred > > }); > > > > do_it($dbh); > > > > sub do_it { > > my $dbh = shift; > > > > eval {$dbh->do(q/drop table mytest/);}; > > $dbh->do(q/create table mytest (a int primary key, b char(20))/); > > > > my $sth = $dbh->prepare(q/insert into mytest values (?,?)/); > > $sth->bind_param(1, 1); > > $sth->bind_param(2, 'onetwothree'); > > $sth->execute; > > > > $sth->bind_param_array(1, [51,1,52,53]); > > $sth->bind_param_array(2, ['fiftyone', 'fiftytwo', 'fiftythree', > > 'one']); > > my (@tuple_status, $inserted); > > eval { > > $inserted = $sth->execute_array( > > { ArrayTupleStatus => \@tuple_status } ); > > }; > > if ($@) { > > print "Exception: $@\n"; > > } > > print "Error from execute_array - " . $sth->errstr . ",", > > $sth->err ."\n" > > if (!$inserted); > > for (@tuple_status) { > > print Dumper($_), "\n"; > > } > > } > > > > which outputs for the DBD::Oracle part: > > > > $ perl execute_array/execute_array.pl > > DBD::Oracle::st execute_array warning: ORA-24381: error(s) in array > > DML (DBD SUCCESS_WITH_INFO: OCIStmtExecute) [for Statement "insert > > into mytest values (?,?)"] at execute_array/execute_array.pl line 43. > > Error from execute_array - ORA-24381: error(s) in array DML (DBD > > SUCCESS_WITH_INFO: OCIStmtExecute),0 > > $VAR1 = -1; > > > > $VAR1 = [ > > 1, > > 'ORA-00001: unique constraint (BET.SYS_C0096150) violated > > (DBD SUCCESS_WITH_INFO)' > > ]; > > > > $VAR1 = -1; > > > > $VAR1 = -1; > > > > Notable from this is that: > > > > a) even though RaiseError was set, no error was raised although a > > warning was. > > b) execute_array returned undef (correct) > > c) errstr is set but err is not (0) > > d) the HandleError routine was not called - due to (a)? > > e) the count of rows affected is -1 for all rows which worked - I > > believe this is permissible > > > > For the DBD::ODBC run which does not do execute_array itself you get: > > > > Error Handler called > > $VAR1 = [ > > 'DBD::ODBC::st execute_array failed: executing 4 generated 1 > > errors', > > bless( {}, 'DBI::st' ), > > undef > > ]; > > handle_error: DBD::ODBC::st execute_array failed: executing 4 > > generated 1 errors > > handle: DBI::st=HASH(0xa071d00) > > val=Exception: DBD::ODBC::st execute_array failed: executing 4 > > generated 1 errors at > > execute_array/execute_array.pl line 43. > > > > Error from execute_array - executing 4 generated 1 errors,2000000000 > > $VAR1 = 1; > > > > $VAR1 = [ > > 1, > > '[unixODBC][Easysoft][SQL Server Driver][SQL > > Server]Violation of PRIMARY KEY constraint \'PK__mytest__3661ABE9\'. > > Cannot insert duplicate key in object \'dbo.mytest\'. (SQL-23000) > > [state was 23000 now 01000] > > [unixODBC][Easysoft][SQL Server Driver][SQL Server]The statement has > > been terminated. (SQL-01000)', > > '01000' > > ]; > > > > $VAR1 = 1; > > > > $VAR1 = 1; > > > > Notice the difference: > > > > a) an error was raised (different from DBD::Oracle) saying 1 of 4 failed > > b) execute_array returned undef (the same) > > c) both errstr and err are set although where 2000000000 comes from > > I'm not sure > > d) the HandleError routine was called (different from DBD::Oracle) > > e) the count of rows affected is 1 for all the rows which worked > > > > For anyone using execute_array this represents somewhat of a problem > > unless they write substantial code per DBD. The clarification required > > is: > > > > a) if execute_array fails on any row should that raise an error? > > Obviously, if it does, then HandleError comes in to it > > b) if execute_array fails should that set errstr AND err > > > > I believe the count per row of affected is driver dependent so I'll > > ignore that but there is a lot of code out there (perhaps doing things > > wrong) which examines "err" (like DBIx::Class) which is not set in > > DBD::Oracle's case. The strict interpretation of the pod for > > execute_array suggests execute_array will return undef on any failure > > (which it does in both cases) but not whether any row is an > > error/warning and whether "err" and "errstr" are set. > > > > BTW, please keep Peter (ribasushi) on the cc list as he is not > > subscribed to dbi-dev but is an interested party. > > > > Martin > -- H.Merijn Brand http://tux.nl Perl Monger http://amsterdam.pm.org/ using 5.00307 through 5.12 and porting perl5.13.x on HP-UX 10.20, 11.00, 11.11, 11.23 and 11.31, OpenSuSE 10.1, 11.0 .. 11.3 and AIX 5.2 and 5.3. http://mirrors.develooper.com/hpux/ http://www.test-smoke.org/ http://qa.perl.org http://www.goldmark.org/jeff/stupid-disclaimers/