Thread: [HACKERS] Composite IS NULL again, this time with plpgsql

[HACKERS] Composite IS NULL again, this time with plpgsql

From
Andrew Gierth
Date:
This came up recently on irc:

create type t1 as (a integer, b integer);
create type t2 as (p t1, q t1);
create function null_t2() returns t2 language sql as $f$ select null::t2; $f$;

Now consider the following plpgsql:

declare v t2;
begin v := null_t2(); raise info 'v is null = %', v is null;  -- shows 'f'
end;

The value of the plpgsql variable v always behaves as if it had been
assigned row(row(null,null),row(null,null)) -- which, as we thrashed out
in detail sometime last year, isn't the same as row(null,null) or just
null and isn't considered null by IS NULL.

So there's no non-ugly way to preserve the nullity or otherwise of the
function result. The best I could come up with in answer to the original
problem (how to detect in plpgsql whether a composite value returned by
a function was actually null) was this:

declare v t2; v_null boolean;
begin select x into v from null_t2() as x where not (x is null); v_null := not found; raise info 'v is null = %',
v_null;
end;

which lacks a certain obviousness (and you have to remember to use not
(x is null) rather than x is not null).

This obviously happens because plpgsql is storing the variable as an
expanded list of fields rather than as a single composite datum, and
rebuilding the datum value when it needs to be passed to SQL for
evaluation.  Without an "isnull" flag for each composite subvalue, this
can't regenerate the original datum closely enough to give the same
value on an isnull test.

What to do about this?

-- 
Andrew (irc:RhodiumToad)



Re: [HACKERS] Composite IS NULL again, this time with plpgsql

From
Tom Lane
Date:
Andrew Gierth <andrew@tao11.riddles.org.uk> writes:
> This obviously happens because plpgsql is storing the variable as an
> expanded list of fields rather than as a single composite datum, and
> rebuilding the datum value when it needs to be passed to SQL for
> evaluation.  Without an "isnull" flag for each composite subvalue, this
> can't regenerate the original datum closely enough to give the same
> value on an isnull test.

> What to do about this?

Sooner or later, I think we're going to have to bite the bullet and
switch over to using regular composite datums for plpgsql composite
variables --- that is, use the REC not ROW code paths for that case.

The ROW code paths should only get used for cases like "SELECT INTO a,b,c"
--- where there's no way to name the row-as-a-whole so no question
arises of whether it's null or not.

People have pushed back on that idea for various reasons, but we're
never going to have consistent behavior at this level of detail
until we do it.
        regards, tom lane