mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-08 10:29:20 +02:00
b81844b173
tests pass.
357 lines
8.9 KiB
C
357 lines
8.9 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* lmgr.c
|
|
* POSTGRES lock manager code
|
|
*
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lmgr.c,v 1.52 2001/10/25 05:49:42 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/transam.h"
|
|
#include "access/xact.h"
|
|
#include "catalog/catalog.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/lmgr.h"
|
|
#include "utils/inval.h"
|
|
|
|
|
|
static LOCKMASK LockConflicts[] = {
|
|
0,
|
|
|
|
/* AccessShareLock */
|
|
(1 << AccessExclusiveLock),
|
|
|
|
/* RowShareLock */
|
|
(1 << ExclusiveLock) | (1 << AccessExclusiveLock),
|
|
|
|
/* RowExclusiveLock */
|
|
(1 << ShareLock) | (1 << ShareRowExclusiveLock) |
|
|
(1 << ExclusiveLock) | (1 << AccessExclusiveLock),
|
|
|
|
/* ShareUpdateExclusiveLock */
|
|
(1 << ShareUpdateExclusiveLock) |
|
|
(1 << ShareLock) | (1 << ShareRowExclusiveLock) |
|
|
(1 << ExclusiveLock) | (1 << AccessExclusiveLock),
|
|
|
|
/* ShareLock */
|
|
(1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
|
|
(1 << ShareRowExclusiveLock) |
|
|
(1 << ExclusiveLock) | (1 << AccessExclusiveLock),
|
|
|
|
/* ShareRowExclusiveLock */
|
|
(1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
|
|
(1 << ShareLock) | (1 << ShareRowExclusiveLock) |
|
|
(1 << ExclusiveLock) | (1 << AccessExclusiveLock),
|
|
|
|
/* ExclusiveLock */
|
|
(1 << RowShareLock) |
|
|
(1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
|
|
(1 << ShareLock) | (1 << ShareRowExclusiveLock) |
|
|
(1 << ExclusiveLock) | (1 << AccessExclusiveLock),
|
|
|
|
/* AccessExclusiveLock */
|
|
(1 << AccessShareLock) | (1 << RowShareLock) |
|
|
(1 << RowExclusiveLock) | (1 << ShareUpdateExclusiveLock) |
|
|
(1 << ShareLock) | (1 << ShareRowExclusiveLock) |
|
|
(1 << ExclusiveLock) | (1 << AccessExclusiveLock)
|
|
|
|
};
|
|
|
|
static int LockPrios[] = {
|
|
0,
|
|
/* AccessShareLock */
|
|
1,
|
|
/* RowShareLock */
|
|
2,
|
|
/* RowExclusiveLock */
|
|
3,
|
|
/* ShareUpdateExclusiveLock */
|
|
4,
|
|
/* ShareLock */
|
|
5,
|
|
/* ShareRowExclusiveLock */
|
|
6,
|
|
/* ExclusiveLock */
|
|
7,
|
|
/* AccessExclusiveLock */
|
|
8
|
|
};
|
|
|
|
LOCKMETHOD LockTableId = (LOCKMETHOD) NULL;
|
|
LOCKMETHOD LongTermTableId = (LOCKMETHOD) NULL;
|
|
|
|
/*
|
|
* Create the lock table described by LockConflicts and LockPrios.
|
|
*/
|
|
LOCKMETHOD
|
|
InitLockTable(int maxBackends)
|
|
{
|
|
int lockmethod;
|
|
|
|
lockmethod = LockMethodTableInit("LockTable",
|
|
LockConflicts, LockPrios,
|
|
MAX_LOCKMODES - 1, maxBackends);
|
|
LockTableId = lockmethod;
|
|
|
|
if (!(LockTableId))
|
|
elog(ERROR, "InitLockTable: couldn't initialize lock table");
|
|
|
|
#ifdef USER_LOCKS
|
|
|
|
/*
|
|
* Allocate another tableId for long-term locks
|
|
*/
|
|
LongTermTableId = LockMethodTableRename(LockTableId);
|
|
if (!(LongTermTableId))
|
|
elog(ERROR, "InitLockTable: couldn't rename long-term lock table");
|
|
#endif
|
|
|
|
return LockTableId;
|
|
}
|
|
|
|
/*
|
|
* 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)));
|
|
|
|
relation->rd_lockInfo.lockRelId.relId = RelationGetRelid(relation);
|
|
|
|
if (relation->rd_rel->relisshared)
|
|
relation->rd_lockInfo.lockRelId.dbId = InvalidOid;
|
|
else
|
|
relation->rd_lockInfo.lockRelId.dbId = MyDatabaseId;
|
|
}
|
|
|
|
/*
|
|
* LockRelation
|
|
*/
|
|
void
|
|
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;
|
|
|
|
if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
|
|
lockmode, false))
|
|
elog(ERROR, "LockRelation: LockAcquire failed");
|
|
|
|
/*
|
|
* Check to see if the relcache entry has been invalidated while we
|
|
* were waiting to lock it. If so, rebuild it, or elog() trying.
|
|
* Increment the refcount to ensure that RelationFlushRelation will
|
|
* rebuild it and not just delete it.
|
|
*/
|
|
RelationIncrementReferenceCount(relation);
|
|
AcceptInvalidationMessages();
|
|
RelationDecrementReferenceCount(relation);
|
|
}
|
|
|
|
/*
|
|
* 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 the other
|
|
* LockXXX routines in this file, but they could easily be added if needed.
|
|
*/
|
|
bool
|
|
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;
|
|
|
|
if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
|
|
lockmode, true))
|
|
return false;
|
|
|
|
/*
|
|
* Check to see if the relcache entry has been invalidated while we
|
|
* were waiting to lock it. If so, rebuild it, or elog() trying.
|
|
* Increment the refcount to ensure that RelationFlushRelation will
|
|
* rebuild it and not just delete it.
|
|
*/
|
|
RelationIncrementReferenceCount(relation);
|
|
AcceptInvalidationMessages();
|
|
RelationDecrementReferenceCount(relation);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* UnlockRelation
|
|
*/
|
|
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;
|
|
|
|
LockRelease(LockTableId, &tag, GetCurrentTransactionId(), lockmode);
|
|
}
|
|
|
|
/*
|
|
* LockRelationForSession
|
|
*
|
|
* 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 elog(ERROR) occurs,
|
|
* or if the backend exits.
|
|
*
|
|
* Note that one should also grab a transaction-level lock on the rel
|
|
* in any transaction that actually uses the rel, to ensure that the
|
|
* relcache entry is up to date.
|
|
*/
|
|
void
|
|
LockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
|
|
{
|
|
LOCKTAG tag;
|
|
|
|
MemSet(&tag, 0, sizeof(tag));
|
|
tag.relId = relid->relId;
|
|
tag.dbId = relid->dbId;
|
|
tag.objId.blkno = InvalidBlockNumber;
|
|
|
|
if (!LockAcquire(LockTableId, &tag, InvalidTransactionId,
|
|
lockmode, false))
|
|
elog(ERROR, "LockRelationForSession: LockAcquire failed");
|
|
}
|
|
|
|
/*
|
|
* UnlockRelationForSession
|
|
*/
|
|
void
|
|
UnlockRelationForSession(LockRelId *relid, LOCKMODE lockmode)
|
|
{
|
|
LOCKTAG tag;
|
|
|
|
MemSet(&tag, 0, sizeof(tag));
|
|
tag.relId = relid->relId;
|
|
tag.dbId = relid->dbId;
|
|
tag.objId.blkno = InvalidBlockNumber;
|
|
|
|
LockRelease(LockTableId, &tag, InvalidTransactionId, 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.
|
|
*/
|
|
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;
|
|
|
|
if (!LockAcquire(LockTableId, &tag, GetCurrentTransactionId(),
|
|
lockmode, false))
|
|
elog(ERROR, "LockPage: LockAcquire failed");
|
|
}
|
|
|
|
/*
|
|
* UnlockPage
|
|
*/
|
|
void
|
|
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;
|
|
|
|
LockRelease(LockTableId, &tag, GetCurrentTransactionId(), lockmode);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
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;
|
|
|
|
if (!LockAcquire(LockTableId, &tag, xid,
|
|
ExclusiveLock, false))
|
|
elog(ERROR, "XactLockTableInsert: LockAcquire failed");
|
|
}
|
|
|
|
/*
|
|
* XactLockTableWait
|
|
*
|
|
* Wait for the specified transaction to commit or abort.
|
|
*/
|
|
void
|
|
XactLockTableWait(TransactionId xid)
|
|
{
|
|
LOCKTAG tag;
|
|
TransactionId myxid = GetCurrentTransactionId();
|
|
|
|
Assert(!TransactionIdEquals(xid, myxid));
|
|
|
|
MemSet(&tag, 0, sizeof(tag));
|
|
tag.relId = XactLockTableId;
|
|
tag.dbId = InvalidOid;
|
|
tag.objId.xid = xid;
|
|
|
|
if (!LockAcquire(LockTableId, &tag, myxid,
|
|
ShareLock, false))
|
|
elog(ERROR, "XactLockTableWait: LockAcquire failed");
|
|
|
|
LockRelease(LockTableId, &tag, myxid, ShareLock);
|
|
|
|
/*
|
|
* 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);
|
|
}
|