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/

Reply via email to