@@ -308,124 +308,114 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot,
308
308
309
309
310
310
/*
311
- * Scan through posting tree, delete empty tuples from leaf pages.
312
- * Also, this function collects empty subtrees (with all empty leafs).
313
- * For parents of these subtrees CleanUp lock is taken, then we call
314
- * ScanToDelete. This is done for every inner page, which points to
315
- * empty subtree.
311
+ * Scan through posting tree leafs, delete empty tuples. Returns true if there
312
+ * is at least one empty page.
316
313
*/
317
314
static bool
318
- ginVacuumPostingTreeLeaves (GinVacuumState * gvs , BlockNumber blkno , bool isRoot )
315
+ ginVacuumPostingTreeLeaves (GinVacuumState * gvs , BlockNumber blkno )
319
316
{
320
317
Buffer buffer ;
321
318
Page page ;
322
319
bool hasVoidPage = FALSE;
323
320
MemoryContext oldCxt ;
324
321
325
- buffer = ReadBufferExtended (gvs -> index , MAIN_FORKNUM , blkno ,
326
- RBM_NORMAL , gvs -> strategy );
327
- page = BufferGetPage (buffer );
322
+ /* Find leftmost leaf page of posting tree and lock it in exclusive mode */
323
+ while (true)
324
+ {
325
+ PostingItem * pitem ;
328
326
329
- ginTraverseLock (buffer , false);
327
+ buffer = ReadBufferExtended (gvs -> index , MAIN_FORKNUM , blkno ,
328
+ RBM_NORMAL , gvs -> strategy );
329
+ LockBuffer (buffer , GIN_SHARE );
330
+ page = BufferGetPage (buffer );
330
331
331
- Assert (GinPageIsData (page ));
332
+ Assert (GinPageIsData (page ));
332
333
333
- if (GinPageIsLeaf (page ))
334
+ if (GinPageIsLeaf (page ))
335
+ {
336
+ LockBuffer (buffer , GIN_UNLOCK );
337
+ LockBuffer (buffer , GIN_EXCLUSIVE );
338
+ break ;
339
+ }
340
+
341
+ Assert (PageGetMaxOffsetNumber (page ) >= FirstOffsetNumber );
342
+
343
+ pitem = GinDataPageGetPostingItem (page , FirstOffsetNumber );
344
+ blkno = PostingItemGetBlockNumber (pitem );
345
+ Assert (blkno != InvalidBlockNumber );
346
+
347
+ UnlockReleaseBuffer (buffer );
348
+ }
349
+
350
+ /* Iterate all posting tree leaves using rightlinks and vacuum them */
351
+ while (true)
334
352
{
335
353
oldCxt = MemoryContextSwitchTo (gvs -> tmpCxt );
336
354
ginVacuumPostingTreeLeaf (gvs -> index , buffer , gvs );
337
355
MemoryContextSwitchTo (oldCxt );
338
356
MemoryContextReset (gvs -> tmpCxt );
339
357
340
- /* if root is a leaf page, we don't desire further processing */
341
358
if (GinDataLeafPageIsEmpty (page ))
342
359
hasVoidPage = TRUE;
343
360
344
- UnlockReleaseBuffer (buffer );
345
-
346
- return hasVoidPage ;
347
- }
348
- else
349
- {
350
- OffsetNumber i ;
351
- bool hasEmptyChild = FALSE;
352
- bool hasNonEmptyChild = FALSE;
353
- OffsetNumber maxoff = GinPageGetOpaque (page )-> maxoff ;
354
- BlockNumber * children = palloc (sizeof (BlockNumber ) * (maxoff + 1 ));
355
-
356
- /*
357
- * Read all children BlockNumbers. Not sure it is safe if there are
358
- * many concurrent vacuums.
359
- */
360
-
361
- for (i = FirstOffsetNumber ; i <= maxoff ; i ++ )
362
- {
363
- PostingItem * pitem = GinDataPageGetPostingItem (page , i );
364
-
365
- children [i ] = PostingItemGetBlockNumber (pitem );
366
- }
361
+ blkno = GinPageGetOpaque (page )-> rightlink ;
367
362
368
363
UnlockReleaseBuffer (buffer );
369
364
370
- for (i = FirstOffsetNumber ; i <= maxoff ; i ++ )
371
- {
372
- if (ginVacuumPostingTreeLeaves (gvs , children [i ], FALSE))
373
- hasEmptyChild = TRUE;
374
- else
375
- hasNonEmptyChild = TRUE;
376
- }
365
+ if (blkno == InvalidBlockNumber )
366
+ break ;
377
367
378
- pfree (children );
368
+ buffer = ReadBufferExtended (gvs -> index , MAIN_FORKNUM , blkno ,
369
+ RBM_NORMAL , gvs -> strategy );
370
+ LockBuffer (buffer , GIN_EXCLUSIVE );
371
+ page = BufferGetPage (buffer );
372
+ }
379
373
380
- vacuum_delay_point ();
374
+ return hasVoidPage ;
375
+ }
381
376
377
+ static void
378
+ ginVacuumPostingTree (GinVacuumState * gvs , BlockNumber rootBlkno )
379
+ {
380
+ if (ginVacuumPostingTreeLeaves (gvs , rootBlkno ))
381
+ {
382
382
/*
383
- * All subtree is empty - just return TRUE to indicate that parent
384
- * must do a cleanup. Unless we are ROOT an there is way to go upper .
383
+ * There is at least one empty page. So we have to rescan the tree
384
+ * deleting empty pages .
385
385
*/
386
+ Buffer buffer ;
387
+ DataPageDeleteStack root ,
388
+ * ptr ,
389
+ * tmp ;
386
390
387
- if ( hasEmptyChild && ! hasNonEmptyChild && ! isRoot )
388
- return TRUE ;
391
+ buffer = ReadBufferExtended ( gvs -> index , MAIN_FORKNUM , rootBlkno ,
392
+ RBM_NORMAL , gvs -> strategy ) ;
389
393
390
- if (hasEmptyChild )
391
- {
392
- DataPageDeleteStack root ,
393
- * ptr ,
394
- * tmp ;
395
-
396
- buffer = ReadBufferExtended (gvs -> index , MAIN_FORKNUM , blkno ,
397
- RBM_NORMAL , gvs -> strategy );
398
- LockBufferForCleanup (buffer );
399
-
400
- memset (& root , 0 , sizeof (DataPageDeleteStack ));
401
- root .leftBlkno = InvalidBlockNumber ;
402
- root .isRoot = TRUE;
394
+ /*
395
+ * Lock posting tree root for cleanup to ensure there are no concurrent
396
+ * inserts.
397
+ */
398
+ LockBufferForCleanup (buffer );
403
399
404
- ginScanToDelete (gvs , blkno , TRUE, & root , InvalidOffsetNumber );
400
+ memset (& root , 0 , sizeof (DataPageDeleteStack ));
401
+ root .leftBlkno = InvalidBlockNumber ;
402
+ root .isRoot = true;
405
403
406
- ptr = root . child ;
404
+ ginScanToDelete ( gvs , rootBlkno , true, & root , InvalidOffsetNumber ) ;
407
405
408
- while (ptr )
409
- {
410
- tmp = ptr -> child ;
411
- pfree (ptr );
412
- ptr = tmp ;
413
- }
406
+ ptr = root .child ;
414
407
415
- UnlockReleaseBuffer (buffer );
408
+ while (ptr )
409
+ {
410
+ tmp = ptr -> child ;
411
+ pfree (ptr );
412
+ ptr = tmp ;
416
413
}
417
414
418
- /* Here we have deleted all empty subtrees */
419
- return FALSE;
415
+ UnlockReleaseBuffer (buffer );
420
416
}
421
417
}
422
418
423
- static void
424
- ginVacuumPostingTree (GinVacuumState * gvs , BlockNumber rootBlkno )
425
- {
426
- ginVacuumPostingTreeLeaves (gvs , rootBlkno , TRUE);
427
- }
428
-
429
419
/*
430
420
* returns modified page or NULL if page isn't modified.
431
421
* Function works with original page until first change is occurred,
0 commit comments