Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q9a0W-0004M7-Pk for pgsql-bugs@arkaria.postgresql.org; Wed, 14 Jun 2023 23:34:32 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1q9a0U-00054q-BR for pgsql-bugs@arkaria.postgresql.org; Wed, 14 Jun 2023 23:34:30 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1q9a0T-00054R-VC for pgsql-bugs@lists.postgresql.org; Wed, 14 Jun 2023 23:34:29 +0000 Received: from sss.pgh.pa.us ([66.207.139.130]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1q9a0R-002FAk-GG for pgsql-bugs@lists.postgresql.org; Wed, 14 Jun 2023 23:34:28 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.15.2/8.15.2) with ESMTP id 35ENYO5E2023629; Wed, 14 Jun 2023 19:34:24 -0400 From: Tom Lane To: zuming.jiang@inf.ethz.ch cc: pgsql-bugs@lists.postgresql.org Subject: Re: BUG #17976: Inconsistent results of SELECT using CASE WHEN clause In-reply-to: <17976-4b638b525e9a983b@postgresql.org> References: <17976-4b638b525e9a983b@postgresql.org> Comments: In-reply-to PG Bug reporting form message dated "Wed, 14 Jun 2023 15:59:47 -0000" MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <2023627.1686785663.1@sss.pgh.pa.us> Content-Transfer-Encoding: quoted-printable Date: Wed, 14 Jun 2023 19:34:23 -0400 Message-ID: <2023628.1686785663@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk PG Bug reporting form writes: > My fuzzer finds a logical bug in Postgres, which makes Postgres return > inconsistent results. Thanks for the report! I poked at this a little bit. v16 is actually generating an incorrect plan for both query variants; the bad plan for the second variant just happens to not give visibly wrong answers for this input data. regression=3D# explain (costs off) regression-# select count(*) as c_6 regression-# from regression-# (t1 as ref_15 regression(# left outer join t4 as ref_16 regression(# on (ref_15.pkey =3D ref_16.c_2)) regression-# where (case when true regression(# then ref_16.c_3 else ref_16.c_3 end regression(# ) =3D pg_catalog.dcbrt(case when (((ref_15.c5 like '7= %z') regression(# and (not (ref_15.c5 li= ke '7%z'))) regression(# and ((ref_15.c5 like '= 7%z') is not null)) regression(# then ref_16.c_6 else ref_15= .c8 end); QUERY PLAN = --------------------------------------------------- Aggregate -> Hash Join Hash Cond: (ref_1.c9 =3D ref_15.pkey) -> Nested Loop Left Join Join Filter: ('1'::text =3D ref_1.c5) -> Seq Scan on t1 ref_1 -> Materialize -> Seq Scan on t2 ref_1_1 Filter: (c12 > c12) -> Hash -> Seq Scan on t1 ref_15 (11 rows) v15 and before give Aggregate -> Hash Join Hash Cond: (ref_1.c9 =3D ref_15.pkey) Join Filter: (dcbrt(CASE WHEN ((ref_15.c5 ~~ '7%z'::text) AND (re= f_15.c5 !~~ '7%z'::text) AND ((ref_15.c5 ~~ '7%z'::text) IS NOT NULL)) THE= N ((1))::double precision ELSE ref_15.c8 END) =3D ref_1.c8) -> Nested Loop Left Join Join Filter: ('1'::text =3D ref_1.c5) -> Seq Scan on t1 ref_1 -> Materialize -> Hash Left Join Hash Cond: (ref_1_1.c10 =3D ref_0.c2) -> Seq Scan on t2 ref_1_1 Filter: (c12 > c12) -> Hash -> HashAggregate Group Key: ref_0.c2 -> Seq Scan on t0 ref_0 -> Hash -> Seq Scan on t1 ref_15 So the good news is that v16 correctly recognizes that the left join to ref_0 can be discarded. (Older versions recognize this if you just select directly from t3 or t4, but fail to make that deduction when it's buried under an additional layer of outer join. I believe this better result is due to the outer-join-aware-Vars changes.) The bad news is that the top-level join filter condition has gone missing. That still happens even with a greatly simplified WHERE condition, ie this isn't really about CASE: regression=3D# explain (costs off) select count(*) as c_6 from (t1 as ref_15 left outer join t4 as ref_16 on (ref_15.pkey =3D ref_16.c_2)) where ref_16.c_6 =3D ref_15.c8; QUERY PLAN = --------------------------------------------------- Aggregate -> Hash Right Join Hash Cond: (ref_1.c9 =3D ref_15.pkey) -> Nested Loop Left Join Join Filter: ('1'::text =3D ref_1.c5) -> Seq Scan on t1 ref_1 -> Materialize -> Seq Scan on t2 ref_1_1 Filter: (c12 > c12) -> Hash -> Seq Scan on t1 ref_15 (11 rows) We are converting this WHERE condition to an EquivalenceClass with members "ref_16.c_6" and "ref_15.c8", and what seems to be the problem is that analyzejoins.c fails to strip the removed rel(s) from the EquivalenceMember for "ref_16.c_6", so it never looks like we've reached a join level where it's time to enforce that. I'd always kind of wondered how we got away with not updating EquivalenceClasses during join removal, and the answer evidently is that we can't anymore. I've not tried to write a patch yet. regards, tom lane