]> granicus.if.org Git - postgresql/commitdiff
Skip ambulkdelete scan if there's nothing to delete and the index is not
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Feb 2006 23:31:34 +0000 (23:31 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Feb 2006 23:31:34 +0000 (23:31 +0000)
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.

doc/src/sgml/indexam.sgml
src/backend/access/gist/gistvacuum.c
src/backend/access/hash/hash.c
src/backend/access/index/indexam.c
src/backend/access/nbtree/nbtree.c
src/backend/commands/vacuum.c
src/backend/commands/vacuumlazy.c
src/include/access/genam.h

index 7480c98a15b4d38d54a6b97e3cc1cba4a935a14d..d766467bdb496115e8d2dfa6cfdcf5d2860aaeeb 100644 (file)
@@ -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 *
index afd743a5a2642c4841300de9084050e612c13634..b96d84fd024ebc00a14ca265b84f80ec821f7320 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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);
 
index cb82a38d901793542c82ea9379c70ab9b66cf59e..ca0d6ec96dd6a0985f3a029da1f7ea3fce1746e4 100644 (file)
@@ -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;
index 6a681192cafbb0cdca6fd18cf8afc8aa2707546e..69c9ecb918987e9b7d8863b5fe2d4385cc8a8aa2 100644 (file)
@@ -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
  * ----------------
  */
index 4fb70302d7aaf67337e5abddc2a7da60e4ea47e2..e28faef141d83b966f3ba34d7dad669e7ffbdb16 100644 (file)
@@ -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);
 }
 
index d60c433095d1147142c96dfd42490296de656b17..3153a0a559a928f6fd037bfa6a8bf01fb0a1c78f 100644 (file)
@@ -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);
 
index a65c269fc8c8849b6b76e172f1da0d2075a73abc..2c1ba517638aaf76e1683c6a5b4796372abe0da5 100644 (file)
@@ -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);
 
index 859c4e53b2943e108efd3f93a2bd414ace8b0ba6..eace51db0e6f106aa33781bf8ff49b7f8999b98f 100644 (file)
@@ -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 */