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