Re: wrong query result due to wang plan

From: Richard Guo <guofenglinux(at)gmail(dot)com>
To: tender wang <tndrwang(at)gmail(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: wrong query result due to wang plan
Date: 2023-02-16 09:50:59
Message-ID: CAMbWs48MiT0HNm9ng_DKdDEPDTC=Bk==aenkauoOp0-s8Fhy8w@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Thu, Feb 16, 2023 at 3:16 PM tender wang <tndrwang(at)gmail(dot)com> wrote:

> select ref_1.r_comment as c0, subq_0.c1 as c1 from public.region as
> sample_0 right join public.partsupp as sample_1 right join public.lineitem
> as sample_2 on (cast(null as path) = cast(null as path)) on (cast(null as
> "timestamp") < cast(null as "timestamp")) inner join public.lineitem as
> ref_0 on (true) left join (select sample_3.ps_availqty as c1,
> sample_3.ps_comment as c2 from public.partsupp as sample_3 where false
> order by c1, c2 ) as subq_0 on (sample_1.ps_supplycost = subq_0.c1 ) right
> join public.region as ref_1 on (sample_1.ps_availqty = ref_1.r_regionkey )
> where ref_1.r_comment is not NULL order by c0, c1;
>

The repro can be reduced to the query below.

create table t (a int, b int);

# explain (costs off) select * from t t1 left join (t t2 inner join t t3 on
false left join t t4 on t2.b = t4.b) on t1.a = t2.a;
QUERY PLAN
--------------------------
Result
One-Time Filter: false
(2 rows)

As we can see, the joinrel at the final level is marked as dummy, which
is wrong. I traced this issue down to distribute_qual_to_rels() when we
handle variable-free clause. If such a clause is not an outer-join
clause, and it contains no volatile functions either, we assign it the
full relid set of the current JoinDomain. I doubt this is always
correct.

Such as in the query above, the clause 'false' is assigned relids {t2,
t3, t4, t2/t4}. And that makes it a pushed down restriction to the
second left join. This is all right if we plan this query in the
user-given order. But if we've commuted the two left joins, which is
legal, this pushed down and constant false restriction would make the
final joinrel be dummy.

It seems we still need to check whether a variable-free qual comes from
somewhere that is below the nullable side of an outer join before we
decide that it can be evaluated at join domain level, just like we did
before. So I wonder if we can add a 'below_outer_join' flag in
JoinTreeItem, fill its value during deconstruct_recurse, and check it in
distribute_qual_to_rels() like

/* eval at join domain level if not below outer join */
- relids = bms_copy(jtitem->jdomain->jd_relids);
+ relids = jtitem->below_outer_join ?
+ bms_copy(qualscope) : bms_copy(jtitem->jdomain->jd_relids);

Thanks
Richard

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Drouvot, Bertrand 2023-02-16 09:55:32 Re: Normalization of utility queries in pg_stat_statements
Previous Message Robert Haas 2023-02-16 09:48:57 Re: Weird failure with latches in curculio on v15