diff options
Diffstat (limited to 'src/backend/access')
-rw-r--r-- | src/backend/access/gin/gininsert.c | 11 | ||||
-rw-r--r-- | src/backend/access/gist/gist.c | 6 | ||||
-rw-r--r-- | src/backend/access/hash/hash.c | 6 | ||||
-rw-r--r-- | src/backend/access/heap/tuptoaster.c | 6 | ||||
-rw-r--r-- | src/backend/access/index/indexam.c | 6 | ||||
-rw-r--r-- | src/backend/access/nbtree/nbtinsert.c | 113 | ||||
-rw-r--r-- | src/backend/access/nbtree/nbtree.c | 9 |
7 files changed, 120 insertions, 37 deletions
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c index 2adaed43d48..d175a5a99e6 100644 --- a/src/backend/access/gin/gininsert.c +++ b/src/backend/access/gin/gininsert.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.22 2009/06/11 14:48:53 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.23 2009/07/29 20:56:17 tgl Exp $ *------------------------------------------------------------------------- */ @@ -415,12 +415,11 @@ gininsert(PG_FUNCTION_ARGS) #ifdef NOT_USED Relation heapRel = (Relation) PG_GETARG_POINTER(4); - bool checkUnique = PG_GETARG_BOOL(5); + IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5); #endif GinState ginstate; MemoryContext oldCtx; MemoryContext insertCtx; - uint32 res = 0; int i; insertCtx = AllocSetContextCreate(CurrentMemoryContext, @@ -440,7 +439,7 @@ gininsert(PG_FUNCTION_ARGS) memset(&collector, 0, sizeof(GinTupleCollector)); for (i = 0; i < ginstate.origTupdesc->natts; i++) if (!isnull[i]) - res += ginHeapTupleFastCollect(index, &ginstate, &collector, + ginHeapTupleFastCollect(index, &ginstate, &collector, (OffsetNumber) (i + 1), values[i], ht_ctid); ginHeapTupleFastInsert(index, &ginstate, &collector); @@ -449,7 +448,7 @@ gininsert(PG_FUNCTION_ARGS) { for (i = 0; i < ginstate.origTupdesc->natts; i++) if (!isnull[i]) - res += ginHeapTupleInsert(index, &ginstate, + ginHeapTupleInsert(index, &ginstate, (OffsetNumber) (i + 1), values[i], ht_ctid); } @@ -457,5 +456,5 @@ gininsert(PG_FUNCTION_ARGS) MemoryContextSwitchTo(oldCtx); MemoryContextDelete(insertCtx); - PG_RETURN_BOOL(res > 0); + PG_RETURN_BOOL(false); } diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 2742969c6af..ef6febef85c 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.156 2009/01/01 17:23:34 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.157 2009/07/29 20:56:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -225,7 +225,7 @@ gistinsert(PG_FUNCTION_ARGS) #ifdef NOT_USED Relation heapRel = (Relation) PG_GETARG_POINTER(4); - bool checkUnique = PG_GETARG_BOOL(5); + IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5); #endif IndexTuple itup; GISTSTATE giststate; @@ -248,7 +248,7 @@ gistinsert(PG_FUNCTION_ARGS) MemoryContextSwitchTo(oldCtx); MemoryContextDelete(insertCtx); - PG_RETURN_BOOL(true); + PG_RETURN_BOOL(false); } diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index 49b6594f1eb..35056838364 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.112 2009/06/11 14:48:53 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.113 2009/07/29 20:56:18 tgl Exp $ * * NOTES * This file contains only the public interface routines. @@ -165,7 +165,7 @@ hashinsert(PG_FUNCTION_ARGS) #ifdef NOT_USED Relation heapRel = (Relation) PG_GETARG_POINTER(4); - bool checkUnique = PG_GETARG_BOOL(5); + IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5); #endif IndexTuple itup; @@ -192,7 +192,7 @@ hashinsert(PG_FUNCTION_ARGS) pfree(itup); - PG_RETURN_BOOL(true); + PG_RETURN_BOOL(false); } diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 781bfd2e486..2fbf8f43bbd 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.94 2009/07/22 01:21:22 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.95 2009/07/29 20:56:18 tgl Exp $ * * * INTERFACE ROUTINES @@ -1229,7 +1229,9 @@ toast_save_datum(Relation rel, Datum value, int options) */ index_insert(toastidx, t_values, t_isnull, &(toasttup->t_self), - toastrel, toastidx->rd_index->indisunique); + toastrel, + toastidx->rd_index->indisunique ? + UNIQUE_CHECK_YES : UNIQUE_CHECK_NO); /* * Free memory diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 32623965c78..f4ffeccd328 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.114 2009/06/11 14:48:54 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.115 2009/07/29 20:56:18 tgl Exp $ * * INTERFACE ROUTINES * index_open - open an index relation by relation OID @@ -185,7 +185,7 @@ index_insert(Relation indexRelation, bool *isnull, ItemPointer heap_t_ctid, Relation heapRelation, - bool check_uniqueness) + IndexUniqueCheck checkUnique) { FmgrInfo *procedure; @@ -201,7 +201,7 @@ index_insert(Relation indexRelation, PointerGetDatum(isnull), PointerGetDatum(heap_t_ctid), PointerGetDatum(heapRelation), - BoolGetDatum(check_uniqueness))); + Int32GetDatum((int32) checkUnique))); } /* diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index a06faa20203..f0137182249 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.170 2009/06/11 14:48:54 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.171 2009/07/29 20:56:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -49,8 +49,9 @@ typedef struct static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf); static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, - Relation heapRel, Buffer buf, OffsetNumber ioffset, - ScanKey itup_scankey); + Relation heapRel, Buffer buf, OffsetNumber offset, + ScanKey itup_scankey, + IndexUniqueCheck checkUnique, bool *is_unique); static void _bt_findinsertloc(Relation rel, Buffer *bufptr, OffsetNumber *offsetptr, @@ -85,11 +86,24 @@ static void _bt_vacuum_one_page(Relation rel, Buffer buffer); * * This routine is called by the public interface routines, btbuild * and btinsert. By here, itup is filled in, including the TID. + * + * If checkUnique is UNIQUE_CHECK_NO or UNIQUE_CHECK_PARTIAL, this + * will allow duplicates. Otherwise (UNIQUE_CHECK_YES or + * UNIQUE_CHECK_EXISTING) it will throw error for a duplicate. + * For UNIQUE_CHECK_EXISTING we merely run the duplicate check, and + * don't actually insert. + * + * The result value is only significant for UNIQUE_CHECK_PARTIAL: + * it must be TRUE if the entry is known unique, else FALSE. + * (In the current implementation we'll also return TRUE after a + * successful UNIQUE_CHECK_YES or UNIQUE_CHECK_EXISTING call, but + * that's just a coding artifact.) */ -void +bool _bt_doinsert(Relation rel, IndexTuple itup, - bool index_is_unique, Relation heapRel) + IndexUniqueCheck checkUnique, Relation heapRel) { + bool is_unique = false; int natts = rel->rd_rel->relnatts; ScanKey itup_scankey; BTStack stack; @@ -134,13 +148,18 @@ top: * * If we must wait for another xact, we release the lock while waiting, * and then must start over completely. + * + * For a partial uniqueness check, we don't wait for the other xact. + * Just let the tuple in and return false for possibly non-unique, + * or true for definitely unique. */ - if (index_is_unique) + if (checkUnique != UNIQUE_CHECK_NO) { TransactionId xwait; offset = _bt_binsrch(rel, buf, natts, itup_scankey, false); - xwait = _bt_check_unique(rel, itup, heapRel, buf, offset, itup_scankey); + xwait = _bt_check_unique(rel, itup, heapRel, buf, offset, itup_scankey, + checkUnique, &is_unique); if (TransactionIdIsValid(xwait)) { @@ -153,13 +172,23 @@ top: } } - /* do the insertion */ - _bt_findinsertloc(rel, &buf, &offset, natts, itup_scankey, itup); - _bt_insertonpg(rel, buf, stack, itup, offset, false); + if (checkUnique != UNIQUE_CHECK_EXISTING) + { + /* do the insertion */ + _bt_findinsertloc(rel, &buf, &offset, natts, itup_scankey, itup); + _bt_insertonpg(rel, buf, stack, itup, offset, false); + } + else + { + /* just release the buffer */ + _bt_relbuf(rel, buf); + } /* be tidy */ _bt_freestack(stack); _bt_freeskey(itup_scankey); + + return is_unique; } /* @@ -172,10 +201,16 @@ top: * 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(). + * + * However, if checkUnique == UNIQUE_CHECK_PARTIAL, we always return + * InvalidTransactionId because we don't want to wait. In this case we + * set *is_unique to false if there is a potential conflict, and the + * core code must redo the uniqueness check later. */ static TransactionId _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, - Buffer buf, OffsetNumber offset, ScanKey itup_scankey) + Buffer buf, OffsetNumber offset, ScanKey itup_scankey, + IndexUniqueCheck checkUnique, bool *is_unique) { TupleDesc itupdesc = RelationGetDescr(rel); int natts = rel->rd_rel->relnatts; @@ -184,6 +219,10 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, Page page; BTPageOpaque opaque; Buffer nbuf = InvalidBuffer; + bool found = false; + + /* Assume unique until we find a duplicate */ + *is_unique = true; InitDirtySnapshot(SnapshotDirty); @@ -241,21 +280,48 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, htid = curitup->t_tid; /* + * If we are doing a recheck, we expect to find the tuple we + * are rechecking. It's not a duplicate, but we have to keep + * scanning. + */ + if (checkUnique == UNIQUE_CHECK_EXISTING && + ItemPointerCompare(&htid, &itup->t_tid) == 0) + { + found = true; + } + + /* * We check the whole HOT-chain to see if there is any tuple * that satisfies SnapshotDirty. This is necessary because we * have just a single index entry for the entire chain. */ - if (heap_hot_search(&htid, heapRel, &SnapshotDirty, &all_dead)) + else if (heap_hot_search(&htid, heapRel, &SnapshotDirty, + &all_dead)) { - /* it is a duplicate */ - TransactionId xwait = - (TransactionIdIsValid(SnapshotDirty.xmin)) ? - SnapshotDirty.xmin : SnapshotDirty.xmax; + TransactionId xwait; + + /* + * It is a duplicate. If we are only doing a partial + * check, then don't bother checking if the tuple is + * being updated in another transaction. Just return + * the fact that it is a potential conflict and leave + * the full check till later. + */ + if (checkUnique == UNIQUE_CHECK_PARTIAL) + { + if (nbuf != InvalidBuffer) + _bt_relbuf(rel, nbuf); + *is_unique = false; + return InvalidTransactionId; + } /* * If this tuple is being updated by other transaction * then we have to wait for its commit/abort. */ + xwait = (TransactionIdIsValid(SnapshotDirty.xmin)) ? + SnapshotDirty.xmin : SnapshotDirty.xmax; + if (TransactionIdIsValid(xwait)) { if (nbuf != InvalidBuffer) @@ -295,6 +361,9 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, break; } + /* + * This is a definite conflict. + */ ereport(ERROR, (errcode(ERRCODE_UNIQUE_VIOLATION), errmsg("duplicate key value violates unique constraint \"%s\"", @@ -349,6 +418,18 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, } } + /* + * If we are doing a recheck then we should have found the tuple we + * are checking. Otherwise there's something very wrong --- probably, + * the index is on a non-immutable expression. + */ + if (checkUnique == UNIQUE_CHECK_EXISTING && !found) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("failed to re-find tuple within index \"%s\"", + RelationGetRelationName(rel)), + errhint("This may be because of a non-immutable index expression."))); + if (nbuf != InvalidBuffer) _bt_relbuf(rel, nbuf); diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 2b76e7cd453..87a8a225dbf 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -12,7 +12,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.171 2009/06/11 14:48:54 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.172 2009/07/29 20:56:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -217,18 +217,19 @@ btinsert(PG_FUNCTION_ARGS) bool *isnull = (bool *) PG_GETARG_POINTER(2); ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3); Relation heapRel = (Relation) PG_GETARG_POINTER(4); - bool checkUnique = PG_GETARG_BOOL(5); + IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5); + bool result; IndexTuple itup; /* generate an index tuple */ itup = index_form_tuple(RelationGetDescr(rel), values, isnull); itup->t_tid = *ht_ctid; - _bt_doinsert(rel, itup, checkUnique, heapRel); + result = _bt_doinsert(rel, itup, checkUnique, heapRel); pfree(itup); - PG_RETURN_BOOL(true); + PG_RETURN_BOOL(result); } /* |