<!--
-$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">
</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 *
amvacuumcleanup (Relation indexRelation,
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
bool emptypage;
} ArrayTuple;
+
static ArrayTuple
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;
needwrite = true;
res.emptypage = true;
GistPageSetDeleted(page);
- gv->result->pages_deleted++;
+ gv->result->std.pages_deleted++;
}
}
else
}
/*
- * 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
*/
{
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,
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
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);
}
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)
{
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)
stack->next = ptr;
if (GistTupleIsInvalid(idxtuple))
- needFullVacuum = true;
+ result->needFullVacuum = true;
}
}
if (needLock)
LockRelationForExtension(rel, ExclusiveLock);
- result->num_pages = RelationGetNumberOfBlocks(rel);
+ result->std.num_pages = RelationGetNumberOfBlocks(rel);
if (needLock)
UnlockRelationForExtension(rel, ExclusiveLock);
*
*
* 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.
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;
*
*
* 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
* 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
* ----------------
*/
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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 (;;)
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);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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);
/* 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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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);
/* 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);
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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 */