[HACKERS] Caching index AM working data across aminsert calls - Mailing list pgsql-hackers

From Tom Lane
Subject [HACKERS] Caching index AM working data across aminsert calls
Date
Msg-id 27568.1486508680@sss.pgh.pa.us
Whole thread Raw
Responses Re: [HACKERS] Caching index AM working data across aminsert calls  (Robert Haas <robertmhaas@gmail.com>)
List pgsql-hackers
It's always been possible for index AMs to cache data across successive
amgettuple calls within a single SQL command: the IndexScanDesc.opaque
field is meant for precisely that.  However, no comparable facility
exists for amortizing setup work across successive aminsert calls.
The attached proposed patch adds such a feature and teaches gin,
gist, and brin to use it.  (The other standard index AMs keep everything
they need in the relcache, so there's little to improve there.)

The improvement I see from this is fairly modest in a normal build.
In an example similar to the gin regression test's main insert query,

insert into gin_test_tbl select array[1, 2, g] from generate_series(1, 1000000) g;

the overall insertion speed increases perhaps 10%, which is nice but
not great.  gist and brin are less, maybe 5% or so.  However, because
most of what happens in the saved work is catalog lookups, the savings
in a CLOBBER_CACHE_ALWAYS test build are pretty substantial: the runtime
of the gin regression script, on my workstation, goes from 40 minutes
to 4 seconds.  (Yes, really.)  The gist and brin test scripts are less
insert-heavy but still lose several minutes apiece.  Since the core
regression tests are run multiple times (twice serially and once in
parallel) in the standard buildfarm cycle, I estimate that this patch
would cut over 1.5 hours from the cycle time for a CLOBBER_CACHE_ALWAYS
critter running on hardware similar to mine.  I think that alone makes it
worth doing.

The reason this has been hard up to now is that the aminsert function
is not passed any useful place to cache per-statement data.  What I
chose to do in the attached is to add suitable fields to struct IndexInfo
and pass that to aminsert.  That's not widening the index AM API very
much because IndexInfo is already within the ken of ambuild.  I figured
that this might be a particularly useful way to do it because it means
that aminsert also has access to the other data in the IndexInfo struct,
which might save it having to recompute some things.  And it makes the
DDL info available to ambuild and aminsert more similar, which seems good
on general principles.

I also looked into the idea of using the index relcache entry's
rd_amcache field for this purpose, but that fails immediately in a
CLOBBER_CACHE_ALWAYS build, because gininsert (at least, probably the
other ones too) is not robust against its GinState disappearing from under
it mid-insert.  Since rd_amcache goes away on a cache flush even if the
index is open, that doesn't work.  We could maybe fix that by introducing
some way for AMs to control the lifetime of rd_amcache, but it would
result in a substantially more complex and invasive patch than this one,
and I'm unconvinced that it'd be worth the trouble.

Thoughts, objections?

            regards, tom lane

diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index 29bc5ce..913f1f8 100644
*** a/contrib/bloom/blinsert.c
--- b/contrib/bloom/blinsert.c
*************** blbuildempty(Relation index)
*** 190,196 ****
   */
  bool
  blinsert(Relation index, Datum *values, bool *isnull,
!          ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
  {
      BloomState    blstate;
      BloomTuple *itup;
--- 190,198 ----
   */
  bool
  blinsert(Relation index, Datum *values, bool *isnull,
!          ItemPointer ht_ctid, Relation heapRel,
!          IndexUniqueCheck checkUnique,
!          IndexInfo *indexInfo)
  {
      BloomState    blstate;
      BloomTuple *itup;
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index a75a235..39d8d05 100644
*** a/contrib/bloom/bloom.h
--- b/contrib/bloom/bloom.h
*************** extern bool blvalidate(Oid opclassoid);
*** 189,195 ****
  /* index access method interface functions */
  extern bool blinsert(Relation index, Datum *values, bool *isnull,
           ItemPointer ht_ctid, Relation heapRel,
!          IndexUniqueCheck checkUnique);
  extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
  extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
  extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
--- 189,196 ----
  /* index access method interface functions */
  extern bool blinsert(Relation index, Datum *values, bool *isnull,
           ItemPointer ht_ctid, Relation heapRel,
!          IndexUniqueCheck checkUnique,
!          struct IndexInfo *indexInfo);
  extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
  extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
  extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index 5d8e557..9afd7f6 100644
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
*************** aminsert (Relation indexRelation,
*** 259,265 ****
            bool *isnull,
            ItemPointer heap_tid,
            Relation heapRelation,
!           IndexUniqueCheck checkUnique);
  </programlisting>
     Insert a new tuple into an existing index.  The <literal>values</> and
     <literal>isnull</> arrays give the key values to be indexed, and
--- 259,266 ----
            bool *isnull,
            ItemPointer heap_tid,
            Relation heapRelation,
!           IndexUniqueCheck checkUnique,
!           IndexInfo *indexInfo);
  </programlisting>
     Insert a new tuple into an existing index.  The <literal>values</> and
     <literal>isnull</> arrays give the key values to be indexed, and
*************** aminsert (Relation indexRelation,
*** 288,293 ****
--- 289,302 ----
    </para>

    <para>
+    If the index AM wishes to cache data across successive index insertions
+    within a SQL statement, it can allocate space
+    in <literal>indexInfo->ii_Context</literal> and store a pointer to the
+    data in <literal>indexInfo->ii_AmCache</literal> (which will be NULL
+    initially).
+   </para>
+
+   <para>
  <programlisting>
  IndexBulkDeleteResult *
  ambulkdelete (IndexVacuumInfo *info,
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index b2afdb7..4ff046b 100644
*** a/src/backend/access/brin/brin.c
--- b/src/backend/access/brin/brin.c
*************** brinhandler(PG_FUNCTION_ARGS)
*** 131,144 ****
  bool
  brininsert(Relation idxRel, Datum *values, bool *nulls,
             ItemPointer heaptid, Relation heapRel,
!            IndexUniqueCheck checkUnique)
  {
      BlockNumber pagesPerRange;
!     BrinDesc   *bdesc = NULL;
      BrinRevmap *revmap;
      Buffer        buf = InvalidBuffer;
      MemoryContext tupcxt = NULL;
!     MemoryContext oldcxt = NULL;

      revmap = brinRevmapInitialize(idxRel, &pagesPerRange, NULL);

--- 131,145 ----
  bool
  brininsert(Relation idxRel, Datum *values, bool *nulls,
             ItemPointer heaptid, Relation heapRel,
!            IndexUniqueCheck checkUnique,
!            IndexInfo *indexInfo)
  {
      BlockNumber pagesPerRange;
!     BrinDesc   *bdesc = (BrinDesc *) indexInfo->ii_AmCache;
      BrinRevmap *revmap;
      Buffer        buf = InvalidBuffer;
      MemoryContext tupcxt = NULL;
!     MemoryContext oldcxt = CurrentMemoryContext;

      revmap = brinRevmapInitialize(idxRel, &pagesPerRange, NULL);

*************** brininsert(Relation idxRel, Datum *value
*** 163,176 ****
          if (!brtup)
              break;

!         /* First time through? */
          if (bdesc == NULL)
          {
              bdesc = brin_build_desc(idxRel);
              tupcxt = AllocSetContextCreate(CurrentMemoryContext,
                                             "brininsert cxt",
                                             ALLOCSET_DEFAULT_SIZES);
!             oldcxt = MemoryContextSwitchTo(tupcxt);
          }

          dtup = brin_deform_tuple(bdesc, brtup);
--- 164,184 ----
          if (!brtup)
              break;

!         /* First time through in this statement? */
          if (bdesc == NULL)
          {
+             MemoryContextSwitchTo(indexInfo->ii_Context);
              bdesc = brin_build_desc(idxRel);
+             indexInfo->ii_AmCache = (void *) bdesc;
+             MemoryContextSwitchTo(oldcxt);
+         }
+         /* First time through in this brininsert call? */
+         if (tupcxt == NULL)
+         {
              tupcxt = AllocSetContextCreate(CurrentMemoryContext,
                                             "brininsert cxt",
                                             ALLOCSET_DEFAULT_SIZES);
!             MemoryContextSwitchTo(tupcxt);
          }

          dtup = brin_deform_tuple(bdesc, brtup);
*************** brininsert(Relation idxRel, Datum *value
*** 261,272 ****
      brinRevmapTerminate(revmap);
      if (BufferIsValid(buf))
          ReleaseBuffer(buf);
!     if (bdesc != NULL)
!     {
!         brin_free_desc(bdesc);
!         MemoryContextSwitchTo(oldcxt);
          MemoryContextDelete(tupcxt);
-     }

      return false;
  }
--- 269,277 ----
      brinRevmapTerminate(revmap);
      if (BufferIsValid(buf))
          ReleaseBuffer(buf);
!     MemoryContextSwitchTo(oldcxt);
!     if (tupcxt != NULL)
          MemoryContextDelete(tupcxt);

      return false;
  }
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index 03a7235..3d3b9e0 100644
*** a/src/backend/access/gin/gininsert.c
--- b/src/backend/access/gin/gininsert.c
*************** ginHeapTupleInsert(GinState *ginstate, O
*** 482,520 ****
  bool
  gininsert(Relation index, Datum *values, bool *isnull,
            ItemPointer ht_ctid, Relation heapRel,
!           IndexUniqueCheck checkUnique)
  {
!     GinState    ginstate;
      MemoryContext oldCtx;
      MemoryContext insertCtx;
      int            i;

      insertCtx = AllocSetContextCreate(CurrentMemoryContext,
                                        "Gin insert temporary context",
                                        ALLOCSET_DEFAULT_SIZES);

      oldCtx = MemoryContextSwitchTo(insertCtx);

-     initGinState(&ginstate, index);
-
      if (GinGetUseFastUpdate(index))
      {
          GinTupleCollector collector;

          memset(&collector, 0, sizeof(GinTupleCollector));

!         for (i = 0; i < ginstate.origTupdesc->natts; i++)
!             ginHeapTupleFastCollect(&ginstate, &collector,
                                      (OffsetNumber) (i + 1),
                                      values[i], isnull[i],
                                      ht_ctid);

!         ginHeapTupleFastInsert(&ginstate, &collector);
      }
      else
      {
!         for (i = 0; i < ginstate.origTupdesc->natts; i++)
!             ginHeapTupleInsert(&ginstate, (OffsetNumber) (i + 1),
                                 values[i], isnull[i],
                                 ht_ctid);
      }
--- 482,529 ----
  bool
  gininsert(Relation index, Datum *values, bool *isnull,
            ItemPointer ht_ctid, Relation heapRel,
!           IndexUniqueCheck checkUnique,
!           IndexInfo *indexInfo)
  {
!     GinState   *ginstate = (GinState *) indexInfo->ii_AmCache;
      MemoryContext oldCtx;
      MemoryContext insertCtx;
      int            i;

+     /* Initialize GinState cache if first call in this statement */
+     if (ginstate == NULL)
+     {
+         oldCtx = MemoryContextSwitchTo(indexInfo->ii_Context);
+         ginstate = (GinState *) palloc(sizeof(GinState));
+         initGinState(ginstate, index);
+         indexInfo->ii_AmCache = (void *) ginstate;
+         MemoryContextSwitchTo(oldCtx);
+     }
+
      insertCtx = AllocSetContextCreate(CurrentMemoryContext,
                                        "Gin insert temporary context",
                                        ALLOCSET_DEFAULT_SIZES);

      oldCtx = MemoryContextSwitchTo(insertCtx);

      if (GinGetUseFastUpdate(index))
      {
          GinTupleCollector collector;

          memset(&collector, 0, sizeof(GinTupleCollector));

!         for (i = 0; i < ginstate->origTupdesc->natts; i++)
!             ginHeapTupleFastCollect(ginstate, &collector,
                                      (OffsetNumber) (i + 1),
                                      values[i], isnull[i],
                                      ht_ctid);

!         ginHeapTupleFastInsert(ginstate, &collector);
      }
      else
      {
!         for (i = 0; i < ginstate->origTupdesc->natts; i++)
!             ginHeapTupleInsert(ginstate, (OffsetNumber) (i + 1),
                                 values[i], isnull[i],
                                 ht_ctid);
      }
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index c2247ad..96ead53 100644
*** a/src/backend/access/gist/gist.c
--- b/src/backend/access/gist/gist.c
***************
*** 18,23 ****
--- 18,24 ----
  #include "access/gistscan.h"
  #include "catalog/pg_collation.h"
  #include "miscadmin.h"
+ #include "nodes/execnodes.h"
  #include "utils/builtins.h"
  #include "utils/index_selfuncs.h"
  #include "utils/memutils.h"
*************** gistbuildempty(Relation index)
*** 144,164 ****
  bool
  gistinsert(Relation r, Datum *values, bool *isnull,
             ItemPointer ht_ctid, Relation heapRel,
!            IndexUniqueCheck checkUnique)
  {
      IndexTuple    itup;
-     GISTSTATE  *giststate;
      MemoryContext oldCxt;

!     giststate = initGISTstate(r);

-     /*
-      * We use the giststate's scan context as temp context too.  This means
-      * that any memory leaked by the support functions is not reclaimed until
-      * end of insert.  In most cases, we aren't going to call the support
-      * functions very many times before finishing the insert, so this seems
-      * cheaper than resetting a temp context for each function call.
-      */
      oldCxt = MemoryContextSwitchTo(giststate->tempCxt);

      itup = gistFormTuple(giststate, r,
--- 145,167 ----
  bool
  gistinsert(Relation r, Datum *values, bool *isnull,
             ItemPointer ht_ctid, Relation heapRel,
!            IndexUniqueCheck checkUnique,
!            IndexInfo *indexInfo)
  {
+     GISTSTATE  *giststate = (GISTSTATE *) indexInfo->ii_AmCache;
      IndexTuple    itup;
      MemoryContext oldCxt;

!     /* Initialize GISTSTATE cache if first call in this statement */
!     if (giststate == NULL)
!     {
!         oldCxt = MemoryContextSwitchTo(indexInfo->ii_Context);
!         giststate = initGISTstate(r);
!         giststate->tempCxt = createTempGistContext();
!         indexInfo->ii_AmCache = (void *) giststate;
!         MemoryContextSwitchTo(oldCxt);
!     }

      oldCxt = MemoryContextSwitchTo(giststate->tempCxt);

      itup = gistFormTuple(giststate, r,
*************** gistinsert(Relation r, Datum *values, bo
*** 169,175 ****

      /* cleanup */
      MemoryContextSwitchTo(oldCxt);
!     freeGISTstate(giststate);

      return false;
  }
--- 172,178 ----

      /* cleanup */
      MemoryContextSwitchTo(oldCxt);
!     MemoryContextReset(giststate->tempCxt);

      return false;
  }
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 97ad22a..bca77a8 100644
*** a/src/backend/access/hash/hash.c
--- b/src/backend/access/hash/hash.c
*************** hashbuildCallback(Relation index,
*** 232,238 ****
  bool
  hashinsert(Relation rel, Datum *values, bool *isnull,
             ItemPointer ht_ctid, Relation heapRel,
!            IndexUniqueCheck checkUnique)
  {
      Datum        index_values[1];
      bool        index_isnull[1];
--- 232,239 ----
  bool
  hashinsert(Relation rel, Datum *values, bool *isnull,
             ItemPointer ht_ctid, Relation heapRel,
!            IndexUniqueCheck checkUnique,
!            IndexInfo *indexInfo)
  {
      Datum        index_values[1];
      bool        index_isnull[1];
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 496648c..19e7048 100644
*** a/src/backend/access/heap/tuptoaster.c
--- b/src/backend/access/heap/tuptoaster.c
*************** toast_save_datum(Relation rel, Datum val
*** 1604,1610 ****
           * Create the index entry.  We cheat a little here by not using
           * FormIndexDatum: this relies on the knowledge that the index columns
           * are the same as the initial columns of the table for all the
!          * indexes.
           *
           * Note also that there had better not be any user-created index on
           * the TOAST table, since we don't bother to update anything else.
--- 1604,1612 ----
           * Create the index entry.  We cheat a little here by not using
           * FormIndexDatum: this relies on the knowledge that the index columns
           * are the same as the initial columns of the table for all the
!          * indexes.  We also cheat by not providing an IndexInfo: this is okay
!          * for now because btree doesn't need one, but we might have to be
!          * more honest someday.
           *
           * Note also that there had better not be any user-created index on
           * the TOAST table, since we don't bother to update anything else.
*************** toast_save_datum(Relation rel, Datum val
*** 1617,1623 ****
                               &(toasttup->t_self),
                               toastrel,
                               toastidxs[i]->rd_index->indisunique ?
!                              UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
          }

          /*
--- 1619,1626 ----
                               &(toasttup->t_self),
                               toastrel,
                               toastidxs[i]->rd_index->indisunique ?
!                              UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
!                              NULL);
          }

          /*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index ba27c1e..4e7eca7 100644
*** a/src/backend/access/index/indexam.c
--- b/src/backend/access/index/indexam.c
*************** index_insert(Relation indexRelation,
*** 196,202 ****
               bool *isnull,
               ItemPointer heap_t_ctid,
               Relation heapRelation,
!              IndexUniqueCheck checkUnique)
  {
      RELATION_CHECKS;
      CHECK_REL_PROCEDURE(aminsert);
--- 196,203 ----
               bool *isnull,
               ItemPointer heap_t_ctid,
               Relation heapRelation,
!              IndexUniqueCheck checkUnique,
!              IndexInfo *indexInfo)
  {
      RELATION_CHECKS;
      CHECK_REL_PROCEDURE(aminsert);
*************** index_insert(Relation indexRelation,
*** 208,214 ****

      return indexRelation->rd_amroutine->aminsert(indexRelation, values, isnull,
                                                   heap_t_ctid, heapRelation,
!                                                  checkUnique);
  }

  /*
--- 209,215 ----

      return indexRelation->rd_amroutine->aminsert(indexRelation, values, isnull,
                                                   heap_t_ctid, heapRelation,
!                                                  checkUnique, indexInfo);
  }

  /*
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 469e7ab..945e563 100644
*** a/src/backend/access/nbtree/nbtree.c
--- b/src/backend/access/nbtree/nbtree.c
*************** btbuildempty(Relation index)
*** 276,282 ****
  bool
  btinsert(Relation rel, Datum *values, bool *isnull,
           ItemPointer ht_ctid, Relation heapRel,
!          IndexUniqueCheck checkUnique)
  {
      bool        result;
      IndexTuple    itup;
--- 276,283 ----
  bool
  btinsert(Relation rel, Datum *values, bool *isnull,
           ItemPointer ht_ctid, Relation heapRel,
!          IndexUniqueCheck checkUnique,
!          IndexInfo *indexInfo)
  {
      bool        result;
      IndexTuple    itup;
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
index b42f4b7..14f8a9e 100644
*** a/src/backend/access/spgist/spginsert.c
--- b/src/backend/access/spgist/spginsert.c
*************** spgbuildempty(Relation index)
*** 206,212 ****
  bool
  spginsert(Relation index, Datum *values, bool *isnull,
            ItemPointer ht_ctid, Relation heapRel,
!           IndexUniqueCheck checkUnique)
  {
      SpGistState spgstate;
      MemoryContext oldCtx;
--- 206,213 ----
  bool
  spginsert(Relation index, Datum *values, bool *isnull,
            ItemPointer ht_ctid, Relation heapRel,
!           IndexUniqueCheck checkUnique,
!           IndexInfo *indexInfo)
  {
      SpGistState spgstate;
      MemoryContext oldCtx;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 815a694..f8d9214 100644
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
*************** BuildIndexInfo(Relation index)
*** 1687,1692 ****
--- 1687,1696 ----
      ii->ii_Concurrent = false;
      ii->ii_BrokenHotChain = false;

+     /* set up for possible use by index AM */
+     ii->ii_AmCache = NULL;
+     ii->ii_Context = CurrentMemoryContext;
+
      return ii;
  }

*************** validate_index_heapscan(Relation heapRel
*** 3158,3164 ****
                           &rootTuple,
                           heapRelation,
                           indexInfo->ii_Unique ?
!                          UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);

              state->tups_inserted += 1;
          }
--- 3162,3169 ----
                           &rootTuple,
                           heapRelation,
                           indexInfo->ii_Unique ?
!                          UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
!                          indexInfo);

              state->tups_inserted += 1;
          }
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 76268e1..abc344a 100644
*** a/src/backend/catalog/indexing.c
--- b/src/backend/catalog/indexing.c
*************** CatalogIndexInsert(CatalogIndexState ind
*** 139,145 ****
                       &(heapTuple->t_self),        /* tid of heap tuple */
                       heapRelation,
                       relationDescs[i]->rd_index->indisunique ?
!                      UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
      }

      ExecDropSingleTupleTableSlot(slot);
--- 139,146 ----
                       &(heapTuple->t_self),        /* tid of heap tuple */
                       heapRelation,
                       relationDescs[i]->rd_index->indisunique ?
!                      UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
!                      indexInfo);
      }

      ExecDropSingleTupleTableSlot(slot);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index e5f773d..0e42316 100644
*** a/src/backend/catalog/toasting.c
--- b/src/backend/catalog/toasting.c
*************** create_toast_table(Relation rel, Oid toa
*** 315,320 ****
--- 315,322 ----
      indexInfo->ii_ReadyForInserts = true;
      indexInfo->ii_Concurrent = false;
      indexInfo->ii_BrokenHotChain = false;
+     indexInfo->ii_AmCache = NULL;
+     indexInfo->ii_Context = CurrentMemoryContext;

      collationObjectId[0] = InvalidOid;
      collationObjectId[1] = InvalidOid;
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index e9eeacd..e2544e5 100644
*** a/src/backend/commands/constraint.c
--- b/src/backend/commands/constraint.c
*************** unique_key_recheck(PG_FUNCTION_ARGS)
*** 165,171 ****
           * index will know about.
           */
          index_insert(indexRel, values, isnull, &(new_row->t_self),
!                      trigdata->tg_relation, UNIQUE_CHECK_EXISTING);
      }
      else
      {
--- 165,172 ----
           * index will know about.
           */
          index_insert(indexRel, values, isnull, &(new_row->t_self),
!                      trigdata->tg_relation, UNIQUE_CHECK_EXISTING,
!                      indexInfo);
      }
      else
      {
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index f4814c0..265e9b3 100644
*** a/src/backend/commands/indexcmds.c
--- b/src/backend/commands/indexcmds.c
*************** CheckIndexCompatible(Oid oldId,
*** 183,188 ****
--- 183,190 ----
      indexInfo->ii_ExclusionOps = NULL;
      indexInfo->ii_ExclusionProcs = NULL;
      indexInfo->ii_ExclusionStrats = NULL;
+     indexInfo->ii_AmCache = NULL;
+     indexInfo->ii_Context = CurrentMemoryContext;
      typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
      collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
      classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
*************** DefineIndex(Oid relationId,
*** 562,567 ****
--- 564,571 ----
      indexInfo->ii_ReadyForInserts = !stmt->concurrent;
      indexInfo->ii_Concurrent = stmt->concurrent;
      indexInfo->ii_BrokenHotChain = false;
+     indexInfo->ii_AmCache = NULL;
+     indexInfo->ii_Context = CurrentMemoryContext;

      typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
      collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 8d119f6..5242dee 100644
*** a/src/backend/executor/execIndexing.c
--- b/src/backend/executor/execIndexing.c
*************** ExecInsertIndexTuples(TupleTableSlot *sl
*** 391,397 ****
                           isnull,    /* null flags */
                           tupleid,        /* tid of heap tuple */
                           heapRelation,    /* heap relation */
!                          checkUnique);    /* type of uniqueness check to do */

          /*
           * If the index has an associated exclusion constraint, check that.
--- 391,398 ----
                           isnull,    /* null flags */
                           tupleid,        /* tid of heap tuple */
                           heapRelation,    /* heap relation */
!                          checkUnique,    /* type of uniqueness check to do */
!                          indexInfo);    /* index AM may need this */

          /*
           * If the index has an associated exclusion constraint, check that.
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index e91e41d..b0730bf 100644
*** a/src/include/access/amapi.h
--- b/src/include/access/amapi.h
*************** typedef bool (*aminsert_function) (Relat
*** 72,78 ****
                                                 bool *isnull,
                                                 ItemPointer heap_tid,
                                                 Relation heapRelation,
!                                                IndexUniqueCheck checkUnique);

  /* bulk delete */
  typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info,
--- 72,79 ----
                                                 bool *isnull,
                                                 ItemPointer heap_tid,
                                                 Relation heapRelation,
!                                                IndexUniqueCheck checkUnique,
!                                                struct IndexInfo *indexInfo);

  /* bulk delete */
  typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info,
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 4031260..abe8877 100644
*** a/src/include/access/brin_internal.h
--- b/src/include/access/brin_internal.h
*************** extern IndexBuildResult *brinbuild(Relat
*** 89,95 ****
  extern void brinbuildempty(Relation index);
  extern bool brininsert(Relation idxRel, Datum *values, bool *nulls,
             ItemPointer heaptid, Relation heapRel,
!            IndexUniqueCheck checkUnique);
  extern IndexScanDesc brinbeginscan(Relation r, int nkeys, int norderbys);
  extern int64 bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
  extern void brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
--- 89,96 ----
  extern void brinbuildempty(Relation index);
  extern bool brininsert(Relation idxRel, Datum *values, bool *nulls,
             ItemPointer heaptid, Relation heapRel,
!            IndexUniqueCheck checkUnique,
!            struct IndexInfo *indexInfo);
  extern IndexScanDesc brinbeginscan(Relation r, int nkeys, int norderbys);
  extern int64 bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
  extern void brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 51466b9..f467b18 100644
*** a/src/include/access/genam.h
--- b/src/include/access/genam.h
***************
*** 21,26 ****
--- 21,29 ----
  #include "utils/relcache.h"
  #include "utils/snapshot.h"

+ /* We don't want this file to depend on execnodes.h. */
+ struct IndexInfo;
+
  /*
   * Struct for statistics returned by ambuild
   */
*************** extern bool index_insert(Relation indexR
*** 131,137 ****
               Datum *values, bool *isnull,
               ItemPointer heap_t_ctid,
               Relation heapRelation,
!              IndexUniqueCheck checkUnique);

  extern IndexScanDesc index_beginscan(Relation heapRelation,
                  Relation indexRelation,
--- 134,141 ----
               Datum *values, bool *isnull,
               ItemPointer heap_t_ctid,
               Relation heapRelation,
!              IndexUniqueCheck checkUnique,
!              struct IndexInfo *indexInfo);

  extern IndexScanDesc index_beginscan(Relation heapRelation,
                  Relation indexRelation,
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index d46274e..7e1557e 100644
*** a/src/include/access/gin_private.h
--- b/src/include/access/gin_private.h
*************** extern IndexBuildResult *ginbuild(Relati
*** 617,623 ****
  extern void ginbuildempty(Relation index);
  extern bool gininsert(Relation index, Datum *values, bool *isnull,
            ItemPointer ht_ctid, Relation heapRel,
!           IndexUniqueCheck checkUnique);
  extern void ginEntryInsert(GinState *ginstate,
                 OffsetNumber attnum, Datum key, GinNullCategory category,
                 ItemPointerData *items, uint32 nitem,
--- 617,624 ----
  extern void ginbuildempty(Relation index);
  extern bool gininsert(Relation index, Datum *values, bool *isnull,
            ItemPointer ht_ctid, Relation heapRel,
!           IndexUniqueCheck checkUnique,
!           struct IndexInfo *indexInfo);
  extern void ginEntryInsert(GinState *ginstate,
                 OffsetNumber attnum, Datum key, GinNullCategory category,
                 ItemPointerData *items, uint32 nitem,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 60a770a..a4d3e7c 100644
*** a/src/include/access/gist_private.h
--- b/src/include/access/gist_private.h
*************** typedef struct GiSTOptions
*** 426,432 ****
  extern void gistbuildempty(Relation index);
  extern bool gistinsert(Relation r, Datum *values, bool *isnull,
             ItemPointer ht_ctid, Relation heapRel,
!            IndexUniqueCheck checkUnique);
  extern MemoryContext createTempGistContext(void);
  extern GISTSTATE *initGISTstate(Relation index);
  extern void freeGISTstate(GISTSTATE *giststate);
--- 426,433 ----
  extern void gistbuildempty(Relation index);
  extern bool gistinsert(Relation r, Datum *values, bool *isnull,
             ItemPointer ht_ctid, Relation heapRel,
!            IndexUniqueCheck checkUnique,
!            struct IndexInfo *indexInfo);
  extern MemoryContext createTempGistContext(void);
  extern GISTSTATE *initGISTstate(Relation index);
  extern void freeGISTstate(GISTSTATE *giststate);
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index c045585..3bf587b 100644
*** a/src/include/access/hash.h
--- b/src/include/access/hash.h
*************** extern IndexBuildResult *hashbuild(Relat
*** 277,283 ****
  extern void hashbuildempty(Relation index);
  extern bool hashinsert(Relation rel, Datum *values, bool *isnull,
             ItemPointer ht_ctid, Relation heapRel,
!            IndexUniqueCheck checkUnique);
  extern bool hashgettuple(IndexScanDesc scan, ScanDirection dir);
  extern int64 hashgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
  extern IndexScanDesc hashbeginscan(Relation rel, int nkeys, int norderbys);
--- 277,284 ----
  extern void hashbuildempty(Relation index);
  extern bool hashinsert(Relation rel, Datum *values, bool *isnull,
             ItemPointer ht_ctid, Relation heapRel,
!            IndexUniqueCheck checkUnique,
!            struct IndexInfo *indexInfo);
  extern bool hashgettuple(IndexScanDesc scan, ScanDirection dir);
  extern int64 hashgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
  extern IndexScanDesc hashbeginscan(Relation rel, int nkeys, int norderbys);
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 011a72e..70ff3aa 100644
*** a/src/include/access/nbtree.h
--- b/src/include/access/nbtree.h
*************** extern IndexBuildResult *btbuild(Relatio
*** 659,665 ****
  extern void btbuildempty(Relation index);
  extern bool btinsert(Relation rel, Datum *values, bool *isnull,
           ItemPointer ht_ctid, Relation heapRel,
!          IndexUniqueCheck checkUnique);
  extern IndexScanDesc btbeginscan(Relation rel, int nkeys, int norderbys);
  extern bool btgettuple(IndexScanDesc scan, ScanDirection dir);
  extern int64 btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
--- 659,666 ----
  extern void btbuildempty(Relation index);
  extern bool btinsert(Relation rel, Datum *values, bool *isnull,
           ItemPointer ht_ctid, Relation heapRel,
!          IndexUniqueCheck checkUnique,
!          struct IndexInfo *indexInfo);
  extern IndexScanDesc btbeginscan(Relation rel, int nkeys, int norderbys);
  extern bool btgettuple(IndexScanDesc scan, ScanDirection dir);
  extern int64 btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index aaf78bc..c69af63 100644
*** a/src/include/access/spgist.h
--- b/src/include/access/spgist.h
*************** extern IndexBuildResult *spgbuild(Relati
*** 191,197 ****
  extern void spgbuildempty(Relation index);
  extern bool spginsert(Relation index, Datum *values, bool *isnull,
            ItemPointer ht_ctid, Relation heapRel,
!           IndexUniqueCheck checkUnique);

  /* spgscan.c */
  extern IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz);
--- 191,198 ----
  extern void spgbuildempty(Relation index);
  extern bool spginsert(Relation index, Datum *values, bool *isnull,
            ItemPointer ht_ctid, Relation heapRel,
!           IndexUniqueCheck checkUnique,
!           struct IndexInfo *indexInfo);

  /* spgscan.c */
  extern IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index f9bcdd6..42c6c58 100644
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 52,57 ****
--- 52,59 ----
   *        ReadyForInserts        is it valid for inserts?
   *        Concurrent            are we doing a concurrent index build?
   *        BrokenHotChain        did we detect any broken HOT chains?
+  *        AmCache                private cache area for index AM
+  *        Context                memory context holding this IndexInfo
   *
   * ii_Concurrent and ii_BrokenHotChain are used only during index build;
   * they're conventionally set to false otherwise.
*************** typedef struct IndexInfo
*** 76,81 ****
--- 78,85 ----
      bool        ii_ReadyForInserts;
      bool        ii_Concurrent;
      bool        ii_BrokenHotChain;
+     void       *ii_AmCache;
+     MemoryContext ii_Context;
  } IndexInfo;

  /* ----------------

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

pgsql-hackers by date:

Previous
From: "Jonathan S. Katz"
Date:
Subject: Re: [HACKERS] Press Release Draft - 2016-02-09 Cumulative Update
Next
From: Alvaro Herrera
Date:
Subject: Re: [HACKERS] Press Release Draft - 2016-02-09 Cumulative Update