From fd267c1ebc363ae6b1bf586794fa5cc9e8cca43c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 11 Feb 2006 23:31:34 +0000 Subject: Skip ambulkdelete scan if there's nothing to delete and the index is not partial. None of the existing AMs do anything useful except counting tuples when there's nothing to delete, and we can get a tuple count from the heap as long as it's not a partial index. (hash actually can skip anyway because it maintains a tuple count in the index metapage.) GIST is not currently able to exploit this optimization because, due to failure to index NULLs, GIST is always effectively partial. Possibly we should fix that sometime. Simon Riggs w/ some review by Tom Lane. --- src/backend/access/gist/gistvacuum.c | 73 ++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 23 deletions(-) (limited to 'src/backend/access/gist/gistvacuum.c') diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index afd743a5a26..b96d84fd024 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.13 2006/02/11 17:14:08 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.14 2006/02/11 23:31:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,16 +25,19 @@ #include "storage/freespace.h" #include "storage/smgr.h" -/* filled by gistbulkdelete, cleared by gistvacuumpcleanup */ -static bool needFullVacuum = false; +typedef struct GistBulkDeleteResult +{ + IndexBulkDeleteResult std; /* common state */ + bool needFullVacuum; +} GistBulkDeleteResult; typedef struct { GISTSTATE giststate; Relation index; MemoryContext opCtx; - IndexBulkDeleteResult *result; + GistBulkDeleteResult *result; } GistVacuum; typedef struct @@ -44,6 +47,7 @@ typedef struct bool emptypage; } ArrayTuple; + static ArrayTuple gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion) { @@ -125,7 +129,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion) if (chldtuple.ituplen > 1) { /* - * child was splitted, so we need mark completion + * child was split, so we need mark completion * insert(split) */ int j; @@ -262,7 +266,7 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion) needwrite = true; res.emptypage = true; GistPageSetDeleted(page); - gv->result->pages_deleted++; + gv->result->std.pages_deleted++; } } else @@ -329,9 +333,9 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion) } /* - * For usial vacuum just update FSM, for full vacuum + * For usual vacuum just update FSM, for full vacuum * reforms parent tuples if some of childs was deleted or changed, - * update invalid tuples (they can exsist from last crash recovery only), + * update invalid tuples (they can exist from last crash recovery only), * tries to get smaller index */ @@ -340,7 +344,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) { Relation rel = (Relation) PG_GETARG_POINTER(0); IndexVacuumCleanupInfo *info = (IndexVacuumCleanupInfo *) PG_GETARG_POINTER(1); - IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(2); + GistBulkDeleteResult *stats = (GistBulkDeleteResult *) PG_GETARG_POINTER(2); BlockNumber npages, blkno; BlockNumber nFreePages, @@ -377,13 +381,11 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) freeGISTstate(&(gv.giststate)); MemoryContextDelete(gv.opCtx); } - else if (needFullVacuum) + else if (stats->needFullVacuum) ereport(NOTICE, (errmsg("index \"%s\" needs VACUUM FULL or REINDEX to finish crash recovery", RelationGetRelationName(rel)))); - needFullVacuum = false; - if (info->vacuum_full) needLock = false; /* relation locked with AccessExclusiveLock */ else @@ -438,23 +440,30 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) if (lastBlock > lastFilledBlock) RelationTruncate(rel, lastFilledBlock + 1); - stats->pages_removed = lastBlock - lastFilledBlock; + stats->std.pages_removed = lastBlock - lastFilledBlock; } RecordIndexFreeSpace(&rel->rd_node, nFreePages, freePages); pfree(freePages); /* return statistics */ - stats->pages_free = nFreePages; + stats->std.pages_free = nFreePages; if (needLock) LockRelationForExtension(rel, ExclusiveLock); - stats->num_pages = RelationGetNumberOfBlocks(rel); + stats->std.num_pages = RelationGetNumberOfBlocks(rel); if (needLock) UnlockRelationForExtension(rel, ExclusiveLock); if (info->vacuum_full) UnlockRelation(rel, AccessExclusiveLock); + /* if gistbulkdelete skipped the scan, use heap's tuple count */ + if (stats->std.num_index_tuples < 0) + { + Assert(info->num_heap_tuples >= 0); + stats->std.num_index_tuples = info->num_heap_tuples; + } + PG_RETURN_POINTER(stats); } @@ -500,15 +509,33 @@ gistbulkdelete(PG_FUNCTION_ARGS) Relation rel = (Relation) PG_GETARG_POINTER(0); IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(1); void *callback_state = (void *) PG_GETARG_POINTER(2); - IndexBulkDeleteResult *result = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); + GistBulkDeleteResult *result; GistBDItem *stack, *ptr; bool needLock; - stack = (GistBDItem *) palloc0(sizeof(GistBDItem)); + result = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult)); - stack->blkno = GIST_ROOT_BLKNO; - needFullVacuum = false; + /* + * We can skip the scan entirely if there's nothing to delete (indicated + * by callback_state == NULL) and the index isn't partial. For a partial + * index we must scan in order to derive a trustworthy tuple count. + * + * XXX as of PG 8.2 this is dead code because GIST indexes are always + * effectively partial ... but keep it anyway in case our null-handling + * gets fixed. + */ + if (callback_state || vac_is_partial_index(rel)) + { + stack = (GistBDItem *) palloc0(sizeof(GistBDItem)); + stack->blkno = GIST_ROOT_BLKNO; + } + else + { + /* skip scan and set flag for gistvacuumcleanup */ + stack = NULL; + result->std.num_index_tuples = -1; + } while (stack) { @@ -561,11 +588,11 @@ gistbulkdelete(PG_FUNCTION_ARGS) i--; maxoff--; ntodelete++; - result->tuples_removed += 1; + result->std.tuples_removed += 1; Assert(maxoff == PageGetMaxOffsetNumber(page)); } else - result->num_index_tuples += 1; + result->std.num_index_tuples += 1; } if (ntodelete) @@ -615,7 +642,7 @@ gistbulkdelete(PG_FUNCTION_ARGS) stack->next = ptr; if (GistTupleIsInvalid(idxtuple)) - needFullVacuum = true; + result->needFullVacuum = true; } } @@ -634,7 +661,7 @@ gistbulkdelete(PG_FUNCTION_ARGS) if (needLock) LockRelationForExtension(rel, ExclusiveLock); - result->num_pages = RelationGetNumberOfBlocks(rel); + result->std.num_pages = RelationGetNumberOfBlocks(rel); if (needLock) UnlockRelationForExtension(rel, ExclusiveLock); -- cgit v1.2.3