BUG #15297: Irregular comparison rules for NULLs in tuples

Started by PG Bug reporting formover 7 years ago4 messagesbugs
Jump to latest
#1PG Bug reporting form
noreply@postgresql.org

The following bug has been logged on the website:

Bug reference: 15297
Logged by: kena
Email address: knz@thaumogen.net
PostgreSQL version: 10.4
Operating system: FreeBSD + Linux
Description:

The SQL standard mandates ternary logic for values that involve NULL, where
NULL means "unknown".

This works in many cases correctly in pg:

"select NULL in (1,2)" -> returns NULL, correct
"select (1, NULL::int) in ((1, 1), (1, 2))" -> returns NULL, correct
"select 1 < NULL" -> returns NULL, correct
"select (1, NULL::int) > (1, 2)" -> returns NULL, correct

However as soon as a tuple/composite value contains itself composites, the
rule is not obeyed any more recursively:

"select (1, (1, NULL::int)) in ((1, (1, 0)), (1, (1, 2)))" -> returns false
!?

"select (1, (1, NULL::int)) > (1, (1, 2))" -> returns true !?

Is this intended behavior? If so, where is it documented?

If not documented, any suggestion as to how to work around it?

Thanks in advance,

--
Raphael 'kena' Poss

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: PG Bug reporting form (#1)
Re: BUG #15297: Irregular comparison rules for NULLs in tuples

=?utf-8?q?PG_Bug_reporting_form?= <noreply@postgresql.org> writes:

The SQL standard mandates ternary logic for values that involve NULL, where
NULL means "unknown".

Right.

"select (1, (1, NULL::int)) > (1, (1, 2))" -> returns true !?

We insist on non-null record values being totally ordered, because without
that you can't build a working btree opclass for them. So the general
principle for comparing corresponding fields in two records is that nulls
sort after non-nulls and two nulls are treated as equal. The same goes
for elements in other container types such as arrays.

I don't know whether this behavior can be justified by chapter and verse
in the SQL standard, but it doesn't really matter; we will not change it.

regards, tom lane

#3Raphael 'kena' Poss
knz@thaumogen.net
In reply to: Tom Lane (#2)
Re: BUG #15297: Irregular comparison rules for NULLs in tuples

Op 26-07-18 om 10:03 schreef Tom Lane:

"select (1, (1, NULL::int)) > (1, (1, 2))" -> returns true !?

We insist on non-null record values being totally ordered, because without
that you can't build a working btree opclass for them. So the general
principle for comparing corresponding fields in two records is that nulls
sort after non-nulls and two nulls are treated as equal.

I'd really like this to be true (I like it, it's simple) but then how do
you explain that row(1, null) > row(1, 2) is NULL, and not true? both
sides are record values and they are not null, after all.

--
Raphael 'kena' Poss

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Raphael 'kena' Poss (#3)
Re: BUG #15297: Irregular comparison rules for NULLs in tuples

"Raphael 'kena' Poss" <knz@thaumogen.net> writes:

Op 26-07-18 om 10:03 schreef Tom Lane:

We insist on non-null record values being totally ordered, because without
that you can't build a working btree opclass for them. So the general
principle for comparing corresponding fields in two records is that nulls
sort after non-nulls and two nulls are treated as equal.

I'd really like this to be true (I like it, it's simple) but then how do
you explain that row(1, null) > row(1, 2) is NULL, and not true? both
sides are record values and they are not null, after all.

Yeah, well, if we have a comparison operator comparing two syntactic row
constructors, it works differently: that case breaks down the two field
lists and applies the named operator to each pair of values. The behavior
for nulls is just one of the discrepancies; that case is also more
forgiving about field type differences. For instance this is allowed:

regression=# select (1, 1, NULL::int) > (1, 1, 2.0);
?column?
----------

(1 row)

but this not so much:

regression=# create type int3 as (f1 int,f2 int,f3 int);
CREATE TYPE
regression=# create table i3 (c1 int3);
CREATE TABLE
regression=# insert into i3 values ((1,1,1));
INSERT 0 1
regression=# select c1 > (1, 1, 2.0) from i3;
ERROR: cannot compare dissimilar column types integer and numeric at record column 3

Personally I'd be happy to lose all that special-case behavior for row
constructors, but we'd get push-back on backwards compatibility.

regards, tom lane