PostgreSQL Source Code  git master
ginvacuum.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * ginvacuum.c
4  * delete & vacuum routines for the postgres GIN
5  *
6  *
7  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  * src/backend/access/gin/ginvacuum.c
12  *-------------------------------------------------------------------------
13  */
14 
15 #include "postgres.h"
16 
17 #include "access/gin_private.h"
18 #include "access/ginxlog.h"
19 #include "access/xloginsert.h"
20 #include "commands/vacuum.h"
21 #include "miscadmin.h"
22 #include "postmaster/autovacuum.h"
23 #include "storage/indexfsm.h"
24 #include "storage/lmgr.h"
25 #include "storage/predicate.h"
26 #include "utils/memutils.h"
27 
29 {
37 };
38 
39 /*
40  * Vacuums an uncompressed posting list. The size of the must can be specified
41  * in number of items (nitems).
42  *
43  * If none of the items need to be removed, returns NULL. Otherwise returns
44  * a new palloc'd array with the remaining items. The number of remaining
45  * items is returned in *nremaining.
46  */
49  int nitem, int *nremaining)
50 {
51  int i,
52  remaining = 0;
53  ItemPointer tmpitems = NULL;
54 
55  /*
56  * Iterate over TIDs array
57  */
58  for (i = 0; i < nitem; i++)
59  {
60  if (gvs->callback(items + i, gvs->callback_state))
61  {
62  gvs->result->tuples_removed += 1;
63  if (!tmpitems)
64  {
65  /*
66  * First TID to be deleted: allocate memory to hold the
67  * remaining items.
68  */
69  tmpitems = palloc(sizeof(ItemPointerData) * nitem);
70  memcpy(tmpitems, items, sizeof(ItemPointerData) * i);
71  }
72  }
73  else
74  {
75  gvs->result->num_index_tuples += 1;
76  if (tmpitems)
77  tmpitems[remaining] = items[i];
78  remaining++;
79  }
80  }
81 
82  *nremaining = remaining;
83  return tmpitems;
84 }
85 
86 /*
87  * Create a WAL record for vacuuming entry tree leaf page.
88  */
89 static void
91 {
92  Page page = BufferGetPage(buffer);
93  XLogRecPtr recptr;
94 
95  /* This is only used for entry tree leaf pages. */
96  Assert(!GinPageIsData(page));
97  Assert(GinPageIsLeaf(page));
98 
99  if (!RelationNeedsWAL(index))
100  return;
101 
102  /*
103  * Always create a full image, we don't track the changes on the page at
104  * any more fine-grained level. This could obviously be improved...
105  */
106  XLogBeginInsert();
108 
109  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_VACUUM_PAGE);
110  PageSetLSN(page, recptr);
111 }
112 
113 
114 typedef struct DataPageDeleteStack
115 {
118 
119  BlockNumber blkno; /* current block number */
120  Buffer leftBuffer; /* pinned and locked rightest non-deleted page
121  * on left */
122  bool isRoot;
124 
125 
126 /*
127  * Delete a posting tree page.
128  */
129 static void
131  BlockNumber parentBlkno, OffsetNumber myoff, bool isParentRoot)
132 {
133  Buffer dBuffer;
134  Buffer lBuffer;
135  Buffer pBuffer;
136  Page page,
137  parentPage;
138  BlockNumber rightlink;
139 
140  /*
141  * This function MUST be called only if someone of parent pages hold
142  * exclusive cleanup lock. This guarantees that no insertions currently
143  * happen in this subtree. Caller also acquires Exclusive locks on
144  * deletable, parent and left pages.
145  */
146  lBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, leftBlkno,
147  RBM_NORMAL, gvs->strategy);
148  dBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, deleteBlkno,
149  RBM_NORMAL, gvs->strategy);
150  pBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, parentBlkno,
151  RBM_NORMAL, gvs->strategy);
152 
153  page = BufferGetPage(dBuffer);
154  rightlink = GinPageGetOpaque(page)->rightlink;
155 
156  /*
157  * Any insert which would have gone on the leaf block will now go to its
158  * right sibling.
159  */
160  PredicateLockPageCombine(gvs->index, deleteBlkno, rightlink);
161 
163 
164  /* Unlink the page by changing left sibling's rightlink */
165  page = BufferGetPage(lBuffer);
166  GinPageGetOpaque(page)->rightlink = rightlink;
167 
168  /* Delete downlink from parent */
169  parentPage = BufferGetPage(pBuffer);
170 #ifdef USE_ASSERT_CHECKING
171  do
172  {
173  PostingItem *tod = GinDataPageGetPostingItem(parentPage, myoff);
174 
175  Assert(PostingItemGetBlockNumber(tod) == deleteBlkno);
176  } while (0);
177 #endif
178  GinPageDeletePostingItem(parentPage, myoff);
179 
180  page = BufferGetPage(dBuffer);
181 
182  /*
183  * we shouldn't change rightlink field to save workability of running
184  * search scan
185  */
186 
187  /*
188  * Mark page as deleted, and remember last xid which could know its
189  * address.
190  */
191  GinPageSetDeleted(page);
193 
194  MarkBufferDirty(pBuffer);
195  MarkBufferDirty(lBuffer);
196  MarkBufferDirty(dBuffer);
197 
198  if (RelationNeedsWAL(gvs->index))
199  {
200  XLogRecPtr recptr;
202 
203  /*
204  * We can't pass REGBUF_STANDARD for the deleted page, because we
205  * didn't set pd_lower on pre-9.4 versions. The page might've been
206  * binary-upgraded from an older version, and hence not have pd_lower
207  * set correctly. Ditto for the left page, but removing the item from
208  * the parent updated its pd_lower, so we know that's OK at this
209  * point.
210  */
211  XLogBeginInsert();
212  XLogRegisterBuffer(0, dBuffer, 0);
213  XLogRegisterBuffer(1, pBuffer, REGBUF_STANDARD);
214  XLogRegisterBuffer(2, lBuffer, 0);
215 
216  data.parentOffset = myoff;
217  data.rightLink = GinPageGetOpaque(page)->rightlink;
218  data.deleteXid = GinPageGetDeleteXid(page);
219 
220  XLogRegisterData((char *) &data, sizeof(ginxlogDeletePage));
221 
222  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE);
223  PageSetLSN(page, recptr);
224  PageSetLSN(parentPage, recptr);
225  PageSetLSN(BufferGetPage(lBuffer), recptr);
226  }
227 
228  ReleaseBuffer(pBuffer);
229  ReleaseBuffer(lBuffer);
230  ReleaseBuffer(dBuffer);
231 
233 
234  gvs->result->pages_newly_deleted++;
235  gvs->result->pages_deleted++;
236 }
237 
238 
239 /*
240  * Scans posting tree and deletes empty pages. Caller must lock root page for
241  * cleanup. During scan path from root to current page is kept exclusively
242  * locked. Also keep left page exclusively locked, because ginDeletePage()
243  * needs it. If we try to relock left page later, it could deadlock with
244  * ginStepRight().
245  */
246 static bool
249 {
251  Buffer buffer;
252  Page page;
253  bool meDelete = false;
254  bool isempty;
255 
256  if (isRoot)
257  {
258  me = parent;
259  }
260  else
261  {
262  if (!parent->child)
263  {
265  me->parent = parent;
266  parent->child = me;
268  }
269  else
270  me = parent->child;
271  }
272 
273  buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
274  RBM_NORMAL, gvs->strategy);
275 
276  if (!isRoot)
277  LockBuffer(buffer, GIN_EXCLUSIVE);
278 
279  page = BufferGetPage(buffer);
280 
281  Assert(GinPageIsData(page));
282 
283  if (!GinPageIsLeaf(page))
284  {
285  OffsetNumber i;
286 
287  me->blkno = blkno;
288  for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
289  {
290  PostingItem *pitem = GinDataPageGetPostingItem(page, i);
291 
292  if (ginScanToDelete(gvs, PostingItemGetBlockNumber(pitem), false, me, i))
293  i--;
294  }
295 
296  if (GinPageRightMost(page) && BufferIsValid(me->child->leftBuffer))
297  {
300  }
301  }
302 
303  if (GinPageIsLeaf(page))
304  isempty = GinDataLeafPageIsEmpty(page);
305  else
306  isempty = GinPageGetOpaque(page)->maxoff < FirstOffsetNumber;
307 
308  if (isempty)
309  {
310  /* we never delete the left- or rightmost branch */
311  if (BufferIsValid(me->leftBuffer) && !GinPageRightMost(page))
312  {
313  Assert(!isRoot);
315  me->parent->blkno, myoff, me->parent->isRoot);
316  meDelete = true;
317  }
318  }
319 
320  if (!meDelete)
321  {
322  if (BufferIsValid(me->leftBuffer))
324  me->leftBuffer = buffer;
325  }
326  else
327  {
328  if (!isRoot)
329  LockBuffer(buffer, GIN_UNLOCK);
330 
331  ReleaseBuffer(buffer);
332  }
333 
334  if (isRoot)
335  ReleaseBuffer(buffer);
336 
337  return meDelete;
338 }
339 
340 
341 /*
342  * Scan through posting tree leafs, delete empty tuples. Returns true if there
343  * is at least one empty page.
344  */
345 static bool
347 {
348  Buffer buffer;
349  Page page;
350  bool hasVoidPage = false;
351  MemoryContext oldCxt;
352 
353  /* Find leftmost leaf page of posting tree and lock it in exclusive mode */
354  while (true)
355  {
356  PostingItem *pitem;
357 
358  buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
359  RBM_NORMAL, gvs->strategy);
360  LockBuffer(buffer, GIN_SHARE);
361  page = BufferGetPage(buffer);
362 
363  Assert(GinPageIsData(page));
364 
365  if (GinPageIsLeaf(page))
366  {
367  LockBuffer(buffer, GIN_UNLOCK);
368  LockBuffer(buffer, GIN_EXCLUSIVE);
369  break;
370  }
371 
373 
377 
378  UnlockReleaseBuffer(buffer);
379  }
380 
381  /* Iterate all posting tree leaves using rightlinks and vacuum them */
382  while (true)
383  {
384  oldCxt = MemoryContextSwitchTo(gvs->tmpCxt);
385  ginVacuumPostingTreeLeaf(gvs->index, buffer, gvs);
386  MemoryContextSwitchTo(oldCxt);
388 
389  if (GinDataLeafPageIsEmpty(page))
390  hasVoidPage = true;
391 
392  blkno = GinPageGetOpaque(page)->rightlink;
393 
394  UnlockReleaseBuffer(buffer);
395 
396  if (blkno == InvalidBlockNumber)
397  break;
398 
399  buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
400  RBM_NORMAL, gvs->strategy);
401  LockBuffer(buffer, GIN_EXCLUSIVE);
402  page = BufferGetPage(buffer);
403  }
404 
405  return hasVoidPage;
406 }
407 
408 static void
410 {
411  if (ginVacuumPostingTreeLeaves(gvs, rootBlkno))
412  {
413  /*
414  * There is at least one empty page. So we have to rescan the tree
415  * deleting empty pages.
416  */
417  Buffer buffer;
419  *ptr,
420  *tmp;
421 
422  buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, rootBlkno,
423  RBM_NORMAL, gvs->strategy);
424 
425  /*
426  * Lock posting tree root for cleanup to ensure there are no
427  * concurrent inserts.
428  */
429  LockBufferForCleanup(buffer);
430 
431  memset(&root, 0, sizeof(DataPageDeleteStack));
432  root.leftBuffer = InvalidBuffer;
433  root.isRoot = true;
434 
435  ginScanToDelete(gvs, rootBlkno, true, &root, InvalidOffsetNumber);
436 
437  ptr = root.child;
438 
439  while (ptr)
440  {
441  tmp = ptr->child;
442  pfree(ptr);
443  ptr = tmp;
444  }
445 
446  UnlockReleaseBuffer(buffer);
447  }
448 }
449 
450 /*
451  * returns modified page or NULL if page isn't modified.
452  * Function works with original page until first change is occurred,
453  * then page is copied into temporary one.
454  */
455 static Page
457 {
458  Page origpage = BufferGetPage(buffer),
459  tmppage;
460  OffsetNumber i,
461  maxoff = PageGetMaxOffsetNumber(origpage);
462 
463  tmppage = origpage;
464 
465  *nroot = 0;
466 
467  for (i = FirstOffsetNumber; i <= maxoff; i++)
468  {
469  IndexTuple itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
470 
471  if (GinIsPostingTree(itup))
472  {
473  /*
474  * store posting tree's roots for further processing, we can't
475  * vacuum it just now due to risk of deadlocks with scans/inserts
476  */
477  roots[*nroot] = GinGetDownlink(itup);
478  (*nroot)++;
479  }
480  else if (GinGetNPosting(itup) > 0)
481  {
482  int nitems;
483  ItemPointer items_orig;
484  bool free_items_orig;
486 
487  /* Get list of item pointers from the tuple. */
488  if (GinItupIsCompressed(itup))
489  {
490  items_orig = ginPostingListDecode((GinPostingList *) GinGetPosting(itup), &nitems);
491  free_items_orig = true;
492  }
493  else
494  {
495  items_orig = (ItemPointer) GinGetPosting(itup);
496  nitems = GinGetNPosting(itup);
497  free_items_orig = false;
498  }
499 
500  /* Remove any items from the list that need to be vacuumed. */
501  items = ginVacuumItemPointers(gvs, items_orig, nitems, &nitems);
502 
503  if (free_items_orig)
504  pfree(items_orig);
505 
506  /* If any item pointers were removed, recreate the tuple. */
507  if (items)
508  {
510  Datum key;
511  GinNullCategory category;
512  GinPostingList *plist;
513  int plistsize;
514 
515  if (nitems > 0)
516  {
518  plistsize = SizeOfGinPostingList(plist);
519  }
520  else
521  {
522  plist = NULL;
523  plistsize = 0;
524  }
525 
526  /*
527  * if we already created a temporary page, make changes in
528  * place
529  */
530  if (tmppage == origpage)
531  {
532  /*
533  * On first difference, create a temporary copy of the
534  * page and copy the tuple's posting list to it.
535  */
536  tmppage = PageGetTempPageCopy(origpage);
537 
538  /* set itup pointer to new page */
539  itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
540  }
541 
542  attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
543  key = gintuple_get_key(&gvs->ginstate, itup, &category);
544  itup = GinFormTuple(&gvs->ginstate, attnum, key, category,
545  (char *) plist, plistsize,
546  nitems, true);
547  if (plist)
548  pfree(plist);
549  PageIndexTupleDelete(tmppage, i);
550 
551  if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)
552  elog(ERROR, "failed to add item to index page in \"%s\"",
554 
555  pfree(itup);
556  pfree(items);
557  }
558  }
559  }
560 
561  return (tmppage == origpage) ? NULL : tmppage;
562 }
563 
566  IndexBulkDeleteCallback callback, void *callback_state)
567 {
568  Relation index = info->index;
570  GinVacuumState gvs;
571  Buffer buffer;
572  BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
573  uint32 nRoot;
574 
576  "Gin vacuum temporary context",
578  gvs.index = index;
579  gvs.callback = callback;
580  gvs.callback_state = callback_state;
581  gvs.strategy = info->strategy;
582  initGinState(&gvs.ginstate, index);
583 
584  /* first time through? */
585  if (stats == NULL)
586  {
587  /* Yes, so initialize stats to zeroes */
589 
590  /*
591  * and cleanup any pending inserts
592  */
594  false, true, stats);
595  }
596 
597  /* we'll re-count the tuples each time */
598  stats->num_index_tuples = 0;
599  gvs.result = stats;
600 
602  RBM_NORMAL, info->strategy);
603 
604  /* find leaf page */
605  for (;;)
606  {
607  Page page = BufferGetPage(buffer);
608  IndexTuple itup;
609 
610  LockBuffer(buffer, GIN_SHARE);
611 
612  Assert(!GinPageIsData(page));
613 
614  if (GinPageIsLeaf(page))
615  {
616  LockBuffer(buffer, GIN_UNLOCK);
617  LockBuffer(buffer, GIN_EXCLUSIVE);
618 
619  if (blkno == GIN_ROOT_BLKNO && !GinPageIsLeaf(page))
620  {
621  LockBuffer(buffer, GIN_UNLOCK);
622  continue; /* check it one more */
623  }
624  break;
625  }
626 
628 
629  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
630  blkno = GinGetDownlink(itup);
632 
633  UnlockReleaseBuffer(buffer);
635  RBM_NORMAL, info->strategy);
636  }
637 
638  /* right now we found leftmost page in entry's BTree */
639 
640  for (;;)
641  {
642  Page page = BufferGetPage(buffer);
643  Page resPage;
644  uint32 i;
645 
646  Assert(!GinPageIsData(page));
647 
648  resPage = ginVacuumEntryPage(&gvs, buffer, rootOfPostingTree, &nRoot);
649 
650  blkno = GinPageGetOpaque(page)->rightlink;
651 
652  if (resPage)
653  {
655  PageRestoreTempPage(resPage, page);
656  MarkBufferDirty(buffer);
657  xlogVacuumPage(gvs.index, buffer);
658  UnlockReleaseBuffer(buffer);
660  }
661  else
662  {
663  UnlockReleaseBuffer(buffer);
664  }
665 
667 
668  for (i = 0; i < nRoot; i++)
669  {
670  ginVacuumPostingTree(&gvs, rootOfPostingTree[i]);
672  }
673 
674  if (blkno == InvalidBlockNumber) /* rightmost page */
675  break;
676 
678  RBM_NORMAL, info->strategy);
679  LockBuffer(buffer, GIN_EXCLUSIVE);
680  }
681 
683 
684  return gvs.result;
685 }
686 
689 {
690  Relation index = info->index;
691  bool needLock;
692  BlockNumber npages,
693  blkno;
694  BlockNumber totFreePages;
695  GinState ginstate;
696  GinStatsData idxStat;
697 
698  /*
699  * In an autovacuum analyze, we want to clean up pending insertions.
700  * Otherwise, an ANALYZE-only call is a no-op.
701  */
702  if (info->analyze_only)
703  {
705  {
706  initGinState(&ginstate, index);
707  ginInsertCleanup(&ginstate, false, true, true, stats);
708  }
709  return stats;
710  }
711 
712  /*
713  * Set up all-zero stats and cleanup pending inserts if ginbulkdelete
714  * wasn't called
715  */
716  if (stats == NULL)
717  {
719  initGinState(&ginstate, index);
721  false, true, stats);
722  }
723 
724  memset(&idxStat, 0, sizeof(idxStat));
725 
726  /*
727  * XXX we always report the heap tuple count as the number of index
728  * entries. This is bogus if the index is partial, but it's real hard to
729  * tell how many distinct heap entries are referenced by a GIN index.
730  */
731  stats->num_index_tuples = Max(info->num_heap_tuples, 0);
732  stats->estimated_count = info->estimated_count;
733 
734  /*
735  * Need lock unless it's local to this backend.
736  */
737  needLock = !RELATION_IS_LOCAL(index);
738 
739  if (needLock)
742  if (needLock)
744 
745  totFreePages = 0;
746 
747  for (blkno = GIN_ROOT_BLKNO; blkno < npages; blkno++)
748  {
749  Buffer buffer;
750  Page page;
751 
753 
755  RBM_NORMAL, info->strategy);
756  LockBuffer(buffer, GIN_SHARE);
757  page = (Page) BufferGetPage(buffer);
758 
759  if (GinPageIsRecyclable(page))
760  {
763  totFreePages++;
764  }
765  else if (GinPageIsData(page))
766  {
767  idxStat.nDataPages++;
768  }
769  else if (!GinPageIsList(page))
770  {
771  idxStat.nEntryPages++;
772 
773  if (GinPageIsLeaf(page))
774  idxStat.nEntries += PageGetMaxOffsetNumber(page);
775  }
776 
777  UnlockReleaseBuffer(buffer);
778  }
779 
780  /* Update the metapage with accurate page and entry counts */
781  idxStat.nTotalPages = npages;
782  ginUpdateStats(info->index, &idxStat, false);
783 
784  /* Finally, vacuum the FSM */
786 
787  stats->pages_free = totFreePages;
788 
789  if (needLock)
792  if (needLock)
794 
795  return stats;
796 }
797 
798 /*
799  * Return whether Page can safely be recycled.
800  */
801 bool
803 {
804  TransactionId delete_xid;
805 
806  if (PageIsNew(page))
807  return true;
808 
809  if (!GinPageIsDeleted(page))
810  return false;
811 
812  delete_xid = GinPageGetDeleteXid(page);
813 
814  if (!TransactionIdIsValid(delete_xid))
815  return true;
816 
817  /*
818  * If no backend still could view delete_xid as in running, all scans
819  * concurrent with ginDeletePage() must have finished.
820  */
821  return GlobalVisCheckRemovableXid(NULL, delete_xid);
822 }
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
int Buffer
Definition: buf.h:23
#define InvalidBuffer
Definition: buf.h:25
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3667
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4850
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4867
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2474
void LockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:5165
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5085
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:792
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:281
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:408
@ RBM_NORMAL
Definition: bufmgr.h:45
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:359
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:424
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:1052
Page PageGetTempPageCopy(Page page)
Definition: bufpage.c:382
Pointer Page
Definition: bufpage.h:78
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:351
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:240
static bool PageIsNew(Page page)
Definition: bufpage.h:230
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:388
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:369
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:468
unsigned int uint32
Definition: c.h:506
#define Max(x, y)
Definition: c.h:998
#define Assert(condition)
Definition: c.h:858
uint32 TransactionId
Definition: c.h:652
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
bool(* IndexBulkDeleteCallback)(ItemPointer itemptr, void *state)
Definition: genam.h:87
#define GIN_UNLOCK
Definition: gin_private.h:49
#define GIN_EXCLUSIVE
Definition: gin_private.h:51
#define GIN_SHARE
Definition: gin_private.h:50
#define GinPageGetDeleteXid(page)
Definition: ginblock.h:135
#define GinIsPostingTree(itup)
Definition: ginblock.h:231
#define GinPageGetOpaque(page)
Definition: ginblock.h:110
#define GinGetPosting(itup)
Definition: ginblock.h:238
#define SizeOfGinPostingList(plist)
Definition: ginblock.h:342
#define GinPageSetDeleteXid(page, xid)
Definition: ginblock.h:136
#define GinPageIsList(page)
Definition: ginblock.h:117
#define GIN_ROOT_BLKNO
Definition: ginblock.h:52
#define GinGetDownlink(itup)
Definition: ginblock.h:257
#define GinItupIsCompressed(itup)
Definition: ginblock.h:239
#define GinGetNPosting(itup)
Definition: ginblock.h:228
#define GinPageIsData(page)
Definition: ginblock.h:115
signed char GinNullCategory
Definition: ginblock.h:206
#define GinPageRightMost(page)
Definition: ginblock.h:129
#define GinDataPageGetPostingItem(page, i)
Definition: ginblock.h:298
#define PostingItemGetBlockNumber(pointer)
Definition: ginblock.h:189
#define GinDataLeafPageIsEmpty(page)
Definition: ginblock.h:283
#define GinPageIsDeleted(page)
Definition: ginblock.h:124
#define GinPageSetDeleted(page)
Definition: ginblock.h:125
#define GinMaxItemSize
Definition: ginblock.h:248
#define GinPageIsLeaf(page)
Definition: ginblock.h:112
void GinPageDeletePostingItem(Page page, OffsetNumber offset)
Definition: gindatapage.c:417
void ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer, GinVacuumState *gvs)
Definition: gindatapage.c:738
IndexTuple GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, Pointer data, Size dataSize, int nipd, bool errorTooBig)
Definition: ginentrypage.c:44
void ginInsertCleanup(GinState *ginstate, bool full_clean, bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats)
Definition: ginfast.c:780
GinPostingList * ginCompressPostingList(const ItemPointer ipd, int nipd, int maxsize, int *nwritten)
ItemPointer ginPostingListDecode(GinPostingList *plist, int *ndecoded_out)
OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
Definition: ginutil.c:226
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Definition: ginutil.c:259
void initGinState(GinState *state, Relation index)
Definition: ginutil.c:97
void ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
Definition: ginutil.c:650
struct DataPageDeleteStack DataPageDeleteStack
IndexBulkDeleteResult * ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: ginvacuum.c:565
static bool ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno)
Definition: ginvacuum.c:346
static void xlogVacuumPage(Relation index, Buffer buffer)
Definition: ginvacuum.c:90
ItemPointer ginVacuumItemPointers(GinVacuumState *gvs, ItemPointerData *items, int nitem, int *nremaining)
Definition: ginvacuum.c:48
bool GinPageIsRecyclable(Page page)
Definition: ginvacuum.c:802
static void ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno)
Definition: ginvacuum.c:409
IndexBulkDeleteResult * ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: ginvacuum.c:688
static void ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkno, BlockNumber parentBlkno, OffsetNumber myoff, bool isParentRoot)
Definition: ginvacuum.c:130
static Page ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint32 *nroot)
Definition: ginvacuum.c:456
static bool ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDeleteStack *parent, OffsetNumber myoff)
Definition: ginvacuum.c:247
#define XLOG_GIN_VACUUM_PAGE
Definition: ginxlog.h:135
#define XLOG_GIN_DELETE_PAGE
Definition: ginxlog.h:153
#define nitems(x)
Definition: indent.h:31
void IndexFreeSpaceMapVacuum(Relation rel)
Definition: indexfsm.c:71
void RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
Definition: indexfsm.c:52
int remaining
Definition: informix.c:673
int i
Definition: isn.c:73
Pointer Item
Definition: item.h:17
ItemPointerData * ItemPointer
Definition: itemptr.h:49
IndexTupleData * IndexTuple
Definition: itup.h:53
#define IndexTupleSize(itup)
Definition: itup.h:70
struct IndexTupleData IndexTupleData
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:430
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:480
#define ExclusiveLock
Definition: lockdefs.h:42
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
void pfree(void *pointer)
Definition: mcxt.c:1520
void * palloc0(Size size)
Definition: mcxt.c:1346
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
void * palloc(Size size)
Definition: mcxt.c:1316
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define AmAutoVacuumWorkerProcess()
Definition: miscadmin.h:375
#define START_CRIT_SECTION()
Definition: miscadmin.h:149
#define END_CRIT_SECTION()
Definition: miscadmin.h:151
#define InvalidOffsetNumber
Definition: off.h:26
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
int16 attnum
Definition: pg_attribute.h:74
const void * data
uintptr_t Datum
Definition: postgres.h:64
void PredicateLockPageCombine(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3214
bool GlobalVisCheckRemovableXid(Relation rel, TransactionId xid)
Definition: procarray.c:4284
MemoryContextSwitchTo(old_ctx)
tree ctl root
Definition: radixtree.h:1880
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:648
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define RelationNeedsWAL(relation)
Definition: rel.h:628
@ MAIN_FORKNUM
Definition: relpath.h:50
struct DataPageDeleteStack * parent
Definition: ginvacuum.c:117
BlockNumber blkno
Definition: ginvacuum.c:119
struct DataPageDeleteStack * child
Definition: ginvacuum.c:116
BlockNumber nDataPages
Definition: gin.h:47
BlockNumber nEntryPages
Definition: gin.h:46
int64 nEntries
Definition: gin.h:48
BlockNumber nTotalPages
Definition: gin.h:45
MemoryContext tmpCxt
Definition: ginvacuum.c:36
BufferAccessStrategy strategy
Definition: ginvacuum.c:35
IndexBulkDeleteCallback callback
Definition: ginvacuum.c:32
IndexBulkDeleteResult * result
Definition: ginvacuum.c:31
void * callback_state
Definition: ginvacuum.c:33
GinState ginstate
Definition: ginvacuum.c:34
Relation index
Definition: ginvacuum.c:30
bool estimated_count
Definition: genam.h:78
BlockNumber pages_deleted
Definition: genam.h:82
BlockNumber pages_newly_deleted
Definition: genam.h:81
BlockNumber pages_free
Definition: genam.h:83
BlockNumber num_pages
Definition: genam.h:77
double tuples_removed
Definition: genam.h:80
double num_index_tuples
Definition: genam.h:79
Relation index
Definition: genam.h:46
double num_heap_tuples
Definition: genam.h:52
bool analyze_only
Definition: genam.h:48
BufferAccessStrategy strategy
Definition: genam.h:53
bool estimated_count
Definition: genam.h:50
Definition: type.h:95
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:46
static ItemArray items
Definition: test_tidstore.c:49
static TransactionId ReadNextTransactionId(void)
Definition: transam.h:315
#define TransactionIdIsValid(xid)
Definition: transam.h:41
void vacuum_delay_point(void)
Definition: vacuum.c:2336
uint64 XLogRecPtr
Definition: xlogdefs.h:21
void XLogRegisterData(char *data, uint32 len)
Definition: xloginsert.c:364
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:242
void XLogBeginInsert(void)
Definition: xloginsert.c:149
#define REGBUF_STANDARD
Definition: xloginsert.h:34
#define REGBUF_FORCE_IMAGE
Definition: xloginsert.h:31