PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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-2017, 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 "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  BlockNumber leftBlkno; /* rightest non-deleted page on left */
120  bool isRoot;
122 
123 
124 /*
125  * Delete a posting tree page.
126  */
127 static void
129  BlockNumber parentBlkno, OffsetNumber myoff, bool isParentRoot)
130 {
131  Buffer dBuffer;
132  Buffer lBuffer;
133  Buffer pBuffer;
134  Page page,
135  parentPage;
136  BlockNumber rightlink;
137 
138  /*
139  * This function MUST be called only if someone of parent pages hold
140  * exclusive cleanup lock. This guarantees that no insertions currently
141  * happen in this subtree. Caller also acquire Exclusive lock on deletable
142  * page and is acquiring and releasing exclusive lock on left page before.
143  * Left page was locked and released. Then parent and this page are
144  * locked. We acquire left page lock here only to mark page dirty after
145  * changing right pointer.
146  */
147  lBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, leftBlkno,
148  RBM_NORMAL, gvs->strategy);
149  dBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, deleteBlkno,
150  RBM_NORMAL, gvs->strategy);
151  pBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, parentBlkno,
152  RBM_NORMAL, gvs->strategy);
153 
154  LockBuffer(lBuffer, GIN_EXCLUSIVE);
155 
157 
158  /* Unlink the page by changing left sibling's rightlink */
159  page = BufferGetPage(dBuffer);
160  rightlink = GinPageGetOpaque(page)->rightlink;
161 
162  page = BufferGetPage(lBuffer);
163  GinPageGetOpaque(page)->rightlink = rightlink;
164 
165  /* Delete downlink from parent */
166  parentPage = BufferGetPage(pBuffer);
167 #ifdef USE_ASSERT_CHECKING
168  do
169  {
170  PostingItem *tod = GinDataPageGetPostingItem(parentPage, myoff);
171 
172  Assert(PostingItemGetBlockNumber(tod) == deleteBlkno);
173  } while (0);
174 #endif
175  GinPageDeletePostingItem(parentPage, myoff);
176 
177  page = BufferGetPage(dBuffer);
178 
179  /*
180  * we shouldn't change rightlink field to save workability of running
181  * search scan
182  */
183  GinPageGetOpaque(page)->flags = GIN_DELETED;
184 
185  MarkBufferDirty(pBuffer);
186  MarkBufferDirty(lBuffer);
187  MarkBufferDirty(dBuffer);
188 
189  if (RelationNeedsWAL(gvs->index))
190  {
191  XLogRecPtr recptr;
192  ginxlogDeletePage data;
193 
194  /*
195  * We can't pass REGBUF_STANDARD for the deleted page, because we
196  * didn't set pd_lower on pre-9.4 versions. The page might've been
197  * binary-upgraded from an older version, and hence not have pd_lower
198  * set correctly. Ditto for the left page, but removing the item from
199  * the parent updated its pd_lower, so we know that's OK at this
200  * point.
201  */
202  XLogBeginInsert();
203  XLogRegisterBuffer(0, dBuffer, 0);
204  XLogRegisterBuffer(1, pBuffer, REGBUF_STANDARD);
205  XLogRegisterBuffer(2, lBuffer, 0);
206 
207  data.parentOffset = myoff;
208  data.rightLink = GinPageGetOpaque(page)->rightlink;
209 
210  XLogRegisterData((char *) &data, sizeof(ginxlogDeletePage));
211 
212  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE);
213  PageSetLSN(page, recptr);
214  PageSetLSN(parentPage, recptr);
215  PageSetLSN(BufferGetPage(lBuffer), recptr);
216  }
217 
218  ReleaseBuffer(pBuffer);
219  UnlockReleaseBuffer(lBuffer);
220  ReleaseBuffer(dBuffer);
221 
223 
224  gvs->result->pages_deleted++;
225 }
226 
227 
228 /*
229  * scans posting tree and deletes empty pages
230  */
231 static bool
234 {
236  Buffer buffer;
237  Page page;
238  bool meDelete = FALSE;
239  bool isempty;
240 
241  if (isRoot)
242  {
243  me = parent;
244  }
245  else
246  {
247  if (!parent->child)
248  {
250  me->parent = parent;
251  parent->child = me;
253  }
254  else
255  me = parent->child;
256  }
257 
258  buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
259  RBM_NORMAL, gvs->strategy);
260 
261  if (!isRoot)
262  LockBuffer(buffer, GIN_EXCLUSIVE);
263 
264  page = BufferGetPage(buffer);
265 
266  Assert(GinPageIsData(page));
267 
268  if (!GinPageIsLeaf(page))
269  {
270  OffsetNumber i;
271 
272  me->blkno = blkno;
273  for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
274  {
275  PostingItem *pitem = GinDataPageGetPostingItem(page, i);
276 
277  if (ginScanToDelete(gvs, PostingItemGetBlockNumber(pitem), FALSE, me, i))
278  i--;
279  }
280  }
281 
282  if (GinPageIsLeaf(page))
283  isempty = GinDataLeafPageIsEmpty(page);
284  else
285  isempty = GinPageGetOpaque(page)->maxoff < FirstOffsetNumber;
286 
287  if (isempty)
288  {
289  /* we never delete the left- or rightmost branch */
290  if (me->leftBlkno != InvalidBlockNumber && !GinPageRightMost(page))
291  {
292  Assert(!isRoot);
293  ginDeletePage(gvs, blkno, me->leftBlkno, me->parent->blkno, myoff, me->parent->isRoot);
294  meDelete = TRUE;
295  }
296  }
297 
298  if (!isRoot)
299  LockBuffer(buffer, GIN_UNLOCK);
300 
301  ReleaseBuffer(buffer);
302 
303  if (!meDelete)
304  me->leftBlkno = blkno;
305 
306  return meDelete;
307 }
308 
309 
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.
316  */
317 static bool
319 {
320  Buffer buffer;
321  Page page;
322  bool hasVoidPage = FALSE;
323  MemoryContext oldCxt;
324 
325  buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
326  RBM_NORMAL, gvs->strategy);
327  page = BufferGetPage(buffer);
328 
329  ginTraverseLock(buffer, false);
330 
331  Assert(GinPageIsData(page));
332 
333  if (GinPageIsLeaf(page))
334  {
335  oldCxt = MemoryContextSwitchTo(gvs->tmpCxt);
336  ginVacuumPostingTreeLeaf(gvs->index, buffer, gvs);
337  MemoryContextSwitchTo(oldCxt);
339 
340  /* if root is a leaf page, we don't desire further processing */
341  if (GinDataLeafPageIsEmpty(page))
342  hasVoidPage = TRUE;
343 
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  }
367 
368  UnlockReleaseBuffer(buffer);
369 
370  for (i = FirstOffsetNumber; i <= maxoff; i++)
371  {
372  if (ginVacuumPostingTreeLeaves(gvs, children[i], FALSE))
373  hasEmptyChild = TRUE;
374  else
375  hasNonEmptyChild = TRUE;
376  }
377 
378  pfree(children);
379 
381 
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.
385  */
386 
387  if (hasEmptyChild && !hasNonEmptyChild && !isRoot)
388  return TRUE;
389 
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));
402  root.isRoot = TRUE;
403 
404  ginScanToDelete(gvs, blkno, TRUE, &root, InvalidOffsetNumber);
405 
406  ptr = root.child;
407 
408  while (ptr)
409  {
410  tmp = ptr->child;
411  pfree(ptr);
412  ptr = tmp;
413  }
414 
415  UnlockReleaseBuffer(buffer);
416  }
417 
418  /* Here we have deleted all empty subtrees */
419  return FALSE;
420  }
421 }
422 
423 static void
425 {
426  ginVacuumPostingTreeLeaves(gvs, rootBlkno, TRUE);
427 }
428 
429 /*
430  * returns modified page or NULL if page isn't modified.
431  * Function works with original page until first change is occurred,
432  * then page is copied into temporary one.
433  */
434 static Page
436 {
437  Page origpage = BufferGetPage(buffer),
438  tmppage;
439  OffsetNumber i,
440  maxoff = PageGetMaxOffsetNumber(origpage);
441 
442  tmppage = origpage;
443 
444  *nroot = 0;
445 
446  for (i = FirstOffsetNumber; i <= maxoff; i++)
447  {
448  IndexTuple itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
449 
450  if (GinIsPostingTree(itup))
451  {
452  /*
453  * store posting tree's roots for further processing, we can't
454  * vacuum it just now due to risk of deadlocks with scans/inserts
455  */
456  roots[*nroot] = GinGetDownlink(itup);
457  (*nroot)++;
458  }
459  else if (GinGetNPosting(itup) > 0)
460  {
461  int nitems;
462  ItemPointer items_orig;
463  bool free_items_orig;
464  ItemPointer items;
465 
466  /* Get list of item pointers from the tuple. */
467  if (GinItupIsCompressed(itup))
468  {
469  items_orig = ginPostingListDecode((GinPostingList *) GinGetPosting(itup), &nitems);
470  free_items_orig = true;
471  }
472  else
473  {
474  items_orig = (ItemPointer) GinGetPosting(itup);
475  nitems = GinGetNPosting(itup);
476  free_items_orig = false;
477  }
478 
479  /* Remove any items from the list that need to be vacuumed. */
480  items = ginVacuumItemPointers(gvs, items_orig, nitems, &nitems);
481 
482  if (free_items_orig)
483  pfree(items_orig);
484 
485  /* If any item pointers were removed, recreate the tuple. */
486  if (items)
487  {
488  OffsetNumber attnum;
489  Datum key;
490  GinNullCategory category;
491  GinPostingList *plist;
492  int plistsize;
493 
494  if (nitems > 0)
495  {
496  plist = ginCompressPostingList(items, nitems, GinMaxItemSize, NULL);
497  plistsize = SizeOfGinPostingList(plist);
498  }
499  else
500  {
501  plist = NULL;
502  plistsize = 0;
503  }
504 
505  /*
506  * if we already created a temporary page, make changes in
507  * place
508  */
509  if (tmppage == origpage)
510  {
511  /*
512  * On first difference, create a temporary copy of the
513  * page and copy the tuple's posting list to it.
514  */
515  tmppage = PageGetTempPageCopy(origpage);
516 
517  /* set itup pointer to new page */
518  itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
519  }
520 
521  attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
522  key = gintuple_get_key(&gvs->ginstate, itup, &category);
523  itup = GinFormTuple(&gvs->ginstate, attnum, key, category,
524  (char *) plist, plistsize,
525  nitems, true);
526  if (plist)
527  pfree(plist);
528  PageIndexTupleDelete(tmppage, i);
529 
530  if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)
531  elog(ERROR, "failed to add item to index page in \"%s\"",
533 
534  pfree(itup);
535  pfree(items);
536  }
537  }
538  }
539 
540  return (tmppage == origpage) ? NULL : tmppage;
541 }
542 
545  IndexBulkDeleteCallback callback, void *callback_state)
546 {
547  Relation index = info->index;
549  GinVacuumState gvs;
550  Buffer buffer;
551  BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
552  uint32 nRoot;
553 
555  "Gin vacuum temporary context",
557  gvs.index = index;
558  gvs.callback = callback;
559  gvs.callback_state = callback_state;
560  gvs.strategy = info->strategy;
561  initGinState(&gvs.ginstate, index);
562 
563  /* first time through? */
564  if (stats == NULL)
565  {
566  /* Yes, so initialize stats to zeroes */
568 
569  /*
570  * and cleanup any pending inserts
571  */
573  false, stats);
574  }
575 
576  /* we'll re-count the tuples each time */
577  stats->num_index_tuples = 0;
578  gvs.result = stats;
579 
580  buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
581  RBM_NORMAL, info->strategy);
582 
583  /* find leaf page */
584  for (;;)
585  {
586  Page page = BufferGetPage(buffer);
587  IndexTuple itup;
588 
589  LockBuffer(buffer, GIN_SHARE);
590 
591  Assert(!GinPageIsData(page));
592 
593  if (GinPageIsLeaf(page))
594  {
595  LockBuffer(buffer, GIN_UNLOCK);
596  LockBuffer(buffer, GIN_EXCLUSIVE);
597 
598  if (blkno == GIN_ROOT_BLKNO && !GinPageIsLeaf(page))
599  {
600  LockBuffer(buffer, GIN_UNLOCK);
601  continue; /* check it one more */
602  }
603  break;
604  }
605 
607 
608  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
609  blkno = GinGetDownlink(itup);
610  Assert(blkno != InvalidBlockNumber);
611 
612  UnlockReleaseBuffer(buffer);
613  buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
614  RBM_NORMAL, info->strategy);
615  }
616 
617  /* right now we found leftmost page in entry's BTree */
618 
619  for (;;)
620  {
621  Page page = BufferGetPage(buffer);
622  Page resPage;
623  uint32 i;
624 
625  Assert(!GinPageIsData(page));
626 
627  resPage = ginVacuumEntryPage(&gvs, buffer, rootOfPostingTree, &nRoot);
628 
629  blkno = GinPageGetOpaque(page)->rightlink;
630 
631  if (resPage)
632  {
634  PageRestoreTempPage(resPage, page);
635  MarkBufferDirty(buffer);
636  xlogVacuumPage(gvs.index, buffer);
637  UnlockReleaseBuffer(buffer);
639  }
640  else
641  {
642  UnlockReleaseBuffer(buffer);
643  }
644 
646 
647  for (i = 0; i < nRoot; i++)
648  {
649  ginVacuumPostingTree(&gvs, rootOfPostingTree[i]);
651  }
652 
653  if (blkno == InvalidBlockNumber) /* rightmost page */
654  break;
655 
656  buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
657  RBM_NORMAL, info->strategy);
658  LockBuffer(buffer, GIN_EXCLUSIVE);
659  }
660 
662 
663  return gvs.result;
664 }
665 
668 {
669  Relation index = info->index;
670  bool needLock;
671  BlockNumber npages,
672  blkno;
673  BlockNumber totFreePages;
674  GinState ginstate;
675  GinStatsData idxStat;
676 
677  /*
678  * In an autovacuum analyze, we want to clean up pending insertions.
679  * Otherwise, an ANALYZE-only call is a no-op.
680  */
681  if (info->analyze_only)
682  {
684  {
685  initGinState(&ginstate, index);
686  ginInsertCleanup(&ginstate, false, true, stats);
687  }
688  return stats;
689  }
690 
691  /*
692  * Set up all-zero stats and cleanup pending inserts if ginbulkdelete
693  * wasn't called
694  */
695  if (stats == NULL)
696  {
698  initGinState(&ginstate, index);
700  false, stats);
701  }
702 
703  memset(&idxStat, 0, sizeof(idxStat));
704 
705  /*
706  * XXX we always report the heap tuple count as the number of index
707  * entries. This is bogus if the index is partial, but it's real hard to
708  * tell how many distinct heap entries are referenced by a GIN index.
709  */
710  stats->num_index_tuples = info->num_heap_tuples;
711  stats->estimated_count = info->estimated_count;
712 
713  /*
714  * Need lock unless it's local to this backend.
715  */
716  needLock = !RELATION_IS_LOCAL(index);
717 
718  if (needLock)
720  npages = RelationGetNumberOfBlocks(index);
721  if (needLock)
723 
724  totFreePages = 0;
725 
726  for (blkno = GIN_ROOT_BLKNO; blkno < npages; blkno++)
727  {
728  Buffer buffer;
729  Page page;
730 
732 
733  buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
734  RBM_NORMAL, info->strategy);
735  LockBuffer(buffer, GIN_SHARE);
736  page = (Page) BufferGetPage(buffer);
737 
738  if (PageIsNew(page) || GinPageIsDeleted(page))
739  {
740  Assert(blkno != GIN_ROOT_BLKNO);
741  RecordFreeIndexPage(index, blkno);
742  totFreePages++;
743  }
744  else if (GinPageIsData(page))
745  {
746  idxStat.nDataPages++;
747  }
748  else if (!GinPageIsList(page))
749  {
750  idxStat.nEntryPages++;
751 
752  if (GinPageIsLeaf(page))
753  idxStat.nEntries += PageGetMaxOffsetNumber(page);
754  }
755 
756  UnlockReleaseBuffer(buffer);
757  }
758 
759  /* Update the metapage with accurate page and entry counts */
760  idxStat.nTotalPages = npages;
761  ginUpdateStats(info->index, &idxStat);
762 
763  /* Finally, vacuum the FSM */
765 
766  stats->pages_free = totFreePages;
767 
768  if (needLock)
770  stats->num_pages = RelationGetNumberOfBlocks(index);
771  if (needLock)
773 
774  return stats;
775 }
int remaining
Definition: informix.c:692
#define GIN_UNLOCK
Definition: gin_private.h:43
#define GIN_DELETED
Definition: ginblock.h:41
BlockNumber nEntryPages
Definition: gin.h:45
GinPostingList * ginCompressPostingList(const ItemPointer ipd, int nipd, int maxsize, int *nwritten)
IndexBulkDeleteResult * ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: ginvacuum.c:667
void LockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:3603
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
static bool ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno, bool isRoot)
Definition: ginvacuum.c:318
double tuples_removed
Definition: genam.h:77
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:407
ItemPointer ginPostingListDecode(GinPostingList *plist, int *ndecoded)
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:727
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
#define ExclusiveLock
Definition: lockdefs.h:44
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
Relation index
Definition: ginvacuum.c:29
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:523
bool analyze_only
Definition: genam.h:47
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
BlockNumber rightLink
Definition: ginxlog.h:160
BufferAccessStrategy strategy
Definition: ginvacuum.c:34
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
BufferAccessStrategy strategy
Definition: genam.h:51
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Pointer Item
Definition: item.h:17
ItemPointer ginVacuumItemPointers(GinVacuumState *gvs, ItemPointerData *items, int nitem, int *nremaining)
Definition: ginvacuum.c:47
void ginInsertCleanup(GinState *ginstate, bool full_clean, bool fill_fsm, IndexBulkDeleteResult *stats)
Definition: ginfast.c:729
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
Relation index
Definition: genam.h:46
int64 nEntries
Definition: gin.h:47
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:412
#define GinGetNPosting(itup)
Definition: ginblock.h:219
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:135
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
#define GinPageGetOpaque(page)
Definition: ginblock.h:109
OffsetNumber parentOffset
Definition: ginxlog.h:159
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
static void ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkno, BlockNumber parentBlkno, OffsetNumber myoff, bool isParentRoot)
Definition: ginvacuum.c:128
#define GinGetPosting(itup)
Definition: ginblock.h:229
IndexBulkDeleteResult * result
Definition: ginvacuum.c:30
#define GinItupIsCompressed(itup)
Definition: ginblock.h:230
#define GinDataLeafPageIsEmpty(page)
Definition: ginblock.h:274
uint16 OffsetNumber
Definition: off.h:24
ItemPointerData * ItemPointer
Definition: itemptr.h:49
Definition: type.h:89
BlockNumber leftBlkno
Definition: ginvacuum.c:119
void pfree(void *pointer)
Definition: mcxt.c:949
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
#define ERROR
Definition: elog.h:43
#define GinPageRightMost(page)
Definition: ginblock.h:128
signed char GinNullCategory
Definition: ginblock.h:197
static void xlogVacuumPage(Relation index, Buffer buffer)
Definition: ginvacuum.c:89
#define GinDataPageGetPostingItem(page, i)
Definition: ginblock.h:289
#define FALSE
Definition: c.h:219
BlockNumber num_pages
Definition: genam.h:73
BlockNumber pages_free
Definition: genam.h:79
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:165
void ginUpdateStats(Relation index, const GinStatsData *stats)
Definition: ginutil.c:661
BlockNumber blkno
Definition: ginvacuum.c:118
OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
Definition: ginutil.c:215
void ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer, GinVacuumState *gvs)
Definition: gindatapage.c:731
static Page ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint32 *nroot)
Definition: ginvacuum.c:435
IndexBulkDeleteCallback callback
Definition: ginvacuum.c:31
#define FirstOffsetNumber
Definition: off.h:27
IndexTupleData * IndexTuple
Definition: itup.h:53
#define REGBUF_STANDARD
Definition: xloginsert.h:34
void initGinState(GinState *state, Relation index)
Definition: ginutil.c:86
#define RelationGetRelationName(relation)
Definition: rel.h:436
unsigned int uint32
Definition: c.h:258
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
#define GinPageIsLeaf(page)
Definition: ginblock.h:111
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Definition: ginutil.c:248
BlockNumber pages_deleted
Definition: genam.h:78
void * callback_state
Definition: ginvacuum.c:32
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
struct DataPageDeleteStack DataPageDeleteStack
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3256
#define GIN_SHARE
Definition: gin_private.h:44
#define REGBUF_FORCE_IMAGE
Definition: xloginsert.h:30
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:332
#define XLOG_GIN_VACUUM_PAGE
Definition: ginxlog.h:137
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:322
void * palloc0(Size size)
Definition: mcxt.c:877
#define GinPageIsDeleted(page)
Definition: ginblock.h:123
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:382
uintptr_t Datum
Definition: postgres.h:372
struct IndexTupleData IndexTupleData
#define GIN_EXCLUSIVE
Definition: gin_private.h:45
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
#define GinGetDownlink(itup)
Definition: ginblock.h:248
void GinPageDeletePostingItem(Page page, OffsetNumber offset)
Definition: gindatapage.c:413
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:199
struct DataPageDeleteStack * child
Definition: ginvacuum.c:115
#define InvalidOffsetNumber
Definition: off.h:26
BlockNumber nDataPages
Definition: gin.h:46
#define GinPageIsData(page)
Definition: ginblock.h:114
double num_heap_tuples
Definition: genam.h:50
#define PostingItemGetBlockNumber(pointer)
Definition: ginblock.h:183
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:664
IndexTuple GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, Pointer data, Size dataSize, int nipd, bool errorTooBig)
Definition: ginentrypage.c:45
void IndexFreeSpaceMapVacuum(Relation rel)
Definition: indexfsm.c:71
struct DataPageDeleteStack * parent
Definition: ginvacuum.c:116
IndexBulkDeleteResult * ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: ginvacuum.c:544
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:214
#define GinPageIsList(page)
Definition: ginblock.h:116
#define InvalidBlockNumber
Definition: block.h:33
BlockNumber nTotalPages
Definition: gin.h:44
#define RelationNeedsWAL(relation)
Definition: rel.h:505
#define PageIsNew(page)
Definition: bufpage.h:225
#define GinIsPostingTree(itup)
Definition: ginblock.h:222
void * palloc(Size size)
Definition: mcxt.c:848
Page PageGetTempPageCopy(Page page)
Definition: bufpage.c:365
#define SizeOfGinPostingList(plist)
Definition: ginblock.h:333
#define GinMaxItemSize
Definition: ginblock.h:239
int i
#define TRUE
Definition: c.h:215
#define elog
Definition: elog.h:219
static void ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno)
Definition: ginvacuum.c:424
void vacuum_delay_point(void)
Definition: vacuum.c:1560
void XLogBeginInsert(void)
Definition: xloginsert.c:120
#define XLOG_GIN_DELETE_PAGE
Definition: ginxlog.h:155
void RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
Definition: indexfsm.c:52
MemoryContext tmpCxt
Definition: ginvacuum.c:35
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
double num_index_tuples
Definition: genam.h:76
int Buffer
Definition: buf.h:23
bool estimated_count
Definition: genam.h:75
bool(* IndexBulkDeleteCallback)(ItemPointer itemptr, void *state)
Definition: genam.h:83
#define PageGetItem(page, itemId)
Definition: bufpage.h:336
#define GIN_ROOT_BLKNO
Definition: ginblock.h:51
Pointer Page
Definition: bufpage.h:74
#define IndexTupleSize(itup)
Definition: itup.h:70
static bool ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDeleteStack *parent, OffsetNumber myoff)
Definition: ginvacuum.c:232
int ginTraverseLock(Buffer buffer, bool searchMode)
Definition: ginbtree.c:35
bool estimated_count
Definition: genam.h:48
GinState ginstate
Definition: ginvacuum.c:33