Thread: BUG #7808: unnest doesn't handle nulls in array of composite types correctly
BUG #7808: unnest doesn't handle nulls in array of composite types correctly
From
joe@tanga.com
Date:
The following bug has been logged on the website: Bug reference: 7808 Logged by: Joe Van Dyk Email address: joe@tanga.com PostgreSQL version: 9.2.1 Operating system: OSX Description: = RhodiumToad says this is a bug in unnest, but honestly I don't quite understand it all. = He said: "if you have an array of composite, then a null element provokes that error, as opposed to an element all of whose columns are null. basically, unnest(array[null::g]) breaks, while unnest(array[row(null,null)::g]) works" My goal is to remove nulls from an array. The array could be an array of a composite type. begin; = = = = create table f (id integer); = = insert into f values (1), (2); = = = = create table g (id integer, f_id integer); = = insert into g values (1, 1); = = insert into g values (2, 1); = = = = create function no_nulls(anyarray) returns anyarray as $$ = = select array(select x from unnest($1) x where not (x is null)) = = $$ language sql; = = = = select f.id, no_nulls(array_agg(g)) = = from f = = left join g on g.f_id =3D f.id = = = group by f; = = Expected Result: = = id | array_agg = = ----+------------------- = = 1 | {"(1,1)","(2,1)"} = = 2 | {} = = = = = = Getting this error: = = = = psql:/tmp/n.sql:18: ERROR: function returning set of rows cannot return null value = CONTEXT: SQL function "no_nulls" statement 1 = = =
Re: BUG #7808: unnest doesn't handle nulls in array of composite types correctly
From
Stephen Frost
Date:
* joe@tanga.com (joe@tanga.com) wrote: > My goal is to remove nulls from an array. The array could be an array of a > composite type. A much simpler case is: =3D> create type xt as (a integer); CREATE TYPE =3D> select * from unnest(array[null::xt]); ERROR: function returning set of rows cannot return null value =3D> select * from unnest(array[row(null)::xt]); a=20 --- =20 (1 row) Thanks, Stephen
Re: BUG #7808: unnest doesn't handle nulls in array of composite typescorrectly
From
Andrew Gierth
Date:
This bug was reported three and a half years ago and apparently ignored... but it came to my attention in the IS NULL discussion. This patch doesn't address unnest() explicitly, rather it modifies ExecMakeTableFunctionResult to treat an isnull return equivalently to an all-nulls tuple. This isn't ideal, especially in view of the points discussed in the other threads; it leaves these inconsistencies: create type c1 as (a text, b numeric); select u, u is distinct from null from (select unnest(array[null::c1,row('a',1)::c1,null::c1]) as u) s; u | ?column? -------+---------- | f (a,1) | t | f (3 rows) select u, u is distinct from null from unnest(array[null::c1,row('a',1)::c1,null::c1]) u; u | ?column? -------+---------- (,) | t (a,1) | t (,) | t (3 rows) But as far as I can tell, the spec actually requires that unnest cope with nulls and produce actual columns; the syntax transformations result in something roughly like: SELECT (e).* FROM (SELECT a[i] AS e FROM ...) and I don't see anything that licenses (e).* to fail just because a[i] is the null value of a row type. -- Andrew (irc:RhodiumToad)
Attachment
Andrew Gierth <andrew@tao11.riddles.org.uk> writes: > This bug was reported three and a half years ago and apparently > ignored... but it came to my attention in the IS NULL discussion. > This patch doesn't address unnest() explicitly, rather it modifies > ExecMakeTableFunctionResult to treat an isnull return equivalently to an > all-nulls tuple. I do not see how you can propose this, which creates an explicit equivalence between a plain null and an all-nulls row, and simultaneously advocate that we change IS NULL to remove its treatment of those things as equivalent. I think the theory behind the existing code here is that if the SRF wants its output to be interpreted as an all-nulls row, it can perfectly well return an all-nulls row. I wonder whether we should address this by adjusting unnest's behavior instead. regards, tom lane
Re: BUG #7808: unnest doesn't handle nulls in array of composite typescorrectly
From
Andrew Gierth
Date:
>>>>> "Tom" == Tom Lane <tgl@sss.pgh.pa.us> writes: Tom> I do not see how you can propose this, which creates an explicit Tom> equivalence between a plain null and an all-nulls row, and Tom> simultaneously advocate that we change IS NULL to remove its Tom> treatment of those things as equivalent. Because the proposal to change IS NULL is a discussion point that I'm not dogmatically committed to. Tom> I think the theory behind the existing code here is that if the Tom> SRF wants its output to be interpreted as an all-nulls row, it can Tom> perfectly well return an all-nulls row. I wonder whether we Tom> should address this by adjusting unnest's behavior instead. One problem with changing unnest() is that it makes the operation array(select unnest(a)) return an array that's not equal to the original one (and which takes up a lot more space if there are many nulls). This is far more likely to break existing code than simply allowing what was previously an error case to work. -- Andrew (irc:RhodiumToad)
Andrew Gierth <andrew@tao11.riddles.org.uk> writes: > "Tom" == Tom Lane <tgl@sss.pgh.pa.us> writes: > Tom> I think the theory behind the existing code here is that if the > Tom> SRF wants its output to be interpreted as an all-nulls row, it can > Tom> perfectly well return an all-nulls row. I wonder whether we > Tom> should address this by adjusting unnest's behavior instead. > One problem with changing unnest() is that it makes the operation > array(select unnest(a)) return an array that's not equal to the original > one (and which takes up a lot more space if there are many nulls). Fair point. I didn't much like the "initial_nulls" counter in your patch, but actually there's no reason we can't just push an all-nulls row into the tuplestore immediately on seeing a null, the same way as happens in the last-ditch case at the bottom of ExecMakeTableFunctionResult. I whacked that around a bit and pushed it. regards, tom lane
Re: BUG #7808: unnest doesn't handle nulls in array of composite typescorrectly
From
Andrew Gierth
Date:
>>>>> "Tom" == Tom Lane <tgl@sss.pgh.pa.us> writes: Tom> I didn't much like the "initial_nulls" counter in your patch, but Tom> actually there's no reason we can't just push an all-nulls row Tom> into the tuplestore immediately on seeing a null, the same way as Tom> happens in the last-ditch case at the bottom of Tom> ExecMakeTableFunctionResult. I whacked that around a bit and Tom> pushed it. I did think about that, but the existing code seems to go out of its way to build the tuplestore using the tupdesc obtained from the actual function result row, rather than the "expected" tupdesc, and it wasn't really obvious why that was. -- Andrew (irc:RhodiumToad)
Andrew Gierth <andrew@tao11.riddles.org.uk> writes: > I did think about that, but the existing code seems to go out of its way > to build the tuplestore using the tupdesc obtained from the actual > function result row, rather than the "expected" tupdesc, and it wasn't > really obvious why that was. As I noted in a comment, the key hazard would be that heap_form_tuple copies the tupdesc's type identifier info into the created composite Datum, thus possibly leading to a mixture of type IDs in the stored tuples. But tuplestore stores MinimalTuples which omit that data, so it doesn't matter. I have a feeling that the existing code here might've been older than the changes to store MinimalTuples rather than regular tuple datums in tuplestores; or at least, it was written with the assumption that that was important. regards, tom lane