diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index 7480c98a15b4d38d54a6b97e3cc1cba4a935a14d..d766467bdb496115e8d2dfa6cfdcf5d2860aaeeb 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.7 2005/11/04 23:14:00 petere Exp $ +$PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.8 2006/02/11 23:31:32 tgl Exp $ --> <chapter id="indexam"> @@ -200,6 +200,12 @@ ambulkdelete (Relation indexRelation, struct containing statistics about the effects of the deletion operation. </para> + <para> + If <literal>callback_state</> is NULL then no tuples are to be deleted. + The index AM may choose to optimize this case (eg by not scanning the + index) but it is still expected to deliver accurate statistics. + </para> + <para> <programlisting> IndexBulkDeleteResult * diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index afd743a5a2642c4841300de9084050e612c13634..b96d84fd024ebc00a14ca265b84f80ec821f7320 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); diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index cb82a38d901793542c82ea9379c70ab9b66cf59e..ca0d6ec96dd6a0985f3a029da1f7ea3fce1746e4 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.85 2006/02/11 17:14:08 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.86 2006/02/11 23:31:33 tgl Exp $ * * NOTES * This file contains only the public interface routines. @@ -517,6 +517,18 @@ hashbulkdelete(PG_FUNCTION_ARGS) cur_maxbucket = orig_maxbucket; loop_top: + + /* + * If we don't have anything to delete, skip the scan, and report the + * number of tuples shown in the metapage. (Unlike btree and gist, + * we can trust this number even for a partial index.) + */ + if (!callback_state) + { + cur_bucket = cur_maxbucket + 1; + num_index_tuples = local_metapage.hashm_ntuples; + } + while (cur_bucket <= cur_maxbucket) { BlockNumber bucket_blkno; diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 6a681192cafbb0cdca6fd18cf8afc8aa2707546e..69c9ecb918987e9b7d8863b5fe2d4385cc8a8aa2 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.89 2006/02/11 17:14:08 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.90 2006/02/11 23:31:33 tgl Exp $ * * INTERFACE ROUTINES * index_open - open an index relation by relation OID @@ -685,6 +685,10 @@ index_getmulti(IndexScanDesc scan, * callback routine tells whether a given main-heap tuple is * to be deleted * + * if callback_state is NULL then there are no tuples to be deleted; + * index AM can choose to avoid work in this case, but must still + * follow the protocol of returning statistical info. + * * return value is an optional palloc'd struct of statistics * ---------------- */ diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 4fb70302d7aaf67337e5abddc2a7da60e4ea47e2..e28faef141d83b966f3ba34d7dad669e7ffbdb16 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -12,7 +12,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.138 2006/02/11 17:14:08 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.139 2006/02/11 23:31:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -564,8 +564,22 @@ btbulkdelete(PG_FUNCTION_ARGS) * further to its right, which the indexscan will have no pin on.) We can * skip obtaining exclusive lock on empty pages though, since no indexscan * could be stopped on those. + * + * 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. */ - buf = _bt_get_endpoint(rel, 0, false); + if (callback_state || vac_is_partial_index(rel)) + { + buf = _bt_get_endpoint(rel, 0, false); + } + else + { + /* skip scan and set flag for btvacuumcleanup */ + buf = InvalidBuffer; + num_index_tuples = -1; + } + if (BufferIsValid(buf)) /* check for empty index */ { for (;;) @@ -836,6 +850,13 @@ btvacuumcleanup(PG_FUNCTION_ARGS) stats->pages_deleted = pages_deleted; stats->pages_free = nFreePages; + /* if btbulkdelete skipped the scan, use heap's tuple count */ + if (stats->num_index_tuples < 0) + { + Assert(info->num_heap_tuples >= 0); + stats->num_index_tuples = info->num_heap_tuples; + } + PG_RETURN_POINTER(stats); } diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index d60c433095d1147142c96dfd42490296de656b17..3153a0a559a928f6fd037bfa6a8bf01fb0a1c78f 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.323 2006/02/11 17:14:09 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.324 2006/02/11 23:31:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2955,6 +2955,7 @@ scan_index(Relation indrel, double num_tuples) /* Do post-VACUUM cleanup, even though we deleted nothing */ vcinfo.vacuum_full = true; vcinfo.message_level = elevel; + vcinfo.num_heap_tuples = num_tuples; stats = index_vacuum_cleanup(indrel, &vcinfo, stats); @@ -3022,6 +3023,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel, /* Do post-VACUUM cleanup */ vcinfo.vacuum_full = true; vcinfo.message_level = elevel; + vcinfo.num_heap_tuples = num_tuples + keep_tuples; stats = index_vacuum_cleanup(indrel, &vcinfo, stats); diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index a65c269fc8c8849b6b76e172f1da0d2075a73abc..2c1ba517638aaf76e1683c6a5b4796372abe0da5 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -31,7 +31,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.65 2006/02/11 17:14:09 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.66 2006/02/11 23:31:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -625,6 +625,7 @@ lazy_scan_index(Relation indrel, LVRelStats *vacrelstats) /* Do post-VACUUM cleanup, even though we deleted nothing */ vcinfo.vacuum_full = false; vcinfo.message_level = elevel; + vcinfo.num_heap_tuples = vacrelstats->rel_tuples; stats = index_vacuum_cleanup(indrel, &vcinfo, stats); @@ -697,6 +698,9 @@ lazy_vacuum_index(Relation indrel, /* Do post-VACUUM cleanup */ vcinfo.vacuum_full = false; vcinfo.message_level = elevel; + /* We don't yet know rel_tuples, so pass -1 */ + /* index_bulk_delete can't have skipped scan anyway ... */ + vcinfo.num_heap_tuples = -1; stats = index_vacuum_cleanup(indrel, &vcinfo, stats); diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 859c4e53b2943e108efd3f93a2bd414ace8b0ba6..eace51db0e6f106aa33781bf8ff49b7f8999b98f 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.56 2006/02/11 17:14:09 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.57 2006/02/11 23:31:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -51,6 +51,7 @@ typedef struct IndexVacuumCleanupInfo { bool vacuum_full; /* VACUUM FULL (we have exclusive lock) */ int message_level; /* ereport level for progress messages */ + double num_heap_tuples; /* tuples remaining in heap */ } IndexVacuumCleanupInfo; /* Struct for heap-or-index scans of system tables */