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;