Header And Logo

PostgreSQL
| The world's most advanced open source database.

gistvacuum.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * gistvacuum.c
00004  *    vacuuming routines for the postgres GiST index access method.
00005  *
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/access/gist/gistvacuum.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include "access/genam.h"
00018 #include "access/gist_private.h"
00019 #include "commands/vacuum.h"
00020 #include "miscadmin.h"
00021 #include "storage/indexfsm.h"
00022 #include "storage/lmgr.h"
00023 
00024 
00025 /*
00026  * VACUUM cleanup: update FSM
00027  */
00028 Datum
00029 gistvacuumcleanup(PG_FUNCTION_ARGS)
00030 {
00031     IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
00032     IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
00033     Relation    rel = info->index;
00034     BlockNumber npages,
00035                 blkno;
00036     BlockNumber totFreePages;
00037     bool        needLock;
00038 
00039     /* No-op in ANALYZE ONLY mode */
00040     if (info->analyze_only)
00041         PG_RETURN_POINTER(stats);
00042 
00043     /* Set up all-zero stats if gistbulkdelete wasn't called */
00044     if (stats == NULL)
00045     {
00046         stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
00047         /* use heap's tuple count */
00048         stats->num_index_tuples = info->num_heap_tuples;
00049         stats->estimated_count = info->estimated_count;
00050 
00051         /*
00052          * XXX the above is wrong if index is partial.  Would it be OK to just
00053          * return NULL, or is there work we must do below?
00054          */
00055     }
00056 
00057     /*
00058      * Need lock unless it's local to this backend.
00059      */
00060     needLock = !RELATION_IS_LOCAL(rel);
00061 
00062     /* try to find deleted pages */
00063     if (needLock)
00064         LockRelationForExtension(rel, ExclusiveLock);
00065     npages = RelationGetNumberOfBlocks(rel);
00066     if (needLock)
00067         UnlockRelationForExtension(rel, ExclusiveLock);
00068 
00069     totFreePages = 0;
00070     for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
00071     {
00072         Buffer      buffer;
00073         Page        page;
00074 
00075         vacuum_delay_point();
00076 
00077         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
00078                                     info->strategy);
00079         LockBuffer(buffer, GIST_SHARE);
00080         page = (Page) BufferGetPage(buffer);
00081 
00082         if (PageIsNew(page) || GistPageIsDeleted(page))
00083         {
00084             totFreePages++;
00085             RecordFreeIndexPage(rel, blkno);
00086         }
00087         UnlockReleaseBuffer(buffer);
00088     }
00089 
00090     /* Finally, vacuum the FSM */
00091     IndexFreeSpaceMapVacuum(info->index);
00092 
00093     /* return statistics */
00094     stats->pages_free = totFreePages;
00095     if (needLock)
00096         LockRelationForExtension(rel, ExclusiveLock);
00097     stats->num_pages = RelationGetNumberOfBlocks(rel);
00098     if (needLock)
00099         UnlockRelationForExtension(rel, ExclusiveLock);
00100 
00101     PG_RETURN_POINTER(stats);
00102 }
00103 
00104 typedef struct GistBDItem
00105 {
00106     GistNSN     parentlsn;
00107     BlockNumber blkno;
00108     struct GistBDItem *next;
00109 } GistBDItem;
00110 
00111 static void
00112 pushStackIfSplited(Page page, GistBDItem *stack)
00113 {
00114     GISTPageOpaque opaque = GistPageGetOpaque(page);
00115 
00116     if (stack->blkno != GIST_ROOT_BLKNO && !XLogRecPtrIsInvalid(stack->parentlsn) &&
00117         (GistFollowRight(page) || stack->parentlsn < GistPageGetNSN(page)) &&
00118         opaque->rightlink != InvalidBlockNumber /* sanity check */ )
00119     {
00120         /* split page detected, install right link to the stack */
00121 
00122         GistBDItem *ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
00123 
00124         ptr->blkno = opaque->rightlink;
00125         ptr->parentlsn = stack->parentlsn;
00126         ptr->next = stack->next;
00127         stack->next = ptr;
00128     }
00129 }
00130 
00131 
00132 /*
00133  * Bulk deletion of all index entries pointing to a set of heap tuples and
00134  * check invalid tuples left after upgrade.
00135  * The set of target tuples is specified via a callback routine that tells
00136  * whether any given heap tuple (identified by ItemPointer) is being deleted.
00137  *
00138  * Result: a palloc'd struct containing statistical info for VACUUM displays.
00139  */
00140 Datum
00141 gistbulkdelete(PG_FUNCTION_ARGS)
00142 {
00143     IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
00144     IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
00145     IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
00146     void       *callback_state = (void *) PG_GETARG_POINTER(3);
00147     Relation    rel = info->index;
00148     GistBDItem *stack,
00149                *ptr;
00150 
00151     /* first time through? */
00152     if (stats == NULL)
00153         stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
00154     /* we'll re-count the tuples each time */
00155     stats->estimated_count = false;
00156     stats->num_index_tuples = 0;
00157 
00158     stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
00159     stack->blkno = GIST_ROOT_BLKNO;
00160 
00161     while (stack)
00162     {
00163         Buffer      buffer;
00164         Page        page;
00165         OffsetNumber i,
00166                     maxoff;
00167         IndexTuple  idxtuple;
00168         ItemId      iid;
00169 
00170         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
00171                                     RBM_NORMAL, info->strategy);
00172         LockBuffer(buffer, GIST_SHARE);
00173         gistcheckpage(rel, buffer);
00174         page = (Page) BufferGetPage(buffer);
00175 
00176         if (GistPageIsLeaf(page))
00177         {
00178             OffsetNumber todelete[MaxOffsetNumber];
00179             int         ntodelete = 0;
00180 
00181             LockBuffer(buffer, GIST_UNLOCK);
00182             LockBuffer(buffer, GIST_EXCLUSIVE);
00183 
00184             page = (Page) BufferGetPage(buffer);
00185             if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page))
00186             {
00187                 /* only the root can become non-leaf during relock */
00188                 UnlockReleaseBuffer(buffer);
00189                 /* one more check */
00190                 continue;
00191             }
00192 
00193             /*
00194              * check for split proceeded after look at parent, we should check
00195              * it after relock
00196              */
00197             pushStackIfSplited(page, stack);
00198 
00199             /*
00200              * Remove deletable tuples from page
00201              */
00202 
00203             maxoff = PageGetMaxOffsetNumber(page);
00204 
00205             for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
00206             {
00207                 iid = PageGetItemId(page, i);
00208                 idxtuple = (IndexTuple) PageGetItem(page, iid);
00209 
00210                 if (callback(&(idxtuple->t_tid), callback_state))
00211                 {
00212                     todelete[ntodelete] = i - ntodelete;
00213                     ntodelete++;
00214                     stats->tuples_removed += 1;
00215                 }
00216                 else
00217                     stats->num_index_tuples += 1;
00218             }
00219 
00220             if (ntodelete)
00221             {
00222                 START_CRIT_SECTION();
00223 
00224                 MarkBufferDirty(buffer);
00225 
00226                 for (i = 0; i < ntodelete; i++)
00227                     PageIndexTupleDelete(page, todelete[i]);
00228                 GistMarkTuplesDeleted(page);
00229 
00230                 if (RelationNeedsWAL(rel))
00231                 {
00232                     XLogRecPtr  recptr;
00233 
00234                     recptr = gistXLogUpdate(rel->rd_node, buffer,
00235                                             todelete, ntodelete,
00236                                             NULL, 0, InvalidBuffer);
00237                     PageSetLSN(page, recptr);
00238                 }
00239                 else
00240                     PageSetLSN(page, gistGetFakeLSN(rel));
00241 
00242                 END_CRIT_SECTION();
00243             }
00244 
00245         }
00246         else
00247         {
00248             /* check for split proceeded after look at parent */
00249             pushStackIfSplited(page, stack);
00250 
00251             maxoff = PageGetMaxOffsetNumber(page);
00252 
00253             for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
00254             {
00255                 iid = PageGetItemId(page, i);
00256                 idxtuple = (IndexTuple) PageGetItem(page, iid);
00257 
00258                 ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
00259                 ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
00260                 ptr->parentlsn = PageGetLSN(page);
00261                 ptr->next = stack->next;
00262                 stack->next = ptr;
00263 
00264                 if (GistTupleIsInvalid(idxtuple))
00265                     ereport(LOG,
00266                             (errmsg("index \"%s\" contains an inner tuple marked as invalid",
00267                                     RelationGetRelationName(rel)),
00268                              errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
00269                              errhint("Please REINDEX it.")));
00270             }
00271         }
00272 
00273         UnlockReleaseBuffer(buffer);
00274 
00275         ptr = stack->next;
00276         pfree(stack);
00277         stack = ptr;
00278 
00279         vacuum_delay_point();
00280     }
00281 
00282     PG_RETURN_POINTER(stats);
00283 }