On 04-Sep-2006 Tim Bunce wrote:
> On Mon, Sep 04, 2006 at 05:21:17PM +0100, Martin J. Evans wrote:
>> 
>> It did raise an issue where I could do with some clarification. The slides
>> say:
>> 
>>  Execute a statement for multiple values
>>    $sth = $dbh->prepare("insert into table (foo,bar) values (?,?)");
>>    $tuples = $sth->execute_array(\%attr, [EMAIL PROTECTED], [EMAIL 
>> PROTECTED]);
>> 
>>  returns count of executions (even ones that failed) and not rows-affected.
>> 
>> but the latest DBI pod says:
>> 
>>  The execute_array() method returns the number of tuples executed,
>>  or "undef" if an error occured. Like execute(), a successful exe-
>>  cute_array() always returns true regardless of the number of tuples
>>  executed, even if it's zero.  See the "ArrayTupleStatus" attribute
>>  below for how to determine the execution status for each tuple.
>> 
>> I read that latter as if undef is returned 1 or more of the executes failed
>> but
>> now I read in the slides they suggest I may get a true value when an error
>> occurs. My experience with execute_array and Oracle shows I get:
>> 
>> return 0, when none resulted in a change but no errors
>>   e.g.
>>   drop table mytest
>>   create table mytest (a int primary key, b char(20))
>>   insert into mytest values (1,'onetwothree')
>>   update mytest set b = ? where a = ?
>>     $sth->bind_param_array(2, [2])
>>     $sth->bind_param_array(1, ['two'])
>>   $sth->execute_array( { ArrayTupleStatus => [EMAIL PROTECTED] } )
>> 
>>   returns 0
>> 
>>   No, error, just no update occurred. It does not return 1 as the slide
>>   suggests and does seem to be the number of rows changed - none.
>> 
>> returns undef when there is an error
>>   e.g.
>>   as per above example but the bind_param_array for p2 is invalid e.g. 'xxx'
>> 
>>   This seems to also contradict the slide but agrees with the DBI pod.
>> 
>> lastly I get undef if there are multiple rows but the last one errors.
>> e.g. as above except the bind_param_arrays are:
>> 
>>  $sth->bind_param_array(2, [1, 'xxx']);
>>  $sth->bind_param_array(1, ['one', 'two']);
>> 
>>  returns undef but executes the 1,'one'.
>> 
>> I am now unclear as to what is correct. Can you clarify?
> 
> The pod is right and both the slides and DBD::Oracle are wrong,
> though in different ways.
> 
> The behaviour should match that of the default execute_for_fetch
> method (which execute_array calls) provided by the DBI - shown below.
> 
> Thanks! I've fixed my copy of the slide to say "returns count of
> executions, not rows-affected, or undef if any failed".
> 
> Tim.
> 
> p.s. I'd be grateful if you could file a bug at rt.cpan.org for DBD::Oracle.
> 
> 
>     sub execute_for_fetch {
>         my ($sth, $fetch_tuple_sub, $tuple_status) = @_;
>         # start with empty status array
>         ($tuple_status) ? @$tuple_status = () : $tuple_status = [];
> 
>         my ($err_count, %errstr_cache);
>         while ( my $tuple = &$fetch_tuple_sub() ) {
>             if ( my $rc = $sth->execute(@$tuple) ) {
>                 push @$tuple_status, $rc;
>             }
>             else {
>                 $err_count++;
>                 my $err = $sth->err;
>                 push @$tuple_status, [ $err, $errstr_cache{$err} ||=
> $sth->errstr, $sth->state ];
>             }
>         }
>         my $tuples = @$tuple_status;
>         return $sth->set_err(1, "executing $tuples generated $err_count
> errors")
>             if $err_count;
>         return scalar(@$tuple_status) || "0E0";
>     }

Thanks for the clarification.

I'll report the problems just as soon as I'm certain what they are. Here is
my summary:

1. insert into table values (?,?) where all values are valid and 3 rows of
   values

     returns: 3
       (correct as 3 tuples executed)
     ArrayTupleStatus = (-1, -1, -1)
       (incorrect - should be (1,1,1) as DBD::Oracle does know a row was
        inserted each time as evidenced by execute itself which returns rows
        affected)

2. insert into table values (?,?) where there are 2 rows of data but second one
   is invalid

   returns: undef
     (correct)
   ArrayTupleStatus = (-1, [1772, 'ORA-01722.......'])
     (incorrect as this should be (1, [1772, 'ORA-01722.......'])
      probably same issue as 1).

3. update table where the update does not affect any rows

   returns: 0
     (incorrect - should be 1 for 1 tuple executed)
   ArrayTupleStatus = (-1)
     (incorrect - should be (0) for no rows affected)

If my conclusions look right to you I'll report them on rt.

As an aside, I am finding execute_array returning the number of tuples executed
rather than the number of rows affected a bit of a pain. It seems to make it
easy to ascertain how many tuples were executed but hard to find out how
many rows were affected when I would have thought the former is less useful
and easily calculated as [EMAIL PROTECTED]). What I normally do on a
single insert/update execute is:

$affected = $sth->execute(@params)
ERROR if ($affected != $expected_inserts_or_updates);

as this catches errors in the execute and error in the program logic (not
inserting/updating what I expected). execute_array does not map easily to being
n * execute as far as I can see since the equivalent test for execute_array is:

$tuples_executed = $sth->execute_array
if (!$tuples_executed) {
  # there was an error
} else {
  # here $tuples_executed has no meaning for me since I know what it is going
  # to be before calling execute_array - it is going to be the number of tuples
  # I passed to execute_array
  my $affected = 0;
  $affected += $_  foreach (@array_tuple_status);
  ERROR if ($affected != $expected_inserts_or_updates)
}

It's possible I've misunderstood what is returned by execute_array but it is
difficult to actually try it out when I don't seem to have a driver that does
it properly.

Martin
--
Martin J. Evans
Easysoft Ltd, UK
http://www.easysoft.com

Reply via email to