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	18 Apr 2005 22:01:48 -0000
***************
*** 40,45 ****
--- 40,46 ----
  
  #include "access/heapam.h"
  #include "access/hio.h"
+ #include "access/multixact.h"
  #include "access/tuptoaster.h"
  #include "access/valid.h"
  #include "access/xlogutils.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))
--- 1241,1257 ----
  	{
  		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_XMAX_SHARED_LOCK)
! 			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;
--- 1269,1276 ----
  			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);
--- 1299,1305 ----
  	/* store transaction information of xact deleting the tuple */
  	tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
  							   HEAP_XMAX_INVALID |
! 							   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))
--- 1478,1487 ----
  
  		/* sleep until concurrent transaction ends */
  		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
! 		if (oldtup.t_data->t_infomask & HEAP_XMAX_SHARED_LOCK)
! 			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;
--- 1499,1506 ----
  			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);
--- 1568,1574 ----
  	{
  		oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
  									   HEAP_XMAX_INVALID |
! 									   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);
--- 1654,1660 ----
  	{
  		oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
  									   HEAP_XMAX_INVALID |
! 									   HEAP_LOCKED |
  									   HEAP_MOVED);
  		HeapTupleHeaderSetXmax(oldtup.t_data, xid);
  		HeapTupleHeaderSetCmax(oldtup.t_data, cid);
***************
*** 1739,1755 ****
  }
  
  /*
!  *	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;
  	HTSU_Result	result;
  
  	*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
  	LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
--- 1751,1768 ----
  }
  
  /*
!  *	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;
  	HTSU_Result	result;
+ 	MultiXactId	lockers = InvalidMultiXactId;
  
  	*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
  	LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
***************
*** 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)
  	{
--- 1780,1828 ----
  	{
  		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))
! 		{
! 			lockers = HeapTupleHeaderGetXmax(tuple->t_data);
! 			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_XMAX_SHARED_LOCK)
! 				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;
--- 1833,1863 ----
  	}
  
  	/*
! 	 * 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 */
  	tuple->t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
  								   HEAP_XMAX_INVALID |
+ 								   HEAP_LOCKED |
  								   HEAP_MOVED);
! 
! 
! 	if (mode == LockTupleShared)
! 	{
! 		lockers = MultiXactIdExpand(lockers, xid);
! 
! 		tuple->t_data->t_infomask |= HEAP_XMAX_SHARED_LOCK;
! 		HeapTupleHeaderSetXmax(tuple->t_data, lockers);
! 	}
! 	else
! 	{
! 		tuple->t_data->t_infomask |= HEAP_XMAX_EXCLUSIVE_LOCK;
! 		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;
***************
*** 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);
--- 2036,2042 ----
  		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);
--- 2224,2230 ----
  	{
  		htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
  							  HEAP_XMAX_INVALID |
! 							  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);
--- 2404,2410 ----
  		{
  			htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
  								  HEAP_XMAX_INVALID |
! 								  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	16 Apr 2005 18:20:47 -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	18 Apr 2005 22:01:49 -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"
***************
*** 148,153 ****
--- 149,156 ----
  			else
  				MyProc->subxids.overflowed = true;
  		}
+ 
+ 		MyProc->mxmin = InvalidMultiXactId;
  	}
  
  	LWLockRelease(XidGenLock);
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	18 Apr 2005 22:01:49 -0000
***************
*** 20,25 ****
--- 20,26 ----
  #include <time.h>
  #include <unistd.h>
  
+ #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.187
diff -c -r1.187 xlog.c
*** src/backend/access/transam/xlog.c	17 Apr 2005 03:04:29 -0000	1.187
--- src/backend/access/transam/xlog.c	18 Apr 2005 22:11:10 -0000
***************
*** 23,28 ****
--- 23,29 ----
  #include <sys/time.h>
  
  #include "access/clog.h"
+ #include "access/multixact.h"
  #include "access/subtrans.h"
  #include "access/xact.h"
  #include "access/xlog.h"
***************
*** 3534,3544 ****
--- 3535,3549 ----
  	checkPoint.ThisTimeLineID = ThisTimeLineID;
  	checkPoint.nextXid = FirstNormalTransactionId;
  	checkPoint.nextOid = FirstBootstrapObjectId;
+ 	checkPoint.nextMulti = FirstMultiXactId;
  	checkPoint.time = time(NULL);
  
  	ShmemVariableCache->nextXid = checkPoint.nextXid;
  	ShmemVariableCache->nextOid = checkPoint.nextOid;
  	ShmemVariableCache->oidCount = 0;
+ 	ShmemVariableCache->nextMXact = checkPoint.nextMulti;
+ 	ShmemVariableCache->nextMXoffset = 0;
+ 	ShmemVariableCache->mXactCount = 0;
  
  	/* Set up the XLOG page header */
  	page->xlp_magic = XLOG_PAGE_MAGIC;
***************
*** 3613,3618 ****
--- 3618,3624 ----
  	/* 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")));
--- 4193,4200 ----
  					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")));
***************
*** 4196,4201 ****
--- 4202,4210 ----
  	ShmemVariableCache->nextXid = checkPoint.nextXid;
  	ShmemVariableCache->nextOid = checkPoint.nextOid;
  	ShmemVariableCache->oidCount = 0;
+ 	ShmemVariableCache->nextMXact = checkPoint.nextMulti;
+ 	ShmemVariableCache->mXactCount = 0;
+ 	ShmemVariableCache->nextMXoffset = 0;
  
  	/*
  	 * We must replay WAL entries using the same TimeLineID they were
***************
*** 4549,4554 ****
--- 4558,4564 ----
  	/* Start up the commit log, too */
  	StartupCLOG();
  	StartupSUBTRANS();
+ 	StartupMultiXact();
  
  	ereport(LOG,
  			(errmsg("database system is ready")));
***************
*** 4737,4742 ****
--- 4747,4753 ----
  	CreateCheckPoint(true, true);
  	ShutdownCLOG();
  	ShutdownSUBTRANS();
+ 	ShutdownMultiXact();
  	CritSectionCount--;
  
  	ereport(LOG,
***************
*** 4919,4924 ****
--- 4930,4941 ----
  		checkPoint.nextOid += ShmemVariableCache->oidCount;
  	LWLockRelease(OidGenLock);
  
+ 	LWLockAcquire(MultiXactGenLock, LW_SHARED);
+ 	checkPoint.nextMulti = ShmemVariableCache->nextMXact;
+ 	if (!shutdown)
+ 		checkPoint.nextMulti += ShmemVariableCache->mXactCount;
+ 	LWLockRelease(MultiXactGenLock);
+ 
  	/*
  	 * Having constructed the checkpoint record, ensure all shmem disk
  	 * buffers and commit-log buffers are flushed to disk.
***************
*** 4935,4940 ****
--- 4952,4958 ----
  
  	CheckPointCLOG();
  	CheckPointSUBTRANS();
+ 	CheckPointMultiXact();
  	FlushBufferPool();
  
  	START_CRIT_SECTION();
***************
*** 5053,5058 ****
--- 5071,5088 ----
  	(void) XLogInsert(RM_XLOG_ID, XLOG_NEXTOID, &rdata);
  }
  
+ 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
   */
***************
*** 5072,5077 ****
--- 5102,5119 ----
  			ShmemVariableCache->oidCount = 0;
  		}
  	}
+ 	else if (info == XLOG_NEXTMULTI)
+ 	{
+ 		MultiXactId	nextMulti;
+ 
+ 		memcpy(&nextMulti, XLogRecGetData(record), sizeof(MultiXactId));
+ 		if (ShmemVariableCache->nextMXact < nextMulti)
+ 		{
+ 			ShmemVariableCache->nextMXact = nextMulti;
+ 			ShmemVariableCache->mXactCount = 0;
+ 			ShmemVariableCache->nextMXoffset = 0;
+ 		}
+ 	}
  	else if (info == XLOG_CHECKPOINT_SHUTDOWN)
  	{
  		CheckPoint	checkPoint;
***************
*** 5081,5086 ****
--- 5123,5131 ----
  		ShmemVariableCache->nextXid = checkPoint.nextXid;
  		ShmemVariableCache->nextOid = checkPoint.nextOid;
  		ShmemVariableCache->oidCount = 0;
+ 		ShmemVariableCache->nextMXact = checkPoint.nextMulti;
+ 		ShmemVariableCache->mXactCount = 0;
+ 		ShmemVariableCache->nextMXoffset = 0;
  
  		/*
  		 * TLI may change in a shutdown checkpoint, but it shouldn't
***************
*** 5150,5155 ****
--- 5195,5207 ----
  		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	18 Apr 2005 22:01:50 -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	18 Apr 2005 22:20:48 -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_locktuple 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	18 Apr 2005 22:23:18 -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	18 Apr 2005 22:24:43 -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_locktuple 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.121
diff -c -r1.121 execUtils.c
*** src/backend/executor/execUtils.c	14 Apr 2005 20:03:24 -0000	1.121
--- src/backend/executor/execUtils.c	18 Apr 2005 22:01:52 -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.301
diff -c -r1.301 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	7 Apr 2005 01:51:38 -0000	1.301
--- src/backend/nodes/copyfuncs.c	18 Apr 2005 22:01:52 -0000
***************
*** 1518,1523 ****
--- 1518,1524 ----
  	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);
***************
*** 1598,1604 ****
  	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);
--- 1599,1605 ----
  	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	18 Apr 2005 22:01:53 -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.246
diff -c -r1.246 outfuncs.c
*** src/backend/nodes/outfuncs.c	6 Apr 2005 16:34:05 -0000	1.246
--- src/backend/nodes/outfuncs.c	18 Apr 2005 22:01:53 -0000
***************
*** 1192,1198 ****
  	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);
--- 1192,1198 ----
  	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);
***************
*** 1309,1314 ****
--- 1309,1315 ----
  	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	18 Apr 2005 22:01:53 -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.125
diff -c -r1.125 allpaths.c
*** src/backend/optimizer/path/allpaths.c	6 Apr 2005 16:34:05 -0000	1.125
--- src/backend/optimizer/path/allpaths.c	18 Apr 2005 22:01:54 -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	16 Apr 2005 18:17:20 -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	18 Apr 2005 22:01:54 -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	18 Apr 2005 22:01:54 -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	18 Apr 2005 22:01:55 -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	18 Apr 2005 22:01:55 -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.487
diff -c -r2.487 gram.y
*** src/backend/parser/gram.y	7 Apr 2005 01:51:38 -0000	2.487
--- src/backend/parser/gram.y	18 Apr 2005 22:01:55 -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 <oncommit> OnCommitOption
  %type <withoids> OptWithOids WithOidsAs
  
! %type <list>	for_update_clause opt_for_update_clause update_list
  %type <boolean>	opt_all
  
  %type <node>	join_outer join_qual
--- 242,249 ----
  %type <oncommit> OnCommitOption
  %type <withoids> OptWithOids WithOidsAs
  
! %type <list>	for_locking_clause opt_for_locking_clause
! 				update_list
  %type <boolean>	opt_all
  
  %type <node>	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	18 Apr 2005 22:08:39 -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	16 Apr 2005 18:17:20 -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	18 Apr 2005 22:01:59 -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	16 Apr 2005 18:17:18 -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/ipc/sinval.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/ipc/sinval.c,v
retrieving revision 1.75
diff -c -r1.75 sinval.c
*** src/backend/storage/ipc/sinval.c	31 Dec 2004 22:00:56 -0000	1.75
--- src/backend/storage/ipc/sinval.c	18 Apr 2005 20:35:52 -0000
***************
*** 16,21 ****
--- 16,22 ----
  
  #include <signal.h>
  
+ #include "access/multixact.h"
  #include "access/subtrans.h"
  #include "access/transam.h"
  #include "commands/async.h"
***************
*** 1113,1115 ****
--- 1114,1164 ----
  }
  
  #endif   /* XIDCACHE_DEBUG */
+ 
+ /*
+  * GetNewMinMultiXact -- Get the new minimum MultiXactId
+  *
+  * Find out what the minimum MultiXactId of the remaining running
+  * transactions, that is, excluding myself.  It may very well be
+  * InvalidMultiXactId if no other transaction is using MultiXactIds.
+  *
+  * The caller should hold MultiXactGenLock so that no other backend
+  * can change its mxmin while this is running.
+  */
+ MultiXactId
+ GetNewMinMultiXact(void)
+ {
+ 	SISeg	   *segP = shmInvalBuffer;
+ 	ProcState  *stateP = segP->procState;
+ 	int			index;
+ 	MultiXactId	newMin = InvalidMultiXactId;
+ 
+ 	LWLockAcquire(SInvalLock, LW_SHARED);
+ 
+ 	for (index = 0; index < segP->lastBackend; index++)
+ 	{
+ 		SHMEM_OFFSET pOffset = stateP[index].procStruct;
+ 
+ 		if (pOffset != INVALID_OFFSET)
+ 		{
+ 			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
+ 
+ 			if (proc == MyProc)
+ 				continue;		/* skip myself */
+ 			if (proc->mxmin == InvalidMultiXactId)
+ 				continue;		/* skip non-MultiXact-using backends */
+ 			elog(NOTICE, "probando: proc->mxmin %u, min %u",
+ 					proc->mxmin, newMin);
+ 			if ((!MultiXactIdIsValid(newMin)) ||
+ 				 MultiXactIdPrecedes(proc->mxmin, newMin))
+ 			{
+ 				newMin = proc->mxmin;
+ 				elog(NOTICE, "ok! newmin: %u", newMin);
+ 			}
+ 		}
+ 	}
+ 
+ 	LWLockRelease(SInvalLock);
+ 
+ 	return newMin;
+ }
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	18 Apr 2005 22:01:59 -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	18 Apr 2005 22:02:00 -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	16 Apr 2005 18:17:17 -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	18 Apr 2005 23:04:30 -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_SHARED_LOCK))
  	{
! 		if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
! 		{
! 			if (tuple->t_infomask & HEAP_XMAX_EXCLUSIVE_LOCK)
! 				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_XMAX_EXCLUSIVE_LOCK)
  		{
  			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_SHARED_LOCK))
  	{
! 		if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
! 		{
! 			if (tuple->t_infomask & HEAP_XMAX_EXCLUSIVE_LOCK)
! 				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_XMAX_EXCLUSIVE_LOCK)
  		{
  			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,652 ----
  
  	if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
  	{
! 		if (tuple->t_infomask & HEAP_LOCKED)
  			return HeapTupleMayBeUpdated;
  		return HeapTupleUpdated;	/* updated by other */
  	}
  
! 	/*
! 	 * If it isn't share-locked then Xmax is a real Xid.
! 	 */
! 	if (!(tuple->t_infomask & HEAP_XMAX_SHARED_LOCK))
  	{
! 		if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
! 		{
! 			if (tuple->t_infomask & HEAP_XMAX_EXCLUSIVE_LOCK)
! 				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_XMAX_EXCLUSIVE_LOCK)
  		{
  			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_SHARED_LOCK);
! 			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;
--- 731,737 ----
  
  			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 */
--- 762,768 ----
  
  	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 */
  }
  
  /*
--- 770,814 ----
  
  	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
  	{
! 		if (tuple->t_infomask & HEAP_LOCKED)
  			return true;
  		return false;
  	}
  
! 	if (!(tuple->t_infomask & HEAP_XMAX_SHARED_LOCK))
  	{
! 		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_XMAX_EXCLUSIVE_LOCK)
  		{
  			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)
--- 899,905 ----
  
  			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);
  	}
  
  	/*
--- 962,1001 ----
  	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_SHARED_LOCK))
  		{
! 			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;
--- 1111,1117 ----
  		{
  			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
--- 1142,1148 ----
  	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);
  	}
  
  	/*
--- 1168,1200 ----
  
  	if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
  	{
! 		if (!(tuple->t_infomask & HEAP_XMAX_SHARED_LOCK))
  		{
! 			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	18 Apr 2005 22:02:05 -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	16 Apr 2005 18:17:09 -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 04:09:09 -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	18 Apr 2005 22:02:08 -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	16 Apr 2005 18:17: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,167 ----
  										 * 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)
! 
! /* 0x0080 is presently 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 */
! /* 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 */
  
  
  /*
--- 169,175 ----
  										 * VACUUM FULL */
  #define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN)
  
! #define HEAP_XACT_MASK			0xEF60	/* visibility-related bits */
  
  
  /*
Index: src/include/access/transam.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/access/transam.h,v
retrieving revision 1.54
diff -c -r1.54 transam.h
*** src/include/access/transam.h	13 Apr 2005 18:54:57 -0000	1.54
--- src/include/access/transam.h	18 Apr 2005 22:30:21 -0000
***************
*** 86,91 ****
--- 86,95 ----
  {
  	Oid			nextOid;		/* next OID to assign */
  	uint32		oidCount;		/* OIDs available before must do XLOG work */
+ 	MultiXactId	nextMXact;		/* next MultiXactId to assign */
+ 	uint32		mXactCount;		/* MultiXactIds available before XLogging */
+ 	uint32		nextMXoffset;	/* next MultiXactId offset to assign */
+ 	MultiXactId	minMultiXact;	/* oldest known-running MultiXactId */
  	TransactionId nextXid;		/* next XID to assign */
  	TransactionId xidWarnLimit;	/* start complaining here */
  	TransactionId xidStopLimit;	/* refuse to advance nextXid beyond here */
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	16 Apr 2005 18:24:06 -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	18 Apr 2005 03:52:11 -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	81
  
  /*
   * 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.125
diff -c -r1.125 execnodes.h
*** src/include/nodes/execnodes.h	25 Mar 2005 21:58:00 -0000	1.125
--- src/include/nodes/execnodes.h	16 Apr 2005 18:17:21 -0000
***************
*** 317,322 ****
--- 317,323 ----
  	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	18 Apr 2005 22:02:12 -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	16 Apr 2005 18:17:22 -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	16 Apr 2005 18:17:22 -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	16 Apr 2005 18:17:21 -0000
***************
*** 40,45 ****
--- 40,48 ----
  	CheckpointStartLock,
  	CLogControlLock,
  	SubtransControlLock,
+ 	MultiXactGenLock,
+ 	MultiXactOffsetControlLock,
+ 	MultiXactMemberControlLock,
  	RelCacheInitLock,
  	BgWriterCommLock,
  
Index: src/include/storage/proc.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/storage/proc.h,v
retrieving revision 1.77
diff -c -r1.77 proc.h
*** src/include/storage/proc.h	31 Dec 2004 22:03:42 -0000	1.77
--- src/include/storage/proc.h	18 Apr 2005 03:52:02 -0000
***************
*** 63,68 ****
--- 63,70 ----
  								 * were starting our xact: vacuum must not
  								 * remove tuples deleted by xid >= xmin ! */
  
+ 	MultiXactId	mxmin;			/* smallest MultiXactId we were assigned */
+ 
  	int			pid;			/* This backend's process id */
  	Oid			databaseId;		/* OID of database this backend is using */
  
Index: src/include/storage/sinval.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/storage/sinval.h,v
retrieving revision 1.40
diff -c -r1.40 sinval.h
*** src/include/storage/sinval.h	10 Jan 2005 21:57:19 -0000	1.40
--- src/include/storage/sinval.h	18 Apr 2005 03:51:52 -0000
***************
*** 98,103 ****
--- 98,104 ----
  extern TransactionId GetOldestXmin(bool allDbs);
  extern int	CountActiveBackends(void);
  extern int	CountEmptyBackendSlots(void);
+ extern MultiXactId GetNewMinMultiXact(void);
  
  /* Use "struct PGPROC", not PGPROC, to avoid including proc.h here */
  extern struct PGPROC *BackendIdGetProc(BackendId procId);
