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