v1-0001-Fix-negative-bitmapset-member-error.patch

application/octet-stream

Filename: v1-0001-Fix-negative-bitmapset-member-error.patch
Type: application/octet-stream
Part: 0
Message: Re: BUG #18260: Unexpected error: "negative bitmapset member not allowed" triggered by multiple JOIN

Patch

Format: format-patch
Series: patch v1-0001
Subject: Fix 'negative bitmapset member' error
File+
src/backend/nodes/bitmapset.c 2 2
src/test/regress/expected/join.out 20 0
src/test/regress/sql/join.sql 8 0
From 5429590c0a058bc681b450b0130ecb1f848fb740 Mon Sep 17 00:00:00 2001
From: Richard Guo <guofenglinux@gmail.com>
Date: Thu, 28 Dec 2023 10:56:17 +0800
Subject: [PATCH v1] Fix 'negative bitmapset member' error

When removing a useless join, we'd remove PHVs that are not used at join
partner rels or above the join.  A PHV that references the join's relid
in ph_eval_at is logically "above" the join and thus should not be
removed.  We have the following check for that:

    !bms_is_member(ojrelid, phinfo->ph_eval_at)

However, in the case of SJE removing an useless inner join, 'ojrelid' is
set to -1, which would trigger the "negative bitmapset member not
allowed" error in bms_is_member().

We can fix it by skipping checking ojrelid for inner joins.  But this
patch chooses to modify bms_is_member() to return false for negative
numbers instead of emitting an error.
---
 src/backend/nodes/bitmapset.c      |  4 ++--
 src/test/regress/expected/join.out | 20 ++++++++++++++++++++
 src/test/regress/sql/join.sql      |  8 ++++++++
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index e13ecaa155..2a828ce709 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -477,9 +477,9 @@ bms_is_member(int x, const Bitmapset *a)
 	int			wordnum,
 				bitnum;
 
-	/* XXX better to just return false for x<0 ? */
+	/* negative number cannot be a member of the bitmapset */
 	if (x < 0)
-		elog(ERROR, "negative bitmapset member not allowed");
+		return false;
 	if (a == NULL)
 		return false;
 
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 1557e17299..dc5d11317f 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -6821,6 +6821,26 @@ on true;
                            Filter: (id IS NOT NULL)
 (8 rows)
 
+-- Check that SJE removes the whole PHVs correctly
+explain (verbose, costs off)
+select 1 from emp1 t1 left join
+    (lateral (select 1 as x, * from emp1 t2) s1 inner join
+        (select * from emp1 t3) s2 on s1.id = s2.id)
+    on true
+where s1.x = 1;
+                       QUERY PLAN                        
+---------------------------------------------------------
+ Nested Loop
+   Output: 1
+   ->  Seq Scan on public.emp1 t1
+         Output: t1.id, t1.code
+   ->  Materialize
+         Output: t3.id
+         ->  Seq Scan on public.emp1 t3
+               Output: t3.id
+               Filter: ((t3.id IS NOT NULL) AND (1 = 1))
+(9 rows)
+
 -- Check that PHVs do not impose any constraints on removing self joins
 explain (verbose, costs off)
 select * from emp1 t1 join emp1 t2 on t1.id = t2.id left join
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index fed9e83e31..e2ec5c1c12 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -2600,6 +2600,14 @@ select * from emp1 t1 left join
         on true)
 on true;
 
+-- Check that SJE removes the whole PHVs correctly
+explain (verbose, costs off)
+select 1 from emp1 t1 left join
+    (lateral (select 1 as x, * from emp1 t2) s1 inner join
+        (select * from emp1 t3) s2 on s1.id = s2.id)
+    on true
+where s1.x = 1;
+
 -- Check that PHVs do not impose any constraints on removing self joins
 explain (verbose, costs off)
 select * from emp1 t1 join emp1 t2 on t1.id = t2.id left join
-- 
2.31.0