Subject: Re: Check each of base restriction clauses for constant-FALSE-or-NULL

From: Ashutosh Bapat <[email-masked]>
Date: Mon, 9 Oct 2023 15:17:50 +0530

On Sat, Oct 7, 2023 at 3:14 PM Richard Guo <[email-masked]> wrote:
>
> In relation_excluded_by_constraints() when we're trying to figure out
> whether the relation need not be scanned, one of the checks we do is to
> detect constant-FALSE-or-NULL restriction clauses.  Currently we perform
> this check only when there is exactly one baserestrictinfo entry, and
> the comment explains this as below.
>
>  * Regardless of the setting of constraint_exclusion, detect
>  * constant-FALSE-or-NULL restriction clauses.  Because const-folding will
>  * reduce "anything AND FALSE" to just "FALSE", any such case should
>  * result in exactly one baserestrictinfo entry.
>
> This doesn't seem entirely correct, because equivclass.c may generate
> constant-FALSE baserestrictinfo entry on the fly.  In addition, other
> quals could get pushed down to the baserel.  All these cases would
> result in that the baserestrictinfo list might possibly have other
> members besides the FALSE constant.
>
> So I'm wondering if we should check each of base restriction clauses for
> constant-FALSE-or-NULL quals, like attached.
>
> Here are some examples.
>
> -- #1 constant-FALSE generated by ECs
>
> -- unpatched (in all branches)
>
>         QUERY PLAN
> --------------------------
>  Result
>    One-Time Filter: false
>    ->  Seq Scan on t t1
>          Filter: (a = 1)
> (4 rows)
>

I used a slightly modified query as below

# explain (costs off) select * from pg_class t1 where oid = 1 and oid = 2;
                        QUERY PLAN
----------------------------------------------------------
 Result
   One-Time Filter: false
   ->  Index Scan using pg_class_oid_index on pg_class t1
         Index Cond: (oid = '1'::oid)
(4 rows)

postgres@312571=# explain (analyze, costs off) select * from pg_class
t1 where oid = 1 and oid = 2;
                                QUERY PLAN
---------------------------------------------------------------------------
 Result (actual time=0.002..0.003 rows=0 loops=1)
   One-Time Filter: false
   ->  Index Scan using pg_class_oid_index on pg_class t1 (never executed)
         Index Cond: (oid = '1'::oid)
 Planning Time: 0.176 ms
 Execution Time: 0.052 ms
(6 rows)

You will see that the scan node was never executed. Hence there's no
execution time benefit if we remove the scan plan.

Where do we produce the single baserestrictinfo mentioned in the
comments? Is it before the planning proper starts?

get_gating_quals does what you are doing much earlier in the query
processing. Your code would just duplicate that.

>
> -- patched
> explain (costs off)
> select * from t t1 left join (select * from t t2 where false) s on s.a = 1;
>            QUERY PLAN
> --------------------------------
>  Nested Loop Left Join
>    ->  Seq Scan on t t1
>    ->  Result
>          One-Time Filter: false
> (4 rows)

Does your code have any other benefits like deeming an inner join as empty?

-- 
Best Wishes,
Ashutosh Bapat