Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f844022

Browse files
committed
Fix two ancient bugs in GiST code to re-find a parent after page split:
First, when following a right-link, we incorrectly marked the current page as the parent of the right sibling. In reality, the parent of the right page is the same as the parent of the current page (or some page to the right of it, gistFindCorrectParent() will sort that out). Secondly, when we follow a right-link, we must prepend, not append, the right page to our list of pages to visit. That's because we assume that once we hit a leaf page in the list, all the rest are leaf pages too, and give up. To hit these bugs, you need concurrent actions and several unlucky accidents. Another backend must split the root page, while you're in process of splitting a lower-level page. Furthermore, while you scan the internal nodes to re-find the parent, another backend needs to again split some more internal pages. Even then, the bugs don't necessarily manifest as user-visible errors or index corruption. While we're at it, make the error reporting a bit better if gistFindPath() fails to re-find the parent. It used to be an assertion, but an elog() seems more appropriate. Backpatch to all supported branches.
1 parent cf82452 commit f844022

File tree

1 file changed

+24
-9
lines changed

1 file changed

+24
-9
lines changed

src/backend/access/gist/gist.c

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -883,9 +883,12 @@ gistFindPath(Relation r, BlockNumber child)
883883

884884
if (GistPageIsLeaf(page))
885885
{
886-
/* we can safety go away, follows only leaf pages */
886+
/*
887+
* Because we scan the index top-down, all the rest of the pages
888+
* in the queue must be leaf pages as well.
889+
*/
887890
UnlockReleaseBuffer(buffer);
888-
return NULL;
891+
break;
889892
}
890893

891894
top->lsn = PageGetLSN(page);
@@ -900,14 +903,25 @@ gistFindPath(Relation r, BlockNumber child)
900903
if (top->parent && XLByteLT(top->parent->lsn, GistPageGetOpaque(page)->nsn) &&
901904
GistPageGetOpaque(page)->rightlink != InvalidBlockNumber /* sanity check */ )
902905
{
903-
/* page splited while we thinking of... */
906+
/*
907+
* Page was split while we looked elsewhere. We didn't see the
908+
* downlink to the right page when we scanned the parent, so
909+
* add it to the queue now.
910+
*
911+
* Put the right page ahead of the queue, so that we visit it
912+
* next. That's important, because if this is the lowest internal
913+
* level, just above leaves, we might already have queued up some
914+
* leaf pages, and we assume that there can't be any non-leaf
915+
* pages behind leaf pages.
916+
*/
904917
ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack));
905918
ptr->blkno = GistPageGetOpaque(page)->rightlink;
906919
ptr->childoffnum = InvalidOffsetNumber;
907-
ptr->parent = top;
908-
ptr->next = NULL;
909-
tail->next = ptr;
910-
tail = ptr;
920+
ptr->parent = top->parent;
921+
ptr->next = top->next;
922+
top->next = ptr;
923+
if (tail == top)
924+
tail = ptr;
911925
}
912926

913927
maxoff = PageGetMaxOffsetNumber(page);
@@ -963,7 +977,9 @@ gistFindPath(Relation r, BlockNumber child)
963977
top = top->next;
964978
}
965979

966-
return NULL;
980+
elog(ERROR, "failed to re-find parent of a page in index \"%s\", block %u",
981+
RelationGetRelationName(r), child);
982+
return NULL; /* keep compiler quiet */
967983
}
968984

969985
/*
@@ -1034,7 +1050,6 @@ gistFindCorrectParent(Relation r, GISTInsertStack *child)
10341050

10351051
/* ok, find new path */
10361052
ptr = parent = gistFindPath(r, child->blkno);
1037-
Assert(ptr != NULL);
10381053

10391054
/* read all buffers as expected by caller */
10401055
/* note we don't lock them or gistcheckpage them here! */

0 commit comments

Comments
 (0)