From c17abc1c5463119803be73990c9aa570763fd0c1 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 24 Jul 2005 02:25:26 +0000
Subject: [PATCH] Fix logic error in tbm_intersect: the intersection of a
 normal page and a lossy page has to be lossy, because we don't know exactly
 which tuples on the page should remain part of the bitmap.  Per Jie Zhang.

---
 src/backend/nodes/tidbitmap.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index 1da1aa785ef..777bf6482d2 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -23,7 +23,7 @@
  * Copyright (c) 2003-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/tidbitmap.c,v 1.4 2005/05/29 04:23:03 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/tidbitmap.c,v 1.5 2005/07/24 02:25:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -135,7 +135,8 @@ struct TIDBitmap
 
 /* Local function prototypes */
 static void tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage);
-static bool tbm_intersect_page(PagetableEntry *apage, const TIDBitmap *b);
+static bool tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage,
+							   const TIDBitmap *b);
 static const PagetableEntry *tbm_find_pageentry(const TIDBitmap *tbm,
 												BlockNumber pageno);
 static PagetableEntry *tbm_get_pageentry(TIDBitmap *tbm, BlockNumber pageno);
@@ -382,7 +383,7 @@ tbm_intersect(TIDBitmap *a, const TIDBitmap *b)
 	/* Scan through chunks and pages in a, try to match to b */
 	if (a->status == TBM_ONE_PAGE)
 	{
-		if (tbm_intersect_page(&a->entry1, b))
+		if (tbm_intersect_page(a, &a->entry1, b))
 		{
 			/* Page is now empty, remove it from a */
 			Assert(!a->entry1.ischunk);
@@ -401,7 +402,7 @@ tbm_intersect(TIDBitmap *a, const TIDBitmap *b)
 		hash_seq_init(&status, a->pagetable);
 		while ((apage = (PagetableEntry *) hash_seq_search(&status)) != NULL)
 		{
-			if (tbm_intersect_page(apage, b))
+			if (tbm_intersect_page(a, apage, b))
 			{
 				/* Page or chunk is now empty, remove it from a */
 				if (apage->ischunk)
@@ -424,7 +425,7 @@ tbm_intersect(TIDBitmap *a, const TIDBitmap *b)
  * Returns TRUE if apage is now empty and should be deleted from a
  */
 static bool
-tbm_intersect_page(PagetableEntry *apage, const TIDBitmap *b)
+tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, const TIDBitmap *b)
 {
 	const PagetableEntry *bpage;
 	int		wordnum;
@@ -470,7 +471,23 @@ tbm_intersect_page(PagetableEntry *apage, const TIDBitmap *b)
 	}
 	else if (tbm_page_is_lossy(b, apage->blockno))
 	{
-		/* page is lossy in b, cannot clear any bits */
+		/*
+		 * When the page is lossy in b, we have to mark it lossy in a too.
+		 * We know that no bits need be set in bitmap a, but we do not know
+		 * which ones should be cleared, and we have no API for "at most
+		 * these tuples need be checked".  (Perhaps it's worth adding that?)
+		 */
+		tbm_mark_page_lossy(a, apage->blockno);
+
+		/*
+		 * Note: tbm_mark_page_lossy will have removed apage from a, and
+		 * may have inserted a new lossy chunk instead.  We can continue the
+		 * same seq_search scan at the caller level, because it does not
+		 * matter whether we visit such a new chunk or not: it will have
+		 * only the bit for apage->blockno set, which is correct.
+		 *
+		 * We must return false here since apage was already deleted.
+		 */
 		return false;
 	}
 	else
-- 
GitLab