Fix bug with page deletion. If inner page is removed and it tries to
authorTeodor Sigaev
Thu, 30 Nov 2006 16:22:32 +0000 (16:22 +0000)
committerTeodor Sigaev
Thu, 30 Nov 2006 16:22:32 +0000 (16:22 +0000)
remove page on next level linked from next inner page, ginScanToDelete()
wrongly sets parent page. Bug reveals when many item pointers from index
was deleted ( several hundred thousands).

Bug is discovered by hubert depesz lubaczewski 

Suppose, we need rc2 before release...

src/backend/access/gin/ginvacuum.c

index c3415ffe44ff2f30c57943a75d05aef215f1abb1..225dbc6047787e6a1239d8bbc097852e5adb18e0 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *         $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.8 2006/11/12 06:55:53 neilc Exp $
+ *         $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.9 2006/11/30 16:22:32 teodor Exp $
  *-------------------------------------------------------------------------
  */
 
@@ -265,6 +265,12 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn
    }
 
    parentPage = BufferGetPage(pBuffer);
+#ifdef USE_ASSERT_CHECKING
+   do {
+       PostingItem *tod=(PostingItem *) GinDataPageGetItem(parentPage, myoff);
+       Assert( PostingItemGetBlockNumber(tod) == deleteBlkno );
+   } while(0);
+#endif
    PageDeletePostingItem(parentPage, myoff);
 
    page = BufferGetPage(dBuffer);
@@ -351,7 +357,8 @@ typedef struct DataPageDeleteStack
    struct DataPageDeleteStack *child;
    struct DataPageDeleteStack *parent;
 
-   BlockNumber blkno;
+   BlockNumber blkno; /* current block number */
+   BlockNumber leftBlkno; /* rightest non-deleted page on left */
    bool        isRoot;
 } DataPageDeleteStack;
 
@@ -377,7 +384,7 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDel
            me = (DataPageDeleteStack *) palloc0(sizeof(DataPageDeleteStack));
            me->parent = parent;
            parent->child = me;
-           me->blkno = InvalidBlockNumber;
+           me->leftBlkno = InvalidBlockNumber;
        }
        else
            me = parent->child;
@@ -392,6 +399,7 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDel
    {
        OffsetNumber i;
 
+       me->blkno = blkno;
        for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
        {
            PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, i);
@@ -403,13 +411,13 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDel
 
    if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
    {
-       if (!(me->blkno == InvalidBlockNumber && GinPageRightMost(page)))
+       if (!(me->leftBlkno == InvalidBlockNumber && GinPageRightMost(page)))
        {
            /* we never delete right most branch */
            Assert(!isRoot);
            if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
            {
-               ginDeletePage(gvs, blkno, me->blkno, me->parent->blkno, myoff, me->parent->isRoot);
+               ginDeletePage(gvs, blkno, me->leftBlkno, me->parent->blkno, myoff, me->parent->isRoot);
                meDelete = TRUE;
            }
        }
@@ -418,7 +426,7 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDel
    ReleaseBuffer(buffer);
 
    if (!meDelete)
-       me->blkno = blkno;
+       me->leftBlkno = blkno;
 
    return meDelete;
 }
@@ -438,7 +446,7 @@ ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno)
    }
 
    memset(&root, 0, sizeof(DataPageDeleteStack));
-   root.blkno = rootBlkno;
+   root.leftBlkno = InvalidBlockNumber;
    root.isRoot = TRUE;
 
    vacuum_delay_point();