Skip to content

Commit a246bf9

Browse files
committed
Back-patch fix for race condition in heap_update (make sure we hold
the buffer lock while checking page free space).
1 parent 6d85003 commit a246bf9

File tree

1 file changed

+57
-11
lines changed

1 file changed

+57
-11
lines changed

src/backend/access/heap/heapam.c

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.113 2001/03/25 23:23:58 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.113.2.1 2001/05/17 00:48:45 tgl Exp $
1212
*
1313
*
1414
* INTERFACE ROUTINES
@@ -1578,6 +1578,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
15781578
newbuf;
15791579
bool need_toast,
15801580
already_marked;
1581+
Size newtupsize,
1582+
pagefree;
15811583
int result;
15821584

15831585
/* increment access statistics */
@@ -1685,8 +1687,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
16851687
HeapTupleHasExtended(newtup) ||
16861688
(MAXALIGN(newtup->t_len) > TOAST_TUPLE_THRESHOLD));
16871689

1688-
if (need_toast ||
1689-
(unsigned) MAXALIGN(newtup->t_len) > PageGetFreeSpace((Page) dp))
1690+
newtupsize = MAXALIGN(newtup->t_len);
1691+
pagefree = PageGetFreeSpace((Page) dp);
1692+
1693+
if (need_toast || newtupsize > pagefree)
16901694
{
16911695
_locked_tuple_.node = relation->rd_node;
16921696
_locked_tuple_.tid = oldtup.t_self;
@@ -1704,17 +1708,59 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
17041708

17051709
/* Let the toaster do its thing */
17061710
if (need_toast)
1711+
{
17071712
heap_tuple_toast_attrs(relation, newtup, &oldtup);
1713+
newtupsize = MAXALIGN(newtup->t_len);
1714+
}
17081715

1709-
/* Now, do we need a new page for the tuple, or not? */
1710-
if ((unsigned) MAXALIGN(newtup->t_len) <= PageGetFreeSpace((Page) dp))
1711-
newbuf = buffer;
1712-
else
1716+
/*
1717+
* Now, do we need a new page for the tuple, or not? This is a bit
1718+
* tricky since someone else could have added tuples to the page
1719+
* while we weren't looking. We have to recheck the available space
1720+
* after reacquiring the buffer lock. But don't bother to do that
1721+
* if the former amount of free space is still not enough; it's
1722+
* unlikely there's more free now than before.
1723+
*
1724+
* What's more, if we need to get a new page, we will need to acquire
1725+
* buffer locks on both old and new pages. To avoid deadlock against
1726+
* some other backend trying to get the same two locks in the other
1727+
* order, we must be consistent about the order we get the locks in.
1728+
* We use the rule "lock the higher-numbered page of the relation
1729+
* first". To implement this, we must do RelationGetBufferForTuple
1730+
* while not holding the lock on the old page. In 7.1, that routine
1731+
* always returns the last page of the relation, so the rule holds.
1732+
* (7.2 will use a different approach, but the same ordering rule.)
1733+
*/
1734+
if (newtupsize > pagefree)
1735+
{
1736+
/* Assume there's no chance to put newtup on same page. */
17131737
newbuf = RelationGetBufferForTuple(relation, newtup->t_len);
1714-
1715-
/* Re-acquire the lock on the old tuple's page. */
1716-
/* this seems to be deadlock free... */
1717-
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
1738+
/* Now reacquire lock on old tuple's page. */
1739+
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
1740+
}
1741+
else
1742+
{
1743+
/* Re-acquire the lock on the old tuple's page. */
1744+
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
1745+
/* Re-check using the up-to-date free space */
1746+
pagefree = PageGetFreeSpace((Page) dp);
1747+
if (newtupsize > pagefree)
1748+
{
1749+
/*
1750+
* Rats, it doesn't fit anymore. We must now unlock and
1751+
* relock to avoid deadlock. Fortunately, this path should
1752+
* seldom be taken.
1753+
*/
1754+
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
1755+
newbuf = RelationGetBufferForTuple(relation, newtup->t_len);
1756+
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
1757+
}
1758+
else
1759+
{
1760+
/* OK, it fits here, so we're done. */
1761+
newbuf = buffer;
1762+
}
1763+
}
17181764
}
17191765
else
17201766
{

0 commit comments

Comments
 (0)