Mailing List Archive

perl bug: array of undefs is falsely false (was: Fetching null columns)
> From: mhm@austin.ibm.com
>
> Ok, question time. How do we expect the DBI::DBD interfaces to work
> when it comes to columns returning null?
>
Instances of NULLS are represented on input and output as undefs. Period.


> $sth->execute();
> while (@row = $sth->fetchrow) {
> ...
> }
>
> If the $stmt requests only one column from the table
> and the column can contain NULLs then it is possible to return only
> part of the data.

This is a perl bug discovered recently by Michael Peppler working on Sybase.
I CC'd a reply to perlbug@perl.com but I think it bounced. I'll CC this.

> If $stmt requests more than one column and the first
> column requested can contain NULLs, we break again.
>
Not quite. It breaks if *all* the columns are nulls. Here's a test case:

sub undef_a { (undef, undef, 1, undef) }
sub undef_b { (undef, undef, undef, undef) }
sub undef_c { (undef) }

(@ary = undef_a()) ? print "ok\n" : print "not ok\n";
(@ary = undef_b()) ? print "ok\n" : print "not ok\n";
(@ary = undef_c()) ? print "ok\n" : print "not ok\n";

It prints:

ok
not ok
not ok

This *is* a serious problem for database work!

> I guess, my point is that we need some method of returning something
> other than a Nullsv or sv_undef and giving the enduser a method of
> checking the null indicator from the fbh struct.
>
Nope. Perl needs fixing! This really needs to be fixed before 5.002.
(Note: _never_ return Nullsv on the stack, always use &sv_undef.)

In the meantime you can use the alternative (and faster) $sth->fetch
method upon which fetchrow is implemented:

while($aryref = $sth->fetch) {
.... code using @$aryref or $aryref->[n] ...
}

but this won't help people who need to stick to the Oraperl emulation interface.


Tim.
Re: perl bug: array of undefs is falsely false (was: Fetching null columns) [ In reply to ]
On Wed, 4 Oct 1995 12:29:37 +0100 Tim Bunce wrote:


> Not quite. It breaks if *all* the columns are nulls. Here's a test case:

The following test with perl 5.001 patchlevel 1l indicates that it is quite
subtle. It is the evaluation in scalar context of the assignment of a list
of undefs which is broken. Evaluation of the list itself in scalar context
works.

This would not be a good reason to change the DBI spec;-)

Sample program:

print "$]\n";
@u = (undef); # a list of one undef
@x = ('x'); # a list of one defined value
$un1 = @u;
$un2 = (@v = @u);
$un3 = @v;
$xn1 = @x;
$xn2 = (@y = @x);
$xn3 = @y;
(defined $un1) ? print "un1=$un1\n" : print "un1 undefined\n";
(defined $un2) ? print "un2=$un2\n" : print "un2 undefined\n";
(defined $un3) ? print "un3=$un3\n" : print "un3 undefined\n";
(defined $xn1) ? print "xn1=$xn1\n" : print "xn1 undefined\n";
(defined $xn2) ? print "xn2=$xn2\n" : print "xn2 undefined\n";
(defined $xn3) ? print "xn3=$xn3\n" : print "xn3 undefined\n";

Output:

5.001
un1=1
un2 undefined
un3=1
xn1=1
xn2=1
xn3=1


For the record, perl4 does not show this bug, so existing oraperl users
need not worry.

Charles Jardine, Univ of Cambridge (UK).
Re: perl bug: array of undefs is falsely false (was: Fetching null columns) [ In reply to ]
I've added Mark Jefferys to this discussion, since he was involved in
the change to list assignment, I believe.

Larry

: > From: mhm@austin.ibm.com
: >
: > Ok, question time. How do we expect the DBI::DBD interfaces to work
: > when it comes to columns returning null?
: >
: Instances of NULLS are represented on input and output as undefs. Period.
:
:
: > $sth->execute();
: > while (@row = $sth->fetchrow) {
: > ...
: > }
: >
: > If the $stmt requests only one column from the table
: > and the column can contain NULLs then it is possible to return only
: > part of the data.
:
: This is a perl bug discovered recently by Michael Peppler working on Sybase.
: I CC'd a reply to perlbug@perl.com but I think it bounced. I'll CC this.
:
: > If $stmt requests more than one column and the first
: > column requested can contain NULLs, we break again.
: >
: Not quite. It breaks if *all* the columns are nulls. Here's a test case:
:
: sub undef_a { (undef, undef, 1, undef) }
: sub undef_b { (undef, undef, undef, undef) }
: sub undef_c { (undef) }
:
: (@ary = undef_a()) ? print "ok\n" : print "not ok\n";
: (@ary = undef_b()) ? print "ok\n" : print "not ok\n";
: (@ary = undef_c()) ? print "ok\n" : print "not ok\n";
:
: It prints:
:
: ok
: not ok
: not ok
:
: This *is* a serious problem for database work!
:
: > I guess, my point is that we need some method of returning something
: > other than a Nullsv or sv_undef and giving the enduser a method of
: > checking the null indicator from the fbh struct.
: >
: Nope. Perl needs fixing! This really needs to be fixed before 5.002.
: (Note: _never_ return Nullsv on the stack, always use &sv_undef.)
:
: In the meantime you can use the alternative (and faster) $sth->fetch
: method upon which fetchrow is implemented:
:
: while($aryref = $sth->fetch) {
: .... code using @$aryref or $aryref->[n] ...
: }
:
: but this won't help people who need to stick to the Oraperl emulation interface.
:
:
: Tim.
:
Re: perl bug: array of undefs is falsely false (was: Fetching null columns) [ In reply to ]
Someone (Tim?) wrote:

% > From: mhm@austin.ibm.com
% >
% > Ok, question time. How do we expect the DBI::DBD interfaces to work
% > when it comes to columns returning null?
% >
% Instances of NULLS are represented on input and output as undefs. Period.
%
%
% > $sth->execute();
% > while (@row = $sth->fetchrow) {
% > ...
% > }
% >
% > If the $stmt requests only one column from the table
% > and the column can contain NULLs then it is possible to return only
% > part of the data.
%
% This is a perl bug discovered recently by Michael Peppler working on Sybase.
% I CC'd a reply to perlbug@perl.com but I think it bounced. I'll CC this.

Well, this can't be considered a bug, since it's intentional (maybe a
documentation bug---I was really lax on that obligation). It might be
a bad idea, although I don't really like any of the solutions:

This is there so that loops like

while (($user, $uid) = (getpwent)[0,2]) {
...
}

doesn't run forever. (getpwent returns () at the end, which turns
into (undef) x 2, which still ends up false on assignment.)


There are several ways I can think of doing this, offhand:

1. Document the real behavior. Suggest using

@arr = func(); if (@arr) ...

or

if ((@arr = func()), @arr) ...

when a list of undefs should be true. I suspect there are a
some other cases where this is what the programmer wants, but
I have no idea how often.


2. Make list-assignments return undef for zero objects assigned,
and SV_NO for undef-only assignments. This allows:

if (defined(@arr = func())) ...


3. Make list-assignments normal, but have list-slices cut off
trailing undefs, so

(getpwent)[0,2]

returns an empty list. (This method was implemented in one of
the perl alphas.) This would fix the immediate problem, but
would add others:

A. This will cause code which relies on list-slices
returning a fixed number of items to break. Sub calls
are a likely source of problems:

func1((func2())[1,3], ++$cnt);
# Expects $cnt in 3rd place, not last.

It gets even worse if someone tries to write a DB module
in Perl:

sub row_subset {
my($self, $row, @cols) = @_;

($self->get_row($row))[$self->col_nums(@cols)];
# col_nums converts column names to numerical indices
}

if (@arr = row_subset($i, X, Y)) { # oops: X,Y are undefs
# Same problem, different cause

B. If any of the elements sliced is magic, we either have to
get its value, which wastes time and can cause problems,
or *not* get its value, which can fail.


4. Same as #3, except only eliminate undefs if you can eliminate
all of them. Slightly reduces the problem with #A, does nothing
for #B.


5. Same as #3, except cut off trailing *out-of-bound* references in
list-slices. More likely to do what you want with #A, and fixes
#B. Brings into sharp relief the question, "Should array-slices
act like list-slices here." I don't like making them different,
but it makes it that much harder to get tied arrays working.
(And if you find this question hard, should hash slices cut off
trailing non-existent elements? 1/2 :-))


6. Combine #4 and #5. Same comments as #5.


7. Make a list-slice of an empty list be empty. This is an example
of minimal intervention: make sure empty lists stay empty, and
thus false, but otherwise acts normally. This would be somewhat
easier to implement for tied arrays (and hashes).


8. Make array assignments of slices magical, returning false if the
original list was empty (or something). A really do-what-I-want
solution.


9. Use awk. :-)


I kinda like #2 or #7 at the moment; #8 has a quirky, Perlish
feel to it that attracts me, although I suspect that it's a bad idea.

Actually, #9 is looking pretty good at the moment...


Mark