* lmgr.c
* POSTGRES lock manager code
*
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.63 2004/05/28 05:13:04 tgl Exp $
+ * src/backend/storage/lmgr/lmgr.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "access/subtrans.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
+#include "storage/procarray.h"
#include "utils/inval.h"
-static LOCKMASK LockConflicts[] = {
- 0,
-
- /* AccessShareLock */
- (1 << AccessExclusiveLock),
+/*
+ * RelationInitLockInfo
+ * Initializes the lock information in a relation descriptor.
+ *
+ * relcache.c must call this during creation of any reldesc.
+ */
+void
+RelationInitLockInfo(Relation relation)
+{
+ Assert(RelationIsValid(relation));
+ Assert(OidIsValid(RelationGetRelid(relation)));
- /* RowShareLock */
- (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
+ relation->rd_lockInfo.lockRelId.relId = RelationGetRelid(relation);
- /* RowExclusiveLock */
- (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
- (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
+ if (relation->rd_rel->relisshared)
+ relation->rd_lockInfo.lockRelId.dbId = InvalidOid;
+ else
+ relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId;
+}
- /* ShareUpdateExclusiveLock */
- (1 << ShareUpdateExclusiveLock) |
- (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
- (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
+/*
+ * SetLocktagRelationOid
+ * Set up a locktag for a relation, given only relation OID
+ */
+static inline void
+SetLocktagRelationOid(LOCKTAG *tag, Oid relid)
+{
+ Oid dbid;
- /* ShareLock */
- (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
- (1 << ShareRowExclusiveLock) |
- (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
+ if (IsSharedRelation(relid))
+ dbid = InvalidOid;
+ else
+ dbid = MyDatabaseId;
- /* ShareRowExclusiveLock */
- (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
- (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
- (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
+ SET_LOCKTAG_RELATION(*tag, dbid, relid);
+}
- /* ExclusiveLock */
- (1 << RowShareLock) |
- (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
- (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
- (1 << ExclusiveLock) | (1 << AccessExclusiveLock),
+/*
+ * LockRelationOid
+ *
+ * Lock a relation given only its OID. This should generally be used
+ * before attempting to open the relation's relcache entry.
+ */
+void
+LockRelationOid(Oid relid, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+ LockAcquireResult res;
- /* AccessExclusiveLock */
- (1 << AccessShareLock) | (1 << RowShareLock) |
- (1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
- (1 << ShareLock) | (1 << ShareRowExclusiveLock) |
- (1 << ExclusiveLock) | (1 << AccessExclusiveLock)
+ SetLocktagRelationOid(&tag, relid);
-};
+ res = LockAcquire(&tag, lockmode, false, false);
-static LOCKMETHODID LockTableId = INVALID_LOCKMETHOD;
+ /*
+ * Now that we have the lock, check for invalidation messages, so that we
+ * will update or flush any stale relcache entry before we try to use it.
+ * We can skip this in the not-uncommon case that we already had the same
+ * type of lock being requested, since then no one else could have
+ * modified the relcache entry in an undesirable way. (In the case where
+ * our own xact modifies the rel, the relcache update happens via
+ * CommandCounterIncrement, not here.)
+ */
+ if (res != LOCKACQUIRE_ALREADY_HELD)
+ AcceptInvalidationMessages();
+}
/*
- * Create the lock table described by LockConflicts
+ * ConditionalLockRelationOid
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE iff the lock was acquired.
+ *
+ * NOTE: we do not currently need conditional versions of all the
+ * LockXXX routines in this file, but they could easily be added if needed.
*/
-void
-InitLockTable(int maxBackends)
+bool
+ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
{
- LOCKMETHODID LongTermTableId;
+ LOCKTAG tag;
+ LockAcquireResult res;
- /* there's no zero-th table */
- NumLockMethods = 1;
+ SetLocktagRelationOid(&tag, relid);
+
+ res = LockAcquire(&tag, lockmode, false, true);
+
+ if (res == LOCKACQUIRE_NOT_AVAIL)
+ return false;
/*
- * Create the default lock method table
+ * Now that we have the lock, check for invalidation messages; see notes
+ * in LockRelationOid.
*/
+ if (res != LOCKACQUIRE_ALREADY_HELD)
+ AcceptInvalidationMessages();
- /* number of lock modes is lengthof()-1 because of dummy zero */
- LockTableId = LockMethodTableInit("LockTable",
- LockConflicts,
- lengthof(LockConflicts) - 1,
- maxBackends);
- if (!LockMethodIsValid(LockTableId))
- elog(ERROR, "could not initialize lock table");
- Assert(LockTableId == DEFAULT_LOCKMETHOD);
+ return true;
+}
-#ifdef USER_LOCKS
+/*
+ * UnlockRelationId
+ *
+ * Unlock, given a LockRelId. This is preferred over UnlockRelationOid
+ * for speed reasons.
+ */
+void
+UnlockRelationId(LockRelId *relid, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
- /*
- * Allocate another tableId for long-term locks
- */
- LongTermTableId = LockMethodTableRename(LockTableId);
- if (!LockMethodIsValid(LongTermTableId))
- elog(ERROR, "could not rename long-term lock table");
- Assert(LongTermTableId == USER_LOCKMETHOD);
-#endif
+ SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
+
+ LockRelease(&tag, lockmode, false);
}
/*
- * RelationInitLockInfo
- * Initializes the lock information in a relation descriptor.
+ * UnlockRelationOid
*
- * relcache.c must call this during creation of any reldesc.
+ * Unlock, given only a relation Oid. Use UnlockRelationId if you can.
*/
void
-RelationInitLockInfo(Relation relation)
+UnlockRelationOid(Oid relid, LOCKMODE lockmode)
{
- Assert(RelationIsValid(relation));
- Assert(OidIsValid(RelationGetRelid(relation)));
+ LOCKTAG tag;
- relation->rd_lockInfo.lockRelId.relId = RelationGetRelid(relation);
+ SetLocktagRelationOid(&tag, relid);
- if (relation->rd_rel->relisshared)
- relation->rd_lockInfo.lockRelId.dbId = InvalidOid;
- else
- relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId;
+ LockRelease(&tag, lockmode, false);
}
/*
* LockRelation
+ *
+ * This is a convenience routine for acquiring an additional lock on an
+ * already-open relation. Never try to do "relation_open(foo, NoLock)"
+ * and then lock with this.
*/
void
LockRelation(Relation relation, LOCKMODE lockmode)
{
LOCKTAG tag;
+ LockAcquireResult res;
- 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, GetCurrentTransactionId(),
- lockmode, false))
- elog(ERROR, "LockAcquire failed");
+ res = LockAcquire(&tag, lockmode, false, false);
/*
- * Check to see if the relcache entry has been invalidated while we
- * were waiting to lock it. If so, rebuild it, or ereport() trying.
- * Increment the refcount to ensure that RelationFlushRelation will
- * rebuild it and not just delete it.
+ * Now that we have the lock, check for invalidation messages; see notes
+ * in LockRelationOid.
*/
- RelationIncrementReferenceCount(relation);
- AcceptInvalidationMessages();
- RelationDecrementReferenceCount(relation);
+ if (res != LOCKACQUIRE_ALREADY_HELD)
+ AcceptInvalidationMessages();
}
/*
* ConditionalLockRelation
*
- * As above, but only lock if we can get the lock without blocking.
- * Returns TRUE iff the lock was acquired.
- *
- * NOTE: we do not currently need conditional versions of all the
- * LockXXX routines in this file, but they could easily be added if needed.
+ * This is a convenience routine for acquiring an additional lock on an
+ * already-open relation. Never try to do "relation_open(foo, NoLock)"
+ * and then lock with this.
*/
bool
ConditionalLockRelation(Relation relation, LOCKMODE lockmode)
{
LOCKTAG tag;
+ LockAcquireResult res;
- 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, GetCurrentTransactionId(),
- lockmode, true))
+ res = LockAcquire(&tag, lockmode, false, true);
+
+ if (res == LOCKACQUIRE_NOT_AVAIL)
return false;
/*
- * Check to see if the relcache entry has been invalidated while we
- * were waiting to lock it. If so, rebuild it, or ereport() trying.
- * Increment the refcount to ensure that RelationFlushRelation will
- * rebuild it and not just delete it.
+ * Now that we have the lock, check for invalidation messages; see notes
+ * in LockRelationOid.
*/
- RelationIncrementReferenceCount(relation);
- AcceptInvalidationMessages();
- RelationDecrementReferenceCount(relation);
+ if (res != LOCKACQUIRE_ALREADY_HELD)
+ AcceptInvalidationMessages();
return true;
}
/*
* UnlockRelation
+ *
+ * This is a convenience routine for unlocking a relation without also
+ * closing it.
*/
void
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, GetCurrentTransactionId(), lockmode);
+ LockRelease(&tag, lockmode, false);
}
/*
- * LockRelationForSession
+ * LockRelationIdForSession
*
* This routine grabs a session-level lock on the target relation. The
* session lock persists across transaction boundaries. It will be removed
- * when UnlockRelationForSession() is called, or if an ereport(ERROR) occurs,
+ * when UnlockRelationIdForSession() is called, or if an ereport(ERROR) occurs,
* or if the backend exits.
*
* Note that one should also grab a transaction-level lock on the rel
* relcache entry is up to date.
*/
void
-LockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
+LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);
+
+ (void) LockAcquire(&tag, lockmode, true, false);
+}
+
+/*
+ * UnlockRelationIdForSession
+ */
+void
+UnlockRelationIdForSession(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))
- elog(ERROR, "LockAcquire failed");
+ LockRelease(&tag, lockmode, true);
}
/*
- * UnlockRelationForSession
+ * 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
-UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
+LockRelationForExtension(Relation relation, LOCKMODE lockmode)
{
LOCKTAG tag;
- MemSet(&tag, 0, sizeof(tag));
- tag.relId = relid->relId;
- tag.dbId = relid->dbId;
- tag.objId.blkno = InvalidBlockNumber;
+ SET_LOCKTAG_RELATION_EXTEND(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId);
- LockRelease(LockTableId, &tag, InvalidTransactionId, lockmode);
+ (void) LockAcquire(&tag, lockmode, false, false);
+}
+
+/*
+ * 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(&tag, lockmode, false);
}
/*
* 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, GetCurrentTransactionId(),
- lockmode, false))
- elog(ERROR, "LockAcquire failed");
+ (void) LockAcquire(&tag, lockmode, false, false);
}
/*
{
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, GetCurrentTransactionId(),
- lockmode, true);
+ return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
}
/*
{
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, GetCurrentTransactionId(), lockmode);
+ LockRelease(&tag, lockmode, false);
+}
+
+/*
+ * LockTuple
+ *
+ * Obtain a tuple-level lock. This is used in a less-than-intuitive fashion
+ * because we can't afford to keep a separate lock in shared memory for every
+ * tuple. See heap_lock_tuple before using this!
+ */
+void
+LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_TUPLE(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId,
+ ItemPointerGetBlockNumber(tid),
+ ItemPointerGetOffsetNumber(tid));
+
+ (void) LockAcquire(&tag, lockmode, false, false);
+}
+
+/*
+ * ConditionalLockTuple
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE iff the lock was acquired.
+ */
+bool
+ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_TUPLE(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId,
+ ItemPointerGetBlockNumber(tid),
+ ItemPointerGetOffsetNumber(tid));
+
+ return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
+}
+
+/*
+ * UnlockTuple
+ */
+void
+UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_TUPLE(tag,
+ relation->rd_lockInfo.lockRelId.dbId,
+ relation->rd_lockInfo.lockRelId.relId,
+ ItemPointerGetBlockNumber(tid),
+ ItemPointerGetOffsetNumber(tid));
+
+ LockRelease(&tag, lockmode, false);
}
/*
* XactLockTableInsert
*
* Insert a lock showing that the given transaction ID is running ---
- * this is done during xact startup. The lock can then be used to wait
- * for the transaction to finish.
- *
- * We need no corresponding unlock function, since the lock will always
- * be released implicitly at transaction commit/abort, never any other way.
+ * this is done when an XID is acquired by a transaction or subtransaction.
+ * The lock can then be used to wait for the transaction to finish.
*/
void
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);
+
+ (void) LockAcquire(&tag, ExclusiveLock, false, false);
+}
+
+/*
+ * XactLockTableDelete
+ *
+ * Delete the lock showing that the given transaction ID is running.
+ * (This is never used for main transaction IDs; those locks are only
+ * released implicitly at transaction end. But we do use it for subtrans IDs.)
+ */
+void
+XactLockTableDelete(TransactionId xid)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_TRANSACTION(tag, xid);
- if (!LockAcquire(LockTableId, &tag, xid,
- ExclusiveLock, false))
- elog(ERROR, "LockAcquire failed");
+ LockRelease(&tag, ExclusiveLock, false);
}
/*
* XactLockTableWait
*
* Wait for the specified transaction to commit or abort.
+ *
+ * Note that this does the right thing for subtransactions: if we wait on a
+ * subtransaction, we will exit as soon as it aborts or its top parent commits.
+ * It takes some extra work to ensure this, because to save on shared memory
+ * the XID lock of a subtransaction is released when it ends, whether
+ * successfully or unsuccessfully. So we have to check if it's "still running"
+ * and if so wait for its parent.
*/
void
XactLockTableWait(TransactionId xid)
{
LOCKTAG tag;
- TransactionId myxid = GetCurrentTransactionId();
- Assert(!TransactionIdEquals(xid, myxid));
+ for (;;)
+ {
+ Assert(TransactionIdIsValid(xid));
+ Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny()));
- 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");
+ (void) LockAcquire(&tag, ShareLock, false, false);
- LockRelease(LockTableId, &tag, myxid, ShareLock);
+ LockRelease(&tag, ShareLock, false);
- /*
- * Transaction was committed/aborted/crashed - we have to update
- * pg_clog if transaction is still marked as running.
- */
- if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
- TransactionIdAbort(xid);
+ if (!TransactionIdIsInProgress(xid))
+ break;
+ xid = SubTransGetParent(xid);
+ }
+}
+
+/*
+ * ConditionalXactLockTableWait
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE if the lock was acquired.
+ */
+bool
+ConditionalXactLockTableWait(TransactionId xid)
+{
+ LOCKTAG tag;
+
+ for (;;)
+ {
+ Assert(TransactionIdIsValid(xid));
+ Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny()));
+
+ SET_LOCKTAG_TRANSACTION(tag, xid);
+
+ if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
+ return false;
+
+ LockRelease(&tag, ShareLock, false);
+
+ if (!TransactionIdIsInProgress(xid))
+ break;
+ xid = SubTransGetParent(xid);
+ }
+
+ return true;
+}
+
+
+/*
+ * VirtualXactLockTableInsert
+ *
+ * Insert a lock showing that the given virtual transaction ID is running ---
+ * this is done at main transaction start when its VXID is assigned.
+ * The lock can then be used to wait for the transaction to finish.
+ */
+void
+VirtualXactLockTableInsert(VirtualTransactionId vxid)
+{
+ LOCKTAG tag;
+
+ Assert(VirtualTransactionIdIsValid(vxid));
+
+ SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
+
+ (void) LockAcquire(&tag, ExclusiveLock, false, false);
+}
+
+/*
+ * VirtualXactLockTableWait
+ *
+ * Waits until the lock on the given VXID is released, which shows that
+ * the top-level transaction owning the VXID has ended.
+ */
+void
+VirtualXactLockTableWait(VirtualTransactionId vxid)
+{
+ LOCKTAG tag;
+
+ Assert(VirtualTransactionIdIsValid(vxid));
+
+ SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
+
+ (void) LockAcquire(&tag, ShareLock, false, false);
+
+ LockRelease(&tag, ShareLock, false);
+}
+
+/*
+ * ConditionalVirtualXactLockTableWait
+ *
+ * As above, but only lock if we can get the lock without blocking.
+ * Returns TRUE if the lock was acquired.
+ */
+bool
+ConditionalVirtualXactLockTableWait(VirtualTransactionId vxid)
+{
+ LOCKTAG tag;
+
+ Assert(VirtualTransactionIdIsValid(vxid));
+
+ SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
+
+ if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
+ return false;
+
+ LockRelease(&tag, ShareLock, false);
+
+ return true;
+}
+
+
+/*
+ * LockDatabaseObject
+ *
+ * Obtain a lock on a general object of the current database. Don't use
+ * this for shared objects (such as tablespaces). It's unwise to apply it
+ * to relations, also, since a lock taken this way will NOT conflict with
+ * locks taken via LockRelation and friends.
+ */
+void
+LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
+ LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_OBJECT(tag,
+ MyDatabaseId,
+ classid,
+ objid,
+ objsubid);
+
+ (void) LockAcquire(&tag, lockmode, false, false);
+
+ /* Make sure syscaches are up-to-date with any changes we waited for */
+ AcceptInvalidationMessages();
+}
+
+/*
+ * UnlockDatabaseObject
+ */
+void
+UnlockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
+ LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_OBJECT(tag,
+ MyDatabaseId,
+ classid,
+ objid,
+ objsubid);
+
+ LockRelease(&tag, lockmode, false);
+}
+
+/*
+ * LockSharedObject
+ *
+ * Obtain a lock on a shared-across-databases object.
+ */
+void
+LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
+ LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_OBJECT(tag,
+ InvalidOid,
+ classid,
+ objid,
+ objsubid);
+
+ (void) LockAcquire(&tag, lockmode, false, false);
+
+ /* Make sure syscaches are up-to-date with any changes we waited for */
+ AcceptInvalidationMessages();
+}
+
+/*
+ * UnlockSharedObject
+ */
+void
+UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
+ LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_OBJECT(tag,
+ InvalidOid,
+ classid,
+ objid,
+ objsubid);
+
+ LockRelease(&tag, lockmode, false);
+}
+
+/*
+ * LockSharedObjectForSession
+ *
+ * Obtain a session-level lock on a shared-across-databases object.
+ * See LockRelationIdForSession for notes about session-level locks.
+ */
+void
+LockSharedObjectForSession(Oid classid, Oid objid, uint16 objsubid,
+ LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_OBJECT(tag,
+ InvalidOid,
+ classid,
+ objid,
+ objsubid);
+
+ (void) LockAcquire(&tag, lockmode, true, false);
+}
+
+/*
+ * UnlockSharedObjectForSession
+ */
+void
+UnlockSharedObjectForSession(Oid classid, Oid objid, uint16 objsubid,
+ LOCKMODE lockmode)
+{
+ LOCKTAG tag;
+
+ SET_LOCKTAG_OBJECT(tag,
+ InvalidOid,
+ classid,
+ objid,
+ objsubid);
+
+ LockRelease(&tag, lockmode, true);
+}
+
+
+/*
+ * Append a description of a lockable object to buf.
+ *
+ * Ideally we would print names for the numeric values, but that requires
+ * getting locks on system tables, which might cause problems since this is
+ * typically used to report deadlock situations.
+ */
+void
+DescribeLockTag(StringInfo buf, const LOCKTAG *tag)
+{
+ switch ((LockTagType) tag->locktag_type)
+ {
+ case LOCKTAG_RELATION:
+ appendStringInfo(buf,
+ _("relation %u of database %u"),
+ tag->locktag_field2,
+ tag->locktag_field1);
+ break;
+ case LOCKTAG_RELATION_EXTEND:
+ appendStringInfo(buf,
+ _("extension of relation %u of database %u"),
+ tag->locktag_field2,
+ tag->locktag_field1);
+ break;
+ case LOCKTAG_PAGE:
+ appendStringInfo(buf,
+ _("page %u of relation %u of database %u"),
+ tag->locktag_field3,
+ tag->locktag_field2,
+ tag->locktag_field1);
+ break;
+ case LOCKTAG_TUPLE:
+ appendStringInfo(buf,
+ _("tuple (%u,%u) of relation %u of database %u"),
+ tag->locktag_field3,
+ tag->locktag_field4,
+ tag->locktag_field2,
+ tag->locktag_field1);
+ break;
+ case LOCKTAG_TRANSACTION:
+ appendStringInfo(buf,
+ _("transaction %u"),
+ tag->locktag_field1);
+ break;
+ case LOCKTAG_VIRTUALTRANSACTION:
+ appendStringInfo(buf,
+ _("virtual transaction %d/%u"),
+ tag->locktag_field1,
+ tag->locktag_field2);
+ break;
+ case LOCKTAG_OBJECT:
+ appendStringInfo(buf,
+ _("object %u of class %u of database %u"),
+ tag->locktag_field3,
+ tag->locktag_field2,
+ tag->locktag_field1);
+ break;
+ case LOCKTAG_USERLOCK:
+ /* reserved for old contrib code, now on pgfoundry */
+ appendStringInfo(buf,
+ _("user lock [%u,%u,%u]"),
+ tag->locktag_field1,
+ tag->locktag_field2,
+ tag->locktag_field3);
+ break;
+ case LOCKTAG_ADVISORY:
+ appendStringInfo(buf,
+ _("advisory lock [%u,%u,%u,%u]"),
+ tag->locktag_field1,
+ tag->locktag_field2,
+ tag->locktag_field3,
+ tag->locktag_field4);
+ break;
+ default:
+ appendStringInfo(buf,
+ _("unrecognized locktag type %d"),
+ (int) tag->locktag_type);
+ break;
+ }
}