http://archives.postgresql.org/pgsql-hackers/2004-10/msg00464.php.
This fix is intended to be permanent: it moves the responsibility for
calling SetBufferCommitInfoNeedsSave() into the tqual.c routines,
eliminating the requirement for callers to test whether t_infomask changed.
Also, tighten validity checking on buffer IDs in bufmgr.c --- several
routines were paranoid about out-of-range shared buffer numbers but not
about out-of-range local ones, which seems a tad pointless.
/*
- * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.16 2004/08/29 05:06:37 momjian Exp $
+ * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.17 2004/10/15 22:39:38 tgl Exp $
*
* Copyright (c) 2001,2002 Tatsuo Ishii
*
/* scan the relation */
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (HeapTupleSatisfiesNow(tuple->t_data))
+ /* must hold a buffer lock to call HeapTupleSatisfiesNow */
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+ if (HeapTupleSatisfiesNow(tuple->t_data, scan->rs_cbuf))
{
tuple_len += tuple->t_len;
tuple_count++;
dead_tuple_count++;
}
+ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
+
/*
* To avoid physically reading the table twice, try to do the
* free-space scan in parallel with the heap scan. However,
while (block <= tupblock)
{
buffer = ReadBuffer(rel, block);
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
free_space += PageGetFreeSpace((Page) BufferGetPage(buffer));
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(buffer);
block++;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.178 2004/10/12 21:54:34 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.179 2004/10/15 22:39:42 tgl Exp $
*
*
* INTERFACE ROUTINES
tp.t_tableOid = relation->rd_id;
l1:
- result = HeapTupleSatisfiesUpdate(tp.t_data, cid);
+ result = HeapTupleSatisfiesUpdate(tp.t_data, cid, buffer);
if (result == HeapTupleInvisible)
{
XactLockTableWait(xwait);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- if (TransactionIdDidAbort(xwait))
+ if (!TransactionIdDidCommit(xwait))
goto l1;
/*
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for serializable RI updates */
- if (!HeapTupleSatisfiesSnapshot(tp.t_data, crosscheck))
+ if (!HeapTupleSatisfiesSnapshot(tp.t_data, crosscheck, buffer))
result = HeapTupleUpdated;
}
*/
l2:
- result = HeapTupleSatisfiesUpdate(oldtup.t_data, cid);
+ result = HeapTupleSatisfiesUpdate(oldtup.t_data, cid, buffer);
if (result == HeapTupleInvisible)
{
XactLockTableWait(xwait);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
- if (TransactionIdDidAbort(xwait))
+ if (!TransactionIdDidCommit(xwait))
goto l2;
/*
if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
{
/* Perform additional check for serializable RI updates */
- if (!HeapTupleSatisfiesSnapshot(oldtup.t_data, crosscheck))
+ if (!HeapTupleSatisfiesSnapshot(oldtup.t_data, crosscheck, buffer))
result = HeapTupleUpdated;
}
tuple->t_len = ItemIdGetLength(lp);
l3:
- result = HeapTupleSatisfiesUpdate(tuple->t_data, cid);
+ result = HeapTupleSatisfiesUpdate(tuple->t_data, cid, *buffer);
if (result == HeapTupleInvisible)
{
XactLockTableWait(xwait);
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
- if (TransactionIdDidAbort(xwait))
+ if (!TransactionIdDidCommit(xwait))
goto l3;
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.75 2004/09/30 23:21:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.76 2004/10/15 22:39:46 tgl Exp $
*
* INTERFACE ROUTINES
* index_open - open an index relation by relation OID
for (;;)
{
bool found;
- uint16 sv_infomask;
pgstat_count_index_scan(&scan->xs_pgstat_info);
* index AM to not return it on future indexscans.
*
* We told heap_release_fetch to keep a pin on the buffer, so we can
- * re-access the tuple here. But we must re-lock the buffer
- * first. Also, it's just barely possible for an update of hint
- * bits to occur here.
+ * re-access the tuple here. But we must re-lock the buffer first.
*/
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
- sv_infomask = heapTuple->t_data->t_infomask;
- if (HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin) ==
- HEAPTUPLE_DEAD)
+ if (HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
+ scan->xs_cbuf) == HEAPTUPLE_DEAD)
scan->kill_prior_tuple = true;
- if (sv_infomask != heapTuple->t_data->t_infomask)
- SetBufferCommitInfoNeedsSave(scan->xs_cbuf);
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.116 2004/08/29 05:06:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.117 2004/10/15 22:39:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* marked killed. This logic should match
* index_getnext and btgettuple.
*/
- uint16 sv_infomask;
-
LockBuffer(hbuffer, BUFFER_LOCK_SHARE);
- sv_infomask = htup.t_data->t_infomask;
- if (HeapTupleSatisfiesVacuum(htup.t_data,
- RecentGlobalXmin) ==
- HEAPTUPLE_DEAD)
+ if (HeapTupleSatisfiesVacuum(htup.t_data, RecentGlobalXmin,
+ hbuffer) == HEAPTUPLE_DEAD)
{
curitemid->lp_flags |= LP_DELETE;
SetBufferCommitInfoNeedsSave(buf);
}
- if (sv_infomask != htup.t_data->t_infomask)
- SetBufferCommitInfoNeedsSave(hbuffer);
LockBuffer(hbuffer, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(hbuffer);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.240 2004/10/01 17:11:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.241 2004/10/15 22:39:53 tgl Exp $
*
*
* INTERFACE ROUTINES
{
/* do our own time qual check */
bool indexIt;
- uint16 sv_infomask;
/*
- * HeapTupleSatisfiesVacuum may update tuple's hint status
- * bits. We could possibly get away with not locking the
- * buffer here, since caller should hold ShareLock on the
- * relation, but let's be conservative about it.
+ * We could possibly get away with not locking the buffer here,
+ * since caller should hold ShareLock on the relation, but let's
+ * be conservative about it.
*/
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
- sv_infomask = heapTuple->t_data->t_infomask;
- switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin))
+ switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin,
+ scan->rs_cbuf))
{
case HEAPTUPLE_DEAD:
indexIt = false;
break;
}
- /* check for hint-bit update by HeapTupleSatisfiesVacuum */
- if (sv_infomask != heapTuple->t_data->t_infomask)
- SetBufferCommitInfoNeedsSave(scan->rs_cbuf);
-
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
if (!indexIt)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.294 2004/10/07 14:19:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.295 2004/10/15 22:39:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
buf = ReadBuffer(onerel, blkno);
page = BufferGetPage(buf);
+ /*
+ * We don't bother to do LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE)
+ * because we assume that holding exclusive lock on the relation
+ * will keep other backends from looking at the page.
+ */
+
vacpage->blkno = blkno;
vacpage->offsets_used = 0;
vacpage->offsets_free = 0;
offnum <= maxoff;
offnum = OffsetNumberNext(offnum))
{
- uint16 sv_infomask;
ItemId itemid = PageGetItemId(page, offnum);
bool tupgone = false;
tuple.t_len = ItemIdGetLength(itemid);
ItemPointerSet(&(tuple.t_self), blkno, offnum);
- sv_infomask = tuple.t_data->t_infomask;
-
- switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
+ switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
tupgone = true; /* we can delete the tuple */
break;
}
- /* check for hint-bit update by HeapTupleSatisfiesVacuum */
- if (sv_infomask != tuple.t_data->t_infomask)
- pgchanged = true;
-
if (tupgone)
{
ItemId lpp;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.46 2004/09/30 23:21:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.47 2004/10/15 22:39:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
offnum = OffsetNumberNext(offnum))
{
ItemId itemid;
- uint16 sv_infomask;
itemid = PageGetItemId(page, offnum);
ItemPointerSet(&(tuple.t_self), blkno, offnum);
tupgone = false;
- sv_infomask = tuple.t_data->t_infomask;
- switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
+ switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
tupgone = true; /* we can delete the tuple */
break;
}
- /* check for hint-bit update by HeapTupleSatisfiesVacuum */
- if (sv_infomask != tuple.t_data->t_infomask)
- pgchanged = true;
-
if (tupgone)
{
lazy_record_dead_tuple(vacrelstats, &(tuple.t_self));
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
if (pgchanged)
- SetBufferCommitInfoNeedsSave(buf);
-
- ReleaseBuffer(buf);
+ WriteBuffer(buf);
+ else
+ ReleaseBuffer(buf);
}
/* save stats for use later */
Page page;
OffsetNumber offnum,
maxoff;
- bool pgchanged,
- tupgone,
+ bool tupgone,
hastup;
vacuum_delay_point();
continue;
}
- pgchanged = false;
hastup = false;
maxoff = PageGetMaxOffsetNumber(page);
for (offnum = FirstOffsetNumber;
offnum = OffsetNumberNext(offnum))
{
ItemId itemid;
- uint16 sv_infomask;
itemid = PageGetItemId(page, offnum);
ItemPointerSet(&(tuple.t_self), blkno, offnum);
tupgone = false;
- sv_infomask = tuple.t_data->t_infomask;
- switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin))
+ switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
tupgone = true; /* we can delete the tuple */
break;
}
- /* check for hint-bit update by HeapTupleSatisfiesVacuum */
- if (sv_infomask != tuple.t_data->t_infomask)
- pgchanged = true;
-
if (!tupgone)
{
hastup = true;
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
- if (pgchanged)
- WriteBuffer(buf);
- else
- ReleaseBuffer(buf);
+ ReleaseBuffer(buf);
/* Done scanning if we found a tuple here */
if (hastup)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.177 2004/09/06 17:31:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.178 2004/10/15 22:39:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
BufferDesc *bufHdr;
+ if (!BufferIsValid(buffer))
+ elog(ERROR, "bad buffer id: %d", buffer);
+
if (BufferIsLocal(buffer))
{
WriteLocalBuffer(buffer, release);
return;
}
- if (BAD_BUFFER_ID(buffer))
- elog(ERROR, "bad buffer id: %d", buffer);
-
bufHdr = &BufferDescriptors[buffer - 1];
Assert(PrivateRefCount[buffer - 1] > 0);
{
BufferDesc *bufHdr;
+ if (!BufferIsValid(buffer))
+ elog(ERROR, "bad buffer id: %d", buffer);
+
ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer);
if (BufferIsLocal(buffer))
return;
}
- if (BAD_BUFFER_ID(buffer))
- elog(ERROR, "bad buffer id: %d", buffer);
-
bufHdr = &BufferDescriptors[buffer - 1];
Assert(PrivateRefCount[buffer - 1] > 0);
void
IncrBufferRefCount(Buffer buffer)
{
+ Assert(BufferIsValid(buffer));
ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
ResourceOwnerRememberBuffer(CurrentResourceOwner, buffer);
if (BufferIsLocal(buffer))
{
- Assert(buffer >= -NLocBuffer);
Assert(LocalRefCount[-buffer - 1] > 0);
LocalRefCount[-buffer - 1]++;
}
else
{
- Assert(!BAD_BUFFER_ID(buffer));
Assert(PrivateRefCount[buffer - 1] > 0);
PrivateRefCount[buffer - 1]++;
}
*
* Mark a buffer dirty when we have updated tuple commit-status bits in it.
*
- * This is similar to WriteNoReleaseBuffer, except that we have not made a
- * critical change that has to be flushed to disk before xact commit --- the
- * status-bit update could be redone by someone else just as easily.
+ * This is essentially the same as WriteNoReleaseBuffer. We preserve the
+ * distinction as a way of documenting that the caller has not made a critical
+ * data change --- the status-bit update could be redone by someone else just
+ * as easily. Therefore, no WAL log record need be generated, whereas calls
+ * to WriteNoReleaseBuffer really ought to be associated with a WAL-entry-
+ * creating action.
*
* This routine might get called many times on the same page, if we are making
* the first scan after commit of an xact that added/deleted many tuples.
{
BufferDesc *bufHdr;
+ if (!BufferIsValid(buffer))
+ elog(ERROR, "bad buffer id: %d", buffer);
+
if (BufferIsLocal(buffer))
{
WriteLocalBuffer(buffer, false);
return;
}
- if (BAD_BUFFER_ID(buffer))
- elog(ERROR, "bad buffer id: %d", buffer);
-
bufHdr = &BufferDescriptors[buffer - 1];
if ((bufHdr->flags & (BM_DIRTY | BM_JUST_DIRTIED)) !=
if (buflocks == 0)
continue;
- Assert(BufferIsValid(i + 1));
buf = &(BufferDescriptors[i]);
HOLD_INTERRUPTS(); /* don't want to die() partway through... */
*
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.73 2004/09/13 20:07:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.74 2004/10/15 22:40:11 tgl Exp $
*
* ----------
*/
* We should not even consider checking the row if it is no longer
* valid since it was either deleted (doesn't matter) or updated (in
* which case it'll be checked with its final values).
+ *
+ * We do not know what buffer the new_row is in, but it doesn't matter
+ * since it's not possible for a hint-bit update to occur here (the
+ * new_row could only contain our own XID, and we haven't yet committed
+ * or aborted...)
*/
if (new_row)
{
- if (!HeapTupleSatisfiesItself(new_row->t_data))
+ if (!HeapTupleSatisfiesItself(new_row->t_data, InvalidBuffer))
{
heap_close(pk_rel, RowShareLock);
return PointerGetDatum(NULL);
* tqual.c
* POSTGRES "time" qualification code, ie, tuple visibility rules.
*
- * NOTE: all the HeapTupleSatisfies routines will update the tuple's
- * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted. The caller is responsible for noticing any
- * change in t_infomask and scheduling a disk write if so. Note that the
- * caller must hold at least a shared buffer context lock on the buffer
+ * The caller must hold at least a shared buffer context lock on the buffer
* containing the tuple. (VACUUM FULL assumes it's sufficient to have
* exclusive lock on the containing relation, instead.)
*
+ * NOTE: all the HeapTupleSatisfies routines will update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted.
+ *
*
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.79 2004/09/16 18:35:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.80 2004/10/15 22:40:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Xmax is not committed))) that has not been committed
*/
bool
-HeapTupleSatisfiesItself(HeapTupleHeader tuple)
+HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer)
{
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
{
if (TransactionIdDidCommit(xvac))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
}
}
else if (tuple->t_infomask & HEAP_MOVED_IN)
if (TransactionIdIsInProgress(xvac))
return false;
if (TransactionIdDidCommit(xvac))
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
}
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return true;
}
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
- tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */
+ {
+ tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
return false;
}
else
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
}
/* by here, the inserting transaction has committed */
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
- tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */
+ {
+ tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
return true;
}
if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return true;
}
tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
* that do catalog accesses. this is unfortunate, but not critical.
*/
bool
-HeapTupleSatisfiesNow(HeapTupleHeader tuple)
+HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer)
{
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
{
if (TransactionIdDidCommit(xvac))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
}
}
else if (tuple->t_infomask & HEAP_MOVED_IN)
if (TransactionIdIsInProgress(xvac))
return false;
if (TransactionIdDidCommit(xvac))
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
}
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return true;
}
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
- tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */
+ {
+ tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
return false;
}
else
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
}
/* by here, the inserting transaction has committed */
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
- tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */
+ {
+ tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
return true;
}
if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return true;
}
tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
* table.
*/
bool
-HeapTupleSatisfiesToast(HeapTupleHeader tuple)
+HeapTupleSatisfiesToast(HeapTupleHeader tuple, Buffer buffer)
{
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
{
if (TransactionIdDidCommit(xvac))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
}
}
else if (tuple->t_infomask & HEAP_MOVED_IN)
if (TransactionIdIsInProgress(xvac))
return false;
if (TransactionIdDidCommit(xvac))
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
}
* CurrentCommandId.
*/
int
-HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid)
+HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
+ Buffer buffer)
{
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
{
if (TransactionIdDidCommit(xvac))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleInvisible;
}
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
}
}
else if (tuple->t_infomask & HEAP_MOVED_IN)
if (TransactionIdIsInProgress(xvac))
return HeapTupleInvisible;
if (TransactionIdDidCommit(xvac))
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleInvisible;
}
}
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleMayBeUpdated;
}
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
- tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */
+ {
+ tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
return HeapTupleInvisible;
}
else
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
}
/* by here, the inserting transaction has committed */
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
- tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */
+ tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleMayBeUpdated;
}
/* running xact */
if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleMayBeUpdated;
}
tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleUpdated; /* updated by other */
}
* t_ctid (forward link) is returned if it's being updated.
*/
bool
-HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
+HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
{
SnapshotDirty->xmin = SnapshotDirty->xmax = InvalidTransactionId;
ItemPointerSetInvalid(&(SnapshotDirty->tid));
if (TransactionIdDidCommit(xvac))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
}
}
else if (tuple->t_infomask & HEAP_MOVED_IN)
if (TransactionIdIsInProgress(xvac))
return false;
if (TransactionIdDidCommit(xvac))
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
}
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return true;
}
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
SnapshotDirty->xmin = HeapTupleHeaderGetXmin(tuple);
return true; /* in insertion by other */
}
else
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
}
/* by here, the inserting transaction has committed */
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
- tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */
+ tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return true;
}
/* running xact */
if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return true;
}
tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
SnapshotDirty->tid = tuple->t_ctid;
return false; /* updated by other */
}
* can't see it.)
*/
bool
-HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
+HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot,
+ Buffer buffer)
{
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
{
if (TransactionIdDidCommit(xvac))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
}
}
else if (tuple->t_infomask & HEAP_MOVED_IN)
if (TransactionIdIsInProgress(xvac))
return false;
if (TransactionIdDidCommit(xvac))
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return false;
}
}
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return true;
}
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
+ {
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
return false;
}
else
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
}
/*
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
- tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */
+ {
+ tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
return true;
}
/* xmax transaction committed */
tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
}
/*
* even if we see that the deleting transaction has committed.
*/
HTSV_Result
-HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
+HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
+ Buffer buffer)
{
/*
* Has inserting transaction committed?
if (TransactionIdDidCommit(xvac))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HEAPTUPLE_DEAD;
}
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
}
else if (tuple->t_infomask & HEAP_MOVED_IN)
{
if (TransactionIdIsInProgress(xvac))
return HEAPTUPLE_INSERT_IN_PROGRESS;
if (TransactionIdDidCommit(xvac))
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HEAPTUPLE_DEAD;
}
}
return HEAPTUPLE_DELETE_IN_PROGRESS;
}
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
+ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
/*
* crashed
*/
tuple->t_infomask |= HEAP_XMIN_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HEAPTUPLE_DEAD;
}
/* Should only get here if we set XMIN_COMMITTED */
* it did not and will never actually update it.
*/
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
}
return HEAPTUPLE_LIVE;
}
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
return HEAPTUPLE_DELETE_IN_PROGRESS;
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
+ {
tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
else
{
/*
* crashed
*/
tuple->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(buffer);
return HEAPTUPLE_LIVE;
}
/* Should only get here if we set XMAX_COMMITTED */
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/valid.h,v 1.34 2004/08/29 04:13:04 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/valid.h,v 1.35 2004/10/15 22:40:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef VALID_H
#define VALID_H
-/* ----------------
+/*
* HeapKeyTest
*
- * Test a heap tuple with respect to a scan key.
- * ----------------
+ * Test a heap tuple to see if it satisfies a scan key.
*/
-
#define HeapKeyTest(tuple, \
tupdesc, \
nkeys, \
result) \
do \
{ \
-/* We use underscores to protect the variable passed in as parameters */ \
-/* We use two underscore here because this macro is included in the \
- macro below */ \
+ /* Use underscores to protect the variables passed in as parameters */ \
int __cur_nkeys = (nkeys); \
ScanKey __cur_keys = (keys); \
\
Datum __atp; \
bool __isnull; \
Datum __test; \
+ \
+ if (__cur_keys->sk_flags & SK_ISNULL) \
+ { \
+ (result) = false; \
+ break; \
+ } \
\
__atp = heap_getattr((tuple), \
__cur_keys->sk_attno, \
&__isnull); \
\
if (__isnull) \
- { \
- /* XXX eventually should check if SK_ISNULL */ \
- (result) = false; \
- break; \
- } \
- \
- if (__cur_keys->sk_flags & SK_ISNULL) \
{ \
(result) = false; \
break; \
} \
} while (0)
-/* ----------------
+/*
* HeapTupleSatisfies
*
* res is set TRUE if the HeapTuple satisfies the timequal and keytest,
* least likely to fail, too. we should really add the time qual test to
* the restriction and optimize it in the normal way. this has interactions
* with joey's expensive function work.
- * ----------------
*/
#define HeapTupleSatisfies(tuple, \
relation, \
res) \
do \
{ \
-/* We use underscores to protect the variable passed in as parameters */ \
if ((key) != NULL) \
- HeapKeyTest(tuple, RelationGetDescr(relation), \
- (nKeys), (key), (res)); \
+ HeapKeyTest(tuple, RelationGetDescr(relation), nKeys, key, res); \
else \
(res) = true; \
\
- if (res) \
- { \
- if ((relation)->rd_rel->relkind != RELKIND_UNCATALOGED) \
- { \
- uint16 _infomask = (tuple)->t_data->t_infomask; \
- \
- (res) = HeapTupleSatisfiesVisibility((tuple), (snapshot)); \
- if ((tuple)->t_data->t_infomask != _infomask) \
- SetBufferCommitInfoNeedsSave(buffer); \
- } \
- } \
+ if ((res) && (relation)->rd_rel->relkind != RELKIND_UNCATALOGED) \
+ (res) = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer); \
} while (0)
#endif /* VALID_H */
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/storage/bufmgr.h,v 1.86 2004/08/29 05:06:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/storage/bufmgr.h,v 1.87 2004/10/15 22:40:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* These routines are beaten on quite heavily, hence the macroization.
*/
-#define BAD_BUFFER_ID(bid) ((bid) < 1 || (bid) > NBuffers)
-
/*
* BufferIsValid
* True iff the given buffer number is valid (either as a shared
* or local buffer).
*
+ * This is not quite the inverse of the BufferIsInvalid() macro, since this
+ * adds sanity rangechecks on the buffer number.
+ *
* Note: For a long time this was defined the same as BufferIsPinned,
* that is it would say False if you didn't hold a pin on the buffer.
* I believe this was bogus and served only to mask logic errors.
*/
#define BufferIsValid(bufnum) \
( \
- BufferIsLocal(bufnum) ? \
- ((bufnum) >= -NLocBuffer) \
- : \
- (! BAD_BUFFER_ID(bufnum)) \
+ (bufnum) != InvalidBuffer && \
+ (bufnum) >= -NLocBuffer && \
+ (bufnum) <= NBuffers \
)
/*
*/
#define BufferIsPinned(bufnum) \
( \
- BufferIsLocal(bufnum) ? \
- ((bufnum) >= -NLocBuffer && LocalRefCount[-(bufnum) - 1] > 0) \
+ !BufferIsValid(bufnum) ? \
+ false \
: \
- ( \
- BAD_BUFFER_ID(bufnum) ? \
- false \
+ BufferIsLocal(bufnum) ? \
+ (LocalRefCount[-(bufnum) - 1] > 0) \
: \
(PrivateRefCount[(bufnum) - 1] > 0) \
- ) \
)
/*
/*-------------------------------------------------------------------------
*
* tqual.h
- * POSTGRES "time" qualification definitions, ie, tuple visibility rules.
+ * POSTGRES "time qualification" definitions, ie, tuple visibility rules.
*
* Should be moved/renamed... - vadim 07/28/98
*
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.53 2004/09/16 18:35:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.54 2004/10/15 22:40:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/htup.h"
#include "access/xact.h"
+#include "storage/buf.h"
/*
* Assumes heap tuple is valid.
* Beware of multiple evaluations of snapshot argument.
*/
-#define HeapTupleSatisfiesVisibility(tuple, snapshot) \
+#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
((snapshot) == SnapshotNow ? \
- HeapTupleSatisfiesNow((tuple)->t_data) \
+ HeapTupleSatisfiesNow((tuple)->t_data, buffer) \
: \
((snapshot) == SnapshotSelf ? \
- HeapTupleSatisfiesItself((tuple)->t_data) \
+ HeapTupleSatisfiesItself((tuple)->t_data, buffer) \
: \
((snapshot) == SnapshotAny ? \
true \
: \
((snapshot) == SnapshotToast ? \
- HeapTupleSatisfiesToast((tuple)->t_data) \
+ HeapTupleSatisfiesToast((tuple)->t_data, buffer) \
: \
((snapshot) == SnapshotDirty ? \
- HeapTupleSatisfiesDirty((tuple)->t_data) \
+ HeapTupleSatisfiesDirty((tuple)->t_data, buffer) \
: \
- HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \
+ HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot, buffer) \
) \
) \
) \
HEAPTUPLE_DEAD, /* tuple is dead and deletable */
HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */
HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */
- HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in
- * progress */
+ HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
} HTSV_Result;
-extern bool HeapTupleSatisfiesItself(HeapTupleHeader tuple);
-extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple);
-extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple);
-extern bool HeapTupleSatisfiesToast(HeapTupleHeader tuple);
+extern bool HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer);
+extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer);
+extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer);
+extern bool HeapTupleSatisfiesToast(HeapTupleHeader tuple, Buffer buffer);
extern bool HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple,
- Snapshot snapshot);
+ Snapshot snapshot, Buffer buffer);
extern int HeapTupleSatisfiesUpdate(HeapTupleHeader tuple,
- CommandId curcid);
+ CommandId curcid, Buffer buffer);
extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTupleHeader tuple,
- TransactionId OldestXmin);
+ TransactionId OldestXmin, Buffer buffer);
extern Snapshot GetTransactionSnapshot(void);
extern Snapshot GetLatestSnapshot(void);