@@ -1018,95 +1018,114 @@ gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum)
10181018 * remain so at exit, but it might not be the same page anymore.
10191019 */
10201020static void
1021- gistFindCorrectParent (Relation r , GISTInsertStack * child )
1021+ gistFindCorrectParent (Relation r , GISTInsertStack * child , bool is_build )
10221022{
10231023 GISTInsertStack * parent = child -> parent ;
1024+ ItemId iid ;
1025+ IndexTuple idxtuple ;
1026+ OffsetNumber maxoff ;
1027+ GISTInsertStack * ptr ;
10241028
10251029 gistcheckpage (r , parent -> buffer );
10261030 parent -> page = (Page ) BufferGetPage (parent -> buffer );
1031+ maxoff = PageGetMaxOffsetNumber (parent -> page );
10271032
1028- /* here we don't need to distinguish between split and page update */
1029- if (child -> downlinkoffnum == InvalidOffsetNumber ||
1030- parent -> lsn != PageGetLSN (parent -> page ))
1033+ /* Check if the downlink is still where it was before */
1034+ if (child -> downlinkoffnum != InvalidOffsetNumber && child -> downlinkoffnum <= maxoff )
10311035 {
1032- /* parent is changed, look child in right links until found */
1033- OffsetNumber i ,
1034- maxoff ;
1035- ItemId iid ;
1036- IndexTuple idxtuple ;
1037- GISTInsertStack * ptr ;
1036+ iid = PageGetItemId (parent -> page , child -> downlinkoffnum );
1037+ idxtuple = (IndexTuple ) PageGetItem (parent -> page , iid );
1038+ if (ItemPointerGetBlockNumber (& (idxtuple -> t_tid )) == child -> blkno )
1039+ return ; /* still there */
1040+ }
10381041
1039- while (true)
1040- {
1041- maxoff = PageGetMaxOffsetNumber (parent -> page );
1042- for (i = FirstOffsetNumber ; i <= maxoff ; i = OffsetNumberNext (i ))
1043- {
1044- iid = PageGetItemId (parent -> page , i );
1045- idxtuple = (IndexTuple ) PageGetItem (parent -> page , iid );
1046- if (ItemPointerGetBlockNumber (& (idxtuple -> t_tid )) == child -> blkno )
1047- {
1048- /* yes!!, found */
1049- child -> downlinkoffnum = i ;
1050- return ;
1051- }
1052- }
1042+ /*
1043+ * The page has changed since we looked. During normal operation, every
1044+ * update of a page changes its LSN, so the LSN we memorized should have
1045+ * changed too. During index build, however, we don't WAL-log the changes
1046+ * until we have built the index, so the LSN doesn't change. There is no
1047+ * concurrent activity during index build, but we might have changed the
1048+ * parent ourselves.
1049+ */
1050+ Assert (parent -> lsn != PageGetLSN (parent -> page ) || is_build );
1051+
1052+ /*
1053+ * Scan the page to re-find the downlink. If the page was split, it might
1054+ * have moved to a different page, so follow the right links until we find
1055+ * it.
1056+ */
1057+ while (true)
1058+ {
1059+ OffsetNumber i ;
10531060
1054- parent -> blkno = GistPageGetOpaque (parent -> page )-> rightlink ;
1055- UnlockReleaseBuffer (parent -> buffer );
1056- if (parent -> blkno == InvalidBlockNumber )
1061+ maxoff = PageGetMaxOffsetNumber (parent -> page );
1062+ for (i = FirstOffsetNumber ; i <= maxoff ; i = OffsetNumberNext (i ))
1063+ {
1064+ iid = PageGetItemId (parent -> page , i );
1065+ idxtuple = (IndexTuple ) PageGetItem (parent -> page , iid );
1066+ if (ItemPointerGetBlockNumber (& (idxtuple -> t_tid )) == child -> blkno )
10571067 {
1058- /*
1059- * End of chain and still didn't find parent. It's a very-very
1060- * rare situation when root splitted.
1061- */
1062- break ;
1068+ /* yes!!, found */
1069+ child -> downlinkoffnum = i ;
1070+ return ;
10631071 }
1064- parent -> buffer = ReadBuffer (r , parent -> blkno );
1065- LockBuffer (parent -> buffer , GIST_EXCLUSIVE );
1066- gistcheckpage (r , parent -> buffer );
1067- parent -> page = (Page ) BufferGetPage (parent -> buffer );
10681072 }
10691073
1070- /*
1071- * awful!!, we need search tree to find parent ... , but before we
1072- * should release all old parent
1073- */
1074-
1075- ptr = child -> parent -> parent ; /* child->parent already released
1076- * above */
1077- while (ptr )
1074+ parent -> blkno = GistPageGetOpaque (parent -> page )-> rightlink ;
1075+ parent -> downlinkoffnum = InvalidOffsetNumber ;
1076+ UnlockReleaseBuffer (parent -> buffer );
1077+ if (parent -> blkno == InvalidBlockNumber )
10781078 {
1079- ReleaseBuffer (ptr -> buffer );
1080- ptr = ptr -> parent ;
1079+ /*
1080+ * End of chain and still didn't find parent. It's a very-very
1081+ * rare situation when root splitted.
1082+ */
1083+ break ;
10811084 }
1085+ parent -> buffer = ReadBuffer (r , parent -> blkno );
1086+ LockBuffer (parent -> buffer , GIST_EXCLUSIVE );
1087+ gistcheckpage (r , parent -> buffer );
1088+ parent -> page = (Page ) BufferGetPage (parent -> buffer );
1089+ }
10821090
1083- /* ok, find new path */
1084- ptr = parent = gistFindPath (r , child -> blkno , & child -> downlinkoffnum );
1091+ /*
1092+ * awful!!, we need search tree to find parent ... , but before we should
1093+ * release all old parent
1094+ */
10851095
1086- /* read all buffers as expected by caller */
1087- /* note we don't lock them or gistcheckpage them here! */
1088- while (ptr )
1089- {
1090- ptr -> buffer = ReadBuffer (r , ptr -> blkno );
1091- ptr -> page = (Page ) BufferGetPage (ptr -> buffer );
1092- ptr = ptr -> parent ;
1093- }
1096+ ptr = child -> parent -> parent ; /* child->parent already released above */
1097+ while (ptr )
1098+ {
1099+ ReleaseBuffer (ptr -> buffer );
1100+ ptr = ptr -> parent ;
1101+ }
10941102
1095- /* install new chain of parents to stack */
1096- child -> parent = parent ;
1103+ /* ok, find new path */
1104+ ptr = parent = gistFindPath ( r , child -> blkno , & child -> downlinkoffnum ) ;
10971105
1098- /* make recursive call to normal processing */
1099- LockBuffer (child -> parent -> buffer , GIST_EXCLUSIVE );
1100- gistFindCorrectParent (r , child );
1106+ /* read all buffers as expected by caller */
1107+ /* note we don't lock them or gistcheckpage them here! */
1108+ while (ptr )
1109+ {
1110+ ptr -> buffer = ReadBuffer (r , ptr -> blkno );
1111+ ptr -> page = (Page ) BufferGetPage (ptr -> buffer );
1112+ ptr = ptr -> parent ;
11011113 }
1114+
1115+ /* install new chain of parents to stack */
1116+ child -> parent = parent ;
1117+
1118+ /* make recursive call to normal processing */
1119+ LockBuffer (child -> parent -> buffer , GIST_EXCLUSIVE );
1120+ gistFindCorrectParent (r , child , is_build );
11021121}
11031122
11041123/*
11051124 * Form a downlink pointer for the page in 'buf'.
11061125 */
11071126static IndexTuple
11081127gistformdownlink (Relation rel , Buffer buf , GISTSTATE * giststate ,
1109- GISTInsertStack * stack )
1128+ GISTInsertStack * stack , bool is_build )
11101129{
11111130 Page page = BufferGetPage (buf );
11121131 OffsetNumber maxoff ;
@@ -1147,7 +1166,7 @@ gistformdownlink(Relation rel, Buffer buf, GISTSTATE *giststate,
11471166 ItemId iid ;
11481167
11491168 LockBuffer (stack -> parent -> buffer , GIST_EXCLUSIVE );
1150- gistFindCorrectParent (rel , stack );
1169+ gistFindCorrectParent (rel , stack , is_build );
11511170 iid = PageGetItemId (stack -> parent -> page , stack -> downlinkoffnum );
11521171 downlink = (IndexTuple ) PageGetItem (stack -> parent -> page , iid );
11531172 downlink = CopyIndexTuple (downlink );
@@ -1193,7 +1212,7 @@ gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
11931212 page = BufferGetPage (buf );
11941213
11951214 /* Form the new downlink tuples to insert to parent */
1196- downlink = gistformdownlink (state -> r , buf , giststate , stack );
1215+ downlink = gistformdownlink (state -> r , buf , giststate , stack , state -> is_build );
11971216
11981217 si -> buf = buf ;
11991218 si -> downlink = downlink ;
@@ -1347,7 +1366,7 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack,
13471366 right = (GISTPageSplitInfo * ) list_nth (splitinfo , pos );
13481367 left = (GISTPageSplitInfo * ) list_nth (splitinfo , pos - 1 );
13491368
1350- gistFindCorrectParent (state -> r , stack );
1369+ gistFindCorrectParent (state -> r , stack , state -> is_build );
13511370 if (gistinserttuples (state , stack -> parent , giststate ,
13521371 & right -> downlink , 1 ,
13531372 InvalidOffsetNumber ,
@@ -1372,21 +1391,22 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack,
13721391 */
13731392 tuples [0 ] = left -> downlink ;
13741393 tuples [1 ] = right -> downlink ;
1375- gistFindCorrectParent (state -> r , stack );
1376- if (gistinserttuples (state , stack -> parent , giststate ,
1377- tuples , 2 ,
1378- stack -> downlinkoffnum ,
1379- left -> buf , right -> buf ,
1380- true, /* Unlock parent */
1381- unlockbuf /* Unlock stack->buffer if caller wants
1382- * that */
1383- ))
1384- {
1385- /*
1386- * If the parent page was split, the downlink might have moved.
1387- */
1388- stack -> downlinkoffnum = InvalidOffsetNumber ;
1389- }
1394+ gistFindCorrectParent (state -> r , stack , state -> is_build );
1395+ (void ) gistinserttuples (state , stack -> parent , giststate ,
1396+ tuples , 2 ,
1397+ stack -> downlinkoffnum ,
1398+ left -> buf , right -> buf ,
1399+ true, /* Unlock parent */
1400+ unlockbuf /* Unlock stack->buffer if caller
1401+ * wants that */
1402+ );
1403+
1404+ /*
1405+ * The downlink might have moved when we updated it. Even if the page
1406+ * wasn't split, because gistinserttuples() implements updating the old
1407+ * tuple by removing and re-inserting it!
1408+ */
1409+ stack -> downlinkoffnum = InvalidOffsetNumber ;
13901410
13911411 Assert (left -> buf == stack -> buffer );
13921412
0 commit comments