@@ -1018,95 +1018,114 @@ gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum)
1018
1018
* remain so at exit, but it might not be the same page anymore.
1019
1019
*/
1020
1020
static void
1021
- gistFindCorrectParent (Relation r , GISTInsertStack * child )
1021
+ gistFindCorrectParent (Relation r , GISTInsertStack * child , bool is_build )
1022
1022
{
1023
1023
GISTInsertStack * parent = child -> parent ;
1024
+ ItemId iid ;
1025
+ IndexTuple idxtuple ;
1026
+ OffsetNumber maxoff ;
1027
+ GISTInsertStack * ptr ;
1024
1028
1025
1029
gistcheckpage (r , parent -> buffer );
1026
1030
parent -> page = (Page ) BufferGetPage (parent -> buffer );
1031
+ maxoff = PageGetMaxOffsetNumber (parent -> page );
1027
1032
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 )
1031
1035
{
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
+ }
1038
1041
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 ;
1053
1060
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 )
1057
1067
{
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 ;
1063
1071
}
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 );
1068
1072
}
1069
1073
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 )
1078
1078
{
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 ;
1081
1084
}
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
+ }
1082
1090
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
+ */
1085
1095
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
+ }
1094
1102
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 ) ;
1097
1105
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 ;
1101
1113
}
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 );
1102
1121
}
1103
1122
1104
1123
/*
1105
1124
* Form a downlink pointer for the page in 'buf'.
1106
1125
*/
1107
1126
static IndexTuple
1108
1127
gistformdownlink (Relation rel , Buffer buf , GISTSTATE * giststate ,
1109
- GISTInsertStack * stack )
1128
+ GISTInsertStack * stack , bool is_build )
1110
1129
{
1111
1130
Page page = BufferGetPage (buf );
1112
1131
OffsetNumber maxoff ;
@@ -1147,7 +1166,7 @@ gistformdownlink(Relation rel, Buffer buf, GISTSTATE *giststate,
1147
1166
ItemId iid ;
1148
1167
1149
1168
LockBuffer (stack -> parent -> buffer , GIST_EXCLUSIVE );
1150
- gistFindCorrectParent (rel , stack );
1169
+ gistFindCorrectParent (rel , stack , is_build );
1151
1170
iid = PageGetItemId (stack -> parent -> page , stack -> downlinkoffnum );
1152
1171
downlink = (IndexTuple ) PageGetItem (stack -> parent -> page , iid );
1153
1172
downlink = CopyIndexTuple (downlink );
@@ -1193,7 +1212,7 @@ gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
1193
1212
page = BufferGetPage (buf );
1194
1213
1195
1214
/* 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 );
1197
1216
1198
1217
si -> buf = buf ;
1199
1218
si -> downlink = downlink ;
@@ -1347,7 +1366,7 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack,
1347
1366
right = (GISTPageSplitInfo * ) list_nth (splitinfo , pos );
1348
1367
left = (GISTPageSplitInfo * ) list_nth (splitinfo , pos - 1 );
1349
1368
1350
- gistFindCorrectParent (state -> r , stack );
1369
+ gistFindCorrectParent (state -> r , stack , state -> is_build );
1351
1370
if (gistinserttuples (state , stack -> parent , giststate ,
1352
1371
& right -> downlink , 1 ,
1353
1372
InvalidOffsetNumber ,
@@ -1372,21 +1391,22 @@ gistfinishsplit(GISTInsertState *state, GISTInsertStack *stack,
1372
1391
*/
1373
1392
tuples [0 ] = left -> downlink ;
1374
1393
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 ;
1390
1410
1391
1411
Assert (left -> buf == stack -> buffer );
1392
1412
0 commit comments