aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-04-29 22:28:24 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-04-29 22:28:24 +0000
commit3a694bb0a16fea1662f1ffd31506a72effdd4a93 (patch)
tree50bbf16b3117aada49b2709f524b3bdcf1a36815 /src
parent32d3b47e6f05c7137debddb68730a25fe1bb0cd6 (diff)
downloadpostgresql-3a694bb0a16fea1662f1ffd31506a72effdd4a93.tar.gz
postgresql-3a694bb0a16fea1662f1ffd31506a72effdd4a93.zip
Restructure LOCKTAG as per discussions of a couple months ago.
Essentially, we shoehorn in a lockable-object-type field by taking a byte away from the lockmethodid, which can surely fit in one byte instead of two. This allows less artificial definitions of all the other fields of LOCKTAG; we can get rid of the special pg_xactlock pseudo-relation, and also support locks on individual tuples and general database objects (including shared objects). None of those possibilities are actually exploited just yet, however. I removed pg_xactlock from pg_class, but did not force initdb for that change. At this point, relkind 's' (SPECIAL) is unused and could be removed entirely.
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/heap/hio.c11
-rw-r--r--src/backend/access/nbtree/nbtpage.c6
-rw-r--r--src/backend/storage/lmgr/README34
-rw-r--r--src/backend/storage/lmgr/deadlock.c101
-rw-r--r--src/backend/storage/lmgr/lmgr.c124
-rw-r--r--src/backend/storage/lmgr/lock.c84
-rw-r--r--src/backend/utils/adt/lockfuncs.c32
-rw-r--r--src/include/catalog/pg_attribute.h12
-rw-r--r--src/include/catalog/pg_class.h7
-rw-r--r--src/include/storage/lmgr.h8
-rw-r--r--src/include/storage/lock.h112
11 files changed, 319 insertions, 212 deletions
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index 27af323ed11..db0bda6699b 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.54 2004/12/31 21:59:16 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.55 2005/04/29 22:28:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -79,11 +79,6 @@ RelationPutHeapTuple(Relation relation,
* happen if space is freed in that page after heap_update finds there's not
* enough there). In that case, the page will be pinned and locked only once.
*
- * Note that we use LockPage(rel, 0) to lock relation for extension.
- * We can do this as long as in all other places we use page-level locking
- * for indices only. Alternatively, we could define pseudo-table as
- * we do for transactions with XactLockTable.
- *
* ereport(ERROR) is allowed here, so this routine *must* be called
* before any (unlogged) changes are made in buffer pool.
*/
@@ -235,7 +230,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
needLock = !RELATION_IS_LOCAL(relation);
if (needLock)
- LockPage(relation, 0, ExclusiveLock);
+ LockRelationForExtension(relation, ExclusiveLock);
/*
* XXX This does an lseek - rather expensive - but at the moment it is
@@ -251,7 +246,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
* extend the relation some more.
*/
if (needLock)
- UnlockPage(relation, 0, ExclusiveLock);
+ UnlockRelationForExtension(relation, ExclusiveLock);
/*
* We can be certain that locking the otherBuffer first is OK, since
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 2b82a87a1d4..222ed764357 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.82 2005/03/22 06:17:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtpage.c,v 1.83 2005/04/29 22:28:24 tgl Exp $
*
* NOTES
* Postgres btree pages look like ordinary relation pages. The opaque
@@ -487,7 +487,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access)
needLock = !RELATION_IS_LOCAL(rel);
if (needLock)
- LockPage(rel, 0, ExclusiveLock);
+ LockRelationForExtension(rel, ExclusiveLock);
buf = ReadBuffer(rel, P_NEW);
@@ -496,7 +496,7 @@ _bt_getbuf(Relation rel, BlockNumber blkno, int access)
* to extend the relation some more.
*/
if (needLock)
- UnlockPage(rel, 0, ExclusiveLock);
+ UnlockRelationForExtension(rel, ExclusiveLock);
/* Acquire appropriate buffer lock on new page */
LockBuffer(buf, access);
diff --git a/src/backend/storage/lmgr/README b/src/backend/storage/lmgr/README
index d8489ab1211..6e5c36e1373 100644
--- a/src/backend/storage/lmgr/README
+++ b/src/backend/storage/lmgr/README
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/storage/lmgr/README,v 1.15 2004/08/27 17:07:41 tgl Exp $
+$PostgreSQL: pgsql/src/backend/storage/lmgr/README,v 1.16 2005/04/29 22:28:24 tgl Exp $
LOCKING OVERVIEW
@@ -74,30 +74,14 @@ The lock manager's LOCK objects contain:
tag -
The key fields that are used for hashing locks in the shared memory
- lock hash table. This is declared as a separate struct to ensure that
- we always zero out the correct number of bytes. It is critical that
- any alignment-padding bytes the compiler might insert in the struct
- be zeroed out, else the hash computation will be random.
-
- tag.relId -
- Uniquely identifies the relation that the lock corresponds to.
-
- tag.dbId -
- Uniquely identifies the database in which the relation lives. If
- this is a shared system relation (e.g. pg_database) the dbId must
- be set to 0.
-
- tag.objId -
- Uniquely identifies the block/page within the relation and the
- tuple within the block. If we are setting a table level lock
- both the blockId and tupleId (in an item pointer this is called
- the position) are set to invalid, if it is a page level lock the
- blockId is valid, while the tupleId is still invalid. Finally if
- this is a tuple level lock (we currently never do this) then both
- the blockId and tupleId are set to valid specifications. This is
- how we get the appearance of a multi-level lock table while using
- only a single table (see Gray's paper on 2 phase locking if
- you are puzzled about how multi-level lock tables work).
+ lock hash table. The contents of the tag essentially define an
+ individual lockable object. See include/storage/lock.h for details
+ about the supported types of lockable objects. This is declared as
+ a separate struct to ensure that we always zero out the correct number
+ of bytes. It is critical that any alignment-padding bytes the compiler
+ might insert in the struct be zeroed out, else the hash computation
+ will be random. (Currently, we are careful to define struct LOCKTAG
+ so that there are no padding bytes.)
grantMask -
This bitmask indicates what types of locks are currently held on the
diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c
index 7b4ca899b62..7edabff6dd4 100644
--- a/src/backend/storage/lmgr/deadlock.c
+++ b/src/backend/storage/lmgr/deadlock.c
@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.33 2005/02/22 04:36:49 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.34 2005/04/29 22:28:24 tgl Exp $
*
* Interface:
*
@@ -837,15 +837,81 @@ PrintLockQueue(LOCK *lock, const char *info)
#endif
/*
+ * Append a description of a lockable object to buf.
+ *
+ * XXX probably this should be exported from lmgr.c or some such place.
+ */
+static void
+DescribeLockTag(StringInfo buf, const LOCKTAG *lock)
+{
+ switch (lock->locktag_type)
+ {
+ case LOCKTAG_RELATION:
+ appendStringInfo(buf,
+ _("relation %u of database %u"),
+ lock->locktag_field2,
+ lock->locktag_field1);
+ break;
+ case LOCKTAG_RELATION_EXTEND:
+ appendStringInfo(buf,
+ _("extension of relation %u of database %u"),
+ lock->locktag_field2,
+ lock->locktag_field1);
+ break;
+ case LOCKTAG_PAGE:
+ appendStringInfo(buf,
+ _("page %u of relation %u of database %u"),
+ lock->locktag_field3,
+ lock->locktag_field2,
+ lock->locktag_field1);
+ break;
+ case LOCKTAG_TUPLE:
+ appendStringInfo(buf,
+ _("tuple (%u,%u) of relation %u of database %u"),
+ lock->locktag_field3,
+ lock->locktag_field4,
+ lock->locktag_field2,
+ lock->locktag_field1);
+ break;
+ case LOCKTAG_TRANSACTION:
+ appendStringInfo(buf,
+ _("transaction %u"),
+ lock->locktag_field1);
+ break;
+ case LOCKTAG_OBJECT:
+ appendStringInfo(buf,
+ _("object %u of class %u of database %u"),
+ lock->locktag_field3,
+ lock->locktag_field2,
+ lock->locktag_field1);
+ break;
+ case LOCKTAG_USERLOCK:
+ appendStringInfo(buf,
+ _("user lock [%u,%u]"),
+ lock->locktag_field1,
+ lock->locktag_field2);
+ break;
+ default:
+ appendStringInfo(buf,
+ _("unknown locktag type %d"),
+ lock->locktag_type);
+ break;
+ }
+}
+
+/*
* Report a detected deadlock, with available details.
*/
void
DeadLockReport(void)
{
StringInfoData buf;
+ StringInfoData buf2;
int i;
initStringInfo(&buf);
+ initStringInfo(&buf2);
+
for (i = 0; i < nDeadlockDetails; i++)
{
DEADLOCK_INFO *info = &deadlockDetails[i];
@@ -860,27 +926,18 @@ DeadLockReport(void)
if (i > 0)
appendStringInfoChar(&buf, '\n');
- if (info->locktag.relId == XactLockTableId && info->locktag.dbId == 0)
- {
- /* Lock is for transaction ID */
- appendStringInfo(&buf,
- _("Process %d waits for %s on transaction %u; blocked by process %d."),
- info->pid,
- GetLockmodeName(info->lockmode),
- info->locktag.objId.xid,
- nextpid);
- }
- else
- {
- /* Lock is for a relation */
- appendStringInfo(&buf,
- _("Process %d waits for %s on relation %u of database %u; blocked by process %d."),
- info->pid,
- GetLockmodeName(info->lockmode),
- info->locktag.relId,
- info->locktag.dbId,
- nextpid);
- }
+ /* reset buf2 to hold next object description */
+ buf2.len = 0;
+ buf2.data[0] = '\0';
+
+ DescribeLockTag(&buf2, &info->locktag);
+
+ appendStringInfo(&buf,
+ _("Process %d waits for %s on %s; blocked by process %d."),
+ info->pid,
+ GetLockmodeName(info->lockmode),
+ buf2.data,
+ nextpid);
}
ereport(ERROR,
(errcode(ERRCODE_T_R_DEADLOCK_DETECTED),
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index c1a69401ce1..d527724b9a6 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.71 2004/12/31 22:01:05 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.72 2005/04/29 22:28:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,7 +25,10 @@
#include "utils/inval.h"
-static LOCKMASK LockConflicts[] = {
+/*
+ * This conflict table defines the semantics of the various lock modes.
+ */
+static const LOCKMASK LockConflicts[] = {
0,
/* AccessShareLock */
@@ -69,6 +72,7 @@ static LOCKMASK LockConflicts[] = {
static LOCKMETHODID LockTableId = INVALID_LOCKMETHOD;
+
/*
* Create the lock table described by LockConflicts
*/
@@ -96,11 +100,11 @@ InitLockTable(int maxBackends)
#ifdef USER_LOCKS
/*
- * Allocate another tableId for long-term locks
+ * Allocate another tableId for user locks (same shared hashtable though)
*/
LongTermTableId = LockMethodTableRename(LockTableId);
if (!LockMethodIsValid(LongTermTableId))
- elog(ERROR, "could not rename long-term lock table");
+ elog(ERROR, "could not rename user lock table");
Assert(LongTermTableId == USER_LOCKMETHOD);
#endif
}
@@ -133,10 +137,9 @@ LockRelation(Relation relation, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relation->rd_lockInfo.lockRelId.relId;
- tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
- tag.objId.blkno = InvalidBlockNumber;
+ SET_LOCKTAG_RELATION(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId);
if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(),
lockmode, false))
@@ -167,10 +170,9 @@ ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relation->rd_lockInfo.lockRelId.relId;
- tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
- tag.objId.blkno = InvalidBlockNumber;
+ SET_LOCKTAG_RELATION(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId);
if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(),
lockmode, true))
@@ -197,10 +199,9 @@ UnlockRelation(Relation relation, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relation->rd_lockInfo.lockRelId.relId;
- tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
- tag.objId.blkno = InvalidBlockNumber;
+ SET_LOCKTAG_RELATION(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId);
LockRelease(LockTableId, &tag, GetTopTransactionId(), lockmode);
}
@@ -222,10 +223,7 @@ LockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relid->relId;
- tag.dbId = relid->dbId;
- tag.objId.blkno = InvalidBlockNumber;
+ SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
if (!LockAcquire(LockTableId, &tag, InvalidTransactionId,
lockmode, false))
@@ -240,30 +238,65 @@ UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relid->relId;
- tag.dbId = relid->dbId;
- tag.objId.blkno = InvalidBlockNumber;
+ SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
LockRelease(LockTableId, &tag, InvalidTransactionId, lockmode);
}
/*
+ * LockRelationForExtension
+ *
+ * This lock tag is used to interlock addition of pages to relations.
+ * We need such locking because bufmgr/smgr definition of P_NEW is not
+ * race-condition-proof.
+ *
+ * We assume the caller is already holding some type of regular lock on
+ * the relation, so no AcceptInvalidationMessages call is needed here.
+ */
+void
+LockRelationForExtension(Relation relation, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_RELATION_EXTEND(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId);
+
+ if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(),
+ lockmode, false))
+ elog(ERROR, "LockAcquire failed");
+}
+
+/*
+ * UnlockRelationForExtension
+ */
+void
+UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_RELATION_EXTEND(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId);
+
+ LockRelease(LockTableId, &tag, GetTopTransactionId(), lockmode);
+}
+
+/*
* LockPage
*
* Obtain a page-level lock. This is currently used by some index access
- * methods to lock index pages. For heap relations, it is used only with
- * blkno == 0 to signify locking the relation for extension.
+ * methods to lock individual index pages.
*/
void
LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relation->rd_lockInfo.lockRelId.relId;
- tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
- tag.objId.blkno = blkno;
+ SET_LOCKTAG_PAGE(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId,
+ blkno);
if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(),
lockmode, false))
@@ -281,10 +314,10 @@ ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relation->rd_lockInfo.lockRelId.relId;
- tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
- tag.objId.blkno = blkno;
+ SET_LOCKTAG_PAGE(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId,
+ blkno);
return LockAcquire(LockTableId, &tag, GetTopTransactionId(),
lockmode, true);
@@ -298,10 +331,10 @@ UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relation->rd_lockInfo.lockRelId.relId;
- tag.dbId = relation->rd_lockInfo.lockRelId.dbId;
- tag.objId.blkno = blkno;
+ SET_LOCKTAG_PAGE(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId,
+ blkno);
LockRelease(LockTableId, &tag, GetTopTransactionId(), lockmode);
}
@@ -318,10 +351,7 @@ XactLockTableInsert(TransactionId xid)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = XactLockTableId;
- tag.dbId = InvalidOid; /* xids are globally unique */
- tag.objId.xid = xid;
+ SET_LOCKTAG_TRANSACTION(tag, xid);
if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(),
ExclusiveLock, false))
@@ -341,10 +371,7 @@ XactLockTableDelete(TransactionId xid)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = XactLockTableId;
- tag.dbId = InvalidOid; /* xids are globally unique */
- tag.objId.xid = xid;
+ SET_LOCKTAG_TRANSACTION(tag, xid);
LockRelease(LockTableId, &tag, GetTopTransactionId(), ExclusiveLock);
}
@@ -372,10 +399,7 @@ XactLockTableWait(TransactionId xid)
Assert(TransactionIdIsValid(xid));
Assert(!TransactionIdEquals(xid, myxid));
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = XactLockTableId;
- tag.dbId = InvalidOid;
- tag.objId.xid = xid;
+ SET_LOCKTAG_TRANSACTION(tag, xid);
if (!LockAcquire(LockTableId, &tag, myxid, ShareLock, false))
elog(ERROR, "LockAcquire failed");
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 7c7b2b1abdd..576fc205299 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.149 2005/04/13 18:54:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.150 2005/04/29 22:28:24 tgl Exp $
*
* NOTES
* Outside modules can create a lock table and acquire/release
@@ -108,10 +108,11 @@ inline static bool
LOCK_DEBUG_ENABLED(const LOCK *lock)
{
return
- (((LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD && Trace_locks)
- || (LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD && Trace_userlocks))
- && (lock->tag.relId >= (Oid) Trace_lock_oidmin))
- || (Trace_lock_table && (lock->tag.relId == Trace_lock_table));
+ (((Trace_locks && LOCK_LOCKMETHOD(*lock) == DEFAULT_LOCKMETHOD)
+ || (Trace_userlocks && LOCK_LOCKMETHOD(*lock) == USER_LOCKMETHOD))
+ && ((Oid) lock->tag.locktag_field2 >= (Oid) Trace_lock_oidmin))
+ || (Trace_lock_table
+ && (lock->tag.locktag_field2 == Trace_lock_table));
}
@@ -120,12 +121,14 @@ LOCK_PRINT(const char *where, const LOCK *lock, LOCKMODE type)
{
if (LOCK_DEBUG_ENABLED(lock))
elog(LOG,
- "%s: lock(%lx) tbl(%d) rel(%u) db(%u) obj(%u) grantMask(%x) "
+ "%s: lock(%lx) id(%u,%u,%u,%u,%u,%u) grantMask(%x) "
"req(%d,%d,%d,%d,%d,%d,%d)=%d "
"grant(%d,%d,%d,%d,%d,%d,%d)=%d wait(%d) type(%s)",
where, MAKE_OFFSET(lock),
- lock->tag.lockmethodid, lock->tag.relId, lock->tag.dbId,
- lock->tag.objId.blkno, lock->grantMask,
+ lock->tag.locktag_field1, lock->tag.locktag_field2,
+ lock->tag.locktag_field3, lock->tag.locktag_field4,
+ lock->tag.locktag_type, lock->tag.locktag_lockmethodid,
+ lock->grantMask,
lock->requested[1], lock->requested[2], lock->requested[3],
lock->requested[4], lock->requested[5], lock->requested[6],
lock->requested[7], lock->nRequested,
@@ -139,14 +142,9 @@ LOCK_PRINT(const char *where, const LOCK *lock, LOCKMODE type)
inline static void
PROCLOCK_PRINT(const char *where, const PROCLOCK *proclockP)
{
- if (
- (((PROCLOCK_LOCKMETHOD(*proclockP) == DEFAULT_LOCKMETHOD && Trace_locks)
- || (PROCLOCK_LOCKMETHOD(*proclockP) == USER_LOCKMETHOD && Trace_userlocks))
- && (((LOCK *) MAKE_PTR(proclockP->tag.lock))->tag.relId >= (Oid) Trace_lock_oidmin))
- || (Trace_lock_table && (((LOCK *) MAKE_PTR(proclockP->tag.lock))->tag.relId == Trace_lock_table))
- )
+ if (LOCK_DEBUG_ENABLED((LOCK *) MAKE_PTR(proclockP->tag.lock)))
elog(LOG,
- "%s: proclock(%lx) lock(%lx) tbl(%d) proc(%lx) xid(%u) hold(%x)",
+ "%s: proclock(%lx) lock(%lx) method(%u) proc(%lx) xid(%u) hold(%x)",
where, MAKE_OFFSET(proclockP), proclockP->tag.lock,
PROCLOCK_LOCKMETHOD(*(proclockP)),
proclockP->tag.proc, proclockP->tag.xid,
@@ -346,13 +344,9 @@ LockMethodTableInit(const char *tabName,
* LockMethodTableRename -- allocate another lockmethod ID to the same
* lock table.
*
- * NOTES: Both the lock module and the lock chain (lchain.c)
- * module use table id's to distinguish between different
- * kinds of locks. Short term and long term locks look
- * the same to the lock table, but are handled differently
- * by the lock chain manager. This function allows the
- * client to use different lockmethods when acquiring/releasing
- * short term and long term locks, yet store them all in one hashtable.
+ * NOTES: This function makes it possible to have different lockmethodids,
+ * and hence different locking semantics, while still storing all
+ * the data in one shared-memory hashtable.
*/
LOCKMETHODID
@@ -404,33 +398,16 @@ LockMethodTableRename(LOCKMETHODID lockmethodid)
* the lock. While the lock is active other clients can still
* read and write the tuple but they can be aware that it has
* been locked at the application level by someone.
- * User locks use lock tags made of an uint16 and an uint32, for
- * example 0 and a tuple oid, or any other arbitrary pair of
- * numbers following a convention established by the application.
- * In this sense tags don't refer to tuples or database entities.
+ *
* User locks and normal locks are completely orthogonal and
- * they don't interfere with each other, so it is possible
- * to acquire a normal lock on an user-locked tuple or user-lock
- * a tuple for which a normal write lock already exists.
+ * they don't interfere with each other.
+ *
* User locks are always non blocking, therefore they are never
* acquired if already held by another process. They must be
* released explicitly by the application but they are released
* automatically when a backend terminates.
* They are indicated by a lockmethod 2 which is an alias for the
- * normal lock table, and are distinguished from normal locks
- * by the following differences:
- *
- * normal lock user lock
- *
- * lockmethodid 1 2
- * tag.dbId database oid database oid
- * tag.relId rel oid or 0 0
- * tag.objId block id lock id2
- * or xact id
- * tag.offnum 0 lock id1
- * proclock.xid xid or 0 0
- * persistence transaction user or backend
- * or backend
+ * normal lock table.
*
* The lockmode parameter can have the same values for normal locks
* although probably only WRITE_LOCK can have some practical use.
@@ -456,13 +433,14 @@ LockAcquire(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
int i;
#ifdef LOCK_DEBUG
- if (lockmethodid == USER_LOCKMETHOD && Trace_userlocks)
- elog(LOG, "LockAcquire: user lock [%u] %s",
- locktag->objId.blkno, lock_mode_names[lockmode]);
+ if (Trace_userlocks && lockmethodid == USER_LOCKMETHOD)
+ elog(LOG, "LockAcquire: user lock [%u,%u] %s",
+ locktag->locktag_field1, locktag->locktag_field2,
+ lock_mode_names[lockmode]);
#endif
- /* ???????? This must be changed when short term locks will be used */
- locktag->lockmethodid = lockmethodid;
+ /* ugly */
+ locktag->locktag_lockmethodid = lockmethodid;
Assert(lockmethodid < NumLockMethods);
lockMethodTable = LockMethods[lockmethodid];
@@ -1231,12 +1209,14 @@ LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
bool wakeupNeeded;
#ifdef LOCK_DEBUG
- if (lockmethodid == USER_LOCKMETHOD && Trace_userlocks)
- elog(LOG, "LockRelease: user lock tag [%u] %d", locktag->objId.blkno, lockmode);
+ if (Trace_userlocks && lockmethodid == USER_LOCKMETHOD)
+ elog(LOG, "LockRelease: user lock [%u,%u] %s",
+ locktag->locktag_field1, locktag->locktag_field2,
+ lock_mode_names[lockmode]);
#endif
- /* ???????? This must be changed when short term locks will be used */
- locktag->lockmethodid = lockmethodid;
+ /* ugly */
+ locktag->locktag_lockmethodid = lockmethodid;
Assert(lockmethodid < NumLockMethods);
lockMethodTable = LockMethods[lockmethodid];
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index e6e14647daf..6878662b79f 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -6,7 +6,7 @@
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.16 2005/01/01 05:43:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.17 2005/04/29 22:28:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -155,20 +155,24 @@ pg_lock_status(PG_FUNCTION_ARGS)
MemSet(values, 0, sizeof(values));
MemSet(nulls, ' ', sizeof(nulls));
- if (lock->tag.relId == XactLockTableId && lock->tag.dbId == 0)
+ switch (lock->tag.locktag_type)
{
- /* Lock is for transaction ID */
- nulls[0] = 'n';
- nulls[1] = 'n';
- values[2] = TransactionIdGetDatum(lock->tag.objId.xid);
- }
- else
- {
- /* Lock is for a relation */
- values[0] = ObjectIdGetDatum(lock->tag.relId);
- values[1] = ObjectIdGetDatum(lock->tag.dbId);
- nulls[2] = 'n';
-
+ case LOCKTAG_RELATION:
+ case LOCKTAG_RELATION_EXTEND:
+ case LOCKTAG_PAGE:
+ case LOCKTAG_TUPLE:
+ values[0] = ObjectIdGetDatum(lock->tag.locktag_field2);
+ values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
+ nulls[2] = 'n';
+ break;
+ case LOCKTAG_TRANSACTION:
+ nulls[0] = 'n';
+ nulls[1] = 'n';
+ values[2] = TransactionIdGetDatum(lock->tag.locktag_field1);
+ break;
+ default:
+ /* XXX Ignore all other lock types for now */
+ continue;
}
values[3] = Int32GetDatum(proc->pid);
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index a58f1d886b9..ddca993a68a 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.116 2005/04/14 01:38:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.117 2005/04/29 22:28:24 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -461,14 +461,4 @@ DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
{ 0, {"indexprs"}, 25, -1, -1, 9, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
{ 0, {"indpred"}, 25, -1, -1, 10, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
-/* ----------------
- * pg_xactlock - this is not a real relation, but is a placeholder
- * to allow a relation OID to be used for transaction
- * waits. We need a pg_xactlock entry in pg_class only to
- * ensure that that OID can never be allocated to a real
- * table; and this entry is just to link to that one.
- * ----------------
- */
-DATA(insert ( 376 xactlockfoo 26 0 4 1 0 -1 -1 t p i t f f t 0));
-
#endif /* PG_ATTRIBUTE_H */
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 9ffc79ad8ca..e08d6d60d33 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.87 2005/04/14 01:38:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.88 2005/04/29 22:28:24 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -147,11 +147,6 @@ DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 18 0 0
DESCR("");
DATA(insert OID = 1259 ( pg_class PGNSP 83 PGUID 0 1259 0 0 0 0 0 f f r 25 0 0 0 0 0 t f f f _null_ ));
DESCR("");
-DATA(insert OID = 376 ( pg_xactlock PGNSP 0 PGUID 0 0 1664 0 0 0 0 f t s 1 0 0 0 0 0 f f f f _null_ ));
-DESCR("");
-
-/* Xact lock pseudo-table */
-#define XactLockTableId 376
#define RELKIND_INDEX 'i' /* secondary index */
#define RELKIND_RELATION 'r' /* ordinary cataloged heap */
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index 8d63294f5cd..4d1027c174e 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.46 2005/04/28 21:47:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.47 2005/04/29 22:28:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -51,7 +51,11 @@ extern void UnlockRelation(Relation relation, LOCKMODE lockmode);
extern void LockRelationForSession(LockRelId *relid, LOCKMODE lockmode);
extern void UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode);
-/* Lock a page (mainly used for indexes) */
+/* Lock a relation for extension */
+extern void LockRelationForExtension(Relation relation, LOCKMODE lockmode);
+extern void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode);
+
+/* Lock a page (currently only used within indexes) */
extern void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
extern bool ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 7f67a3ac3a9..b1744325ff2 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.84 2004/12/31 22:03:42 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.85 2005/04/29 22:28:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -58,7 +58,8 @@ typedef int LOCKMODE;
/*
* There is normally only one lock method, the default one.
* If user locks are enabled, an additional lock method is present.
- * Lock methods are identified by LOCKMETHODID.
+ * Lock methods are identified by LOCKMETHODID. (Despite the declaration as
+ * uint16, we are constrained to 256 lockmethods by the layout of LOCKTAG.)
*/
typedef uint16 LOCKMETHODID;
@@ -103,27 +104,100 @@ typedef LockMethodData *LockMethod;
/*
* LOCKTAG is the key information needed to look up a LOCK item in the
* lock hashtable. A LOCKTAG value uniquely identifies a lockable object.
+ *
+ * The LockTagType enum defines the different kinds of objects we can lock.
+ * We can handle up to 256 different LockTagTypes.
*/
-typedef struct LOCKTAG
+typedef enum LockTagType
{
- Oid relId;
- Oid dbId;
- union
- {
- BlockNumber blkno;
- TransactionId xid;
- } objId;
-
+ LOCKTAG_RELATION, /* whole relation */
+ /* ID info for a relation is DB OID + REL OID; DB OID = 0 if shared */
+ LOCKTAG_RELATION_EXTEND, /* the right to extend a relation */
+ /* same ID info as RELATION */
+ LOCKTAG_PAGE, /* one page of a relation */
+ /* ID info for a page is RELATION info + BlockNumber */
+ LOCKTAG_TUPLE, /* one physical tuple */
+ /* ID info for a tuple is PAGE info + OffsetNumber */
+ LOCKTAG_TRANSACTION, /* transaction (for waiting for xact done) */
+ /* ID info for a transaction is its TransactionId */
+ LOCKTAG_OBJECT, /* non-relation database object */
+ /* ID info for an object is DB OID + CLASS OID + OBJECT OID + SUBID */
/*
- * offnum should be part of objId union above, but doing that would
- * increase sizeof(LOCKTAG) due to padding. Currently used by
- * userlocks only.
+ * Note: object ID has same representation as in pg_depend and
+ * pg_description, but notice that we are constraining SUBID to 16 bits.
+ * Also, we use DB OID = 0 for shared objects such as tablespaces.
*/
- OffsetNumber offnum;
+ LOCKTAG_USERLOCK /* reserved for contrib/userlock */
+ /* ID info for a userlock is defined by user_locks.c */
+} LockTagType;
- LOCKMETHODID lockmethodid; /* needed by userlocks */
+/*
+ * The LOCKTAG struct is defined with malice aforethought to fit into 16
+ * bytes with no padding. Note that this would need adjustment if we were
+ * to widen Oid, BlockNumber, or TransactionId to more than 32 bits.
+ *
+ * We include lockmethodid in the locktag so that a single hash table in
+ * shared memory can store locks of different lockmethods. For largely
+ * historical reasons, it's passed to the lock.c routines as a separate
+ * argument and then stored into the locktag.
+ */
+typedef struct LOCKTAG
+{
+ uint32 locktag_field1; /* a 32-bit ID field */
+ uint32 locktag_field2; /* a 32-bit ID field */
+ uint32 locktag_field3; /* a 32-bit ID field */
+ uint16 locktag_field4; /* a 16-bit ID field */
+ uint8 locktag_type; /* see enum LockTagType */
+ uint8 locktag_lockmethodid; /* lockmethod indicator */
} LOCKTAG;
+/*
+ * These macros define how we map logical IDs of lockable objects into
+ * the physical fields of LOCKTAG. Use these to set up LOCKTAG values,
+ * rather than accessing the fields directly. Note multiple eval of target!
+ */
+#define SET_LOCKTAG_RELATION(locktag,dboid,reloid) \
+ ((locktag).locktag_field1 = (dboid), \
+ (locktag).locktag_field2 = (reloid), \
+ (locktag).locktag_field3 = 0, \
+ (locktag).locktag_field4 = 0, \
+ (locktag).locktag_type = LOCKTAG_RELATION)
+
+#define SET_LOCKTAG_RELATION_EXTEND(locktag,dboid,reloid) \
+ ((locktag).locktag_field1 = (dboid), \
+ (locktag).locktag_field2 = (reloid), \
+ (locktag).locktag_field3 = 0, \
+ (locktag).locktag_field4 = 0, \
+ (locktag).locktag_type = LOCKTAG_RELATION_EXTEND)
+
+#define SET_LOCKTAG_PAGE(locktag,dboid,reloid,blocknum) \
+ ((locktag).locktag_field1 = (dboid), \
+ (locktag).locktag_field2 = (reloid), \
+ (locktag).locktag_field3 = (blocknum), \
+ (locktag).locktag_field4 = 0, \
+ (locktag).locktag_type = LOCKTAG_PAGE)
+
+#define SET_LOCKTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \
+ ((locktag).locktag_field1 = (dboid), \
+ (locktag).locktag_field2 = (reloid), \
+ (locktag).locktag_field3 = (blocknum), \
+ (locktag).locktag_field4 = (offnum), \
+ (locktag).locktag_type = LOCKTAG_TUPLE)
+
+#define SET_LOCKTAG_TRANSACTION(locktag,xid) \
+ ((locktag).locktag_field1 = (xid), \
+ (locktag).locktag_field2 = 0, \
+ (locktag).locktag_field3 = 0, \
+ (locktag).locktag_field4 = 0, \
+ (locktag).locktag_type = LOCKTAG_TRANSACTION)
+
+#define SET_LOCKTAG_OBJECT(locktag,dboid,classoid,objoid,objsubid) \
+ ((locktag).locktag_field1 = (dboid), \
+ (locktag).locktag_field2 = (classoid), \
+ (locktag).locktag_field3 = (objoid), \
+ (locktag).locktag_field4 = (objsubid), \
+ (locktag).locktag_type = LOCKTAG_OBJECT)
+
/*
* Per-locked-object lock information:
@@ -157,7 +231,7 @@ typedef struct LOCK
int nGranted; /* total of granted[] array */
} LOCK;
-#define LOCK_LOCKMETHOD(lock) ((lock).tag.lockmethodid)
+#define LOCK_LOCKMETHOD(lock) ((LOCKMETHODID) (lock).tag.locktag_lockmethodid)
/*
@@ -211,7 +285,7 @@ typedef struct PROCLOCK
} PROCLOCK;
#define PROCLOCK_LOCKMETHOD(proclock) \
- (((LOCK *) MAKE_PTR((proclock).tag.lock))->tag.lockmethodid)
+ LOCK_LOCKMETHOD(*((LOCK *) MAKE_PTR((proclock).tag.lock)))
/*
* Each backend also maintains a local hash table with information about each
@@ -253,7 +327,7 @@ typedef struct LOCALLOCK
LOCALLOCKOWNER *lockOwners; /* dynamically resizable array */
} LOCALLOCK;
-#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.lockmethodid)
+#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.locktag_lockmethodid)
/*