@@ -1011,97 +1011,114 @@ gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum)
1011
1011
* remain so at exit, but it might not be the same page anymore.
1012
1012
*/
1013
1013
static void
1014
- gistFindCorrectParent (Relation r , GISTInsertStack * child )
1014
+ gistFindCorrectParent (Relation r , GISTInsertStack * child , bool is_build )
1015
1015
{
1016
1016
GISTInsertStack * parent = child -> parent ;
1017
+ ItemId iid ;
1018
+ IndexTuple idxtuple ;
1019
+ OffsetNumber maxoff ;
1020
+ GISTInsertStack * ptr ;
1017
1021
1018
1022
gistcheckpage (r , parent -> buffer );
1019
1023
parent -> page = (Page ) BufferGetPage (parent -> buffer );
1024
+ maxoff = PageGetMaxOffsetNumber (parent -> page );
1020
1025
1021
- /* here we don't need to distinguish between split and page update */
1022
- if (child -> downlinkoffnum == InvalidOffsetNumber ||
1023
- parent -> lsn != PageGetLSN (parent -> page ))
1026
+ /* Check if the downlink is still where it was before */
1027
+ if (child -> downlinkoffnum != InvalidOffsetNumber && child -> downlinkoffnum <= maxoff )
1024
1028
{
1025
- /* parent is changed, look child in right links until found */
1026
- OffsetNumber i ,
1027
- maxoff ;
1028
- ItemId iid ;
1029
- IndexTuple idxtuple ;
1030
- GISTInsertStack * ptr ;
1029
+ iid = PageGetItemId (parent -> page , child -> downlinkoffnum );
1030
+ idxtuple = (IndexTuple ) PageGetItem (parent -> page , iid );
1031
+ if (ItemPointerGetBlockNumber (& (idxtuple -> t_tid )) == child -> blkno )
1032
+ return ; /* still there */
1033
+ }
1031
1034
1032
- while (true)
1033
- {
1034
- maxoff = PageGetMaxOffsetNumber (parent -> page );
1035
- for (i = FirstOffsetNumber ; i <= maxoff ; i = OffsetNumberNext (i ))
1036
- {
1037
- iid = PageGetItemId (parent -> page , i );
1038
- idxtuple = (IndexTuple ) PageGetItem (parent -> page , iid );
1039
- if (ItemPointerGetBlockNumber (& (idxtuple -> t_tid )) == child -> blkno )
1040
- {
1041
- /* yes!!, found */
1042
- child -> downlinkoffnum = i ;
1043
- return ;
1044
- }
1045
- }
1035
+ /*
1036
+ * The page has changed since we looked. During normal operation, every
1037
+ * update of a page changes its LSN, so the LSN we memorized should have
1038
+ * changed too. During index build, however, we don't WAL-log the changes
1039
+ * until we have built the index, so the LSN doesn't change. There is no
1040
+ * concurrent activity during index build, but we might have changed the
1041
+ * parent ourselves.
1042
+ */
1043
+ Assert (parent -> lsn != PageGetLSN (parent -> page ) || is_build );
1044
+
1045
+ /*
1046
+ * Scan the page to re-find the downlink. If the page was split, it might
1047
+ * have moved to a different page, so follow the right links until we find
1048
+ * it.
1049
+ */
1050
+ while (true)
1051
+ {
1052
+ OffsetNumber i ;
1046
1053
1047
- parent -> blkno = GistPageGetOpaque (parent -> page )-> rightlink ;
1048
- UnlockReleaseBuffer (parent -> buffer );
1049
- if (parent -> blkno == InvalidBlockNumber )
1054
+ maxoff = PageGetMaxOffsetNumber (parent -> page );
1055
+ for (i = FirstOffsetNumber ; i <= maxoff ; i = OffsetNumberNext (i ))
1056
+ {
1057
+ iid = PageGetItemId (parent -> page , i );
1058
+ idxtuple = (IndexTuple ) PageGetItem (parent -> page , iid );
1059
+ if (ItemPointerGetBlockNumber (& (idxtuple -> t_tid )) == child -> blkno )
1050
1060
{
1051
- /*
1052
- * End of chain and still didn't find parent. It's a very-very
1053
- * rare situation when root splited.
1054
- */
1055
- break ;
1061
+ /* yes!!, found */
1062
+ child -> downlinkoffnum = i ;
1063
+ return ;
1056
1064
}
1057
- parent -> buffer = ReadBuffer (r , parent -> blkno );
1058
- LockBuffer (parent -> buffer , GIST_EXCLUSIVE );
1059
- gistcheckpage (r , parent -> buffer );
1060
- parent -> page = (Page ) BufferGetPage (parent -> buffer );
1061
1065
}
1062
1066
1063
- /*
1064
- * awful!!, we need search tree to find parent ... , but before we
1065
- * should release all old parent
1066
- */
1067
-
1068
- ptr = child -> parent -> parent ; /* child->parent already released
1069
- * above */
1070
- while (ptr )
1067
+ parent -> blkno = GistPageGetOpaque (parent -> page )-> rightlink ;
1068
+ parent -> downlinkoffnum = InvalidOffsetNumber ;
1069
+ UnlockReleaseBuffer (parent -> buffer );
1070
+ if (parent -> blkno == InvalidBlockNumber )
1071
1071
{
1072
- ReleaseBuffer (ptr -> buffer );
1073
- ptr = ptr -> parent ;
1072
+ /*
1073
+ * End of chain and still didn't find parent. It's a very-very
1074
+ * rare situation when root splitted.
1075
+ */
1076
+ break ;
1074
1077
}
1078
+ parent -> buffer = ReadBuffer (r , parent -> blkno );
1079
+ LockBuffer (parent -> buffer , GIST_EXCLUSIVE );
1080
+ gistcheckpage (r , parent -> buffer );
1081
+ parent -> page = (Page ) BufferGetPage (parent -> buffer );
1082
+ }
1075
1083
1076
- /* ok, find new path */
1077
- ptr = parent = gistFindPath (r , child -> blkno , & child -> downlinkoffnum );
1084
+ /*
1085
+ * awful!!, we need search tree to find parent ... , but before we should
1086
+ * release all old parent
1087
+ */
1078
1088
1079
- /* read all buffers as expected by caller */
1080
- /* note we don't lock them or gistcheckpage them here! */
1081
- while (ptr )
1082
- {
1083
- ptr -> buffer = ReadBuffer (r , ptr -> blkno );
1084
- ptr -> page = (Page ) BufferGetPage (ptr -> buffer );
1085
- ptr = ptr -> parent ;
1086
- }
1089
+ ptr = child -> parent -> parent ; /* child->parent already released above */
1090
+ while (ptr )
1091
+ {
1092
+ ReleaseBuffer (ptr -> buffer );
1093
+ ptr = ptr -> parent ;
1094
+ }
1087
1095
1088
- /* install new chain of parents to stack */
1089
- child -> parent = parent ;
1096
+ /* ok, find new path */
1097
+ ptr = parent = gistFindPath ( r , child -> blkno , & child -> downlinkoffnum ) ;
1090
1098
1091
- /* make recursive call to normal processing */
1092
- LockBuffer (child -> parent -> buffer , GIST_EXCLUSIVE );
1093
- gistFindCorrectParent (r , child );
1099
+ /* read all buffers as expected by caller */
1100
+ /* note we don't lock them or gistcheckpage them here! */
1101
+ while (ptr )
1102
+ {
1103
+ ptr -> buffer = ReadBuffer (r , ptr -> blkno );
1104
+ ptr -> page = (Page ) BufferGetPage (ptr -> buffer );
1105
+ ptr = ptr -> parent ;
1094
1106
}
1095
1107
1096
- return ;
1108
+ /* install new chain of parents to stack */
1109
+ child -> parent = parent ;
1110
+
1111
+ /* make recursive call to normal processing */
1112
+ LockBuffer (child -> parent -> buffer , GIST_EXCLUSIVE );
1113
+ gistFindCorrectParent (r , child , is_build );
1097
1114
}
1098
1115
1099
1116
/*
1100
1117
* Form a downlink pointer for the page in 'buf'.
1101
1118
*/
1102
1119
static IndexTuple
1103
1120
gistformdownlink (Relation rel , Buffer buf , GISTSTATE * giststate ,
1104
- GISTInsertStack * stack )
1121
+ GISTInsertStack * stack , bool is_build )
1105
1122
{
1106
1123
Page page = BufferGetPage (buf );
1107
1124
OffsetNumber maxoff ;
@@ -1142,7 +1159,7 @@ gistformdownlink(Relation rel, Buffer buf, GISTSTATE *giststate,
1142
1159
ItemId iid ;
1143
1160
1144
1161
LockBuffer (stack -> parent -> buffer , GIST_EXCLUSIVE );
1145
- gistFindCorrectParent (rel , stack );
1162
+ gistFindCorrectParent (rel , stack , is_build );
1146
1163
iid = PageGetItemId (stack -> parent -> page , stack -> downlinkoffnum );
1147
1164
downlink = (IndexTuple ) PageGetItem (stack -> parent -> page , iid );
1148
1165
downlink = CopyIndexTuple (downlink );
@@ -1187,7 +1204,7 @@ gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
1187
1204
page = BufferGetPage (buf );
1188
1205
1189
1206
/* Form the new downlink tuples to insert to parent */
1190
- downlink = gistformdownlink (state -> r , buf , giststate , stack );
1207
+ downlink = gistformdownlink (state -> r , buf , giststate , stack , state -> is_build );
1191
1208
1192
1209
si -> buf = buf ;
1193
1210
si -> downlink = downlink ;
@@ -1351,7 +1368,7 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack,
1351
1368
right = (GISTPageSplitInfo * ) linitial (reversed );
1352
1369
left = (GISTPageSplitInfo * ) lsecond (reversed );
1353
1370
1354
- gistFindCorrectParent (state -> r , stack );
1371
+ gistFindCorrectParent (state -> r , stack , state -> is_build );
1355
1372
if (gistinserttuples (state , stack -> parent , giststate ,
1356
1373
& right -> downlink , 1 ,
1357
1374
InvalidOffsetNumber ,
@@ -1377,20 +1394,22 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack,
1377
1394
*/
1378
1395
tuples [0 ] = left -> downlink ;
1379
1396
tuples [1 ] = right -> downlink ;
1380
- gistFindCorrectParent (state -> r , stack );
1381
- if (gistinserttuples (state , stack -> parent , giststate ,
1382
- tuples , 2 ,
1383
- stack -> downlinkoffnum ,
1384
- left -> buf , right -> buf ,
1385
- true, /* Unlock parent */
1386
- unlockbuf /* Unlock stack->buffer if caller wants that */
1387
- ))
1388
- {
1389
- /*
1390
- * If the parent page was split, the downlink might have moved.
1391
- */
1392
- stack -> downlinkoffnum = InvalidOffsetNumber ;
1393
- }
1397
+ gistFindCorrectParent (state -> r , stack , state -> is_build );
1398
+ (void ) gistinserttuples (state , stack -> parent , giststate ,
1399
+ tuples , 2 ,
1400
+ stack -> downlinkoffnum ,
1401
+ left -> buf , right -> buf ,
1402
+ true, /* Unlock parent */
1403
+ unlockbuf /* Unlock stack->buffer if caller
1404
+ * wants that */
1405
+ );
1406
+
1407
+ /*
1408
+ * The downlink might have moved when we updated it. Even if the page
1409
+ * wasn't split, because gistinserttuples() implements updating the old
1410
+ * tuple by removing and re-inserting it!
1411
+ */
1412
+ stack -> downlinkoffnum = InvalidOffsetNumber ;
1394
1413
1395
1414
Assert (left -> buf == stack -> buffer );
1396
1415
0 commit comments