mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
Fix race condition that could allow two concurrent transactions
to insert the same key into a supposedly unique index. The bug is of low probability, and may not explain any of the recent reports of duplicated rows; but a bug is a bug.
This commit is contained in:
parent
c546170e77
commit
1ccc67600b
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.87 2001/10/25 05:49:21 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.88 2002/01/01 20:32:37 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -75,7 +75,6 @@ static void _bt_pgaddtup(Relation rel, Page page,
|
|||||||
static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum,
|
static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum,
|
||||||
int keysz, ScanKey scankey);
|
int keysz, ScanKey scankey);
|
||||||
|
|
||||||
static Relation _xlheapRel; /* temporary hack */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _bt_doinsert() -- Handle insertion of a single btitem in the tree.
|
* _bt_doinsert() -- Handle insertion of a single btitem in the tree.
|
||||||
@ -116,7 +115,21 @@ top:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If we're not allowing duplicates, make sure the key isn't already
|
* If we're not allowing duplicates, make sure the key isn't already
|
||||||
* in the index. XXX this belongs somewhere else, likely
|
* 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 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.
|
||||||
*/
|
*/
|
||||||
if (index_is_unique)
|
if (index_is_unique)
|
||||||
{
|
{
|
||||||
@ -135,8 +148,6 @@ top:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_xlheapRel = heapRel; /* temporary hack */
|
|
||||||
|
|
||||||
/* do the insertion */
|
/* do the insertion */
|
||||||
res = _bt_insertonpg(rel, buf, stack, natts, itup_scankey, btitem, 0);
|
res = _bt_insertonpg(rel, buf, stack, natts, itup_scankey, btitem, 0);
|
||||||
|
|
||||||
@ -397,9 +408,16 @@ _bt_insertonpg(Relation rel,
|
|||||||
{
|
{
|
||||||
/* step right one page */
|
/* step right one page */
|
||||||
BlockNumber rblkno = lpageop->btpo_next;
|
BlockNumber rblkno = lpageop->btpo_next;
|
||||||
|
Buffer rbuf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* must write-lock next page before releasing write lock on
|
||||||
|
* current page; else someone else's _bt_check_unique scan
|
||||||
|
* could fail to see our insertion.
|
||||||
|
*/
|
||||||
|
rbuf = _bt_getbuf(rel, rblkno, BT_WRITE);
|
||||||
_bt_relbuf(rel, buf);
|
_bt_relbuf(rel, buf);
|
||||||
buf = _bt_getbuf(rel, rblkno, BT_WRITE);
|
buf = rbuf;
|
||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
lpageop = (BTPageOpaque) PageGetSpecialPointer(page);
|
lpageop = (BTPageOpaque) PageGetSpecialPointer(page);
|
||||||
movedright = true;
|
movedright = true;
|
||||||
@ -833,7 +851,6 @@ _bt_split(Relation rel, Buffer buf, OffsetNumber firstright,
|
|||||||
* page is not updated yet. Log changes before continuing.
|
* page is not updated yet. Log changes before continuing.
|
||||||
*
|
*
|
||||||
* NO ELOG(ERROR) till right sibling is updated.
|
* NO ELOG(ERROR) till right sibling is updated.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
START_CRIT_SECTION();
|
START_CRIT_SECTION();
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user