Clean up API for ambulkdelete/amvacuumcleanup as per today's discussion.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 2 May 2006 22:25:10 +0000 (22:25 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 2 May 2006 22:25:10 +0000 (22:25 +0000)
This formulation requires every AM to provide amvacuumcleanup, unlike before,
but it's surely a whole lot cleaner.  Also, add an 'amstorage' column to
pg_am so that we can get rid of hardwired knowledge in DefineOpClass().

16 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/indexam.sgml
src/backend/access/gin/ginvacuum.c
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/cluster.c
src/backend/commands/opclasscmds.c
src/backend/commands/vacuum.c
src/backend/commands/vacuumlazy.c
src/include/access/genam.h
src/include/access/hash.h
src/include/catalog/catversion.h
src/include/catalog/pg_am.h
src/include/catalog/pg_proc.h

index 43f0c166e0a5c11b830ea2d173b3421fcdec1533..fe993aa4eeb7aa4caa61c38a18e6024bcc831d80 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.121 2006/03/10 19:10:46 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.122 2006/05/02 22:25:09 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
       <entry>Does the access method support null index entries?</entry>
      </row>
 
+     <row>
+      <entry><structfield>amstorage</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry></entry>
+      <entry>Can index storage data type differ from column data type?</entry>
+     </row>
+
      <row>
       <entry><structfield>amconcurrent</structfield></entry>
       <entry><type>bool</type></entry>
       <entry>Does the access method support concurrent updates?</entry>
      </row>
 
+     <row>
+      <entry><structfield>amclusterable</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry></entry>
+      <entry>Can an index of this type be CLUSTERed on?</entry>
+     </row>
+
      <row>
       <entry><structfield>aminsert</structfield></entry>
       <entry><type>regproc</type></entry>
index 623066b66cccdfd6321c14caa4dda4abd576d43d..01381ff7058049d15c59d8ef0788323de7833dcc 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.9 2006/03/10 19:10:48 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.10 2006/05/02 22:25:09 tgl Exp $ -->
 
 <chapter id="indexam">
  <title>Index Access Method Interface Definition</title>
@@ -184,44 +184,52 @@ aminsert (Relation indexRelation,
   <para>
 <programlisting>
 IndexBulkDeleteResult *
-ambulkdelete (Relation indexRelation,
+ambulkdelete (IndexVacuumInfo *info,
+              IndexBulkDeleteResult *stats,
               IndexBulkDeleteCallback callback,
               void *callback_state);
 </programlisting>
    Delete tuple(s) from the index.  This is a <quote>bulk delete</> operation
    that is intended to be implemented by scanning the whole index and checking
    each entry to see if it should be deleted.
-   The passed-in <literal>callback</> function may be called, in the style
+   The passed-in <literal>callback</> function must be called, in the style
    <literal>callback(<replaceable>TID</>, callback_state) returns bool</literal>,
    to determine whether any particular index entry, as identified by its
    referenced TID, is to be deleted.  Must return either NULL or a palloc'd
    struct containing statistics about the effects of the deletion operation.
+   It is OK to return NULL if no information needs to be passed on to
+   <function>amvacuumcleanup</>.
   </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.
+   Because of limited <varname>maintenance_work_mem</>,
+   <function>ambulkdelete</> may need to be called more than once when many
+   tuples are to be deleted.  The <literal>stats</> argument is the result
+   of the previous call for this index (it is NULL for the first call within a
+   <command>VACUUM</> operation).  This allows the AM to accumulate statistics
+   across the whole operation.  Typically, <function>ambulkdelete</> will
+   modify and return the same struct if the passed <literal>stats</> is not
+   null.
   </para>
 
   <para>
 <programlisting>
 IndexBulkDeleteResult *
-amvacuumcleanup (Relation indexRelation,
-                 IndexVacuumCleanupInfo *info,
+amvacuumcleanup (IndexVacuumInfo *info,
                  IndexBulkDeleteResult *stats);
 </programlisting>
-   Clean up after a <command>VACUUM</command> operation (one or more
-   <function>ambulkdelete</> calls).  An index access method does not have
-   to provide this function (if so, the entry in <structname>pg_am</> must
-   be zero).  If it is provided, it is typically used for bulk cleanup
-   such as reclaiming empty index pages.  <literal>info</>
-   provides some additional arguments such as a message level for statistical
-   reports, and <literal>stats</> is whatever the last
-   <function>ambulkdelete</> call returned.  <function>amvacuumcleanup</>
-   may replace or modify this struct before returning it.  If the result
-   is not NULL it must be a palloc'd struct.  The statistics it contains
-   will be reported by <command>VACUUM</> if <literal>VERBOSE</> is given.
+   Clean up after a <command>VACUUM</command> operation (zero or more
+   <function>ambulkdelete</> calls).  This does not have to do anything
+   beyond returning index statistics, but it may perform bulk cleanup
+   such as reclaiming empty index pages.  <literal>stats</> is whatever the
+   last <function>ambulkdelete</> call returned, or NULL if
+   <function>ambulkdelete</> was not called because no tuples needed to be
+   deleted.  If the result is not NULL it must be a palloc'd struct.
+   The statistics it contains will be used to update <structname>pg_class</>,
+   and will be reported by <command>VACUUM</> if <literal>VERBOSE</> is given.
+   It is OK to return NULL if the index was not changed at all during the
+   <command>VACUUM</command> operation, but otherwise correct stats should
+   be returned.
   </para>
 
   <para>
index ab1861fdaa94b575b1ec2c9f969d78332a35643c..bc8a3e53efc71ef2e4dbd738f0f04d807bc83262 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *          $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.1 2006/05/02 11:28:54 teodor Exp $
+ *          $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.2 2006/05/02 22:25:10 tgl Exp $
  *-------------------------------------------------------------------------
  */
 
@@ -474,17 +474,25 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
 
 Datum
 ginbulkdelete(PG_FUNCTION_ARGS) {
-   Relation    index = (Relation) PG_GETARG_POINTER(0);
-   IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(1);
-   void       *callback_state = (void *) PG_GETARG_POINTER(2);
+   IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
+   IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
+   IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
+   void       *callback_state = (void *) PG_GETARG_POINTER(3);
+   Relation    index = info->index;
    BlockNumber blkno = GIN_ROOT_BLKNO;
    GinVacuumState  gvs;
    Buffer      buffer;
    BlockNumber rootOfPostingTree[ BLCKSZ/ (sizeof(IndexTupleData)+sizeof(ItemId)) ];
    uint32 nRoot;
 
+   /* first time through? */
+   if (stats == NULL)
+       stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+   /* we'll re-count the tuples each time */
+   stats->num_index_tuples = 0;
+
    gvs.index = index;
-   gvs.result = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+   gvs.result = stats;
    gvs.callback = callback;
    gvs.callback_state = callback_state;
    initGinState(&gvs.ginstate, index);
@@ -564,9 +572,9 @@ ginbulkdelete(PG_FUNCTION_ARGS) {
 
 Datum 
 ginvacuumcleanup(PG_FUNCTION_ARGS) {
-   Relation    index = (Relation) PG_GETARG_POINTER(0);
-   IndexVacuumCleanupInfo *info = (IndexVacuumCleanupInfo *) PG_GETARG_POINTER(1);
-   IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(2);
+   IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
+   IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
+   Relation    index = info->index;
    bool     needLock = !RELATION_IS_LOCAL(index);
     BlockNumber npages,
                blkno;
@@ -576,6 +584,15 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) {
    BlockNumber lastBlock = GIN_ROOT_BLKNO,
                   lastFilledBlock = GIN_ROOT_BLKNO;
 
+   /* Set up all-zero stats if ginbulkdelete wasn't called */
+   if (stats == NULL)
+       stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+   /*
+    * XXX we always report the heap tuple count as the number of index
+    * entries.  This is bogus if the index is partial, but it's real hard
+    * to tell how many distinct heap entries are referenced by a GIN index.
+    */
+   stats->num_index_tuples = info->num_heap_tuples;
 
    if (info->vacuum_full) {
        LockRelation(index, AccessExclusiveLock);
index 285f13013217adfdf6bc6950dca7807a8dbc4487..eafd472c5fd351b05f50bd15804ab0d4fe40f405 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.18 2006/03/31 23:32:05 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.19 2006/05/02 22:25:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -343,9 +343,9 @@ gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
 Datum
 gistvacuumcleanup(PG_FUNCTION_ARGS)
 {
-   Relation    rel = (Relation) PG_GETARG_POINTER(0);
-   IndexVacuumCleanupInfo *info = (IndexVacuumCleanupInfo *) PG_GETARG_POINTER(1);
-   GistBulkDeleteResult *stats = (GistBulkDeleteResult *) PG_GETARG_POINTER(2);
+   IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
+   GistBulkDeleteResult *stats = (GistBulkDeleteResult *) PG_GETARG_POINTER(1);
+   Relation    rel = info->index;
    BlockNumber npages,
                blkno;
    BlockNumber nFreePages,
@@ -355,6 +355,19 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
                lastFilledBlock = GIST_ROOT_BLKNO;
    bool        needLock;
 
+   /* Set up all-zero stats if gistbulkdelete wasn't called */
+   if (stats == NULL)
+   {
+       stats = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult));
+       /* use heap's tuple count */
+       Assert(info->num_heap_tuples >= 0);
+       stats->std.num_index_tuples = info->num_heap_tuples;
+       /*
+        * 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?
+        */
+   }
+
    /* gistVacuumUpdate may cause hard work */
    if (info->vacuum_full)
    {
@@ -460,13 +473,6 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
    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);
 }
 
@@ -509,36 +515,22 @@ pushStackIfSplited(Page page, GistBDItem *stack)
 Datum
 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);
-   GistBulkDeleteResult *result;
+   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;
    GistBDItem *stack,
               *ptr;
-   bool        needLock;
 
-   result = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult));
+   /* first time through? */
+   if (stats == NULL)
+       stats = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult));
+   /* we'll re-count the tuples each time */
+   stats->std.num_index_tuples = 0;
 
-   /*
-    * 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;
-   }
+   stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
+   stack->blkno = GIST_ROOT_BLKNO;
 
    while (stack)
    {
@@ -601,11 +593,11 @@ gistbulkdelete(PG_FUNCTION_ARGS)
                    i--;
                    maxoff--;
                    ntodelete++;
-                   result->std.tuples_removed += 1;
+                   stats->std.tuples_removed += 1;
                    Assert(maxoff == PageGetMaxOffsetNumber(page));
                }
                else
-                   result->std.num_index_tuples += 1;
+                   stats->std.num_index_tuples += 1;
            }
 
            if (ntodelete)
@@ -658,7 +650,7 @@ gistbulkdelete(PG_FUNCTION_ARGS)
                stack->next = ptr;
 
                if (GistTupleIsInvalid(idxtuple))
-                   result->needFullVacuum = true;
+                   stats->needFullVacuum = true;
            }
        }
 
@@ -671,13 +663,5 @@ gistbulkdelete(PG_FUNCTION_ARGS)
        vacuum_delay_point();
    }
 
-   needLock = !RELATION_IS_LOCAL(rel);
-
-   if (needLock)
-       LockRelationForExtension(rel, ExclusiveLock);
-   result->std.num_pages = RelationGetNumberOfBlocks(rel);
-   if (needLock)
-       UnlockRelationForExtension(rel, ExclusiveLock);
-
-   PG_RETURN_POINTER(result);
+   PG_RETURN_POINTER(stats);
 }
index 8e232414dc8d55df4a844f2f886bbd4fcf32cc34..ff54052f6de28e2f586103d6567c7a1126020adf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.88 2006/03/24 04:32:12 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.89 2006/05/02 22:25:10 tgl Exp $
  *
  * NOTES
  *   This file contains only the public interface routines.
@@ -478,11 +478,11 @@ hashrestrpos(PG_FUNCTION_ARGS)
 Datum
 hashbulkdelete(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;
-   BlockNumber num_pages;
+   IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
+   IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
+   IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
+   void       *callback_state = (void *) PG_GETARG_POINTER(3);
+   Relation    rel = info->index;
    double      tuples_removed;
    double      num_index_tuples;
    double      orig_ntuples;
@@ -517,18 +517,6 @@ 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;
@@ -657,14 +645,37 @@ loop_top:
    _hash_wrtbuf(rel, metabuf);
 
    /* return statistics */
-   num_pages = RelationGetNumberOfBlocks(rel);
+   if (stats == NULL)
+       stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+   stats->num_index_tuples = num_index_tuples;
+   stats->tuples_removed += tuples_removed;
+   /* hashvacuumcleanup will fill in num_pages */
+
+   PG_RETURN_POINTER(stats);
+}
 
-   result = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
-   result->num_pages = num_pages;
-   result->num_index_tuples = num_index_tuples;
-   result->tuples_removed = tuples_removed;
+/*
+ * Post-VACUUM cleanup.
+ *
+ * Result: a palloc'd struct containing statistical info for VACUUM displays.
+ */
+Datum
+hashvacuumcleanup(PG_FUNCTION_ARGS)
+{
+   IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
+   IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
+   Relation    rel = info->index;
+   BlockNumber num_pages;
+
+   /* If hashbulkdelete wasn't called, return NULL signifying no change */
+   if (stats == NULL)
+       PG_RETURN_POINTER(NULL);
+
+   /* update statistics */
+   num_pages = RelationGetNumberOfBlocks(rel);
+   stats->num_pages = num_pages;
 
-   PG_RETURN_POINTER(result);
+   PG_RETURN_POINTER(stats);
 }
 
 
index 8b19d5def267bc8143d97aeed8453d7a09d9bf32..b54364acb6dca270b75628ac200b24605b5942a4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.91 2006/03/05 15:58:21 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.92 2006/05/02 22:25:10 tgl Exp $
  *
  * INTERFACE ROUTINES
  *     index_open      - open an index relation by relation OID
@@ -684,19 +684,17 @@ 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
  * ----------------
  */
 IndexBulkDeleteResult *
-index_bulk_delete(Relation indexRelation,
+index_bulk_delete(IndexVacuumInfo *info,
+                 IndexBulkDeleteResult *stats,
                  IndexBulkDeleteCallback callback,
                  void *callback_state)
 {
+   Relation    indexRelation = info->index;
    FmgrInfo   *procedure;
    IndexBulkDeleteResult *result;
 
@@ -704,8 +702,9 @@ index_bulk_delete(Relation indexRelation,
    GET_REL_PROCEDURE(ambulkdelete);
 
    result = (IndexBulkDeleteResult *)
-       DatumGetPointer(FunctionCall3(procedure,
-                                     PointerGetDatum(indexRelation),
+       DatumGetPointer(FunctionCall4(procedure,
+                                     PointerGetDatum(info),
+                                     PointerGetDatum(stats),
                                      PointerGetDatum((Pointer) callback),
                                      PointerGetDatum(callback_state)));
 
@@ -719,26 +718,20 @@ index_bulk_delete(Relation indexRelation,
  * ----------------
  */
 IndexBulkDeleteResult *
-index_vacuum_cleanup(Relation indexRelation,
-                    IndexVacuumCleanupInfo *info,
+index_vacuum_cleanup(IndexVacuumInfo *info,
                     IndexBulkDeleteResult *stats)
 {
+   Relation    indexRelation = info->index;
    FmgrInfo   *procedure;
    IndexBulkDeleteResult *result;
 
    RELATION_CHECKS;
-
-   /* It's okay for an index AM not to have a vacuumcleanup procedure */
-   if (!RegProcedureIsValid(indexRelation->rd_am->amvacuumcleanup))
-       return stats;
-
    GET_REL_PROCEDURE(amvacuumcleanup);
 
    result = (IndexBulkDeleteResult *)
-       DatumGetPointer(FunctionCall3(procedure,
-                                     PointerGetDatum(indexRelation),
-                                     PointerGetDatum((Pointer) info),
-                                     PointerGetDatum((Pointer) stats)));
+       DatumGetPointer(FunctionCall2(procedure,
+                                     PointerGetDatum(info),
+                                     PointerGetDatum(stats)));
 
    return result;
 }
index 4f6915e32b4a4fbfae7a3b1d3d2c31a12b7e35ad..1eece10398efaa23ef8e34e2bbd9367bb80a311b 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.145 2006/04/25 22:46:05 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.146 2006/05/02 22:25:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -518,15 +518,15 @@ btrestrpos(PG_FUNCTION_ARGS)
 Datum
 btbulkdelete(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;
+   IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
+   IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
+   IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
+   void       *callback_state = (void *) PG_GETARG_POINTER(3);
+   Relation    rel = info->index;
    double      tuples_removed = 0;
    OffsetNumber deletable[MaxOffsetNumber];
    int         ndeletable;
    Buffer      buf;
-   BlockNumber num_pages;
 
    /*
     * The outer loop iterates over index leaf pages, the inner over items on
@@ -543,14 +543,8 @@ 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).
     */
-   if (callback_state)
-       buf = _bt_get_endpoint(rel, 0, false);
-   else
-       buf = InvalidBuffer;
+   buf = _bt_get_endpoint(rel, 0, false);
 
    if (BufferIsValid(buf))     /* check for empty index */
    {
@@ -626,14 +620,12 @@ btbulkdelete(PG_FUNCTION_ARGS)
    }
 
    /* return statistics */
-   num_pages = RelationGetNumberOfBlocks(rel);
-
-   result = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
-   result->num_pages = num_pages;
-   /* btvacuumcleanup will fill in num_index_tuples */
-   result->tuples_removed = tuples_removed;
+   if (stats == NULL)
+       stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
+   stats->tuples_removed += tuples_removed;
+   /* btvacuumcleanup will fill in num_pages and num_index_tuples */
 
-   PG_RETURN_POINTER(result);
+   PG_RETURN_POINTER(stats);
 }
 
 /*
@@ -646,9 +638,9 @@ btbulkdelete(PG_FUNCTION_ARGS)
 Datum
 btvacuumcleanup(PG_FUNCTION_ARGS)
 {
-   Relation    rel = (Relation) PG_GETARG_POINTER(0);
-   IndexVacuumCleanupInfo *info = (IndexVacuumCleanupInfo *) PG_GETARG_POINTER(1);
-   IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(2);
+   IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
+   IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
+   Relation    rel = info->index;
    BlockNumber num_pages;
    BlockNumber blkno;
    BlockNumber *freePages;
@@ -660,7 +652,9 @@ btvacuumcleanup(PG_FUNCTION_ARGS)
    MemoryContext oldcontext;
    bool        needLock;
 
-   Assert(stats != NULL);
+   /* Set up all-zero stats if btbulkdelete wasn't called */
+   if (stats == NULL)
+       stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
 
    /*
     * First find out the number of pages in the index.  We must acquire the
index 756b97ee0c36ef2b8528929afbe2d04dad6fa72f..381b6ed596230d4e029df9b1bae9c1586e3eac3a 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.146 2006/05/02 15:45:37 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.147 2006/05/02 22:25:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -339,6 +339,12 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
                 errmsg("cannot cluster on partial index \"%s\"",
                        RelationGetRelationName(OldIndex))));
 
+   if (!OldIndex->rd_am->amclusterable) 
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
+                       RelationGetRelationName(OldIndex))));
+
    if (!OldIndex->rd_am->amindexnulls)
    {
        AttrNumber  colno;
@@ -376,12 +382,6 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
                            RelationGetRelationName(OldIndex))));
    }
 
-   if (!OldIndex->rd_am->amclusterable) 
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
-                       RelationGetRelationName(OldIndex))));
-
    /*
     * Disallow clustering system relations.  This will definitely NOT work
     * for shared relations (we have no way to update pg_class rows in other
index c493cc8e0beed6d7f049fd280010fca0af067be1..8e67219cc0bff9a14e8453f8a18e8a1863c83a7f 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.44 2006/05/02 11:28:54 teodor Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.45 2006/05/02 22:25:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,11 +77,13 @@ DefineOpClass(CreateOpClassStmt *stmt)
                opclassoid;     /* oid of opclass we create */
    int         numOperators,   /* amstrategies value */
                numProcs;       /* amsupport value */
+   bool        amstorage;      /* amstorage flag */
    List       *operators;      /* OpClassMember list for operators */
    List       *procedures;     /* OpClassMember list for support procs */
    ListCell   *l;
    Relation    rel;
    HeapTuple   tup;
+   Form_pg_am  pg_am;
    Datum       values[Natts_pg_opclass];
    char        nulls[Natts_pg_opclass];
    AclResult   aclresult;
@@ -111,8 +113,10 @@ DefineOpClass(CreateOpClassStmt *stmt)
                        stmt->amname)));
 
    amoid = HeapTupleGetOid(tup);
-   numOperators = ((Form_pg_am) GETSTRUCT(tup))->amstrategies;
-   numProcs = ((Form_pg_am) GETSTRUCT(tup))->amsupport;
+   pg_am = (Form_pg_am) GETSTRUCT(tup);
+   numOperators = pg_am->amstrategies;
+   numProcs = pg_am->amsupport;
+   amstorage = pg_am->amstorage;
 
    /* XXX Should we make any privilege check against the AM? */
 
@@ -270,19 +274,11 @@ DefineOpClass(CreateOpClassStmt *stmt)
        /* Just drop the spec if same as column datatype */
        if (storageoid == typeoid)
            storageoid = InvalidOid;
-       else
-       {
-           /*
-            * Currently, only GiST and GIN allows storagetype different from
-            * datatype.  This hardcoded test should be eliminated in favor of
-            * adding another boolean column to pg_am ...
-            */
-           if (!(amoid == GIST_AM_OID || amoid == GIN_AM_OID))
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                        errmsg("storage type may not be different from data type for access method \"%s\"",
-                               stmt->amname)));
-       }
+       else if (!amstorage)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                    errmsg("storage type may not be different from data type for access method \"%s\"",
+                           stmt->amname)));
    }
 
    rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
index ececa0c07329fe1954aec4e64098cfd3ffcac017..a5d704c1d861a0e00d604fee9a95935923ac198c 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.327 2006/05/02 11:28:54 teodor Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.328 2006/05/02 22:25:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -230,7 +230,6 @@ static void vacuum_index(VacPageList vacpagelist, Relation indrel,
             double num_tuples, int keep_tuples);
 static void scan_index(Relation indrel, double num_tuples);
 static bool tid_reaped(ItemPointer itemptr, void *state);
-static bool dummy_tid_reaped(ItemPointer itemptr, void *state);
 static void vac_update_fsm(Relation onerel, VacPageList fraged_pages,
               BlockNumber rel_pages);
 static VacPage copy_vac_page(VacPage vacpage);
@@ -2933,7 +2932,7 @@ vacuum_page(Relation onerel, Buffer buffer, VacPage vacpage)
 }
 
 /*
- * scan_index() -- scan one index relation to update statistic.
+ * scan_index() -- scan one index relation to update pg_class statistics.
  *
  * We use this when we have no deletions to do.
  */
@@ -2941,25 +2940,17 @@ static void
 scan_index(Relation indrel, double num_tuples)
 {
    IndexBulkDeleteResult *stats;
-   IndexVacuumCleanupInfo vcinfo;
+   IndexVacuumInfo ivinfo;
    PGRUsage    ru0;
 
    pg_rusage_init(&ru0);
 
-   /*
-    * Even though we're not planning to delete anything, we use the
-    * ambulkdelete call, because (a) the scan happens within the index AM for
-    * more speed, and (b) it may want to pass private statistics to the
-    * amvacuumcleanup call.
-    */
-   stats = index_bulk_delete(indrel, dummy_tid_reaped, NULL);
+   ivinfo.index = indrel;
+   ivinfo.vacuum_full = true;
+   ivinfo.message_level = elevel;
+   ivinfo.num_heap_tuples = 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);
+   stats = index_vacuum_cleanup(&ivinfo, NULL);
 
    if (!stats)
        return;
@@ -2982,16 +2973,7 @@ scan_index(Relation indrel, double num_tuples)
    /*
     * Check for tuple count mismatch.  If the index is partial, then it's OK
     * for it to have fewer tuples than the heap; else we got trouble.
-    *
-    * XXX Hack. Since GIN stores every pointer to heap several times and
-    * counting num_index_tuples during vacuum is very comlpex and slow
-    * we just copy num_tuples to num_index_tuples as upper limit to avoid
-    * WARNING and optimizer mistakes.
     */
-   if ( indrel->rd_rel->relam == GIN_AM_OID ) 
-   {
-       stats->num_index_tuples = num_tuples; 
-   } else
    if (stats->num_index_tuples != num_tuples)
    {
        if (stats->num_index_tuples > num_tuples ||
@@ -3023,20 +3005,21 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
             double num_tuples, int keep_tuples)
 {
    IndexBulkDeleteResult *stats;
-   IndexVacuumCleanupInfo vcinfo;
+   IndexVacuumInfo ivinfo;
    PGRUsage    ru0;
 
    pg_rusage_init(&ru0);
 
+   ivinfo.index = indrel;
+   ivinfo.vacuum_full = true;
+   ivinfo.message_level = elevel;
+   ivinfo.num_heap_tuples = num_tuples + keep_tuples;
+
    /* Do bulk deletion */
-   stats = index_bulk_delete(indrel, tid_reaped, (void *) vacpagelist);
+   stats = index_bulk_delete(&ivinfo, NULL, tid_reaped, (void *) vacpagelist);
 
    /* 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);
+   stats = index_vacuum_cleanup(&ivinfo, stats);
 
    if (!stats)
        return;
@@ -3061,16 +3044,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
    /*
     * Check for tuple count mismatch.  If the index is partial, then it's OK
     * for it to have fewer tuples than the heap; else we got trouble.
-    *
-    * XXX Hack. Since GIN stores every pointer to heap several times and
-    * counting num_index_tuples during vacuum is very comlpex and slow
-    * we just copy num_tuples to num_index_tuples as upper limit to avoid
-    * WARNING and optimizer mistakes.
     */
-   if ( indrel->rd_rel->relam == GIN_AM_OID ) 
-   {
-       stats->num_index_tuples = num_tuples; 
-   } else
    if (stats->num_index_tuples != num_tuples + keep_tuples)
    {
        if (stats->num_index_tuples > num_tuples + keep_tuples ||
@@ -3137,15 +3111,6 @@ tid_reaped(ItemPointer itemptr, void *state)
    return true;
 }
 
-/*
- * Dummy version for scan_index.
- */
-static bool
-dummy_tid_reaped(ItemPointer itemptr, void *state)
-{
-   return false;
-}
-
 /*
  * Update the shared Free Space Map with the info we now have about
  * free space in the relation, discarding any old info the map may have.
index b270538bd8680539b4b96b3d79903f6993cd8d68..00fda19231077304daec40b6049ffb8d1f4881a2 100644 (file)
@@ -31,7 +31,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.69 2006/03/31 23:32:06 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.70 2006/05/02 22:25:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -96,11 +96,12 @@ static TransactionId FreezeLimit;
 static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
               Relation *Irel, int nindexes);
 static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
-static void lazy_scan_index(Relation indrel, LVRelStats *vacrelstats);
 static void lazy_vacuum_index(Relation indrel,
-                 double *index_tups_vacuumed,
-                 BlockNumber *index_pages_removed,
-                 LVRelStats *vacrelstats);
+                             IndexBulkDeleteResult **stats,
+                             LVRelStats *vacrelstats);
+static void lazy_cleanup_index(Relation indrel,
+                              IndexBulkDeleteResult *stats,
+                              LVRelStats *vacrelstats);
 static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
                 int tupindex, LVRelStats *vacrelstats);
 static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats);
@@ -112,7 +113,6 @@ static void lazy_record_dead_tuple(LVRelStats *vacrelstats,
 static void lazy_record_free_space(LVRelStats *vacrelstats,
                       BlockNumber page, Size avail);
 static bool lazy_tid_reaped(ItemPointer itemptr, void *state);
-static bool dummy_tid_reaped(ItemPointer itemptr, void *state);
 static void lazy_update_fsm(Relation onerel, LVRelStats *vacrelstats);
 static int vac_cmp_itemptr(const void *left, const void *right);
 static int vac_cmp_page_spaces(const void *left, const void *right);
@@ -207,9 +207,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
                tups_vacuumed,
                nkeep,
                nunused;
-   double     *index_tups_vacuumed;
-   BlockNumber *index_pages_removed;
-   bool        did_vacuum_index = false;
+   IndexBulkDeleteResult **indstats;
    int         i;
    PGRUsage    ru0;
 
@@ -224,15 +222,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
    empty_pages = 0;
    num_tuples = tups_vacuumed = nkeep = nunused = 0;
 
-   /*
-    * Because index vacuuming is done in multiple passes, we have to keep
-    * track of the total number of rows and pages removed from each index.
-    * index_tups_vacuumed[i] is the number removed so far from the i'th
-    * index.  (For partial indexes this could well be different from
-    * tups_vacuumed.)  Likewise for index_pages_removed[i].
-    */
-   index_tups_vacuumed = (double *) palloc0(nindexes * sizeof(double));
-   index_pages_removed = (BlockNumber *) palloc0(nindexes * sizeof(BlockNumber));
+   indstats = (IndexBulkDeleteResult **)
+       palloc0(nindexes * sizeof(IndexBulkDeleteResult *));
 
    nblocks = RelationGetNumberOfBlocks(onerel);
    vacrelstats->rel_pages = nblocks;
@@ -263,10 +254,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
            /* Remove index entries */
            for (i = 0; i < nindexes; i++)
                lazy_vacuum_index(Irel[i],
-                                 &index_tups_vacuumed[i],
-                                 &index_pages_removed[i],
+                                 &indstats[i],
                                  vacrelstats);
-           did_vacuum_index = true;
            /* Remove tuples from heap */
            lazy_vacuum_heap(onerel, vacrelstats);
            /* Forget the now-vacuumed tuples, and press on */
@@ -454,18 +443,15 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
        /* Remove index entries */
        for (i = 0; i < nindexes; i++)
            lazy_vacuum_index(Irel[i],
-                             &index_tups_vacuumed[i],
-                             &index_pages_removed[i],
+                             &indstats[i],
                              vacrelstats);
        /* Remove tuples from heap */
        lazy_vacuum_heap(onerel, vacrelstats);
    }
-   else if (!did_vacuum_index)
-   {
-       /* Must do post-vacuum cleanup and statistics update anyway */
-       for (i = 0; i < nindexes; i++)
-           lazy_scan_index(Irel[i], vacrelstats);
-   }
+
+   /* Do post-vacuum cleanup and statistics update for each index */
+   for (i = 0; i < nindexes; i++)
+       lazy_cleanup_index(Irel[i], indstats[i], vacrelstats);
 
    ereport(elevel,
            (errmsg("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
@@ -591,15 +577,17 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
 }
 
 /*
- * lazy_scan_index() -- scan one index relation to update pg_class statistic.
+ * lazy_vacuum_index() -- vacuum one index relation.
  *
- * We use this when we have no deletions to do.
+ *     Delete all the index entries pointing to tuples listed in
+ *     vacrelstats->dead_tuples, and update running statistics.
  */
 static void
-lazy_scan_index(Relation indrel, LVRelStats *vacrelstats)
+lazy_vacuum_index(Relation indrel,
+                 IndexBulkDeleteResult **stats,
+                 LVRelStats *vacrelstats)
 {
-   IndexBulkDeleteResult *stats;
-   IndexVacuumCleanupInfo vcinfo;
+   IndexVacuumInfo ivinfo;
    PGRUsage    ru0;
 
    pg_rusage_init(&ru0);
@@ -613,20 +601,15 @@ lazy_scan_index(Relation indrel, LVRelStats *vacrelstats)
    else
        LockRelation(indrel, AccessExclusiveLock);
 
-   /*
-    * Even though we're not planning to delete anything, we use the
-    * ambulkdelete call, because (a) the scan happens within the index AM for
-    * more speed, and (b) it may want to pass private statistics to the
-    * amvacuumcleanup call.
-    */
-   stats = index_bulk_delete(indrel, dummy_tid_reaped, NULL);
-
-   /* Do post-VACUUM cleanup, even though we deleted nothing */
-   vcinfo.vacuum_full = false;
-   vcinfo.message_level = elevel;
-   vcinfo.num_heap_tuples = vacrelstats->rel_tuples;
+   ivinfo.index = indrel;
+   ivinfo.vacuum_full = false;
+   ivinfo.message_level = elevel;
+   /* We don't yet know rel_tuples, so pass -1 */
+   ivinfo.num_heap_tuples = -1;
 
-   stats = index_vacuum_cleanup(indrel, &vcinfo, stats);
+   /* Do bulk deletion */
+   *stats = index_bulk_delete(&ivinfo, *stats,
+                              lazy_tid_reaped, (void *) vacrelstats);
 
    /*
     * Release lock acquired above.
@@ -636,48 +619,22 @@ lazy_scan_index(Relation indrel, LVRelStats *vacrelstats)
    else
        UnlockRelation(indrel, AccessExclusiveLock);
 
-   if (!stats)
-       return;
-
-   /* now update statistics in pg_class */
-   vac_update_relstats(RelationGetRelid(indrel),
-                       stats->num_pages,
-                       stats->num_index_tuples,
-                       false);
-
    ereport(elevel,
-           (errmsg("index \"%s\" now contains %.0f row versions in %u pages",
+           (errmsg("scanned index \"%s\" to remove %d row versions",
                    RelationGetRelationName(indrel),
-                   stats->num_index_tuples,
-                   stats->num_pages),
-   errdetail("%u index pages have been deleted, %u are currently reusable.\n"
-             "%s.",
-             stats->pages_deleted, stats->pages_free,
-             pg_rusage_show(&ru0))));
-
-   pfree(stats);
+                   vacrelstats->num_dead_tuples),
+            errdetail("%s.", pg_rusage_show(&ru0))));
 }
 
 /*
- * lazy_vacuum_index() -- vacuum one index relation.
- *
- *     Delete all the index entries pointing to tuples listed in
- *     vacrelstats->dead_tuples.
- *
- *     Increment *index_tups_vacuumed by the number of index entries
- *     removed, and *index_pages_removed by the number of pages removed.
- *
- *     Finally, we arrange to update the index relation's statistics in
- *     pg_class.
+ * lazy_cleanup_index() -- do post-vacuum cleanup for one index relation.
  */
 static void
-lazy_vacuum_index(Relation indrel,
-                 double *index_tups_vacuumed,
-                 BlockNumber *index_pages_removed,
-                 LVRelStats *vacrelstats)
+lazy_cleanup_index(Relation indrel,
+                  IndexBulkDeleteResult *stats,
+                  LVRelStats *vacrelstats)
 {
-   IndexBulkDeleteResult *stats;
-   IndexVacuumCleanupInfo vcinfo;
+   IndexVacuumInfo ivinfo;
    PGRUsage    ru0;
 
    pg_rusage_init(&ru0);
@@ -691,17 +648,12 @@ lazy_vacuum_index(Relation indrel,
    else
        LockRelation(indrel, AccessExclusiveLock);
 
-   /* Do bulk deletion */
-   stats = index_bulk_delete(indrel, lazy_tid_reaped, (void *) vacrelstats);
-
-   /* 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;
+   ivinfo.index = indrel;
+   ivinfo.vacuum_full = false;
+   ivinfo.message_level = elevel;
+   ivinfo.num_heap_tuples = vacrelstats->rel_tuples;
 
-   stats = index_vacuum_cleanup(indrel, &vcinfo, stats);
+   stats = index_vacuum_cleanup(&ivinfo, stats);
 
    /*
     * Release lock acquired above.
@@ -714,10 +666,6 @@ lazy_vacuum_index(Relation indrel,
    if (!stats)
        return;
 
-   /* accumulate total removed over multiple index-cleaning cycles */
-   *index_tups_vacuumed += stats->tuples_removed;
-   *index_pages_removed += stats->pages_removed;
-
    /* now update statistics in pg_class */
    vac_update_relstats(RelationGetRelid(indrel),
                        stats->num_pages,
@@ -1134,15 +1082,6 @@ lazy_tid_reaped(ItemPointer itemptr, void *state)
    return (res != NULL);
 }
 
-/*
- * Dummy version for lazy_scan_index.
- */
-static bool
-dummy_tid_reaped(ItemPointer itemptr, void *state)
-{
-   return false;
-}
-
 /*
  * Update the shared Free Space Map with the info we now have about
  * free space in the relation, discarding any old info the map may have.
index 4a9284e0a72c2f5b9c6869ee24881668cbda666d..67a9ef60ee9c8ef85fc511c9102e6d39b90f49c6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.58 2006/03/05 15:58:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.59 2006/05/02 22:25:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 
 /*
- * Struct for statistics returned by bulk-delete operation
+ * Struct for input arguments passed to ambulkdelete and amvacuumcleanup
  *
- * This is now also passed to the index AM's vacuum-cleanup operation,
- * if it has one, which can modify the results as needed.  Note that
- * an index AM could choose to have bulk-delete return a larger struct
- * of which this is just the first field; this provides a way for bulk-delete
- * to communicate additional private data to vacuum-cleanup.
+ * Note that num_heap_tuples will not be valid during ambulkdelete,
+ * only amvacuumcleanup.
+ */
+typedef struct IndexVacuumInfo
+{
+   Relation    index;          /* the index being vacuumed */
+   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 */
+} IndexVacuumInfo;
+
+/*
+ * Struct for statistics returned by ambulkdelete and amvacuumcleanup
+ *
+ * This struct is normally allocated by the first ambulkdelete call and then
+ * passed along through subsequent ones until amvacuumcleanup; however,
+ * amvacuumcleanup must be prepared to allocate it in the case where no
+ * ambulkdelete calls were made (because no tuples needed deletion).
+ * Note that an index AM could choose to return a larger struct
+ * of which this is just the first field; this provides a way for ambulkdelete
+ * to communicate additional private data to amvacuumcleanup.
  *
  * Note: pages_removed is the amount by which the index physically shrank,
  * if any (ie the change in its total size on disk).  pages_deleted and
@@ -36,9 +52,9 @@
 typedef struct IndexBulkDeleteResult
 {
    BlockNumber num_pages;      /* pages remaining in index */
-   BlockNumber pages_removed;  /* # removed by bulk-delete operation */
+   BlockNumber pages_removed;  /* # removed during vacuum operation */
    double      num_index_tuples;       /* tuples remaining */
-   double      tuples_removed; /* # removed by bulk-delete operation */
+   double      tuples_removed; /* # removed during vacuum operation */
    BlockNumber pages_deleted;  /* # unused pages in index */
    BlockNumber pages_free;     /* # pages available for reuse */
 } IndexBulkDeleteResult;
@@ -46,14 +62,6 @@ typedef struct IndexBulkDeleteResult
 /* Typedef for callback function to determine if a tuple is bulk-deletable */
 typedef bool (*IndexBulkDeleteCallback) (ItemPointer itemptr, void *state);
 
-/* Struct for additional arguments passed to vacuum-cleanup operation */
-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 */
 typedef struct SysScanDescData
 {
@@ -98,11 +106,11 @@ extern bool index_getmulti(IndexScanDesc scan,
               ItemPointer tids, int32 max_tids,
               int32 *returned_tids);
 
-extern IndexBulkDeleteResult *index_bulk_delete(Relation indexRelation,
+extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
+                 IndexBulkDeleteResult *stats,
                  IndexBulkDeleteCallback callback,
                  void *callback_state);
-extern IndexBulkDeleteResult *index_vacuum_cleanup(Relation indexRelation,
-                    IndexVacuumCleanupInfo *info,
+extern IndexBulkDeleteResult *index_vacuum_cleanup(IndexVacuumInfo *info,
                     IndexBulkDeleteResult *stats);
 extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
                uint16 procnum);
index 6f22d50da795b4bdfcd872f00a5f65057c4d69ac..226b164ac8584ede3af9eb051db315896e7a25ea 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/hash.h,v 1.68 2006/03/31 23:32:06 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/hash.h,v 1.69 2006/05/02 22:25:10 tgl Exp $
  *
  * NOTES
  *     modeled after Margo Seltzer's hash implementation for unix.
@@ -233,6 +233,7 @@ extern Datum hashendscan(PG_FUNCTION_ARGS);
 extern Datum hashmarkpos(PG_FUNCTION_ARGS);
 extern Datum hashrestrpos(PG_FUNCTION_ARGS);
 extern Datum hashbulkdelete(PG_FUNCTION_ARGS);
+extern Datum hashvacuumcleanup(PG_FUNCTION_ARGS);
 
 /*
  * Datatype-specific hash functions in hashfunc.c.
index cc29eca707c913fae542c7be012e630d183559e1..299bae5a84dd8d1081af0196d12b7e0a4fb071e4 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.330 2006/05/02 15:23:16 teodor Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.331 2006/05/02 22:25:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200605022
+#define CATALOG_VERSION_NO 200605023
 
 #endif
index 29d19147d2d4df669e46a667acfc914764d28e75..07a74c892d149aff97d6c8a65506483972aaef9c 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.41 2006/05/02 11:28:55 teodor Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.42 2006/05/02 22:25:10 tgl Exp $
  *
  * NOTES
  *     the genbki.sh script reads this file and generates .bki
@@ -50,6 +50,7 @@ CATALOG(pg_am,2601)
    bool        amcanmulticol;  /* does AM support multi-column indexes? */
    bool        amoptionalkey;  /* can query omit key for the first column? */
    bool        amindexnulls;   /* does AM support NULL index entries? */
+   bool        amstorage;      /* can storage type differ from column type? */
    bool        amconcurrent;   /* does AM support concurrent updates? */
    bool        amclusterable;  /* does AM support cluster command? */
    regproc     aminsert;       /* "insert this tuple" function */
@@ -77,7 +78,7 @@ typedef FormData_pg_am *Form_pg_am;
  *     compiler constants for pg_am
  * ----------------
  */
-#define Natts_pg_am                        22
+#define Natts_pg_am                        23
 #define Anum_pg_am_amname              1
 #define Anum_pg_am_amstrategies            2
 #define Anum_pg_am_amsupport           3
@@ -86,36 +87,37 @@ typedef FormData_pg_am *Form_pg_am;
 #define Anum_pg_am_amcanmulticol       6
 #define Anum_pg_am_amoptionalkey       7
 #define Anum_pg_am_amindexnulls            8
-#define Anum_pg_am_amconcurrent            9
-#define Anum_pg_am_amclusterable       10
-#define Anum_pg_am_aminsert                11
-#define Anum_pg_am_ambeginscan         12
-#define Anum_pg_am_amgettuple          13
-#define Anum_pg_am_amgetmulti          14
-#define Anum_pg_am_amrescan                15
-#define Anum_pg_am_amendscan           16
-#define Anum_pg_am_ammarkpos           17
-#define Anum_pg_am_amrestrpos          18
-#define Anum_pg_am_ambuild             19
-#define Anum_pg_am_ambulkdelete            20
-#define Anum_pg_am_amvacuumcleanup     21
-#define Anum_pg_am_amcostestimate      22
+#define Anum_pg_am_amstorage           9
+#define Anum_pg_am_amconcurrent            10
+#define Anum_pg_am_amclusterable       11
+#define Anum_pg_am_aminsert                12
+#define Anum_pg_am_ambeginscan         13
+#define Anum_pg_am_amgettuple          14
+#define Anum_pg_am_amgetmulti          15
+#define Anum_pg_am_amrescan                16
+#define Anum_pg_am_amendscan           17
+#define Anum_pg_am_ammarkpos           18
+#define Anum_pg_am_amrestrpos          19
+#define Anum_pg_am_ambuild             20
+#define Anum_pg_am_ambulkdelete            21
+#define Anum_pg_am_amvacuumcleanup     22
+#define Anum_pg_am_amcostestimate      23
 
 /* ----------------
  *     initial contents of pg_am
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree 5 1 1 t t t t t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate ));
+DATA(insert OID = 403 (  btree 5 1 1 t t t t t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate ));
 DESCR("b-tree index access method");
 #define BTREE_AM_OID 403
-DATA(insert OID = 405 (  hash  1 1 0 f f f f t f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete - hashcostestimate ));
+DATA(insert OID = 405 (  hash  1 1 0 f f f f f t f hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate ));
 DESCR("hash index access method");
 #define HASH_AM_OID 405
-DATA(insert OID = 783 (  gist  100 7 0 f t f f t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate ));
+DATA(insert OID = 783 (  gist  100 7 0 f t f f t t gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate ));
 DESCR("GiST index access method");
 #define GIST_AM_OID 783
-DATA(insert OID = 2742 (  gin  100 4 0 f f f f t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ));
+DATA(insert OID = 2742 (  gin  100 4 0 f f f f t f gininsert ginbeginscan gingettuple gingetmulti ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
 
index c4e41ba9c6d31fea973825a3a9dc03bd4ff79afb..ade008fa4e6af1cbad48b2c66a3ca167c3c4d2cc 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.409 2006/05/02 11:28:55 teodor Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.410 2006/05/02 22:25:10 tgl Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -674,9 +674,9 @@ DATA(insert OID = 337 (  btrestrpos        PGNSP PGUID 12 f f t f v 1 2278 "2281" _
 DESCR("btree(internal)");
 DATA(insert OID = 338 (  btbuild          PGNSP PGUID 12 f f t f v 3 2278 "2281 2281 2281" _null_ _null_ _null_ btbuild - _null_ ));
 DESCR("btree(internal)");
-DATA(insert OID = 332 (  btbulkdelete     PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ btbulkdelete - _null_ ));
+DATA(insert OID = 332 (  btbulkdelete     PGNSP PGUID 12 f f t f v 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ btbulkdelete - _null_ ));
 DESCR("btree(internal)");
-DATA(insert OID = 972 (  btvacuumcleanup   PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ btvacuumcleanup - _null_ ));
+DATA(insert OID = 972 (  btvacuumcleanup   PGNSP PGUID 12 f f t f v 2 2281 "2281 2281" _null_ _null_ _null_ btvacuumcleanup - _null_ ));
 DESCR("btree(internal)");
 DATA(insert OID = 1268 (  btcostestimate   PGNSP PGUID 12 f f t f v 7 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_  btcostestimate - _null_ ));
 DESCR("btree(internal)");
@@ -791,7 +791,9 @@ DATA(insert OID = 447 (  hashrestrpos      PGNSP PGUID 12 f f t f v 1 2278 "2281"
 DESCR("hash(internal)");
 DATA(insert OID = 448 (  hashbuild        PGNSP PGUID 12 f f t f v 3 2278 "2281 2281 2281" _null_ _null_ _null_ hashbuild - _null_ ));
 DESCR("hash(internal)");
-DATA(insert OID = 442 (  hashbulkdelete    PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ hashbulkdelete - _null_ ));
+DATA(insert OID = 442 (  hashbulkdelete    PGNSP PGUID 12 f f t f v 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ hashbulkdelete - _null_ ));
+DESCR("hash(internal)");
+DATA(insert OID = 425 (  hashvacuumcleanup PGNSP PGUID 12 f f t f v 2 2281 "2281 2281" _null_ _null_ _null_ hashvacuumcleanup - _null_ ));
 DESCR("hash(internal)");
 DATA(insert OID = 438 (  hashcostestimate  PGNSP PGUID 12 f f t f v 7 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_  hashcostestimate - _null_ ));
 DESCR("hash(internal)");
@@ -1055,9 +1057,10 @@ DATA(insert OID = 781 (  gistrestrpos       PGNSP PGUID 12 f f t f v 1 2278 "2281"
 DESCR("gist(internal)");
 DATA(insert OID = 782 (  gistbuild        PGNSP PGUID 12 f f t f v 3 2278 "2281 2281 2281" _null_ _null_ _null_ gistbuild - _null_ ));
 DESCR("gist(internal)");
-DATA(insert OID = 776 (  gistbulkdelete    PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ gistbulkdelete - _null_ ));
+DATA(insert OID = 776 (  gistbulkdelete    PGNSP PGUID 12 f f t f v 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ gistbulkdelete - _null_ ));
+DESCR("gist(internal)");
+DATA(insert OID = 2561 (  gistvacuumcleanup   PGNSP PGUID 12 f f t f v 2 2281 "2281 2281" _null_ _null_ _null_ gistvacuumcleanup - _null_ ));
 DESCR("gist(internal)");
-DATA(insert OID = 2561 (  gistvacuumcleanup   PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ gistvacuumcleanup - _null_ ));
 DATA(insert OID = 772 (  gistcostestimate  PGNSP PGUID 12 f f t f v 7 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_  gistcostestimate - _null_ ));
 DESCR("gist(internal)");
 
@@ -3832,9 +3835,9 @@ DATA(insert OID = 2737 (  ginrestrpos    PGNSP PGUID 12 f f t f v 1 2278 "2281"
 DESCR("gin(internal)");
 DATA(insert OID = 2738 (  ginbuild        PGNSP PGUID 12 f f t f v 3 2278 "2281 2281 2281" _null_ _null_ _null_ ginbuild - _null_ ));
 DESCR("gin(internal)");
-DATA(insert OID = 2739 (  ginbulkdelete    PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ ginbulkdelete - _null_ ));
+DATA(insert OID = 2739 (  ginbulkdelete    PGNSP PGUID 12 f f t f v 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ ginbulkdelete - _null_ ));
 DESCR("gin(internal)");
-DATA(insert OID = 2740 (  ginvacuumcleanup PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ ginvacuumcleanup - _null_ ));
+DATA(insert OID = 2740 (  ginvacuumcleanup PGNSP PGUID 12 f f t f v 2 2281 "2281 2281" _null_ _null_ _null_ ginvacuumcleanup - _null_ ));
 DESCR("gin(internal)");
 DATA(insert OID = 2741 (  gincostestimate  PGNSP PGUID 12 f f t f v 7 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_  gincostestimate - _null_ ));
 DESCR("gin(internal)");