Ok. Here's a `gmake check` passing version that actually makes use of the new
AM entries in ExecInsert. Currently, AFAICS, this *only* provides an improvement
for systems that frequently insert duplicate rows. Perhaps not a common case,
but an improvement nonetheless, IMO.

I leaned in this direction(insert scans) for the handling of unique violations
as it seemed like a better idea to check ahead of time than to recover after
the fact. Being able to do checks ahead of time simply allows one to eliminate
the need for unwinding code. (Simply, think about not needing to heap_delete in
error log cases. [Well, potentially ;])

The problem with the AM no longer having the ability to make guarantees about
lock/state duration seems more or less unavoidable with this two-step process.
I'm not sure that I can actually resolve any concerns involved with the
transference of responsibility to the user of the AM.


Currently, in ExecInsert(), I not-so-safely assume that a index implements
scanforinsert if it's unique. This appears to be safe as far as all the
stock AMs are concerned. However, it will likely throw an exception for indexes
that are said to be unique whose AM doesn't implement scanforinsert
(thinking of external AMs?). I imagine this is something that I should fix, yes?

Besides that I think it's ready for review.
-- 
Regards, James William Pye
Index: src/backend/access/index/genam.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/index/genam.c,v
retrieving revision 1.53
diff -c -r1.53 genam.c
*** src/backend/access/index/genam.c    5 Jan 2006 10:07:44 -0000       1.53
--- src/backend/access/index/genam.c    2 Mar 2006 15:28:31 -0000
***************
*** 136,141 ****
--- 136,174 ----
        pfree(scan);
  }
  
+ /* ----------------
+  *    RelationGetIndexScanForInsert -- Create and fill an IndexInsertScanDesc.
+  *
+  *            Parameters:
+  *                            indexRelation -- index relation for scan.
+  *
+  *            Returns:
+  *                            An initialized IndexInsertScanDesc.
+  * ----------------
+  */
+ IndexInsertScanDesc
+ RelationGetIndexInsertScan(Relation indexRelation)
+ {
+       IndexInsertScanDesc scan;
+ 
+       scan = (IndexInsertScanDesc) palloc0(sizeof(IndexInsertScanDescData));
+       scan->indexRelation = indexRelation;
+       scan->heapRelation = NULL;
+       scan->opaque = NULL;
+       ItemPointerSetInvalid(&scan->heapItem);
+ 
+       return scan;
+ }
+ 
+ /* ----------------
+  *    IndexScanForInsertEnd -- End an index insertion scan.
+  * ----------------
+  */
+ void
+ IndexInsertScanEnd(IndexInsertScanDesc scan)
+ {
+       pfree(scan);
+ }
  
  /* ----------------------------------------------------------------
   *            heap-or-index-scan access to system catalogs
Index: src/backend/access/index/indexam.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/index/indexam.c,v
retrieving revision 1.90
diff -c -r1.90 indexam.c
*** src/backend/access/index/indexam.c  11 Feb 2006 23:31:33 -0000      1.90
--- src/backend/access/index/indexam.c  2 Mar 2006 15:28:31 -0000
***************
*** 222,227 ****
--- 222,265 ----
                                                                          
BoolGetDatum(check_uniqueness)));
  }
  
+ /* ----------------
+  *            index_scanforinsert - scan/prepare an index for later insertion
+  * ----------------
+  */
+ IndexInsertScanDesc
+ index_scanforinsert(Relation indexRelation,
+                        Datum *values,
+                        bool *isnull,
+                        Relation heapRelation)
+ {
+       FmgrInfo   *procedure;
+ 
+       GET_REL_PROCEDURE(amscanforinsert);
+ 
+       return (IndexInsertScanDesc) DatumGetPointer(FunctionCall4(procedure,
+                                                                         
PointerGetDatum(indexRelation),
+                                                                         
PointerGetDatum(values),
+                                                                         
PointerGetDatum(isnull),
+                                                                         
PointerGetDatum(heapRelation)));
+ }
+ 
+ /* ----------------
+  *            index_insertfromscan - insert tuple from the initiated scan
+  * ----------------
+  */
+ void
+ index_insertfromscan(IndexInsertScanDesc scan, ItemPointer heap_t_ctid)
+ {
+       FmgrInfo   *procedure;
+       Relation indexRelation = scan->indexRelation;
+ 
+       GET_REL_PROCEDURE(aminsertfromscan);
+ 
+       FunctionCall2(procedure,
+                                 PointerGetDatum(scan),
+                                 PointerGetDatum(heap_t_ctid));
+ }
+ 
  /*
   * index_beginscan - start a scan of an index with amgettuple
   *
Index: src/backend/access/nbtree/nbtinsert.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v
retrieving revision 1.132
diff -c -r1.132 nbtinsert.c
*** src/backend/access/nbtree/nbtinsert.c       25 Jan 2006 23:04:20 -0000      
1.132
--- src/backend/access/nbtree/nbtinsert.c       2 Mar 2006 15:28:31 -0000
***************
*** 38,46 ****
  
  static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf);
  
! static TransactionId _bt_check_unique(Relation rel, IndexTuple itup,
                                 Relation heapRel, Buffer buf,
!                                ScanKey itup_scankey);
  static void _bt_insertonpg(Relation rel, Buffer buf,
                           BTStack stack,
                           int keysz, ScanKey scankey,
--- 38,47 ----
  
  static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf);
  
! static TransactionId _bt_evaluate(Relation rel, IndexTuple itup,
                                 Relation heapRel, Buffer buf,
!                                ScanKey itup_scankey, ItemPointer iptr);
! 
  static void _bt_insertonpg(Relation rel, Buffer buf,
                           BTStack stack,
                           int keysz, ScanKey scankey,
***************
*** 72,79 ****
   */
  void
  _bt_doinsert(Relation rel, IndexTuple itup,
!                        bool index_is_unique, Relation heapRel)
  {
        int                     natts = rel->rd_rel->relnatts;
        ScanKey         itup_scankey;
        BTStack         stack;
--- 73,81 ----
   */
  void
  _bt_doinsert(Relation rel, IndexTuple itup,
!                 bool index_is_unique, Relation heapRel)
  {
+       TransactionId xwait = InvalidTransactionId;
        int                     natts = rel->rd_rel->relnatts;
        ScanKey         itup_scankey;
        BTStack         stack;
***************
*** 100,109 ****
        buf = _bt_moveright(rel, buf, natts, itup_scankey, false, BT_WRITE);
  
        /*
!        * If we're not allowing duplicates, make sure the key isn't already in
!        * the index.
         *
!        * NOTE: obviously, _bt_check_unique can only detect keys that are 
already
         * in the index; so it cannot defend against concurrent insertions of 
the
         * same key.  We protect against that by means of holding a write lock 
on
         * the target page.  Any other would-be inserter of the same key must
--- 102,110 ----
        buf = _bt_moveright(rel, buf, natts, itup_scankey, false, BT_WRITE);
  
        /*
!        * Evaluate that the key is or isn't already in the index.
         *
!        * NOTE: obviously, _bt_evaluate can only detect keys that are already
         * in the index; so it cannot defend against concurrent insertions of 
the
         * same key.  We protect against that by means of holding a write lock 
on
         * the target page.  Any other would-be inserter of the same key must
***************
*** 118,126 ****
         */
        if (index_is_unique)
        {
!               TransactionId xwait;
  
!               xwait = _bt_check_unique(rel, itup, heapRel, buf, itup_scankey);
  
                if (TransactionIdIsValid(xwait))
                {
--- 119,135 ----
         */
        if (index_is_unique)
        {
!               xwait = _bt_evaluate(rel, itup, heapRel, buf, itup_scankey, 
NULL);
  
!               /* Tuple exists; insert violates uniqueness; raise error. */
!               if (xwait == FrozenTransactionId)
!               {
!                       _bt_wrtbuf(rel, buf);
!                       _bt_freestack(stack);
!                       ereport(ERROR, (errcode(ERRCODE_UNIQUE_VIOLATION),
!                                               errmsg("duplicate key violates 
unique constraint \"%s\"",
!                                                       
RelationGetRelationName(rel))));
!               }
  
                if (TransactionIdIsValid(xwait))
                {
***************
*** 141,156 ****
        _bt_freeskey(itup_scankey);
  }
  
  /*
!  *    _bt_check_unique() -- Check for violation of unique index constraint
   *
!  * Returns InvalidTransactionId if there is no conflict, else an xact ID
!  * we must wait for to see if it commits a conflicting tuple. If an actual
!  * conflict is detected, no return --- just ereport().
   */
  static TransactionId
! _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
!                                Buffer buf, ScanKey itup_scankey)
  {
        TupleDesc       itupdesc = RelationGetDescr(rel);
        int                     natts = rel->rd_rel->relnatts;
--- 150,242 ----
        _bt_freeskey(itup_scankey);
  }
  
+ /*----------
+  *    _bt_doscanforinsert() -- Insert a tuple on a particular page in the 
index.
+  *----------
+  */
+ void
+ _bt_doscanforinsert(IndexInsertScanDesc scan, IndexTuple itup)
+ {
+       Relation                rel = scan->indexRelation;
+       int                     natts = rel->rd_rel->relnatts;
+       BTInsertScanOpaque opaque;
+       ScanKey         itup_scankey;
+       BTStack         stack;
+       Buffer          buf;
+       TransactionId xwait;
+ 
+       /* we need an insertion scan key to do our search, so build one */
+       opaque = (BTInsertScanOpaque) IndexInsertScanGetOpaque(scan);
+       opaque->indexTuple = itup;
+       opaque->keyData = itup_scankey = _bt_mkscankey(rel, itup);
+ 
+ top:
+       /* find the first page containing this key */
+       stack = _bt_search(rel, natts, itup_scankey, false, &buf, BT_WRITE);
+ 
+       /* trade in our read lock for a write lock */
+       LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+       LockBuffer(buf, BT_WRITE);
+ 
+       /*
+        * If the page was split between the time that we surrendered our read
+        * lock and acquired our write lock, then this page may no longer be the
+        * right place for the key we want to insert.  In this case, we need to
+        * move right in the tree.      See Lehman and Yao for an excruciatingly
+        * precise description.
+        */
+       buf = _bt_moveright(rel, buf, natts, itup_scankey, false, BT_WRITE);
+ 
+       /*
+        * Evaluate that the key is or isn't already in the index.
+        *
+        * NOTE: obviously, _bt_evaluate can only detect keys that are already
+        * in the index; so it cannot defend against concurrent insertions of 
the
+        * same key.  We protect against that by means of holding a write lock 
on
+        * the target page.  Any other would-be inserter of the same key must
+        * acquire a write lock on the same target page, so only one would-be
+        * inserter can be making the check at one time.  Furthermore, once we 
are
+        * past the check we hold write locks continuously until we have 
performed
+        * our insertion, so no later inserter can fail to see our insertion.
+        * (This requires some care in _bt_insertonpg.)
+        *
+        * If we must wait for another xact, we release the lock while waiting,
+        * and then must start over completely.
+        */
+       xwait = _bt_evaluate(scan->indexRelation, itup,
+                                               scan->heapRelation, buf, 
itup_scankey,
+                                               &scan->heapItem);
+ 
+       /* Conclusive match made?  */
+       if (xwait != FrozenTransactionId && TransactionIdIsValid(xwait))
+       {
+               /* Maybe. Have to wait for the other guy ... */
+               _bt_relbuf(rel, buf);
+               XactLockTableWait(xwait);
+               /* start over... */
+               _bt_freestack(stack);
+               goto top;
+       }
+ 
+       opaque->stack = stack;
+       opaque->lockedBuffer = buf;
+       /*
+        * The insert scan now has the necessary locks in the appropriate 
location
+        * for a later insertfromscan by the holder of the IndexInsertScanDesc.
+        */
+ }
+ 
  /*
!  *    _bt_evalate() -- evaluate the existence of the corresponding key
   *
!  * Returns InvalidTransactionId if there is no existing tuple at that key,
!  * FrozenTransactionId if there an existing live tuple at that key, and
!  * a regular, running transaction id if we must wait for to see if it commits
!  * a conflicting tuple.
   */
  static TransactionId
! _bt_evaluate(Relation rel, IndexTuple itup, Relation heapRel,
!                                Buffer buf, ScanKey itup_scankey, ItemPointer 
iptr)
  {
        TupleDesc       itupdesc = RelationGetDescr(rel);
        int                     natts = rel->rd_rel->relnatts;
***************
*** 235,251 ****
                                        {
                                                if (nbuf != InvalidBuffer)
                                                        _bt_relbuf(rel, nbuf);
!                                               /* Tell _bt_doinsert to wait... 
*/
                                                return xwait;
                                        }
  
!                                       /*
!                                        * Otherwise we have a definite 
conflict.
!                                        */
!                                       ereport(ERROR,
!                                                       
(errcode(ERRCODE_UNIQUE_VIOLATION),
!                                       errmsg("duplicate key violates unique 
constraint \"%s\"",
!                                                  
RelationGetRelationName(rel))));
                                }
                                else if (htup.t_data != NULL)
                                {
--- 321,334 ----
                                        {
                                                if (nbuf != InvalidBuffer)
                                                        _bt_relbuf(rel, nbuf);
!                                               /* Tell caller to wait... */
                                                return xwait;
                                        }
  
!                                       /* Signal caller that it's at a live 
tuple */
!                                       if (iptr != NULL)
!                                               ItemPointerCopy(&htup.t_self, 
iptr);
!                                       return FrozenTransactionId;
                                }
                                else if (htup.t_data != NULL)
                                {
***************
*** 308,313 ****
--- 391,440 ----
  }
  
  /*----------
+  *    _bt_doinsertfromscan() -- Insert a tuple on the found page in the index
+  *----------
+  */
+ void
+ _bt_doinsertfromscan(IndexInsertScanDesc scan, ItemPointer iptr)
+ {
+       Relation rel = scan->indexRelation;
+       BTInsertScanOpaque opaque;
+       opaque = (BTInsertScanOpaque) IndexInsertScanGetOpaque(scan);
+ 
+       /* Do the insertion iff given a valid ItemPointer */
+       if (ItemPointerIsValid(iptr))
+       {
+               ItemPointerCopy(iptr, &(opaque->indexTuple->t_tid));
+ 
+               _bt_insertonpg(scan->indexRelation, opaque->lockedBuffer, 
opaque->stack,
+                               rel->rd_rel->relnatts, opaque->keyData,
+                               opaque->indexTuple, 0, false);
+               /*
+                * _bt_insertonpg will write and release the lockedBuffer.
+                */
+       }
+       else
+       {
+               /*
+                * User decided not to insert despite the valient efforts.
+                */
+               _bt_relbuf(rel, opaque->lockedBuffer);
+       }
+ 
+       opaque->lockedBuffer = InvalidBuffer;
+       pfree(opaque->indexTuple);
+       opaque->indexTuple = NULL;
+ 
+       _bt_freeskey(opaque->keyData);
+       opaque->keyData = NULL;
+       _bt_freestack(opaque->stack);
+       opaque->stack = NULL;
+ 
+       IndexInsertScanSetOpaque(scan, NULL);
+       pfree(opaque);
+ }
+ 
+ /*----------
   *    _bt_insertonpg() -- Insert a tuple on a particular page in the index.
   *
   *            This recursive procedure does the following things:
***************
*** 430,436 ****
                         * step right to next non-dead page
                         *
                         * must write-lock that page before releasing write 
lock on
!                        * current page; else someone else's _bt_check_unique 
scan could
                         * fail to see our insertion.  write locks on 
intermediate dead
                         * pages won't do because we don't know when they will 
get
                         * de-linked from the tree.
--- 557,563 ----
                         * step right to next non-dead page
                         *
                         * must write-lock that page before releasing write 
lock on
!                        * current page; else someone else's _bt_evaluate scan 
could
                         * fail to see our insertion.  write locks on 
intermediate dead
                         * pages won't do because we don't know when they will 
get
                         * de-linked from the tree.
Index: src/backend/access/nbtree/nbtree.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v
retrieving revision 1.141
diff -c -r1.141 nbtree.c
*** src/backend/access/nbtree/nbtree.c  14 Feb 2006 17:20:01 -0000      1.141
--- src/backend/access/nbtree/nbtree.c  2 Mar 2006 15:28:32 -0000
***************
*** 227,232 ****
--- 227,274 ----
  }
  
  /*
+  * btscanforinsert() -- scan for later insertion
+  *
+  *    Descend the tree recursively, find the appropriate location for our
+  *    new tuple, and return the btree state of this scan for later insertion.
+  */
+ Datum
+ btscanforinsert(PG_FUNCTION_ARGS)
+ {
+       Relation        rel = (Relation) PG_GETARG_POINTER(0);
+       Datum *values = (Datum *) PG_GETARG_POINTER(1);
+       bool *isnull = (bool *) PG_GETARG_POINTER(2);
+       Relation        heapRel = (Relation) PG_GETARG_POINTER(3);
+       IndexInsertScanDesc scan = NULL;
+       IndexTuple itup;
+ 
+       scan = RelationGetIndexInsertScan(rel);
+       scan->heapRelation = heapRel;
+ 
+       /* generate an index tuple */
+       itup = index_form_tuple(RelationGetDescr(rel), values, isnull);
+       IndexInsertScanSetOpaque(scan, palloc(sizeof(BTInsertScanOpaqueData)));
+       _bt_doscanforinsert(scan, itup);
+ 
+       PG_RETURN_POINTER(scan);
+ }
+ 
+ /*
+  * btinsertfromscan() -- insert the item into the position of the scan
+  */
+ Datum
+ btinsertfromscan(PG_FUNCTION_ARGS)
+ {
+       IndexInsertScanDesc scan = (IndexInsertScanDesc) PG_GETARG_POINTER(0);
+       ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(1);
+ 
+       _bt_doinsertfromscan(scan, ht_ctid);
+       IndexInsertScanSetOpaque(scan, NULL);
+ 
+       PG_RETURN_VOID();
+ }
+ 
+ /*
   *    btgettuple() -- Get the next tuple in the scan.
   */
  Datum
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.267
diff -c -r1.267 execMain.c
*** src/backend/executor/execMain.c     21 Feb 2006 23:01:54 -0000      1.267
--- src/backend/executor/execMain.c     2 Mar 2006 15:28:32 -0000
***************
*** 33,41 ****
--- 33,43 ----
  #include "postgres.h"
  
  #include "access/heapam.h"
+ #include "access/genam.h"
  #include "access/xlog.h"
  #include "catalog/heap.h"
  #include "catalog/namespace.h"
+ #include "catalog/index.h"
  #include "commands/tablecmds.h"
  #include "commands/tablespace.h"
  #include "commands/trigger.h"
***************
*** 1399,1404 ****
--- 1401,1412 ----
        (estate->es_processed)++;
  }
  
+ static bool
+ select_nonunique(Relation reld)
+ {
+       return ((bool) !(reld->rd_index->indisunique));
+ }
+ 
  /* ----------------------------------------------------------------
   *            ExecInsert
   *
***************
*** 1416,1421 ****
--- 1424,1431 ----
        ResultRelInfo *resultRelInfo;
        Relation        resultRelationDesc;
        Oid                     newId;
+       unsigned int    insertScans = 0;
+       IndexInsertScanDesc *insertScanDescs = NULL;
  
        /*
         * get the heap tuple out of the tuple table slot, making sure we have a
***************
*** 1467,1472 ****
--- 1477,1561 ----
                ExecConstraints(resultRelInfo, slot, estate);
  
        /*
+        * Create index insert scans for unique constraint violations.
+        */
+       if (resultRelInfo->ri_NumIndices > 0)
+       {
+               ExprContext *econtext;
+               RelationPtr relationDescs = 
resultRelInfo->ri_IndexRelationDescs;
+               IndexInfo **indexInfoArray = 
resultRelInfo->ri_IndexRelationInfo;
+               unsigned int i, currscan = 0;
+ 
+               /*
+                * We will use the EState's per-tuple context for evaluating 
predicates
+                * and index expressions (creating it if it's not already 
there).
+                */
+               econtext = GetPerTupleExprContext(estate);
+ 
+               /* Arrange for econtext's scan tuple to be the tuple under test 
*/
+               econtext->ecxt_scantuple = slot;
+ 
+               for (i = 0; i < resultRelInfo->ri_NumIndices; ++i)
+                       if (relationDescs[i]->rd_index->indisunique)
+                               ++insertScans;
+ 
+               insertScanDescs = palloc(sizeof(insertScanDescs) * insertScans);
+               for (i = 0; i < resultRelInfo->ri_NumIndices; ++i)
+               {
+                       Relation indexRelation = relationDescs[i];
+ 
+                       if (indexRelation->rd_index->indisunique)
+                       {
+                               IndexInfo *indexInfo = indexInfoArray[i];
+                               Datum           values[INDEX_MAX_KEYS] = {0,};
+                               bool            isnull[INDEX_MAX_KEYS] = 
{false,};
+                               IndexInsertScanDesc scan;
+ 
+                               if (indexInfo->ii_Predicate != NIL)
+                               {
+                                       if 
(!ExecSatisfiesIndexPredicate(estate, econtext, indexInfo))
+                                       {
+                                               insertScanDescs[i] = NULL;
+                                               continue;
+                                       }
+                               }
+ 
+                               FormIndexDatum(indexInfo,
+                                                               slot,
+                                                               estate,
+                                                               values,
+                                                               isnull);
+ 
+                               scan = index_scanforinsert(indexRelation,
+                                                               values, isnull, 
resultRelationDesc);
+                               insertScanDescs[currscan] = scan;
+                               ++currscan;
+ 
+                               /*
+                                * Key Violation?
+                                */
+                               if (ItemPointerIsValid(&scan->heapItem))
+                               {
+                                       /*
+                                        * Yep. Be tidy and clean up any 
current scans.
+                                        */
+                                       do
+                                       {
+                                               --currscan;
+                                               
index_insertfromscan(insertScanDescs[currscan], NULL);
+                                               insertScanDescs[currscan] = 
NULL;
+                                       }
+                                       while (currscan > 0);
+ 
+                                       ereport(ERROR, 
(errcode(ERRCODE_UNIQUE_VIOLATION),
+                                                       errmsg("duplicate key 
violates unique constraint \"%s\"",
+                                                               
RelationGetRelationName(indexRelation))));
+                               }
+                       }
+               }
+       }
+ 
+       /*
         * insert the tuple
         *
         * Note: heap_insert returns the tid (location) of the new tuple in the
***************
*** 1476,1481 ****
--- 1565,1590 ----
                                                estate->es_snapshot->curcid,
                                                true, true);
  
+       /*
+        * Complete the insert scans with the new item pointer.
+        */
+       if (insertScans > 0)
+       {
+               unsigned int i;
+ 
+               for (i = 0; i < insertScans; ++i)
+               {
+                       if (insertScanDescs[i] != NULL)
+                       {
+                               index_insertfromscan(insertScanDescs[i], 
&(tuple->t_self));
+                               IncrIndexInserted();
+                               insertScanDescs[i] = NULL;
+                       }
+               }
+ 
+               pfree(insertScanDescs);
+       }
+ 
        IncrAppended();
        (estate->es_processed)++;
        estate->es_lastoid = newId;
***************
*** 1485,1491 ****
         * insert index entries for tuple
         */
        if (resultRelInfo->ri_NumIndices > 0)
!               ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
  
        /* AFTER ROW INSERT Triggers */
        ExecARInsertTriggers(estate, resultRelInfo, tuple);
--- 1594,1603 ----
         * insert index entries for tuple
         */
        if (resultRelInfo->ri_NumIndices > 0)
!       {
!               ExecInsertSelectIndexTuples(slot, &(tuple->t_self),
!                                         estate, false, select_nonunique);
!       }
  
        /* AFTER ROW INSERT Triggers */
        ExecARInsertTriggers(estate, resultRelInfo, tuple);
Index: src/backend/executor/execUtils.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/execUtils.c,v
retrieving revision 1.132
diff -c -r1.132 execUtils.c
*** src/backend/executor/execUtils.c    14 Jan 2006 22:03:35 -0000      1.132
--- src/backend/executor/execUtils.c    2 Mar 2006 15:28:32 -0000
***************
*** 931,953 ****
         */
  }
  
  /* ----------------------------------------------------------------
!  *            ExecInsertIndexTuples
   *
!  *            This routine takes care of inserting index tuples
!  *            into all the relations indexing the result relation
!  *            when a heap tuple is inserted into the result relation.
!  *            Much of this code should be moved into the genam
!  *            stuff as it only exists here because the genam stuff
!  *            doesn't provide the functionality needed by the
!  *            executor.. -cim 9/27/89
   * ----------------------------------------------------------------
   */
  void
! ExecInsertIndexTuples(TupleTableSlot *slot,
                                          ItemPointer tupleid,
                                          EState *estate,
!                                         bool is_vacuum)
  {
        ResultRelInfo *resultRelInfo;
        int                     i;
--- 931,971 ----
         */
  }
  
+ bool
+ ExecSatisfiesIndexPredicate(EState *estate,
+               ExprContext *econtext, IndexInfo *indexInfo)
+ {
+       List       *predicate;
+       /*
+        * If predicate state not set up yet, create it (in the estate's
+        * per-query context)
+        */
+       predicate = indexInfo->ii_PredicateState;
+       if (predicate == NIL)
+       {
+               predicate = (List *)
+                       ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
+                                                       estate);
+               indexInfo->ii_PredicateState = predicate;
+       }
+ 
+       return ExecQual(predicate, econtext, false);
+ }
+ 
  /* ----------------------------------------------------------------
!  *            ExecInsertSelectIndexTuples
   *
!  * Insert an index tuple into select indexes; where select indexes
!  * are those granted using the selected_index function given.
!  * selected_index == NULL for all indexes.
   * ----------------------------------------------------------------
   */
  void
! ExecInsertSelectIndexTuples(TupleTableSlot *slot,
                                          ItemPointer tupleid,
                                          EState *estate,
!                                         bool is_vacuum,
!                                         bool (*selected_index)(Relation rel))
  {
        ResultRelInfo *resultRelInfo;
        int                     i;
***************
*** 984,1016 ****
        {
                IndexInfo  *indexInfo;
  
!               if (relationDescs[i] == NULL)
                        continue;
  
                indexInfo = indexInfoArray[i];
  
                /* Check for partial index */
!               if (indexInfo->ii_Predicate != NIL)
!               {
!                       List       *predicate;
! 
!                       /*
!                        * If predicate state not set up yet, create it (in the 
estate's
!                        * per-query context)
!                        */
!                       predicate = indexInfo->ii_PredicateState;
!                       if (predicate == NIL)
!                       {
!                               predicate = (List *)
!                                       ExecPrepareExpr((Expr *) 
indexInfo->ii_Predicate,
!                                                                       estate);
!                               indexInfo->ii_PredicateState = predicate;
!                       }
! 
!                       /* Skip this index-update if the predicate isn't 
satisfied */
!                       if (!ExecQual(predicate, econtext, false))
!                               continue;
!               }
  
                /*
                 * FormIndexDatum fills in its values and isnull parameters 
with the
--- 1002,1017 ----
        {
                IndexInfo  *indexInfo;
  
!               if (relationDescs[i] == NULL ||
!                               (selected_index != NULL && 
!selected_index(relationDescs[i])))
                        continue;
  
                indexInfo = indexInfoArray[i];
  
                /* Check for partial index */
!               if (indexInfo->ii_Predicate != NIL &&
!                               !ExecSatisfiesIndexPredicate(estate, econtext, 
indexInfo))
!                       continue;
  
                /*
                 * FormIndexDatum fills in its values and isnull parameters 
with the
***************
*** 1041,1046 ****
--- 1042,1068 ----
        }
  }
  
+ /* ----------------------------------------------------------------
+  *            ExecInsertIndexTuples
+  *
+  *            This routine takes care of inserting index tuples
+  *            into all the relations indexing the result relation
+  *            when a heap tuple is inserted into the result relation.
+  *            Much of this code should be moved into the genam
+  *            stuff as it only exists here because the genam stuff
+  *            doesn't provide the functionality needed by the
+  *            executor.. -cim 9/27/89
+  * ----------------------------------------------------------------
+  */
+ void
+ ExecInsertIndexTuples(TupleTableSlot *slot,
+                                         ItemPointer tupleid,
+                                         EState *estate,
+                                         bool is_vacuum)
+ {
+       ExecInsertSelectIndexTuples(slot, tupleid, estate, is_vacuum, NULL);
+ }
+ 
  /*
   * UpdateChangedParamSet
   *            Add changed parameters to a plan node's chgParam set
Index: src/include/access/genam.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/access/genam.h,v
retrieving revision 1.57
diff -c -r1.57 genam.h
*** src/include/access/genam.h  11 Feb 2006 23:31:34 -0000      1.57
--- src/include/access/genam.h  2 Mar 2006 15:28:33 -0000
***************
*** 73,88 ****
  extern Relation index_openrv(const RangeVar *relation);
  extern void index_close(Relation relation);
  extern bool index_insert(Relation indexRelation,
                         Datum *values, bool *isnull,
!                        ItemPointer heap_t_ctid,
!                        Relation heapRelation,
!                        bool check_uniqueness);
  
  extern IndexScanDesc index_beginscan(Relation heapRelation,
!                               Relation indexRelation,
!                               bool need_index_lock,
!                               Snapshot snapshot,
!                               int nkeys, ScanKey key);
  extern IndexScanDesc index_beginscan_multi(Relation indexRelation,
                                          bool need_index_lock,
                                          Snapshot snapshot,
--- 73,95 ----
  extern Relation index_openrv(const RangeVar *relation);
  extern void index_close(Relation relation);
  extern bool index_insert(Relation indexRelation,
+                       Datum *values, bool *isnull,
+                       ItemPointer heap_t_ctid,
+                       Relation heapRelation,
+                       bool check_uniqueness);
+ 
+ extern IndexInsertScanDesc index_scanforinsert(Relation indexRelation,
                         Datum *values, bool *isnull,
!                        Relation heapRelation);
! extern void index_insertfromscan(IndexInsertScanDesc scanSource,
!                        ItemPointer heap_t_ctid);
! 
  
  extern IndexScanDesc index_beginscan(Relation heapRelation,
!                                 Relation indexRelation,
!                                 bool need_index_lock,
!                                 Snapshot snapshot,
!                                 int nkeys, ScanKey key);
  extern IndexScanDesc index_beginscan_multi(Relation indexRelation,
                                          bool need_index_lock,
                                          Snapshot snapshot,
***************
*** 116,121 ****
--- 123,131 ----
                                         int nkeys, ScanKey key);
  extern void IndexScanEnd(IndexScanDesc scan);
  
+ extern IndexInsertScanDesc RelationGetIndexInsertScan(Relation indexRelation);
+ extern void IndexInsertScanEnd(IndexInsertScanDesc scan);
+ 
  /*
   * heap-or-index access to system catalogs (in genam.c)
   */
Index: src/include/access/nbtree.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/access/nbtree.h,v
retrieving revision 1.91
diff -c -r1.91 nbtree.h
*** src/include/access/nbtree.h 25 Jan 2006 23:04:21 -0000      1.91
--- src/include/access/nbtree.h 2 Mar 2006 15:28:34 -0000
***************
*** 377,382 ****
--- 377,397 ----
  typedef BTScanOpaqueData *BTScanOpaque;
  
  /*
+  * BTInsertScanOpaqueData is used to remember position and the buffers
+  * that are locked down for a potential insertion.
+  */
+ 
+ typedef struct BTInsertScanOpaqueData
+ {
+       Buffer           lockedBuffer;
+       BTStack          stack;
+       IndexTuple       indexTuple;
+       ScanKey          keyData;               /* array of preprocessed scan 
keys */
+ } BTInsertScanOpaqueData;
+ 
+ typedef BTInsertScanOpaqueData *BTInsertScanOpaque;
+ 
+ /*
   * We use these private sk_flags bits in preprocessed scan keys
   */
  #define SK_BT_REQFWD  0x00010000      /* required to continue forward scan */
***************
*** 397,411 ****
  extern Datum btrestrpos(PG_FUNCTION_ARGS);
  extern Datum btbulkdelete(PG_FUNCTION_ARGS);
  extern Datum btvacuumcleanup(PG_FUNCTION_ARGS);
  
  /*
   * prototypes for functions in nbtinsert.c
   */
  extern void _bt_doinsert(Relation rel, IndexTuple itup,
!                        bool index_is_unique, Relation heapRel);
  extern Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access);
  extern void _bt_insert_parent(Relation rel, Buffer buf, Buffer rbuf,
                                  BTStack stack, bool is_root, bool is_only);
  
  /*
   * prototypes for functions in nbtpage.c
--- 412,430 ----
  extern Datum btrestrpos(PG_FUNCTION_ARGS);
  extern Datum btbulkdelete(PG_FUNCTION_ARGS);
  extern Datum btvacuumcleanup(PG_FUNCTION_ARGS);
+ extern Datum btscanforinsert(PG_FUNCTION_ARGS);
+ extern Datum btinsertfromscan(PG_FUNCTION_ARGS);
  
  /*
   * prototypes for functions in nbtinsert.c
   */
  extern void _bt_doinsert(Relation rel, IndexTuple itup,
!                 bool index_is_unique, Relation heapRel);
  extern Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access);
  extern void _bt_insert_parent(Relation rel, Buffer buf, Buffer rbuf,
                                  BTStack stack, bool is_root, bool is_only);
+ extern void _bt_doscanforinsert(IndexInsertScanDesc scan, IndexTuple itup);
+ extern void _bt_doinsertfromscan(IndexInsertScanDesc scan, ItemPointer iptr);
  
  /*
   * prototypes for functions in nbtpage.c
Index: src/include/access/relscan.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/access/relscan.h,v
retrieving revision 1.43
diff -c -r1.43 relscan.h
*** src/include/access/relscan.h        3 Dec 2005 05:51:03 -0000       1.43
--- src/include/access/relscan.h        2 Mar 2006 15:28:34 -0000
***************
*** 100,105 ****
--- 100,131 ----
  typedef IndexScanDescData *IndexScanDesc;
  
  
+ typedef struct IndexInsertScanDescData
+ {
+       Relation        heapRelation;   /* heap relation descriptor */
+       Relation        indexRelation;  /* index relation descriptor */
+ 
+       /* InvalidItemPointer iff none is found by scan */
+       ItemPointerData heapItem;       /* heap pointer */
+ 
+       /* scan state */
+       void       *opaque;                     /* access-method-specific info 
*/
+ } IndexInsertScanDescData;
+ 
+ typedef IndexInsertScanDescData *IndexInsertScanDesc;
+ 
+ /*
+  * IndexInsertScanGetOpaque
+  *    Get the opaque structure out of a IndexInsertScanDesc.
+  */
+ #define IndexInsertScanGetOpaque(S) (S->opaque)
+ 
+ /*
+  * IndexInsertScanGetOpaque
+  *    Set the opaque structure in a IndexInsertScanDesc.
+  */
+ #define IndexInsertScanSetOpaque(S, O) (S->opaque = O)
+ 
  /*
   * HeapScanIsValid
   *            True iff the heap scan is valid.
***************
*** 112,115 ****
--- 138,147 ----
   */
  #define IndexScanIsValid(scan) PointerIsValid(scan)
  
+ /*
+  * IndexInsertScanIsValid
+  *            True iff the index insert scan is valid.
+  */
+ #define IndexInsertScanIsValid(scan) PointerIsValid(scan)
+ 
  #endif   /* RELSCAN_H */
Index: src/include/catalog/pg_am.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_am.h,v
retrieving revision 1.39
diff -c -r1.39 pg_am.h
*** src/include/catalog/pg_am.h 7 Nov 2005 17:36:46 -0000       1.39
--- src/include/catalog/pg_am.h 2 Mar 2006 15:28:34 -0000
***************
*** 63,68 ****
--- 63,70 ----
        regproc         ambulkdelete;   /* bulk-delete function */
        regproc         amvacuumcleanup;        /* post-VACUUM cleanup function 
*/
        regproc         amcostestimate; /* estimate cost of an indexscan */
+       regproc         amscanforinsert; /* run a scan for a tuple keeping 
state */
+       regproc         aminsertfromscan; /* insert tuple from prior scan */
  } FormData_pg_am;
  
  /* ----------------
***************
*** 76,82 ****
   *            compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am                                           21
  #define Anum_pg_am_amname                             1
  #define Anum_pg_am_amstrategies                       2
  #define Anum_pg_am_amsupport                  3
--- 78,84 ----
   *            compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am                                           23
  #define Anum_pg_am_amname                             1
  #define Anum_pg_am_amstrategies                       2
  #define Anum_pg_am_amsupport                  3
***************
*** 98,116 ****
  #define Anum_pg_am_ambulkdelete                       19
  #define Anum_pg_am_amvacuumcleanup            20
  #define Anum_pg_am_amcostestimate             21
  
  /* ----------------
   *            initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree        5 1 1 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 hashinsert hashbeginscan 
hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos 
hashbuild hashbulkdelete - hashcostestimate ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist 100 7 0 f t f f t gistinsert gistbeginscan 
gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos 
gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
  
--- 100,120 ----
  #define Anum_pg_am_ambulkdelete                       19
  #define Anum_pg_am_amvacuumcleanup            20
  #define Anum_pg_am_amcostestimate             21
+ #define Anum_pg_am_amscanforinsert            22
+ #define Anum_pg_am_aminsertfromscan           23
  
  /* ----------------
   *            initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree        5 1 1 t t t t t btinsert btbeginscan 
btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild 
btbulkdelete btvacuumcleanup btcostestimate btscanforinsert btinsertfromscan ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash 1 1 0 f f f f t hashinsert hashbeginscan 
hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos 
hashbuild hashbulkdelete - hashcostestimate - - ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist 100 7 0 f t f f t gistinsert gistbeginscan 
gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos 
gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate - - ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
  
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.398
diff -c -r1.398 pg_proc.h
*** src/include/catalog/pg_proc.h       26 Feb 2006 18:36:21 -0000      1.398
--- src/include/catalog/pg_proc.h       2 Mar 2006 15:28:34 -0000
***************
*** 680,685 ****
--- 680,690 ----
  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)");
+ DATA(insert OID = 2596 (  btscanforinsert                PGNSP PGUID 12 f f t 
f v 3 2281 "2281 2281 2281" _null_ _null_ _null_        btscanforinsert - 
_null_ ));
+ DESCR("btree(internal)");
+ DATA(insert OID = 2597 (  btinsertfromscan               PGNSP PGUID 12 f f t 
f v 1 16 "2281" _null_ _null_ _null_    btinsertfromscan - _null_ ));
+ DESCR("btree(internal)");
+ 
  
  DATA(insert OID = 339 (  poly_same               PGNSP PGUID 12 f f t f i 2 
16 "604 604" _null_ _null_ _null_ poly_same - _null_ ));
  DESCR("same as?");
Index: src/include/executor/executor.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/executor/executor.h,v
retrieving revision 1.124
diff -c -r1.124 executor.h
*** src/include/executor/executor.h     12 Jan 2006 21:48:53 -0000      1.124
--- src/include/executor/executor.h     2 Mar 2006 15:28:34 -0000
***************
*** 237,242 ****
--- 237,247 ----
  
  extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
  extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
+ extern bool ExecSatisfiesIndexPredicate(EState *estate,
+                                         ExprContext *econtext, IndexInfo 
*indexInfo);
+ extern void ExecInsertSelectIndexTuples(TupleTableSlot *slot,
+                                         ItemPointer tupleid, EState *estate, 
bool is_vacuum,
+                                         bool (*selected_index)(Relation rel));
  extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
                                          EState *estate, bool is_vacuum);
  
Index: src/include/utils/rel.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/utils/rel.h,v
retrieving revision 1.87
diff -c -r1.87 rel.h
*** src/include/utils/rel.h     15 Oct 2005 02:49:46 -0000      1.87
--- src/include/utils/rel.h     2 Mar 2006 15:28:34 -0000
***************
*** 115,120 ****
--- 115,122 ----
        FmgrInfo        ambulkdelete;
        FmgrInfo        amvacuumcleanup;
        FmgrInfo        amcostestimate;
+       FmgrInfo amscanforinsert;
+       FmgrInfo aminsertfromscan;
  } RelationAmInfo;
  
  
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
       choose an index scan if your joining column's datatypes do not
       match

Reply via email to