1 /*-------------------------------------------------------------------------
4 * vacuuming routines for the postgres GiST index access method.
7 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/gist/gistvacuum.c
13 *-------------------------------------------------------------------------
17 #include "access/genam.h"
18 #include "access/gist_private.h"
19 #include "catalog/storage.h"
20 #include "commands/vacuum.h"
21 #include "miscadmin.h"
22 #include "storage/bufmgr.h"
23 #include "storage/freespace.h"
24 #include "storage/indexfsm.h"
25 #include "storage/lmgr.h"
26 #include "utils/memutils.h"
30 * VACUUM cleanup: update FSM
33 gistvacuumcleanup(PG_FUNCTION_ARGS)
35 IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
36 IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
37 Relation rel = info->index;
40 BlockNumber totFreePages;
43 /* No-op in ANALYZE ONLY mode */
44 if (info->analyze_only)
45 PG_RETURN_POINTER(stats);
47 /* Set up all-zero stats if gistbulkdelete wasn't called */
50 stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
51 /* use heap's tuple count */
52 stats->num_index_tuples = info->num_heap_tuples;
53 stats->estimated_count = info->estimated_count;
56 * XXX the above is wrong if index is partial. Would it be OK to just
57 * return NULL, or is there work we must do below?
62 * Need lock unless it's local to this backend.
64 needLock = !RELATION_IS_LOCAL(rel);
66 /* try to find deleted pages */
68 LockRelationForExtension(rel, ExclusiveLock);
69 npages = RelationGetNumberOfBlocks(rel);
71 UnlockRelationForExtension(rel, ExclusiveLock);
74 for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
81 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
83 LockBuffer(buffer, GIST_SHARE);
84 page = (Page) BufferGetPage(buffer);
86 if (PageIsNew(page) || GistPageIsDeleted(page))
89 RecordFreeIndexPage(rel, blkno);
91 UnlockReleaseBuffer(buffer);
94 /* Finally, vacuum the FSM */
95 IndexFreeSpaceMapVacuum(info->index);
97 /* return statistics */
98 stats->pages_free = totFreePages;
100 LockRelationForExtension(rel, ExclusiveLock);
101 stats->num_pages = RelationGetNumberOfBlocks(rel);
103 UnlockRelationForExtension(rel, ExclusiveLock);
105 PG_RETURN_POINTER(stats);
108 typedef struct GistBDItem
112 struct GistBDItem *next;
116 pushStackIfSplited(Page page, GistBDItem *stack)
118 GISTPageOpaque opaque = GistPageGetOpaque(page);
120 if (stack->blkno != GIST_ROOT_BLKNO && !XLogRecPtrIsInvalid(stack->parentlsn) &&
121 (GistFollowRight(page) || XLByteLT(stack->parentlsn, opaque->nsn)) &&
122 opaque->rightlink != InvalidBlockNumber /* sanity check */ )
124 /* split page detected, install right link to the stack */
126 GistBDItem *ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
128 ptr->blkno = opaque->rightlink;
129 ptr->parentlsn = stack->parentlsn;
130 ptr->next = stack->next;
137 * Bulk deletion of all index entries pointing to a set of heap tuples and
138 * check invalid tuples after crash recovery.
139 * The set of target tuples is specified via a callback routine that tells
140 * whether any given heap tuple (identified by ItemPointer) is being deleted.
142 * Result: a palloc'd struct containing statistical info for VACUUM displays.
145 gistbulkdelete(PG_FUNCTION_ARGS)
147 IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
148 IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
149 IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
150 void *callback_state = (void *) PG_GETARG_POINTER(3);
151 Relation rel = info->index;
155 /* first time through? */
157 stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
158 /* we'll re-count the tuples each time */
159 stats->estimated_count = false;
160 stats->num_index_tuples = 0;
162 stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
163 stack->blkno = GIST_ROOT_BLKNO;
174 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
175 RBM_NORMAL, info->strategy);
176 LockBuffer(buffer, GIST_SHARE);
177 gistcheckpage(rel, buffer);
178 page = (Page) BufferGetPage(buffer);
180 if (GistPageIsLeaf(page))
182 OffsetNumber todelete[MaxOffsetNumber];
185 LockBuffer(buffer, GIST_UNLOCK);
186 LockBuffer(buffer, GIST_EXCLUSIVE);
188 page = (Page) BufferGetPage(buffer);
189 if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page))
191 /* only the root can become non-leaf during relock */
192 UnlockReleaseBuffer(buffer);
198 * check for split proceeded after look at parent, we should check
201 pushStackIfSplited(page, stack);
204 * Remove deletable tuples from page
207 maxoff = PageGetMaxOffsetNumber(page);
209 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
211 iid = PageGetItemId(page, i);
212 idxtuple = (IndexTuple) PageGetItem(page, iid);
214 if (callback(&(idxtuple->t_tid), callback_state))
216 todelete[ntodelete] = i - ntodelete;
218 stats->tuples_removed += 1;
221 stats->num_index_tuples += 1;
226 START_CRIT_SECTION();
228 MarkBufferDirty(buffer);
230 for (i = 0; i < ntodelete; i++)
231 PageIndexTupleDelete(page, todelete[i]);
232 GistMarkTuplesDeleted(page);
234 if (RelationNeedsWAL(rel))
238 recptr = gistXLogUpdate(rel->rd_node, buffer,
240 NULL, 0, InvalidBuffer);
241 PageSetLSN(page, recptr);
242 PageSetTLI(page, ThisTimeLineID);
245 PageSetLSN(page, GetXLogRecPtrForTemp());
253 /* check for split proceeded after look at parent */
254 pushStackIfSplited(page, stack);
256 maxoff = PageGetMaxOffsetNumber(page);
258 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
260 iid = PageGetItemId(page, i);
261 idxtuple = (IndexTuple) PageGetItem(page, iid);
263 ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
264 ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
265 ptr->parentlsn = PageGetLSN(page);
266 ptr->next = stack->next;
269 if (GistTupleIsInvalid(idxtuple))
271 (errmsg("index \"%s\" contains an inner tuple marked as invalid",
272 RelationGetRelationName(rel)),
273 errdetail("This is caused by an incomplete page split at crash recovery before upgrading to 9.1."),
274 errhint("Please REINDEX it.")));
278 UnlockReleaseBuffer(buffer);
284 vacuum_delay_point();
287 PG_RETURN_POINTER(stats);