[WIP] shared locks
Hackers,
Here is another attempt at the shared locks patch. This is a radically
different approach, using Xmax in the tuples and SLRU areas instead of
the lock manager.
The idea is that a tuple's Xmax can either be a real TransactionId
(which is used normally like current CVS tip), or, if the infomask has
HEAP_XMAX_SHARED_LOCK, a MultiXactId.
A MultiXactId is an abstraction for a set of TransactionIds. So a
locker can sleep on the set (effectively calling XactLockTableWait on
each member), add itself to a previously existing set, or create a new
set with only itself.
MultiXactIds are implemented using two SLRU areas and a couple of
variables in ShmemVariableCache. We also XLog groups of them just like
we do for Oids.
This patch is a work in progress. I need to test it more, but the basic
infrastructure is written and working. I'd love some comments on the
basic design idea.
The patch applies cleanly to current CVS tip. There are two new files
which are src/backend/access/transam/multixact.c and
src/include/access/multixact.h, included separately.
Thanks,
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"I dream about dreams about dreams", sang the nightingale
under the pale moon (Sandman)
Attachments:
shared-locks-1.patchtext/plain; charset=us-asciiDownload
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);
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
The idea is that a tuple's Xmax can either be a real TransactionId
(which is used normally like current CVS tip), or, if the infomask has
HEAP_XMAX_SHARED_LOCK, a MultiXactId.
Interesting idea. Would it be possible to invoke this mechanism only
when actually needed --- that is, the first locker of a given tuple
puts his plain TransactionId into Xmax (and also sets an infomask bit
indicating his intent to have a shared rather than exclusive lock),
and then the second locker to come along replaces the TransactionId
with a MultiTransactionId including himself and the first locker?
This requires 2 infomask bits: 1 for shared vs exclusive lock and one
for whether the Xmax contains a TransactionId or MultiTransactionId.
But we have them available, and I think I like keeping those concepts
separate anyway. (Who's to say we wouldn't want to allow a
MultiTransactionId to hold an exclusive lock, someday?)
The advantage of course would be substantially less overhead in the very
common case where there's no actual contention for the tuple.
MultiXactIds are implemented using two SLRU areas and a couple of
variables in ShmemVariableCache. We also XLog groups of them just like
we do for Oids.
So no need for expansible shmem storage? Might be the way to go.
I haven't read the patch yet but the idea sounds promising.
regards, tom lane
Tom Lane wrote:
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
The idea is that a tuple's Xmax can either be a real TransactionId
(which is used normally like current CVS tip), or, if the infomask has
HEAP_XMAX_SHARED_LOCK, a MultiXactId.Interesting idea. Would it be possible to invoke this mechanism only
when actually needed --- that is, the first locker of a given tuple
puts his plain TransactionId into Xmax (and also sets an infomask bit
indicating his intent to have a shared rather than exclusive lock),
and then the second locker to come along replaces the TransactionId
with a MultiTransactionId including himself and the first locker?This requires 2 infomask bits: 1 for shared vs exclusive lock and one
for whether the Xmax contains a TransactionId or MultiTransactionId.
But we have them available, and I think I like keeping those concepts
separate anyway. (Who's to say we wouldn't want to allow a
MultiTransactionId to hold an exclusive lock, someday?)The advantage of course would be substantially less overhead in the very
common case where there's no actual contention for the tuple.
Yes, that is certainly possible. Alvaro felt he wanted something
simpler and that the two-bit case would add complexity, but I agree it
would reduce overhead in the most common case.
MultiXactIds are implemented using two SLRU areas and a couple of
variables in ShmemVariableCache. We also XLog groups of them just like
we do for Oids.So no need for expansible shmem storage? Might be the way to go.
I haven't read the patch yet but the idea sounds promising.
Right. What he does is to use something like pg_subtrans to have a
rolling window of current multi-xid sets and the idea is that most
access will fall into a small window that is easily stored in the memory
window.
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073
On Mon, Apr 18, 2005 at 09:53:38PM -0400, Bruce Momjian wrote:
Tom Lane wrote:
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
The idea is that a tuple's Xmax can either be a real TransactionId
(which is used normally like current CVS tip), or, if the infomask has
HEAP_XMAX_SHARED_LOCK, a MultiXactId.Interesting idea. Would it be possible to invoke this mechanism only
when actually needed --- that is, the first locker of a given tuple
puts his plain TransactionId into Xmax (and also sets an infomask bit
indicating his intent to have a shared rather than exclusive lock),
and then the second locker to come along replaces the TransactionId
with a MultiTransactionId including himself and the first locker?This requires 2 infomask bits: 1 for shared vs exclusive lock and one
for whether the Xmax contains a TransactionId or MultiTransactionId.
But we have them available, and I think I like keeping those concepts
separate anyway. (Who's to say we wouldn't want to allow a
MultiTransactionId to hold an exclusive lock, someday?)The advantage of course would be substantially less overhead in the very
common case where there's no actual contention for the tuple.Yes, that is certainly possible. Alvaro felt he wanted something
simpler and that the two-bit case would add complexity, but I agree it
would reduce overhead in the most common case.
I had thought it would make things more complicated. Now that I know
how the whole thing works I can handle the extra complexity, which is not
much really. Also I wasn't sure if we wanted to waste two infomask
bits on this :-)
MultiXactIds are implemented using two SLRU areas and a couple of
variables in ShmemVariableCache. We also XLog groups of them just like
we do for Oids.So no need for expansible shmem storage? Might be the way to go.
Right. I have stashed some info (like next MultiXactId to assign, the
first MultiXactId this transaction was assigned, etc) in
ShmemVariableCache and PGPROC, but I'm now thinking in storing it
in a [fixed size] shmem area private to multixact.c; this way I don't
have to lock SInvalLock.
BTW, I had to use three additional LWLocks: two for SLRU and one for
MultiXactId generation, which also covers the ShmemVariableCache
variables. I hope that's OK.
--
Alvaro Herrera (<alvherre[@]dcc.uchile.cl>)
"No es bueno caminar con un hombre muerto"
On Mon, Apr 18, 2005 at 08:00:57PM -0400, Tom Lane wrote:
Alvaro Herrera <alvherre@dcc.uchile.cl> writes:
The idea is that a tuple's Xmax can either be a real TransactionId
(which is used normally like current CVS tip), or, if the infomask has
HEAP_XMAX_SHARED_LOCK, a MultiXactId.Interesting idea. Would it be possible to invoke this mechanism only
when actually needed --- that is, the first locker of a given tuple
puts his plain TransactionId into Xmax (and also sets an infomask bit
indicating his intent to have a shared rather than exclusive lock),
and then the second locker to come along replaces the TransactionId
with a MultiTransactionId including himself and the first locker?
Ok, here is the patch again. I did this, so there are now two related
bits in the infomask: HEAP_XMAX_IS_MULTI and
HEAP_XMAX_{SHARED,EXCLUSIVE}_LOCK. (I ripped out HEAP_XMAX_FOR_UPDATE).
Locking and using a MultiXactId are orthogonal.
The rest is more or less the same that was in the original patch. I
feel this is in a OK state for review for possible inclusion. Some
testing is still needed regarding MultiXactId wraparound, and SLRU
truncation, and I haven't looked at whether documentation needs
updating.
--
Alvaro Herrera (<alvherre[@]dcc.uchile.cl>)
Jude: I wish humans laid eggs
Ringlord: Why would you want humans to lay eggs?
Jude: So I can eat them
Attachments:
shared-locks-2.patchtext/plain; charset=us-asciiDownload
Index: src/backend/access/heap/heapam.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/heap/heapam.c,v
retrieving revision 1.187
diff -c -r1.187 heapam.c
*** src/backend/access/heap/heapam.c 14 Apr 2005 20:03:22 -0000 1.187
--- src/backend/access/heap/heapam.c 24 Apr 2005 02:22:18 -0000
***************
*** 40,51 ****
--- 40,53 ----
#include "access/heapam.h"
#include "access/hio.h"
+ #include "access/multixact.h"
#include "access/tuptoaster.h"
#include "access/valid.h"
#include "access/xlogutils.h"
#include "catalog/catalog.h"
#include "catalog/namespace.h"
#include "miscadmin.h"
+ #include "storage/sinval.h"
#include "utils/inval.h"
#include "utils/relcache.h"
#include "pgstat.h"
***************
*** 1240,1248 ****
{
TransactionId xwait = HeapTupleHeaderGetXmax(tp.t_data);
! /* sleep until concurrent transaction ends */
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
! XactLockTableWait(xwait);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
if (!TransactionIdDidCommit(xwait))
--- 1242,1261 ----
{
TransactionId xwait = HeapTupleHeaderGetXmax(tp.t_data);
! /*
! * Sleep until concurrent transaction ends. Note that we don't care
! * if the locker has an exclusive or shared lock, because ours is
! * exclusive.
! */
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
!
! if (tp.t_data->t_infomask & HEAP_LOCKED)
! {
! if (tp.t_data->t_infomask & HEAP_XMAX_IS_MULTI)
! MultiXactIdWait(xwait);
! else
! XactLockTableWait(xwait);
! }
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
if (!TransactionIdDidCommit(xwait))
***************
*** 1260,1267 ****
tp.t_data->t_infomask |= HEAP_XMAX_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
}
! /* if tuple was marked for update but not updated... */
! if (tp.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE)
result = HeapTupleMayBeUpdated;
else
result = HeapTupleUpdated;
--- 1273,1280 ----
tp.t_data->t_infomask |= HEAP_XMAX_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
}
! /* if tuple was locked but not updated... */
! if (tp.t_data->t_infomask & HEAP_LOCKED)
result = HeapTupleMayBeUpdated;
else
result = HeapTupleUpdated;
***************
*** 1290,1296 ****
/* store transaction information of xact deleting the tuple */
tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_MARKED_FOR_UPDATE |
HEAP_MOVED);
HeapTupleHeaderSetXmax(tp.t_data, xid);
HeapTupleHeaderSetCmax(tp.t_data, cid);
--- 1303,1310 ----
/* store transaction information of xact deleting the tuple */
tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_XMAX_IS_MULTI |
! HEAP_LOCKED |
HEAP_MOVED);
HeapTupleHeaderSetXmax(tp.t_data, xid);
HeapTupleHeaderSetCmax(tp.t_data, cid);
***************
*** 1469,1475 ****
/* sleep until concurrent transaction ends */
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
! XactLockTableWait(xwait);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
if (!TransactionIdDidCommit(xwait))
--- 1483,1495 ----
/* sleep until concurrent transaction ends */
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
! if (oldtup.t_data->t_infomask & HEAP_LOCKED)
! {
! if (oldtup.t_data->t_infomask & HEAP_XMAX_IS_MULTI)
! MultiXactIdWait(xwait);
! else
! XactLockTableWait(xwait);
! }
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
if (!TransactionIdDidCommit(xwait))
***************
*** 1487,1494 ****
oldtup.t_data->t_infomask |= HEAP_XMAX_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
}
! /* if tuple was marked for update but not updated... */
! if (oldtup.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE)
result = HeapTupleMayBeUpdated;
else
result = HeapTupleUpdated;
--- 1507,1514 ----
oldtup.t_data->t_infomask |= HEAP_XMAX_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
}
! /* if tuple was locked but not updated... */
! if (oldtup.t_data->t_infomask & HEAP_LOCKED)
result = HeapTupleMayBeUpdated;
else
result = HeapTupleUpdated;
***************
*** 1556,1562 ****
{
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_MARKED_FOR_UPDATE |
HEAP_MOVED);
HeapTupleHeaderSetXmax(oldtup.t_data, xid);
HeapTupleHeaderSetCmax(oldtup.t_data, cid);
--- 1576,1583 ----
{
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_XMAX_IS_MULTI |
! HEAP_LOCKED |
HEAP_MOVED);
HeapTupleHeaderSetXmax(oldtup.t_data, xid);
HeapTupleHeaderSetCmax(oldtup.t_data, cid);
***************
*** 1642,1648 ****
{
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_MARKED_FOR_UPDATE |
HEAP_MOVED);
HeapTupleHeaderSetXmax(oldtup.t_data, xid);
HeapTupleHeaderSetCmax(oldtup.t_data, cid);
--- 1663,1670 ----
{
oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_XMAX_IS_MULTI |
! HEAP_LOCKED |
HEAP_MOVED);
HeapTupleHeaderSetXmax(oldtup.t_data, xid);
HeapTupleHeaderSetCmax(oldtup.t_data, cid);
***************
*** 1739,1751 ****
}
/*
! * heap_mark4update - mark a tuple for update
*/
HTSU_Result
! heap_mark4update(Relation relation, HeapTuple tuple, Buffer *buffer,
! CommandId cid)
{
! TransactionId xid = GetCurrentTransactionId();
ItemPointer tid = &(tuple->t_self);
ItemId lp;
PageHeader dp;
--- 1761,1773 ----
}
/*
! * heap_lock_tuple - lock a tuple in shared or exclusive mode
*/
HTSU_Result
! heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
! CommandId cid, LockTupleMode mode)
{
! TransactionId xid;
ItemPointer tid = &(tuple->t_self);
ItemId lp;
PageHeader dp;
***************
*** 1767,1803 ****
{
LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(*buffer);
! elog(ERROR, "attempted to mark4update invisible tuple");
}
else if (result == HeapTupleBeingUpdated)
{
! TransactionId xwait = HeapTupleHeaderGetXmax(tuple->t_data);
! /* sleep until concurrent transaction ends */
! LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
! XactLockTableWait(xwait);
! LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
! if (!TransactionIdDidCommit(xwait))
! goto l3;
! /*
! * xwait is committed but if xwait had just marked the tuple for
! * update then some other xaction could update this tuple before
! * we got to this point.
! */
! if (!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data), xwait))
! goto l3;
! if (!(tuple->t_data->t_infomask & HEAP_XMAX_COMMITTED))
! {
! tuple->t_data->t_infomask |= HEAP_XMAX_COMMITTED;
! SetBufferCommitInfoNeedsSave(*buffer);
}
- /* if tuple was marked for update but not updated... */
- if (tuple->t_data->t_infomask & HEAP_MARKED_FOR_UPDATE)
- result = HeapTupleMayBeUpdated;
- else
- result = HeapTupleUpdated;
}
if (result != HeapTupleMayBeUpdated)
{
--- 1789,1837 ----
{
LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(*buffer);
! elog(ERROR, "attempted to lock invisible tuple");
}
else if (result == HeapTupleBeingUpdated)
{
! if (mode == LockTupleShared &&
! (tuple->t_data->t_infomask & HEAP_XMAX_SHARED_LOCK))
! result = HeapTupleMayBeUpdated;
! else
! {
! TransactionId xwait = HeapTupleHeaderGetXmax(tuple->t_data);
! /* sleep until concurrent transaction ends */
! LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
! if (tuple->t_data->t_infomask & HEAP_LOCKED)
! {
! if (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)
! MultiXactIdWait(xwait);
! else
! XactLockTableWait(xwait);
! }
! LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
! if (!TransactionIdDidCommit(xwait))
! goto l3;
! /*
! * xwait is committed but if xwait had just marked the tuple for
! * update then some other xaction could update this tuple before
! * we got to this point.
! */
! if (!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data), xwait))
! goto l3;
! if (!(tuple->t_data->t_infomask & HEAP_XMAX_COMMITTED))
! {
! tuple->t_data->t_infomask |= HEAP_XMAX_COMMITTED;
! SetBufferCommitInfoNeedsSave(*buffer);
! }
! /* if tuple was marked for update but not updated... */
! if (tuple->t_data->t_infomask & HEAP_LOCKED)
! result = HeapTupleMayBeUpdated;
! else
! result = HeapTupleUpdated;
}
}
if (result != HeapTupleMayBeUpdated)
{
***************
*** 1808,1824 ****
}
/*
! * XLOG stuff: no logging is required as long as we have no
! * savepoints. For savepoints private log could be used...
*/
PageSetTLI(BufferGetPage(*buffer), ThisTimeLineID);
! /* store transaction information of xact marking the tuple */
tuple->t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
! HEAP_XMAX_INVALID |
HEAP_MOVED);
! tuple->t_data->t_infomask |= HEAP_MARKED_FOR_UPDATE;
! HeapTupleHeaderSetXmax(tuple->t_data, xid);
HeapTupleHeaderSetCmax(tuple->t_data, cid);
/* Make sure there is no forward chain link in t_ctid */
tuple->t_data->t_ctid = *tid;
--- 1842,1934 ----
}
/*
! * XLOG stuff: no logging is required as long as we don't have
! * two-phase commit.
*/
PageSetTLI(BufferGetPage(*buffer), ThisTimeLineID);
+ xid = GetCurrentTransactionId();
! /*
! * Store transaction information of xact marking the tuple. Don't reset
! * HEAP_XMAX_INVALID just yet because we will check it below.
! */
tuple->t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
! HEAP_LOCKED |
HEAP_MOVED);
!
! if (mode == LockTupleShared)
! {
! TransactionId xmax = HeapTupleHeaderGetXmax(tuple->t_data);
! TransactionId lockers;
!
! /* Set the minimum MultiXactId for this transaction */
! MultiXactIdSetMin();
! tuple->t_data->t_infomask |= HEAP_XMAX_SHARED_LOCK;
!
! /*
! * We rely on HeapTupleSatisfiesUpdate setting the HEAP_XMAX_INVALID
! * bit if the MultiXactId is not running.
! */
! if (!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID))
! {
! if (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)
! {
! /*
! * If the XMAX is already a MultiXactId, then we need to
! * expand it using the new Xid.
! */
! lockers = MultiXactIdExpand(xmax, true, xid);
! }
! else if (TransactionIdIsInProgress(xmax))
! {
! /*
! * If the old locker is ourselves, mark the tuple again
! * with our own TransactionId.
! */
! if (TransactionIdEquals(xmax, xid))
! lockers = xid;
! else
! {
! /*
! * If the Xmax is a valid TransactionId, then we need to
! * create a new MultiXactId that includes both the old
! * locker and the new xid.
! */
! lockers = MultiXactIdExpand(xmax, false, xid);
! tuple->t_data->t_infomask |= HEAP_XMAX_IS_MULTI;
! }
! }
! else
! {
! /*
! * This can happen iff HeapTupleSatisfiesUpdate saw the
! * MultiXactId as running, but then all the relevant
! * transactions finished before TransactionIdIsInProgress() got
! * to run. Treat it like there's no locker in the tuple.
! */
! lockers = xid;
! }
! }
! else
! {
! /*
! * There was no previous locker, so use a single TransactionId.
! */
! lockers = xid;
! }
!
! HeapTupleHeaderSetXmax(tuple->t_data, lockers);
! }
! else
! {
! /* Grab an exclusive lock on the tuple */
! tuple->t_data->t_infomask |= HEAP_XMAX_EXCLUSIVE_LOCK;
! Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
! HeapTupleHeaderSetXmax(tuple->t_data, xid);
! }
!
! /* Now we can reset this bit. */
! tuple->t_data->t_infomask &= ~(HEAP_XMAX_INVALID);
HeapTupleHeaderSetCmax(tuple->t_data, cid);
/* Make sure there is no forward chain link in t_ctid */
tuple->t_data->t_ctid = *tid;
***************
*** 1997,2003 ****
TransactionId xid[2]; /* xmax, xmin */
if (newtup->t_data->t_infomask & (HEAP_XMAX_INVALID |
! HEAP_MARKED_FOR_UPDATE))
xid[0] = InvalidTransactionId;
else
xid[0] = HeapTupleHeaderGetXmax(newtup->t_data);
--- 2107,2113 ----
TransactionId xid[2]; /* xmax, xmin */
if (newtup->t_data->t_infomask & (HEAP_XMAX_INVALID |
! HEAP_LOCKED))
xid[0] = InvalidTransactionId;
else
xid[0] = HeapTupleHeaderGetXmax(newtup->t_data);
***************
*** 2185,2191 ****
{
htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_MARKED_FOR_UPDATE |
HEAP_MOVED);
HeapTupleHeaderSetXmax(htup, record->xl_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId);
--- 2295,2302 ----
{
htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_XMAX_IS_MULTI |
! HEAP_LOCKED |
HEAP_MOVED);
HeapTupleHeaderSetXmax(htup, record->xl_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId);
***************
*** 2365,2371 ****
{
htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_MARKED_FOR_UPDATE |
HEAP_MOVED);
HeapTupleHeaderSetXmax(htup, record->xl_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId);
--- 2476,2483 ----
{
htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
HEAP_XMAX_INVALID |
! HEAP_XMAX_IS_MULTI |
! HEAP_LOCKED |
HEAP_MOVED);
HeapTupleHeaderSetXmax(htup, record->xl_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId);
Index: src/backend/access/transam/Makefile
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/Makefile,v
retrieving revision 1.19
diff -c -r1.19 Makefile
*** src/backend/access/transam/Makefile 1 Jul 2004 00:49:42 -0000 1.19
--- src/backend/access/transam/Makefile 24 Apr 2005 00:34:20 -0000
***************
*** 12,18 ****
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o
all: SUBSYS.o
--- 12,18 ----
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o multixact.o
all: SUBSYS.o
Index: src/backend/access/transam/varsup.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/varsup.c,v
retrieving revision 1.63
diff -c -r1.63 varsup.c
*** src/backend/access/transam/varsup.c 13 Apr 2005 18:54:56 -0000 1.63
--- src/backend/access/transam/varsup.c 24 Apr 2005 00:35:48 -0000
***************
*** 14,19 ****
--- 14,20 ----
#include "postgres.h"
#include "access/clog.h"
+ #include "access/multixact.h"
#include "access/subtrans.h"
#include "access/transam.h"
#include "miscadmin.h"
Index: src/backend/access/transam/xact.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/xact.c,v
retrieving revision 1.199
diff -c -r1.199 xact.c
*** src/backend/access/transam/xact.c 11 Apr 2005 19:51:14 -0000 1.199
--- src/backend/access/transam/xact.c 24 Apr 2005 00:35:48 -0000
***************
*** 20,25 ****
--- 20,26 ----
#include <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.188
diff -c -r1.188 xlog.c
*** src/backend/access/transam/xlog.c 23 Apr 2005 18:49:54 -0000 1.188
--- src/backend/access/transam/xlog.c 24 Apr 2005 01:29:18 -0000
***************
*** 23,28 ****
--- 23,29 ----
#include <sys/time.h>
#include "access/clog.h"
+ #include "access/multixact.h"
#include "access/subtrans.h"
#include "access/xact.h"
#include "access/xlog.h"
***************
*** 3534,3539 ****
--- 3535,3541 ----
checkPoint.ThisTimeLineID = ThisTimeLineID;
checkPoint.nextXid = FirstNormalTransactionId;
checkPoint.nextOid = FirstBootstrapObjectId;
+ checkPoint.nextMulti = FirstMultiXactId;
checkPoint.time = time(NULL);
ShmemVariableCache->nextXid = checkPoint.nextXid;
***************
*** 3613,3618 ****
--- 3615,3621 ----
/* Bootstrap the commit log, too */
BootStrapCLOG();
BootStrapSUBTRANS();
+ BootStrapMultiXact();
}
static char *
***************
*** 4187,4194 ****
checkPoint.undo.xlogid, checkPoint.undo.xrecoff,
wasShutdown ? "TRUE" : "FALSE")));
ereport(LOG,
! (errmsg("next transaction ID: %u; next OID: %u",
! checkPoint.nextXid, checkPoint.nextOid)));
if (!TransactionIdIsNormal(checkPoint.nextXid))
ereport(PANIC,
(errmsg("invalid next transaction ID")));
--- 4190,4197 ----
checkPoint.undo.xlogid, checkPoint.undo.xrecoff,
wasShutdown ? "TRUE" : "FALSE")));
ereport(LOG,
! (errmsg("next transaction ID: %u; next OID: %u; next MultiXactId: %u",
! checkPoint.nextXid, checkPoint.nextOid, checkPoint.nextMulti)));
if (!TransactionIdIsNormal(checkPoint.nextXid))
ereport(PANIC,
(errmsg("invalid next transaction ID")));
***************
*** 4549,4554 ****
--- 4552,4558 ----
/* Start up the commit log, too */
StartupCLOG();
StartupSUBTRANS();
+ StartupMultiXact(checkPoint.nextMulti);
ereport(LOG,
(errmsg("database system is ready")));
***************
*** 4737,4742 ****
--- 4741,4747 ----
CreateCheckPoint(true, true);
ShutdownCLOG();
ShutdownSUBTRANS();
+ ShutdownMultiXact();
CritSectionCount--;
ereport(LOG,
***************
*** 4919,4924 ****
--- 4924,4931 ----
checkPoint.nextOid += ShmemVariableCache->oidCount;
LWLockRelease(OidGenLock);
+ checkPoint.nextMulti = MultiXactGetCheckptMulti(shutdown);
+
/*
* Having constructed the checkpoint record, ensure all shmem disk
* buffers and commit-log buffers are flushed to disk.
***************
*** 4938,4943 ****
--- 4945,4951 ----
CheckPointCLOG();
CheckPointSUBTRANS();
+ CheckPointMultiXact();
FlushBufferPool();
START_CRIT_SECTION();
***************
*** 5057,5062 ****
--- 5065,5085 ----
}
/*
+ * Write a NEXT_MULTIXACT log record
+ */
+ void
+ XLogPutNextMultiXactId(MultiXactId nextMulti)
+ {
+ XLogRecData rdata;
+
+ rdata.buffer = InvalidBuffer;
+ rdata.data = (char *) (&nextMulti);
+ rdata.len = sizeof(MultiXactId);
+ rdata.next = NULL;
+ (void) XLogInsert(RM_XLOG_ID, XLOG_NEXTMULTI, &rdata);
+ }
+
+ /*
* XLOG resource manager's routines
*/
void
***************
*** 5075,5080 ****
--- 5098,5111 ----
ShmemVariableCache->oidCount = 0;
}
}
+ else if (info == XLOG_NEXTMULTI)
+ {
+ MultiXactId nextMulti;
+
+ memcpy(&nextMulti, XLogRecGetData(record), sizeof(MultiXactId));
+
+ MultiXactSetNextMXact(nextMulti);
+ }
else if (info == XLOG_CHECKPOINT_SHUTDOWN)
{
CheckPoint checkPoint;
***************
*** 5084,5089 ****
--- 5115,5121 ----
ShmemVariableCache->nextXid = checkPoint.nextXid;
ShmemVariableCache->nextOid = checkPoint.nextOid;
ShmemVariableCache->oidCount = 0;
+ MultiXactSetNextMXact(checkPoint.nextMulti);
/*
* TLI may change in a shutdown checkpoint, but it shouldn't
***************
*** 5115,5120 ****
--- 5147,5153 ----
ShmemVariableCache->nextOid = checkPoint.nextOid;
ShmemVariableCache->oidCount = 0;
}
+ MultiXactSetNextMXact(checkPoint.nextMulti);
/* TLI should not change in an on-line checkpoint */
if (checkPoint.ThisTimeLineID != ThisTimeLineID)
ereport(PANIC,
***************
*** 5153,5158 ****
--- 5186,5198 ----
memcpy(&nextOid, rec, sizeof(Oid));
sprintf(buf + strlen(buf), "nextOid: %u", nextOid);
}
+ else if (info == XLOG_NEXTMULTI)
+ {
+ MultiXactId multi;
+
+ memcpy(&multi, rec, sizeof(MultiXactId));
+ sprintf(buf + strlen(buf), "nextMultiXact: %u", multi);
+ }
else
strcat(buf, "UNKNOWN");
}
Index: src/backend/commands/portalcmds.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/portalcmds.c,v
retrieving revision 1.40
diff -c -r1.40 portalcmds.c
*** src/backend/commands/portalcmds.c 11 Apr 2005 15:59:34 -0000 1.40
--- src/backend/commands/portalcmds.c 24 Apr 2005 00:35:52 -0000
***************
*** 90,96 ****
if (query->rowMarks != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("DECLARE CURSOR ... FOR UPDATE is not supported"),
errdetail("Cursors must be READ ONLY.")));
plan = planner(query, true, stmt->options, NULL);
--- 90,96 ----
if (query->rowMarks != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
errdetail("Cursors must be READ ONLY.")));
plan = planner(query, true, stmt->options, NULL);
Index: src/backend/commands/trigger.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/trigger.c,v
retrieving revision 1.186
diff -c -r1.186 trigger.c
*** src/backend/commands/trigger.c 14 Apr 2005 20:03:24 -0000 1.186
--- src/backend/commands/trigger.c 24 Apr 2005 05:12:15 -0000
***************
*** 1597,1603 ****
*newSlot = NULL;
tuple.t_self = *tid;
ltrmark:;
! test = heap_mark4update(relation, &tuple, &buffer, cid);
switch (test)
{
case HeapTupleSelfUpdated:
--- 1597,1603 ----
*newSlot = NULL;
tuple.t_self = *tid;
ltrmark:;
! test = heap_lock_tuple(relation, &tuple, &buffer, cid, LockTupleExclusive);
switch (test)
{
case HeapTupleSelfUpdated:
***************
*** 1634,1643 ****
*/
return NULL;
! default:
ReleaseBuffer(buffer);
! elog(ERROR, "unrecognized heap_mark4update status: %u",
! test);
return NULL; /* keep compiler quiet */
}
}
--- 1634,1643 ----
*/
return NULL;
! case HeapTupleBeingUpdated:
! case HeapTupleInvisible:
ReleaseBuffer(buffer);
! elog(ERROR, "invalid heap_lock_tuple status %d", test);
return NULL; /* keep compiler quiet */
}
}
Index: src/backend/commands/vacuum.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuum.c,v
retrieving revision 1.306
diff -c -r1.306 vacuum.c
*** src/backend/commands/vacuum.c 14 Apr 2005 20:03:24 -0000 1.306
--- src/backend/commands/vacuum.c 24 Apr 2005 00:35:53 -0000
***************
*** 1800,1806 ****
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
OldestXmin)) ||
(!(tuple.t_data->t_infomask & (HEAP_XMAX_INVALID |
! HEAP_MARKED_FOR_UPDATE)) &&
!(ItemPointerEquals(&(tuple.t_self),
&(tuple.t_data->t_ctid)))))
{
--- 1800,1806 ----
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
OldestXmin)) ||
(!(tuple.t_data->t_infomask & (HEAP_XMAX_INVALID |
! HEAP_LOCKED)) &&
!(ItemPointerEquals(&(tuple.t_self),
&(tuple.t_data->t_ctid)))))
{
***************
*** 1839,1845 ****
* we have to move to the end of chain.
*/
while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID |
! HEAP_MARKED_FOR_UPDATE)) &&
!(ItemPointerEquals(&(tp.t_self),
&(tp.t_data->t_ctid))))
{
--- 1839,1845 ----
* we have to move to the end of chain.
*/
while (!(tp.t_data->t_infomask & (HEAP_XMAX_INVALID |
! HEAP_LOCKED)) &&
!(ItemPointerEquals(&(tp.t_self),
&(tp.t_data->t_ctid))))
{
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.246
diff -c -r1.246 execMain.c
*** src/backend/executor/execMain.c 14 Apr 2005 01:38:17 -0000 1.246
--- src/backend/executor/execMain.c 24 Apr 2005 05:13:12 -0000
***************
*** 567,572 ****
--- 567,574 ----
{
ListCell *l;
+ estate->es_forUpdate = parseTree->forUpdate;
+
foreach(l, parseTree->rowMarks)
{
Index rti = lfirst_int(l);
***************
*** 1126,1131 ****
--- 1128,1136 ----
* ctid!! */
tupleid = &tuple_ctid;
}
+ /*
+ * Process any FOR UPDATE or FOR SHARE locking requested.
+ */
else if (estate->es_rowMark != NIL)
{
ListCell *l;
***************
*** 1137,1142 ****
--- 1142,1148 ----
Buffer buffer;
HeapTupleData tuple;
TupleTableSlot *newSlot;
+ LockTupleMode lockmode;
HTSU_Result test;
if (!ExecGetJunkAttribute(junkfilter,
***************
*** 1151,1159 ****
if (isNull)
elog(ERROR, "\"%s\" is NULL", erm->resname);
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
! test = heap_mark4update(erm->relation, &tuple, &buffer,
! estate->es_snapshot->curcid);
ReleaseBuffer(buffer);
switch (test)
{
--- 1157,1171 ----
if (isNull)
elog(ERROR, "\"%s\" is NULL", erm->resname);
+ if (estate->es_forUpdate)
+ lockmode = LockTupleExclusive;
+ else
+ lockmode = LockTupleShared;
+
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
! test = heap_lock_tuple(erm->relation, &tuple, &buffer,
! estate->es_snapshot->curcid,
! lockmode);
ReleaseBuffer(buffer);
switch (test)
{
***************
*** 1189,1195 ****
goto lnext;
default:
! elog(ERROR, "unrecognized heap_mark4update status: %u",
test);
return (NULL);
}
--- 1201,1207 ----
goto lnext;
default:
! elog(ERROR, "unrecognized heap_lock_tuple status: %u",
test);
return (NULL);
}
***************
*** 2088,2093 ****
--- 2100,2106 ----
epqstate->es_param_exec_vals = (ParamExecData *)
palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData));
epqstate->es_rowMark = estate->es_rowMark;
+ epqstate->es_forUpdate = estate->es_forUpdate;
epqstate->es_instrument = estate->es_instrument;
epqstate->es_select_into = estate->es_select_into;
epqstate->es_into_oids = estate->es_into_oids;
Index: src/backend/executor/execUtils.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/executor/execUtils.c,v
retrieving revision 1.122
diff -c -r1.122 execUtils.c
*** src/backend/executor/execUtils.c 23 Apr 2005 21:32:34 -0000 1.122
--- src/backend/executor/execUtils.c 24 Apr 2005 00:53:50 -0000
***************
*** 198,203 ****
--- 198,204 ----
estate->es_processed = 0;
estate->es_lastoid = InvalidOid;
estate->es_rowMark = NIL;
+ estate->es_forUpdate = false;
estate->es_instrument = false;
estate->es_select_into = false;
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.302
diff -c -r1.302 copyfuncs.c
*** src/backend/nodes/copyfuncs.c 19 Apr 2005 22:35:13 -0000 1.302
--- src/backend/nodes/copyfuncs.c 24 Apr 2005 00:35:56 -0000
***************
*** 1606,1611 ****
--- 1606,1612 ----
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(jointree);
COPY_NODE_FIELD(rowMarks);
+ COPY_SCALAR_FIELD(forUpdate);
COPY_NODE_FIELD(targetList);
COPY_NODE_FIELD(groupClause);
COPY_NODE_FIELD(havingQual);
***************
*** 1686,1692 ****
COPY_NODE_FIELD(sortClause);
COPY_NODE_FIELD(limitOffset);
COPY_NODE_FIELD(limitCount);
! COPY_NODE_FIELD(forUpdate);
COPY_SCALAR_FIELD(op);
COPY_SCALAR_FIELD(all);
COPY_NODE_FIELD(larg);
--- 1687,1693 ----
COPY_NODE_FIELD(sortClause);
COPY_NODE_FIELD(limitOffset);
COPY_NODE_FIELD(limitCount);
! COPY_NODE_FIELD(lockingClause);
COPY_SCALAR_FIELD(op);
COPY_SCALAR_FIELD(all);
COPY_NODE_FIELD(larg);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.240
diff -c -r1.240 equalfuncs.c
*** src/backend/nodes/equalfuncs.c 7 Apr 2005 01:51:38 -0000 1.240
--- src/backend/nodes/equalfuncs.c 24 Apr 2005 00:35:56 -0000
***************
*** 642,647 ****
--- 642,648 ----
COMPARE_NODE_FIELD(rtable);
COMPARE_NODE_FIELD(jointree);
COMPARE_NODE_FIELD(rowMarks);
+ COMPARE_SCALAR_FIELD(forUpdate);
COMPARE_NODE_FIELD(targetList);
COMPARE_NODE_FIELD(groupClause);
COMPARE_NODE_FIELD(havingQual);
***************
*** 706,712 ****
COMPARE_NODE_FIELD(sortClause);
COMPARE_NODE_FIELD(limitOffset);
COMPARE_NODE_FIELD(limitCount);
! COMPARE_NODE_FIELD(forUpdate);
COMPARE_SCALAR_FIELD(op);
COMPARE_SCALAR_FIELD(all);
COMPARE_NODE_FIELD(larg);
--- 707,713 ----
COMPARE_NODE_FIELD(sortClause);
COMPARE_NODE_FIELD(limitOffset);
COMPARE_NODE_FIELD(limitCount);
! COMPARE_NODE_FIELD(lockingClause);
COMPARE_SCALAR_FIELD(op);
COMPARE_SCALAR_FIELD(all);
COMPARE_NODE_FIELD(larg);
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.249
diff -c -r1.249 outfuncs.c
*** src/backend/nodes/outfuncs.c 21 Apr 2005 19:18:12 -0000 1.249
--- src/backend/nodes/outfuncs.c 24 Apr 2005 00:54:55 -0000
***************
*** 1269,1275 ****
WRITE_NODE_FIELD(sortClause);
WRITE_NODE_FIELD(limitOffset);
WRITE_NODE_FIELD(limitCount);
! WRITE_NODE_FIELD(forUpdate);
WRITE_ENUM_FIELD(op, SetOperation);
WRITE_BOOL_FIELD(all);
WRITE_NODE_FIELD(larg);
--- 1269,1275 ----
WRITE_NODE_FIELD(sortClause);
WRITE_NODE_FIELD(limitOffset);
WRITE_NODE_FIELD(limitCount);
! WRITE_NODE_FIELD(lockingClause);
WRITE_ENUM_FIELD(op, SetOperation);
WRITE_BOOL_FIELD(all);
WRITE_NODE_FIELD(larg);
***************
*** 1386,1391 ****
--- 1386,1392 ----
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(jointree);
WRITE_NODE_FIELD(rowMarks);
+ WRITE_BOOL_FIELD(forUpdate);
WRITE_NODE_FIELD(targetList);
WRITE_NODE_FIELD(groupClause);
WRITE_NODE_FIELD(havingQual);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/nodes/readfuncs.c,v
retrieving revision 1.176
diff -c -r1.176 readfuncs.c
*** src/backend/nodes/readfuncs.c 6 Apr 2005 16:34:05 -0000 1.176
--- src/backend/nodes/readfuncs.c 24 Apr 2005 00:35:56 -0000
***************
*** 145,150 ****
--- 145,151 ----
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(jointree);
READ_NODE_FIELD(rowMarks);
+ READ_BOOL_FIELD(forUpdate);
READ_NODE_FIELD(targetList);
READ_NODE_FIELD(groupClause);
READ_NODE_FIELD(havingQual);
Index: src/backend/optimizer/path/allpaths.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/path/allpaths.c,v
retrieving revision 1.127
diff -c -r1.127 allpaths.c
*** src/backend/optimizer/path/allpaths.c 21 Apr 2005 19:18:12 -0000 1.127
--- src/backend/optimizer/path/allpaths.c 24 Apr 2005 00:47:15 -0000
***************
*** 215,227 ****
ListCell *il;
/*
! * XXX for now, can't handle inherited expansion of FOR UPDATE; can we
* do better?
*/
if (list_member_int(root->rowMarks, parentRTindex))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not supported for inheritance queries")));
/*
* Initialize to compute size estimates for whole inheritance tree
--- 215,227 ----
ListCell *il;
/*
! * XXX for now, can't handle inherited expansion of FOR UPDATE/SHARE; can we
* do better?
*/
if (list_member_int(root->rowMarks, parentRTindex))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries")));
/*
* Initialize to compute size estimates for whole inheritance tree
Index: src/backend/optimizer/plan/initsplan.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/plan/initsplan.c,v
retrieving revision 1.104
diff -c -r1.104 initsplan.c
*** src/backend/optimizer/plan/initsplan.c 31 Dec 2004 22:00:09 -0000 1.104
--- src/backend/optimizer/plan/initsplan.c 4 Apr 2005 22:07:58 -0000
***************
*** 323,333 ****
Assert(bms_is_subset(rel->outerjoinset, outerrels));
/*
! * Presently the executor cannot support FOR UPDATE marking of
* rels appearing on the nullable side of an outer join. (It's
* somewhat unclear what that would mean, anyway: what should we
* mark when a result row is generated from no element of the
! * nullable relation?) So, complain if target rel is FOR UPDATE.
* It's sufficient to make this check once per rel, so do it only
* if rel wasn't already known nullable.
*/
--- 323,333 ----
Assert(bms_is_subset(rel->outerjoinset, outerrels));
/*
! * Presently the executor cannot support FOR UPDATE/SHARE marking of
* rels appearing on the nullable side of an outer join. (It's
* somewhat unclear what that would mean, anyway: what should we
* mark when a result row is generated from no element of the
! * nullable relation?) So, complain if target rel is FOR UPDATE/SHARE.
* It's sufficient to make this check once per rel, so do it only
* if rel wasn't already known nullable.
*/
***************
*** 336,342 ****
if (list_member_int(root->rowMarks, relno))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE cannot be applied to the nullable side of an outer join")));
}
rel->outerjoinset = outerrels;
--- 336,342 ----
if (list_member_int(root->rowMarks, relno))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE/SHARE cannot be applied to the nullable side of an outer join")));
}
rel->outerjoinset = outerrels;
Index: src/backend/optimizer/plan/planner.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/plan/planner.c,v
retrieving revision 1.184
diff -c -r1.184 planner.c
*** src/backend/optimizer/plan/planner.c 11 Apr 2005 23:06:55 -0000 1.184
--- src/backend/optimizer/plan/planner.c 24 Apr 2005 00:35:58 -0000
***************
*** 635,647 ****
tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
/*
! * Can't handle FOR UPDATE here (parser should have checked
* already, but let's make sure).
*/
if (parse->rowMarks)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));
/*
* Calculate pathkeys that represent result ordering requirements
--- 635,647 ----
tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
/*
! * Can't handle FOR UPDATE/SHARE here (parser should have checked
* already, but let's make sure).
*/
if (parse->rowMarks)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
/*
* Calculate pathkeys that represent result ordering requirements
Index: src/backend/optimizer/prep/prepjointree.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/prep/prepjointree.c,v
retrieving revision 1.26
diff -c -r1.26 prepjointree.c
*** src/backend/optimizer/prep/prepjointree.c 6 Apr 2005 16:34:06 -0000 1.26
--- src/backend/optimizer/prep/prepjointree.c 24 Apr 2005 00:35:58 -0000
***************
*** 276,286 ****
parse->rtable = list_concat(parse->rtable, subquery->rtable);
/*
! * Pull up any FOR UPDATE markers, too. (OffsetVarNodes
* already adjusted the marker values, so just list_concat the
* list.)
*/
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
/*
* We also have to fix the relid sets of any parent
--- 276,287 ----
parse->rtable = list_concat(parse->rtable, subquery->rtable);
/*
! * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes
* already adjusted the marker values, so just list_concat the
* list.)
*/
parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks);
+ parse->forUpdate = subquery->forUpdate;
/*
* We also have to fix the relid sets of any parent
Index: src/backend/optimizer/prep/preptlist.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/optimizer/prep/preptlist.c,v
retrieving revision 1.74
diff -c -r1.74 preptlist.c
*** src/backend/optimizer/prep/preptlist.c 6 Apr 2005 16:34:06 -0000 1.74
--- src/backend/optimizer/prep/preptlist.c 24 Apr 2005 00:35:58 -0000
***************
*** 102,108 ****
}
/*
! * Add TID targets for rels selected FOR UPDATE. The executor
* uses the TID to know which rows to lock, much as for UPDATE or
* DELETE.
*/
--- 102,108 ----
}
/*
! * Add TID targets for rels selected FOR UPDATE/SHARE. The executor
* uses the TID to know which rows to lock, much as for UPDATE or
* DELETE.
*/
***************
*** 111,132 ****
ListCell *l;
/*
! * We've got trouble if the FOR UPDATE appears inside
* grouping, since grouping renders a reference to individual
* tuple CTIDs invalid. This is also checked at parse time,
* but that's insufficient because of rule substitution, query
* pullup, etc.
*/
! CheckSelectForUpdate(parse);
/*
! * Currently the executor only supports FOR UPDATE at top
* level
*/
if (PlannerQueryLevel > 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not allowed in subqueries")));
foreach(l, parse->rowMarks)
{
--- 111,132 ----
ListCell *l;
/*
! * We've got trouble if the FOR UPDATE/SHARE appears inside
* grouping, since grouping renders a reference to individual
* tuple CTIDs invalid. This is also checked at parse time,
* but that's insufficient because of rule substitution, query
* pullup, etc.
*/
! CheckSelectLocking(parse, parse->forUpdate);
/*
! * Currently the executor only supports FOR UPDATE/SHARE at top
* level
*/
if (PlannerQueryLevel > 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE/SHARE is not allowed in subqueries")));
foreach(l, parse->rowMarks)
{
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.320
diff -c -r1.320 analyze.c
*** src/backend/parser/analyze.c 14 Apr 2005 20:03:24 -0000 1.320
--- src/backend/parser/analyze.c 24 Apr 2005 00:35:59 -0000
***************
*** 134,140 ****
bool isAddConstraint);
static void applyColumnNames(List *dst, List *src);
static List *getSetColTypes(ParseState *pstate, Node *node);
! static void transformForUpdate(Query *qry, List *forUpdate);
static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void release_pstate_resources(ParseState *pstate);
--- 134,140 ----
bool isAddConstraint);
static void applyColumnNames(List *dst, List *src);
static List *getSetColTypes(ParseState *pstate, Node *node);
! static void transformLockingClause(Query *qry, List *lockingClause);
static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void release_pstate_resources(ParseState *pstate);
***************
*** 1810,1817 ****
qry->commandType = CMD_SELECT;
! /* make FOR UPDATE clause available to addRangeTableEntry */
! pstate->p_forUpdate = stmt->forUpdate;
/* process the FROM clause */
transformFromClause(pstate, stmt->fromClause);
--- 1810,1833 ----
qry->commandType = CMD_SELECT;
! /* make FOR UPDATE/FOR SHARE clause available to addRangeTableEntry */
! pstate->p_forUpdate = pstate->p_forShare = NULL;
!
! if (stmt->lockingClause)
! {
! /*
! * The first node in the list should be a T_String Value node
! * containing "for_update" or "for_share"
! */
! Value *type = linitial(stmt->lockingClause);
!
! if (strcmp(strVal(type), "for_update") == 0)
! pstate->p_forUpdate = list_copy_tail(stmt->lockingClause, 1);
! else if (strcmp(strVal(type), "for_share") == 0)
! pstate->p_forShare = list_copy_tail(stmt->lockingClause, 1);
! else
! elog(ERROR, "invalid first node in locking clause");
! }
/* process the FROM clause */
transformFromClause(pstate, stmt->fromClause);
***************
*** 1870,1877 ****
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
! if (stmt->forUpdate != NIL)
! transformForUpdate(qry, stmt->forUpdate);
return qry;
}
--- 1886,1893 ----
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
! if (stmt->lockingClause != NIL)
! transformLockingClause(qry, stmt->lockingClause);
return qry;
}
***************
*** 1899,1905 ****
List *sortClause;
Node *limitOffset;
Node *limitCount;
! List *forUpdate;
Node *node;
ListCell *left_tlist,
*dtlist;
--- 1915,1921 ----
List *sortClause;
Node *limitOffset;
Node *limitCount;
! List *lockingClause;
Node *node;
ListCell *left_tlist,
*dtlist;
***************
*** 1937,1954 ****
sortClause = stmt->sortClause;
limitOffset = stmt->limitOffset;
limitCount = stmt->limitCount;
! forUpdate = stmt->forUpdate;
stmt->sortClause = NIL;
stmt->limitOffset = NULL;
stmt->limitCount = NULL;
! stmt->forUpdate = NIL;
! /* We don't support forUpdate with set ops at the moment. */
! if (forUpdate)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));
/*
* Recursively transform the components of the tree.
--- 1953,1970 ----
sortClause = stmt->sortClause;
limitOffset = stmt->limitOffset;
limitCount = stmt->limitCount;
! lockingClause = stmt->lockingClause;
stmt->sortClause = NIL;
stmt->limitOffset = NULL;
stmt->limitCount = NULL;
! stmt->lockingClause = NIL;
! /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
! if (lockingClause)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
/*
* Recursively transform the components of the tree.
***************
*** 2083,2090 ****
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
! if (forUpdate != NIL)
! transformForUpdate(qry, forUpdate);
return qry;
}
--- 2099,2106 ----
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
parseCheckAggregates(pstate, qry);
! if (lockingClause != NIL)
! transformLockingClause(qry, lockingClause);
return qry;
}
***************
*** 2107,2114 ****
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
! /* We don't support forUpdate with set ops at the moment. */
! if (stmt->forUpdate)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));
--- 2123,2130 ----
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
! /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
! if (stmt->lockingClause)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));
***************
*** 2128,2134 ****
{
Assert(stmt->larg != NULL && stmt->rarg != NULL);
if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
! stmt->forUpdate)
isLeaf = true;
else
isLeaf = false;
--- 2144,2150 ----
{
Assert(stmt->larg != NULL && stmt->rarg != NULL);
if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
! stmt->lockingClause)
isLeaf = true;
else
isLeaf = false;
***************
*** 2711,2757 ****
/* exported so planner can check again after rewriting, query pullup, etc */
void
! CheckSelectForUpdate(Query *qry)
{
if (qry->setOperations)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));
if (qry->distinctClause != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not allowed with DISTINCT clause")));
if (qry->groupClause != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not allowed with GROUP BY clause")));
if (qry->havingQual != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not allowed with HAVING clause")));
if (qry->hasAggs)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("SELECT FOR UPDATE is not allowed with aggregate functions")));
}
/*
! * Convert FOR UPDATE name list into rowMarks list of integer relids
*
* NB: if you need to change this, see also markQueryForUpdate()
* in rewriteHandler.c.
*/
static void
! transformForUpdate(Query *qry, List *forUpdate)
{
! List *rowMarks = qry->rowMarks;
ListCell *l;
ListCell *rt;
Index i;
! CheckSelectForUpdate(qry);
! if (linitial(forUpdate) == NULL)
{
/* all regular tables used in query */
i = 0;
--- 2727,2800 ----
/* exported so planner can check again after rewriting, query pullup, etc */
void
! CheckSelectLocking(Query *qry, bool forUpdate)
{
+ char *operation;
+
+ if (forUpdate)
+ operation = "SELECT FOR UPDATE";
+ else
+ operation = "SELECT FOR SHARE";
+
if (qry->setOperations)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! /* translator: %s is a SQL command, like SELECT FOR UPDATE */
! errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", operation)));
if (qry->distinctClause != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! /* translator: %s is a SQL command, like SELECT FOR UPDATE */
! errmsg("%s is not allowed with DISTINCT clause", operation)));
if (qry->groupClause != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! /* translator: %s is a SQL command, like SELECT FOR UPDATE */
! errmsg("%s is not allowed with GROUP BY clause", operation)));
if (qry->havingQual != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! /* translator: %s is a SQL command, like SELECT FOR UPDATE */
! errmsg("%s is not allowed with HAVING clause", operation)));
if (qry->hasAggs)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! /* translator: %s is a SQL command, like SELECT FOR UPDATE */
! errmsg("%s is not allowed with aggregate functions", operation)));
}
/*
! * Convert FOR UPDATE/SHARE name list into rowMarks list of integer relids
*
* NB: if you need to change this, see also markQueryForUpdate()
* in rewriteHandler.c.
*/
static void
! transformLockingClause(Query *qry, List *lockingClause)
{
! List *rowMarks;
ListCell *l;
ListCell *rt;
Index i;
+ Value *type;
+ List *list_of_rels;
+
+ /* This should be a Value node containing "for_update" o "for_share" */
+ type = linitial(lockingClause);
+ if (strcmp(strVal(type), "for_update") == 0)
+ qry->forUpdate = true;
+ else if (strcmp(strVal(type), "for_share") == 0)
+ qry->forUpdate = false;
+ else
+ elog(ERROR, "invalid first node in locking clause");
+
+ CheckSelectLocking(qry, qry->forUpdate);
+
+ rowMarks = qry->rowMarks;
! list_of_rels = list_copy_tail(lockingClause, 1);
! if (linitial(list_of_rels) == NULL)
{
/* all regular tables used in query */
i = 0;
***************
*** 2773,2779 ****
* FOR UPDATE of subquery is propagated to subquery's
* rels
*/
! transformForUpdate(rte->subquery, list_make1(NULL));
break;
default:
/* ignore JOIN, SPECIAL, FUNCTION RTEs */
--- 2816,2822 ----
* FOR UPDATE of subquery is propagated to subquery's
* rels
*/
! transformLockingClause(rte->subquery, lcons(type, list_make1(NULL)));
break;
default:
/* ignore JOIN, SPECIAL, FUNCTION RTEs */
***************
*** 2784,2790 ****
else
{
/* just the named tables */
! foreach(l, forUpdate)
{
char *relname = strVal(lfirst(l));
--- 2827,2833 ----
else
{
/* just the named tables */
! foreach(l, list_of_rels)
{
char *relname = strVal(lfirst(l));
***************
*** 2809,2815 ****
* FOR UPDATE of subquery is propagated to
* subquery's rels
*/
! transformForUpdate(rte->subquery, list_make1(NULL));
break;
case RTE_JOIN:
ereport(ERROR,
--- 2852,2858 ----
* FOR UPDATE of subquery is propagated to
* subquery's rels
*/
! transformLockingClause(rte->subquery, lcons(type, list_make1(NULL)));
break;
case RTE_JOIN:
ereport(ERROR,
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.488
diff -c -r2.488 gram.y
*** src/backend/parser/gram.y 23 Apr 2005 17:22:16 -0000 2.488
--- src/backend/parser/gram.y 24 Apr 2005 00:55:22 -0000
***************
*** 87,93 ****
static List *extractArgTypes(List *parameters);
static SelectStmt *findLeftmostSelect(SelectStmt *node);
static void insertSelectOptions(SelectStmt *stmt,
! List *sortClause, List *forUpdate,
Node *limitOffset, Node *limitCount);
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
static Node *doNegate(Node *n);
--- 87,93 ----
static List *extractArgTypes(List *parameters);
static SelectStmt *findLeftmostSelect(SelectStmt *node);
static void insertSelectOptions(SelectStmt *stmt,
! List *sortClause, List *lockingClause,
Node *limitOffset, Node *limitCount);
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
static Node *doNegate(Node *n);
***************
*** 242,248 ****
%type <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 24 Apr 2005 00:36:00 -0000
***************
*** 40,46 ****
--- 40,48 ----
Oid relid);
static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
RangeTblEntry *rte1, const char *aliasname1);
+ static bool isForLocking(List *list, char *refname);
static bool isForUpdate(ParseState *pstate, char *refname);
+ static bool isForShare(ParseState *pstate, char *refname);
static void expandRelation(Oid relid, Alias *eref,
int rtindex, int sublevels_up,
bool include_dropped,
***************
*** 761,767 ****
* first access to a rel in a statement, be careful to get the right
* access level depending on whether we're doing SELECT FOR UPDATE.
*/
! lockmode = isForUpdate(pstate, refname) ? RowShareLock : AccessShareLock;
rel = heap_openrv(relation, lockmode);
rte->relid = RelationGetRelid(rel);
--- 763,770 ----
* first access to a rel in a statement, be careful to get the right
* access level depending on whether we're doing SELECT FOR UPDATE.
*/
! lockmode = (isForUpdate(pstate, refname) || isForShare(pstate, refname)) ?
! RowShareLock : AccessShareLock;
rel = heap_openrv(relation, lockmode);
rte->relid = RelationGetRelid(rel);
***************
*** 1121,1126 ****
--- 1124,1160 ----
}
/*
+ * Workhorse for isForUpdate and isForShare
+ */
+ static bool
+ isForLocking(List *list, char *refname)
+ {
+ if (list != NIL)
+ {
+ if (linitial(list) == NULL)
+ {
+ /* all tables used in query */
+ return true;
+ }
+ else
+ {
+ /* just the named tables */
+ ListCell *cell;
+
+ foreach(cell, list)
+ {
+ char *rname = strVal(lfirst(cell));
+
+ if (strcmp(refname, rname) == 0)
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ /*
* Has the specified refname been selected FOR UPDATE?
*/
static bool
***************
*** 1129,1155 ****
/* Outer loop to check parent query levels as well as this one */
while (pstate != NULL)
{
! if (pstate->p_forUpdate != NIL)
! {
! if (linitial(pstate->p_forUpdate) == NULL)
! {
! /* all tables used in query */
! return true;
! }
! else
! {
! /* just the named tables */
! ListCell *l;
! foreach(l, pstate->p_forUpdate)
! {
! char *rname = strVal(lfirst(l));
- if (strcmp(refname, rname) == 0)
- return true;
- }
- }
- }
pstate = pstate->parentParseState;
}
return false;
--- 1163,1188 ----
/* Outer loop to check parent query levels as well as this one */
while (pstate != NULL)
{
! if (isForLocking(pstate->p_forUpdate, refname))
! return true;
! pstate = pstate->parentParseState;
! }
! return false;
! }
!
! /*
! * Has the specified refname been selected FOR SHARE?
! */
! static bool
! isForShare(ParseState *pstate, char *refname)
! {
! /* Outer loop to check parent query levels as well as this one */
! while (pstate != NULL)
! {
! if (isForLocking(pstate->p_forUpdate, refname))
! return true;
pstate = pstate->parentParseState;
}
return false;
Index: src/backend/parser/parse_type.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/parse_type.c,v
retrieving revision 1.73
diff -c -r1.73 parse_type.c
*** src/backend/parser/parse_type.c 31 Dec 2004 22:00:27 -0000 1.73
--- src/backend/parser/parse_type.c 4 Apr 2005 22:07:58 -0000
***************
*** 432,438 ****
stmt->sortClause != NIL ||
stmt->limitOffset != NULL ||
stmt->limitCount != NULL ||
! stmt->forUpdate != NIL ||
stmt->op != SETOP_NONE)
goto fail;
if (list_length(stmt->targetList) != 1)
--- 432,438 ----
stmt->sortClause != NIL ||
stmt->limitOffset != NULL ||
stmt->limitCount != NULL ||
! stmt->lockingClause != NIL ||
stmt->op != SETOP_NONE)
goto fail;
if (list_length(stmt->targetList) != 1)
Index: src/backend/rewrite/rewriteHandler.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/rewrite/rewriteHandler.c,v
retrieving revision 1.150
diff -c -r1.150 rewriteHandler.c
*** src/backend/rewrite/rewriteHandler.c 6 Apr 2005 16:34:06 -0000 1.150
--- src/backend/rewrite/rewriteHandler.c 24 Apr 2005 00:36:02 -0000
***************
*** 51,57 ****
TargetEntry *prior_tle,
const char *attrName);
static Node *get_assignment_input(Node *node);
! static void markQueryForUpdate(Query *qry, bool skipOldNew);
static List *matchLocks(CmdType event, RuleLock *rulelocks,
int varno, Query *parsetree);
static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
--- 51,57 ----
TargetEntry *prior_tle,
const char *attrName);
static Node *get_assignment_input(Node *node);
! static void markQueryForLocking(Query *qry, bool skipOldNew);
static List *matchLocks(CmdType event, RuleLock *rulelocks,
int varno, Query *parsetree);
static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
***************
*** 745,780 ****
rte->checkAsUser = 0;
/*
! * FOR UPDATE of view?
*/
if (list_member_int(parsetree->rowMarks, rt_index))
{
/*
* Remove the view from the list of rels that will actually be
! * marked FOR UPDATE by the executor. It will still be access-
* checked for write access, though.
*/
parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index);
/*
! * Set up the view's referenced tables as if FOR UPDATE.
*/
! markQueryForUpdate(rule_action, true);
}
return parsetree;
}
/*
! * Recursively mark all relations used by a view as FOR UPDATE.
*
* This may generate an invalid query, eg if some sub-query uses an
* aggregate. We leave it to the planner to detect that.
*
! * NB: this must agree with the parser's transformForUpdate() routine.
*/
static void
! markQueryForUpdate(Query *qry, bool skipOldNew)
{
Index rti = 0;
ListCell *l;
--- 745,780 ----
rte->checkAsUser = 0;
/*
! * FOR UPDATE/SHARE of view?
*/
if (list_member_int(parsetree->rowMarks, rt_index))
{
/*
* Remove the view from the list of rels that will actually be
! * marked FOR UPDATE/SHARE by the executor. It will still be access-
* checked for write access, though.
*/
parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index);
/*
! * Set up the view's referenced tables as if FOR UPDATE/SHARE.
*/
! markQueryForLocking(rule_action, true);
}
return parsetree;
}
/*
! * Recursively mark all relations used by a view as FOR UPDATE/SHARE.
*
* This may generate an invalid query, eg if some sub-query uses an
* aggregate. We leave it to the planner to detect that.
*
! * NB: this must agree with the parser's transformLockingClause() routine.
*/
static void
! markQueryForLocking(Query *qry, bool skipOldNew)
{
Index rti = 0;
ListCell *l;
***************
*** 798,805 ****
}
else if (rte->rtekind == RTE_SUBQUERY)
{
! /* FOR UPDATE of subquery is propagated to subquery's rels */
! markQueryForUpdate(rte->subquery, false);
}
}
}
--- 798,805 ----
}
else if (rte->rtekind == RTE_SUBQUERY)
{
! /* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
! markQueryForLocking(rte->subquery, false);
}
}
}
***************
*** 911,917 ****
* If the relation is the query's result relation, then
* RewriteQuery() already got the right lock on it, so we need no
* additional lock. Otherwise, check to see if the relation is
! * accessed FOR UPDATE or not.
*/
if (rt_index == parsetree->resultRelation)
lockmode = NoLock;
--- 911,917 ----
* If the relation is the query's result relation, then
* RewriteQuery() already got the right lock on it, so we need no
* additional lock. Otherwise, check to see if the relation is
! * accessed FOR UPDATE/SHARE or not.
*/
if (rt_index == parsetree->resultRelation)
lockmode = NoLock;
Index: src/backend/storage/ipc/ipci.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/ipc/ipci.c,v
retrieving revision 1.74
diff -c -r1.74 ipci.c
*** src/backend/storage/ipc/ipci.c 31 Dec 2004 22:00:56 -0000 1.74
--- src/backend/storage/ipc/ipci.c 10 Apr 2005 23:29:45 -0000
***************
*** 15,20 ****
--- 15,21 ----
#include "postgres.h"
#include "access/clog.h"
+ #include "access/multixact.h"
#include "access/subtrans.h"
#include "access/xlog.h"
#include "miscadmin.h"
***************
*** 75,80 ****
--- 76,82 ----
size += XLOGShmemSize();
size += CLOGShmemSize();
size += SUBTRANSShmemSize();
+ size += MultiXactShmemSize();
size += LWLockShmemSize();
size += SInvalShmemSize(maxBackends);
size += FreeSpaceShmemSize();
***************
*** 140,145 ****
--- 142,148 ----
XLOGShmemInit();
CLOGShmemInit();
SUBTRANSShmemInit();
+ MultiXactShmemInit();
InitBufferPool();
/*
Index: src/backend/storage/lmgr/lwlock.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/lmgr/lwlock.c,v
retrieving revision 1.27
diff -c -r1.27 lwlock.c
*** src/backend/storage/lmgr/lwlock.c 8 Apr 2005 14:18:35 -0000 1.27
--- src/backend/storage/lmgr/lwlock.c 24 Apr 2005 00:36:03 -0000
***************
*** 114,119 ****
--- 114,125 ----
/* subtrans.c needs one per SubTrans buffer */
numLocks += NUM_SLRU_BUFFERS;
+ /*
+ * multixact.c needs one per MultiXact buffers, but there are
+ * two SLRU areas for MultiXact
+ */
+ numLocks += 2 * NUM_SLRU_BUFFERS;
+
/* Perhaps create a few more for use by user-defined modules? */
return numLocks;
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.235
diff -c -r1.235 utility.c
*** src/backend/tcop/utility.c 14 Apr 2005 01:38:18 -0000 1.235
--- src/backend/tcop/utility.c 24 Apr 2005 00:36:03 -0000
***************
*** 1663,1669 ****
if (parsetree->into != NULL)
tag = "SELECT INTO";
else if (parsetree->rowMarks != NIL)
! tag = "SELECT FOR UPDATE";
else
tag = "SELECT";
break;
--- 1663,1674 ----
if (parsetree->into != NULL)
tag = "SELECT INTO";
else if (parsetree->rowMarks != NIL)
! {
! if (parsetree->forUpdate)
! tag = "SELECT FOR UPDATE";
! else
! tag = "SELECT FOR SHARE";
! }
else
tag = "SELECT";
break;
Index: src/backend/utils/adt/ri_triggers.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/adt/ri_triggers.c,v
retrieving revision 1.76
diff -c -r1.76 ri_triggers.c
*** src/backend/utils/adt/ri_triggers.c 31 Dec 2004 22:01:22 -0000 1.76
--- src/backend/utils/adt/ri_triggers.c 4 Apr 2005 22:07:53 -0000
***************
*** 206,212 ****
* tuple.
*
* pk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR UPDATE will get on it.
*/
pk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
fk_rel = trigdata->tg_relation;
--- 206,212 ----
* tuple.
*
* pk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR SHARE will get on it.
*/
pk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
fk_rel = trigdata->tg_relation;
***************
*** 267,273 ****
* ----------
*/
quoteRelationName(pkrelname, pk_rel);
! snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x FOR UPDATE OF x",
pkrelname);
/* Prepare and save the plan */
--- 267,273 ----
* ----------
*/
quoteRelationName(pkrelname, pk_rel);
! snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x FOR SHARE OF x",
pkrelname);
/* Prepare and save the plan */
***************
*** 428,434 ****
queryoids[i] = SPI_gettypeid(fk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_FK_IDX]);
}
! strcat(querystr, " FOR UPDATE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
--- 428,434 ----
queryoids[i] = SPI_gettypeid(fk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_FK_IDX]);
}
! strcat(querystr, " FOR SHARE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
***************
*** 590,596 ****
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR UPDATE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
--- 590,596 ----
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR SHARE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
***************
*** 655,661 ****
* tuple.
*
* fk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR UPDATE will get on it.
*/
fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
pk_rel = trigdata->tg_relation;
--- 655,661 ----
* tuple.
*
* fk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR SHARE will get on it.
*/
fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
pk_rel = trigdata->tg_relation;
***************
*** 748,754 ****
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR UPDATE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
--- 748,754 ----
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR SHARE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
***************
*** 834,840 ****
* and old tuple.
*
* fk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR UPDATE will get on it.
*/
fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
pk_rel = trigdata->tg_relation;
--- 834,840 ----
* and old tuple.
*
* fk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR SHARE will get on it.
*/
fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
pk_rel = trigdata->tg_relation;
***************
*** 939,945 ****
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR UPDATE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
--- 939,945 ----
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR SHARE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
***************
*** 1373,1379 ****
* tuple.
*
* fk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR UPDATE will get on it.
*/
fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
pk_rel = trigdata->tg_relation;
--- 1373,1379 ----
* tuple.
*
* fk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR SHARE will get on it.
*/
fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
pk_rel = trigdata->tg_relation;
***************
*** 1453,1459 ****
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR UPDATE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
--- 1453,1459 ----
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR SHARE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
***************
*** 1543,1549 ****
* and old tuple.
*
* fk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR UPDATE will get on it.
*/
fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
pk_rel = trigdata->tg_relation;
--- 1543,1549 ----
* and old tuple.
*
* fk_rel is opened in RowShareLock mode since that's what our eventual
! * SELECT FOR SHARE will get on it.
*/
fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
pk_rel = trigdata->tg_relation;
***************
*** 1634,1640 ****
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR UPDATE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
--- 1634,1640 ----
queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
}
! strcat(querystr, " FOR SHARE OF x");
/* Prepare and save the plan */
qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
Index: src/backend/utils/time/tqual.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/time/tqual.c,v
retrieving revision 1.86
diff -c -r1.86 tqual.c
*** src/backend/utils/time/tqual.c 20 Mar 2005 23:40:27 -0000 1.86
--- src/backend/utils/time/tqual.c 24 Apr 2005 02:11:02 -0000
***************
*** 23,28 ****
--- 23,29 ----
#include "postgres.h"
+ #include "access/multixact.h"
#include "access/subtrans.h"
#include "storage/sinval.h"
#include "utils/tqual.h"
***************
*** 139,145 ****
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
return false;
--- 140,146 ----
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
return false;
***************
*** 167,206 ****
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
return false; /* updated by other */
}
! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
{
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
! return false;
! }
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
}
- return true;
- }
! /* xmax transaction committed */
!
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return true;
}
-
- tuple->t_infomask |= HEAP_XMAX_COMMITTED;
- SetBufferCommitInfoNeedsSave(buffer);
- return false;
}
/*
--- 168,215 ----
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
return false; /* updated by other */
}
! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
{
! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
! {
! if (tuple->t_infomask & HEAP_LOCKED)
! return true;
! return false;
! }
!
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
! }
return true;
! }
! /* xmax transaction committed */
!
! if (tuple->t_infomask & HEAP_LOCKED)
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
+ return true;
}
! tuple->t_infomask |= HEAP_XMAX_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
+ return false;
+ }
+ else
+ {
+ /* Xmax is a MultiXactId */
return true;
}
}
/*
***************
*** 310,316 ****
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
--- 319,325 ----
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
***************
*** 341,383 ****
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
return false;
}
! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
{
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
! if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
! return true; /* deleted after scan started */
! else
! return false; /* deleted before scan started */
! }
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
}
- return true;
- }
-
- /* xmax transaction committed */
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return true;
}
-
- tuple->t_infomask |= HEAP_XMAX_COMMITTED;
- SetBufferCommitInfoNeedsSave(buffer);
- return false;
}
/*
--- 350,400 ----
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
return false;
}
! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
{
! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
! {
! if (tuple->t_infomask & HEAP_LOCKED)
! return true;
! if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
! return true; /* deleted after scan started */
! else
! return false; /* deleted before scan started */
! }
!
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
! }
return true;
! }
! /* xmax transaction committed */
!
! if (tuple->t_infomask & HEAP_LOCKED)
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
+ return true;
}
! tuple->t_infomask |= HEAP_XMAX_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
+ return false;
+ }
+ else
+ {
+ /* Xmax is a MultiXactId */
return true;
}
}
/*
***************
*** 454,459 ****
--- 471,492 ----
* code, since UPDATE needs to know more than "is it visible?". Also,
* tuples of my own xact are tested against the passed CommandId not
* CurrentCommandId.
+ *
+ * The possible return codes are:
+ *
+ * HeapTupleInvisible: the tuple didn't exist at all when the scan started,
+ * e.g. it was created by a later CommandId.
+ *
+ * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be
+ * updated.
+ *
+ * HeapTupleSelfUpdated: The tuple was updated by the current transaction,
+ * after the current scan started.
+ *
+ * HeapTupleUpdated: The tuple was updated by a committed transaction.
+ *
+ * HeapTupleBeingUpdated: The tuple is being updated by an in-progress
+ * transaction other than the current transaction.
*/
HTSU_Result
HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
***************
*** 522,528 ****
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return HeapTupleMayBeUpdated;
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
--- 555,561 ----
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_LOCKED)
return HeapTupleMayBeUpdated;
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
***************
*** 555,600 ****
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return HeapTupleMayBeUpdated;
return HeapTupleUpdated; /* updated by other */
}
! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
{
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
! return HeapTupleMayBeUpdated;
! if (HeapTupleHeaderGetCmax(tuple) >= curcid)
! return HeapTupleSelfUpdated; /* updated after scan
! * started */
! else
! return HeapTupleInvisible; /* updated before scan started */
! }
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleMayBeUpdated;
}
- /* running xact */
- return HeapTupleBeingUpdated; /* in updation by other */
- }
-
- /* xmax transaction committed */
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
! return HeapTupleMayBeUpdated;
}
-
- tuple->t_infomask |= HEAP_XMAX_COMMITTED;
- SetBufferCommitInfoNeedsSave(buffer);
- return HeapTupleUpdated; /* updated by other */
}
/*
--- 588,651 ----
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
! if (tuple->t_infomask & HEAP_LOCKED)
return HeapTupleMayBeUpdated;
return HeapTupleUpdated; /* updated by other */
}
! /*
! * If Xmax isn't a MultiXactId, treat it like a normal TransactionId.
! */
! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
{
! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
! {
! if (tuple->t_infomask & HEAP_LOCKED)
! return HeapTupleMayBeUpdated;
! if (HeapTupleHeaderGetCmax(tuple) >= curcid)
! return HeapTupleSelfUpdated; /* updated after scan
! * started */
! else
! return HeapTupleInvisible; /* updated before scan started */
! }
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
! return HeapTupleMayBeUpdated;
! }
! /* running xact */
! return HeapTupleBeingUpdated; /* in updation by other */
! }
!
! /* xmax transaction committed */
!
! if (tuple->t_infomask & HEAP_LOCKED)
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleMayBeUpdated;
}
! tuple->t_infomask |= HEAP_XMAX_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
! return HeapTupleUpdated; /* updated by other */
! }
! else
! {
! /* Xmax is a MultiXactId */
! if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple)))
! return HeapTupleBeingUpdated;
! else
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
! return HeapTupleMayBeUpdated;
! }
}
}
/*
***************
*** 679,685 ****
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
return false;
--- 730,736 ----
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
return false;
***************
*** 710,716 ****
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
SnapshotDirty->tid = tuple->t_ctid;
return false; /* updated by other */
--- 761,767 ----
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
{
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
SnapshotDirty->tid = tuple->t_ctid;
return false; /* updated by other */
***************
*** 718,754 ****
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
{
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
return false;
}
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return true;
}
- /* running xact */
- SnapshotDirty->xmax = HeapTupleHeaderGetXmax(tuple);
- return true; /* in updation by other */
- }
! /* xmax transaction committed */
!
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return true;
}
-
- tuple->t_infomask |= HEAP_XMAX_COMMITTED;
- SetBufferCommitInfoNeedsSave(buffer);
- SnapshotDirty->tid = tuple->t_ctid;
- return false; /* updated by other */
}
/*
--- 769,813 ----
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
{
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
return false;
}
! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
{
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
! return true;
! }
! /* running xact */
! SnapshotDirty->xmax = HeapTupleHeaderGetXmax(tuple);
! return true; /* in updation by other */
! }
!
! /* xmax transaction committed */
!
! if (tuple->t_infomask & HEAP_LOCKED)
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return true;
}
! tuple->t_infomask |= HEAP_XMAX_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
+ SnapshotDirty->tid = tuple->t_ctid;
+ return false; /* updated by other */
+ }
+ else
+ {
+ /* Xmax is a MultiXactId */
return true;
}
}
/*
***************
*** 839,845 ****
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
--- 898,904 ----
Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
***************
*** 902,933 ****
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
return true;
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return true;
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
{
! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
{
! if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
! return true; /* deleted after scan started */
! else
! return false; /* deleted before scan started */
! }
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
}
return true;
}
-
- /* xmax transaction committed */
- tuple->t_infomask |= HEAP_XMAX_COMMITTED;
- SetBufferCommitInfoNeedsSave(buffer);
}
/*
--- 961,1000 ----
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
return true;
! if (tuple->t_infomask & HEAP_LOCKED)
return true;
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
{
! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
{
! if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
! {
! if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
! return true; /* deleted after scan started */
! else
! return false; /* deleted before scan started */
! }
! if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{
! if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
! {
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
! }
! return true;
}
+
+ /* xmax transaction committed */
+ tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+ SetBufferCommitInfoNeedsSave(buffer);
+ }
+ else
+ {
+ /* Xmax is a MultiXactId */
return true;
}
}
/*
***************
*** 1043,1049 ****
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return HEAPTUPLE_INSERT_IN_PROGRESS;
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
return HEAPTUPLE_INSERT_IN_PROGRESS;
/* inserted and then deleted by same xact */
return HEAPTUPLE_DELETE_IN_PROGRESS;
--- 1110,1116 ----
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return HEAPTUPLE_INSERT_IN_PROGRESS;
! if (tuple->t_infomask & HEAP_LOCKED)
return HEAPTUPLE_INSERT_IN_PROGRESS;
/* inserted and then deleted by same xact */
return HEAPTUPLE_DELETE_IN_PROGRESS;
***************
*** 1074,1080 ****
if (tuple->t_infomask & HEAP_XMAX_INVALID)
return HEAPTUPLE_LIVE;
! if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
{
/*
* "Deleting" xact really only marked it for update, so the tuple
--- 1141,1147 ----
if (tuple->t_infomask & HEAP_XMAX_INVALID)
return HEAPTUPLE_LIVE;
! if (tuple->t_infomask & HEAP_LOCKED)
{
/*
* "Deleting" xact really only marked it for update, so the tuple
***************
*** 1100,1124 ****
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
{
! if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
! return HEAPTUPLE_DELETE_IN_PROGRESS;
! else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{
! tuple->t_infomask |= HEAP_XMAX_COMMITTED;
! SetBufferCommitInfoNeedsSave(buffer);
}
else
{
! /*
! * Not in Progress, Not Committed, so either Aborted or
! * crashed
! */
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
return HEAPTUPLE_LIVE;
}
- /* Should only get here if we set XMAX_COMMITTED */
- Assert(tuple->t_infomask & HEAP_XMAX_COMMITTED);
}
/*
--- 1167,1199 ----
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
{
! if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI))
{
! if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
! return HEAPTUPLE_DELETE_IN_PROGRESS;
! else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
! {
! tuple->t_infomask |= HEAP_XMAX_COMMITTED;
! SetBufferCommitInfoNeedsSave(buffer);
! }
! else
! {
! /*
! * Not in Progress, Not Committed, so either Aborted or
! * crashed
! */
! tuple->t_infomask |= HEAP_XMAX_INVALID;
! SetBufferCommitInfoNeedsSave(buffer);
! return HEAPTUPLE_LIVE;
! }
! /* Should only get here if we set XMAX_COMMITTED */
! Assert(tuple->t_infomask & HEAP_XMAX_COMMITTED);
}
else
{
! /* Xmax is a MultiXactId */
return HEAPTUPLE_LIVE;
}
}
/*
Index: src/bin/initdb/initdb.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/bin/initdb/initdb.c,v
retrieving revision 1.81
diff -c -r1.81 initdb.c
*** src/bin/initdb/initdb.c 12 Apr 2005 19:29:24 -0000 1.81
--- src/bin/initdb/initdb.c 24 Apr 2005 00:36:10 -0000
***************
*** 527,533 ****
{
/*
* POSIX 1003.2: For each dir operand that does not name an
! * existing directory, effects equivalent to those cased by
* the following command shall occcur:
*
* mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode]
--- 527,533 ----
{
/*
* POSIX 1003.2: For each dir operand that does not name an
! * existing directory, effects equivalent to those caused by
* the following command shall occcur:
*
* mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode]
***************
*** 2120,2130 ****
char *pg_data_native;
static const char *subdirs[] = {
"global",
- "pg_xlog",
"pg_xlog/archive_status",
"pg_clog",
"pg_subtrans",
! "base",
"base/1",
"pg_tblspc"
};
--- 2120,2130 ----
char *pg_data_native;
static const char *subdirs[] = {
"global",
"pg_xlog/archive_status",
"pg_clog",
"pg_subtrans",
! "pg_multixact/members",
! "pg_multixact/offsets",
"base/1",
"pg_tblspc"
};
Index: src/bin/pg_controldata/pg_controldata.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/bin/pg_controldata/pg_controldata.c,v
retrieving revision 1.22
diff -c -r1.22 pg_controldata.c
*** src/bin/pg_controldata/pg_controldata.c 29 Mar 2005 03:01:32 -0000 1.22
--- src/bin/pg_controldata/pg_controldata.c 12 Apr 2005 03:42:00 -0000
***************
*** 165,170 ****
--- 165,171 ----
printf(_("Latest checkpoint's TimeLineID: %u\n"), ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's NextXID: %u\n"), ControlFile.checkPointCopy.nextXid);
printf(_("Latest checkpoint's NextOID: %u\n"), ControlFile.checkPointCopy.nextOid);
+ printf(_("Latest checkpoint's NextMultiXactId: %u\n"), ControlFile.checkPointCopy.nextMulti);
printf(_("Time of latest checkpoint: %s\n"), ckpttime_str);
printf(_("Database block size: %u\n"), ControlFile.blcksz);
printf(_("Blocks per segment of large relation: %u\n"), ControlFile.relseg_size);
Index: src/include/c.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/c.h,v
retrieving revision 1.181
diff -c -r1.181 c.h
*** src/include/c.h 29 Mar 2005 00:17:16 -0000 1.181
--- src/include/c.h 18 Apr 2005 03:49:54 -0000
***************
*** 365,371 ****
typedef double float8;
/*
! * Oid, RegProcedure, TransactionId, SubTransactionId, CommandId, AclId
*/
/* typedef Oid is in postgres_ext.h */
--- 365,372 ----
typedef double float8;
/*
! * Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId,
! * CommandId, AclId
*/
/* typedef Oid is in postgres_ext.h */
***************
*** 384,389 ****
--- 385,392 ----
#define InvalidSubTransactionId ((SubTransactionId) 0)
#define TopSubTransactionId ((SubTransactionId) 1)
+ typedef TransactionId MultiXactId;
+
typedef uint32 CommandId;
#define FirstCommandId ((CommandId) 0)
Index: src/include/access/heapam.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/access/heapam.h,v
retrieving revision 1.99
diff -c -r1.99 heapam.h
*** src/include/access/heapam.h 14 Apr 2005 20:03:27 -0000 1.99
--- src/include/access/heapam.h 24 Apr 2005 00:36:13 -0000
***************
*** 123,128 ****
--- 123,134 ----
/* heapam.c */
+ typedef enum
+ {
+ LockTupleShared,
+ LockTupleExclusive
+ } LockTupleMode;
+
extern Relation relation_open(Oid relationId, LOCKMODE lockmode);
extern Relation conditional_relation_open(Oid relationId, LOCKMODE lockmode, bool nowait);
extern Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode);
***************
*** 155,162 ****
CommandId cid, Snapshot crosscheck, bool wait);
extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple tup,
ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait);
! extern HTSU_Result heap_mark4update(Relation relation, HeapTuple tup,
! Buffer *userbuf, CommandId cid);
extern Oid simple_heap_insert(Relation relation, HeapTuple tup);
extern void simple_heap_delete(Relation relation, ItemPointer tid);
--- 161,168 ----
CommandId cid, Snapshot crosscheck, bool wait);
extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple tup,
ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait);
! extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tup,
! Buffer *userbuf, CommandId cid, LockTupleMode mode);
extern Oid simple_heap_insert(Relation relation, HeapTuple tup);
extern void simple_heap_delete(Relation relation, ItemPointer tid);
Index: src/include/access/htup.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/access/htup.h,v
retrieving revision 1.73
diff -c -r1.73 htup.h
*** src/include/access/htup.h 28 Mar 2005 01:50:34 -0000 1.73
--- src/include/access/htup.h 24 Apr 2005 00:34:21 -0000
***************
*** 152,163 ****
* attribute(s) */
#define HEAP_HASEXTENDED 0x000C /* the two above combined */
#define HEAP_HASOID 0x0010 /* has an object-id field */
! /* 0x0020, 0x0040 and 0x0080 are unused */
#define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */
#define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */
#define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */
#define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */
! #define HEAP_MARKED_FOR_UPDATE 0x1000 /* marked for UPDATE */
#define HEAP_UPDATED 0x2000 /* this is UPDATEd version of row */
#define HEAP_MOVED_OFF 0x4000 /* moved to another place by
* VACUUM FULL */
--- 152,168 ----
* attribute(s) */
#define HEAP_HASEXTENDED 0x000C /* the two above combined */
#define HEAP_HASOID 0x0010 /* has an object-id field */
! #define HEAP_XMAX_EXCLUSIVE_LOCK 0x0020 /* xmax is exclusive locker */
! #define HEAP_XMAX_SHARED_LOCK 0x0040 /* xmax is shared locker */
! #define HEAP_LOCKED (HEAP_XMAX_EXCLUSIVE_LOCK | HEAP_XMAX_SHARED_LOCK)
!
! #define HEAP_XMAX_IS_MULTI 0x0080 /* xmax is a MultiXactId */
!
#define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */
#define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */
#define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */
#define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */
! /* 0x1000 is presently unused */
#define HEAP_UPDATED 0x2000 /* this is UPDATEd version of row */
#define HEAP_MOVED_OFF 0x4000 /* moved to another place by
* VACUUM FULL */
***************
*** 165,171 ****
* VACUUM FULL */
#define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN)
! #define HEAP_XACT_MASK 0xFFC0 /* visibility-related bits */
/*
--- 170,176 ----
* VACUUM FULL */
#define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN)
! #define HEAP_XACT_MASK 0xEFE0 /* visibility-related bits */
/*
Index: src/include/access/xlog.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/access/xlog.h,v
retrieving revision 1.59
diff -c -r1.59 xlog.h
*** src/include/access/xlog.h 31 Dec 2004 22:03:21 -0000 1.59
--- src/include/access/xlog.h 18 Apr 2005 21:57:29 -0000
***************
*** 133,138 ****
--- 133,139 ----
extern void InitXLOGAccess(void);
extern void CreateCheckPoint(bool shutdown, bool force);
extern void XLogPutNextOid(Oid nextOid);
+ extern void XLogPutNextMultiXactId(MultiXactId multi);
extern XLogRecPtr GetRedoRecPtr(void);
#endif /* XLOG_H */
Index: src/include/catalog/pg_control.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/pg_control.h,v
retrieving revision 1.20
diff -c -r1.20 pg_control.h
*** src/include/catalog/pg_control.h 29 Mar 2005 03:01:32 -0000 1.20
--- src/include/catalog/pg_control.h 24 Apr 2005 05:19:05 -0000
***************
*** 22,28 ****
/* Version identifier for this pg_control format */
! #define PG_CONTROL_VERSION 74
/*
* Body of CheckPoint XLOG records. This is declared here because we keep
--- 22,28 ----
/* Version identifier for this pg_control format */
! #define PG_CONTROL_VERSION 75
/*
* Body of CheckPoint XLOG records. This is declared here because we keep
***************
*** 39,50 ****
--- 39,52 ----
TimeLineID ThisTimeLineID; /* current TLI */
TransactionId nextXid; /* next free XID */
Oid nextOid; /* next free OID */
+ MultiXactId nextMulti; /* next free MultiXactId */
time_t time; /* time stamp of checkpoint */
} CheckPoint;
/* XLOG info values for XLOG rmgr */
#define XLOG_CHECKPOINT_SHUTDOWN 0x00
#define XLOG_CHECKPOINT_ONLINE 0x10
+ #define XLOG_NEXTMULTI 0x20
#define XLOG_NEXTOID 0x30
Index: src/include/nodes/execnodes.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/execnodes.h,v
retrieving revision 1.127
diff -c -r1.127 execnodes.h
*** src/include/nodes/execnodes.h 20 Apr 2005 15:48:36 -0000 1.127
--- src/include/nodes/execnodes.h 24 Apr 2005 00:58:26 -0000
***************
*** 318,323 ****
--- 318,324 ----
uint32 es_processed; /* # of tuples processed */
Oid es_lastoid; /* last oid processed (by INSERT) */
List *es_rowMark; /* not good place, but there is no other */
+ bool es_forUpdate; /* was it FOR UPDATE or FOR SHARE */
bool es_instrument; /* true requests runtime instrumentation */
bool es_select_into; /* true if doing SELECT INTO */
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.277
diff -c -r1.277 parsenodes.h
*** src/include/nodes/parsenodes.h 7 Apr 2005 01:51:40 -0000 1.277
--- src/include/nodes/parsenodes.h 24 Apr 2005 00:36:17 -0000
***************
*** 91,97 ****
* clauses) */
List *rowMarks; /* integer list of RT indexes of relations
! * that are selected FOR UPDATE */
List *targetList; /* target list (of TargetEntry) */
--- 91,100 ----
* clauses) */
List *rowMarks; /* integer list of RT indexes of relations
! * that are selected FOR UPDATE/SHARE */
!
! bool forUpdate; /* true if rowMarks are FOR UPDATE,
! * false if they are FOR SHARE */
List *targetList; /* target list (of TargetEntry) */
***************
*** 688,694 ****
List *sortClause; /* sort clause (a list of SortBy's) */
Node *limitOffset; /* # of result tuples to skip */
Node *limitCount; /* # of result tuples to return */
! List *forUpdate; /* FOR UPDATE clause */
/*
* These fields are used only in upper-level SelectStmts.
--- 691,697 ----
List *sortClause; /* sort clause (a list of SortBy's) */
Node *limitOffset; /* # of result tuples to skip */
Node *limitCount; /* # of result tuples to return */
! List *lockingClause; /* FOR UPDATE or FOR SHARE clauses */
/*
* These fields are used only in upper-level SelectStmts.
Index: src/include/parser/analyze.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/parser/analyze.h,v
retrieving revision 1.29
diff -c -r1.29 analyze.h
*** src/include/parser/analyze.h 31 Dec 2004 22:03:38 -0000 1.29
--- src/include/parser/analyze.h 4 Apr 2005 22:08:03 -0000
***************
*** 21,26 ****
int *numParams);
extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
! extern void CheckSelectForUpdate(Query *qry);
#endif /* ANALYZE_H */
--- 21,26 ----
int *numParams);
extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
! extern void CheckSelectLocking(Query *qry, bool forUpdate);
#endif /* ANALYZE_H */
Index: src/include/parser/parse_node.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/parser/parse_node.h,v
retrieving revision 1.42
diff -c -r1.42 parse_node.h
*** src/include/parser/parse_node.h 31 Dec 2004 22:03:38 -0000 1.42
--- src/include/parser/parse_node.h 4 Apr 2005 22:08:03 -0000
***************
*** 54,59 ****
--- 54,60 ----
int p_numparams; /* allocated size of p_paramtypes[] */
int p_next_resno; /* next targetlist resno to assign */
List *p_forUpdate; /* FOR UPDATE clause, if any (see gram.y) */
+ List *p_forShare; /* FOR SHARE clause, if any (see gram.y) */
Node *p_value_substitute; /* what to replace VALUE with, if
* any */
bool p_variableparams;
Index: src/include/storage/lwlock.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/storage/lwlock.h,v
retrieving revision 1.17
diff -c -r1.17 lwlock.h
*** src/include/storage/lwlock.h 4 Mar 2005 20:21:07 -0000 1.17
--- src/include/storage/lwlock.h 11 Apr 2005 23:07:34 -0000
***************
*** 40,45 ****
--- 40,48 ----
CheckpointStartLock,
CLogControlLock,
SubtransControlLock,
+ MultiXactGenLock,
+ MultiXactOffsetControlLock,
+ MultiXactMemberControlLock,
RelCacheInitLock,
BgWriterCommLock,
Found another interesting thing while testing this. I got a core dump
from the Assert in GetMultiXactIdMembers, complaining that it was being
asked about a MultiXactId >= nextMXact. Sure enough, there was a
multixact on disk, left over from a previous core-dumped test, that was
larger than the nextMXact the current postmaster had started with.
My interpretation of this is that the MultiXact code is violating the
fundamental WAL rule, namely it is allowing data (multixact IDs in data
pages) to reach disk before the relevant WAL record (here the NEXTMULTI
record that should have advanced nextMXact) got to disk. It is very
easy for this to happen in the current system if the buffer page LSNs
aren't updated properly, because the bgwriter will be industriously
dumping dirty pages in the background.
AFAICS there isn't any very convenient way of propagating the true
location of the NEXTMULTI record into the page LSNs of the buffers that
heap_lock_tuple might stick relevant multi IDs into. What's probably
the easiest solution is for XLogPutNextMultiXactId to XLogFlush the
NEXTMULTI record before it returns. This is a mite annoying for
concurrency (because we'll have to hold MultiXactGenLock while flushing
xlog) but it should occur rarely enough to not be a huge deal.
At this point you're probably wondering why OID generation hasn't got
exactly the same problem, seeing that you borrowed all this logic from
the OID generator. The answer is that it would have the same problem,
except that an OID can only get onto disk as part of a tuple insert or
update, and all such events generate xlog records that must follow any
relevant NEXTOID record. Those records *will* get into the page LSNs,
and so the WAL rule is enforced.
So the problem would go away if heap_lock_tuple were generating any xlog
record of its own, which it might be doing by the time the 2PC dust
settles.
Plan B would be to decide that a multi ID that's >= nextMXact isn't
worthy of an Assert failure, but ought to be treated as just a dead
multixact. I'm kind of inclined to do that anyway, because I am not
convinced that this code guarantees no wraparound of multi IDs.
Thoughts?
regards, tom lane
Import Notes
Reply to msg id not found: 20050427204802.GE27525@dcc.uchile.cl