X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Fstorage%2Flmgr%2Flmgr.c;h=9f335ed4868f70129e81d8481a16b9ce03f66b68;hb=9f2e211386931f7aee48ffbc2fcaef1632d8329f;hp=e4e52b16abf33b283c1be267d5b93d84017a90ee;hpb=1a321f26d88e5c64bccba9d36920aede1e201729;p=postgresql diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c index e4e52b16ab..9f335ed486 100644 --- a/src/backend/storage/lmgr/lmgr.c +++ b/src/backend/storage/lmgr/lmgr.c @@ -3,212 +3,240 @@ * 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 @@ -216,56 +244,82 @@ UnlockRelation(Relation relation, LOCKMODE lockmode) * 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); } /* @@ -279,13 +333,12 @@ 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, GetCurrentTransactionId(), - lockmode, true); + return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL); } /* @@ -296,67 +349,437 @@ 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, 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; + } }