Re: Unexpected (wrong?) result querying boolean partitioned table with NULL partition

From: David Rowley <dgrowleyml(at)gmail(dot)com>
To: David Kimura <david(dot)g(dot)kimura(at)gmail(dot)com>
Cc: PostgreSQL Development <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Unexpected (wrong?) result querying boolean partitioned table with NULL partition
Date: 2023-04-12 11:13:35
Message-ID: CAApHDvpkzuwfDZvLiW2=52y9X7txUxcS2-c7zAyaHzaM0AxwKg@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Wed, 12 Apr 2023 at 22:13, David Kimura <david(dot)g(dot)kimura(at)gmail(dot)com> wrote:
> Is it fair to assume that, given the same data, a partitioned table should
> return the same results as a non-partitioned table?

Yes, and also the same as when enable_partition_pruning is set to off.

> CREATE TABLE boolpart (a bool) PARTITION BY LIST (a);
> CREATE TABLE boolpart_default PARTITION OF boolpart default;
> CREATE TABLE boolpart_t PARTITION OF boolpart FOR VALUES IN ('true');
> CREATE TABLE boolpart_f PARTITION OF boolpart FOR VALUES IN ('false');
> INSERT INTO boolpart VALUES (true), (false), (null);
>
> EXPLAIN SELECT * FROM boolpart WHERE a IS NOT true;
> QUERY PLAN
> -----------------------------------------------------------------------
> Seq Scan on boolpart_f boolpart (cost=0.00..38.10 rows=1405 width=1)
> Filter: (a IS NOT TRUE)
> (2 rows)
>
> SELECT * FROM boolpart WHERE a IS NOT true;
> a
> ---
> f
> (1 row)
>
> Compare that to the result of a non-partitioned table:
>
> CREATE TABLE booltab (a bool);
> INSERT INTO booltab VALUES (true), (false), (null);
>
> EXPLAIN SELECT * FROM booltab WHERE a IS NOT true;
> QUERY PLAN
> -----------------------------------------------------------
> Seq Scan on booltab (cost=0.00..38.10 rows=1405 width=1)
> Filter: (a IS NOT TRUE)
> (2 rows)
>
> SELECT * FROM booltab WHERE a IS NOT true;
> a
> ---
> f

Ouch. That's certainly not correct.

> I think the issue has to do with assumptions made about boolean test IS NOT
> inequality logic which is different from inequality of other operators.
> Specifically, "true IS NOT NULL" is not the same as "true<>NULL".

Yeah, that's wrong.

> One idea is to use the negation operator for IS_NOT_(true|false) (i.e.
> BooleanNotEqualOperator instead of BooleanEqualOperator). But besides
> presumably being a more expensive operation, not equal is not part of the btree
> opfamily for bool_ops. So, seems like that won't really fit into the current
> partition pruning framework.

There's already code to effectively handle <> operators. Just the
PartClauseInfo.op_is_ne needs to be set to true.
get_matching_list_bounds() then handles that by taking the inverse of
the partitions matching the equality operator.

Effectively, I think that's the attached patch.

There seems to be a bunch of tests checking this already, all of them
assuming the incorrect plans.

David

Attachment Content-Type Size
fix_partprune_handling_of_NOT_TRUE_and_NOT_FALSE_boolean_quals.patch application/octet-stream 4.4 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Amit Kapila 2023-04-12 11:23:35 Re: Support logical replication of DDLs
Previous Message Hayato Kuroda (Fujitsu) 2023-04-12 09:48:15 RE: pg_upgrade and logical replication