Index: src/backend/access/heap/heapam.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/heap/heapam.c,v retrieving revision 1.187 diff -c -r1.187 heapam.c *** src/backend/access/heap/heapam.c 14 Apr 2005 20:03:22 -0000 1.187 --- src/backend/access/heap/heapam.c 24 Apr 2005 02:22:18 -0000 *************** *** 40,51 **** --- 40,53 ---- #include "access/heapam.h" #include "access/hio.h" + #include "access/multixact.h" #include "access/tuptoaster.h" #include "access/valid.h" #include "access/xlogutils.h" #include "catalog/catalog.h" #include "catalog/namespace.h" #include "miscadmin.h" + #include "storage/sinval.h" #include "utils/inval.h" #include "utils/relcache.h" #include "pgstat.h" *************** *** 1240,1248 **** { TransactionId xwait = HeapTupleHeaderGetXmax(tp.t_data); ! /* sleep until concurrent transaction ends */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); if (!TransactionIdDidCommit(xwait)) --- 1242,1261 ---- { TransactionId xwait = HeapTupleHeaderGetXmax(tp.t_data); ! /* ! * Sleep until concurrent transaction ends. Note that we don't care ! * if the locker has an exclusive or shared lock, because ours is ! * exclusive. ! */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ! ! if (tp.t_data->t_infomask & HEAP_LOCKED) ! { ! if (tp.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ! MultiXactIdWait(xwait); ! else ! XactLockTableWait(xwait); ! } LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); if (!TransactionIdDidCommit(xwait)) *************** *** 1260,1267 **** tp.t_data->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } ! /* if tuple was marked for update but not updated... */ ! if (tp.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE) result = HeapTupleMayBeUpdated; else result = HeapTupleUpdated; --- 1273,1280 ---- tp.t_data->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } ! /* if tuple was locked but not updated... */ ! if (tp.t_data->t_infomask & HEAP_LOCKED) result = HeapTupleMayBeUpdated; else result = HeapTupleUpdated; *************** *** 1290,1296 **** /* store transaction information of xact deleting the tuple */ tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_MARKED_FOR_UPDATE | HEAP_MOVED); HeapTupleHeaderSetXmax(tp.t_data, xid); HeapTupleHeaderSetCmax(tp.t_data, cid); --- 1303,1310 ---- /* store transaction information of xact deleting the tuple */ tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_XMAX_IS_MULTI | ! HEAP_LOCKED | HEAP_MOVED); HeapTupleHeaderSetXmax(tp.t_data, xid); HeapTupleHeaderSetCmax(tp.t_data, cid); *************** *** 1469,1475 **** /* sleep until concurrent transaction ends */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); if (!TransactionIdDidCommit(xwait)) --- 1483,1495 ---- /* sleep until concurrent transaction ends */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ! if (oldtup.t_data->t_infomask & HEAP_LOCKED) ! { ! if (oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI) ! MultiXactIdWait(xwait); ! else ! XactLockTableWait(xwait); ! } LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); if (!TransactionIdDidCommit(xwait)) *************** *** 1487,1494 **** oldtup.t_data->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } ! /* if tuple was marked for update but not updated... */ ! if (oldtup.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE) result = HeapTupleMayBeUpdated; else result = HeapTupleUpdated; --- 1507,1514 ---- oldtup.t_data->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } ! /* if tuple was locked but not updated... */ ! if (oldtup.t_data->t_infomask & HEAP_LOCKED) result = HeapTupleMayBeUpdated; else result = HeapTupleUpdated; *************** *** 1556,1562 **** { oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_MARKED_FOR_UPDATE | HEAP_MOVED); HeapTupleHeaderSetXmax(oldtup.t_data, xid); HeapTupleHeaderSetCmax(oldtup.t_data, cid); --- 1576,1583 ---- { oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_XMAX_IS_MULTI | ! HEAP_LOCKED | HEAP_MOVED); HeapTupleHeaderSetXmax(oldtup.t_data, xid); HeapTupleHeaderSetCmax(oldtup.t_data, cid); *************** *** 1642,1648 **** { oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_MARKED_FOR_UPDATE | HEAP_MOVED); HeapTupleHeaderSetXmax(oldtup.t_data, xid); HeapTupleHeaderSetCmax(oldtup.t_data, cid); --- 1663,1670 ---- { oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_XMAX_IS_MULTI | ! HEAP_LOCKED | HEAP_MOVED); HeapTupleHeaderSetXmax(oldtup.t_data, xid); HeapTupleHeaderSetCmax(oldtup.t_data, cid); *************** *** 1739,1751 **** } /* ! * heap_mark4update - mark a tuple for update */ HTSU_Result ! heap_mark4update(Relation relation, HeapTuple tuple, Buffer *buffer, ! CommandId cid) { ! TransactionId xid = GetCurrentTransactionId(); ItemPointer tid = &(tuple->t_self); ItemId lp; PageHeader dp; --- 1761,1773 ---- } /* ! * heap_lock_tuple - lock a tuple in shared or exclusive mode */ HTSU_Result ! heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer, ! CommandId cid, LockTupleMode mode) { ! TransactionId xid; ItemPointer tid = &(tuple->t_self); ItemId lp; PageHeader dp; *************** *** 1767,1803 **** { LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(*buffer); ! elog(ERROR, "attempted to mark4update invisible tuple"); } else if (result == HeapTupleBeingUpdated) { ! TransactionId xwait = HeapTupleHeaderGetXmax(tuple->t_data); ! /* sleep until concurrent transaction ends */ ! LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait); ! LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); ! if (!TransactionIdDidCommit(xwait)) ! goto l3; ! /* ! * xwait is committed but if xwait had just marked the tuple for ! * update then some other xaction could update this tuple before ! * we got to this point. ! */ ! if (!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data), xwait)) ! goto l3; ! if (!(tuple->t_data->t_infomask & HEAP_XMAX_COMMITTED)) ! { ! tuple->t_data->t_infomask |= HEAP_XMAX_COMMITTED; ! SetBufferCommitInfoNeedsSave(*buffer); } - /* if tuple was marked for update but not updated... */ - if (tuple->t_data->t_infomask & HEAP_MARKED_FOR_UPDATE) - result = HeapTupleMayBeUpdated; - else - result = HeapTupleUpdated; } if (result != HeapTupleMayBeUpdated) { --- 1789,1837 ---- { LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(*buffer); ! elog(ERROR, "attempted to lock invisible tuple"); } else if (result == HeapTupleBeingUpdated) { ! if (mode == LockTupleShared && ! (tuple->t_data->t_infomask & HEAP_XMAX_SHARED_LOCK)) ! result = HeapTupleMayBeUpdated; ! else ! { ! TransactionId xwait = HeapTupleHeaderGetXmax(tuple->t_data); ! /* sleep until concurrent transaction ends */ ! LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); ! if (tuple->t_data->t_infomask & HEAP_LOCKED) ! { ! if (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ! MultiXactIdWait(xwait); ! else ! XactLockTableWait(xwait); ! } ! LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); ! if (!TransactionIdDidCommit(xwait)) ! goto l3; ! /* ! * xwait is committed but if xwait had just marked the tuple for ! * update then some other xaction could update this tuple before ! * we got to this point. ! */ ! if (!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data), xwait)) ! goto l3; ! if (!(tuple->t_data->t_infomask & HEAP_XMAX_COMMITTED)) ! { ! tuple->t_data->t_infomask |= HEAP_XMAX_COMMITTED; ! SetBufferCommitInfoNeedsSave(*buffer); ! } ! /* if tuple was marked for update but not updated... */ ! if (tuple->t_data->t_infomask & HEAP_LOCKED) ! result = HeapTupleMayBeUpdated; ! else ! result = HeapTupleUpdated; } } if (result != HeapTupleMayBeUpdated) { *************** *** 1808,1824 **** } /* ! * XLOG stuff: no logging is required as long as we have no ! * savepoints. For savepoints private log could be used... */ PageSetTLI(BufferGetPage(*buffer), ThisTimeLineID); ! /* store transaction information of xact marking the tuple */ tuple->t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | ! HEAP_XMAX_INVALID | HEAP_MOVED); ! tuple->t_data->t_infomask |= HEAP_MARKED_FOR_UPDATE; ! HeapTupleHeaderSetXmax(tuple->t_data, xid); HeapTupleHeaderSetCmax(tuple->t_data, cid); /* Make sure there is no forward chain link in t_ctid */ tuple->t_data->t_ctid = *tid; --- 1842,1934 ---- } /* ! * XLOG stuff: no logging is required as long as we don't have ! * two-phase commit. */ PageSetTLI(BufferGetPage(*buffer), ThisTimeLineID); + xid = GetCurrentTransactionId(); ! /* ! * Store transaction information of xact marking the tuple. Don't reset ! * HEAP_XMAX_INVALID just yet because we will check it below. ! */ tuple->t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | ! HEAP_LOCKED | HEAP_MOVED); ! ! if (mode == LockTupleShared) ! { ! TransactionId xmax = HeapTupleHeaderGetXmax(tuple->t_data); ! TransactionId lockers; ! ! /* Set the minimum MultiXactId for this transaction */ ! MultiXactIdSetMin(); ! tuple->t_data->t_infomask |= HEAP_XMAX_SHARED_LOCK; ! ! /* ! * We rely on HeapTupleSatisfiesUpdate setting the HEAP_XMAX_INVALID ! * bit if the MultiXactId is not running. ! */ ! if (!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID)) ! { ! if (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ! { ! /* ! * If the XMAX is already a MultiXactId, then we need to ! * expand it using the new Xid. ! */ ! lockers = MultiXactIdExpand(xmax, true, xid); ! } ! else if (TransactionIdIsInProgress(xmax)) ! { ! /* ! * If the old locker is ourselves, mark the tuple again ! * with our own TransactionId. ! */ ! if (TransactionIdEquals(xmax, xid)) ! lockers = xid; ! else ! { ! /* ! * If the Xmax is a valid TransactionId, then we need to ! * create a new MultiXactId that includes both the old ! * locker and the new xid. ! */ ! lockers = MultiXactIdExpand(xmax, false, xid); ! tuple->t_data->t_infomask |= HEAP_XMAX_IS_MULTI; ! } ! } ! else ! { ! /* ! * This can happen iff HeapTupleSatisfiesUpdate saw the ! * MultiXactId as running, but then all the relevant ! * transactions finished before TransactionIdIsInProgress() got ! * to run. Treat it like there's no locker in the tuple. ! */ ! lockers = xid; ! } ! } ! else ! { ! /* ! * There was no previous locker, so use a single TransactionId. ! */ ! lockers = xid; ! } ! ! HeapTupleHeaderSetXmax(tuple->t_data, lockers); ! } ! else ! { ! /* Grab an exclusive lock on the tuple */ ! tuple->t_data->t_infomask |= HEAP_XMAX_EXCLUSIVE_LOCK; ! Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)); ! HeapTupleHeaderSetXmax(tuple->t_data, xid); ! } ! ! /* Now we can reset this bit. */ ! tuple->t_data->t_infomask &= ~(HEAP_XMAX_INVALID); HeapTupleHeaderSetCmax(tuple->t_data, cid); /* Make sure there is no forward chain link in t_ctid */ tuple->t_data->t_ctid = *tid; *************** *** 1997,2003 **** TransactionId xid[2]; /* xmax, xmin */ if (newtup->t_data->t_infomask & (HEAP_XMAX_INVALID | ! HEAP_MARKED_FOR_UPDATE)) xid[0] = InvalidTransactionId; else xid[0] = HeapTupleHeaderGetXmax(newtup->t_data); --- 2107,2113 ---- TransactionId xid[2]; /* xmax, xmin */ if (newtup->t_data->t_infomask & (HEAP_XMAX_INVALID | ! HEAP_LOCKED)) xid[0] = InvalidTransactionId; else xid[0] = HeapTupleHeaderGetXmax(newtup->t_data); *************** *** 2185,2191 **** { htup->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_MARKED_FOR_UPDATE | HEAP_MOVED); HeapTupleHeaderSetXmax(htup, record->xl_xid); HeapTupleHeaderSetCmax(htup, FirstCommandId); --- 2295,2302 ---- { htup->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_XMAX_IS_MULTI | ! HEAP_LOCKED | HEAP_MOVED); HeapTupleHeaderSetXmax(htup, record->xl_xid); HeapTupleHeaderSetCmax(htup, FirstCommandId); *************** *** 2365,2371 **** { htup->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_MARKED_FOR_UPDATE | HEAP_MOVED); HeapTupleHeaderSetXmax(htup, record->xl_xid); HeapTupleHeaderSetCmax(htup, FirstCommandId); --- 2476,2483 ---- { htup->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | ! HEAP_XMAX_IS_MULTI | ! HEAP_LOCKED | HEAP_MOVED); HeapTupleHeaderSetXmax(htup, record->xl_xid); HeapTupleHeaderSetCmax(htup, FirstCommandId); Index: src/backend/access/transam/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/Makefile,v retrieving revision 1.19 diff -c -r1.19 Makefile *** src/backend/access/transam/Makefile 1 Jul 2004 00:49:42 -0000 1.19 --- src/backend/access/transam/Makefile 24 Apr 2005 00:34:20 -0000 *************** *** 12,18 **** top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global ! OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o all: SUBSYS.o --- 12,18 ---- top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global ! OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o multixact.o all: SUBSYS.o Index: src/backend/access/transam/varsup.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/varsup.c,v retrieving revision 1.63 diff -c -r1.63 varsup.c *** src/backend/access/transam/varsup.c 13 Apr 2005 18:54:56 -0000 1.63 --- src/backend/access/transam/varsup.c 24 Apr 2005 00:35:48 -0000 *************** *** 14,19 **** --- 14,20 ---- #include "postgres.h" #include "access/clog.h" + #include "access/multixact.h" #include "access/subtrans.h" #include "access/transam.h" #include "miscadmin.h" Index: src/backend/access/transam/xact.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/xact.c,v retrieving revision 1.199 diff -c -r1.199 xact.c *** src/backend/access/transam/xact.c 11 Apr 2005 19:51:14 -0000 1.199 --- src/backend/access/transam/xact.c 24 Apr 2005 00:35:48 -0000 *************** *** 20,25 **** --- 20,26 ---- #include #include + #include "access/multixact.h" #include "access/subtrans.h" #include "access/xact.h" #include "catalog/heap.h" *************** *** 1537,1542 **** --- 1538,1544 ---- * by the ResourceOwner mechanism. The other calls here are for * backend-wide state. */ + AtEOXact_MultiXact(); CallXactCallbacks(XACT_EVENT_COMMIT); *************** *** 1701,1706 **** --- 1703,1709 ---- * Post-abort cleanup. See notes in CommitTransaction() concerning * ordering. */ + AtEOXact_MultiXact(); CallXactCallbacks(XACT_EVENT_ABORT); *************** *** 3622,3630 **** ShowTransactionState(const char *str) { /* skip work if message will definitely not be printed */ ! if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2) { ! elog(DEBUG2, "%s", str); ShowTransactionStateRec(CurrentTransactionState); } } --- 3625,3633 ---- ShowTransactionState(const char *str) { /* skip work if message will definitely not be printed */ ! if (log_min_messages <= DEBUG3 || client_min_messages <= DEBUG3) { ! elog(DEBUG3, "%s", str); ShowTransactionStateRec(CurrentTransactionState); } } *************** *** 3640,3646 **** ShowTransactionStateRec(s->parent); /* use ereport to suppress computation if msg will not be printed */ ! ereport(DEBUG2, (errmsg_internal("name: %s; blockState: %13s; state: %7s, xid/subid/cid: %u/%u/%u, nestlvl: %d, children: %s", PointerIsValid(s->name) ? s->name : "unnamed", BlockStateAsString(s->blockState), --- 3643,3649 ---- ShowTransactionStateRec(s->parent); /* use ereport to suppress computation if msg will not be printed */ ! ereport(DEBUG3, (errmsg_internal("name: %s; blockState: %13s; state: %7s, xid/subid/cid: %u/%u/%u, nestlvl: %d, children: %s", PointerIsValid(s->name) ? s->name : "unnamed", BlockStateAsString(s->blockState), Index: src/backend/access/transam/xlog.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/xlog.c,v retrieving revision 1.188 diff -c -r1.188 xlog.c *** src/backend/access/transam/xlog.c 23 Apr 2005 18:49:54 -0000 1.188 --- src/backend/access/transam/xlog.c 24 Apr 2005 01:29:18 -0000 *************** *** 23,28 **** --- 23,29 ---- #include #include "access/clog.h" + #include "access/multixact.h" #include "access/subtrans.h" #include "access/xact.h" #include "access/xlog.h" *************** *** 3534,3539 **** --- 3535,3541 ---- checkPoint.ThisTimeLineID = ThisTimeLineID; checkPoint.nextXid = FirstNormalTransactionId; checkPoint.nextOid = FirstBootstrapObjectId; + checkPoint.nextMulti = FirstMultiXactId; checkPoint.time = time(NULL); ShmemVariableCache->nextXid = checkPoint.nextXid; *************** *** 3613,3618 **** --- 3615,3621 ---- /* Bootstrap the commit log, too */ BootStrapCLOG(); BootStrapSUBTRANS(); + BootStrapMultiXact(); } static char * *************** *** 4187,4194 **** checkPoint.undo.xlogid, checkPoint.undo.xrecoff, wasShutdown ? "TRUE" : "FALSE"))); ereport(LOG, ! (errmsg("next transaction ID: %u; next OID: %u", ! checkPoint.nextXid, checkPoint.nextOid))); if (!TransactionIdIsNormal(checkPoint.nextXid)) ereport(PANIC, (errmsg("invalid next transaction ID"))); --- 4190,4197 ---- checkPoint.undo.xlogid, checkPoint.undo.xrecoff, wasShutdown ? "TRUE" : "FALSE"))); ereport(LOG, ! (errmsg("next transaction ID: %u; next OID: %u; next MultiXactId: %u", ! checkPoint.nextXid, checkPoint.nextOid, checkPoint.nextMulti))); if (!TransactionIdIsNormal(checkPoint.nextXid)) ereport(PANIC, (errmsg("invalid next transaction ID"))); *************** *** 4549,4554 **** --- 4552,4558 ---- /* Start up the commit log, too */ StartupCLOG(); StartupSUBTRANS(); + StartupMultiXact(checkPoint.nextMulti); ereport(LOG, (errmsg("database system is ready"))); *************** *** 4737,4742 **** --- 4741,4747 ---- CreateCheckPoint(true, true); ShutdownCLOG(); ShutdownSUBTRANS(); + ShutdownMultiXact(); CritSectionCount--; ereport(LOG, *************** *** 4919,4924 **** --- 4924,4931 ---- checkPoint.nextOid += ShmemVariableCache->oidCount; LWLockRelease(OidGenLock); + checkPoint.nextMulti = MultiXactGetCheckptMulti(shutdown); + /* * Having constructed the checkpoint record, ensure all shmem disk * buffers and commit-log buffers are flushed to disk. *************** *** 4938,4943 **** --- 4945,4951 ---- CheckPointCLOG(); CheckPointSUBTRANS(); + CheckPointMultiXact(); FlushBufferPool(); START_CRIT_SECTION(); *************** *** 5057,5062 **** --- 5065,5085 ---- } /* + * Write a NEXT_MULTIXACT log record + */ + void + XLogPutNextMultiXactId(MultiXactId nextMulti) + { + XLogRecData rdata; + + rdata.buffer = InvalidBuffer; + rdata.data = (char *) (&nextMulti); + rdata.len = sizeof(MultiXactId); + rdata.next = NULL; + (void) XLogInsert(RM_XLOG_ID, XLOG_NEXTMULTI, &rdata); + } + + /* * XLOG resource manager's routines */ void *************** *** 5075,5080 **** --- 5098,5111 ---- ShmemVariableCache->oidCount = 0; } } + else if (info == XLOG_NEXTMULTI) + { + MultiXactId nextMulti; + + memcpy(&nextMulti, XLogRecGetData(record), sizeof(MultiXactId)); + + MultiXactSetNextMXact(nextMulti); + } else if (info == XLOG_CHECKPOINT_SHUTDOWN) { CheckPoint checkPoint; *************** *** 5084,5089 **** --- 5115,5121 ---- ShmemVariableCache->nextXid = checkPoint.nextXid; ShmemVariableCache->nextOid = checkPoint.nextOid; ShmemVariableCache->oidCount = 0; + MultiXactSetNextMXact(checkPoint.nextMulti); /* * TLI may change in a shutdown checkpoint, but it shouldn't *************** *** 5115,5120 **** --- 5147,5153 ---- ShmemVariableCache->nextOid = checkPoint.nextOid; ShmemVariableCache->oidCount = 0; } + MultiXactSetNextMXact(checkPoint.nextMulti); /* TLI should not change in an on-line checkpoint */ if (checkPoint.ThisTimeLineID != ThisTimeLineID) ereport(PANIC, *************** *** 5153,5158 **** --- 5186,5198 ---- memcpy(&nextOid, rec, sizeof(Oid)); sprintf(buf + strlen(buf), "nextOid: %u", nextOid); } + else if (info == XLOG_NEXTMULTI) + { + MultiXactId multi; + + memcpy(&multi, rec, sizeof(MultiXactId)); + sprintf(buf + strlen(buf), "nextMultiXact: %u", multi); + } else strcat(buf, "UNKNOWN"); } Index: src/backend/commands/portalcmds.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/portalcmds.c,v retrieving revision 1.40 diff -c -r1.40 portalcmds.c *** src/backend/commands/portalcmds.c 11 Apr 2005 15:59:34 -0000 1.40 --- src/backend/commands/portalcmds.c 24 Apr 2005 00:35:52 -0000 *************** *** 90,96 **** if (query->rowMarks != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("DECLARE CURSOR ... FOR UPDATE is not supported"), errdetail("Cursors must be READ ONLY."))); plan = planner(query, true, stmt->options, NULL); --- 90,96 ---- if (query->rowMarks != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"), errdetail("Cursors must be READ ONLY."))); plan = planner(query, true, stmt->options, NULL); Index: src/backend/commands/trigger.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/trigger.c,v retrieving revision 1.186 diff -c -r1.186 trigger.c *** src/backend/commands/trigger.c 14 Apr 2005 20:03:24 -0000 1.186 --- src/backend/commands/trigger.c 24 Apr 2005 05:12:15 -0000 *************** *** 1597,1603 **** *newSlot = NULL; tuple.t_self = *tid; ltrmark:; ! test = heap_mark4update(relation, &tuple, &buffer, cid); switch (test) { case HeapTupleSelfUpdated: --- 1597,1603 ---- *newSlot = NULL; tuple.t_self = *tid; ltrmark:; ! test = heap_lock_tuple(relation, &tuple, &buffer, cid, LockTupleExclusive); switch (test) { case HeapTupleSelfUpdated: *************** *** 1634,1643 **** */ return NULL; ! default: ReleaseBuffer(buffer); ! elog(ERROR, "unrecognized heap_mark4update status: %u", ! test); return NULL; /* keep compiler quiet */ } } --- 1634,1643 ---- */ return NULL; ! case HeapTupleBeingUpdated: ! case HeapTupleInvisible: ReleaseBuffer(buffer); ! elog(ERROR, "invalid heap_lock_tuple status %d", test); return NULL; /* keep compiler quiet */ } } Index: src/backend/commands/vacuum.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuum.c,v retrieving revision 1.306 diff -c -r1.306 vacuum.c *** src/backend/commands/vacuum.c 14 Apr 2005 20:03:24 -0000 1.306 --- src/backend/commands/vacuum.c 24 Apr 2005 00:35:53 -0000 *************** *** 1800,1806 **** !TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data), OldestXmin)) || (!(tuple.t_data->t_infomask & (HEAP_XMAX_INVALID | ! HEAP_MARKED_FOR_UPDATE)) && !(ItemPointerEquals(&(tuple.t_self), &(tuple.t_data->t_ctid))))) { --- 1800,1806 ---- !TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data), OldestXmin)) || (!(tuple.t_data->t_infomask & (HEAP_XMAX_INVALID | ! HEAP_LOCKED)) && !(ItemPointerEquals(&(tuple.t_self), &(tuple.t_data->t_ctid))))) { *************** *** 1839,1845 **** * we have to move to the end of chain. */ while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID | ! HEAP_MARKED_FOR_UPDATE)) && !(ItemPointerEquals(&(tp.t_self), &(tp.t_data->t_ctid)))) { --- 1839,1845 ---- * we have to move to the end of chain. */ while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID | ! HEAP_LOCKED)) && !(ItemPointerEquals(&(tp.t_self), &(tp.t_data->t_ctid)))) { Index: src/backend/executor/execMain.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/executor/execMain.c,v retrieving revision 1.246 diff -c -r1.246 execMain.c *** src/backend/executor/execMain.c 14 Apr 2005 01:38:17 -0000 1.246 --- src/backend/executor/execMain.c 24 Apr 2005 05:13:12 -0000 *************** *** 567,572 **** --- 567,574 ---- { ListCell *l; + estate->es_forUpdate = parseTree->forUpdate; + foreach(l, parseTree->rowMarks) { Index rti = lfirst_int(l); *************** *** 1126,1131 **** --- 1128,1136 ---- * ctid!! */ tupleid = &tuple_ctid; } + /* + * Process any FOR UPDATE or FOR SHARE locking requested. + */ else if (estate->es_rowMark != NIL) { ListCell *l; *************** *** 1137,1142 **** --- 1142,1148 ---- Buffer buffer; HeapTupleData tuple; TupleTableSlot *newSlot; + LockTupleMode lockmode; HTSU_Result test; if (!ExecGetJunkAttribute(junkfilter, *************** *** 1151,1159 **** if (isNull) elog(ERROR, "\"%s\" is NULL", erm->resname); tuple.t_self = *((ItemPointer) DatumGetPointer(datum)); ! test = heap_mark4update(erm->relation, &tuple, &buffer, ! estate->es_snapshot->curcid); ReleaseBuffer(buffer); switch (test) { --- 1157,1171 ---- if (isNull) elog(ERROR, "\"%s\" is NULL", erm->resname); + if (estate->es_forUpdate) + lockmode = LockTupleExclusive; + else + lockmode = LockTupleShared; + tuple.t_self = *((ItemPointer) DatumGetPointer(datum)); ! test = heap_lock_tuple(erm->relation, &tuple, &buffer, ! estate->es_snapshot->curcid, ! lockmode); ReleaseBuffer(buffer); switch (test) { *************** *** 1189,1195 **** goto lnext; default: ! elog(ERROR, "unrecognized heap_mark4update status: %u", test); return (NULL); } --- 1201,1207 ---- goto lnext; default: ! elog(ERROR, "unrecognized heap_lock_tuple status: %u", test); return (NULL); } *************** *** 2088,2093 **** --- 2100,2106 ---- epqstate->es_param_exec_vals = (ParamExecData *) palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData)); epqstate->es_rowMark = estate->es_rowMark; + epqstate->es_forUpdate = estate->es_forUpdate; epqstate->es_instrument = estate->es_instrument; epqstate->es_select_into = estate->es_select_into; epqstate->es_into_oids = estate->es_into_oids; Index: src/backend/executor/execUtils.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/executor/execUtils.c,v retrieving revision 1.122 diff -c -r1.122 execUtils.c *** src/backend/executor/execUtils.c 23 Apr 2005 21:32:34 -0000 1.122 --- src/backend/executor/execUtils.c 24 Apr 2005 00:53:50 -0000 *************** *** 198,203 **** --- 198,204 ---- estate->es_processed = 0; estate->es_lastoid = InvalidOid; estate->es_rowMark = NIL; + estate->es_forUpdate = false; estate->es_instrument = false; estate->es_select_into = false; Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.302 diff -c -r1.302 copyfuncs.c *** src/backend/nodes/copyfuncs.c 19 Apr 2005 22:35:13 -0000 1.302 --- src/backend/nodes/copyfuncs.c 24 Apr 2005 00:35:56 -0000 *************** *** 1606,1611 **** --- 1606,1612 ---- COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(rowMarks); + COPY_SCALAR_FIELD(forUpdate); COPY_NODE_FIELD(targetList); COPY_NODE_FIELD(groupClause); COPY_NODE_FIELD(havingQual); *************** *** 1686,1692 **** COPY_NODE_FIELD(sortClause); COPY_NODE_FIELD(limitOffset); COPY_NODE_FIELD(limitCount); ! COPY_NODE_FIELD(forUpdate); COPY_SCALAR_FIELD(op); COPY_SCALAR_FIELD(all); COPY_NODE_FIELD(larg); --- 1687,1693 ---- COPY_NODE_FIELD(sortClause); COPY_NODE_FIELD(limitOffset); COPY_NODE_FIELD(limitCount); ! COPY_NODE_FIELD(lockingClause); COPY_SCALAR_FIELD(op); COPY_SCALAR_FIELD(all); COPY_NODE_FIELD(larg); Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.240 diff -c -r1.240 equalfuncs.c *** src/backend/nodes/equalfuncs.c 7 Apr 2005 01:51:38 -0000 1.240 --- src/backend/nodes/equalfuncs.c 24 Apr 2005 00:35:56 -0000 *************** *** 642,647 **** --- 642,648 ---- COMPARE_NODE_FIELD(rtable); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(rowMarks); + COMPARE_SCALAR_FIELD(forUpdate); COMPARE_NODE_FIELD(targetList); COMPARE_NODE_FIELD(groupClause); COMPARE_NODE_FIELD(havingQual); *************** *** 706,712 **** COMPARE_NODE_FIELD(sortClause); COMPARE_NODE_FIELD(limitOffset); COMPARE_NODE_FIELD(limitCount); ! COMPARE_NODE_FIELD(forUpdate); COMPARE_SCALAR_FIELD(op); COMPARE_SCALAR_FIELD(all); COMPARE_NODE_FIELD(larg); --- 707,713 ---- COMPARE_NODE_FIELD(sortClause); COMPARE_NODE_FIELD(limitOffset); COMPARE_NODE_FIELD(limitCount); ! COMPARE_NODE_FIELD(lockingClause); COMPARE_SCALAR_FIELD(op); COMPARE_SCALAR_FIELD(all); COMPARE_NODE_FIELD(larg); Index: src/backend/nodes/outfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/outfuncs.c,v retrieving revision 1.249 diff -c -r1.249 outfuncs.c *** src/backend/nodes/outfuncs.c 21 Apr 2005 19:18:12 -0000 1.249 --- src/backend/nodes/outfuncs.c 24 Apr 2005 00:54:55 -0000 *************** *** 1269,1275 **** WRITE_NODE_FIELD(sortClause); WRITE_NODE_FIELD(limitOffset); WRITE_NODE_FIELD(limitCount); ! WRITE_NODE_FIELD(forUpdate); WRITE_ENUM_FIELD(op, SetOperation); WRITE_BOOL_FIELD(all); WRITE_NODE_FIELD(larg); --- 1269,1275 ---- WRITE_NODE_FIELD(sortClause); WRITE_NODE_FIELD(limitOffset); WRITE_NODE_FIELD(limitCount); ! WRITE_NODE_FIELD(lockingClause); WRITE_ENUM_FIELD(op, SetOperation); WRITE_BOOL_FIELD(all); WRITE_NODE_FIELD(larg); *************** *** 1386,1391 **** --- 1386,1392 ---- WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(rowMarks); + WRITE_BOOL_FIELD(forUpdate); WRITE_NODE_FIELD(targetList); WRITE_NODE_FIELD(groupClause); WRITE_NODE_FIELD(havingQual); Index: src/backend/nodes/readfuncs.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/readfuncs.c,v retrieving revision 1.176 diff -c -r1.176 readfuncs.c *** src/backend/nodes/readfuncs.c 6 Apr 2005 16:34:05 -0000 1.176 --- src/backend/nodes/readfuncs.c 24 Apr 2005 00:35:56 -0000 *************** *** 145,150 **** --- 145,151 ---- READ_NODE_FIELD(rtable); READ_NODE_FIELD(jointree); READ_NODE_FIELD(rowMarks); + READ_BOOL_FIELD(forUpdate); READ_NODE_FIELD(targetList); READ_NODE_FIELD(groupClause); READ_NODE_FIELD(havingQual); Index: src/backend/optimizer/path/allpaths.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/path/allpaths.c,v retrieving revision 1.127 diff -c -r1.127 allpaths.c *** src/backend/optimizer/path/allpaths.c 21 Apr 2005 19:18:12 -0000 1.127 --- src/backend/optimizer/path/allpaths.c 24 Apr 2005 00:47:15 -0000 *************** *** 215,227 **** ListCell *il; /* ! * XXX for now, can't handle inherited expansion of FOR UPDATE; can we * do better? */ if (list_member_int(root->rowMarks, parentRTindex)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not supported for inheritance queries"))); /* * Initialize to compute size estimates for whole inheritance tree --- 215,227 ---- ListCell *il; /* ! * XXX for now, can't handle inherited expansion of FOR UPDATE/SHARE; can we * do better? */ if (list_member_int(root->rowMarks, parentRTindex)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries"))); /* * Initialize to compute size estimates for whole inheritance tree Index: src/backend/optimizer/plan/initsplan.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/plan/initsplan.c,v retrieving revision 1.104 diff -c -r1.104 initsplan.c *** src/backend/optimizer/plan/initsplan.c 31 Dec 2004 22:00:09 -0000 1.104 --- src/backend/optimizer/plan/initsplan.c 4 Apr 2005 22:07:58 -0000 *************** *** 323,333 **** Assert(bms_is_subset(rel->outerjoinset, outerrels)); /* ! * Presently the executor cannot support FOR UPDATE marking of * rels appearing on the nullable side of an outer join. (It's * somewhat unclear what that would mean, anyway: what should we * mark when a result row is generated from no element of the ! * nullable relation?) So, complain if target rel is FOR UPDATE. * It's sufficient to make this check once per rel, so do it only * if rel wasn't already known nullable. */ --- 323,333 ---- Assert(bms_is_subset(rel->outerjoinset, outerrels)); /* ! * Presently the executor cannot support FOR UPDATE/SHARE marking of * rels appearing on the nullable side of an outer join. (It's * somewhat unclear what that would mean, anyway: what should we * mark when a result row is generated from no element of the ! * nullable relation?) So, complain if target rel is FOR UPDATE/SHARE. * It's sufficient to make this check once per rel, so do it only * if rel wasn't already known nullable. */ *************** *** 336,342 **** if (list_member_int(root->rowMarks, relno)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE cannot be applied to the nullable side of an outer join"))); } rel->outerjoinset = outerrels; --- 336,342 ---- if (list_member_int(root->rowMarks, relno)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE/SHARE cannot be applied to the nullable side of an outer join"))); } rel->outerjoinset = outerrels; Index: src/backend/optimizer/plan/planner.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/plan/planner.c,v retrieving revision 1.184 diff -c -r1.184 planner.c *** src/backend/optimizer/plan/planner.c 11 Apr 2005 23:06:55 -0000 1.184 --- src/backend/optimizer/plan/planner.c 24 Apr 2005 00:35:58 -0000 *************** *** 635,647 **** tlist = postprocess_setop_tlist(result_plan->targetlist, tlist); /* ! * Can't handle FOR UPDATE here (parser should have checked * already, but let's make sure). */ if (parse->rowMarks) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * Calculate pathkeys that represent result ordering requirements --- 635,647 ---- tlist = postprocess_setop_tlist(result_plan->targetlist, tlist); /* ! * Can't handle FOR UPDATE/SHARE here (parser should have checked * already, but let's make sure). */ if (parse->rowMarks) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * Calculate pathkeys that represent result ordering requirements Index: src/backend/optimizer/prep/prepjointree.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/prep/prepjointree.c,v retrieving revision 1.26 diff -c -r1.26 prepjointree.c *** src/backend/optimizer/prep/prepjointree.c 6 Apr 2005 16:34:06 -0000 1.26 --- src/backend/optimizer/prep/prepjointree.c 24 Apr 2005 00:35:58 -0000 *************** *** 276,286 **** parse->rtable = list_concat(parse->rtable, subquery->rtable); /* ! * Pull up any FOR UPDATE markers, too. (OffsetVarNodes * already adjusted the marker values, so just list_concat the * list.) */ parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); /* * We also have to fix the relid sets of any parent --- 276,287 ---- parse->rtable = list_concat(parse->rtable, subquery->rtable); /* ! * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes * already adjusted the marker values, so just list_concat the * list.) */ parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); + parse->forUpdate = subquery->forUpdate; /* * We also have to fix the relid sets of any parent Index: src/backend/optimizer/prep/preptlist.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/prep/preptlist.c,v retrieving revision 1.74 diff -c -r1.74 preptlist.c *** src/backend/optimizer/prep/preptlist.c 6 Apr 2005 16:34:06 -0000 1.74 --- src/backend/optimizer/prep/preptlist.c 24 Apr 2005 00:35:58 -0000 *************** *** 102,108 **** } /* ! * Add TID targets for rels selected FOR UPDATE. The executor * uses the TID to know which rows to lock, much as for UPDATE or * DELETE. */ --- 102,108 ---- } /* ! * Add TID targets for rels selected FOR UPDATE/SHARE. The executor * uses the TID to know which rows to lock, much as for UPDATE or * DELETE. */ *************** *** 111,132 **** ListCell *l; /* ! * We've got trouble if the FOR UPDATE appears inside * grouping, since grouping renders a reference to individual * tuple CTIDs invalid. This is also checked at parse time, * but that's insufficient because of rule substitution, query * pullup, etc. */ ! CheckSelectForUpdate(parse); /* ! * Currently the executor only supports FOR UPDATE at top * level */ if (PlannerQueryLevel > 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not allowed in subqueries"))); foreach(l, parse->rowMarks) { --- 111,132 ---- ListCell *l; /* ! * We've got trouble if the FOR UPDATE/SHARE appears inside * grouping, since grouping renders a reference to individual * tuple CTIDs invalid. This is also checked at parse time, * but that's insufficient because of rule substitution, query * pullup, etc. */ ! CheckSelectLocking(parse, parse->forUpdate); /* ! * Currently the executor only supports FOR UPDATE/SHARE at top * level */ if (PlannerQueryLevel > 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE/SHARE is not allowed in subqueries"))); foreach(l, parse->rowMarks) { Index: src/backend/parser/analyze.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/analyze.c,v retrieving revision 1.320 diff -c -r1.320 analyze.c *** src/backend/parser/analyze.c 14 Apr 2005 20:03:24 -0000 1.320 --- src/backend/parser/analyze.c 24 Apr 2005 00:35:59 -0000 *************** *** 134,140 **** bool isAddConstraint); static void applyColumnNames(List *dst, List *src); static List *getSetColTypes(ParseState *pstate, Node *node); ! static void transformForUpdate(Query *qry, List *forUpdate); static void transformConstraintAttrs(List *constraintList); static void transformColumnType(ParseState *pstate, ColumnDef *column); static void release_pstate_resources(ParseState *pstate); --- 134,140 ---- bool isAddConstraint); static void applyColumnNames(List *dst, List *src); static List *getSetColTypes(ParseState *pstate, Node *node); ! static void transformLockingClause(Query *qry, List *lockingClause); static void transformConstraintAttrs(List *constraintList); static void transformColumnType(ParseState *pstate, ColumnDef *column); static void release_pstate_resources(ParseState *pstate); *************** *** 1810,1817 **** qry->commandType = CMD_SELECT; ! /* make FOR UPDATE clause available to addRangeTableEntry */ ! pstate->p_forUpdate = stmt->forUpdate; /* process the FROM clause */ transformFromClause(pstate, stmt->fromClause); --- 1810,1833 ---- qry->commandType = CMD_SELECT; ! /* make FOR UPDATE/FOR SHARE clause available to addRangeTableEntry */ ! pstate->p_forUpdate = pstate->p_forShare = NULL; ! ! if (stmt->lockingClause) ! { ! /* ! * The first node in the list should be a T_String Value node ! * containing "for_update" or "for_share" ! */ ! Value *type = linitial(stmt->lockingClause); ! ! if (strcmp(strVal(type), "for_update") == 0) ! pstate->p_forUpdate = list_copy_tail(stmt->lockingClause, 1); ! else if (strcmp(strVal(type), "for_share") == 0) ! pstate->p_forShare = list_copy_tail(stmt->lockingClause, 1); ! else ! elog(ERROR, "invalid first node in locking clause"); ! } /* process the FROM clause */ transformFromClause(pstate, stmt->fromClause); *************** *** 1870,1877 **** if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); ! if (stmt->forUpdate != NIL) ! transformForUpdate(qry, stmt->forUpdate); return qry; } --- 1886,1893 ---- if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); ! if (stmt->lockingClause != NIL) ! transformLockingClause(qry, stmt->lockingClause); return qry; } *************** *** 1899,1905 **** List *sortClause; Node *limitOffset; Node *limitCount; ! List *forUpdate; Node *node; ListCell *left_tlist, *dtlist; --- 1915,1921 ---- List *sortClause; Node *limitOffset; Node *limitCount; ! List *lockingClause; Node *node; ListCell *left_tlist, *dtlist; *************** *** 1937,1954 **** sortClause = stmt->sortClause; limitOffset = stmt->limitOffset; limitCount = stmt->limitCount; ! forUpdate = stmt->forUpdate; stmt->sortClause = NIL; stmt->limitOffset = NULL; stmt->limitCount = NULL; ! stmt->forUpdate = NIL; ! /* We don't support forUpdate with set ops at the moment. */ ! if (forUpdate) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * Recursively transform the components of the tree. --- 1953,1970 ---- sortClause = stmt->sortClause; limitOffset = stmt->limitOffset; limitCount = stmt->limitCount; ! lockingClause = stmt->lockingClause; stmt->sortClause = NIL; stmt->limitOffset = NULL; stmt->limitCount = NULL; ! stmt->lockingClause = NIL; ! /* We don't support FOR UPDATE/SHARE with set ops at the moment. */ ! if (lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * Recursively transform the components of the tree. *************** *** 2083,2090 **** if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); ! if (forUpdate != NIL) ! transformForUpdate(qry, forUpdate); return qry; } --- 2099,2106 ---- if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); ! if (lockingClause != NIL) ! transformLockingClause(qry, lockingClause); return qry; } *************** *** 2107,2114 **** ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"))); ! /* We don't support forUpdate with set ops at the moment. */ ! if (stmt->forUpdate) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); --- 2123,2130 ---- ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"))); ! /* We don't support FOR UPDATE/SHARE with set ops at the moment. */ ! if (stmt->lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); *************** *** 2128,2134 **** { Assert(stmt->larg != NULL && stmt->rarg != NULL); if (stmt->sortClause || stmt->limitOffset || stmt->limitCount || ! stmt->forUpdate) isLeaf = true; else isLeaf = false; --- 2144,2150 ---- { Assert(stmt->larg != NULL && stmt->rarg != NULL); if (stmt->sortClause || stmt->limitOffset || stmt->limitCount || ! stmt->lockingClause) isLeaf = true; else isLeaf = false; *************** *** 2711,2757 **** /* exported so planner can check again after rewriting, query pullup, etc */ void ! CheckSelectForUpdate(Query *qry) { if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not allowed with DISTINCT clause"))); if (qry->groupClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not allowed with GROUP BY clause"))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not allowed with HAVING clause"))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("SELECT FOR UPDATE is not allowed with aggregate functions"))); } /* ! * Convert FOR UPDATE name list into rowMarks list of integer relids * * NB: if you need to change this, see also markQueryForUpdate() * in rewriteHandler.c. */ static void ! transformForUpdate(Query *qry, List *forUpdate) { ! List *rowMarks = qry->rowMarks; ListCell *l; ListCell *rt; Index i; ! CheckSelectForUpdate(qry); ! if (linitial(forUpdate) == NULL) { /* all regular tables used in query */ i = 0; --- 2727,2800 ---- /* exported so planner can check again after rewriting, query pullup, etc */ void ! CheckSelectLocking(Query *qry, bool forUpdate) { + char *operation; + + if (forUpdate) + operation = "SELECT FOR UPDATE"; + else + operation = "SELECT FOR SHARE"; + if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! /* translator: %s is a SQL command, like SELECT FOR UPDATE */ ! errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", operation))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! /* translator: %s is a SQL command, like SELECT FOR UPDATE */ ! errmsg("%s is not allowed with DISTINCT clause", operation))); if (qry->groupClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! /* translator: %s is a SQL command, like SELECT FOR UPDATE */ ! errmsg("%s is not allowed with GROUP BY clause", operation))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! /* translator: %s is a SQL command, like SELECT FOR UPDATE */ ! errmsg("%s is not allowed with HAVING clause", operation))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! /* translator: %s is a SQL command, like SELECT FOR UPDATE */ ! errmsg("%s is not allowed with aggregate functions", operation))); } /* ! * Convert FOR UPDATE/SHARE name list into rowMarks list of integer relids * * NB: if you need to change this, see also markQueryForUpdate() * in rewriteHandler.c. */ static void ! transformLockingClause(Query *qry, List *lockingClause) { ! List *rowMarks; ListCell *l; ListCell *rt; Index i; + Value *type; + List *list_of_rels; + + /* This should be a Value node containing "for_update" o "for_share" */ + type = linitial(lockingClause); + if (strcmp(strVal(type), "for_update") == 0) + qry->forUpdate = true; + else if (strcmp(strVal(type), "for_share") == 0) + qry->forUpdate = false; + else + elog(ERROR, "invalid first node in locking clause"); + + CheckSelectLocking(qry, qry->forUpdate); + + rowMarks = qry->rowMarks; ! list_of_rels = list_copy_tail(lockingClause, 1); ! if (linitial(list_of_rels) == NULL) { /* all regular tables used in query */ i = 0; *************** *** 2773,2779 **** * FOR UPDATE of subquery is propagated to subquery's * rels */ ! transformForUpdate(rte->subquery, list_make1(NULL)); break; default: /* ignore JOIN, SPECIAL, FUNCTION RTEs */ --- 2816,2822 ---- * FOR UPDATE of subquery is propagated to subquery's * rels */ ! transformLockingClause(rte->subquery, lcons(type, list_make1(NULL))); break; default: /* ignore JOIN, SPECIAL, FUNCTION RTEs */ *************** *** 2784,2790 **** else { /* just the named tables */ ! foreach(l, forUpdate) { char *relname = strVal(lfirst(l)); --- 2827,2833 ---- else { /* just the named tables */ ! foreach(l, list_of_rels) { char *relname = strVal(lfirst(l)); *************** *** 2809,2815 **** * FOR UPDATE of subquery is propagated to * subquery's rels */ ! transformForUpdate(rte->subquery, list_make1(NULL)); break; case RTE_JOIN: ereport(ERROR, --- 2852,2858 ---- * FOR UPDATE of subquery is propagated to * subquery's rels */ ! transformLockingClause(rte->subquery, lcons(type, list_make1(NULL))); break; case RTE_JOIN: ereport(ERROR, Index: src/backend/parser/gram.y =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v retrieving revision 2.488 diff -c -r2.488 gram.y *** src/backend/parser/gram.y 23 Apr 2005 17:22:16 -0000 2.488 --- src/backend/parser/gram.y 24 Apr 2005 00:55:22 -0000 *************** *** 87,93 **** static List *extractArgTypes(List *parameters); static SelectStmt *findLeftmostSelect(SelectStmt *node); static void insertSelectOptions(SelectStmt *stmt, ! List *sortClause, List *forUpdate, Node *limitOffset, Node *limitCount); static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); static Node *doNegate(Node *n); --- 87,93 ---- static List *extractArgTypes(List *parameters); static SelectStmt *findLeftmostSelect(SelectStmt *node); static void insertSelectOptions(SelectStmt *stmt, ! List *sortClause, List *lockingClause, Node *limitOffset, Node *limitCount); static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); static Node *doNegate(Node *n); *************** *** 242,248 **** %type OnCommitOption %type OptWithOids WithOidsAs ! %type for_update_clause opt_for_update_clause update_list %type opt_all %type join_outer join_qual --- 242,249 ---- %type OnCommitOption %type OptWithOids WithOidsAs ! %type for_locking_clause opt_for_locking_clause ! update_list %type opt_all %type join_outer join_qual *************** *** 4886,4894 **** ; /* ! * FOR UPDATE may be before or after LIMIT/OFFSET. * In <=7.2.X, LIMIT/OFFSET had to be after FOR UPDATE ! * We now support both orderings, but prefer LIMIT/OFFSET before FOR UPDATE * 2002-08-28 bjm */ select_no_parens: --- 4887,4895 ---- ; /* ! * FOR UPDATE/SHARE may be before or after LIMIT/OFFSET. * In <=7.2.X, LIMIT/OFFSET had to be after FOR UPDATE ! * We now support both orderings, but prefer LIMIT/OFFSET before FOR UPDATE/SHARE * 2002-08-28 bjm */ select_no_parens: *************** *** 4899,4911 **** NULL, NULL); $$ = $1; } ! | select_clause opt_sort_clause for_update_clause opt_select_limit { insertSelectOptions((SelectStmt *) $1, $2, $3, list_nth($4, 0), list_nth($4, 1)); $$ = $1; } ! | select_clause opt_sort_clause select_limit opt_for_update_clause { insertSelectOptions((SelectStmt *) $1, $2, $4, list_nth($3, 0), list_nth($3, 1)); --- 4900,4912 ---- NULL, NULL); $$ = $1; } ! | select_clause opt_sort_clause for_locking_clause opt_select_limit { insertSelectOptions((SelectStmt *) $1, $2, $3, list_nth($4, 0), list_nth($4, 1)); $$ = $1; } ! | select_clause opt_sort_clause select_limit opt_for_locking_clause { insertSelectOptions((SelectStmt *) $1, $2, $4, list_nth($3, 0), list_nth($3, 1)); *************** *** 5146,5158 **** | /*EMPTY*/ { $$ = NULL; } ; ! for_update_clause: ! FOR UPDATE update_list { $$ = $3; } | FOR READ ONLY { $$ = NULL; } ; ! opt_for_update_clause: ! for_update_clause { $$ = $1; } | /* EMPTY */ { $$ = NULL; } ; --- 5147,5160 ---- | /*EMPTY*/ { $$ = NULL; } ; ! for_locking_clause: ! FOR SHARE update_list { $$ = lcons(makeString("for_share"), $3); } ! | FOR UPDATE update_list { $$ = lcons(makeString("for_update"), $3); } | FOR READ ONLY { $$ = NULL; } ; ! opt_for_locking_clause: ! for_locking_clause { $$ = $1; } | /* EMPTY */ { $$ = NULL; } ; *************** *** 8379,8385 **** */ static void insertSelectOptions(SelectStmt *stmt, ! List *sortClause, List *forUpdate, Node *limitOffset, Node *limitCount) { /* --- 8381,8387 ---- */ static void insertSelectOptions(SelectStmt *stmt, ! List *sortClause, List *lockingClause, Node *limitOffset, Node *limitCount) { /* *************** *** 8394,8406 **** errmsg("multiple ORDER BY clauses not allowed"))); stmt->sortClause = sortClause; } ! if (forUpdate) { ! if (stmt->forUpdate) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("multiple FOR UPDATE clauses not allowed"))); ! stmt->forUpdate = forUpdate; } if (limitOffset) { --- 8396,8408 ---- errmsg("multiple ORDER BY clauses not allowed"))); stmt->sortClause = sortClause; } ! if (lockingClause) { ! if (stmt->lockingClause) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("multiple FOR UPDATE/FOR SHARE clauses not allowed"))); ! stmt->lockingClause = lockingClause; } if (limitOffset) { Index: src/backend/parser/parse_relation.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/parse_relation.c,v retrieving revision 1.106 diff -c -r1.106 parse_relation.c *** src/backend/parser/parse_relation.c 13 Apr 2005 16:50:55 -0000 1.106 --- src/backend/parser/parse_relation.c 24 Apr 2005 00:36:00 -0000 *************** *** 40,46 **** --- 40,48 ---- Oid relid); static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, RangeTblEntry *rte1, const char *aliasname1); + static bool isForLocking(List *list, char *refname); static bool isForUpdate(ParseState *pstate, char *refname); + static bool isForShare(ParseState *pstate, char *refname); static void expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, bool include_dropped, *************** *** 761,767 **** * first access to a rel in a statement, be careful to get the right * access level depending on whether we're doing SELECT FOR UPDATE. */ ! lockmode = isForUpdate(pstate, refname) ? RowShareLock : AccessShareLock; rel = heap_openrv(relation, lockmode); rte->relid = RelationGetRelid(rel); --- 763,770 ---- * first access to a rel in a statement, be careful to get the right * access level depending on whether we're doing SELECT FOR UPDATE. */ ! lockmode = (isForUpdate(pstate, refname) || isForShare(pstate, refname)) ? ! RowShareLock : AccessShareLock; rel = heap_openrv(relation, lockmode); rte->relid = RelationGetRelid(rel); *************** *** 1121,1126 **** --- 1124,1160 ---- } /* + * Workhorse for isForUpdate and isForShare + */ + static bool + isForLocking(List *list, char *refname) + { + if (list != NIL) + { + if (linitial(list) == NULL) + { + /* all tables used in query */ + return true; + } + else + { + /* just the named tables */ + ListCell *cell; + + foreach(cell, list) + { + char *rname = strVal(lfirst(cell)); + + if (strcmp(refname, rname) == 0) + return true; + } + } + } + return false; + } + + + /* * Has the specified refname been selected FOR UPDATE? */ static bool *************** *** 1129,1155 **** /* Outer loop to check parent query levels as well as this one */ while (pstate != NULL) { ! if (pstate->p_forUpdate != NIL) ! { ! if (linitial(pstate->p_forUpdate) == NULL) ! { ! /* all tables used in query */ ! return true; ! } ! else ! { ! /* just the named tables */ ! ListCell *l; ! foreach(l, pstate->p_forUpdate) ! { ! char *rname = strVal(lfirst(l)); - if (strcmp(refname, rname) == 0) - return true; - } - } - } pstate = pstate->parentParseState; } return false; --- 1163,1188 ---- /* Outer loop to check parent query levels as well as this one */ while (pstate != NULL) { ! if (isForLocking(pstate->p_forUpdate, refname)) ! return true; ! pstate = pstate->parentParseState; ! } ! return false; ! } ! ! /* ! * Has the specified refname been selected FOR SHARE? ! */ ! static bool ! isForShare(ParseState *pstate, char *refname) ! { ! /* Outer loop to check parent query levels as well as this one */ ! while (pstate != NULL) ! { ! if (isForLocking(pstate->p_forUpdate, refname)) ! return true; pstate = pstate->parentParseState; } return false; Index: src/backend/parser/parse_type.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/parse_type.c,v retrieving revision 1.73 diff -c -r1.73 parse_type.c *** src/backend/parser/parse_type.c 31 Dec 2004 22:00:27 -0000 1.73 --- src/backend/parser/parse_type.c 4 Apr 2005 22:07:58 -0000 *************** *** 432,438 **** stmt->sortClause != NIL || stmt->limitOffset != NULL || stmt->limitCount != NULL || ! stmt->forUpdate != NIL || stmt->op != SETOP_NONE) goto fail; if (list_length(stmt->targetList) != 1) --- 432,438 ---- stmt->sortClause != NIL || stmt->limitOffset != NULL || stmt->limitCount != NULL || ! stmt->lockingClause != NIL || stmt->op != SETOP_NONE) goto fail; if (list_length(stmt->targetList) != 1) Index: src/backend/rewrite/rewriteHandler.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/rewrite/rewriteHandler.c,v retrieving revision 1.150 diff -c -r1.150 rewriteHandler.c *** src/backend/rewrite/rewriteHandler.c 6 Apr 2005 16:34:06 -0000 1.150 --- src/backend/rewrite/rewriteHandler.c 24 Apr 2005 00:36:02 -0000 *************** *** 51,57 **** TargetEntry *prior_tle, const char *attrName); static Node *get_assignment_input(Node *node); ! static void markQueryForUpdate(Query *qry, bool skipOldNew); static List *matchLocks(CmdType event, RuleLock *rulelocks, int varno, Query *parsetree); static Query *fireRIRrules(Query *parsetree, List *activeRIRs); --- 51,57 ---- TargetEntry *prior_tle, const char *attrName); static Node *get_assignment_input(Node *node); ! static void markQueryForLocking(Query *qry, bool skipOldNew); static List *matchLocks(CmdType event, RuleLock *rulelocks, int varno, Query *parsetree); static Query *fireRIRrules(Query *parsetree, List *activeRIRs); *************** *** 745,780 **** rte->checkAsUser = 0; /* ! * FOR UPDATE of view? */ if (list_member_int(parsetree->rowMarks, rt_index)) { /* * Remove the view from the list of rels that will actually be ! * marked FOR UPDATE by the executor. It will still be access- * checked for write access, though. */ parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index); /* ! * Set up the view's referenced tables as if FOR UPDATE. */ ! markQueryForUpdate(rule_action, true); } return parsetree; } /* ! * Recursively mark all relations used by a view as FOR UPDATE. * * This may generate an invalid query, eg if some sub-query uses an * aggregate. We leave it to the planner to detect that. * ! * NB: this must agree with the parser's transformForUpdate() routine. */ static void ! markQueryForUpdate(Query *qry, bool skipOldNew) { Index rti = 0; ListCell *l; --- 745,780 ---- rte->checkAsUser = 0; /* ! * FOR UPDATE/SHARE of view? */ if (list_member_int(parsetree->rowMarks, rt_index)) { /* * Remove the view from the list of rels that will actually be ! * marked FOR UPDATE/SHARE by the executor. It will still be access- * checked for write access, though. */ parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index); /* ! * Set up the view's referenced tables as if FOR UPDATE/SHARE. */ ! markQueryForLocking(rule_action, true); } return parsetree; } /* ! * Recursively mark all relations used by a view as FOR UPDATE/SHARE. * * This may generate an invalid query, eg if some sub-query uses an * aggregate. We leave it to the planner to detect that. * ! * NB: this must agree with the parser's transformLockingClause() routine. */ static void ! markQueryForLocking(Query *qry, bool skipOldNew) { Index rti = 0; ListCell *l; *************** *** 798,805 **** } else if (rte->rtekind == RTE_SUBQUERY) { ! /* FOR UPDATE of subquery is propagated to subquery's rels */ ! markQueryForUpdate(rte->subquery, false); } } } --- 798,805 ---- } else if (rte->rtekind == RTE_SUBQUERY) { ! /* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */ ! markQueryForLocking(rte->subquery, false); } } } *************** *** 911,917 **** * If the relation is the query's result relation, then * RewriteQuery() already got the right lock on it, so we need no * additional lock. Otherwise, check to see if the relation is ! * accessed FOR UPDATE or not. */ if (rt_index == parsetree->resultRelation) lockmode = NoLock; --- 911,917 ---- * If the relation is the query's result relation, then * RewriteQuery() already got the right lock on it, so we need no * additional lock. Otherwise, check to see if the relation is ! * accessed FOR UPDATE/SHARE or not. */ if (rt_index == parsetree->resultRelation) lockmode = NoLock; Index: src/backend/storage/ipc/ipci.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/ipc/ipci.c,v retrieving revision 1.74 diff -c -r1.74 ipci.c *** src/backend/storage/ipc/ipci.c 31 Dec 2004 22:00:56 -0000 1.74 --- src/backend/storage/ipc/ipci.c 10 Apr 2005 23:29:45 -0000 *************** *** 15,20 **** --- 15,21 ---- #include "postgres.h" #include "access/clog.h" + #include "access/multixact.h" #include "access/subtrans.h" #include "access/xlog.h" #include "miscadmin.h" *************** *** 75,80 **** --- 76,82 ---- size += XLOGShmemSize(); size += CLOGShmemSize(); size += SUBTRANSShmemSize(); + size += MultiXactShmemSize(); size += LWLockShmemSize(); size += SInvalShmemSize(maxBackends); size += FreeSpaceShmemSize(); *************** *** 140,145 **** --- 142,148 ---- XLOGShmemInit(); CLOGShmemInit(); SUBTRANSShmemInit(); + MultiXactShmemInit(); InitBufferPool(); /* Index: src/backend/storage/lmgr/lwlock.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/lmgr/lwlock.c,v retrieving revision 1.27 diff -c -r1.27 lwlock.c *** src/backend/storage/lmgr/lwlock.c 8 Apr 2005 14:18:35 -0000 1.27 --- src/backend/storage/lmgr/lwlock.c 24 Apr 2005 00:36:03 -0000 *************** *** 114,119 **** --- 114,125 ---- /* subtrans.c needs one per SubTrans buffer */ numLocks += NUM_SLRU_BUFFERS; + /* + * multixact.c needs one per MultiXact buffers, but there are + * two SLRU areas for MultiXact + */ + numLocks += 2 * NUM_SLRU_BUFFERS; + /* Perhaps create a few more for use by user-defined modules? */ return numLocks; Index: src/backend/tcop/utility.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.235 diff -c -r1.235 utility.c *** src/backend/tcop/utility.c 14 Apr 2005 01:38:18 -0000 1.235 --- src/backend/tcop/utility.c 24 Apr 2005 00:36:03 -0000 *************** *** 1663,1669 **** if (parsetree->into != NULL) tag = "SELECT INTO"; else if (parsetree->rowMarks != NIL) ! tag = "SELECT FOR UPDATE"; else tag = "SELECT"; break; --- 1663,1674 ---- if (parsetree->into != NULL) tag = "SELECT INTO"; else if (parsetree->rowMarks != NIL) ! { ! if (parsetree->forUpdate) ! tag = "SELECT FOR UPDATE"; ! else ! tag = "SELECT FOR SHARE"; ! } else tag = "SELECT"; break; Index: src/backend/utils/adt/ri_triggers.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/adt/ri_triggers.c,v retrieving revision 1.76 diff -c -r1.76 ri_triggers.c *** src/backend/utils/adt/ri_triggers.c 31 Dec 2004 22:01:22 -0000 1.76 --- src/backend/utils/adt/ri_triggers.c 4 Apr 2005 22:07:53 -0000 *************** *** 206,212 **** * tuple. * * pk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR UPDATE will get on it. */ pk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); fk_rel = trigdata->tg_relation; --- 206,212 ---- * tuple. * * pk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR SHARE will get on it. */ pk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); fk_rel = trigdata->tg_relation; *************** *** 267,273 **** * ---------- */ quoteRelationName(pkrelname, pk_rel); ! snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x FOR UPDATE OF x", pkrelname); /* Prepare and save the plan */ --- 267,273 ---- * ---------- */ quoteRelationName(pkrelname, pk_rel); ! snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x FOR SHARE OF x", pkrelname); /* Prepare and save the plan */ *************** *** 428,434 **** queryoids[i] = SPI_gettypeid(fk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_FK_IDX]); } ! strcat(querystr, " FOR UPDATE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, --- 428,434 ---- queryoids[i] = SPI_gettypeid(fk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_FK_IDX]); } ! strcat(querystr, " FOR SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, *************** *** 590,596 **** queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR UPDATE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, --- 590,596 ---- queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, *************** *** 655,661 **** * tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR UPDATE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; --- 655,661 ---- * tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR SHARE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; *************** *** 748,754 **** queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR UPDATE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, --- 748,754 ---- queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, *************** *** 834,840 **** * and old tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR UPDATE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; --- 834,840 ---- * and old tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR SHARE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; *************** *** 939,945 **** queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR UPDATE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, --- 939,945 ---- queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, *************** *** 1373,1379 **** * tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR UPDATE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; --- 1373,1379 ---- * tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR SHARE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; *************** *** 1453,1459 **** queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR UPDATE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, --- 1453,1459 ---- queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, *************** *** 1543,1549 **** * and old tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR UPDATE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; --- 1543,1549 ---- * and old tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual ! * SELECT FOR SHARE will get on it. */ fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; *************** *** 1634,1640 **** queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR UPDATE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, --- 1634,1640 ---- queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } ! strcat(querystr, " FOR SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids, Index: src/backend/utils/time/tqual.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/time/tqual.c,v retrieving revision 1.86 diff -c -r1.86 tqual.c *** src/backend/utils/time/tqual.c 20 Mar 2005 23:40:27 -0000 1.86 --- src/backend/utils/time/tqual.c 24 Apr 2005 02:11:02 -0000 *************** *** 23,28 **** --- 23,29 ---- #include "postgres.h" + #include "access/multixact.h" #include "access/subtrans.h" #include "storage/sinval.h" #include "utils/tqual.h" *************** *** 139,145 **** Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; --- 140,146 ---- Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_LOCKED) return true; return false; *************** *** 167,206 **** if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; /* updated by other */ } ! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; ! return false; ! } ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); } - return true; - } ! /* xmax transaction committed */ ! ! 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; } /* --- 168,215 ---- if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { ! if (tuple->t_infomask & HEAP_LOCKED) return true; return false; /* updated by other */ } ! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) { ! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) ! { ! if (tuple->t_infomask & HEAP_LOCKED) ! return true; ! return false; ! } ! ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) ! { ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); ! } return true; ! } ! /* xmax transaction committed */ ! ! if (tuple->t_infomask & HEAP_LOCKED) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); + return true; } ! tuple->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); + return false; + } + else + { + /* Xmax is a MultiXactId */ return true; } } /* *************** *** 310,316 **** Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId()) --- 319,325 ---- Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_LOCKED) return true; if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId()) *************** *** 341,383 **** if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } ! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; ! if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId()) ! return true; /* deleted after scan started */ ! else ! return false; /* deleted before scan started */ ! } ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); } - return true; - } - - /* xmax transaction committed */ ! 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; } /* --- 350,400 ---- if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { ! if (tuple->t_infomask & HEAP_LOCKED) return true; return false; } ! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) { ! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) ! { ! if (tuple->t_infomask & HEAP_LOCKED) ! return true; ! if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId()) ! return true; /* deleted after scan started */ ! else ! return false; /* deleted before scan started */ ! } ! ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) ! { ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); ! } return true; ! } ! /* xmax transaction committed */ ! ! if (tuple->t_infomask & HEAP_LOCKED) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); + return true; } ! tuple->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); + return false; + } + else + { + /* Xmax is a MultiXactId */ return true; } } /* *************** *** 454,459 **** --- 471,492 ---- * code, since UPDATE needs to know more than "is it visible?". Also, * tuples of my own xact are tested against the passed CommandId not * CurrentCommandId. + * + * The possible return codes are: + * + * HeapTupleInvisible: the tuple didn't exist at all when the scan started, + * e.g. it was created by a later CommandId. + * + * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be + * updated. + * + * HeapTupleSelfUpdated: The tuple was updated by the current transaction, + * after the current scan started. + * + * HeapTupleUpdated: The tuple was updated by a committed transaction. + * + * HeapTupleBeingUpdated: The tuple is being updated by an in-progress + * transaction other than the current transaction. */ HTSU_Result HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, *************** *** 522,528 **** Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return HeapTupleMayBeUpdated; if (HeapTupleHeaderGetCmax(tuple) >= curcid) --- 555,561 ---- Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_LOCKED) return HeapTupleMayBeUpdated; if (HeapTupleHeaderGetCmax(tuple) >= curcid) *************** *** 555,600 **** if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return HeapTupleMayBeUpdated; return HeapTupleUpdated; /* updated by other */ } ! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) ! return HeapTupleMayBeUpdated; ! if (HeapTupleHeaderGetCmax(tuple) >= curcid) ! return HeapTupleSelfUpdated; /* updated after scan ! * started */ ! else ! return HeapTupleInvisible; /* updated before scan started */ ! } ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return HeapTupleMayBeUpdated; } - /* running xact */ - return HeapTupleBeingUpdated; /* in updation by other */ - } - - /* xmax transaction committed */ ! 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 */ } /* --- 588,651 ---- if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { ! if (tuple->t_infomask & HEAP_LOCKED) return HeapTupleMayBeUpdated; return HeapTupleUpdated; /* updated by other */ } ! /* ! * If Xmax isn't a MultiXactId, treat it like a normal TransactionId. ! */ ! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) { ! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) ! { ! if (tuple->t_infomask & HEAP_LOCKED) ! return HeapTupleMayBeUpdated; ! if (HeapTupleHeaderGetCmax(tuple) >= curcid) ! return HeapTupleSelfUpdated; /* updated after scan ! * started */ ! else ! return HeapTupleInvisible; /* updated before scan started */ ! } ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) ! { ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); ! return HeapTupleMayBeUpdated; ! } ! /* running xact */ ! return HeapTupleBeingUpdated; /* in updation by other */ ! } ! ! /* xmax transaction committed */ ! ! if (tuple->t_infomask & HEAP_LOCKED) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return HeapTupleMayBeUpdated; } ! tuple->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); ! return HeapTupleUpdated; /* updated by other */ ! } ! else ! { ! /* Xmax is a MultiXactId */ ! if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple))) ! return HeapTupleBeingUpdated; ! else ! { ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); ! return HeapTupleMayBeUpdated; ! } } } /* *************** *** 679,685 **** Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; --- 730,736 ---- Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_LOCKED) return true; return false; *************** *** 710,716 **** if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; SnapshotDirty->tid = tuple->t_ctid; return false; /* updated by other */ --- 761,767 ---- if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { ! if (tuple->t_infomask & HEAP_LOCKED) return true; SnapshotDirty->tid = tuple->t_ctid; return false; /* updated by other */ *************** *** 718,754 **** if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return true; } - /* running xact */ - SnapshotDirty->xmax = HeapTupleHeaderGetXmax(tuple); - return true; /* in updation by other */ - } ! /* xmax transaction committed */ ! ! 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 */ } /* --- 769,813 ---- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { ! if (tuple->t_infomask & HEAP_LOCKED) return true; return false; } ! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) { ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) ! { ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); ! return true; ! } ! /* running xact */ ! SnapshotDirty->xmax = HeapTupleHeaderGetXmax(tuple); ! return true; /* in updation by other */ ! } ! ! /* xmax transaction committed */ ! ! if (tuple->t_infomask & HEAP_LOCKED) { 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 */ + } + else + { + /* Xmax is a MultiXactId */ return true; } } /* *************** *** 839,845 **** Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) --- 898,904 ---- Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); ! if (tuple->t_infomask & HEAP_LOCKED) return true; if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) *************** *** 902,933 **** if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { ! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { ! if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) ! return true; /* deleted after scan started */ ! else ! return false; /* deleted before scan started */ ! } ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) { ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); } return true; } - - /* xmax transaction committed */ - tuple->t_infomask |= HEAP_XMAX_COMMITTED; - SetBufferCommitInfoNeedsSave(buffer); } /* --- 961,1000 ---- if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; ! if (tuple->t_infomask & HEAP_LOCKED) return true; if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { ! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) { ! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) ! { ! if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) ! return true; /* deleted after scan started */ ! else ! return false; /* deleted before scan started */ ! } ! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) { ! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) ! { ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); ! } ! return true; } + + /* xmax transaction committed */ + tuple->t_infomask |= HEAP_XMAX_COMMITTED; + SetBufferCommitInfoNeedsSave(buffer); + } + else + { + /* Xmax is a MultiXactId */ return true; } } /* *************** *** 1043,1049 **** { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HEAPTUPLE_INSERT_IN_PROGRESS; ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return HEAPTUPLE_INSERT_IN_PROGRESS; /* inserted and then deleted by same xact */ return HEAPTUPLE_DELETE_IN_PROGRESS; --- 1110,1116 ---- { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HEAPTUPLE_INSERT_IN_PROGRESS; ! if (tuple->t_infomask & HEAP_LOCKED) return HEAPTUPLE_INSERT_IN_PROGRESS; /* inserted and then deleted by same xact */ return HEAPTUPLE_DELETE_IN_PROGRESS; *************** *** 1074,1080 **** if (tuple->t_infomask & HEAP_XMAX_INVALID) return HEAPTUPLE_LIVE; ! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) { /* * "Deleting" xact really only marked it for update, so the tuple --- 1141,1147 ---- if (tuple->t_infomask & HEAP_XMAX_INVALID) return HEAPTUPLE_LIVE; ! if (tuple->t_infomask & HEAP_LOCKED) { /* * "Deleting" xact really only marked it for update, so the tuple *************** *** 1100,1124 **** if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { ! if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) ! return HEAPTUPLE_DELETE_IN_PROGRESS; ! else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) { ! tuple->t_infomask |= HEAP_XMAX_COMMITTED; ! SetBufferCommitInfoNeedsSave(buffer); } else { ! /* ! * Not in Progress, Not Committed, so either Aborted or ! * crashed ! */ ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); return HEAPTUPLE_LIVE; } - /* Should only get here if we set XMAX_COMMITTED */ - Assert(tuple->t_infomask & HEAP_XMAX_COMMITTED); } /* --- 1167,1199 ---- if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { ! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) { ! if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) ! return HEAPTUPLE_DELETE_IN_PROGRESS; ! else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) ! { ! tuple->t_infomask |= HEAP_XMAX_COMMITTED; ! SetBufferCommitInfoNeedsSave(buffer); ! } ! else ! { ! /* ! * Not in Progress, Not Committed, so either Aborted or ! * crashed ! */ ! tuple->t_infomask |= HEAP_XMAX_INVALID; ! SetBufferCommitInfoNeedsSave(buffer); ! return HEAPTUPLE_LIVE; ! } ! /* Should only get here if we set XMAX_COMMITTED */ ! Assert(tuple->t_infomask & HEAP_XMAX_COMMITTED); } else { ! /* Xmax is a MultiXactId */ return HEAPTUPLE_LIVE; } } /* Index: src/bin/initdb/initdb.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/bin/initdb/initdb.c,v retrieving revision 1.81 diff -c -r1.81 initdb.c *** src/bin/initdb/initdb.c 12 Apr 2005 19:29:24 -0000 1.81 --- src/bin/initdb/initdb.c 24 Apr 2005 00:36:10 -0000 *************** *** 527,533 **** { /* * POSIX 1003.2: For each dir operand that does not name an ! * existing directory, effects equivalent to those cased by * the following command shall occcur: * * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] --- 527,533 ---- { /* * POSIX 1003.2: For each dir operand that does not name an ! * existing directory, effects equivalent to those caused by * the following command shall occcur: * * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] *************** *** 2120,2130 **** char *pg_data_native; static const char *subdirs[] = { "global", - "pg_xlog", "pg_xlog/archive_status", "pg_clog", "pg_subtrans", ! "base", "base/1", "pg_tblspc" }; --- 2120,2130 ---- char *pg_data_native; static const char *subdirs[] = { "global", "pg_xlog/archive_status", "pg_clog", "pg_subtrans", ! "pg_multixact/members", ! "pg_multixact/offsets", "base/1", "pg_tblspc" }; Index: src/bin/pg_controldata/pg_controldata.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/bin/pg_controldata/pg_controldata.c,v retrieving revision 1.22 diff -c -r1.22 pg_controldata.c *** src/bin/pg_controldata/pg_controldata.c 29 Mar 2005 03:01:32 -0000 1.22 --- src/bin/pg_controldata/pg_controldata.c 12 Apr 2005 03:42:00 -0000 *************** *** 165,170 **** --- 165,171 ---- printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile.checkPointCopy.ThisTimeLineID); printf(_("Latest checkpoint's NextXID: %u\n"), ControlFile.checkPointCopy.nextXid); printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile.checkPointCopy.nextOid); + printf(_("Latest checkpoint's NextMultiXactId: %u\n"), ControlFile.checkPointCopy.nextMulti); printf(_("Time of latest checkpoint: %s\n"), ckpttime_str); printf(_("Database block size: %u\n"), ControlFile.blcksz); printf(_("Blocks per segment of large relation: %u\n"), ControlFile.relseg_size); Index: src/include/c.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/c.h,v retrieving revision 1.181 diff -c -r1.181 c.h *** src/include/c.h 29 Mar 2005 00:17:16 -0000 1.181 --- src/include/c.h 18 Apr 2005 03:49:54 -0000 *************** *** 365,371 **** typedef double float8; /* ! * Oid, RegProcedure, TransactionId, SubTransactionId, CommandId, AclId */ /* typedef Oid is in postgres_ext.h */ --- 365,372 ---- typedef double float8; /* ! * Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId, ! * CommandId, AclId */ /* typedef Oid is in postgres_ext.h */ *************** *** 384,389 **** --- 385,392 ---- #define InvalidSubTransactionId ((SubTransactionId) 0) #define TopSubTransactionId ((SubTransactionId) 1) + typedef TransactionId MultiXactId; + typedef uint32 CommandId; #define FirstCommandId ((CommandId) 0) Index: src/include/access/heapam.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/access/heapam.h,v retrieving revision 1.99 diff -c -r1.99 heapam.h *** src/include/access/heapam.h 14 Apr 2005 20:03:27 -0000 1.99 --- src/include/access/heapam.h 24 Apr 2005 00:36:13 -0000 *************** *** 123,128 **** --- 123,134 ---- /* heapam.c */ + typedef enum + { + LockTupleShared, + LockTupleExclusive + } LockTupleMode; + extern Relation relation_open(Oid relationId, LOCKMODE lockmode); extern Relation conditional_relation_open(Oid relationId, LOCKMODE lockmode, bool nowait); extern Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode); *************** *** 155,162 **** CommandId cid, Snapshot crosscheck, bool wait); extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple tup, ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait); ! extern HTSU_Result heap_mark4update(Relation relation, HeapTuple tup, ! Buffer *userbuf, CommandId cid); extern Oid simple_heap_insert(Relation relation, HeapTuple tup); extern void simple_heap_delete(Relation relation, ItemPointer tid); --- 161,168 ---- CommandId cid, Snapshot crosscheck, bool wait); extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple tup, ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait); ! extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tup, ! Buffer *userbuf, CommandId cid, LockTupleMode mode); extern Oid simple_heap_insert(Relation relation, HeapTuple tup); extern void simple_heap_delete(Relation relation, ItemPointer tid); Index: src/include/access/htup.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/access/htup.h,v retrieving revision 1.73 diff -c -r1.73 htup.h *** src/include/access/htup.h 28 Mar 2005 01:50:34 -0000 1.73 --- src/include/access/htup.h 24 Apr 2005 00:34:21 -0000 *************** *** 152,163 **** * attribute(s) */ #define HEAP_HASEXTENDED 0x000C /* the two above combined */ #define HEAP_HASOID 0x0010 /* has an object-id field */ ! /* 0x0020, 0x0040 and 0x0080 are unused */ #define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */ #define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */ #define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */ #define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */ ! #define HEAP_MARKED_FOR_UPDATE 0x1000 /* marked for UPDATE */ #define HEAP_UPDATED 0x2000 /* this is UPDATEd version of row */ #define HEAP_MOVED_OFF 0x4000 /* moved to another place by * VACUUM FULL */ --- 152,168 ---- * attribute(s) */ #define HEAP_HASEXTENDED 0x000C /* the two above combined */ #define HEAP_HASOID 0x0010 /* has an object-id field */ ! #define HEAP_XMAX_EXCLUSIVE_LOCK 0x0020 /* xmax is exclusive locker */ ! #define HEAP_XMAX_SHARED_LOCK 0x0040 /* xmax is shared locker */ ! #define HEAP_LOCKED (HEAP_XMAX_EXCLUSIVE_LOCK | HEAP_XMAX_SHARED_LOCK) ! ! #define HEAP_XMAX_IS_MULTI 0x0080 /* xmax is a MultiXactId */ ! #define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */ #define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */ #define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */ #define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */ ! /* 0x1000 is presently unused */ #define HEAP_UPDATED 0x2000 /* this is UPDATEd version of row */ #define HEAP_MOVED_OFF 0x4000 /* moved to another place by * VACUUM FULL */ *************** *** 165,171 **** * VACUUM FULL */ #define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN) ! #define HEAP_XACT_MASK 0xFFC0 /* visibility-related bits */ /* --- 170,176 ---- * VACUUM FULL */ #define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN) ! #define HEAP_XACT_MASK 0xEFE0 /* visibility-related bits */ /* Index: src/include/access/xlog.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/access/xlog.h,v retrieving revision 1.59 diff -c -r1.59 xlog.h *** src/include/access/xlog.h 31 Dec 2004 22:03:21 -0000 1.59 --- src/include/access/xlog.h 18 Apr 2005 21:57:29 -0000 *************** *** 133,138 **** --- 133,139 ---- extern void InitXLOGAccess(void); extern void CreateCheckPoint(bool shutdown, bool force); extern void XLogPutNextOid(Oid nextOid); + extern void XLogPutNextMultiXactId(MultiXactId multi); extern XLogRecPtr GetRedoRecPtr(void); #endif /* XLOG_H */ Index: src/include/catalog/pg_control.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_control.h,v retrieving revision 1.20 diff -c -r1.20 pg_control.h *** src/include/catalog/pg_control.h 29 Mar 2005 03:01:32 -0000 1.20 --- src/include/catalog/pg_control.h 24 Apr 2005 05:19:05 -0000 *************** *** 22,28 **** /* Version identifier for this pg_control format */ ! #define PG_CONTROL_VERSION 74 /* * Body of CheckPoint XLOG records. This is declared here because we keep --- 22,28 ---- /* Version identifier for this pg_control format */ ! #define PG_CONTROL_VERSION 75 /* * Body of CheckPoint XLOG records. This is declared here because we keep *************** *** 39,50 **** --- 39,52 ---- TimeLineID ThisTimeLineID; /* current TLI */ TransactionId nextXid; /* next free XID */ Oid nextOid; /* next free OID */ + MultiXactId nextMulti; /* next free MultiXactId */ time_t time; /* time stamp of checkpoint */ } CheckPoint; /* XLOG info values for XLOG rmgr */ #define XLOG_CHECKPOINT_SHUTDOWN 0x00 #define XLOG_CHECKPOINT_ONLINE 0x10 + #define XLOG_NEXTMULTI 0x20 #define XLOG_NEXTOID 0x30 Index: src/include/nodes/execnodes.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/execnodes.h,v retrieving revision 1.127 diff -c -r1.127 execnodes.h *** src/include/nodes/execnodes.h 20 Apr 2005 15:48:36 -0000 1.127 --- src/include/nodes/execnodes.h 24 Apr 2005 00:58:26 -0000 *************** *** 318,323 **** --- 318,324 ---- uint32 es_processed; /* # of tuples processed */ Oid es_lastoid; /* last oid processed (by INSERT) */ List *es_rowMark; /* not good place, but there is no other */ + bool es_forUpdate; /* was it FOR UPDATE or FOR SHARE */ bool es_instrument; /* true requests runtime instrumentation */ bool es_select_into; /* true if doing SELECT INTO */ Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.277 diff -c -r1.277 parsenodes.h *** src/include/nodes/parsenodes.h 7 Apr 2005 01:51:40 -0000 1.277 --- src/include/nodes/parsenodes.h 24 Apr 2005 00:36:17 -0000 *************** *** 91,97 **** * clauses) */ List *rowMarks; /* integer list of RT indexes of relations ! * that are selected FOR UPDATE */ List *targetList; /* target list (of TargetEntry) */ --- 91,100 ---- * clauses) */ List *rowMarks; /* integer list of RT indexes of relations ! * that are selected FOR UPDATE/SHARE */ ! ! bool forUpdate; /* true if rowMarks are FOR UPDATE, ! * false if they are FOR SHARE */ List *targetList; /* target list (of TargetEntry) */ *************** *** 688,694 **** List *sortClause; /* sort clause (a list of SortBy's) */ Node *limitOffset; /* # of result tuples to skip */ Node *limitCount; /* # of result tuples to return */ ! List *forUpdate; /* FOR UPDATE clause */ /* * These fields are used only in upper-level SelectStmts. --- 691,697 ---- List *sortClause; /* sort clause (a list of SortBy's) */ Node *limitOffset; /* # of result tuples to skip */ Node *limitCount; /* # of result tuples to return */ ! List *lockingClause; /* FOR UPDATE or FOR SHARE clauses */ /* * These fields are used only in upper-level SelectStmts. Index: src/include/parser/analyze.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/parser/analyze.h,v retrieving revision 1.29 diff -c -r1.29 analyze.h *** src/include/parser/analyze.h 31 Dec 2004 22:03:38 -0000 1.29 --- src/include/parser/analyze.h 4 Apr 2005 22:08:03 -0000 *************** *** 21,26 **** int *numParams); extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState); extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt); ! extern void CheckSelectForUpdate(Query *qry); #endif /* ANALYZE_H */ --- 21,26 ---- int *numParams); extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState); extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt); ! extern void CheckSelectLocking(Query *qry, bool forUpdate); #endif /* ANALYZE_H */ Index: src/include/parser/parse_node.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/parser/parse_node.h,v retrieving revision 1.42 diff -c -r1.42 parse_node.h *** src/include/parser/parse_node.h 31 Dec 2004 22:03:38 -0000 1.42 --- src/include/parser/parse_node.h 4 Apr 2005 22:08:03 -0000 *************** *** 54,59 **** --- 54,60 ---- int p_numparams; /* allocated size of p_paramtypes[] */ int p_next_resno; /* next targetlist resno to assign */ List *p_forUpdate; /* FOR UPDATE clause, if any (see gram.y) */ + List *p_forShare; /* FOR SHARE clause, if any (see gram.y) */ Node *p_value_substitute; /* what to replace VALUE with, if * any */ bool p_variableparams; Index: src/include/storage/lwlock.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/storage/lwlock.h,v retrieving revision 1.17 diff -c -r1.17 lwlock.h *** src/include/storage/lwlock.h 4 Mar 2005 20:21:07 -0000 1.17 --- src/include/storage/lwlock.h 11 Apr 2005 23:07:34 -0000 *************** *** 40,45 **** --- 40,48 ---- CheckpointStartLock, CLogControlLock, SubtransControlLock, + MultiXactGenLock, + MultiXactOffsetControlLock, + MultiXactMemberControlLock, RelCacheInitLock, BgWriterCommLock,