2005-06-20 12:29:37 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* gistvacuum.c
|
2010-02-08 06:17:31 +01:00
|
|
|
* vacuuming routines for the postgres GiST index access method.
|
2005-06-20 12:29:37 +02:00
|
|
|
*
|
|
|
|
*
|
2010-01-02 17:58:17 +01:00
|
|
|
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
2005-06-20 12:29:37 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-02-08 06:17:31 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.48 2010/02/08 05:17:31 tgl Exp $
|
2005-06-20 12:29:37 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/genam.h"
|
|
|
|
#include "access/gist_private.h"
|
2008-11-19 11:34:52 +01:00
|
|
|
#include "catalog/storage.h"
|
2005-06-20 12:29:37 +02:00
|
|
|
#include "commands/vacuum.h"
|
|
|
|
#include "miscadmin.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "storage/bufmgr.h"
|
2005-06-20 12:29:37 +02:00
|
|
|
#include "storage/freespace.h"
|
2008-09-30 12:52:14 +02:00
|
|
|
#include "storage/indexfsm.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "storage/lmgr.h"
|
2006-07-11 19:04:13 +02:00
|
|
|
#include "utils/memutils.h"
|
2005-06-20 12:29:37 +02:00
|
|
|
|
|
|
|
|
2006-02-12 00:31:34 +01:00
|
|
|
typedef struct GistBulkDeleteResult
|
|
|
|
{
|
|
|
|
IndexBulkDeleteResult std; /* common state */
|
2010-02-08 05:33:55 +01:00
|
|
|
bool needReindex;
|
2006-02-12 00:31:34 +01:00
|
|
|
} GistBulkDeleteResult;
|
2005-06-20 12:29:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
2010-02-08 05:33:55 +01:00
|
|
|
* VACUUM cleanup: update FSM
|
2005-06-20 12:29:37 +02:00
|
|
|
*/
|
|
|
|
Datum
|
2005-09-22 22:44:36 +02:00
|
|
|
gistvacuumcleanup(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2006-05-03 00:25:10 +02:00
|
|
|
IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
|
|
|
|
GistBulkDeleteResult *stats = (GistBulkDeleteResult *) PG_GETARG_POINTER(1);
|
|
|
|
Relation rel = info->index;
|
2005-09-22 22:44:36 +02:00
|
|
|
BlockNumber npages,
|
|
|
|
blkno;
|
2009-06-11 16:49:15 +02:00
|
|
|
BlockNumber totFreePages;
|
2005-09-22 22:44:36 +02:00
|
|
|
BlockNumber lastBlock = GIST_ROOT_BLKNO,
|
|
|
|
lastFilledBlock = GIST_ROOT_BLKNO;
|
|
|
|
bool needLock;
|
2005-06-20 12:29:37 +02:00
|
|
|
|
2009-03-24 21:17:18 +01:00
|
|
|
/* No-op in ANALYZE ONLY mode */
|
|
|
|
if (info->analyze_only)
|
|
|
|
PG_RETURN_POINTER(stats);
|
|
|
|
|
2006-05-03 00:25:10 +02:00
|
|
|
/* Set up all-zero stats if gistbulkdelete wasn't called */
|
|
|
|
if (stats == NULL)
|
|
|
|
{
|
|
|
|
stats = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult));
|
|
|
|
/* use heap's tuple count */
|
|
|
|
stats->std.num_index_tuples = info->num_heap_tuples;
|
2009-06-07 00:13:52 +02:00
|
|
|
stats->std.estimated_count = info->estimated_count;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-05-03 00:25:10 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* XXX the above is wrong if index is partial. Would it be OK to just
|
|
|
|
* return NULL, or is there work we must do below?
|
2006-05-03 00:25:10 +02:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2010-02-08 05:33:55 +01:00
|
|
|
if (stats->needReindex)
|
2005-09-22 20:49:45 +02:00
|
|
|
ereport(NOTICE,
|
|
|
|
(errmsg("index \"%s\" needs VACUUM FULL or REINDEX to finish crash recovery",
|
|
|
|
RelationGetRelationName(rel))));
|
2005-06-20 12:29:37 +02:00
|
|
|
|
2006-07-31 22:09:10 +02:00
|
|
|
/*
|
2010-02-08 05:33:55 +01:00
|
|
|
* Need lock unless it's local to this backend.
|
2006-07-31 22:09:10 +02:00
|
|
|
*/
|
2010-02-08 05:33:55 +01:00
|
|
|
needLock = !RELATION_IS_LOCAL(rel);
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
/* try to find deleted pages */
|
2005-06-27 14:45:23 +02:00
|
|
|
if (needLock)
|
|
|
|
LockRelationForExtension(rel, ExclusiveLock);
|
2005-06-20 12:29:37 +02:00
|
|
|
npages = RelationGetNumberOfBlocks(rel);
|
2005-06-27 14:45:23 +02:00
|
|
|
if (needLock)
|
|
|
|
UnlockRelationForExtension(rel, ExclusiveLock);
|
|
|
|
|
2008-09-30 12:52:14 +02:00
|
|
|
totFreePages = 0;
|
2005-09-22 22:44:36 +02:00
|
|
|
for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
|
|
|
|
{
|
2006-02-14 17:39:32 +01:00
|
|
|
Buffer buffer;
|
2005-09-22 22:44:36 +02:00
|
|
|
Page page;
|
|
|
|
|
2006-02-14 17:39:32 +01:00
|
|
|
vacuum_delay_point();
|
|
|
|
|
Unite ReadBufferWithFork, ReadBufferWithStrategy, and ZeroOrReadBuffer
functions into one ReadBufferExtended function, that takes the strategy
and mode as argument. There's three modes, RBM_NORMAL which is the default
used by plain ReadBuffer(), RBM_ZERO, which replaces ZeroOrReadBuffer, and
a new mode RBM_ZERO_ON_ERROR, which allows callers to read corrupt pages
without throwing an error. The FSM needs the new mode to recover from
corrupt pages, which could happend if we crash after extending an FSM file,
and the new page is "torn".
Add fork number to some error messages in bufmgr.c, that still lacked it.
2008-10-31 16:05:00 +01:00
|
|
|
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
|
|
|
|
info->strategy);
|
2005-09-22 22:44:36 +02:00
|
|
|
LockBuffer(buffer, GIST_SHARE);
|
|
|
|
page = (Page) BufferGetPage(buffer);
|
|
|
|
|
2005-11-06 23:39:21 +01:00
|
|
|
if (PageIsNew(page) || GistPageIsDeleted(page))
|
2005-09-22 22:44:36 +02:00
|
|
|
{
|
2006-09-21 22:31:22 +02:00
|
|
|
totFreePages++;
|
2008-09-30 12:52:14 +02:00
|
|
|
RecordFreeIndexPage(rel, blkno);
|
2005-09-22 22:44:36 +02:00
|
|
|
}
|
|
|
|
else
|
2005-06-20 12:29:37 +02:00
|
|
|
lastFilledBlock = blkno;
|
2006-04-01 01:32:07 +02:00
|
|
|
UnlockReleaseBuffer(buffer);
|
2005-06-20 12:29:37 +02:00
|
|
|
}
|
2005-09-22 22:44:36 +02:00
|
|
|
lastBlock = npages - 1;
|
|
|
|
|
2008-10-06 10:04:11 +02:00
|
|
|
/* Finally, vacuum the FSM */
|
|
|
|
IndexFreeSpaceMapVacuum(info->index);
|
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
/* return statistics */
|
2006-09-21 22:31:22 +02:00
|
|
|
stats->std.pages_free = totFreePages;
|
2005-06-27 14:45:23 +02:00
|
|
|
if (needLock)
|
|
|
|
LockRelationForExtension(rel, ExclusiveLock);
|
2006-02-12 00:31:34 +01:00
|
|
|
stats->std.num_pages = RelationGetNumberOfBlocks(rel);
|
2005-06-27 14:45:23 +02:00
|
|
|
if (needLock)
|
|
|
|
UnlockRelationForExtension(rel, ExclusiveLock);
|
2005-06-20 12:29:37 +02:00
|
|
|
|
|
|
|
PG_RETURN_POINTER(stats);
|
|
|
|
}
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
typedef struct GistBDItem
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
GistNSN parentlsn;
|
2005-09-22 22:44:36 +02:00
|
|
|
BlockNumber blkno;
|
|
|
|
struct GistBDItem *next;
|
2005-06-20 12:29:37 +02:00
|
|
|
} GistBDItem;
|
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
static void
|
2005-09-22 22:44:36 +02:00
|
|
|
pushStackIfSplited(Page page, GistBDItem *stack)
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
GISTPageOpaque opaque = GistPageGetOpaque(page);
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (stack->blkno != GIST_ROOT_BLKNO && !XLogRecPtrIsInvalid(stack->parentlsn) &&
|
|
|
|
XLByteLT(stack->parentlsn, opaque->nsn) &&
|
|
|
|
opaque->rightlink != InvalidBlockNumber /* sanity check */ )
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
/* split page detected, install right link to the stack */
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
GistBDItem *ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
|
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
ptr->blkno = opaque->rightlink;
|
|
|
|
ptr->parentlsn = stack->parentlsn;
|
|
|
|
ptr->next = stack->next;
|
|
|
|
stack->next = ptr;
|
|
|
|
}
|
2005-09-22 22:44:36 +02:00
|
|
|
}
|
2005-06-27 14:45:23 +02:00
|
|
|
|
|
|
|
|
2005-06-20 12:29:37 +02:00
|
|
|
/*
|
|
|
|
* Bulk deletion of all index entries pointing to a set of heap tuples and
|
2005-06-27 14:45:23 +02:00
|
|
|
* check invalid tuples after crash recovery.
|
2005-06-20 12:29:37 +02:00
|
|
|
* The set of target tuples is specified via a callback routine that tells
|
|
|
|
* whether any given heap tuple (identified by ItemPointer) is being deleted.
|
|
|
|
*
|
|
|
|
* Result: a palloc'd struct containing statistical info for VACUUM displays.
|
|
|
|
*/
|
|
|
|
Datum
|
2005-09-22 22:44:36 +02:00
|
|
|
gistbulkdelete(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2006-05-03 00:25:10 +02:00
|
|
|
IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
|
|
|
|
GistBulkDeleteResult *stats = (GistBulkDeleteResult *) PG_GETARG_POINTER(1);
|
|
|
|
IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
|
|
|
|
void *callback_state = (void *) PG_GETARG_POINTER(3);
|
|
|
|
Relation rel = info->index;
|
2005-09-22 22:44:36 +02:00
|
|
|
GistBDItem *stack,
|
|
|
|
*ptr;
|
|
|
|
|
2006-05-03 00:25:10 +02:00
|
|
|
/* first time through? */
|
|
|
|
if (stats == NULL)
|
|
|
|
stats = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult));
|
|
|
|
/* we'll re-count the tuples each time */
|
2009-06-07 00:13:52 +02:00
|
|
|
stats->std.estimated_count = false;
|
2006-05-03 00:25:10 +02:00
|
|
|
stats->std.num_index_tuples = 0;
|
2005-06-20 12:29:37 +02:00
|
|
|
|
2006-05-03 00:25:10 +02:00
|
|
|
stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
|
|
|
|
stack->blkno = GIST_ROOT_BLKNO;
|
2005-06-20 12:29:37 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
while (stack)
|
|
|
|
{
|
Unite ReadBufferWithFork, ReadBufferWithStrategy, and ZeroOrReadBuffer
functions into one ReadBufferExtended function, that takes the strategy
and mode as argument. There's three modes, RBM_NORMAL which is the default
used by plain ReadBuffer(), RBM_ZERO, which replaces ZeroOrReadBuffer, and
a new mode RBM_ZERO_ON_ERROR, which allows callers to read corrupt pages
without throwing an error. The FSM needs the new mode to recover from
corrupt pages, which could happend if we crash after extending an FSM file,
and the new page is "torn".
Add fork number to some error messages in bufmgr.c, that still lacked it.
2008-10-31 16:05:00 +01:00
|
|
|
Buffer buffer;
|
2005-09-22 22:44:36 +02:00
|
|
|
Page page;
|
|
|
|
OffsetNumber i,
|
|
|
|
maxoff;
|
2005-06-20 12:29:37 +02:00
|
|
|
IndexTuple idxtuple;
|
|
|
|
ItemId iid;
|
2005-06-27 14:45:23 +02:00
|
|
|
|
Unite ReadBufferWithFork, ReadBufferWithStrategy, and ZeroOrReadBuffer
functions into one ReadBufferExtended function, that takes the strategy
and mode as argument. There's three modes, RBM_NORMAL which is the default
used by plain ReadBuffer(), RBM_ZERO, which replaces ZeroOrReadBuffer, and
a new mode RBM_ZERO_ON_ERROR, which allows callers to read corrupt pages
without throwing an error. The FSM needs the new mode to recover from
corrupt pages, which could happend if we crash after extending an FSM file,
and the new page is "torn".
Add fork number to some error messages in bufmgr.c, that still lacked it.
2008-10-31 16:05:00 +01:00
|
|
|
buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
|
|
|
|
RBM_NORMAL, info->strategy);
|
2005-09-22 22:44:36 +02:00
|
|
|
LockBuffer(buffer, GIST_SHARE);
|
2005-11-06 23:39:21 +01:00
|
|
|
gistcheckpage(rel, buffer);
|
2005-09-22 22:44:36 +02:00
|
|
|
page = (Page) BufferGetPage(buffer);
|
2005-06-20 12:29:37 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (GistPageIsLeaf(page))
|
|
|
|
{
|
2005-09-02 21:02:20 +02:00
|
|
|
OffsetNumber todelete[MaxOffsetNumber];
|
2005-09-22 22:44:36 +02:00
|
|
|
int ntodelete = 0;
|
2005-06-27 14:45:23 +02:00
|
|
|
|
|
|
|
LockBuffer(buffer, GIST_UNLOCK);
|
|
|
|
LockBuffer(buffer, GIST_EXCLUSIVE);
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
page = (Page) BufferGetPage(buffer);
|
|
|
|
if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page))
|
|
|
|
{
|
2006-02-14 17:39:32 +01:00
|
|
|
/* only the root can become non-leaf during relock */
|
2006-04-01 01:32:07 +02:00
|
|
|
UnlockReleaseBuffer(buffer);
|
2005-06-27 14:45:23 +02:00
|
|
|
/* one more check */
|
|
|
|
continue;
|
|
|
|
}
|
2005-06-20 12:29:37 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
/*
|
|
|
|
* check for split proceeded after look at parent, we should check
|
|
|
|
* it after relock
|
|
|
|
*/
|
2005-06-27 14:45:23 +02:00
|
|
|
pushStackIfSplited(page, stack);
|
|
|
|
|
2006-03-31 01:03:10 +02:00
|
|
|
/*
|
|
|
|
* Remove deletable tuples from page
|
|
|
|
*/
|
|
|
|
|
2005-06-27 14:45:23 +02:00
|
|
|
maxoff = PageGetMaxOffsetNumber(page);
|
2005-06-20 12:29:37 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
|
|
|
{
|
|
|
|
iid = PageGetItemId(page, i);
|
2005-06-20 12:29:37 +02:00
|
|
|
idxtuple = (IndexTuple) PageGetItem(page, iid);
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (callback(&(idxtuple->t_tid), callback_state))
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
todelete[ntodelete] = i - ntodelete;
|
2005-09-22 22:44:36 +02:00
|
|
|
ntodelete++;
|
2006-05-03 00:25:10 +02:00
|
|
|
stats->std.tuples_removed += 1;
|
2005-09-22 22:44:36 +02:00
|
|
|
}
|
|
|
|
else
|
2006-05-03 00:25:10 +02:00
|
|
|
stats->std.num_index_tuples += 1;
|
2005-06-20 12:29:37 +02:00
|
|
|
}
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (ntodelete)
|
|
|
|
{
|
2006-05-10 11:19:54 +02:00
|
|
|
START_CRIT_SECTION();
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2006-04-01 01:32:07 +02:00
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
|
2006-10-04 02:30:14 +02:00
|
|
|
for (i = 0; i < ntodelete; i++)
|
2006-05-10 11:19:54 +02:00
|
|
|
PageIndexTupleDelete(page, todelete[i]);
|
|
|
|
GistMarkTuplesDeleted(page);
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (!rel->rd_istemp)
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
XLogRecData *rdata;
|
2005-09-22 22:44:36 +02:00
|
|
|
XLogRecPtr recptr;
|
2006-03-31 01:03:10 +02:00
|
|
|
gistxlogPageUpdate *xlinfo;
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2006-03-31 01:03:10 +02:00
|
|
|
rdata = formUpdateRdata(rel->rd_node, buffer,
|
2006-10-04 02:30:14 +02:00
|
|
|
todelete, ntodelete,
|
2006-03-31 01:03:10 +02:00
|
|
|
NULL, 0,
|
|
|
|
NULL);
|
2006-05-17 18:34:59 +02:00
|
|
|
xlinfo = (gistxlogPageUpdate *) rdata->next->data;
|
2005-06-27 14:45:23 +02:00
|
|
|
|
2006-03-31 01:03:10 +02:00
|
|
|
recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
|
2005-06-27 14:45:23 +02:00
|
|
|
PageSetLSN(page, recptr);
|
|
|
|
PageSetTLI(page, ThisTimeLineID);
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
pfree(xlinfo);
|
|
|
|
pfree(rdata);
|
|
|
|
}
|
|
|
|
else
|
2005-06-27 14:45:23 +02:00
|
|
|
PageSetLSN(page, XLogRecPtrForTemp);
|
2006-05-10 11:19:54 +02:00
|
|
|
|
|
|
|
END_CRIT_SECTION();
|
2005-06-27 14:45:23 +02:00
|
|
|
}
|
2006-03-31 01:03:10 +02:00
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-06-27 14:45:23 +02:00
|
|
|
/* check for split proceeded after look at parent */
|
|
|
|
pushStackIfSplited(page, stack);
|
|
|
|
|
|
|
|
maxoff = PageGetMaxOffsetNumber(page);
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
|
|
|
{
|
2005-06-20 12:29:37 +02:00
|
|
|
iid = PageGetItemId(page, i);
|
|
|
|
idxtuple = (IndexTuple) PageGetItem(page, iid);
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
|
|
|
|
ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
|
|
|
|
ptr->parentlsn = PageGetLSN(page);
|
2005-06-20 12:29:37 +02:00
|
|
|
ptr->next = stack->next;
|
|
|
|
stack->next = ptr;
|
|
|
|
|
2005-09-22 22:44:36 +02:00
|
|
|
if (GistTupleIsInvalid(idxtuple))
|
2010-02-08 05:33:55 +01:00
|
|
|
stats->needReindex = true;
|
2005-06-20 12:29:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-01 01:32:07 +02:00
|
|
|
UnlockReleaseBuffer(buffer);
|
2005-06-20 12:29:37 +02:00
|
|
|
|
|
|
|
ptr = stack->next;
|
2005-09-22 22:44:36 +02:00
|
|
|
pfree(stack);
|
2005-06-20 12:29:37 +02:00
|
|
|
stack = ptr;
|
2005-06-20 17:22:38 +02:00
|
|
|
|
|
|
|
vacuum_delay_point();
|
2005-06-20 12:29:37 +02:00
|
|
|
}
|
|
|
|
|
2006-05-03 00:25:10 +02:00
|
|
|
PG_RETURN_POINTER(stats);
|
2005-06-20 12:29:37 +02:00
|
|
|
}
|