Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wkVQf-000iSQ-0S for pgsql-bugs@arkaria.postgresql.org; Thu, 16 Jul 2026 23:23:45 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wkVQc-00FWh5-10 for pgsql-bugs@arkaria.postgresql.org; Thu, 16 Jul 2026 23:23:42 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wkVQb-00FWgx-2r for pgsql-bugs@lists.postgresql.org; Thu, 16 Jul 2026 23:23:41 +0000 Received: from sss.pgh.pa.us ([68.162.161.243]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wkVQZ-00000000aS0-0O0p for pgsql-bugs@lists.postgresql.org; Thu, 16 Jul 2026 23:23:40 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.18.1/8.18.1) with ESMTP id 66GNNU3A3824829; Thu, 16 Jul 2026 19:23:30 -0400 From: Tom Lane To: "Matheus Alcantara" cc: leis@in.tum.de, pgsql-bugs@lists.postgresql.org Subject: Re: BUG #19553: Wrong results from nested LEFT JOINs over an empty subquery (regression since v16) In-reply-to: References: <19553-4561747f93f368a7@postgresql.org> Comments: In-reply-to "Matheus Alcantara" message dated "Thu, 16 Jul 2026 19:37:27 -0300" MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <3824827.1784244210.1@sss.pgh.pa.us> Date: Thu, 16 Jul 2026 19:23:30 -0400 Message-ID: <3824828.1784244210@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk "Matheus Alcantara" writes: > On Thu Jul 16, 2026 at 11:12 AM -03, PG Bug reporting form wrote: >> The following self-contained query returns wrong results on every release >> since v16: >> >> select * from (values (1),(2)) v(x) >> left join (select q from (select 7 as q from (select where false) ss1) >> ss2 >> left join (select 8 as z) ss3 on true) ss4 on true; > According to my findings, the issue seems to be in > remove_useless_results_recurse(), specifically in find_dependent_phvs(). Yeah, I had just come to the same conclusion: we are deciding that the PHV is not dependent on the RTE_RESULT we're considering removing, but it really is. > Attached patch seems to fix find_dependent_phvs() to test membership > rather than exact equality, since that's what actually indicates the > PHV's value depends on that relation. I had thought of that too, but I think it is wrong and will result in not pursuing optimizations that are valid. I instrumented the code like this: *** 4309,4314 **** --- 4309,4319 ---- if (phv->phlevelsup == context->sublevels_up && bms_equal(context->relids, phv->phrels)) return true; + if (phv->phlevelsup == context->sublevels_up && + bms_is_subset(context->relids, phv->phrels)) + elog(WARNING, "dubious case detected: looking for %s, PHV has %s", + bmsToString(context->relids), + bmsToString(phv->phrels)); /* fall through to examine children */ } and observed that this warning fires in several join.sql cases that are not giving wrong answers. (Unfortunately, those tests only check the query results not the plan, so they'd not show any change in behavior from your patch.) What I see here is that the PHV in question initially has :phrels (b 7 9 10) -- lower OJ, RESULT, RESULT :phnullingrels (b 3) -- upper OJ and we decide that RTE 10 can be removed, leaving :phrels (b 7 9) -- lower OJ, RESULT :phnullingrels (b 3) -- upper OJ That's fine, but when we come to consider RTE 9, we decide it can be removed, which is wrong. I think the core of the problem here is that this code was written back when phrels contained only baserels, and now that it also contains OJ rels, we're mistakenly concluding that the presence of those bits indicates there's another place to evaluate the PHV. So the simplest fix is probably to mask off OJ bits and consider only baserels when deciding if phrels equals the target. Unfortunately, this happens long before we compute root->all_baserels or anything like that, so remove_useless_result_rtes is on its own to figure out which those are. I think we can extend it to build a bitmapset of relevant baserel RT indexes while it is scanning the tree (so that we don't need an additional recursive scan just to get that). But I've not tried to write any code yet; do you feel like attacking that? find_dependent_phvs_in_jointree most likely needs the same fix. I don't believe your conclusion that it should act differently. BTW, I think "git bisect"'s finding that the bug started with commit 3af87736b is mostly accidental. That commit removed a different limitation preventing the intermediate FromExpr from getting flattened, allowing the problem to be reached. regards, tom lane