PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
spgvacuum.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/spgist_private.h"
#include "access/spgxlog.h"
#include "access/transam.h"
#include "access/xloginsert.h"
#include "catalog/storage_xlog.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/snapmgr.h"
Include dependency graph for spgvacuum.c:

Go to the source code of this file.

Data Structures

struct  spgVacPendingItem
 
struct  spgBulkDeleteState
 

Typedefs

typedef struct spgVacPendingItem spgVacPendingItem
 
typedef struct spgBulkDeleteState spgBulkDeleteState
 

Functions

static void spgAddPendingTID (spgBulkDeleteState *bds, ItemPointer tid)
 
static void spgClearPendingList (spgBulkDeleteState *bds)
 
static void vacuumLeafPage (spgBulkDeleteState *bds, Relation index, Buffer buffer, bool forPending)
 
static void vacuumLeafRoot (spgBulkDeleteState *bds, Relation index, Buffer buffer)
 
static void vacuumRedirectAndPlaceholder (Relation index, Buffer buffer)
 
static void spgvacuumpage (spgBulkDeleteState *bds, BlockNumber blkno)
 
static void spgprocesspending (spgBulkDeleteState *bds)
 
static void spgvacuumscan (spgBulkDeleteState *bds)
 
IndexBulkDeleteResultspgbulkdelete (IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
 
static bool dummy_callback (ItemPointer itemptr, void *state)
 
IndexBulkDeleteResultspgvacuumcleanup (IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 

Typedef Documentation

Function Documentation

static bool dummy_callback ( ItemPointer  itemptr,
void *  state 
)
static

Definition at line 906 of file spgvacuum.c.

Referenced by spgvacuumcleanup().

907 {
908  return false;
909 }
static void spgAddPendingTID ( spgBulkDeleteState bds,
ItemPointer  tid 
)
static

Definition at line 64 of file spgvacuum.c.

References spgVacPendingItem::done, ItemPointerEquals(), spgVacPendingItem::next, NULL, palloc(), spgBulkDeleteState::pendingList, and spgVacPendingItem::tid.

Referenced by spgprocesspending(), and vacuumLeafPage().

65 {
66  spgVacPendingItem *pitem;
67  spgVacPendingItem **listLink;
68 
69  /* search the list for pre-existing entry */
70  listLink = &bds->pendingList;
71  while (*listLink != NULL)
72  {
73  pitem = *listLink;
74  if (ItemPointerEquals(tid, &pitem->tid))
75  return; /* already in list, do nothing */
76  listLink = &pitem->next;
77  }
78  /* not there, so append new entry */
79  pitem = (spgVacPendingItem *) palloc(sizeof(spgVacPendingItem));
80  pitem->tid = *tid;
81  pitem->done = false;
82  pitem->next = NULL;
83  *listLink = pitem;
84 }
struct spgVacPendingItem * next
Definition: spgvacuum.c:37
#define NULL
Definition: c.h:229
spgVacPendingItem * pendingList
Definition: spgvacuum.c:51
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
void * palloc(Size size)
Definition: mcxt.c:849
ItemPointerData tid
Definition: spgvacuum.c:35
IndexBulkDeleteResult* spgbulkdelete ( IndexVacuumInfo info,
IndexBulkDeleteResult stats,
IndexBulkDeleteCallback  callback,
void *  callback_state 
)

Definition at line 886 of file spgvacuum.c.

References spgBulkDeleteState::callback, callback(), spgBulkDeleteState::callback_state, spgBulkDeleteState::info, NULL, palloc0(), spgvacuumscan(), and spgBulkDeleteState::stats.

Referenced by spghandler().

888 {
889  spgBulkDeleteState bds;
890 
891  /* allocate stats if first time through, else re-use existing struct */
892  if (stats == NULL)
894  bds.info = info;
895  bds.stats = stats;
896  bds.callback = callback;
897  bds.callback_state = callback_state;
898 
899  spgvacuumscan(&bds);
900 
901  return stats;
902 }
IndexVacuumInfo * info
Definition: spgvacuum.c:44
static void spgvacuumscan(spgBulkDeleteState *bds)
Definition: spgvacuum.c:790
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48
IndexBulkDeleteCallback callback
Definition: spgvacuum.c:46
void * palloc0(Size size)
Definition: mcxt.c:878
void * callback_state
Definition: spgvacuum.c:47
#define NULL
Definition: c.h:229
IndexBulkDeleteResult * stats
Definition: spgvacuum.c:45
static void spgClearPendingList ( spgBulkDeleteState bds)
static

Definition at line 90 of file spgvacuum.c.

References Assert, spgVacPendingItem::done, spgVacPendingItem::next, NULL, spgBulkDeleteState::pendingList, and pfree().

Referenced by spgprocesspending().

91 {
92  spgVacPendingItem *pitem;
93  spgVacPendingItem *nitem;
94 
95  for (pitem = bds->pendingList; pitem != NULL; pitem = nitem)
96  {
97  nitem = pitem->next;
98  /* All items in list should have been dealt with */
99  Assert(pitem->done);
100  pfree(pitem);
101  }
102  bds->pendingList = NULL;
103 }
void pfree(void *pointer)
Definition: mcxt.c:950
struct spgVacPendingItem * next
Definition: spgvacuum.c:37
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:675
spgVacPendingItem * pendingList
Definition: spgvacuum.c:51
static void spgprocesspending ( spgBulkDeleteState bds)
static

Definition at line 678 of file spgvacuum.c.

References buffer, BUFFER_LOCK_EXCLUSIVE, BufferGetPage, spgVacPendingItem::done, elog, ERROR, i, IndexVacuumInfo::index, spgBulkDeleteState::info, ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, ItemPointerIsValid, LockBuffer(), MAIN_FORKNUM, spgVacPendingItem::next, NULL, PageGetItem, PageGetItemId, PageIsNew, spgBulkDeleteState::pendingList, RBM_NORMAL, ReadBufferExtended(), RelationGetRelationName, SGITITERATE, spgAddPendingTID(), spgClearPendingList(), SPGIST_LIVE, SPGIST_REDIRECT, SpGistBlockIsRoot, SpGistPageIsDeleted, SpGistPageIsLeaf, SpGistSetLastUsedPage(), IndexVacuumInfo::strategy, IndexTupleData::t_tid, spgVacPendingItem::tid, SpGistInnerTupleData::tupstate, UnlockReleaseBuffer(), vacuum_delay_point(), vacuumLeafPage(), and vacuumRedirectAndPlaceholder().

Referenced by spgvacuumscan().

679 {
680  Relation index = bds->info->index;
681  spgVacPendingItem *pitem;
682  spgVacPendingItem *nitem;
683  BlockNumber blkno;
684  Buffer buffer;
685  Page page;
686 
687  for (pitem = bds->pendingList; pitem != NULL; pitem = pitem->next)
688  {
689  if (pitem->done)
690  continue; /* ignore already-done items */
691 
692  /* call vacuum_delay_point while not holding any buffer lock */
694 
695  /* examine the referenced page */
696  blkno = ItemPointerGetBlockNumber(&pitem->tid);
697  buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
698  RBM_NORMAL, bds->info->strategy);
700  page = (Page) BufferGetPage(buffer);
701 
702  if (PageIsNew(page) || SpGistPageIsDeleted(page))
703  {
704  /* Probably shouldn't happen, but ignore it */
705  }
706  else if (SpGistPageIsLeaf(page))
707  {
708  if (SpGistBlockIsRoot(blkno))
709  {
710  /* this should definitely not happen */
711  elog(ERROR, "redirection leads to root page of index \"%s\"",
712  RelationGetRelationName(index));
713  }
714 
715  /* deal with any deletable tuples */
716  vacuumLeafPage(bds, index, buffer, true);
717  /* might as well do this while we are here */
718  vacuumRedirectAndPlaceholder(index, buffer);
719 
720  SpGistSetLastUsedPage(index, buffer);
721 
722  /*
723  * We can mark as done not only this item, but any later ones
724  * pointing at the same page, since we vacuumed the whole page.
725  */
726  pitem->done = true;
727  for (nitem = pitem->next; nitem != NULL; nitem = nitem->next)
728  {
729  if (ItemPointerGetBlockNumber(&nitem->tid) == blkno)
730  nitem->done = true;
731  }
732  }
733  else
734  {
735  /*
736  * On an inner page, visit the referenced inner tuple and add all
737  * its downlinks to the pending list. We might have pending items
738  * for more than one inner tuple on the same page (in fact this is
739  * pretty likely given the way space allocation works), so get
740  * them all while we are here.
741  */
742  for (nitem = pitem; nitem != NULL; nitem = nitem->next)
743  {
744  if (nitem->done)
745  continue;
746  if (ItemPointerGetBlockNumber(&nitem->tid) == blkno)
747  {
748  OffsetNumber offset;
749  SpGistInnerTuple innerTuple;
750 
751  offset = ItemPointerGetOffsetNumber(&nitem->tid);
752  innerTuple = (SpGistInnerTuple) PageGetItem(page,
753  PageGetItemId(page, offset));
754  if (innerTuple->tupstate == SPGIST_LIVE)
755  {
756  SpGistNodeTuple node;
757  int i;
758 
759  SGITITERATE(innerTuple, i, node)
760  {
761  if (ItemPointerIsValid(&node->t_tid))
762  spgAddPendingTID(bds, &node->t_tid);
763  }
764  }
765  else if (innerTuple->tupstate == SPGIST_REDIRECT)
766  {
767  /* transfer attention to redirect point */
768  spgAddPendingTID(bds,
769  &((SpGistDeadTuple) innerTuple)->pointer);
770  }
771  else
772  elog(ERROR, "unexpected SPGiST tuple state: %d",
773  innerTuple->tupstate);
774 
775  nitem->done = true;
776  }
777  }
778  }
779 
780  UnlockReleaseBuffer(buffer);
781  }
782 
783  spgClearPendingList(bds);
784 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:59
SpGistInnerTupleData * SpGistInnerTuple
#define SpGistPageIsLeaf(page)
#define SPGIST_REDIRECT
IndexVacuumInfo * info
Definition: spgvacuum.c:44
#define SGITITERATE(x, i, nt)
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
ItemPointerData t_tid
Definition: itup.h:37
BufferAccessStrategy strategy
Definition: genam.h:51
void SpGistSetLastUsedPage(Relation index, Buffer buffer)
Definition: spgutils.c:464
Relation index
Definition: genam.h:46
uint32 BlockNumber
Definition: block.h:31
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:89
static void spgClearPendingList(spgBulkDeleteState *bds)
Definition: spgvacuum.c:90
uint16 OffsetNumber
Definition: off.h:24
Definition: type.h:90
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
#define ERROR
Definition: elog.h:43
#define RelationGetRelationName(relation)
Definition: rel.h:437
struct spgVacPendingItem * next
Definition: spgvacuum.c:37
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
static void vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer, bool forPending)
Definition: spgvacuum.c:126
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:232
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
#define NULL
Definition: c.h:229
spgVacPendingItem * pendingList
Definition: spgvacuum.c:51
#define SpGistPageIsDeleted(page)
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:211
#define SpGistBlockIsRoot(blkno)
#define SPGIST_LIVE
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:94
static void spgAddPendingTID(spgBulkDeleteState *bds, ItemPointer tid)
Definition: spgvacuum.c:64
#define PageIsNew(page)
Definition: bufpage.h:226
int i
ItemPointerData tid
Definition: spgvacuum.c:35
static void vacuumRedirectAndPlaceholder(Relation index, Buffer buffer)
Definition: spgvacuum.c:492
#define elog
Definition: elog.h:219
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:75
void vacuum_delay_point(void)
Definition: vacuum.c:1560
int Buffer
Definition: buf.h:23
#define PageGetItem(page, itemId)
Definition: bufpage.h:337
Pointer Page
Definition: bufpage.h:74
IndexBulkDeleteResult* spgvacuumcleanup ( IndexVacuumInfo info,
IndexBulkDeleteResult stats 
)

Definition at line 917 of file spgvacuum.c.

References IndexVacuumInfo::analyze_only, spgBulkDeleteState::callback, spgBulkDeleteState::callback_state, dummy_callback(), IndexVacuumInfo::estimated_count, IndexVacuumInfo::index, IndexFreeSpaceMapVacuum(), spgBulkDeleteState::info, NULL, IndexVacuumInfo::num_heap_tuples, IndexBulkDeleteResult::num_index_tuples, palloc0(), spgvacuumscan(), and spgBulkDeleteState::stats.

Referenced by spghandler().

918 {
919  Relation index = info->index;
920  spgBulkDeleteState bds;
921 
922  /* No-op in ANALYZE ONLY mode */
923  if (info->analyze_only)
924  return stats;
925 
926  /*
927  * We don't need to scan the index if there was a preceding bulkdelete
928  * pass. Otherwise, make a pass that won't delete any live tuples, but
929  * might still accomplish useful stuff with redirect/placeholder cleanup,
930  * and in any case will provide stats.
931  */
932  if (stats == NULL)
933  {
935  bds.info = info;
936  bds.stats = stats;
937  bds.callback = dummy_callback;
938  bds.callback_state = NULL;
939 
940  spgvacuumscan(&bds);
941  }
942 
943  /* Finally, vacuum the FSM */
945 
946  /*
947  * It's quite possible for us to be fooled by concurrent tuple moves into
948  * double-counting some index tuples, so disbelieve any total that exceeds
949  * the underlying heap's count ... if we know that accurately. Otherwise
950  * this might just make matters worse.
951  */
952  if (!info->estimated_count)
953  {
954  if (stats->num_index_tuples > info->num_heap_tuples)
955  stats->num_index_tuples = info->num_heap_tuples;
956  }
957 
958  return stats;
959 }
IndexVacuumInfo * info
Definition: spgvacuum.c:44
bool analyze_only
Definition: genam.h:47
Relation index
Definition: genam.h:46
Definition: type.h:90
static void spgvacuumscan(spgBulkDeleteState *bds)
Definition: spgvacuum.c:790
IndexBulkDeleteCallback callback
Definition: spgvacuum.c:46
void * palloc0(Size size)
Definition: mcxt.c:878
static bool dummy_callback(ItemPointer itemptr, void *state)
Definition: spgvacuum.c:906
double num_heap_tuples
Definition: genam.h:50
void * callback_state
Definition: spgvacuum.c:47
#define NULL
Definition: c.h:229
void IndexFreeSpaceMapVacuum(Relation rel)
Definition: indexfsm.c:71
IndexBulkDeleteResult * stats
Definition: spgvacuum.c:45
double num_index_tuples
Definition: genam.h:76
bool estimated_count
Definition: genam.h:48
static void spgvacuumpage ( spgBulkDeleteState bds,
BlockNumber  blkno 
)
static

Definition at line 607 of file spgvacuum.c.

References buffer, BUFFER_LOCK_EXCLUSIVE, BufferGetPage, IndexVacuumInfo::index, spgBulkDeleteState::info, spgBulkDeleteState::lastFilledBlock, LockBuffer(), MAIN_FORKNUM, PageIsEmpty, PageIsNew, IndexBulkDeleteResult::pages_deleted, RBM_NORMAL, ReadBufferExtended(), RecordFreeIndexPage(), SpGistBlockIsRoot, SpGistPageIsLeaf, SpGistSetLastUsedPage(), spgBulkDeleteState::stats, IndexVacuumInfo::strategy, UnlockReleaseBuffer(), vacuum_delay_point(), vacuumLeafPage(), vacuumLeafRoot(), and vacuumRedirectAndPlaceholder().

Referenced by spgvacuumscan().

608 {
609  Relation index = bds->info->index;
610  Buffer buffer;
611  Page page;
612 
613  /* call vacuum_delay_point while not holding any buffer lock */
615 
616  buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
617  RBM_NORMAL, bds->info->strategy);
619  page = (Page) BufferGetPage(buffer);
620 
621  if (PageIsNew(page))
622  {
623  /*
624  * We found an all-zero page, which could happen if the database
625  * crashed just after extending the file. Recycle it.
626  */
627  }
628  else if (PageIsEmpty(page))
629  {
630  /* nothing to do */
631  }
632  else if (SpGistPageIsLeaf(page))
633  {
634  if (SpGistBlockIsRoot(blkno))
635  {
636  vacuumLeafRoot(bds, index, buffer);
637  /* no need for vacuumRedirectAndPlaceholder */
638  }
639  else
640  {
641  vacuumLeafPage(bds, index, buffer, false);
642  vacuumRedirectAndPlaceholder(index, buffer);
643  }
644  }
645  else
646  {
647  /* inner page */
648  vacuumRedirectAndPlaceholder(index, buffer);
649  }
650 
651  /*
652  * The root pages must never be deleted, nor marked as available in FSM,
653  * because we don't want them ever returned by a search for a place to put
654  * a new tuple. Otherwise, check for empty page, and make sure the FSM
655  * knows about it.
656  */
657  if (!SpGistBlockIsRoot(blkno))
658  {
659  if (PageIsNew(page) || PageIsEmpty(page))
660  {
661  RecordFreeIndexPage(index, blkno);
662  bds->stats->pages_deleted++;
663  }
664  else
665  {
666  SpGistSetLastUsedPage(index, buffer);
667  bds->lastFilledBlock = blkno;
668  }
669  }
670 
671  UnlockReleaseBuffer(buffer);
672 }
#define PageIsEmpty(page)
Definition: bufpage.h:219
#define SpGistPageIsLeaf(page)
BlockNumber lastFilledBlock
Definition: spgvacuum.c:53
static void vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
Definition: spgvacuum.c:407
IndexVacuumInfo * info
Definition: spgvacuum.c:44
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
BufferAccessStrategy strategy
Definition: genam.h:51
void SpGistSetLastUsedPage(Relation index, Buffer buffer)
Definition: spgutils.c:464
Relation index
Definition: genam.h:46
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:89
Definition: type.h:90
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
BlockNumber pages_deleted
Definition: genam.h:78
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
static void vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer, bool forPending)
Definition: spgvacuum.c:126
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
IndexBulkDeleteResult * stats
Definition: spgvacuum.c:45
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:211
#define SpGistBlockIsRoot(blkno)
#define PageIsNew(page)
Definition: bufpage.h:226
static void vacuumRedirectAndPlaceholder(Relation index, Buffer buffer)
Definition: spgvacuum.c:492
void vacuum_delay_point(void)
Definition: vacuum.c:1560
void RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
Definition: indexfsm.c:52
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:74
static void spgvacuumscan ( spgBulkDeleteState bds)
static

Definition at line 790 of file spgvacuum.c.

References IndexBulkDeleteResult::estimated_count, ExclusiveLock, GetActiveSnapshot(), IndexVacuumInfo::index, spgBulkDeleteState::info, initSpGistState(), spgBulkDeleteState::lastFilledBlock, LockRelationForExtension(), spgBulkDeleteState::myXmin, NULL, IndexBulkDeleteResult::num_index_tuples, IndexBulkDeleteResult::num_pages, IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_free, IndexBulkDeleteResult::pages_removed, spgBulkDeleteState::pendingList, RELATION_IS_LOCAL, RelationGetNumberOfBlocks, RelationTruncate(), SPGIST_LAST_FIXED_BLKNO, SPGIST_METAPAGE_BLKNO, SpGistUpdateMetaPage(), spgprocesspending(), spgBulkDeleteState::spgstate, spgvacuumpage(), spgBulkDeleteState::stats, UnlockRelationForExtension(), and SnapshotData::xmin.

Referenced by spgbulkdelete(), and spgvacuumcleanup().

791 {
792  Relation index = bds->info->index;
793  bool needLock;
794  BlockNumber num_pages,
795  blkno;
796 
797  /* Finish setting up spgBulkDeleteState */
798  initSpGistState(&bds->spgstate, index);
799  bds->pendingList = NULL;
800  bds->myXmin = GetActiveSnapshot()->xmin;
802 
803  /*
804  * Reset counts that will be incremented during the scan; needed in case
805  * of multiple scans during a single VACUUM command
806  */
807  bds->stats->estimated_count = false;
808  bds->stats->num_index_tuples = 0;
809  bds->stats->pages_deleted = 0;
810 
811  /* We can skip locking for new or temp relations */
812  needLock = !RELATION_IS_LOCAL(index);
813 
814  /*
815  * The outer loop iterates over all index pages except the metapage, in
816  * physical order (we hope the kernel will cooperate in providing
817  * read-ahead for speed). It is critical that we visit all leaf pages,
818  * including ones added after we start the scan, else we might fail to
819  * delete some deletable tuples. See more extensive comments about this
820  * in btvacuumscan().
821  */
822  blkno = SPGIST_METAPAGE_BLKNO + 1;
823  for (;;)
824  {
825  /* Get the current relation length */
826  if (needLock)
828  num_pages = RelationGetNumberOfBlocks(index);
829  if (needLock)
831 
832  /* Quit if we've scanned the whole relation */
833  if (blkno >= num_pages)
834  break;
835  /* Iterate over pages, then loop back to recheck length */
836  for (; blkno < num_pages; blkno++)
837  {
838  spgvacuumpage(bds, blkno);
839  /* empty the pending-list after each page */
840  if (bds->pendingList != NULL)
841  spgprocesspending(bds);
842  }
843  }
844 
845  /* Propagate local lastUsedPage cache to metablock */
846  SpGistUpdateMetaPage(index);
847 
848  /*
849  * Truncate index if possible
850  *
851  * XXX disabled because it's unsafe due to possible concurrent inserts.
852  * We'd have to rescan the pages to make sure they're still empty, and it
853  * doesn't seem worth it. Note that btree doesn't do this either.
854  *
855  * Another reason not to truncate is that it could invalidate the cached
856  * pages-with-freespace pointers in the metapage and other backends'
857  * relation caches, that is leave them pointing to nonexistent pages.
858  * Adding RelationGetNumberOfBlocks calls to protect the places that use
859  * those pointers would be unduly expensive.
860  */
861 #ifdef NOT_USED
862  if (num_pages > bds->lastFilledBlock + 1)
863  {
864  BlockNumber lastBlock = num_pages - 1;
865 
866  num_pages = bds->lastFilledBlock + 1;
867  RelationTruncate(index, num_pages);
868  bds->stats->pages_removed += lastBlock - bds->lastFilledBlock;
869  bds->stats->pages_deleted -= lastBlock - bds->lastFilledBlock;
870  }
871 #endif
872 
873  /* Report final stats */
874  bds->stats->num_pages = num_pages;
875  bds->stats->pages_free = bds->stats->pages_deleted;
876 }
void SpGistUpdateMetaPage(Relation index)
Definition: spgutils.c:252
BlockNumber lastFilledBlock
Definition: spgvacuum.c:53
#define ExclusiveLock
Definition: lockdefs.h:44
IndexVacuumInfo * info
Definition: spgvacuum.c:44
#define SPGIST_LAST_FIXED_BLKNO
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:524
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:834
Relation index
Definition: genam.h:46
uint32 BlockNumber
Definition: block.h:31
Definition: type.h:90
static void spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno)
Definition: spgvacuum.c:607
BlockNumber num_pages
Definition: genam.h:73
BlockNumber pages_free
Definition: genam.h:79
#define SPGIST_METAPAGE_BLKNO
void initSpGistState(SpGistState *state, Relation index)
Definition: spgutils.c:158
TransactionId xmin
Definition: snapshot.h:66
BlockNumber pages_deleted
Definition: genam.h:78
BlockNumber pages_removed
Definition: genam.h:74
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:332
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:382
TransactionId myXmin
Definition: spgvacuum.c:52
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:199
#define NULL
Definition: c.h:229
spgVacPendingItem * pendingList
Definition: spgvacuum.c:51
IndexBulkDeleteResult * stats
Definition: spgvacuum.c:45
SpGistState spgstate
Definition: spgvacuum.c:50
double num_index_tuples
Definition: genam.h:76
bool estimated_count
Definition: genam.h:75
static void spgprocesspending(spgBulkDeleteState *bds)
Definition: spgvacuum.c:678
void RelationTruncate(Relation rel, BlockNumber nblocks)
Definition: storage.c:227
static void vacuumLeafPage ( spgBulkDeleteState bds,
Relation  index,
Buffer  buffer,
bool  forPending 
)
static

Definition at line 126 of file spgvacuum.c.

References Assert, BufferGetBlockNumber(), BufferGetPage, spgBulkDeleteState::callback, spgBulkDeleteState::callback_state, elog, END_CRIT_SECTION, ERROR, FirstOffsetNumber, SpGistLeafTupleData::heapPtr, i, InvalidBlockNumber, InvalidOffsetNumber, ItemPointerIsValid, MarkBufferDirty(), MaxIndexTuplesPerPage, spgBulkDeleteState::myXmin, spgxlogVacuumLeaf::nChain, spgxlogVacuumLeaf::nDead, SpGistLeafTupleData::nextOffset, SpGistDeadTupleData::nextOffset, spgxlogVacuumLeaf::nMove, spgxlogVacuumLeaf::nPlaceholder, IndexBulkDeleteResult::num_index_tuples, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageSetLSN, SpGistDeadTupleData::pointer, REGBUF_STANDARD, RelationGetRelationName, RelationNeedsWAL, SizeOfSpgxlogVacuumLeaf, spgAddPendingTID(), SPGIST_DEAD, SPGIST_LIVE, SPGIST_PLACEHOLDER, SPGIST_REDIRECT, spgPageIndexMultiDelete(), spgBulkDeleteState::spgstate, START_CRIT_SECTION, spgxlogVacuumLeaf::stateSrc, spgBulkDeleteState::stats, STORE_STATE, TransactionIdFollowsOrEquals(), IndexBulkDeleteResult::tuples_removed, SpGistLeafTupleData::tupstate, SpGistDeadTupleData::xid, XLOG_SPGIST_VACUUM_LEAF, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by spgprocesspending(), and spgvacuumpage().

128 {
129  Page page = BufferGetPage(buffer);
130  spgxlogVacuumLeaf xlrec;
132  OffsetNumber toPlaceholder[MaxIndexTuplesPerPage];
137  OffsetNumber predecessor[MaxIndexTuplesPerPage + 1];
138  bool deletable[MaxIndexTuplesPerPage + 1];
139  int nDeletable;
140  OffsetNumber i,
141  max = PageGetMaxOffsetNumber(page);
142 
143  memset(predecessor, 0, sizeof(predecessor));
144  memset(deletable, 0, sizeof(deletable));
145  nDeletable = 0;
146 
147  /* Scan page, identify tuples to delete, accumulate stats */
148  for (i = FirstOffsetNumber; i <= max; i++)
149  {
150  SpGistLeafTuple lt;
151 
152  lt = (SpGistLeafTuple) PageGetItem(page,
153  PageGetItemId(page, i));
154  if (lt->tupstate == SPGIST_LIVE)
155  {
157 
158  if (bds->callback(&lt->heapPtr, bds->callback_state))
159  {
160  bds->stats->tuples_removed += 1;
161  deletable[i] = true;
162  nDeletable++;
163  }
164  else
165  {
166  if (!forPending)
167  bds->stats->num_index_tuples += 1;
168  }
169 
170  /* Form predecessor map, too */
171  if (lt->nextOffset != InvalidOffsetNumber)
172  {
173  /* paranoia about corrupted chain links */
174  if (lt->nextOffset < FirstOffsetNumber ||
175  lt->nextOffset > max ||
176  predecessor[lt->nextOffset] != InvalidOffsetNumber)
177  elog(ERROR, "inconsistent tuple chain links in page %u of index \"%s\"",
179  RelationGetRelationName(index));
180  predecessor[lt->nextOffset] = i;
181  }
182  }
183  else if (lt->tupstate == SPGIST_REDIRECT)
184  {
186 
189 
190  /*
191  * Add target TID to pending list if the redirection could have
192  * happened since VACUUM started.
193  *
194  * Note: we could make a tighter test by seeing if the xid is
195  * "running" according to the active snapshot; but tqual.c doesn't
196  * currently export a suitable API, and it's not entirely clear
197  * that a tighter test is worth the cycles anyway.
198  */
199  if (TransactionIdFollowsOrEquals(dt->xid, bds->myXmin))
200  spgAddPendingTID(bds, &dt->pointer);
201  }
202  else
203  {
205  }
206  }
207 
208  if (nDeletable == 0)
209  return; /* nothing more to do */
210 
211  /*----------
212  * Figure out exactly what we have to do. We do this separately from
213  * actually modifying the page, mainly so that we have a representation
214  * that can be dumped into WAL and then the replay code can do exactly
215  * the same thing. The output of this step consists of six arrays
216  * describing four kinds of operations, to be performed in this order:
217  *
218  * toDead[]: tuple numbers to be replaced with DEAD tuples
219  * toPlaceholder[]: tuple numbers to be replaced with PLACEHOLDER tuples
220  * moveSrc[]: tuple numbers that need to be relocated to another offset
221  * (replacing the tuple there) and then replaced with PLACEHOLDER tuples
222  * moveDest[]: new locations for moveSrc tuples
223  * chainSrc[]: tuple numbers whose chain links (nextOffset) need updates
224  * chainDest[]: new values of nextOffset for chainSrc members
225  *
226  * It's easiest to figure out what we have to do by processing tuple
227  * chains, so we iterate over all the tuples (not just the deletable
228  * ones!) to identify chain heads, then chase down each chain and make
229  * work item entries for deletable tuples within the chain.
230  *----------
231  */
232  xlrec.nDead = xlrec.nPlaceholder = xlrec.nMove = xlrec.nChain = 0;
233 
234  for (i = FirstOffsetNumber; i <= max; i++)
235  {
236  SpGistLeafTuple head;
237  bool interveningDeletable;
238  OffsetNumber prevLive;
239  OffsetNumber j;
240 
241  head = (SpGistLeafTuple) PageGetItem(page,
242  PageGetItemId(page, i));
243  if (head->tupstate != SPGIST_LIVE)
244  continue; /* can't be a chain member */
245  if (predecessor[i] != 0)
246  continue; /* not a chain head */
247 
248  /* initialize ... */
249  interveningDeletable = false;
250  prevLive = deletable[i] ? InvalidOffsetNumber : i;
251 
252  /* scan down the chain ... */
253  j = head->nextOffset;
254  while (j != InvalidOffsetNumber)
255  {
256  SpGistLeafTuple lt;
257 
258  lt = (SpGistLeafTuple) PageGetItem(page,
259  PageGetItemId(page, j));
260  if (lt->tupstate != SPGIST_LIVE)
261  {
262  /* all tuples in chain should be live */
263  elog(ERROR, "unexpected SPGiST tuple state: %d",
264  lt->tupstate);
265  }
266 
267  if (deletable[j])
268  {
269  /* This tuple should be replaced by a placeholder */
270  toPlaceholder[xlrec.nPlaceholder] = j;
271  xlrec.nPlaceholder++;
272  /* previous live tuple's chain link will need an update */
273  interveningDeletable = true;
274  }
275  else if (prevLive == InvalidOffsetNumber)
276  {
277  /*
278  * This is the first live tuple in the chain. It has to move
279  * to the head position.
280  */
281  moveSrc[xlrec.nMove] = j;
282  moveDest[xlrec.nMove] = i;
283  xlrec.nMove++;
284  /* Chain updates will be applied after the move */
285  prevLive = i;
286  interveningDeletable = false;
287  }
288  else
289  {
290  /*
291  * Second or later live tuple. Arrange to re-chain it to the
292  * previous live one, if there was a gap.
293  */
294  if (interveningDeletable)
295  {
296  chainSrc[xlrec.nChain] = prevLive;
297  chainDest[xlrec.nChain] = j;
298  xlrec.nChain++;
299  }
300  prevLive = j;
301  interveningDeletable = false;
302  }
303 
304  j = lt->nextOffset;
305  }
306 
307  if (prevLive == InvalidOffsetNumber)
308  {
309  /* The chain is entirely removable, so we need a DEAD tuple */
310  toDead[xlrec.nDead] = i;
311  xlrec.nDead++;
312  }
313  else if (interveningDeletable)
314  {
315  /* One or more deletions at end of chain, so close it off */
316  chainSrc[xlrec.nChain] = prevLive;
317  chainDest[xlrec.nChain] = InvalidOffsetNumber;
318  xlrec.nChain++;
319  }
320  }
321 
322  /* sanity check ... */
323  if (nDeletable != xlrec.nDead + xlrec.nPlaceholder + xlrec.nMove)
324  elog(ERROR, "inconsistent counts of deletable tuples");
325 
326  /* Do the updates */
328 
329  spgPageIndexMultiDelete(&bds->spgstate, page,
330  toDead, xlrec.nDead,
333 
334  spgPageIndexMultiDelete(&bds->spgstate, page,
335  toPlaceholder, xlrec.nPlaceholder,
338 
339  /*
340  * We implement the move step by swapping the item pointers of the source
341  * and target tuples, then replacing the newly-source tuples with
342  * placeholders. This is perhaps unduly friendly with the page data
343  * representation, but it's fast and doesn't risk page overflow when a
344  * tuple to be relocated is large.
345  */
346  for (i = 0; i < xlrec.nMove; i++)
347  {
348  ItemId idSrc = PageGetItemId(page, moveSrc[i]);
349  ItemId idDest = PageGetItemId(page, moveDest[i]);
350  ItemIdData tmp;
351 
352  tmp = *idSrc;
353  *idSrc = *idDest;
354  *idDest = tmp;
355  }
356 
357  spgPageIndexMultiDelete(&bds->spgstate, page,
358  moveSrc, xlrec.nMove,
361 
362  for (i = 0; i < xlrec.nChain; i++)
363  {
364  SpGistLeafTuple lt;
365 
366  lt = (SpGistLeafTuple) PageGetItem(page,
367  PageGetItemId(page, chainSrc[i]));
368  Assert(lt->tupstate == SPGIST_LIVE);
369  lt->nextOffset = chainDest[i];
370  }
371 
373 
374  if (RelationNeedsWAL(index))
375  {
376  XLogRecPtr recptr;
377 
378  XLogBeginInsert();
379 
380  STORE_STATE(&bds->spgstate, xlrec.stateSrc);
381 
382  XLogRegisterData((char *) &xlrec, SizeOfSpgxlogVacuumLeaf);
383  /* sizeof(xlrec) should be a multiple of sizeof(OffsetNumber) */
384  XLogRegisterData((char *) toDead, sizeof(OffsetNumber) * xlrec.nDead);
385  XLogRegisterData((char *) toPlaceholder, sizeof(OffsetNumber) * xlrec.nPlaceholder);
386  XLogRegisterData((char *) moveSrc, sizeof(OffsetNumber) * xlrec.nMove);
387  XLogRegisterData((char *) moveDest, sizeof(OffsetNumber) * xlrec.nMove);
388  XLogRegisterData((char *) chainSrc, sizeof(OffsetNumber) * xlrec.nChain);
389  XLogRegisterData((char *) chainDest, sizeof(OffsetNumber) * xlrec.nChain);
390 
392 
393  recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_LEAF);
394 
395  PageSetLSN(page, recptr);
396  }
397 
399 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:59
#define SPGIST_DEAD
double tuples_removed
Definition: genam.h:77
#define SPGIST_REDIRECT
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define SPGIST_PLACEHOLDER
#define END_CRIT_SECTION()
Definition: miscadmin.h:132
bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition: transam.c:349
#define START_CRIT_SECTION()
Definition: miscadmin.h:130
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:354
uint16 OffsetNumber
Definition: off.h:24
void spgPageIndexMultiDelete(SpGistState *state, Page page, OffsetNumber *itemnos, int nitems, int firststate, int reststate, BlockNumber blkno, OffsetNumber offnum)
Definition: spgdoinsert.c:131
ItemPointerData pointer
#define ERROR
Definition: elog.h:43
spgxlogState stateSrc
Definition: spgxlog.h:208
#define FirstOffsetNumber
Definition: off.h:27
#define REGBUF_STANDARD
Definition: xloginsert.h:35
IndexBulkDeleteCallback callback
Definition: spgvacuum.c:46
SpGistDeadTupleData * SpGistDeadTuple
#define RelationGetRelationName(relation)
Definition: rel.h:437
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:232
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
unsigned int tupstate
TransactionId myXmin
Definition: spgvacuum.c:52
#define STORE_STATE(s, d)
OffsetNumber nextOffset
#define InvalidOffsetNumber
Definition: off.h:26
#define SizeOfSpgxlogVacuumLeaf
Definition: spgxlog.h:223
void * callback_state
Definition: spgvacuum.c:47
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:675
uint16 nPlaceholder
Definition: spgxlog.h:204
IndexBulkDeleteResult * stats
Definition: spgvacuum.c:45
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:211
#define SPGIST_LIVE
#define InvalidBlockNumber
Definition: block.h:33
OffsetNumber nextOffset
#define XLOG_SPGIST_VACUUM_LEAF
Definition: spgxlog.h:27
#define RelationNeedsWAL(relation)
Definition: rel.h:506
static void spgAddPendingTID(spgBulkDeleteState *bds, ItemPointer tid)
Definition: spgvacuum.c:64
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
#define MaxIndexTuplesPerPage
Definition: itup.h:137
SpGistState spgstate
Definition: spgvacuum.c:50
int i
ItemPointerData heapPtr
#define elog
Definition: elog.h:219
SpGistLeafTupleData * SpGistLeafTuple
void XLogBeginInsert(void)
Definition: xloginsert.c:120
#define PageSetLSN(page, lsn)
Definition: bufpage.h:365
double num_index_tuples
Definition: genam.h:76
#define PageGetItem(page, itemId)
Definition: bufpage.h:337
Pointer Page
Definition: bufpage.h:74
static void vacuumLeafRoot ( spgBulkDeleteState bds,
Relation  index,
Buffer  buffer 
)
static

Definition at line 407 of file spgvacuum.c.

References Assert, BufferGetPage, spgBulkDeleteState::callback, spgBulkDeleteState::callback_state, elog, END_CRIT_SECTION, ERROR, FirstOffsetNumber, SpGistLeafTupleData::heapPtr, i, ItemPointerIsValid, MarkBufferDirty(), MaxIndexTuplesPerPage, spgxlogVacuumRoot::nDelete, IndexBulkDeleteResult::num_index_tuples, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageIndexMultiDelete(), PageSetLSN, REGBUF_STANDARD, RelationNeedsWAL, SizeOfSpgxlogVacuumRoot, SPGIST_LIVE, spgBulkDeleteState::spgstate, START_CRIT_SECTION, spgxlogVacuumRoot::stateSrc, spgBulkDeleteState::stats, STORE_STATE, IndexBulkDeleteResult::tuples_removed, SpGistLeafTupleData::tupstate, XLOG_SPGIST_VACUUM_ROOT, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by spgvacuumpage().

408 {
409  Page page = BufferGetPage(buffer);
410  spgxlogVacuumRoot xlrec;
412  OffsetNumber i,
413  max = PageGetMaxOffsetNumber(page);
414 
415  xlrec.nDelete = 0;
416 
417  /* Scan page, identify tuples to delete, accumulate stats */
418  for (i = FirstOffsetNumber; i <= max; i++)
419  {
420  SpGistLeafTuple lt;
421 
422  lt = (SpGistLeafTuple) PageGetItem(page,
423  PageGetItemId(page, i));
424  if (lt->tupstate == SPGIST_LIVE)
425  {
427 
428  if (bds->callback(&lt->heapPtr, bds->callback_state))
429  {
430  bds->stats->tuples_removed += 1;
431  toDelete[xlrec.nDelete] = i;
432  xlrec.nDelete++;
433  }
434  else
435  {
436  bds->stats->num_index_tuples += 1;
437  }
438  }
439  else
440  {
441  /* all tuples on root should be live */
442  elog(ERROR, "unexpected SPGiST tuple state: %d",
443  lt->tupstate);
444  }
445  }
446 
447  if (xlrec.nDelete == 0)
448  return; /* nothing more to do */
449 
450  /* Do the update */
452 
453  /* The tuple numbers are in order, so we can use PageIndexMultiDelete */
454  PageIndexMultiDelete(page, toDelete, xlrec.nDelete);
455 
457 
458  if (RelationNeedsWAL(index))
459  {
460  XLogRecPtr recptr;
461 
462  XLogBeginInsert();
463 
464  /* Prepare WAL record */
465  STORE_STATE(&bds->spgstate, xlrec.stateSrc);
466 
467  XLogRegisterData((char *) &xlrec, SizeOfSpgxlogVacuumRoot);
468  /* sizeof(xlrec) should be a multiple of sizeof(OffsetNumber) */
469  XLogRegisterData((char *) toDelete,
470  sizeof(OffsetNumber) * xlrec.nDelete);
471 
473 
474  recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_ROOT);
475 
476  PageSetLSN(page, recptr);
477  }
478 
480 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:59
double tuples_removed
Definition: genam.h:77
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define END_CRIT_SECTION()
Definition: miscadmin.h:132
#define START_CRIT_SECTION()
Definition: miscadmin.h:130
uint16 nDelete
Definition: spgxlog.h:228
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:354
uint16 OffsetNumber
Definition: off.h:24
#define XLOG_SPGIST_VACUUM_ROOT
Definition: spgxlog.h:28
#define ERROR
Definition: elog.h:43
#define FirstOffsetNumber
Definition: off.h:27
#define REGBUF_STANDARD
Definition: xloginsert.h:35
IndexBulkDeleteCallback callback
Definition: spgvacuum.c:46
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:232
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
unsigned int tupstate
#define STORE_STATE(s, d)
void * callback_state
Definition: spgvacuum.c:47
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:675
IndexBulkDeleteResult * stats
Definition: spgvacuum.c:45
spgxlogState stateSrc
Definition: spgxlog.h:230
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:211
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:836
#define SPGIST_LIVE
#define RelationNeedsWAL(relation)
Definition: rel.h:506
#define MaxIndexTuplesPerPage
Definition: itup.h:137
SpGistState spgstate
Definition: spgvacuum.c:50
int i
#define SizeOfSpgxlogVacuumRoot
Definition: spgxlog.h:236
ItemPointerData heapPtr
#define elog
Definition: elog.h:219
SpGistLeafTupleData * SpGistLeafTuple
void XLogBeginInsert(void)
Definition: xloginsert.c:120
#define PageSetLSN(page, lsn)
Definition: bufpage.h:365
double num_index_tuples
Definition: genam.h:76
#define PageGetItem(page, itemId)
Definition: bufpage.h:337
Pointer Page
Definition: bufpage.h:74
static void vacuumRedirectAndPlaceholder ( Relation  index,
Buffer  buffer 
)
static

Definition at line 492 of file spgvacuum.c.

References Assert, BufferGetPage, END_CRIT_SECTION, FirstOffsetNumber, spgxlogVacuumRedirect::firstPlaceholder, i, InvalidOffsetNumber, InvalidTransactionId, ItemPointerSetInvalid, MarkBufferDirty(), MaxIndexTuplesPerPage, spgxlogVacuumRedirect::newestRedirectXid, SpGistPageOpaqueData::nPlaceholder, SpGistPageOpaqueData::nRedirection, spgxlogVacuumRedirect::nToPlaceholder, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageIndexMultiDelete(), PageSetLSN, SpGistDeadTupleData::pointer, RecentGlobalXmin, REGBUF_STANDARD, RelationNeedsWAL, SizeOfSpgxlogVacuumRedirect, SPGIST_PLACEHOLDER, SPGIST_REDIRECT, SpGistPageGetOpaque, START_CRIT_SECTION, TransactionIdIsValid, TransactionIdPrecedes(), SpGistDeadTupleData::tupstate, SpGistDeadTupleData::xid, XLOG_SPGIST_VACUUM_REDIRECT, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by spgprocesspending(), and spgvacuumpage().

493 {
494  Page page = BufferGetPage(buffer);
495  SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
496  OffsetNumber i,
497  max = PageGetMaxOffsetNumber(page),
498  firstPlaceholder = InvalidOffsetNumber;
499  bool hasNonPlaceholder = false;
500  bool hasUpdate = false;
501  OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPage];
503  spgxlogVacuumRedirect xlrec;
504 
505  xlrec.nToPlaceholder = 0;
507 
509 
510  /*
511  * Scan backwards to convert old redirection tuples to placeholder tuples,
512  * and identify location of last non-placeholder tuple while at it.
513  */
514  for (i = max;
515  i >= FirstOffsetNumber &&
516  (opaque->nRedirection > 0 || !hasNonPlaceholder);
517  i--)
518  {
519  SpGistDeadTuple dt;
520 
521  dt = (SpGistDeadTuple) PageGetItem(page, PageGetItemId(page, i));
522 
523  if (dt->tupstate == SPGIST_REDIRECT &&
525  {
527  Assert(opaque->nRedirection > 0);
528  opaque->nRedirection--;
529  opaque->nPlaceholder++;
530 
531  /* remember newest XID among the removed redirects */
534  xlrec.newestRedirectXid = dt->xid;
535 
537 
538  itemToPlaceholder[xlrec.nToPlaceholder] = i;
539  xlrec.nToPlaceholder++;
540 
541  hasUpdate = true;
542  }
543 
544  if (dt->tupstate == SPGIST_PLACEHOLDER)
545  {
546  if (!hasNonPlaceholder)
547  firstPlaceholder = i;
548  }
549  else
550  {
551  hasNonPlaceholder = true;
552  }
553  }
554 
555  /*
556  * Any placeholder tuples at the end of page can safely be removed. We
557  * can't remove ones before the last non-placeholder, though, because we
558  * can't alter the offset numbers of non-placeholder tuples.
559  */
560  if (firstPlaceholder != InvalidOffsetNumber)
561  {
562  /*
563  * We do not store this array to rdata because it's easy to recreate.
564  */
565  for (i = firstPlaceholder; i <= max; i++)
566  itemnos[i - firstPlaceholder] = i;
567 
568  i = max - firstPlaceholder + 1;
569  Assert(opaque->nPlaceholder >= i);
570  opaque->nPlaceholder -= i;
571 
572  /* The array is surely sorted, so can use PageIndexMultiDelete */
573  PageIndexMultiDelete(page, itemnos, i);
574 
575  hasUpdate = true;
576  }
577 
578  xlrec.firstPlaceholder = firstPlaceholder;
579 
580  if (hasUpdate)
582 
583  if (hasUpdate && RelationNeedsWAL(index))
584  {
585  XLogRecPtr recptr;
586 
587  XLogBeginInsert();
588 
590  XLogRegisterData((char *) itemToPlaceholder,
591  sizeof(OffsetNumber) * xlrec.nToPlaceholder);
592 
594 
595  recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_REDIRECT);
596 
597  PageSetLSN(page, recptr);
598  }
599 
601 }
#define XLOG_SPGIST_VACUUM_REDIRECT
Definition: spgxlog.h:29
#define SPGIST_REDIRECT
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define SPGIST_PLACEHOLDER
OffsetNumber firstPlaceholder
Definition: spgxlog.h:241
#define END_CRIT_SECTION()
Definition: miscadmin.h:132
#define START_CRIT_SECTION()
Definition: miscadmin.h:130
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:354
uint16 OffsetNumber
Definition: off.h:24
TransactionId newestRedirectXid
Definition: spgxlog.h:242
ItemPointerData pointer
TransactionId RecentGlobalXmin
Definition: snapmgr.c:166
#define FirstOffsetNumber
Definition: off.h:27
#define REGBUF_STANDARD
Definition: xloginsert.h:35
SpGistDeadTupleData * SpGistDeadTuple
#define InvalidTransactionId
Definition: transam.h:31
#define SizeOfSpgxlogVacuumRedirect
Definition: spgxlog.h:248
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:232
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
unsigned int tupstate
#define InvalidOffsetNumber
Definition: off.h:26
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:675
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:211
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:836
#define RelationNeedsWAL(relation)
Definition: rel.h:506
#define SpGistPageGetOpaque(page)
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:149
#define MaxIndexTuplesPerPage
Definition: itup.h:137
int i
#define TransactionIdIsValid(xid)
Definition: transam.h:41
void XLogBeginInsert(void)
Definition: xloginsert.c:120
#define PageSetLSN(page, lsn)
Definition: bufpage.h:365
#define PageGetItem(page, itemId)
Definition: bufpage.h:337
Pointer Page
Definition: bufpage.h:74