#include "postgres.h"#include "access/htup_details.h"#include "access/slru.h"#include "access/subtrans.h"#include "access/transam.h"#include "access/twophase.h"#include "access/twophase_rmgr.h"#include "access/xact.h"#include "miscadmin.h"#include "storage/bufmgr.h"#include "storage/predicate.h"#include "storage/predicate_internals.h"#include "storage/proc.h"#include "storage/procarray.h"#include "utils/rel.h"#include "utils/snapmgr.h"#include "utils/tqual.h"
Go to the source code of this file.
| #define NPREDICATELOCKTARGETENTS | ( | ) | mul_size(max_predicate_locks_per_xact, add_size(MaxBackends, max_prepared_xacts)) |
Definition at line 246 of file predicate.c.
Referenced by InitPredicateLocks(), and PredicateLockShmemSize().
| #define OLDSERXID_ENTRIESPERPAGE (OLDSERXID_PAGESIZE / OLDSERXID_ENTRYSIZE) |
Definition at line 311 of file predicate.c.
| #define OLDSERXID_ENTRYSIZE sizeof(SerCommitSeqNo) |
Definition at line 310 of file predicate.c.
| #define OLDSERXID_MAX_PAGE |
Min(SLRU_PAGES_PER_SEGMENT * 0x10000 - 1, \ (MaxTransactionId) / OLDSERXID_ENTRIESPERPAGE)
Definition at line 317 of file predicate.c.
Referenced by OldSerXidPagePrecedesLogically().
| #define OLDSERXID_PAGESIZE BLCKSZ |
Definition at line 309 of file predicate.c.
| #define OldSerXidNextPage | ( | page | ) | (((page) >= OLDSERXID_MAX_PAGE) ? 0 : (page) + 1) |
Definition at line 320 of file predicate.c.
Referenced by OldSerXidAdd().
| #define OldSerXidPage | ( | xid | ) | ((((uint32) (xid)) / OLDSERXID_ENTRIESPERPAGE) % (OLDSERXID_MAX_PAGE + 1)) |
Definition at line 326 of file predicate.c.
Referenced by CheckPointPredicate(), OldSerXidAdd(), and OldSerXidGetMinConflictCommitSeqNo().
| #define OldSerXidSegment | ( | page | ) | ((page) / SLRU_PAGES_PER_SEGMENT) |
Definition at line 327 of file predicate.c.
| #define OldSerXidSlruCtl (&OldSerXidSlruCtlData) |
Definition at line 307 of file predicate.c.
Referenced by CheckPointPredicate(), OldSerXidAdd(), OldSerXidGetMinConflictCommitSeqNo(), and OldSerXidInit().
| #define OldSerXidValue | ( | slotno, | ||
| xid | ||||
| ) |
(*((SerCommitSeqNo *) \ (OldSerXidSlruCtl->shared->page_buffer[slotno] + \ ((((uint32) (xid)) % OLDSERXID_ENTRIESPERPAGE) * OLDSERXID_ENTRYSIZE))))
Definition at line 322 of file predicate.c.
Referenced by OldSerXidAdd(), and OldSerXidGetMinConflictCommitSeqNo().
| #define PredicateLockHashCodeFromTargetHashCode | ( | predicatelocktag, | ||
| targethash | ||||
| ) |
((targethash) ^ ((uint32) PointerGetDatum((predicatelocktag)->myXact)) \ << LOG2_NUM_PREDICATELOCK_PARTITIONS)
Definition at line 297 of file predicate.c.
Referenced by CheckTargetForConflictsIn(), ClearOldPredicateLocks(), CreatePredicateLock(), DeleteChildTargetLocks(), DeleteLockTarget(), DropAllPredicateLocksFromTable(), predicatelock_hash(), ReleaseOneSerializableXact(), and TransferPredicateLocksToNewTarget().
| #define PredicateLockHashPartition | ( | hashcode | ) | ((hashcode) % NUM_PREDICATELOCK_PARTITIONS) |
Definition at line 241 of file predicate.c.
| #define PredicateLockHashPartitionLock | ( | hashcode | ) | ((LWLockId) (FirstPredicateLockMgrLock + PredicateLockHashPartition(hashcode))) |
Definition at line 243 of file predicate.c.
Referenced by CheckTargetForConflictsIn(), ClearOldPredicateLocks(), CreatePredicateLock(), DeleteChildTargetLocks(), DeleteLockTarget(), InitPredicateLocks(), PageIsPredicateLocked(), ReleaseOneSerializableXact(), and TransferPredicateLocksToNewTarget().
| #define PredicateLockTargetTagHashCode | ( | predicatelocktargettag | ) | (tag_hash((predicatelocktargettag), sizeof(PREDICATELOCKTARGETTAG))) |
Definition at line 284 of file predicate.c.
Referenced by CheckTargetForConflictsIn(), ClearOldPredicateLocks(), DecrementParentLocks(), DeleteChildTargetLocks(), DropAllPredicateLocksFromTable(), InitPredicateLocks(), PageIsPredicateLocked(), predicatelock_hash(), predicatelock_twophase_recover(), PredicateLockAcquire(), ReleaseOneSerializableXact(), and TransferPredicateLocksToNewTarget().
| #define SxactHasConflictOut | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_CONFLICT_OUT) != 0) |
Definition at line 271 of file predicate.c.
Referenced by CheckForSerializableConflictOut(), OnConflict_CheckForSerializationFailure(), ReleasePredicateLocks(), and SummarizeOldestCommittedSxact().
| #define SxactHasSummaryConflictIn | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_SUMMARY_CONFLICT_IN) != 0) |
Definition at line 264 of file predicate.c.
Referenced by CheckForSerializableConflictOut(), and OnConflict_CheckForSerializationFailure().
| #define SxactHasSummaryConflictOut | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_SUMMARY_CONFLICT_OUT) != 0) |
Definition at line 265 of file predicate.c.
Referenced by CheckForSerializableConflictOut(), OnConflict_CheckForSerializationFailure(), and ReleasePredicateLocks().
| #define SxactIsCommitted | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_COMMITTED) != 0) |
Definition at line 259 of file predicate.c.
Referenced by CheckForSerializableConflictOut(), CheckTargetForConflictsIn(), GetSerializableTransactionSnapshotInt(), OnConflict_CheckForSerializationFailure(), PreCommit_CheckForSerializationFailure(), ReleaseOneSerializableXact(), ReleasePredicateLocks(), and SetNewSxactGlobalXmin().
| #define SxactIsDeferrableWaiting | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_DEFERRABLE_WAITING) != 0) |
Definition at line 272 of file predicate.c.
Referenced by ReleasePredicateLocks().
| #define SxactIsDoomed | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_DOOMED) != 0) |
Definition at line 262 of file predicate.c.
Referenced by CheckForSerializableConflictIn(), CheckForSerializableConflictOut(), CheckTargetForConflictsIn(), GetSerializableTransactionSnapshotInt(), OnConflict_CheckForSerializationFailure(), PreCommit_CheckForSerializationFailure(), ReleasePredicateLocks(), and RWConflictExists().
| #define SxactIsOnFinishedList | ( | sxact | ) | (!SHMQueueIsDetached(&((sxact)->finishedLink))) |
Definition at line 249 of file predicate.c.
Referenced by ReleaseOneSerializableXact(), and ReleasePredicateLocks().
| #define SxactIsPrepared | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_PREPARED) != 0) |
Definition at line 260 of file predicate.c.
Referenced by CheckForSerializableConflictOut(), OnConflict_CheckForSerializationFailure(), PostPrepare_PredicateLocks(), PreCommit_CheckForSerializationFailure(), predicatelock_twophase_recover(), and ReleasePredicateLocks().
| #define SxactIsReadOnly | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_READ_ONLY) != 0) |
Definition at line 263 of file predicate.c.
Referenced by CheckForSerializableConflictOut(), ClearOldPredicateLocks(), FlagSxactUnsafe(), GetSerializableTransactionSnapshotInt(), OnConflict_CheckForSerializationFailure(), PreCommit_CheckForSerializationFailure(), predicatelock_twophase_recover(), ReleasePredicateLocks(), SetPossibleUnsafeConflict(), and SummarizeOldestCommittedSxact().
| #define SxactIsRolledBack | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_ROLLED_BACK) != 0) |
Definition at line 261 of file predicate.c.
Referenced by ReleaseOneSerializableXact(), ReleasePredicateLocks(), and SetNewSxactGlobalXmin().
| #define SxactIsROSafe | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_RO_SAFE) != 0) |
Definition at line 273 of file predicate.c.
Referenced by FlagSxactUnsafe(), GetSafeSnapshot(), ReleasePredicateLocks(), and SerializationNeededForRead().
| #define SxactIsROUnsafe | ( | sxact | ) | (((sxact)->flags & SXACT_FLAG_RO_UNSAFE) != 0) |
Definition at line 274 of file predicate.c.
Referenced by GetSafeSnapshot(), and ReleasePredicateLocks().
| #define TargetTagIsCoveredBy | ( | covered_target, | ||
| covering_target | ||||
| ) |
((GET_PREDICATELOCKTARGETTAG_RELATION(covered_target) == /* (2) */ \ GET_PREDICATELOCKTARGETTAG_RELATION(covering_target)) \ && (GET_PREDICATELOCKTARGETTAG_OFFSET(covering_target) == \ InvalidOffsetNumber) /* (3) */ \ && (((GET_PREDICATELOCKTARGETTAG_OFFSET(covered_target) != \ InvalidOffsetNumber) /* (4a) */ \ && (GET_PREDICATELOCKTARGETTAG_PAGE(covering_target) == \ GET_PREDICATELOCKTARGETTAG_PAGE(covered_target))) \ || ((GET_PREDICATELOCKTARGETTAG_PAGE(covering_target) == \ InvalidBlockNumber) /* (4b) */ \ && (GET_PREDICATELOCKTARGETTAG_PAGE(covered_target) \ != InvalidBlockNumber))) \ && (GET_PREDICATELOCKTARGETTAG_DB(covered_target) == /* (1) */ \ GET_PREDICATELOCKTARGETTAG_DB(covering_target)))
Definition at line 218 of file predicate.c.
Referenced by DeleteChildTargetLocks().
| typedef struct OldSerXidControlData* OldSerXidControl |
Definition at line 337 of file predicate.c.
| typedef struct OldSerXidControlData OldSerXidControlData |
| void AtPrepare_PredicateLocks | ( | void | ) |
Definition at line 4729 of file predicate.c.
References TwoPhasePredicateRecord::data, SERIALIZABLEXACT::flags, TwoPhasePredicateXactRecord::flags, InvalidSerializableXact, TwoPhasePredicateRecord::lockRecord, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myTarget, NULL, offsetof, SERIALIZABLEXACT::predicateLocks, RegisterTwoPhaseRecord(), SerializablePredicateLockListLock, SHMQueueNext(), PREDICATELOCKTARGET::tag, PREDICATELOCK::tag, TwoPhasePredicateLockRecord::target, TWOPHASE_RM_PREDICATELOCK_ID, TwoPhasePredicateRecord::type, PREDICATELOCK::xactLink, TwoPhasePredicateRecord::xactRecord, SERIALIZABLEXACT::xmin, and TwoPhasePredicateXactRecord::xmin.
Referenced by PrepareTransaction().
{
PREDICATELOCK *predlock;
SERIALIZABLEXACT *sxact;
TwoPhasePredicateRecord record;
TwoPhasePredicateXactRecord *xactRecord;
TwoPhasePredicateLockRecord *lockRecord;
sxact = MySerializableXact;
xactRecord = &(record.data.xactRecord);
lockRecord = &(record.data.lockRecord);
if (MySerializableXact == InvalidSerializableXact)
return;
/* Generate a xact record for our SERIALIZABLEXACT */
record.type = TWOPHASEPREDICATERECORD_XACT;
xactRecord->xmin = MySerializableXact->xmin;
xactRecord->flags = MySerializableXact->flags;
/*
* Note that we don't include the list of conflicts in our out in the
* statefile, because new conflicts can be added even after the
* transaction prepares. We'll just make a conservative assumption during
* recovery instead.
*/
RegisterTwoPhaseRecord(TWOPHASE_RM_PREDICATELOCK_ID, 0,
&record, sizeof(record));
/*
* Generate a lock record for each lock.
*
* To do this, we need to walk the predicate lock list in our sxact rather
* than using the local predicate lock table because the latter is not
* guaranteed to be accurate.
*/
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
predlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(sxact->predicateLocks),
offsetof(PREDICATELOCK, xactLink));
while (predlock != NULL)
{
record.type = TWOPHASEPREDICATERECORD_LOCK;
lockRecord->target = predlock->tag.myTarget->tag;
RegisterTwoPhaseRecord(TWOPHASE_RM_PREDICATELOCK_ID, 0,
&record, sizeof(record));
predlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(predlock->xactLink),
offsetof(PREDICATELOCK, xactLink));
}
LWLockRelease(SerializablePredicateLockListLock);
}
| static bool CheckAndPromotePredicateLockRequest | ( | const PREDICATELOCKTARGETTAG * | reqtag | ) | [static] |
Definition at line 2170 of file predicate.c.
References LOCALPREDICATELOCK::childLocks, GetParentPredicateLockTag(), HASH_ENTER, hash_search(), LOCALPREDICATELOCK::held, PredicateLockAcquire(), and PredicateLockPromotionThreshold().
Referenced by PredicateLockAcquire().
{
PREDICATELOCKTARGETTAG targettag,
nexttag,
promotiontag;
LOCALPREDICATELOCK *parentlock;
bool found,
promote;
promote = false;
targettag = *reqtag;
/* check parents iteratively */
while (GetParentPredicateLockTag(&targettag, &nexttag))
{
targettag = nexttag;
parentlock = (LOCALPREDICATELOCK *) hash_search(LocalPredicateLockHash,
&targettag,
HASH_ENTER,
&found);
if (!found)
{
parentlock->held = false;
parentlock->childLocks = 1;
}
else
parentlock->childLocks++;
if (parentlock->childLocks >=
PredicateLockPromotionThreshold(&targettag))
{
/*
* We should promote to this parent lock. Continue to check its
* ancestors, however, both to get their child counts right and to
* check whether we should just go ahead and promote to one of
* them.
*/
promotiontag = targettag;
promote = true;
}
}
if (promote)
{
/* acquire coarsest ancestor eligible for promotion */
PredicateLockAcquire(&promotiontag);
return true;
}
else
return false;
}
Definition at line 4249 of file predicate.c.
References BufferGetBlockNumber(), BufferIsValid, CheckTargetForConflictsIn(), RelFileNode::dbNode, ereport, errcode(), errdetail_internal(), errhint(), errmsg(), ERROR, HeapTupleHeaderGetXmin, ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, MyXactDidWrite, NULL, RelationData::rd_id, RelationData::rd_node, SerializationNeededForWrite(), SET_PREDICATELOCKTARGETTAG_PAGE, SET_PREDICATELOCKTARGETTAG_RELATION, SET_PREDICATELOCKTARGETTAG_TUPLE, SxactIsDoomed, HeapTupleHeaderData::t_ctid, and HeapTupleData::t_data.
Referenced by _bt_doinsert(), heap_delete(), heap_insert(), heap_multi_insert(), heap_update(), and index_insert().
{
PREDICATELOCKTARGETTAG targettag;
if (!SerializationNeededForWrite(relation))
return;
/* Check if someone else has already decided that we need to die */
if (SxactIsDoomed(MySerializableXact))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, during conflict in checking."),
errhint("The transaction might succeed if retried.")));
/*
* We're doing a write which might cause rw-conflicts now or later.
* Memorize that fact.
*/
MyXactDidWrite = true;
/*
* It is important that we check for locks from the finest granularity to
* the coarsest granularity, so that granularity promotion doesn't cause
* us to miss a lock. The new (coarser) lock will be acquired before the
* old (finer) locks are released.
*
* It is not possible to take and hold a lock across the checks for all
* granularities because each target could be in a separate partition.
*/
if (tuple != NULL)
{
SET_PREDICATELOCKTARGETTAG_TUPLE(targettag,
relation->rd_node.dbNode,
relation->rd_id,
ItemPointerGetBlockNumber(&(tuple->t_data->t_ctid)),
ItemPointerGetOffsetNumber(&(tuple->t_data->t_ctid)),
HeapTupleHeaderGetXmin(tuple->t_data));
CheckTargetForConflictsIn(&targettag);
}
if (BufferIsValid(buffer))
{
SET_PREDICATELOCKTARGETTAG_PAGE(targettag,
relation->rd_node.dbNode,
relation->rd_id,
BufferGetBlockNumber(buffer));
CheckTargetForConflictsIn(&targettag);
}
SET_PREDICATELOCKTARGETTAG_RELATION(targettag,
relation->rd_node.dbNode,
relation->rd_id);
CheckTargetForConflictsIn(&targettag);
}
| void CheckForSerializableConflictOut | ( | bool | visible, | |
| Relation | relation, | |||
| HeapTuple | tuple, | |||
| Buffer | buffer, | |||
| Snapshot | snapshot | |||
| ) |
Definition at line 3868 of file predicate.c.
References Assert, SERIALIZABLEXACT::earliestOutConflictCommit, elog, ereport, errcode(), errdetail_internal(), errhint(), errmsg(), ERROR, FlagRWConflict(), SERIALIZABLEXACT::flags, GetTopTransactionIdIfAny(), HASH_FIND, hash_search(), HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleSatisfiesVacuum(), SERIALIZABLEXACT::inConflicts, InvalidSerCommitSeqNo, SERIALIZABLEXACT::lastCommitBeforeSnapshot, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SERIALIZABLEXID::myXact, NULL, OldSerXidGetMinConflictCommitSeqNo(), RWConflictExists(), SERIALIZABLEXACT::SeqNo, SerializableXactHashLock, SerializationNeededForRead(), SHMQueueEmpty(), SubTransGetTopmostTransaction(), SxactHasConflictOut, SxactHasSummaryConflictIn, SxactHasSummaryConflictOut, SxactIsCommitted, SxactIsDoomed, SxactIsPrepared, SxactIsReadOnly, HeapTupleData::t_data, SERIALIZABLEXACT::topXid, TransactionIdEquals, TransactionIdFollowsOrEquals(), TransactionIdIsValid, TransactionIdPrecedes(), TransactionXmin, SERIALIZABLEXIDTAG::xid, and XidIsConcurrent().
Referenced by bitgetpage(), heap_fetch(), heap_get_latest_tid(), heap_hot_search_buffer(), heapgetpage(), and heapgettup().
{
TransactionId xid;
SERIALIZABLEXIDTAG sxidtag;
SERIALIZABLEXID *sxid;
SERIALIZABLEXACT *sxact;
HTSV_Result htsvResult;
if (!SerializationNeededForRead(relation, snapshot))
return;
/* Check if someone else has already decided that we need to die */
if (SxactIsDoomed(MySerializableXact))
{
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, during conflict out checking."),
errhint("The transaction might succeed if retried.")));
}
/*
* Check to see whether the tuple has been written to by a concurrent
* transaction, either to create it not visible to us, or to delete it
* while it is visible to us. The "visible" bool indicates whether the
* tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else
* is going on with it.
*/
htsvResult = HeapTupleSatisfiesVacuum(tuple->t_data, TransactionXmin, buffer);
switch (htsvResult)
{
case HEAPTUPLE_LIVE:
if (visible)
return;
xid = HeapTupleHeaderGetXmin(tuple->t_data);
break;
case HEAPTUPLE_RECENTLY_DEAD:
if (!visible)
return;
xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
break;
case HEAPTUPLE_DELETE_IN_PROGRESS:
xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
break;
case HEAPTUPLE_INSERT_IN_PROGRESS:
xid = HeapTupleHeaderGetXmin(tuple->t_data);
break;
case HEAPTUPLE_DEAD:
return;
default:
/*
* The only way to get to this default clause is if a new value is
* added to the enum type without adding it to this switch
* statement. That's a bug, so elog.
*/
elog(ERROR, "unrecognized return value from HeapTupleSatisfiesVacuum: %u", htsvResult);
/*
* In spite of having all enum values covered and calling elog on
* this default, some compilers think this is a code path which
* allows xid to be used below without initialization. Silence
* that warning.
*/
xid = InvalidTransactionId;
}
Assert(TransactionIdIsValid(xid));
Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
/*
* Find top level xid. Bail out if xid is too early to be a conflict, or
* if it's our own xid.
*/
if (TransactionIdEquals(xid, GetTopTransactionIdIfAny()))
return;
xid = SubTransGetTopmostTransaction(xid);
if (TransactionIdPrecedes(xid, TransactionXmin))
return;
if (TransactionIdEquals(xid, GetTopTransactionIdIfAny()))
return;
/*
* Find sxact or summarized info for the top level xid.
*/
sxidtag.xid = xid;
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
sxid = (SERIALIZABLEXID *)
hash_search(SerializableXidHash, &sxidtag, HASH_FIND, NULL);
if (!sxid)
{
/*
* Transaction not found in "normal" SSI structures. Check whether it
* got pushed out to SLRU storage for "old committed" transactions.
*/
SerCommitSeqNo conflictCommitSeqNo;
conflictCommitSeqNo = OldSerXidGetMinConflictCommitSeqNo(xid);
if (conflictCommitSeqNo != 0)
{
if (conflictCommitSeqNo != InvalidSerCommitSeqNo
&& (!SxactIsReadOnly(MySerializableXact)
|| conflictCommitSeqNo
<= MySerializableXact->SeqNo.lastCommitBeforeSnapshot))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on conflict out to old pivot %u.", xid),
errhint("The transaction might succeed if retried.")));
if (SxactHasSummaryConflictIn(MySerializableXact)
|| !SHMQueueEmpty(&MySerializableXact->inConflicts))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, with conflict out to old committed transaction %u.", xid),
errhint("The transaction might succeed if retried.")));
MySerializableXact->flags |= SXACT_FLAG_SUMMARY_CONFLICT_OUT;
}
/* It's not serializable or otherwise not important. */
LWLockRelease(SerializableXactHashLock);
return;
}
sxact = sxid->myXact;
Assert(TransactionIdEquals(sxact->topXid, xid));
if (sxact == MySerializableXact || SxactIsDoomed(sxact))
{
/* Can't conflict with ourself or a transaction that will roll back. */
LWLockRelease(SerializableXactHashLock);
return;
}
/*
* We have a conflict out to a transaction which has a conflict out to a
* summarized transaction. That summarized transaction must have
* committed first, and we can't tell when it committed in relation to our
* snapshot acquisition, so something needs to be canceled.
*/
if (SxactHasSummaryConflictOut(sxact))
{
if (!SxactIsPrepared(sxact))
{
sxact->flags |= SXACT_FLAG_DOOMED;
LWLockRelease(SerializableXactHashLock);
return;
}
else
{
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on conflict out to old pivot."),
errhint("The transaction might succeed if retried.")));
}
}
/*
* If this is a read-only transaction and the writing transaction has
* committed, and it doesn't have a rw-conflict to a transaction which
* committed before it, no conflict.
*/
if (SxactIsReadOnly(MySerializableXact)
&& SxactIsCommitted(sxact)
&& !SxactHasSummaryConflictOut(sxact)
&& (!SxactHasConflictOut(sxact)
|| MySerializableXact->SeqNo.lastCommitBeforeSnapshot < sxact->SeqNo.earliestOutConflictCommit))
{
/* Read-only transaction will appear to run first. No conflict. */
LWLockRelease(SerializableXactHashLock);
return;
}
if (!XidIsConcurrent(xid))
{
/* This write was already in our snapshot; no conflict. */
LWLockRelease(SerializableXactHashLock);
return;
}
if (RWConflictExists(MySerializableXact, sxact))
{
/* We don't want duplicate conflict records in the list. */
LWLockRelease(SerializableXactHashLock);
return;
}
/*
* Flag the conflict. But first, if this conflict creates a dangerous
* structure, ereport an error.
*/
FlagRWConflict(MySerializableXact, sxact);
LWLockRelease(SerializableXactHashLock);
}
| void CheckPointPredicate | ( | void | ) |
Definition at line 1026 of file predicate.c.
References OldSerXidControlData::headPage, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), OldSerXidLock, OldSerXidPage, OldSerXidSlruCtl, SimpleLruFlush(), SimpleLruTruncate(), OldSerXidControlData::tailXid, and TransactionIdIsValid.
Referenced by CheckPointGuts().
{
int tailPage;
LWLockAcquire(OldSerXidLock, LW_EXCLUSIVE);
/* Exit quickly if the SLRU is currently not in use. */
if (oldSerXidControl->headPage < 0)
{
LWLockRelease(OldSerXidLock);
return;
}
if (TransactionIdIsValid(oldSerXidControl->tailXid))
{
/* We can truncate the SLRU up to the page containing tailXid */
tailPage = OldSerXidPage(oldSerXidControl->tailXid);
}
else
{
/*
* The SLRU is no longer needed. Truncate to head before we set head
* invalid.
*
* XXX: It's possible that the SLRU is not needed again until XID
* wrap-around has happened, so that the segment containing headPage
* that we leave behind will appear to be new again. In that case it
* won't be removed until XID horizon advances enough to make it
* current again.
*/
tailPage = oldSerXidControl->headPage;
oldSerXidControl->headPage = -1;
}
LWLockRelease(OldSerXidLock);
/* Truncate away pages that are no longer required */
SimpleLruTruncate(OldSerXidSlruCtl, tailPage);
/*
* Flush dirty SLRU pages to disk
*
* This is not actually necessary from a correctness point of view. We do
* it merely as a debugging aid.
*
* We're doing this after the truncation to avoid writing pages right
* before deleting the file in which they sit, which would be completely
* pointless.
*/
SimpleLruFlush(OldSerXidSlruCtl, true);
}
| void CheckTableForSerializableConflictIn | ( | Relation | relation | ) |
Definition at line 4334 of file predicate.c.
References Assert, RelFileNode::dbNode, FirstPredicateLockMgrLock, FlagRWConflict(), GET_PREDICATELOCKTARGETTAG_DB, GET_PREDICATELOCKTARGETTAG_RELATION, hash_seq_init(), hash_seq_search(), i, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myXact, MyXactDidWrite, NULL, offsetof, PREDICATELOCKTARGET::predicateLocks, RelationData::rd_id, RelationData::rd_index, RelationData::rd_node, RWConflictExists(), SerializablePredicateLockListLock, SerializableXactHashLock, SerializationNeededForWrite(), SHMQueueNext(), PredXactListData::SxactGlobalXmin, PREDICATELOCK::tag, PREDICATELOCKTARGET::tag, PREDICATELOCK::targetLink, and TransactionIdIsValid.
Referenced by ExecuteTruncate(), and heap_drop_with_catalog().
{
HASH_SEQ_STATUS seqstat;
PREDICATELOCKTARGET *target;
Oid dbId;
Oid heapId;
int i;
/*
* Bail out quickly if there are no serializable transactions running.
* It's safe to check this without taking locks because the caller is
* holding an ACCESS EXCLUSIVE lock on the relation. No new locks which
* would matter here can be acquired while that is held.
*/
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
return;
if (!SerializationNeededForWrite(relation))
return;
/*
* We're doing a write which might cause rw-conflicts now or later.
* Memorize that fact.
*/
MyXactDidWrite = true;
Assert(relation->rd_index == NULL); /* not an index relation */
dbId = relation->rd_node.dbNode;
heapId = relation->rd_id;
LWLockAcquire(SerializablePredicateLockListLock, LW_EXCLUSIVE);
for (i = 0; i < NUM_PREDICATELOCK_PARTITIONS; i++)
LWLockAcquire(FirstPredicateLockMgrLock + i, LW_SHARED);
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
/* Scan through target list */
hash_seq_init(&seqstat, PredicateLockTargetHash);
while ((target = (PREDICATELOCKTARGET *) hash_seq_search(&seqstat)))
{
PREDICATELOCK *predlock;
/*
* Check whether this is a target which needs attention.
*/
if (GET_PREDICATELOCKTARGETTAG_RELATION(target->tag) != heapId)
continue; /* wrong relation id */
if (GET_PREDICATELOCKTARGETTAG_DB(target->tag) != dbId)
continue; /* wrong database id */
/*
* Loop through locks for this target and flag conflicts.
*/
predlock = (PREDICATELOCK *)
SHMQueueNext(&(target->predicateLocks),
&(target->predicateLocks),
offsetof(PREDICATELOCK, targetLink));
while (predlock)
{
PREDICATELOCK *nextpredlock;
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&(target->predicateLocks),
&(predlock->targetLink),
offsetof(PREDICATELOCK, targetLink));
if (predlock->tag.myXact != MySerializableXact
&& !RWConflictExists(predlock->tag.myXact, MySerializableXact))
{
FlagRWConflict(predlock->tag.myXact, MySerializableXact);
}
predlock = nextpredlock;
}
}
/* Release locks in reverse order */
LWLockRelease(SerializableXactHashLock);
for (i = NUM_PREDICATELOCK_PARTITIONS - 1; i >= 0; i--)
LWLockRelease(FirstPredicateLockMgrLock + i);
LWLockRelease(SerializablePredicateLockListLock);
}
| static void CheckTargetForConflictsIn | ( | PREDICATELOCKTARGETTAG * | targettag | ) | [static] |
Definition at line 4071 of file predicate.c.
References Assert, DecrementParentLocks(), SERIALIZABLEXACT::finishedBefore, FlagRWConflict(), GET_PREDICATELOCKTARGETTAG_OFFSET, GetTransactionSnapshot(), HASH_FIND, HASH_REMOVE, hash_search_with_hash_value(), InvalidSerializableXact, IsSubTransaction(), LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myXact, NULL, offsetof, PredicateLockHashCodeFromTargetHashCode, PredicateLockHashPartitionLock, PREDICATELOCKTARGET::predicateLocks, PredicateLockTargetTagHashCode, RemoveTargetIfNoLongerUsed(), RWConflictExists(), SerializablePredicateLockListLock, SerializableXactHashLock, SHMQueueDelete(), SHMQueueNext(), SxactIsCommitted, SxactIsDoomed, PREDICATELOCK::tag, PREDICATELOCK::targetLink, TransactionIdPrecedes(), and PREDICATELOCK::xactLink.
Referenced by CheckForSerializableConflictIn().
{
uint32 targettaghash;
LWLockId partitionLock;
PREDICATELOCKTARGET *target;
PREDICATELOCK *predlock;
PREDICATELOCK *mypredlock = NULL;
PREDICATELOCKTAG mypredlocktag;
Assert(MySerializableXact != InvalidSerializableXact);
/*
* The same hash and LW lock apply to the lock target and the lock itself.
*/
targettaghash = PredicateLockTargetTagHashCode(targettag);
partitionLock = PredicateLockHashPartitionLock(targettaghash);
LWLockAcquire(partitionLock, LW_SHARED);
target = (PREDICATELOCKTARGET *)
hash_search_with_hash_value(PredicateLockTargetHash,
targettag, targettaghash,
HASH_FIND, NULL);
if (!target)
{
/* Nothing has this target locked; we're done here. */
LWLockRelease(partitionLock);
return;
}
/*
* Each lock for an overlapping transaction represents a conflict: a
* rw-dependency in to this transaction.
*/
predlock = (PREDICATELOCK *)
SHMQueueNext(&(target->predicateLocks),
&(target->predicateLocks),
offsetof(PREDICATELOCK, targetLink));
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
while (predlock)
{
SHM_QUEUE *predlocktargetlink;
PREDICATELOCK *nextpredlock;
SERIALIZABLEXACT *sxact;
predlocktargetlink = &(predlock->targetLink);
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&(target->predicateLocks),
predlocktargetlink,
offsetof(PREDICATELOCK, targetLink));
sxact = predlock->tag.myXact;
if (sxact == MySerializableXact)
{
/*
* If we're getting a write lock on a tuple, we don't need a
* predicate (SIREAD) lock on the same tuple. We can safely remove
* our SIREAD lock, but we'll defer doing so until after the loop
* because that requires upgrading to an exclusive partition lock.
*
* We can't use this optimization within a subtransaction because
* the subtransaction could roll back, and we would be left
* without any lock at the top level.
*/
if (!IsSubTransaction()
&& GET_PREDICATELOCKTARGETTAG_OFFSET(*targettag))
{
mypredlock = predlock;
mypredlocktag = predlock->tag;
}
}
else if (!SxactIsDoomed(sxact)
&& (!SxactIsCommitted(sxact)
|| TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
sxact->finishedBefore))
&& !RWConflictExists(sxact, MySerializableXact))
{
LWLockRelease(SerializableXactHashLock);
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/*
* Re-check after getting exclusive lock because the other
* transaction may have flagged a conflict.
*/
if (!SxactIsDoomed(sxact)
&& (!SxactIsCommitted(sxact)
|| TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
sxact->finishedBefore))
&& !RWConflictExists(sxact, MySerializableXact))
{
FlagRWConflict(sxact, MySerializableXact);
}
LWLockRelease(SerializableXactHashLock);
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
}
predlock = nextpredlock;
}
LWLockRelease(SerializableXactHashLock);
LWLockRelease(partitionLock);
/*
* If we found one of our own SIREAD locks to remove, remove it now.
*
* At this point our transaction already has an ExclusiveRowLock on the
* relation, so we are OK to drop the predicate lock on the tuple, if
* found, without fearing that another write against the tuple will occur
* before the MVCC information makes it to the buffer.
*/
if (mypredlock != NULL)
{
uint32 predlockhashcode;
PREDICATELOCK *rmpredlock;
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
LWLockAcquire(partitionLock, LW_EXCLUSIVE);
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/*
* Remove the predicate lock from shared memory, if it wasn't removed
* while the locks were released. One way that could happen is from
* autovacuum cleaning up an index.
*/
predlockhashcode = PredicateLockHashCodeFromTargetHashCode
(&mypredlocktag, targettaghash);
rmpredlock = (PREDICATELOCK *)
hash_search_with_hash_value(PredicateLockHash,
&mypredlocktag,
predlockhashcode,
HASH_FIND, NULL);
if (rmpredlock != NULL)
{
Assert(rmpredlock == mypredlock);
SHMQueueDelete(&(mypredlock->targetLink));
SHMQueueDelete(&(mypredlock->xactLink));
rmpredlock = (PREDICATELOCK *)
hash_search_with_hash_value(PredicateLockHash,
&mypredlocktag,
predlockhashcode,
HASH_REMOVE, NULL);
Assert(rmpredlock == mypredlock);
RemoveTargetIfNoLongerUsed(target, targettaghash);
}
LWLockRelease(SerializableXactHashLock);
LWLockRelease(partitionLock);
LWLockRelease(SerializablePredicateLockListLock);
if (rmpredlock != NULL)
{
/*
* Remove entry in local lock table if it exists. It's OK if it
* doesn't exist; that means the lock was transferred to a new
* target by a different backend.
*/
hash_search_with_hash_value(LocalPredicateLockHash,
targettag, targettaghash,
HASH_REMOVE, NULL);
DecrementParentLocks(targettag);
}
}
}
| static void ClearOldPredicateLocks | ( | void | ) | [static] |
Definition at line 3509 of file predicate.c.
References Assert, PredXactListData::CanPartialClearThrough, PREDICATELOCK::commitSeqNo, SERIALIZABLEXACT::commitSeqNo, SERIALIZABLEXACT::finishedBefore, SERIALIZABLEXACT::finishedLink, HASH_REMOVE, hash_search_with_hash_value(), PredXactListData::HavePartialClearedThrough, InvalidSerCommitSeqNo, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myTarget, NULL, offsetof, PredicateLockHashCodeFromTargetHashCode, PredicateLockHashPartitionLock, SERIALIZABLEXACT::predicateLocks, PredicateLockTargetTagHashCode, ReleaseOneSerializableXact(), RemoveTargetIfNoLongerUsed(), SerializableFinishedListLock, SerializablePredicateLockListLock, SerializableXactHashLock, SHMQueueDelete(), SHMQueueNext(), PredXactListData::SxactGlobalXmin, SxactIsReadOnly, PREDICATELOCKTARGET::tag, PREDICATELOCK::tag, PREDICATELOCK::targetLink, TransactionIdIsValid, TransactionIdPrecedesOrEquals(), and PREDICATELOCK::xactLink.
Referenced by ReleasePredicateLocks().
{
SERIALIZABLEXACT *finishedSxact;
PREDICATELOCK *predlock;
/*
* Loop through finished transactions. They are in commit order, so we can
* stop as soon as we find one that's still interesting.
*/
LWLockAcquire(SerializableFinishedListLock, LW_EXCLUSIVE);
finishedSxact = (SERIALIZABLEXACT *)
SHMQueueNext(FinishedSerializableTransactions,
FinishedSerializableTransactions,
offsetof(SERIALIZABLEXACT, finishedLink));
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
while (finishedSxact)
{
SERIALIZABLEXACT *nextSxact;
nextSxact = (SERIALIZABLEXACT *)
SHMQueueNext(FinishedSerializableTransactions,
&(finishedSxact->finishedLink),
offsetof(SERIALIZABLEXACT, finishedLink));
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin)
|| TransactionIdPrecedesOrEquals(finishedSxact->finishedBefore,
PredXact->SxactGlobalXmin))
{
/*
* This transaction committed before any in-progress transaction
* took its snapshot. It's no longer interesting.
*/
LWLockRelease(SerializableXactHashLock);
SHMQueueDelete(&(finishedSxact->finishedLink));
ReleaseOneSerializableXact(finishedSxact, false, false);
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
}
else if (finishedSxact->commitSeqNo > PredXact->HavePartialClearedThrough
&& finishedSxact->commitSeqNo <= PredXact->CanPartialClearThrough)
{
/*
* Any active transactions that took their snapshot before this
* transaction committed are read-only, so we can clear part of
* its state.
*/
LWLockRelease(SerializableXactHashLock);
if (SxactIsReadOnly(finishedSxact))
{
/* A read-only transaction can be removed entirely */
SHMQueueDelete(&(finishedSxact->finishedLink));
ReleaseOneSerializableXact(finishedSxact, false, false);
}
else
{
/*
* A read-write transaction can only be partially cleared. We
* need to keep the SERIALIZABLEXACT but can release the
* SIREAD locks and conflicts in.
*/
ReleaseOneSerializableXact(finishedSxact, true, false);
}
PredXact->HavePartialClearedThrough = finishedSxact->commitSeqNo;
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
}
else
{
/* Still interesting. */
break;
}
finishedSxact = nextSxact;
}
LWLockRelease(SerializableXactHashLock);
/*
* Loop through predicate locks on dummy transaction for summarized data.
*/
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
predlock = (PREDICATELOCK *)
SHMQueueNext(&OldCommittedSxact->predicateLocks,
&OldCommittedSxact->predicateLocks,
offsetof(PREDICATELOCK, xactLink));
while (predlock)
{
PREDICATELOCK *nextpredlock;
bool canDoPartialCleanup;
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&OldCommittedSxact->predicateLocks,
&predlock->xactLink,
offsetof(PREDICATELOCK, xactLink));
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
Assert(predlock->commitSeqNo != 0);
Assert(predlock->commitSeqNo != InvalidSerCommitSeqNo);
canDoPartialCleanup = (predlock->commitSeqNo <= PredXact->CanPartialClearThrough);
LWLockRelease(SerializableXactHashLock);
/*
* If this lock originally belonged to an old enough transaction, we
* can release it.
*/
if (canDoPartialCleanup)
{
PREDICATELOCKTAG tag;
PREDICATELOCKTARGET *target;
PREDICATELOCKTARGETTAG targettag;
uint32 targettaghash;
LWLockId partitionLock;
tag = predlock->tag;
target = tag.myTarget;
targettag = target->tag;
targettaghash = PredicateLockTargetTagHashCode(&targettag);
partitionLock = PredicateLockHashPartitionLock(targettaghash);
LWLockAcquire(partitionLock, LW_EXCLUSIVE);
SHMQueueDelete(&(predlock->targetLink));
SHMQueueDelete(&(predlock->xactLink));
hash_search_with_hash_value(PredicateLockHash, &tag,
PredicateLockHashCodeFromTargetHashCode(&tag,
targettaghash),
HASH_REMOVE, NULL);
RemoveTargetIfNoLongerUsed(target, targettaghash);
LWLockRelease(partitionLock);
}
predlock = nextpredlock;
}
LWLockRelease(SerializablePredicateLockListLock);
LWLockRelease(SerializableFinishedListLock);
}
| static bool CoarserLockCovers | ( | const PREDICATELOCKTARGETTAG * | newtargettag | ) | [static] |
Definition at line 1958 of file predicate.c.
References GetParentPredicateLockTag(), and PredicateLockExists().
Referenced by PredicateLockAcquire().
{
PREDICATELOCKTARGETTAG targettag,
parenttag;
targettag = *newtargettag;
/* check parents iteratively until no more */
while (GetParentPredicateLockTag(&targettag, &parenttag))
{
targettag = parenttag;
if (PredicateLockExists(&targettag))
return true;
}
/* no more parents to check; lock is not covered */
return false;
}
| static void CreatePredicateLock | ( | const PREDICATELOCKTARGETTAG * | targettag, | |
| uint32 | targettaghash, | |||
| SERIALIZABLEXACT * | sxact | |||
| ) | [static] |
Definition at line 2297 of file predicate.c.
References PREDICATELOCK::commitSeqNo, ereport, errcode(), errhint(), errmsg(), ERROR, HASH_ENTER_NULL, hash_search_with_hash_value(), LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myTarget, PREDICATELOCKTAG::myXact, PredicateLockHashCodeFromTargetHashCode, PredicateLockHashPartitionLock, SERIALIZABLEXACT::predicateLocks, PREDICATELOCKTARGET::predicateLocks, SerializablePredicateLockListLock, SHMQueueInit(), SHMQueueInsertBefore(), PREDICATELOCK::targetLink, and PREDICATELOCK::xactLink.
Referenced by predicatelock_twophase_recover(), and PredicateLockAcquire().
{
PREDICATELOCKTARGET *target;
PREDICATELOCKTAG locktag;
PREDICATELOCK *lock;
LWLockId partitionLock;
bool found;
partitionLock = PredicateLockHashPartitionLock(targettaghash);
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
LWLockAcquire(partitionLock, LW_EXCLUSIVE);
/* Make sure that the target is represented. */
target = (PREDICATELOCKTARGET *)
hash_search_with_hash_value(PredicateLockTargetHash,
targettag, targettaghash,
HASH_ENTER_NULL, &found);
if (!target)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of shared memory"),
errhint("You might need to increase max_pred_locks_per_transaction.")));
if (!found)
SHMQueueInit(&(target->predicateLocks));
/* We've got the sxact and target, make sure they're joined. */
locktag.myTarget = target;
locktag.myXact = sxact;
lock = (PREDICATELOCK *)
hash_search_with_hash_value(PredicateLockHash, &locktag,
PredicateLockHashCodeFromTargetHashCode(&locktag, targettaghash),
HASH_ENTER_NULL, &found);
if (!lock)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of shared memory"),
errhint("You might need to increase max_pred_locks_per_transaction.")));
if (!found)
{
SHMQueueInsertBefore(&(target->predicateLocks), &(lock->targetLink));
SHMQueueInsertBefore(&(sxact->predicateLocks),
&(lock->xactLink));
lock->commitSeqNo = InvalidSerCommitSeqNo;
}
LWLockRelease(partitionLock);
LWLockRelease(SerializablePredicateLockListLock);
}
| static SERIALIZABLEXACT * CreatePredXact | ( | void | ) | [static] |
Definition at line 551 of file predicate.c.
References PredXactListData::activeList, PredXactListData::availableList, PredXactListElementData::link, offsetof, SHMQueueDelete(), SHMQueueInsertBefore(), SHMQueueNext(), and PredXactListElementData::sxact.
Referenced by GetSerializableTransactionSnapshotInt(), InitPredicateLocks(), and predicatelock_twophase_recover().
{
PredXactListElement ptle;
ptle = (PredXactListElement)
SHMQueueNext(&PredXact->availableList,
&PredXact->availableList,
offsetof(PredXactListElementData, link));
if (!ptle)
return NULL;
SHMQueueDelete(&ptle->link);
SHMQueueInsertBefore(&PredXact->activeList, &ptle->link);
return &ptle->sxact;
}
| static void DecrementParentLocks | ( | const PREDICATELOCKTARGETTAG * | targettag | ) | [static] |
Definition at line 2235 of file predicate.c.
References Assert, LOCALPREDICATELOCK::childLocks, GetParentPredicateLockTag(), HASH_FIND, HASH_REMOVE, hash_search_with_hash_value(), LOCALPREDICATELOCK::held, NULL, and PredicateLockTargetTagHashCode.
Referenced by CheckTargetForConflictsIn(), and DeleteChildTargetLocks().
{
PREDICATELOCKTARGETTAG parenttag,
nexttag;
parenttag = *targettag;
while (GetParentPredicateLockTag(&parenttag, &nexttag))
{
uint32 targettaghash;
LOCALPREDICATELOCK *parentlock,
*rmlock PG_USED_FOR_ASSERTS_ONLY;
parenttag = nexttag;
targettaghash = PredicateLockTargetTagHashCode(&parenttag);
parentlock = (LOCALPREDICATELOCK *)
hash_search_with_hash_value(LocalPredicateLockHash,
&parenttag, targettaghash,
HASH_FIND, NULL);
/*
* There's a small chance the parent lock doesn't exist in the lock
* table. This can happen if we prematurely removed it because an
* index split caused the child refcount to be off.
*/
if (parentlock == NULL)
continue;
parentlock->childLocks--;
/*
* Under similar circumstances the parent lock's refcount might be
* zero. This only happens if we're holding that lock (otherwise we
* would have removed the entry).
*/
if (parentlock->childLocks < 0)
{
Assert(parentlock->held);
parentlock->childLocks = 0;
}
if ((parentlock->childLocks == 0) && (!parentlock->held))
{
rmlock = (LOCALPREDICATELOCK *)
hash_search_with_hash_value(LocalPredicateLockHash,
&parenttag, targettaghash,
HASH_REMOVE, NULL);
Assert(rmlock == parentlock);
}
}
}
| static void DeleteChildTargetLocks | ( | const PREDICATELOCKTARGETTAG * | newtargettag | ) | [static] |
Definition at line 2059 of file predicate.c.
References Assert, DecrementParentLocks(), HASH_REMOVE, hash_search_with_hash_value(), LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myTarget, PREDICATELOCKTAG::myXact, NULL, offsetof, PredicateLockHashCodeFromTargetHashCode, PredicateLockHashPartitionLock, SERIALIZABLEXACT::predicateLocks, PredicateLockTargetTagHashCode, RemoveTargetIfNoLongerUsed(), SerializablePredicateLockListLock, SHMQueueDelete(), SHMQueueNext(), PREDICATELOCKTARGET::tag, PREDICATELOCK::tag, PREDICATELOCK::targetLink, TargetTagIsCoveredBy, and PREDICATELOCK::xactLink.
Referenced by PredicateLockAcquire().
{
SERIALIZABLEXACT *sxact;
PREDICATELOCK *predlock;
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
sxact = MySerializableXact;
predlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(sxact->predicateLocks),
offsetof(PREDICATELOCK, xactLink));
while (predlock)
{
SHM_QUEUE *predlocksxactlink;
PREDICATELOCK *nextpredlock;
PREDICATELOCKTAG oldlocktag;
PREDICATELOCKTARGET *oldtarget;
PREDICATELOCKTARGETTAG oldtargettag;
predlocksxactlink = &(predlock->xactLink);
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
predlocksxactlink,
offsetof(PREDICATELOCK, xactLink));
oldlocktag = predlock->tag;
Assert(oldlocktag.myXact == sxact);
oldtarget = oldlocktag.myTarget;
oldtargettag = oldtarget->tag;
if (TargetTagIsCoveredBy(oldtargettag, *newtargettag))
{
uint32 oldtargettaghash;
LWLockId partitionLock;
PREDICATELOCK *rmpredlock PG_USED_FOR_ASSERTS_ONLY;
oldtargettaghash = PredicateLockTargetTagHashCode(&oldtargettag);
partitionLock = PredicateLockHashPartitionLock(oldtargettaghash);
LWLockAcquire(partitionLock, LW_EXCLUSIVE);
SHMQueueDelete(predlocksxactlink);
SHMQueueDelete(&(predlock->targetLink));
rmpredlock = hash_search_with_hash_value
(PredicateLockHash,
&oldlocktag,
PredicateLockHashCodeFromTargetHashCode(&oldlocktag,
oldtargettaghash),
HASH_REMOVE, NULL);
Assert(rmpredlock == predlock);
RemoveTargetIfNoLongerUsed(oldtarget, oldtargettaghash);
LWLockRelease(partitionLock);
DecrementParentLocks(&oldtargettag);
}
predlock = nextpredlock;
}
LWLockRelease(SerializablePredicateLockListLock);
}
| static void DeleteLockTarget | ( | PREDICATELOCKTARGET * | target, | |
| uint32 | targettaghash | |||
| ) | [static] |
Definition at line 2530 of file predicate.c.
References Assert, HASH_REMOVE, hash_search_with_hash_value(), LW_EXCLUSIVE, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), offsetof, PredicateLockHashCodeFromTargetHashCode, PredicateLockHashPartitionLock, PREDICATELOCKTARGET::predicateLocks, RemoveTargetIfNoLongerUsed(), SerializablePredicateLockListLock, SerializableXactHashLock, SHMQueueDelete(), SHMQueueNext(), PREDICATELOCK::tag, PREDICATELOCK::targetLink, and PREDICATELOCK::xactLink.
Referenced by TransferPredicateLocksToNewTarget().
{
PREDICATELOCK *predlock;
SHM_QUEUE *predlocktargetlink;
PREDICATELOCK *nextpredlock;
bool found;
Assert(LWLockHeldByMe(SerializablePredicateLockListLock));
Assert(LWLockHeldByMe(PredicateLockHashPartitionLock(targettaghash)));
predlock = (PREDICATELOCK *)
SHMQueueNext(&(target->predicateLocks),
&(target->predicateLocks),
offsetof(PREDICATELOCK, targetLink));
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
while (predlock)
{
predlocktargetlink = &(predlock->targetLink);
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&(target->predicateLocks),
predlocktargetlink,
offsetof(PREDICATELOCK, targetLink));
SHMQueueDelete(&(predlock->xactLink));
SHMQueueDelete(&(predlock->targetLink));
hash_search_with_hash_value
(PredicateLockHash,
&predlock->tag,
PredicateLockHashCodeFromTargetHashCode(&predlock->tag,
targettaghash),
HASH_REMOVE, &found);
Assert(found);
predlock = nextpredlock;
}
LWLockRelease(SerializableXactHashLock);
/* Remove the target itself, if possible. */
RemoveTargetIfNoLongerUsed(target, targettaghash);
}
Definition at line 2816 of file predicate.c.
References Assert, PREDICATELOCK::commitSeqNo, RelFileNode::dbNode, FirstPredicateLockMgrLock, GET_PREDICATELOCKTARGETTAG_DB, GET_PREDICATELOCKTARGETTAG_RELATION, GET_PREDICATELOCKTARGETTAG_TYPE, HASH_ENTER, HASH_REMOVE, hash_search(), hash_search_with_hash_value(), hash_seq_init(), hash_seq_search(), i, InvalidOid, InvalidSerCommitSeqNo, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myTarget, PREDICATELOCKTAG::myXact, NULL, offsetof, PredicateLockHashCodeFromTargetHashCode, PredicateLockingNeededForRelation(), SERIALIZABLEXACT::predicateLocks, PREDICATELOCKTARGET::predicateLocks, PredicateLockTargetTagHashCode, PREDLOCKTAG_RELATION, RelationData::rd_id, RelationData::rd_index, RelationData::rd_node, RemoveScratchTarget(), RestoreScratchTarget(), SerializablePredicateLockListLock, SerializableXactHashLock, SET_PREDICATELOCKTARGETTAG_RELATION, SHMQueueDelete(), SHMQueueInit(), SHMQueueInsertBefore(), SHMQueueNext(), PredXactListData::SxactGlobalXmin, PREDICATELOCK::tag, PREDICATELOCKTARGET::tag, PREDICATELOCK::targetLink, TransactionIdIsValid, and PREDICATELOCK::xactLink.
Referenced by TransferPredicateLocksToHeapRelation().
{
HASH_SEQ_STATUS seqstat;
PREDICATELOCKTARGET *oldtarget;
PREDICATELOCKTARGET *heaptarget;
Oid dbId;
Oid relId;
Oid heapId;
int i;
bool isIndex;
bool found;
uint32 heaptargettaghash;
/*
* Bail out quickly if there are no serializable transactions running.
* It's safe to check this without taking locks because the caller is
* holding an ACCESS EXCLUSIVE lock on the relation. No new locks which
* would matter here can be acquired while that is held.
*/
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
return;
if (!PredicateLockingNeededForRelation(relation))
return;
dbId = relation->rd_node.dbNode;
relId = relation->rd_id;
if (relation->rd_index == NULL)
{
isIndex = false;
heapId = relId;
}
else
{
isIndex = true;
heapId = relation->rd_index->indrelid;
}
Assert(heapId != InvalidOid);
Assert(transfer || !isIndex); /* index OID only makes sense with
* transfer */
/* Retrieve first time needed, then keep. */
heaptargettaghash = 0;
heaptarget = NULL;
/* Acquire locks on all lock partitions */
LWLockAcquire(SerializablePredicateLockListLock, LW_EXCLUSIVE);
for (i = 0; i < NUM_PREDICATELOCK_PARTITIONS; i++)
LWLockAcquire(FirstPredicateLockMgrLock + i, LW_EXCLUSIVE);
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/*
* Remove the dummy entry to give us scratch space, so we know we'll be
* able to create the new lock target.
*/
if (transfer)
RemoveScratchTarget(true);
/* Scan through target map */
hash_seq_init(&seqstat, PredicateLockTargetHash);
while ((oldtarget = (PREDICATELOCKTARGET *) hash_seq_search(&seqstat)))
{
PREDICATELOCK *oldpredlock;
/*
* Check whether this is a target which needs attention.
*/
if (GET_PREDICATELOCKTARGETTAG_RELATION(oldtarget->tag) != relId)
continue; /* wrong relation id */
if (GET_PREDICATELOCKTARGETTAG_DB(oldtarget->tag) != dbId)
continue; /* wrong database id */
if (transfer && !isIndex
&& GET_PREDICATELOCKTARGETTAG_TYPE(oldtarget->tag) == PREDLOCKTAG_RELATION)
continue; /* already the right lock */
/*
* If we made it here, we have work to do. We make sure the heap
* relation lock exists, then we walk the list of predicate locks for
* the old target we found, moving all locks to the heap relation lock
* -- unless they already hold that.
*/
/*
* First make sure we have the heap relation target. We only need to
* do this once.
*/
if (transfer && heaptarget == NULL)
{
PREDICATELOCKTARGETTAG heaptargettag;
SET_PREDICATELOCKTARGETTAG_RELATION(heaptargettag, dbId, heapId);
heaptargettaghash = PredicateLockTargetTagHashCode(&heaptargettag);
heaptarget = hash_search_with_hash_value(PredicateLockTargetHash,
&heaptargettag,
heaptargettaghash,
HASH_ENTER, &found);
if (!found)
SHMQueueInit(&heaptarget->predicateLocks);
}
/*
* Loop through all the locks on the old target, replacing them with
* locks on the new target.
*/
oldpredlock = (PREDICATELOCK *)
SHMQueueNext(&(oldtarget->predicateLocks),
&(oldtarget->predicateLocks),
offsetof(PREDICATELOCK, targetLink));
while (oldpredlock)
{
PREDICATELOCK *nextpredlock;
PREDICATELOCK *newpredlock;
SerCommitSeqNo oldCommitSeqNo;
SERIALIZABLEXACT *oldXact;
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&(oldtarget->predicateLocks),
&(oldpredlock->targetLink),
offsetof(PREDICATELOCK, targetLink));
/*
* Remove the old lock first. This avoids the chance of running
* out of lock structure entries for the hash table.
*/
oldCommitSeqNo = oldpredlock->commitSeqNo;
oldXact = oldpredlock->tag.myXact;
SHMQueueDelete(&(oldpredlock->xactLink));
/*
* No need for retail delete from oldtarget list, we're removing
* the whole target anyway.
*/
hash_search(PredicateLockHash,
&oldpredlock->tag,
HASH_REMOVE, &found);
Assert(found);
if (transfer)
{
PREDICATELOCKTAG newpredlocktag;
newpredlocktag.myTarget = heaptarget;
newpredlocktag.myXact = oldXact;
newpredlock = (PREDICATELOCK *)
hash_search_with_hash_value(PredicateLockHash,
&newpredlocktag,
PredicateLockHashCodeFromTargetHashCode(&newpredlocktag,
heaptargettaghash),
HASH_ENTER,
&found);
if (!found)
{
SHMQueueInsertBefore(&(heaptarget->predicateLocks),
&(newpredlock->targetLink));
SHMQueueInsertBefore(&(newpredlocktag.myXact->predicateLocks),
&(newpredlock->xactLink));
newpredlock->commitSeqNo = oldCommitSeqNo;
}
else
{
if (newpredlock->commitSeqNo < oldCommitSeqNo)
newpredlock->commitSeqNo = oldCommitSeqNo;
}
Assert(newpredlock->commitSeqNo != 0);
Assert((newpredlock->commitSeqNo == InvalidSerCommitSeqNo)
|| (newpredlock->tag.myXact == OldCommittedSxact));
}
oldpredlock = nextpredlock;
}
hash_search(PredicateLockTargetHash, &oldtarget->tag, HASH_REMOVE,
&found);
Assert(found);
}
/* Put the scratch entry back */
if (transfer)
RestoreScratchTarget(true);
/* Release locks in reverse order */
LWLockRelease(SerializableXactHashLock);
for (i = NUM_PREDICATELOCK_PARTITIONS - 1; i >= 0; i--)
LWLockRelease(FirstPredicateLockMgrLock + i);
LWLockRelease(SerializablePredicateLockListLock);
}
| static SERIALIZABLEXACT * FirstPredXact | ( | void | ) | [static] |
Definition at line 583 of file predicate.c.
References PredXactListData::activeList, offsetof, SHMQueueNext(), and PredXactListElementData::sxact.
Referenced by GetSerializableTransactionSnapshotInt(), and SetNewSxactGlobalXmin().
{
PredXactListElement ptle;
ptle = (PredXactListElement)
SHMQueueNext(&PredXact->activeList,
&PredXact->activeList,
offsetof(PredXactListElementData, link));
if (!ptle)
return NULL;
return &ptle->sxact;
}
| static void FlagRWConflict | ( | SERIALIZABLEXACT * | reader, | |
| SERIALIZABLEXACT * | writer | |||
| ) | [static] |
Definition at line 4426 of file predicate.c.
References Assert, SERIALIZABLEXACT::flags, OnConflict_CheckForSerializationFailure(), and SetRWConflict().
Referenced by CheckForSerializableConflictOut(), CheckTableForSerializableConflictIn(), and CheckTargetForConflictsIn().
{
Assert(reader != writer);
/* First, see if this conflict causes failure. */
OnConflict_CheckForSerializationFailure(reader, writer);
/* Actually do the conflict flagging. */
if (reader == OldCommittedSxact)
writer->flags |= SXACT_FLAG_SUMMARY_CONFLICT_IN;
else if (writer == OldCommittedSxact)
reader->flags |= SXACT_FLAG_SUMMARY_CONFLICT_OUT;
else
SetRWConflict(reader, writer);
}
| static void FlagSxactUnsafe | ( | SERIALIZABLEXACT * | sxact | ) | [static] |
Definition at line 721 of file predicate.c.
References Assert, SERIALIZABLEXACT::flags, RWConflictData::inLink, offsetof, SERIALIZABLEXACT::possibleUnsafeConflicts, ReleaseRWConflict(), SHMQueueNext(), RWConflictData::sxactIn, SxactIsReadOnly, SxactIsROSafe, and RWConflictData::sxactOut.
Referenced by ReleasePredicateLocks().
{
RWConflict conflict,
nextConflict;
Assert(SxactIsReadOnly(sxact));
Assert(!SxactIsROSafe(sxact));
sxact->flags |= SXACT_FLAG_RO_UNSAFE;
/*
* We know this isn't a safe snapshot, so we can stop looking for other
* potential conflicts.
*/
conflict = (RWConflict)
SHMQueueNext(&sxact->possibleUnsafeConflicts,
&sxact->possibleUnsafeConflicts,
offsetof(RWConflictData, inLink));
while (conflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&sxact->possibleUnsafeConflicts,
&conflict->inLink,
offsetof(RWConflictData, inLink));
Assert(!SxactIsReadOnly(conflict->sxactOut));
Assert(sxact == conflict->sxactIn);
ReleaseRWConflict(conflict);
conflict = nextConflict;
}
}
| static bool GetParentPredicateLockTag | ( | const PREDICATELOCKTARGETTAG * | tag, | |
| PREDICATELOCKTARGETTAG * | parent | |||
| ) | [static] |
Definition at line 1919 of file predicate.c.
References Assert, GET_PREDICATELOCKTARGETTAG_DB, GET_PREDICATELOCKTARGETTAG_PAGE, GET_PREDICATELOCKTARGETTAG_RELATION, GET_PREDICATELOCKTARGETTAG_TYPE, PREDLOCKTAG_PAGE, PREDLOCKTAG_RELATION, PREDLOCKTAG_TUPLE, SET_PREDICATELOCKTARGETTAG_PAGE, and SET_PREDICATELOCKTARGETTAG_RELATION.
Referenced by CheckAndPromotePredicateLockRequest(), CoarserLockCovers(), DecrementParentLocks(), and PredicateLockPageSplit().
{
switch (GET_PREDICATELOCKTARGETTAG_TYPE(*tag))
{
case PREDLOCKTAG_RELATION:
/* relation locks have no parent lock */
return false;
case PREDLOCKTAG_PAGE:
/* parent lock is relation lock */
SET_PREDICATELOCKTARGETTAG_RELATION(*parent,
GET_PREDICATELOCKTARGETTAG_DB(*tag),
GET_PREDICATELOCKTARGETTAG_RELATION(*tag));
return true;
case PREDLOCKTAG_TUPLE:
/* parent lock is page lock */
SET_PREDICATELOCKTARGETTAG_PAGE(*parent,
GET_PREDICATELOCKTARGETTAG_DB(*tag),
GET_PREDICATELOCKTARGETTAG_RELATION(*tag),
GET_PREDICATELOCKTARGETTAG_PAGE(*tag));
return true;
}
/* not reachable */
Assert(false);
return false;
}
| PredicateLockData* GetPredicateLockStatusData | ( | void | ) |
Definition at line 1385 of file predicate.c.
References Assert, FirstPredicateLockMgrLock, hash_get_num_entries(), hash_seq_init(), hash_seq_search(), i, PredicateLockData::locktags, LW_SHARED, LWLockAcquire(), LWLockRelease(), PREDICATELOCKTAG::myTarget, PREDICATELOCKTAG::myXact, PredicateLockData::nelements, palloc(), SerializableXactHashLock, PREDICATELOCKTARGET::tag, PREDICATELOCK::tag, and PredicateLockData::xacts.
Referenced by pg_lock_status().
{
PredicateLockData *data;
int i;
int els,
el;
HASH_SEQ_STATUS seqstat;
PREDICATELOCK *predlock;
data = (PredicateLockData *) palloc(sizeof(PredicateLockData));
/*
* To ensure consistency, take simultaneous locks on all partition locks
* in ascending order, then SerializableXactHashLock.
*/
for (i = 0; i < NUM_PREDICATELOCK_PARTITIONS; i++)
LWLockAcquire(FirstPredicateLockMgrLock + i, LW_SHARED);
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
/* Get number of locks and allocate appropriately-sized arrays. */
els = hash_get_num_entries(PredicateLockHash);
data->nelements = els;
data->locktags = (PREDICATELOCKTARGETTAG *)
palloc(sizeof(PREDICATELOCKTARGETTAG) * els);
data->xacts = (SERIALIZABLEXACT *)
palloc(sizeof(SERIALIZABLEXACT) * els);
/* Scan through PredicateLockHash and copy contents */
hash_seq_init(&seqstat, PredicateLockHash);
el = 0;
while ((predlock = (PREDICATELOCK *) hash_seq_search(&seqstat)))
{
data->locktags[el] = predlock->tag.myTarget->tag;
data->xacts[el] = *predlock->tag.myXact;
el++;
}
Assert(el == els);
/* Release locks in reverse order */
LWLockRelease(SerializableXactHashLock);
for (i = NUM_PREDICATELOCK_PARTITIONS - 1; i >= 0; i--)
LWLockRelease(FirstPredicateLockMgrLock + i);
return data;
}
Definition at line 1500 of file predicate.c.
References Assert, DEBUG2, ereport, errcode(), errmsg(), SERIALIZABLEXACT::flags, GetSerializableTransactionSnapshotInt(), InvalidSerializableXact, InvalidTransactionId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SERIALIZABLEXACT::possibleUnsafeConflicts, ProcWaitForSignal(), ReleasePredicateLocks(), SerializableXactHashLock, SHMQueueEmpty(), SxactIsROSafe, SxactIsROUnsafe, XactDeferrable, and XactReadOnly.
Referenced by GetSerializableTransactionSnapshot().
{
Snapshot snapshot;
Assert(XactReadOnly && XactDeferrable);
while (true)
{
/*
* GetSerializableTransactionSnapshotInt is going to call
* GetSnapshotData, so we need to provide it the static snapshot area
* our caller passed to us. The pointer returned is actually the same
* one passed to it, but we avoid assuming that here.
*/
snapshot = GetSerializableTransactionSnapshotInt(origSnapshot,
InvalidTransactionId);
if (MySerializableXact == InvalidSerializableXact)
return snapshot; /* no concurrent r/w xacts; it's safe */
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/*
* Wait for concurrent transactions to finish. Stop early if one of
* them marked us as conflicted.
*/
MySerializableXact->flags |= SXACT_FLAG_DEFERRABLE_WAITING;
while (!(SHMQueueEmpty(&MySerializableXact->possibleUnsafeConflicts) ||
SxactIsROUnsafe(MySerializableXact)))
{
LWLockRelease(SerializableXactHashLock);
ProcWaitForSignal();
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
}
MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
if (!SxactIsROUnsafe(MySerializableXact))
{
LWLockRelease(SerializableXactHashLock);
break; /* success */
}
LWLockRelease(SerializableXactHashLock);
/* else, need to retry... */
ereport(DEBUG2,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("deferrable snapshot was unsafe; trying a new one")));
ReleasePredicateLocks(false);
}
/*
* Now we have a safe snapshot, so we don't need to do any further checks.
*/
Assert(SxactIsROSafe(MySerializableXact));
ReleasePredicateLocks(false);
return snapshot;
}
Definition at line 1572 of file predicate.c.
References Assert, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, GetSafeSnapshot(), GetSerializableTransactionSnapshotInt(), InvalidTransactionId, IsolationIsSerializable, RecoveryInProgress(), XactDeferrable, and XactReadOnly.
Referenced by GetTransactionSnapshot().
{
Assert(IsolationIsSerializable());
/*
* Can't use serializable mode while recovery is still active, as it is,
* for example, on a hot standby. We could get here despite the check
* in check_XactIsoLevel() if default_transaction_isolation is set to
* serializable, so phrase the hint accordingly.
*/
if (RecoveryInProgress())
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use serializable mode in a hot standby"),
errdetail("\"default_transaction_isolation\" is set to \"serializable\"."),
errhint("You can use \"SET default_transaction_isolation = 'repeatable read'\" to change the default.")));
/*
* A special optimization is available for SERIALIZABLE READ ONLY
* DEFERRABLE transactions -- we can wait for a suitable snapshot and
* thereby avoid all SSI overhead once it's running.
*/
if (XactReadOnly && XactDeferrable)
return GetSafeSnapshot(snapshot);
return GetSerializableTransactionSnapshotInt(snapshot,
InvalidTransactionId);
}
| static Snapshot GetSerializableTransactionSnapshotInt | ( | Snapshot | snapshot, | |
| TransactionId | sourcexid | |||
| ) | [static] |
Definition at line 1641 of file predicate.c.
References Assert, SERIALIZABLEXACT::commitSeqNo, CreatePredXact(), HASHCTL::entrysize, ereport, errcode(), errdetail(), errmsg(), ERROR, SERIALIZABLEXACT::finishedBefore, SERIALIZABLEXACT::finishedLink, FirstPredXact(), SERIALIZABLEXACT::flags, GET_VXID_FROM_PGPROC, GetSnapshotData(), GetTopTransactionIdIfAny(), HASHCTL::hash, hash_create(), HASH_ELEM, HASH_FUNCTION, SERIALIZABLEXACT::inConflicts, InvalidSerializableXact, HASHCTL::keysize, SERIALIZABLEXACT::lastCommitBeforeSnapshot, PredXactListData::LastSxactCommitSeqNo, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), max_predicate_locks_per_xact, max_prepared_xacts, MaxBackends, MemSet, MyProc, MyProcPid, MyXactDidWrite, NextPredXact(), NULL, OldSerXidSetActiveSerXmin(), SERIALIZABLEXACT::outConflicts, SERIALIZABLEXACT::pid, SERIALIZABLEXACT::possibleUnsafeConflicts, SERIALIZABLEXACT::predicateLocks, SERIALIZABLEXACT::prepareSeqNo, ProcArrayInstallImportedXmin(), RecoveryInProgress(), ReleasePredXact(), SERIALIZABLEXACT::SeqNo, SerializableXactHashLock, SetPossibleUnsafeConflict(), SHMQueueElemInit(), SHMQueueInit(), SummarizeOldestCommittedSxact(), PredXactListData::SxactGlobalXmin, PredXactListData::SxactGlobalXminCount, SxactIsCommitted, SxactIsDoomed, SxactIsReadOnly, SERIALIZABLEXACT::topXid, TransactionIdEquals, TransactionIdFollows(), TransactionIdIsValid, SERIALIZABLEXACT::vxid, PredXactListData::WritableSxactCount, XactReadOnly, SERIALIZABLEXACT::xmin, and SnapshotData::xmin.
Referenced by GetSafeSnapshot(), GetSerializableTransactionSnapshot(), and SetSerializableTransactionSnapshot().
{
PGPROC *proc;
VirtualTransactionId vxid;
SERIALIZABLEXACT *sxact,
*othersxact;
HASHCTL hash_ctl;
/* We only do this for serializable transactions. Once. */
Assert(MySerializableXact == InvalidSerializableXact);
Assert(!RecoveryInProgress());
proc = MyProc;
Assert(proc != NULL);
GET_VXID_FROM_PGPROC(vxid, *proc);
/*
* First we get the sxact structure, which may involve looping and access
* to the "finished" list to free a structure for use.
*
* We must hold SerializableXactHashLock when taking/checking the snapshot
* to avoid race conditions, for much the same reasons that
* GetSnapshotData takes the ProcArrayLock. Since we might have to
* release SerializableXactHashLock to call SummarizeOldestCommittedSxact,
* this means we have to create the sxact first, which is a bit annoying
* (in particular, an elog(ERROR) in procarray.c would cause us to leak
* the sxact). Consider refactoring to avoid this.
*/
#ifdef TEST_OLDSERXID
SummarizeOldestCommittedSxact();
#endif
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
do
{
sxact = CreatePredXact();
/* If null, push out committed sxact to SLRU summary & retry. */
if (!sxact)
{
LWLockRelease(SerializableXactHashLock);
SummarizeOldestCommittedSxact();
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
}
} while (!sxact);
/* Get the snapshot, or check that it's safe to use */
if (!TransactionIdIsValid(sourcexid))
snapshot = GetSnapshotData(snapshot);
else if (!ProcArrayInstallImportedXmin(snapshot->xmin, sourcexid))
{
ReleasePredXact(sxact);
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not import the requested snapshot"),
errdetail("The source transaction %u is not running anymore.",
sourcexid)));
}
/*
* If there are no serializable transactions which are not read-only, we
* can "opt out" of predicate locking and conflict checking for a
* read-only transaction.
*
* The reason this is safe is that a read-only transaction can only become
* part of a dangerous structure if it overlaps a writable transaction
* which in turn overlaps a writable transaction which committed before
* the read-only transaction started. A new writable transaction can
* overlap this one, but it can't meet the other condition of overlapping
* a transaction which committed before this one started.
*/
if (XactReadOnly && PredXact->WritableSxactCount == 0)
{
ReleasePredXact(sxact);
LWLockRelease(SerializableXactHashLock);
return snapshot;
}
/* Maintain serializable global xmin info. */
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
{
Assert(PredXact->SxactGlobalXminCount == 0);
PredXact->SxactGlobalXmin = snapshot->xmin;
PredXact->SxactGlobalXminCount = 1;
OldSerXidSetActiveSerXmin(snapshot->xmin);
}
else if (TransactionIdEquals(snapshot->xmin, PredXact->SxactGlobalXmin))
{
Assert(PredXact->SxactGlobalXminCount > 0);
PredXact->SxactGlobalXminCount++;
}
else
{
Assert(TransactionIdFollows(snapshot->xmin, PredXact->SxactGlobalXmin));
}
/* Initialize the structure. */
sxact->vxid = vxid;
sxact->SeqNo.lastCommitBeforeSnapshot = PredXact->LastSxactCommitSeqNo;
sxact->prepareSeqNo = InvalidSerCommitSeqNo;
sxact->commitSeqNo = InvalidSerCommitSeqNo;
SHMQueueInit(&(sxact->outConflicts));
SHMQueueInit(&(sxact->inConflicts));
SHMQueueInit(&(sxact->possibleUnsafeConflicts));
sxact->topXid = GetTopTransactionIdIfAny();
sxact->finishedBefore = InvalidTransactionId;
sxact->xmin = snapshot->xmin;
sxact->pid = MyProcPid;
SHMQueueInit(&(sxact->predicateLocks));
SHMQueueElemInit(&(sxact->finishedLink));
sxact->flags = 0;
if (XactReadOnly)
{
sxact->flags |= SXACT_FLAG_READ_ONLY;
/*
* Register all concurrent r/w transactions as possible conflicts; if
* all of them commit without any outgoing conflicts to earlier
* transactions then this snapshot can be deemed safe (and we can run
* without tracking predicate locks).
*/
for (othersxact = FirstPredXact();
othersxact != NULL;
othersxact = NextPredXact(othersxact))
{
if (!SxactIsCommitted(othersxact)
&& !SxactIsDoomed(othersxact)
&& !SxactIsReadOnly(othersxact))
{
SetPossibleUnsafeConflict(sxact, othersxact);
}
}
}
else
{
++(PredXact->WritableSxactCount);
Assert(PredXact->WritableSxactCount <=
(MaxBackends + max_prepared_xacts));
}
MySerializableXact = sxact;
MyXactDidWrite = false; /* haven't written anything yet */
LWLockRelease(SerializableXactHashLock);
/* Initialize the backend-local hash table of parent locks */
Assert(LocalPredicateLockHash == NULL);
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(PREDICATELOCKTARGETTAG);
hash_ctl.entrysize = sizeof(LOCALPREDICATELOCK);
hash_ctl.hash = tag_hash;
LocalPredicateLockHash = hash_create("Local predicate lock",
max_predicate_locks_per_xact,
&hash_ctl,
HASH_ELEM | HASH_FUNCTION);
return snapshot;
}
| void InitPredicateLocks | ( | void | ) |
Definition at line 1091 of file predicate.c.
References PredXactListData::activeList, RWConflictPoolHeaderData::availableList, PredXactListData::availableList, PredXactListData::CanPartialClearThrough, SERIALIZABLEXACT::commitSeqNo, CreatePredXact(), RWConflictPoolHeaderData::element, PredXactListData::element, HASHCTL::entrysize, ereport, errcode(), errmsg(), ERROR, SERIALIZABLEXACT::finishedBefore, SERIALIZABLEXACT::finishedLink, FirstNormalSerCommitSeqNo, SERIALIZABLEXACT::flags, HASHCTL::hash, HASH_ELEM, HASH_ENTER, HASH_FUNCTION, HASH_PARTITION, hash_search(), PredXactListData::HavePartialClearedThrough, i, SERIALIZABLEXACT::inConflicts, HASHCTL::keysize, SERIALIZABLEXACT::lastCommitBeforeSnapshot, PredXactListData::LastSxactCommitSeqNo, PredXactListElementData::link, max_prepared_xacts, MaxBackends, MemSet, mul_size(), NPREDICATELOCKTARGETENTS, NULL, HASHCTL::num_partitions, PredXactListData::OldCommittedSxact, OldSerXidInit(), SERIALIZABLEXACT::outConflicts, RWConflictData::outLink, SERIALIZABLEXACT::pid, SERIALIZABLEXACT::possibleUnsafeConflicts, PredicateLockHashPartitionLock, SERIALIZABLEXACT::predicateLocks, PredicateLockTargetTagHashCode, PredXactListDataSize, PredXactListElementDataSize, SERIALIZABLEXACT::prepareSeqNo, RWConflictDataSize, RWConflictPoolHeaderDataSize, ScratchPartitionLock, ScratchTargetTagHash, SERIALIZABLEXACT::SeqNo, SetInvalidVirtualTransactionId, ShmemAlloc(), ShmemInitHash(), ShmemInitStruct(), SHMQueueInit(), SHMQueueInsertBefore(), PredXactListData::SxactGlobalXmin, PredXactListData::SxactGlobalXminCount, SERIALIZABLEXACT::topXid, SERIALIZABLEXACT::vxid, PredXactListData::WritableSxactCount, and SERIALIZABLEXACT::xmin.
Referenced by CreateSharedMemoryAndSemaphores().
{
HASHCTL info;
int hash_flags;
long max_table_size;
Size requestSize;
bool found;
/*
* Compute size of predicate lock target hashtable. Note these
* calculations must agree with PredicateLockShmemSize!
*/
max_table_size = NPREDICATELOCKTARGETENTS();
/*
* Allocate hash table for PREDICATELOCKTARGET structs. This stores
* per-predicate-lock-target information.
*/
MemSet(&info, 0, sizeof(info));
info.keysize = sizeof(PREDICATELOCKTARGETTAG);
info.entrysize = sizeof(PREDICATELOCKTARGET);
info.hash = tag_hash;
info.num_partitions = NUM_PREDICATELOCK_PARTITIONS;
hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_PARTITION | HASH_FIXED_SIZE);
PredicateLockTargetHash = ShmemInitHash("PREDICATELOCKTARGET hash",
max_table_size,
max_table_size,
&info,
hash_flags);
/* Assume an average of 2 xacts per target */
max_table_size *= 2;
/*
* Reserve a dummy entry in the hash table; we use it to make sure there's
* always one entry available when we need to split or combine a page,
* because running out of space there could mean aborting a
* non-serializable transaction.
*/
hash_search(PredicateLockTargetHash, &ScratchTargetTag, HASH_ENTER, NULL);
/*
* Allocate hash table for PREDICATELOCK structs. This stores per
* xact-lock-of-a-target information.
*/
MemSet(&info, 0, sizeof(info));
info.keysize = sizeof(PREDICATELOCKTAG);
info.entrysize = sizeof(PREDICATELOCK);
info.hash = predicatelock_hash;
info.num_partitions = NUM_PREDICATELOCK_PARTITIONS;
hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_PARTITION | HASH_FIXED_SIZE);
PredicateLockHash = ShmemInitHash("PREDICATELOCK hash",
max_table_size,
max_table_size,
&info,
hash_flags);
/*
* Compute size for serializable transaction hashtable. Note these
* calculations must agree with PredicateLockShmemSize!
*/
max_table_size = (MaxBackends + max_prepared_xacts);
/*
* Allocate a list to hold information on transactions participating in
* predicate locking.
*
* Assume an average of 10 predicate locking transactions per backend.
* This allows aggressive cleanup while detail is present before data must
* be summarized for storage in SLRU and the "dummy" transaction.
*/
max_table_size *= 10;
PredXact = ShmemInitStruct("PredXactList",
PredXactListDataSize,
&found);
if (!found)
{
int i;
SHMQueueInit(&PredXact->availableList);
SHMQueueInit(&PredXact->activeList);
PredXact->SxactGlobalXmin = InvalidTransactionId;
PredXact->SxactGlobalXminCount = 0;
PredXact->WritableSxactCount = 0;
PredXact->LastSxactCommitSeqNo = FirstNormalSerCommitSeqNo - 1;
PredXact->CanPartialClearThrough = 0;
PredXact->HavePartialClearedThrough = 0;
requestSize = mul_size((Size) max_table_size,
PredXactListElementDataSize);
PredXact->element = ShmemAlloc(requestSize);
if (PredXact->element == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("not enough shared memory for elements of data structure"
" \"%s\" (%lu bytes requested)",
"PredXactList", (unsigned long) requestSize)));
/* Add all elements to available list, clean. */
memset(PredXact->element, 0, requestSize);
for (i = 0; i < max_table_size; i++)
{
SHMQueueInsertBefore(&(PredXact->availableList),
&(PredXact->element[i].link));
}
PredXact->OldCommittedSxact = CreatePredXact();
SetInvalidVirtualTransactionId(PredXact->OldCommittedSxact->vxid);
PredXact->OldCommittedSxact->prepareSeqNo = 0;
PredXact->OldCommittedSxact->commitSeqNo = 0;
PredXact->OldCommittedSxact->SeqNo.lastCommitBeforeSnapshot = 0;
SHMQueueInit(&PredXact->OldCommittedSxact->outConflicts);
SHMQueueInit(&PredXact->OldCommittedSxact->inConflicts);
SHMQueueInit(&PredXact->OldCommittedSxact->predicateLocks);
SHMQueueInit(&PredXact->OldCommittedSxact->finishedLink);
SHMQueueInit(&PredXact->OldCommittedSxact->possibleUnsafeConflicts);
PredXact->OldCommittedSxact->topXid = InvalidTransactionId;
PredXact->OldCommittedSxact->finishedBefore = InvalidTransactionId;
PredXact->OldCommittedSxact->xmin = InvalidTransactionId;
PredXact->OldCommittedSxact->flags = SXACT_FLAG_COMMITTED;
PredXact->OldCommittedSxact->pid = 0;
}
/* This never changes, so let's keep a local copy. */
OldCommittedSxact = PredXact->OldCommittedSxact;
/*
* Allocate hash table for SERIALIZABLEXID structs. This stores per-xid
* information for serializable transactions which have accessed data.
*/
MemSet(&info, 0, sizeof(info));
info.keysize = sizeof(SERIALIZABLEXIDTAG);
info.entrysize = sizeof(SERIALIZABLEXID);
info.hash = tag_hash;
hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
SerializableXidHash = ShmemInitHash("SERIALIZABLEXID hash",
max_table_size,
max_table_size,
&info,
hash_flags);
/*
* Allocate space for tracking rw-conflicts in lists attached to the
* transactions.
*
* Assume an average of 5 conflicts per transaction. Calculations suggest
* that this will prevent resource exhaustion in even the most pessimal
* loads up to max_connections = 200 with all 200 connections pounding the
* database with serializable transactions. Beyond that, there may be
* occasional transactions canceled when trying to flag conflicts. That's
* probably OK.
*/
max_table_size *= 5;
RWConflictPool = ShmemInitStruct("RWConflictPool",
RWConflictPoolHeaderDataSize,
&found);
if (!found)
{
int i;
SHMQueueInit(&RWConflictPool->availableList);
requestSize = mul_size((Size) max_table_size,
RWConflictDataSize);
RWConflictPool->element = ShmemAlloc(requestSize);
if (RWConflictPool->element == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("not enough shared memory for elements of data structure"
" \"%s\" (%lu bytes requested)",
"RWConflictPool", (unsigned long) requestSize)));
/* Add all elements to available list, clean. */
memset(RWConflictPool->element, 0, requestSize);
for (i = 0; i < max_table_size; i++)
{
SHMQueueInsertBefore(&(RWConflictPool->availableList),
&(RWConflictPool->element[i].outLink));
}
}
/*
* Create or attach to the header for the list of finished serializable
* transactions.
*/
FinishedSerializableTransactions = (SHM_QUEUE *)
ShmemInitStruct("FinishedSerializableTransactions",
sizeof(SHM_QUEUE),
&found);
if (!found)
SHMQueueInit(FinishedSerializableTransactions);
/*
* Initialize the SLRU storage for old committed serializable
* transactions.
*/
OldSerXidInit();
/* Pre-calculate the hash and partition lock of the scratch entry */
ScratchTargetTagHash = PredicateLockTargetTagHashCode(&ScratchTargetTag);
ScratchPartitionLock = PredicateLockHashPartitionLock(ScratchTargetTagHash);
}
| static SERIALIZABLEXACT * NextPredXact | ( | SERIALIZABLEXACT * | sxact | ) | [static] |
Definition at line 598 of file predicate.c.
References PredXactListData::activeList, Assert, PredXactListElementData::link, offsetof, ShmemAddrIsValid(), SHMQueueNext(), and PredXactListElementData::sxact.
Referenced by GetSerializableTransactionSnapshotInt(), and SetNewSxactGlobalXmin().
{
PredXactListElement ptle;
Assert(ShmemAddrIsValid(sxact));
ptle = (PredXactListElement)
(((char *) sxact)
- offsetof(PredXactListElementData, sxact)
+ offsetof(PredXactListElementData, link));
ptle = (PredXactListElement)
SHMQueueNext(&PredXact->activeList,
&ptle->link,
offsetof(PredXactListElementData, link));
if (!ptle)
return NULL;
return &ptle->sxact;
}
| static void OldSerXidAdd | ( | TransactionId | xid, | |
| SerCommitSeqNo | minConflictCommitSeqNo | |||
| ) | [static] |
Definition at line 822 of file predicate.c.
References Assert, ereport, errhint(), errmsg(), FirstNormalTransactionId, OldSerXidControlData::headPage, OldSerXidControlData::headXid, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), OldSerXidLock, OldSerXidNextPage, OldSerXidPage, OldSerXidPagePrecedesLogically(), OldSerXidSlruCtl, OldSerXidValue, SimpleLruReadPage(), SimpleLruZeroPage(), OldSerXidControlData::tailXid, TransactionIdFollows(), TransactionIdIsValid, TransactionIdPrecedes(), WARNING, and OldSerXidControlData::warningIssued.
Referenced by SummarizeOldestCommittedSxact().
{
TransactionId tailXid;
int targetPage;
int slotno;
int firstZeroPage;
bool isNewPage;
Assert(TransactionIdIsValid(xid));
targetPage = OldSerXidPage(xid);
LWLockAcquire(OldSerXidLock, LW_EXCLUSIVE);
/*
* If no serializable transactions are active, there shouldn't be anything
* to push out to the SLRU. Hitting this assert would mean there's
* something wrong with the earlier cleanup logic.
*/
tailXid = oldSerXidControl->tailXid;
Assert(TransactionIdIsValid(tailXid));
/*
* If the SLRU is currently unused, zero out the whole active region from
* tailXid to headXid before taking it into use. Otherwise zero out only
* any new pages that enter the tailXid-headXid range as we advance
* headXid.
*/
if (oldSerXidControl->headPage < 0)
{
firstZeroPage = OldSerXidPage(tailXid);
isNewPage = true;
}
else
{
firstZeroPage = OldSerXidNextPage(oldSerXidControl->headPage);
isNewPage = OldSerXidPagePrecedesLogically(oldSerXidControl->headPage,
targetPage);
}
if (!TransactionIdIsValid(oldSerXidControl->headXid)
|| TransactionIdFollows(xid, oldSerXidControl->headXid))
oldSerXidControl->headXid = xid;
if (isNewPage)
oldSerXidControl->headPage = targetPage;
/*
* Give a warning if we're about to run out of SLRU pages.
*
* slru.c has a maximum of 64k segments, with 32 (SLRU_PAGES_PER_SEGMENT)
* pages each. We need to store a 64-bit integer for each Xid, and with
* default 8k block size, 65536*32 pages is only enough to cover 2^30
* XIDs. If we're about to hit that limit and wrap around, warn the user.
*
* To avoid spamming the user, we only give one warning when we've used 1
* billion XIDs, and stay silent until the situation is fixed and the
* number of XIDs used falls below 800 million again.
*
* XXX: We have no safeguard to actually *prevent* the wrap-around,
* though. All you get is a warning.
*/
if (oldSerXidControl->warningIssued)
{
TransactionId lowWatermark;
lowWatermark = tailXid + 800000000;
if (lowWatermark < FirstNormalTransactionId)
lowWatermark = FirstNormalTransactionId;
if (TransactionIdPrecedes(xid, lowWatermark))
oldSerXidControl->warningIssued = false;
}
else
{
TransactionId highWatermark;
highWatermark = tailXid + 1000000000;
if (highWatermark < FirstNormalTransactionId)
highWatermark = FirstNormalTransactionId;
if (TransactionIdFollows(xid, highWatermark))
{
oldSerXidControl->warningIssued = true;
ereport(WARNING,
(errmsg("memory for serializable conflict tracking is nearly exhausted"),
errhint("There might be an idle transaction or a forgotten prepared transaction causing this.")));
}
}
if (isNewPage)
{
/* Initialize intervening pages. */
while (firstZeroPage != targetPage)
{
(void) SimpleLruZeroPage(OldSerXidSlruCtl, firstZeroPage);
firstZeroPage = OldSerXidNextPage(firstZeroPage);
}
slotno = SimpleLruZeroPage(OldSerXidSlruCtl, targetPage);
}
else
slotno = SimpleLruReadPage(OldSerXidSlruCtl, targetPage, true, xid);
OldSerXidValue(slotno, xid) = minConflictCommitSeqNo;
OldSerXidSlruCtl->shared->page_dirty[slotno] = true;
LWLockRelease(OldSerXidLock);
}
| static SerCommitSeqNo OldSerXidGetMinConflictCommitSeqNo | ( | TransactionId | xid | ) | [static] |
Definition at line 934 of file predicate.c.
References Assert, OldSerXidControlData::headXid, LW_SHARED, LWLockAcquire(), LWLockRelease(), OldSerXidLock, OldSerXidPage, OldSerXidSlruCtl, OldSerXidValue, SimpleLruReadPage_ReadOnly(), OldSerXidControlData::tailXid, TransactionIdFollows(), TransactionIdIsValid, TransactionIdPrecedes(), and val.
Referenced by CheckForSerializableConflictOut().
{
TransactionId headXid;
TransactionId tailXid;
SerCommitSeqNo val;
int slotno;
Assert(TransactionIdIsValid(xid));
LWLockAcquire(OldSerXidLock, LW_SHARED);
headXid = oldSerXidControl->headXid;
tailXid = oldSerXidControl->tailXid;
LWLockRelease(OldSerXidLock);
if (!TransactionIdIsValid(headXid))
return 0;
Assert(TransactionIdIsValid(tailXid));
if (TransactionIdPrecedes(xid, tailXid)
|| TransactionIdFollows(xid, headXid))
return 0;
/*
* The following function must be called without holding OldSerXidLock,
* but will return with that lock held, which must then be released.
*/
slotno = SimpleLruReadPage_ReadOnly(OldSerXidSlruCtl,
OldSerXidPage(xid), xid);
val = OldSerXidValue(slotno, xid);
LWLockRelease(OldSerXidLock);
return val;
}
| static void OldSerXidInit | ( | void | ) | [static] |
Definition at line 785 of file predicate.c.
References OldSerXidControlData::headPage, OldSerXidControlData::headXid, NUM_OLDSERXID_BUFFERS, OldSerXidLock, OldSerXidSlruCtl, ShmemInitStruct(), SimpleLruInit(), OldSerXidControlData::tailXid, and OldSerXidControlData::warningIssued.
Referenced by InitPredicateLocks().
{
bool found;
/*
* Set up SLRU management of the pg_serial data.
*/
OldSerXidSlruCtl->PagePrecedes = OldSerXidPagePrecedesLogically;
SimpleLruInit(OldSerXidSlruCtl, "OldSerXid SLRU Ctl",
NUM_OLDSERXID_BUFFERS, 0, OldSerXidLock, "pg_serial");
/* Override default assumption that writes should be fsync'd */
OldSerXidSlruCtl->do_fsync = false;
/*
* Create or attach to the OldSerXidControl structure.
*/
oldSerXidControl = (OldSerXidControl)
ShmemInitStruct("OldSerXidControlData", sizeof(OldSerXidControlData), &found);
if (!found)
{
/*
* Set control information to reflect empty SLRU.
*/
oldSerXidControl->headPage = -1;
oldSerXidControl->headXid = InvalidTransactionId;
oldSerXidControl->tailXid = InvalidTransactionId;
oldSerXidControl->warningIssued = false;
}
}
| static bool OldSerXidPagePrecedesLogically | ( | int | p, | |
| int | q | |||
| ) | [static] |
Definition at line 762 of file predicate.c.
References Assert, and OLDSERXID_MAX_PAGE.
Referenced by OldSerXidAdd().
{
int diff;
/*
* We have to compare modulo (OLDSERXID_MAX_PAGE+1)/2. Both inputs should
* be in the range 0..OLDSERXID_MAX_PAGE.
*/
Assert(p >= 0 && p <= OLDSERXID_MAX_PAGE);
Assert(q >= 0 && q <= OLDSERXID_MAX_PAGE);
diff = p - q;
if (diff >= ((OLDSERXID_MAX_PAGE + 1) / 2))
diff -= OLDSERXID_MAX_PAGE + 1;
else if (diff < -((int) (OLDSERXID_MAX_PAGE + 1) / 2))
diff += OLDSERXID_MAX_PAGE + 1;
return diff < 0;
}
| static void OldSerXidSetActiveSerXmin | ( | TransactionId | xid | ) | [static] |
Definition at line 975 of file predicate.c.
References Assert, OldSerXidControlData::headPage, OldSerXidControlData::headXid, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), OldSerXidLock, RecoveryInProgress(), OldSerXidControlData::tailXid, TransactionIdFollows(), TransactionIdIsValid, and TransactionIdPrecedes().
Referenced by GetSerializableTransactionSnapshotInt(), predicatelock_twophase_recover(), and SetNewSxactGlobalXmin().
{
LWLockAcquire(OldSerXidLock, LW_EXCLUSIVE);
/*
* When no sxacts are active, nothing overlaps, set the xid values to
* invalid to show that there are no valid entries. Don't clear headPage,
* though. A new xmin might still land on that page, and we don't want to
* repeatedly zero out the same page.
*/
if (!TransactionIdIsValid(xid))
{
oldSerXidControl->tailXid = InvalidTransactionId;
oldSerXidControl->headXid = InvalidTransactionId;
LWLockRelease(OldSerXidLock);
return;
}
/*
* When we're recovering prepared transactions, the global xmin might move
* backwards depending on the order they're recovered. Normally that's not
* OK, but during recovery no serializable transactions will commit, so
* the SLRU is empty and we can get away with it.
*/
if (RecoveryInProgress())
{
Assert(oldSerXidControl->headPage < 0);
if (!TransactionIdIsValid(oldSerXidControl->tailXid)
|| TransactionIdPrecedes(xid, oldSerXidControl->tailXid))
{
oldSerXidControl->tailXid = xid;
}
LWLockRelease(OldSerXidLock);
return;
}
Assert(!TransactionIdIsValid(oldSerXidControl->tailXid)
|| TransactionIdFollows(xid, oldSerXidControl->tailXid));
oldSerXidControl->tailXid = xid;
LWLockRelease(OldSerXidLock);
}
| static void OnConflict_CheckForSerializationFailure | ( | const SERIALIZABLEXACT * | reader, | |
| SERIALIZABLEXACT * | writer | |||
| ) | [static] |
Definition at line 4461 of file predicate.c.
References Assert, SERIALIZABLEXACT::commitSeqNo, ereport, errcode(), errdetail_internal(), errhint(), errmsg(), ERROR, SERIALIZABLEXACT::flags, SERIALIZABLEXACT::inConflicts, RWConflictData::inLink, SERIALIZABLEXACT::lastCommitBeforeSnapshot, LWLockHeldByMe(), LWLockRelease(), offsetof, SERIALIZABLEXACT::outConflicts, RWConflictData::outLink, SERIALIZABLEXACT::prepareSeqNo, SERIALIZABLEXACT::SeqNo, SerializableXactHashLock, SHMQueueNext(), SxactHasConflictOut, SxactHasSummaryConflictIn, SxactHasSummaryConflictOut, RWConflictData::sxactIn, SxactIsCommitted, SxactIsDoomed, SxactIsPrepared, SxactIsReadOnly, RWConflictData::sxactOut, and SERIALIZABLEXACT::topXid.
Referenced by FlagRWConflict().
{
bool failure;
RWConflict conflict;
Assert(LWLockHeldByMe(SerializableXactHashLock));
failure = false;
/*------------------------------------------------------------------------
* Check for already-committed writer with rw-conflict out flagged
* (conflict-flag on W means that T2 committed before W):
*
* R ------> W ------> T2
* rw rw
*
* That is a dangerous structure, so we must abort. (Since the writer
* has already committed, we must be the reader)
*------------------------------------------------------------------------
*/
if (SxactIsCommitted(writer)
&& (SxactHasConflictOut(writer) || SxactHasSummaryConflictOut(writer)))
failure = true;
/*------------------------------------------------------------------------
* Check whether the writer has become a pivot with an out-conflict
* committed transaction (T2), and T2 committed first:
*
* R ------> W ------> T2
* rw rw
*
* Because T2 must've committed first, there is no anomaly if:
* - the reader committed before T2
* - the writer committed before T2
* - the reader is a READ ONLY transaction and the reader was concurrent
* with T2 (= reader acquired its snapshot before T2 committed)
*
* We also handle the case that T2 is prepared but not yet committed
* here. In that case T2 has already checked for conflicts, so if it
* commits first, making the above conflict real, it's too late for it
* to abort.
*------------------------------------------------------------------------
*/
if (!failure)
{
if (SxactHasSummaryConflictOut(writer))
{
failure = true;
conflict = NULL;
}
else
conflict = (RWConflict)
SHMQueueNext(&writer->outConflicts,
&writer->outConflicts,
offsetof(RWConflictData, outLink));
while (conflict)
{
SERIALIZABLEXACT *t2 = conflict->sxactIn;
if (SxactIsPrepared(t2)
&& (!SxactIsCommitted(reader)
|| t2->prepareSeqNo <= reader->commitSeqNo)
&& (!SxactIsCommitted(writer)
|| t2->prepareSeqNo <= writer->commitSeqNo)
&& (!SxactIsReadOnly(reader)
|| t2->prepareSeqNo <= reader->SeqNo.lastCommitBeforeSnapshot))
{
failure = true;
break;
}
conflict = (RWConflict)
SHMQueueNext(&writer->outConflicts,
&conflict->outLink,
offsetof(RWConflictData, outLink));
}
}
/*------------------------------------------------------------------------
* Check whether the reader has become a pivot with a writer
* that's committed (or prepared):
*
* T0 ------> R ------> W
* rw rw
*
* Because W must've committed first for an anomaly to occur, there is no
* anomaly if:
* - T0 committed before the writer
* - T0 is READ ONLY, and overlaps the writer
*------------------------------------------------------------------------
*/
if (!failure && SxactIsPrepared(writer) && !SxactIsReadOnly(reader))
{
if (SxactHasSummaryConflictIn(reader))
{
failure = true;
conflict = NULL;
}
else
conflict = (RWConflict)
SHMQueueNext(&reader->inConflicts,
&reader->inConflicts,
offsetof(RWConflictData, inLink));
while (conflict)
{
SERIALIZABLEXACT *t0 = conflict->sxactOut;
if (!SxactIsDoomed(t0)
&& (!SxactIsCommitted(t0)
|| t0->commitSeqNo >= writer->prepareSeqNo)
&& (!SxactIsReadOnly(t0)
|| t0->SeqNo.lastCommitBeforeSnapshot >= writer->prepareSeqNo))
{
failure = true;
break;
}
conflict = (RWConflict)
SHMQueueNext(&reader->inConflicts,
&conflict->inLink,
offsetof(RWConflictData, inLink));
}
}
if (failure)
{
/*
* We have to kill a transaction to avoid a possible anomaly from
* occurring. If the writer is us, we can just ereport() to cause a
* transaction abort. Otherwise we flag the writer for termination,
* causing it to abort when it tries to commit. However, if the writer
* is a prepared transaction, already prepared, we can't abort it
* anymore, so we have to kill the reader instead.
*/
if (MySerializableXact == writer)
{
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, during write."),
errhint("The transaction might succeed if retried.")));
}
else if (SxactIsPrepared(writer))
{
LWLockRelease(SerializableXactHashLock);
/* if we're not the writer, we have to be the reader */
Assert(MySerializableXact == reader);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on conflict out to pivot %u, during read.", writer->topXid),
errhint("The transaction might succeed if retried.")));
}
writer->flags |= SXACT_FLAG_DOOMED;
}
}
| bool PageIsPredicateLocked | ( | Relation | relation, | |
| BlockNumber | blkno | |||
| ) |
Definition at line 1855 of file predicate.c.
References RelFileNode::dbNode, HASH_FIND, hash_search_with_hash_value(), LW_SHARED, LWLockAcquire(), LWLockRelease(), NULL, PredicateLockHashPartitionLock, PredicateLockTargetTagHashCode, RelationData::rd_id, RelationData::rd_node, and SET_PREDICATELOCKTARGETTAG_PAGE.
{
PREDICATELOCKTARGETTAG targettag;
uint32 targettaghash;
LWLockId partitionLock;
PREDICATELOCKTARGET *target;
SET_PREDICATELOCKTARGETTAG_PAGE(targettag,
relation->rd_node.dbNode,
relation->rd_id,
blkno);
targettaghash = PredicateLockTargetTagHashCode(&targettag);
partitionLock = PredicateLockHashPartitionLock(targettaghash);
LWLockAcquire(partitionLock, LW_SHARED);
target = (PREDICATELOCKTARGET *)
hash_search_with_hash_value(PredicateLockTargetHash,
&targettag, targettaghash,
HASH_FIND, NULL);
LWLockRelease(partitionLock);
return (target != NULL);
}
| void PostPrepare_PredicateLocks | ( | TransactionId | xid | ) |
Definition at line 4798 of file predicate.c.
References Assert, hash_destroy(), InvalidSerializableXact, MyXactDidWrite, SERIALIZABLEXACT::pid, and SxactIsPrepared.
Referenced by PrepareTransaction().
{
if (MySerializableXact == InvalidSerializableXact)
return;
Assert(SxactIsPrepared(MySerializableXact));
MySerializableXact->pid = 0;
hash_destroy(LocalPredicateLockHash);
LocalPredicateLockHash = NULL;
MySerializableXact = InvalidSerializableXact;
MyXactDidWrite = false;
}
| void PreCommit_CheckForSerializationFailure | ( | void | ) |
Definition at line 4636 of file predicate.c.
References Assert, ereport, errcode(), errdetail_internal(), errhint(), errmsg(), ERROR, SERIALIZABLEXACT::flags, SERIALIZABLEXACT::inConflicts, RWConflictData::inLink, InvalidSerializableXact, IsolationIsSerializable, PredXactListData::LastSxactCommitSeqNo, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), offsetof, SERIALIZABLEXACT::prepareSeqNo, SerializableXactHashLock, SHMQueueNext(), SxactIsCommitted, SxactIsDoomed, SxactIsPrepared, SxactIsReadOnly, and RWConflictData::sxactOut.
Referenced by CommitTransaction(), and PrepareTransaction().
{
RWConflict nearConflict;
if (MySerializableXact == InvalidSerializableXact)
return;
Assert(IsolationIsSerializable());
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/* Check if someone else has already decided that we need to die */
if (SxactIsDoomed(MySerializableXact))
{
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on identification as a pivot, during commit attempt."),
errhint("The transaction might succeed if retried.")));
}
nearConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->inConflicts,
&MySerializableXact->inConflicts,
offsetof(RWConflictData, inLink));
while (nearConflict)
{
if (!SxactIsCommitted(nearConflict->sxactOut)
&& !SxactIsDoomed(nearConflict->sxactOut))
{
RWConflict farConflict;
farConflict = (RWConflict)
SHMQueueNext(&nearConflict->sxactOut->inConflicts,
&nearConflict->sxactOut->inConflicts,
offsetof(RWConflictData, inLink));
while (farConflict)
{
if (farConflict->sxactOut == MySerializableXact
|| (!SxactIsCommitted(farConflict->sxactOut)
&& !SxactIsReadOnly(farConflict->sxactOut)
&& !SxactIsDoomed(farConflict->sxactOut)))
{
/*
* Normally, we kill the pivot transaction to make sure we
* make progress if the failing transaction is retried.
* However, we can't kill it if it's already prepared, so
* in that case we commit suicide instead.
*/
if (SxactIsPrepared(nearConflict->sxactOut))
{
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail_internal("Reason code: Canceled on commit attempt with conflict in from prepared pivot."),
errhint("The transaction might succeed if retried.")));
}
nearConflict->sxactOut->flags |= SXACT_FLAG_DOOMED;
break;
}
farConflict = (RWConflict)
SHMQueueNext(&nearConflict->sxactOut->inConflicts,
&farConflict->inLink,
offsetof(RWConflictData, inLink));
}
}
nearConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->inConflicts,
&nearConflict->inLink,
offsetof(RWConflictData, inLink));
}
MySerializableXact->prepareSeqNo = ++(PredXact->LastSxactCommitSeqNo);
MySerializableXact->flags |= SXACT_FLAG_PREPARED;
LWLockRelease(SerializableXactHashLock);
}
Definition at line 1359 of file predicate.c.
References Assert, PREDICATELOCKTAG::myTarget, PredicateLockHashCodeFromTargetHashCode, PredicateLockTargetTagHashCode, and PREDICATELOCKTARGET::tag.
{
const PREDICATELOCKTAG *predicatelocktag = (const PREDICATELOCKTAG *) key;
uint32 targethash;
Assert(keysize == sizeof(PREDICATELOCKTAG));
/* Look into the associated target object, and compute its hash code */
targethash = PredicateLockTargetTagHashCode(&predicatelocktag->myTarget->tag);
return PredicateLockHashCodeFromTargetHashCode(predicatelocktag, targethash);
}
| void predicatelock_twophase_recover | ( | TransactionId | xid, | |
| uint16 | info, | |||
| void * | recdata, | |||
| uint32 | len | |||
| ) |
Definition at line 4847 of file predicate.c.
References Assert, VirtualTransactionId::backendId, SERIALIZABLEXACT::commitSeqNo, CreatePredicateLock(), CreatePredXact(), TwoPhasePredicateRecord::data, ereport, errcode(), errmsg(), ERROR, SERIALIZABLEXACT::finishedBefore, SERIALIZABLEXACT::finishedLink, TwoPhasePredicateXactRecord::flags, SERIALIZABLEXACT::flags, HASH_ENTER, HASH_FIND, hash_search(), SERIALIZABLEXACT::inConflicts, InvalidSerializableXact, SERIALIZABLEXACT::lastCommitBeforeSnapshot, VirtualTransactionId::localTransactionId, TwoPhasePredicateRecord::lockRecord, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), max_prepared_xacts, MaxBackends, SERIALIZABLEXID::myXact, NULL, OldSerXidSetActiveSerXmin(), SERIALIZABLEXACT::outConflicts, SERIALIZABLEXACT::pid, SERIALIZABLEXACT::possibleUnsafeConflicts, SERIALIZABLEXACT::predicateLocks, PredicateLockTargetTagHashCode, SERIALIZABLEXACT::prepareSeqNo, SERIALIZABLEXACT::SeqNo, SerializableXactHashLock, SHMQueueElemInit(), SHMQueueInit(), PredXactListData::SxactGlobalXmin, PredXactListData::SxactGlobalXminCount, SxactIsPrepared, SxactIsReadOnly, TwoPhasePredicateLockRecord::target, SERIALIZABLEXACT::topXid, TransactionIdEquals, TransactionIdFollows(), TransactionIdIsValid, TWOPHASEPREDICATERECORD_LOCK, TWOPHASEPREDICATERECORD_XACT, TwoPhasePredicateRecord::type, SERIALIZABLEXACT::vxid, PredXactListData::WritableSxactCount, TwoPhasePredicateRecord::xactRecord, SERIALIZABLEXIDTAG::xid, TwoPhasePredicateXactRecord::xmin, and SERIALIZABLEXACT::xmin.
{
TwoPhasePredicateRecord *record;
Assert(len == sizeof(TwoPhasePredicateRecord));
record = (TwoPhasePredicateRecord *) recdata;
Assert((record->type == TWOPHASEPREDICATERECORD_XACT) ||
(record->type == TWOPHASEPREDICATERECORD_LOCK));
if (record->type == TWOPHASEPREDICATERECORD_XACT)
{
/* Per-transaction record. Set up a SERIALIZABLEXACT. */
TwoPhasePredicateXactRecord *xactRecord;
SERIALIZABLEXACT *sxact;
SERIALIZABLEXID *sxid;
SERIALIZABLEXIDTAG sxidtag;
bool found;
xactRecord = (TwoPhasePredicateXactRecord *) &record->data.xactRecord;
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
sxact = CreatePredXact();
if (!sxact)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of shared memory")));
/* vxid for a prepared xact is InvalidBackendId/xid; no pid */
sxact->vxid.backendId = InvalidBackendId;
sxact->vxid.localTransactionId = (LocalTransactionId) xid;
sxact->pid = 0;
/* a prepared xact hasn't committed yet */
sxact->prepareSeqNo = RecoverySerCommitSeqNo;
sxact->commitSeqNo = InvalidSerCommitSeqNo;
sxact->finishedBefore = InvalidTransactionId;
sxact->SeqNo.lastCommitBeforeSnapshot = RecoverySerCommitSeqNo;
/*
* Don't need to track this; no transactions running at the time the
* recovered xact started are still active, except possibly other
* prepared xacts and we don't care whether those are RO_SAFE or not.
*/
SHMQueueInit(&(sxact->possibleUnsafeConflicts));
SHMQueueInit(&(sxact->predicateLocks));
SHMQueueElemInit(&(sxact->finishedLink));
sxact->topXid = xid;
sxact->xmin = xactRecord->xmin;
sxact->flags = xactRecord->flags;
Assert(SxactIsPrepared(sxact));
if (!SxactIsReadOnly(sxact))
{
++(PredXact->WritableSxactCount);
Assert(PredXact->WritableSxactCount <=
(MaxBackends + max_prepared_xacts));
}
/*
* We don't know whether the transaction had any conflicts or not, so
* we'll conservatively assume that it had both a conflict in and a
* conflict out, and represent that with the summary conflict flags.
*/
SHMQueueInit(&(sxact->outConflicts));
SHMQueueInit(&(sxact->inConflicts));
sxact->flags |= SXACT_FLAG_SUMMARY_CONFLICT_IN;
sxact->flags |= SXACT_FLAG_SUMMARY_CONFLICT_OUT;
/* Register the transaction's xid */
sxidtag.xid = xid;
sxid = (SERIALIZABLEXID *) hash_search(SerializableXidHash,
&sxidtag,
HASH_ENTER, &found);
Assert(sxid != NULL);
Assert(!found);
sxid->myXact = (SERIALIZABLEXACT *) sxact;
/*
* Update global xmin. Note that this is a special case compared to
* registering a normal transaction, because the global xmin might go
* backwards. That's OK, because until recovery is over we're not
* going to complete any transactions or create any non-prepared
* transactions, so there's no danger of throwing away.
*/
if ((!TransactionIdIsValid(PredXact->SxactGlobalXmin)) ||
(TransactionIdFollows(PredXact->SxactGlobalXmin, sxact->xmin)))
{
PredXact->SxactGlobalXmin = sxact->xmin;
PredXact->SxactGlobalXminCount = 1;
OldSerXidSetActiveSerXmin(sxact->xmin);
}
else if (TransactionIdEquals(sxact->xmin, PredXact->SxactGlobalXmin))
{
Assert(PredXact->SxactGlobalXminCount > 0);
PredXact->SxactGlobalXminCount++;
}
LWLockRelease(SerializableXactHashLock);
}
else if (record->type == TWOPHASEPREDICATERECORD_LOCK)
{
/* Lock record. Recreate the PREDICATELOCK */
TwoPhasePredicateLockRecord *lockRecord;
SERIALIZABLEXID *sxid;
SERIALIZABLEXACT *sxact;
SERIALIZABLEXIDTAG sxidtag;
uint32 targettaghash;
lockRecord = (TwoPhasePredicateLockRecord *) &record->data.lockRecord;
targettaghash = PredicateLockTargetTagHashCode(&lockRecord->target);
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
sxidtag.xid = xid;
sxid = (SERIALIZABLEXID *)
hash_search(SerializableXidHash, &sxidtag, HASH_FIND, NULL);
LWLockRelease(SerializableXactHashLock);
Assert(sxid != NULL);
sxact = sxid->myXact;
Assert(sxact != InvalidSerializableXact);
CreatePredicateLock(&lockRecord->target, targettaghash, sxact);
}
}
| static void PredicateLockAcquire | ( | const PREDICATELOCKTARGETTAG * | targettag | ) | [static] |
Definition at line 2358 of file predicate.c.
References CheckAndPromotePredicateLockRequest(), LOCALPREDICATELOCK::childLocks, CoarserLockCovers(), CreatePredicateLock(), DeleteChildTargetLocks(), GET_PREDICATELOCKTARGETTAG_TYPE, HASH_ENTER, hash_search_with_hash_value(), LOCALPREDICATELOCK::held, PredicateLockExists(), PredicateLockTargetTagHashCode, and PREDLOCKTAG_TUPLE.
Referenced by CheckAndPromotePredicateLockRequest(), PredicateLockPage(), PredicateLockRelation(), and PredicateLockTuple().
{
uint32 targettaghash;
bool found;
LOCALPREDICATELOCK *locallock;
/* Do we have the lock already, or a covering lock? */
if (PredicateLockExists(targettag))
return;
if (CoarserLockCovers(targettag))
return;
/* the same hash and LW lock apply to the lock target and the local lock. */
targettaghash = PredicateLockTargetTagHashCode(targettag);
/* Acquire lock in local table */
locallock = (LOCALPREDICATELOCK *)
hash_search_with_hash_value(LocalPredicateLockHash,
targettag, targettaghash,
HASH_ENTER, &found);
locallock->held = true;
if (!found)
locallock->childLocks = 0;
/* Actually create the lock */
CreatePredicateLock(targettag, targettaghash, MySerializableXact);
/*
* Lock has been acquired. Check whether it should be promoted to a
* coarser granularity, or whether there are finer-granularity locks to
* clean up.
*/
if (CheckAndPromotePredicateLockRequest(targettag))
{
/*
* Lock request was promoted to a coarser-granularity lock, and that
* lock was acquired. It will delete this lock and any of its
* children, so we're done.
*/
}
else
{
/* Clean up any finer-granularity locks */
if (GET_PREDICATELOCKTARGETTAG_TYPE(*targettag) != PREDLOCKTAG_TUPLE)
DeleteChildTargetLocks(targettag);
}
}
| static bool PredicateLockExists | ( | const PREDICATELOCKTARGETTAG * | targettag | ) | [static] |
Definition at line 1892 of file predicate.c.
References HASH_FIND, hash_search(), LOCALPREDICATELOCK::held, and NULL.
Referenced by CoarserLockCovers(), PredicateLockAcquire(), and PredicateLockTuple().
{
LOCALPREDICATELOCK *lock;
/* check local hash table */
lock = (LOCALPREDICATELOCK *) hash_search(LocalPredicateLockHash,
targettag,
HASH_FIND, NULL);
if (!lock)
return false;
/*
* Found entry in the table, but still need to check whether it's actually
* held -- it could just be a parent of some held lock.
*/
return lock->held;
}
Definition at line 466 of file predicate.c.
References FirstBootstrapObjectId, RelationData::rd_id, RelationData::rd_rel, and RelationUsesLocalBuffers.
Referenced by DropAllPredicateLocksFromTable(), PredicateLockPageSplit(), SerializationNeededForRead(), and SerializationNeededForWrite().
{
return !(relation->rd_id < FirstBootstrapObjectId ||
RelationUsesLocalBuffers(relation) ||
relation->rd_rel->relkind == RELKIND_MATVIEW);
}
| void PredicateLockPage | ( | Relation | relation, | |
| BlockNumber | blkno, | |||
| Snapshot | snapshot | |||
| ) |
Definition at line 2440 of file predicate.c.
References RelFileNode::dbNode, PredicateLockAcquire(), RelationData::rd_id, RelationData::rd_node, SerializationNeededForRead(), and SET_PREDICATELOCKTARGETTAG_PAGE.
Referenced by _bt_endpoint(), _bt_first(), _bt_steppage(), and IndexOnlyNext().
{
PREDICATELOCKTARGETTAG tag;
if (!SerializationNeededForRead(relation, snapshot))
return;
SET_PREDICATELOCKTARGETTAG_PAGE(tag,
relation->rd_node.dbNode,
relation->rd_id,
blkno);
PredicateLockAcquire(&tag);
}
| void PredicateLockPageCombine | ( | Relation | relation, | |
| BlockNumber | oldblkno, | |||
| BlockNumber | newblkno | |||
| ) |
Definition at line 3118 of file predicate.c.
References PredicateLockPageSplit().
Referenced by _bt_pagedel().
{
/*
* Page combines differ from page splits in that we ought to be able to
* remove the locks on the old page after transferring them to the new
* page, instead of duplicating them. However, because we can't edit other
* backends' local lock tables, removing the old lock would leave them
* with an entry in their LocalPredicateLockHash for a lock they're not
* holding, which isn't acceptable. So we wind up having to do the same
* work as a page split, acquiring a lock on the new page and keeping the
* old page locked too. That can lead to some false positives, but should
* be rare in practice.
*/
PredicateLockPageSplit(relation, oldblkno, newblkno);
}
| void PredicateLockPageSplit | ( | Relation | relation, | |
| BlockNumber | oldblkno, | |||
| BlockNumber | newblkno | |||
| ) |
Definition at line 3033 of file predicate.c.
References Assert, BlockNumberIsValid, RelFileNode::dbNode, GetParentPredicateLockTag(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), PredicateLockingNeededForRelation(), RelationData::rd_id, RelationData::rd_node, SerializablePredicateLockListLock, SET_PREDICATELOCKTARGETTAG_PAGE, PredXactListData::SxactGlobalXmin, TransactionIdIsValid, and TransferPredicateLocksToNewTarget().
Referenced by _bt_insertonpg(), and PredicateLockPageCombine().
{
PREDICATELOCKTARGETTAG oldtargettag;
PREDICATELOCKTARGETTAG newtargettag;
bool success;
/*
* Bail out quickly if there are no serializable transactions running.
*
* It's safe to do this check without taking any additional locks. Even if
* a serializable transaction starts concurrently, we know it can't take
* any SIREAD locks on the page being split because the caller is holding
* the associated buffer page lock. Memory reordering isn't an issue; the
* memory barrier in the LWLock acquisition guarantees that this read
* occurs while the buffer page lock is held.
*/
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
return;
if (!PredicateLockingNeededForRelation(relation))
return;
Assert(oldblkno != newblkno);
Assert(BlockNumberIsValid(oldblkno));
Assert(BlockNumberIsValid(newblkno));
SET_PREDICATELOCKTARGETTAG_PAGE(oldtargettag,
relation->rd_node.dbNode,
relation->rd_id,
oldblkno);
SET_PREDICATELOCKTARGETTAG_PAGE(newtargettag,
relation->rd_node.dbNode,
relation->rd_id,
newblkno);
LWLockAcquire(SerializablePredicateLockListLock, LW_EXCLUSIVE);
/*
* Try copying the locks over to the new page's tag, creating it if
* necessary.
*/
success = TransferPredicateLocksToNewTarget(oldtargettag,
newtargettag,
false);
if (!success)
{
/*
* No more predicate lock entries are available. Failure isn't an
* option here, so promote the page lock to a relation lock.
*/
/* Get the parent relation lock's lock tag */
success = GetParentPredicateLockTag(&oldtargettag,
&newtargettag);
Assert(success);
/*
* Move the locks to the parent. This shouldn't fail.
*
* Note that here we are removing locks held by other backends,
* leading to a possible inconsistency in their local lock hash table.
* This is OK because we're replacing it with a lock that covers the
* old one.
*/
success = TransferPredicateLocksToNewTarget(oldtargettag,
newtargettag,
true);
Assert(success);
}
LWLockRelease(SerializablePredicateLockListLock);
}
| static int PredicateLockPromotionThreshold | ( | const PREDICATELOCKTARGETTAG * | tag | ) | [static] |
Definition at line 2136 of file predicate.c.
References Assert, GET_PREDICATELOCKTARGETTAG_TYPE, max_predicate_locks_per_xact, PREDLOCKTAG_PAGE, PREDLOCKTAG_RELATION, and PREDLOCKTAG_TUPLE.
Referenced by CheckAndPromotePredicateLockRequest().
{
switch (GET_PREDICATELOCKTARGETTAG_TYPE(*tag))
{
case PREDLOCKTAG_RELATION:
return max_predicate_locks_per_xact / 2;
case PREDLOCKTAG_PAGE:
return 3;
case PREDLOCKTAG_TUPLE:
/*
* not reachable: nothing is finer-granularity than a tuple, so we
* should never try to promote to it.
*/
Assert(false);
return 0;
}
/* not reachable */
Assert(false);
return 0;
}
Definition at line 2417 of file predicate.c.
References RelFileNode::dbNode, PredicateLockAcquire(), RelationData::rd_id, RelationData::rd_node, SerializationNeededForRead(), and SET_PREDICATELOCKTARGETTAG_RELATION.
Referenced by _bt_endpoint(), _bt_first(), heap_beginscan_internal(), and index_beginscan_internal().
{
PREDICATELOCKTARGETTAG tag;
if (!SerializationNeededForRead(relation, snapshot))
return;
SET_PREDICATELOCKTARGETTAG_RELATION(tag,
relation->rd_node.dbNode,
relation->rd_id);
PredicateLockAcquire(&tag);
}
| Size PredicateLockShmemSize | ( | void | ) |
Definition at line 1297 of file predicate.c.
References add_size(), hash_estimate_size(), max_prepared_xacts, MaxBackends, mul_size(), NPREDICATELOCKTARGETENTS, NUM_OLDSERXID_BUFFERS, PredXactListDataSize, PredXactListElementDataSize, RWConflictDataSize, RWConflictPoolHeaderDataSize, and SimpleLruShmemSize().
Referenced by CreateSharedMemoryAndSemaphores().
{
Size size = 0;
long max_table_size;
/* predicate lock target hash table */
max_table_size = NPREDICATELOCKTARGETENTS();
size = add_size(size, hash_estimate_size(max_table_size,
sizeof(PREDICATELOCKTARGET)));
/* predicate lock hash table */
max_table_size *= 2;
size = add_size(size, hash_estimate_size(max_table_size,
sizeof(PREDICATELOCK)));
/*
* Since NPREDICATELOCKTARGETENTS is only an estimate, add 10% safety
* margin.
*/
size = add_size(size, size / 10);
/* transaction list */
max_table_size = MaxBackends + max_prepared_xacts;
max_table_size *= 10;
size = add_size(size, PredXactListDataSize);
size = add_size(size, mul_size((Size) max_table_size,
PredXactListElementDataSize));
/* transaction xid table */
size = add_size(size, hash_estimate_size(max_table_size,
sizeof(SERIALIZABLEXID)));
/* rw-conflict pool */
max_table_size *= 5;
size = add_size(size, RWConflictPoolHeaderDataSize);
size = add_size(size, mul_size((Size) max_table_size,
RWConflictDataSize));
/* Head for list of finished serializable transactions. */
size = add_size(size, sizeof(SHM_QUEUE));
/* Shared memory structures for SLRU tracking of old committed xids. */
size = add_size(size, sizeof(OldSerXidControlData));
size = add_size(size, SimpleLruShmemSize(NUM_OLDSERXID_BUFFERS, 0));
return size;
}
Definition at line 2462 of file predicate.c.
References RelFileNode::dbNode, GetTopTransactionIdIfAny(), HeapTupleHeaderGetXmin, ItemPointerGetBlockNumber, ItemPointerGetOffsetNumber, NULL, PredicateLockAcquire(), PredicateLockExists(), RelationData::rd_id, RelationData::rd_index, RelationData::rd_node, SerializationNeededForRead(), SET_PREDICATELOCKTARGETTAG_RELATION, SET_PREDICATELOCKTARGETTAG_TUPLE, SubTransGetTopmostTransaction(), HeapTupleData::t_data, HeapTupleData::t_self, TransactionIdEquals, TransactionIdFollowsOrEquals(), TransactionIdIsValid, and TransactionXmin.
Referenced by bitgetpage(), heap_fetch(), and heap_hot_search_buffer().
{
PREDICATELOCKTARGETTAG tag;
ItemPointer tid;
TransactionId targetxmin;
if (!SerializationNeededForRead(relation, snapshot))
return;
/*
* If it's a heap tuple, return if this xact wrote it.
*/
if (relation->rd_index == NULL)
{
TransactionId myxid;
targetxmin = HeapTupleHeaderGetXmin(tuple->t_data);
myxid = GetTopTransactionIdIfAny();
if (TransactionIdIsValid(myxid))
{
if (TransactionIdFollowsOrEquals(targetxmin, TransactionXmin))
{
TransactionId xid = SubTransGetTopmostTransaction(targetxmin);
if (TransactionIdEquals(xid, myxid))
{
/* We wrote it; we already have a write lock. */
return;
}
}
}
}
else
targetxmin = InvalidTransactionId;
/*
* Do quick-but-not-definitive test for a relation lock first. This will
* never cause a return when the relation is *not* locked, but will
* occasionally let the check continue when there really *is* a relation
* level lock.
*/
SET_PREDICATELOCKTARGETTAG_RELATION(tag,
relation->rd_node.dbNode,
relation->rd_id);
if (PredicateLockExists(&tag))
return;
tid = &(tuple->t_self);
SET_PREDICATELOCKTARGETTAG_TUPLE(tag,
relation->rd_node.dbNode,
relation->rd_id,
ItemPointerGetBlockNumber(tid),
ItemPointerGetOffsetNumber(tid),
targetxmin);
PredicateLockAcquire(&tag);
}
| void PredicateLockTwoPhaseFinish | ( | TransactionId | xid, | |
| bool | isCommit | |||
| ) |
Definition at line 4820 of file predicate.c.
References HASH_FIND, hash_search(), LW_SHARED, LWLockAcquire(), LWLockRelease(), SERIALIZABLEXID::myXact, MyXactDidWrite, NULL, ReleasePredicateLocks(), SerializableXactHashLock, and SERIALIZABLEXIDTAG::xid.
Referenced by FinishPreparedTransaction().
{
SERIALIZABLEXID *sxid;
SERIALIZABLEXIDTAG sxidtag;
sxidtag.xid = xid;
LWLockAcquire(SerializableXactHashLock, LW_SHARED);
sxid = (SERIALIZABLEXID *)
hash_search(SerializableXidHash, &sxidtag, HASH_FIND, NULL);
LWLockRelease(SerializableXactHashLock);
/* xid will not be found if it wasn't a serializable transaction */
if (sxid == NULL)
return;
/* Release its locks */
MySerializableXact = sxid->myXact;
MyXactDidWrite = true; /* conservatively assume that we wrote
* something */
ReleasePredicateLocks(isCommit);
}
| void RegisterPredicateLockingXid | ( | TransactionId | xid | ) |
Definition at line 1806 of file predicate.c.
References Assert, HASH_ENTER, hash_search(), InvalidSerializableXact, InvalidTransactionId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SERIALIZABLEXID::myXact, SerializableXactHashLock, SERIALIZABLEXACT::topXid, TransactionIdIsValid, and SERIALIZABLEXIDTAG::xid.
Referenced by AssignTransactionId().
{
SERIALIZABLEXIDTAG sxidtag;
SERIALIZABLEXID *sxid;
bool found;
/*
* If we're not tracking predicate lock data for this transaction, we
* should ignore the request and return quickly.
*/
if (MySerializableXact == InvalidSerializableXact)
return;
/* We should have a valid XID and be at the top level. */
Assert(TransactionIdIsValid(xid));
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/* This should only be done once per transaction. */
Assert(MySerializableXact->topXid == InvalidTransactionId);
MySerializableXact->topXid = xid;
sxidtag.xid = xid;
sxid = (SERIALIZABLEXID *) hash_search(SerializableXidHash,
&sxidtag,
HASH_ENTER, &found);
Assert(!found);
/* Initialize the structure. */
sxid->myXact = MySerializableXact;
LWLockRelease(SerializableXactHashLock);
}
| static void ReleaseOneSerializableXact | ( | SERIALIZABLEXACT * | sxact, | |
| bool | partial, | |||
| bool | summarize | |||
| ) | [static] |
Definition at line 3666 of file predicate.c.
References Assert, SERIALIZABLEXACT::commitSeqNo, PREDICATELOCK::commitSeqNo, ereport, errcode(), errhint(), errmsg(), ERROR, SERIALIZABLEXACT::flags, HASH_ENTER_NULL, HASH_REMOVE, hash_search(), hash_search_with_hash_value(), SERIALIZABLEXACT::inConflicts, RWConflictData::inLink, InvalidSerCommitSeqNo, InvalidTransactionId, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), PREDICATELOCKTAG::myTarget, PREDICATELOCKTAG::myXact, NULL, offsetof, SERIALIZABLEXACT::outConflicts, RWConflictData::outLink, PredicateLockHashCodeFromTargetHashCode, PredicateLockHashPartitionLock, PREDICATELOCKTARGET::predicateLocks, SERIALIZABLEXACT::predicateLocks, PredicateLockTargetTagHashCode, ReleasePredXact(), ReleaseRWConflict(), RemoveTargetIfNoLongerUsed(), SerializableFinishedListLock, SerializablePredicateLockListLock, SerializableXactHashLock, SHMQueueDelete(), SHMQueueInit(), SHMQueueInsertBefore(), SHMQueueNext(), RWConflictData::sxactIn, SxactIsCommitted, SxactIsOnFinishedList, SxactIsRolledBack, RWConflictData::sxactOut, PREDICATELOCKTARGET::tag, PREDICATELOCK::tag, PREDICATELOCK::targetLink, SERIALIZABLEXACT::topXid, PREDICATELOCK::xactLink, and SERIALIZABLEXIDTAG::xid.
Referenced by ClearOldPredicateLocks(), ReleasePredicateLocks(), and SummarizeOldestCommittedSxact().
{
PREDICATELOCK *predlock;
SERIALIZABLEXIDTAG sxidtag;
RWConflict conflict,
nextConflict;
Assert(sxact != NULL);
Assert(SxactIsRolledBack(sxact) || SxactIsCommitted(sxact));
Assert(partial || !SxactIsOnFinishedList(sxact));
Assert(LWLockHeldByMe(SerializableFinishedListLock));
/*
* First release all the predicate locks held by this xact (or transfer
* them to OldCommittedSxact if summarize is true)
*/
LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
predlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(sxact->predicateLocks),
offsetof(PREDICATELOCK, xactLink));
while (predlock)
{
PREDICATELOCK *nextpredlock;
PREDICATELOCKTAG tag;
SHM_QUEUE *targetLink;
PREDICATELOCKTARGET *target;
PREDICATELOCKTARGETTAG targettag;
uint32 targettaghash;
LWLockId partitionLock;
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&(sxact->predicateLocks),
&(predlock->xactLink),
offsetof(PREDICATELOCK, xactLink));
tag = predlock->tag;
targetLink = &(predlock->targetLink);
target = tag.myTarget;
targettag = target->tag;
targettaghash = PredicateLockTargetTagHashCode(&targettag);
partitionLock = PredicateLockHashPartitionLock(targettaghash);
LWLockAcquire(partitionLock, LW_EXCLUSIVE);
SHMQueueDelete(targetLink);
hash_search_with_hash_value(PredicateLockHash, &tag,
PredicateLockHashCodeFromTargetHashCode(&tag,
targettaghash),
HASH_REMOVE, NULL);
if (summarize)
{
bool found;
/* Fold into dummy transaction list. */
tag.myXact = OldCommittedSxact;
predlock = hash_search_with_hash_value(PredicateLockHash, &tag,
PredicateLockHashCodeFromTargetHashCode(&tag,
targettaghash),
HASH_ENTER_NULL, &found);
if (!predlock)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of shared memory"),
errhint("You might need to increase max_pred_locks_per_transaction.")));
if (found)
{
Assert(predlock->commitSeqNo != 0);
Assert(predlock->commitSeqNo != InvalidSerCommitSeqNo);
if (predlock->commitSeqNo < sxact->commitSeqNo)
predlock->commitSeqNo = sxact->commitSeqNo;
}
else
{
SHMQueueInsertBefore(&(target->predicateLocks),
&(predlock->targetLink));
SHMQueueInsertBefore(&(OldCommittedSxact->predicateLocks),
&(predlock->xactLink));
predlock->commitSeqNo = sxact->commitSeqNo;
}
}
else
RemoveTargetIfNoLongerUsed(target, targettaghash);
LWLockRelease(partitionLock);
predlock = nextpredlock;
}
/*
* Rather than retail removal, just re-init the head after we've run
* through the list.
*/
SHMQueueInit(&sxact->predicateLocks);
LWLockRelease(SerializablePredicateLockListLock);
sxidtag.xid = sxact->topXid;
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/* Release all outConflicts (unless 'partial' is true) */
if (!partial)
{
conflict = (RWConflict)
SHMQueueNext(&sxact->outConflicts,
&sxact->outConflicts,
offsetof(RWConflictData, outLink));
while (conflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&sxact->outConflicts,
&conflict->outLink,
offsetof(RWConflictData, outLink));
if (summarize)
conflict->sxactIn->flags |= SXACT_FLAG_SUMMARY_CONFLICT_IN;
ReleaseRWConflict(conflict);
conflict = nextConflict;
}
}
/* Release all inConflicts. */
conflict = (RWConflict)
SHMQueueNext(&sxact->inConflicts,
&sxact->inConflicts,
offsetof(RWConflictData, inLink));
while (conflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&sxact->inConflicts,
&conflict->inLink,
offsetof(RWConflictData, inLink));
if (summarize)
conflict->sxactOut->flags |= SXACT_FLAG_SUMMARY_CONFLICT_OUT;
ReleaseRWConflict(conflict);
conflict = nextConflict;
}
/* Finally, get rid of the xid and the record of the transaction itself. */
if (!partial)
{
if (sxidtag.xid != InvalidTransactionId)
hash_search(SerializableXidHash, &sxidtag, HASH_REMOVE, NULL);
ReleasePredXact(sxact);
}
LWLockRelease(SerializableXactHashLock);
}
| void ReleasePredicateLocks | ( | bool | isCommit | ) |
Definition at line 3190 of file predicate.c.
References Assert, PredXactListData::CanPartialClearThrough, ClearOldPredicateLocks(), SERIALIZABLEXACT::commitSeqNo, SERIALIZABLEXACT::earliestOutConflictCommit, SERIALIZABLEXACT::finishedBefore, SERIALIZABLEXACT::finishedLink, SERIALIZABLEXACT::flags, FlagSxactUnsafe(), hash_destroy(), SERIALIZABLEXACT::inConflicts, RWConflictData::inLink, InvalidSerializableXact, IsolationIsSerializable, SERIALIZABLEXACT::lastCommitBeforeSnapshot, PredXactListData::LastSxactCommitSeqNo, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MyXactDidWrite, VariableCacheData::nextXid, NULL, offsetof, SERIALIZABLEXACT::outConflicts, RWConflictData::outLink, SERIALIZABLEXACT::pid, SERIALIZABLEXACT::possibleUnsafeConflicts, SERIALIZABLEXACT::prepareSeqNo, ProcSendSignal(), ReleaseOneSerializableXact(), ReleaseRWConflict(), SERIALIZABLEXACT::SeqNo, SerializableFinishedListLock, SerializableXactHashLock, SetNewSxactGlobalXmin(), ShmemVariableCache, SHMQueueEmpty(), SHMQueueInsertBefore(), SHMQueueNext(), SXACT_FLAG_CONFLICT_OUT, PredXactListData::SxactGlobalXmin, PredXactListData::SxactGlobalXminCount, SxactHasConflictOut, SxactHasSummaryConflictOut, RWConflictData::sxactIn, SxactIsCommitted, SxactIsDeferrableWaiting, SxactIsDoomed, SxactIsOnFinishedList, SxactIsPrepared, SxactIsReadOnly, SxactIsRolledBack, SxactIsROSafe, SxactIsROUnsafe, RWConflictData::sxactOut, TransactionIdEquals, PredXactListData::WritableSxactCount, and SERIALIZABLEXACT::xmin.
Referenced by GetSafeSnapshot(), PredicateLockTwoPhaseFinish(), ResourceOwnerReleaseInternal(), and SerializationNeededForRead().
{
bool needToClear;
RWConflict conflict,
nextConflict,
possibleUnsafeConflict;
SERIALIZABLEXACT *roXact;
/*
* We can't trust XactReadOnly here, because a transaction which started
* as READ WRITE can show as READ ONLY later, e.g., within
* substransactions. We want to flag a transaction as READ ONLY if it
* commits without writing so that de facto READ ONLY transactions get the
* benefit of some RO optimizations, so we will use this local variable to
* get some cleanup logic right which is based on whether the transaction
* was declared READ ONLY at the top level.
*/
bool topLevelIsDeclaredReadOnly;
if (MySerializableXact == InvalidSerializableXact)
{
Assert(LocalPredicateLockHash == NULL);
return;
}
Assert(!isCommit || SxactIsPrepared(MySerializableXact));
Assert(!isCommit || !SxactIsDoomed(MySerializableXact));
Assert(!SxactIsCommitted(MySerializableXact));
Assert(!SxactIsRolledBack(MySerializableXact));
/* may not be serializable during COMMIT/ROLLBACK PREPARED */
if (MySerializableXact->pid != 0)
Assert(IsolationIsSerializable());
/* We'd better not already be on the cleanup list. */
Assert(!SxactIsOnFinishedList(MySerializableXact));
topLevelIsDeclaredReadOnly = SxactIsReadOnly(MySerializableXact);
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/*
* We don't hold XidGenLock lock here, assuming that TransactionId is
* atomic!
*
* If this value is changing, we don't care that much whether we get the
* old or new value -- it is just used to determine how far
* GlobalSerizableXmin must advance before this transaction can be fully
* cleaned up. The worst that could happen is we wait for one more
* transaction to complete before freeing some RAM; correctness of visible
* behavior is not affected.
*/
MySerializableXact->finishedBefore = ShmemVariableCache->nextXid;
/*
* If it's not a commit it's a rollback, and we can clear our locks
* immediately.
*/
if (isCommit)
{
MySerializableXact->flags |= SXACT_FLAG_COMMITTED;
MySerializableXact->commitSeqNo = ++(PredXact->LastSxactCommitSeqNo);
/* Recognize implicit read-only transaction (commit without write). */
if (!MyXactDidWrite)
MySerializableXact->flags |= SXACT_FLAG_READ_ONLY;
}
else
{
/*
* The DOOMED flag indicates that we intend to roll back this
* transaction and so it should not cause serialization failures for
* other transactions that conflict with it. Note that this flag might
* already be set, if another backend marked this transaction for
* abort.
*
* The ROLLED_BACK flag further indicates that ReleasePredicateLocks
* has been called, and so the SerializableXact is eligible for
* cleanup. This means it should not be considered when calculating
* SxactGlobalXmin.
*/
MySerializableXact->flags |= SXACT_FLAG_DOOMED;
MySerializableXact->flags |= SXACT_FLAG_ROLLED_BACK;
/*
* If the transaction was previously prepared, but is now failing due
* to a ROLLBACK PREPARED or (hopefully very rare) error after the
* prepare, clear the prepared flag. This simplifies conflict
* checking.
*/
MySerializableXact->flags &= ~SXACT_FLAG_PREPARED;
}
if (!topLevelIsDeclaredReadOnly)
{
Assert(PredXact->WritableSxactCount > 0);
if (--(PredXact->WritableSxactCount) == 0)
{
/*
* Release predicate locks and rw-conflicts in for all committed
* transactions. There are no longer any transactions which might
* conflict with the locks and no chance for new transactions to
* overlap. Similarly, existing conflicts in can't cause pivots,
* and any conflicts in which could have completed a dangerous
* structure would already have caused a rollback, so any
* remaining ones must be benign.
*/
PredXact->CanPartialClearThrough = PredXact->LastSxactCommitSeqNo;
}
}
else
{
/*
* Read-only transactions: clear the list of transactions that might
* make us unsafe. Note that we use 'inLink' for the iteration as
* opposed to 'outLink' for the r/w xacts.
*/
possibleUnsafeConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->possibleUnsafeConflicts,
&MySerializableXact->possibleUnsafeConflicts,
offsetof(RWConflictData, inLink));
while (possibleUnsafeConflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->possibleUnsafeConflicts,
&possibleUnsafeConflict->inLink,
offsetof(RWConflictData, inLink));
Assert(!SxactIsReadOnly(possibleUnsafeConflict->sxactOut));
Assert(MySerializableXact == possibleUnsafeConflict->sxactIn);
ReleaseRWConflict(possibleUnsafeConflict);
possibleUnsafeConflict = nextConflict;
}
}
/* Check for conflict out to old committed transactions. */
if (isCommit
&& !SxactIsReadOnly(MySerializableXact)
&& SxactHasSummaryConflictOut(MySerializableXact))
{
/*
* we don't know which old committed transaction we conflicted with,
* so be conservative and use FirstNormalSerCommitSeqNo here
*/
MySerializableXact->SeqNo.earliestOutConflictCommit =
FirstNormalSerCommitSeqNo;
MySerializableXact->flags |= SXACT_FLAG_CONFLICT_OUT;
}
/*
* Release all outConflicts to committed transactions. If we're rolling
* back clear them all. Set SXACT_FLAG_CONFLICT_OUT if any point to
* previously committed transactions.
*/
conflict = (RWConflict)
SHMQueueNext(&MySerializableXact->outConflicts,
&MySerializableXact->outConflicts,
offsetof(RWConflictData, outLink));
while (conflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->outConflicts,
&conflict->outLink,
offsetof(RWConflictData, outLink));
if (isCommit
&& !SxactIsReadOnly(MySerializableXact)
&& SxactIsCommitted(conflict->sxactIn))
{
if ((MySerializableXact->flags & SXACT_FLAG_CONFLICT_OUT) == 0
|| conflict->sxactIn->prepareSeqNo < MySerializableXact->SeqNo.earliestOutConflictCommit)
MySerializableXact->SeqNo.earliestOutConflictCommit = conflict->sxactIn->prepareSeqNo;
MySerializableXact->flags |= SXACT_FLAG_CONFLICT_OUT;
}
if (!isCommit
|| SxactIsCommitted(conflict->sxactIn)
|| (conflict->sxactIn->SeqNo.lastCommitBeforeSnapshot >= PredXact->LastSxactCommitSeqNo))
ReleaseRWConflict(conflict);
conflict = nextConflict;
}
/*
* Release all inConflicts from committed and read-only transactions. If
* we're rolling back, clear them all.
*/
conflict = (RWConflict)
SHMQueueNext(&MySerializableXact->inConflicts,
&MySerializableXact->inConflicts,
offsetof(RWConflictData, inLink));
while (conflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->inConflicts,
&conflict->inLink,
offsetof(RWConflictData, inLink));
if (!isCommit
|| SxactIsCommitted(conflict->sxactOut)
|| SxactIsReadOnly(conflict->sxactOut))
ReleaseRWConflict(conflict);
conflict = nextConflict;
}
if (!topLevelIsDeclaredReadOnly)
{
/*
* Remove ourselves from the list of possible conflicts for concurrent
* READ ONLY transactions, flagging them as unsafe if we have a
* conflict out. If any are waiting DEFERRABLE transactions, wake them
* up if they are known safe or known unsafe.
*/
possibleUnsafeConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->possibleUnsafeConflicts,
&MySerializableXact->possibleUnsafeConflicts,
offsetof(RWConflictData, outLink));
while (possibleUnsafeConflict)
{
nextConflict = (RWConflict)
SHMQueueNext(&MySerializableXact->possibleUnsafeConflicts,
&possibleUnsafeConflict->outLink,
offsetof(RWConflictData, outLink));
roXact = possibleUnsafeConflict->sxactIn;
Assert(MySerializableXact == possibleUnsafeConflict->sxactOut);
Assert(SxactIsReadOnly(roXact));
/* Mark conflicted if necessary. */
if (isCommit
&& MyXactDidWrite
&& SxactHasConflictOut(MySerializableXact)
&& (MySerializableXact->SeqNo.earliestOutConflictCommit
<= roXact->SeqNo.lastCommitBeforeSnapshot))
{
/*
* This releases possibleUnsafeConflict (as well as all other
* possible conflicts for roXact)
*/
FlagSxactUnsafe(roXact);
}
else
{
ReleaseRWConflict(possibleUnsafeConflict);
/*
* If we were the last possible conflict, flag it safe. The
* transaction can now safely release its predicate locks (but
* that transaction's backend has to do that itself).
*/
if (SHMQueueEmpty(&roXact->possibleUnsafeConflicts))
roXact->flags |= SXACT_FLAG_RO_SAFE;
}
/*
* Wake up the process for a waiting DEFERRABLE transaction if we
* now know it's either safe or conflicted.
*/
if (SxactIsDeferrableWaiting(roXact) &&
(SxactIsROUnsafe(roXact) || SxactIsROSafe(roXact)))
ProcSendSignal(roXact->pid);
possibleUnsafeConflict = nextConflict;
}
}
/*
* Check whether it's time to clean up old transactions. This can only be
* done when the last serializable transaction with the oldest xmin among
* serializable transactions completes. We then find the "new oldest"
* xmin and purge any transactions which finished before this transaction
* was launched.
*/
needToClear = false;
if (TransactionIdEquals(MySerializableXact->xmin, PredXact->SxactGlobalXmin))
{
Assert(PredXact->SxactGlobalXminCount > 0);
if (--(PredXact->SxactGlobalXminCount) == 0)
{
SetNewSxactGlobalXmin();
needToClear = true;
}
}
LWLockRelease(SerializableXactHashLock);
LWLockAcquire(SerializableFinishedListLock, LW_EXCLUSIVE);
/* Add this to the list of transactions to check for later cleanup. */
if (isCommit)
SHMQueueInsertBefore(FinishedSerializableTransactions,
&MySerializableXact->finishedLink);
if (!isCommit)
ReleaseOneSerializableXact(MySerializableXact, false, false);
LWLockRelease(SerializableFinishedListLock);
if (needToClear)
ClearOldPredicateLocks();
MySerializableXact = InvalidSerializableXact;
MyXactDidWrite = false;
/* Delete per-transaction lock table */
if (LocalPredicateLockHash != NULL)
{
hash_destroy(LocalPredicateLockHash);
LocalPredicateLockHash = NULL;
}
}
| static void ReleasePredXact | ( | SERIALIZABLEXACT * | sxact | ) | [static] |
Definition at line 568 of file predicate.c.
References Assert, PredXactListData::availableList, PredXactListElementData::link, offsetof, ShmemAddrIsValid(), SHMQueueDelete(), and SHMQueueInsertBefore().
Referenced by GetSerializableTransactionSnapshotInt(), and ReleaseOneSerializableXact().
{
PredXactListElement ptle;
Assert(ShmemAddrIsValid(sxact));
ptle = (PredXactListElement)
(((char *) sxact)
- offsetof(PredXactListElementData, sxact)
+ offsetof(PredXactListElementData, link));
SHMQueueDelete(&ptle->link);
SHMQueueInsertBefore(&PredXact->availableList, &ptle->link);
}
| static void ReleaseRWConflict | ( | RWConflict | conflict | ) | [static] |
Definition at line 713 of file predicate.c.
References RWConflictPoolHeaderData::availableList, RWConflictData::inLink, RWConflictData::outLink, SHMQueueDelete(), and SHMQueueInsertBefore().
Referenced by FlagSxactUnsafe(), ReleaseOneSerializableXact(), and ReleasePredicateLocks().
{
SHMQueueDelete(&conflict->inLink);
SHMQueueDelete(&conflict->outLink);
SHMQueueInsertBefore(&RWConflictPool->availableList, &conflict->outLink);
}
| static void RemoveScratchTarget | ( | bool | lockheld | ) | [static] |
Definition at line 1987 of file predicate.c.
References Assert, HASH_REMOVE, hash_search_with_hash_value(), LW_EXCLUSIVE, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), ScratchPartitionLock, ScratchTargetTagHash, and SerializablePredicateLockListLock.
Referenced by DropAllPredicateLocksFromTable(), and TransferPredicateLocksToNewTarget().
{
bool found;
Assert(LWLockHeldByMe(SerializablePredicateLockListLock));
if (!lockheld)
LWLockAcquire(ScratchPartitionLock, LW_EXCLUSIVE);
hash_search_with_hash_value(PredicateLockTargetHash,
&ScratchTargetTag,
ScratchTargetTagHash,
HASH_REMOVE, &found);
Assert(found);
if (!lockheld)
LWLockRelease(ScratchPartitionLock);
}
| static void RemoveTargetIfNoLongerUsed | ( | PREDICATELOCKTARGET * | target, | |
| uint32 | targettaghash | |||
| ) | [static] |
Definition at line 2030 of file predicate.c.
References Assert, HASH_REMOVE, hash_search_with_hash_value(), LWLockHeldByMe(), NULL, PREDICATELOCKTARGET::predicateLocks, SerializablePredicateLockListLock, SHMQueueEmpty(), and PREDICATELOCKTARGET::tag.
Referenced by CheckTargetForConflictsIn(), ClearOldPredicateLocks(), DeleteChildTargetLocks(), DeleteLockTarget(), ReleaseOneSerializableXact(), and TransferPredicateLocksToNewTarget().
{
PREDICATELOCKTARGET *rmtarget PG_USED_FOR_ASSERTS_ONLY;
Assert(LWLockHeldByMe(SerializablePredicateLockListLock));
/* Can't remove it until no locks at this target. */
if (!SHMQueueEmpty(&target->predicateLocks))
return;
/* Actually remove the target. */
rmtarget = hash_search_with_hash_value(PredicateLockTargetHash,
&target->tag,
targettaghash,
HASH_REMOVE, NULL);
Assert(rmtarget == target);
}
| static void RestoreScratchTarget | ( | bool | lockheld | ) | [static] |
Definition at line 2008 of file predicate.c.
References Assert, HASH_ENTER, hash_search_with_hash_value(), LW_EXCLUSIVE, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), ScratchPartitionLock, ScratchTargetTagHash, and SerializablePredicateLockListLock.
Referenced by DropAllPredicateLocksFromTable(), and TransferPredicateLocksToNewTarget().
{
bool found;
Assert(LWLockHeldByMe(SerializablePredicateLockListLock));
if (!lockheld)
LWLockAcquire(ScratchPartitionLock, LW_EXCLUSIVE);
hash_search_with_hash_value(PredicateLockTargetHash,
&ScratchTargetTag,
ScratchTargetTagHash,
HASH_ENTER, &found);
Assert(!found);
if (!lockheld)
LWLockRelease(ScratchPartitionLock);
}
| static bool RWConflictExists | ( | const SERIALIZABLEXACT * | reader, | |
| const SERIALIZABLEXACT * | writer | |||
| ) | [static] |
Definition at line 624 of file predicate.c.
References Assert, SERIALIZABLEXACT::inConflicts, offsetof, SERIALIZABLEXACT::outConflicts, RWConflictData::outLink, SHMQueueEmpty(), SHMQueueNext(), RWConflictData::sxactIn, and SxactIsDoomed.
Referenced by CheckForSerializableConflictOut(), CheckTableForSerializableConflictIn(), CheckTargetForConflictsIn(), and SetRWConflict().
{
RWConflict conflict;
Assert(reader != writer);
/* Check the ends of the purported conflict first. */
if (SxactIsDoomed(reader)
|| SxactIsDoomed(writer)
|| SHMQueueEmpty(&reader->outConflicts)
|| SHMQueueEmpty(&writer->inConflicts))
return false;
/* A conflict is possible; walk the list to find out. */
conflict = (RWConflict)
SHMQueueNext(&reader->outConflicts,
&reader->outConflicts,
offsetof(RWConflictData, outLink));
while (conflict)
{
if (conflict->sxactIn == writer)
return true;
conflict = (RWConflict)
SHMQueueNext(&reader->outConflicts,
&conflict->outLink,
offsetof(RWConflictData, outLink));
}
/* No conflict found. */
return false;
}
Definition at line 485 of file predicate.c.
References InvalidSerializableXact, IsMVCCSnapshot, PredicateLockingNeededForRelation(), ReleasePredicateLocks(), and SxactIsROSafe.
Referenced by CheckForSerializableConflictOut(), PredicateLockPage(), PredicateLockRelation(), and PredicateLockTuple().
{
/* Nothing to do if this is not a serializable transaction */
if (MySerializableXact == InvalidSerializableXact)
return false;
/*
* Don't acquire locks or conflict when scanning with a special snapshot.
* This excludes things like CLUSTER and REINDEX. They use the wholesale
* functions TransferPredicateLocksToHeapRelation() and
* CheckTableForSerializableConflictIn() to participate serialization, but
* the scans involved don't need serialization.
*/
if (!IsMVCCSnapshot(snapshot))
return false;
/*
* Check if we have just become "RO-safe". If we have, immediately release
* all locks as they're not needed anymore. This also resets
* MySerializableXact, so that subsequent calls to this function can exit
* quickly.
*
* A transaction is flagged as RO_SAFE if all concurrent R/W transactions
* commit without having conflicts out to an earlier snapshot, thus
* ensuring that no conflicts are possible for this transaction.
*/
if (SxactIsROSafe(MySerializableXact))
{
ReleasePredicateLocks(false);
return false;
}
/* Check if the relation doesn't participate in predicate locking */
if (!PredicateLockingNeededForRelation(relation))
return false;
return true; /* no excuse to skip predicate locking */
}
Definition at line 529 of file predicate.c.
References InvalidSerializableXact, and PredicateLockingNeededForRelation().
Referenced by CheckForSerializableConflictIn(), and CheckTableForSerializableConflictIn().
{
/* Nothing to do if this is not a serializable transaction */
if (MySerializableXact == InvalidSerializableXact)
return false;
/* Check if the relation doesn't participate in predicate locking */
if (!PredicateLockingNeededForRelation(relation))
return false;
return true; /* no excuse to skip predicate locking */
}
| static void SetNewSxactGlobalXmin | ( | void | ) | [static] |
Definition at line 3140 of file predicate.c.
References Assert, FirstPredXact(), InvalidTransactionId, LWLockHeldByMe(), NextPredXact(), OldSerXidSetActiveSerXmin(), SerializableXactHashLock, PredXactListData::SxactGlobalXmin, PredXactListData::SxactGlobalXminCount, SxactIsCommitted, SxactIsRolledBack, TransactionIdEquals, TransactionIdIsValid, TransactionIdPrecedes(), and SERIALIZABLEXACT::xmin.
Referenced by ReleasePredicateLocks().
{
SERIALIZABLEXACT *sxact;
Assert(LWLockHeldByMe(SerializableXactHashLock));
PredXact->SxactGlobalXmin = InvalidTransactionId;
PredXact->SxactGlobalXminCount = 0;
for (sxact = FirstPredXact(); sxact != NULL; sxact = NextPredXact(sxact))
{
if (!SxactIsRolledBack(sxact)
&& !SxactIsCommitted(sxact)
&& sxact != OldCommittedSxact)
{
Assert(sxact->xmin != InvalidTransactionId);
if (!TransactionIdIsValid(PredXact->SxactGlobalXmin)
|| TransactionIdPrecedes(sxact->xmin,
PredXact->SxactGlobalXmin))
{
PredXact->SxactGlobalXmin = sxact->xmin;
PredXact->SxactGlobalXminCount = 1;
}
else if (TransactionIdEquals(sxact->xmin,
PredXact->SxactGlobalXmin))
PredXact->SxactGlobalXminCount++;
}
}
OldSerXidSetActiveSerXmin(PredXact->SxactGlobalXmin);
}
| static void SetPossibleUnsafeConflict | ( | SERIALIZABLEXACT * | roXact, | |
| SERIALIZABLEXACT * | activeXact | |||
| ) | [static] |
Definition at line 683 of file predicate.c.
References Assert, RWConflictPoolHeaderData::availableList, ereport, errcode(), errhint(), errmsg(), ERROR, RWConflictData::inLink, offsetof, RWConflictData::outLink, SERIALIZABLEXACT::possibleUnsafeConflicts, SHMQueueDelete(), SHMQueueInsertBefore(), SHMQueueNext(), RWConflictData::sxactIn, SxactIsReadOnly, and RWConflictData::sxactOut.
Referenced by GetSerializableTransactionSnapshotInt().
{
RWConflict conflict;
Assert(roXact != activeXact);
Assert(SxactIsReadOnly(roXact));
Assert(!SxactIsReadOnly(activeXact));
conflict = (RWConflict)
SHMQueueNext(&RWConflictPool->availableList,
&RWConflictPool->availableList,
offsetof(RWConflictData, outLink));
if (!conflict)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("not enough elements in RWConflictPool to record a potential read/write conflict"),
errhint("You might need to run fewer transactions at a time or increase max_connections.")));
SHMQueueDelete(&conflict->outLink);
conflict->sxactOut = activeXact;
conflict->sxactIn = roXact;
SHMQueueInsertBefore(&activeXact->possibleUnsafeConflicts,
&conflict->outLink);
SHMQueueInsertBefore(&roXact->possibleUnsafeConflicts,
&conflict->inLink);
}
| static void SetRWConflict | ( | SERIALIZABLEXACT * | reader, | |
| SERIALIZABLEXACT * | writer | |||
| ) | [static] |
Definition at line 657 of file predicate.c.
References Assert, RWConflictPoolHeaderData::availableList, ereport, errcode(), errhint(), errmsg(), ERROR, SERIALIZABLEXACT::inConflicts, RWConflictData::inLink, offsetof, SERIALIZABLEXACT::outConflicts, RWConflictData::outLink, RWConflictExists(), SHMQueueDelete(), SHMQueueInsertBefore(), SHMQueueNext(), RWConflictData::sxactIn, and RWConflictData::sxactOut.
Referenced by FlagRWConflict().
{
RWConflict conflict;
Assert(reader != writer);
Assert(!RWConflictExists(reader, writer));
conflict = (RWConflict)
SHMQueueNext(&RWConflictPool->availableList,
&RWConflictPool->availableList,
offsetof(RWConflictData, outLink));
if (!conflict)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("not enough elements in RWConflictPool to record a read/write conflict"),
errhint("You might need to run fewer transactions at a time or increase max_connections.")));
SHMQueueDelete(&conflict->outLink);
conflict->sxactOut = reader;
conflict->sxactIn = writer;
SHMQueueInsertBefore(&reader->outConflicts, &conflict->outLink);
SHMQueueInsertBefore(&writer->inConflicts, &conflict->inLink);
}
| void SetSerializableTransactionSnapshot | ( | Snapshot | snapshot, | |
| TransactionId | sourcexid | |||
| ) |
Definition at line 1612 of file predicate.c.
References Assert, ereport, errcode(), errmsg(), ERROR, GetSerializableTransactionSnapshotInt(), IsolationIsSerializable, XactDeferrable, and XactReadOnly.
Referenced by SetTransactionSnapshot().
{
Assert(IsolationIsSerializable());
/*
* We do not allow SERIALIZABLE READ ONLY DEFERRABLE transactions to
* import snapshots, since there's no way to wait for a safe snapshot when
* we're using the snap we're told to. (XXX instead of throwing an error,
* we could just ignore the XactDeferrable flag?)
*/
if (XactReadOnly && XactDeferrable)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("a snapshot-importing transaction must not be READ ONLY DEFERRABLE")));
(void) GetSerializableTransactionSnapshotInt(snapshot, sourcexid);
}
| static void SummarizeOldestCommittedSxact | ( | void | ) | [static] |
Definition at line 1443 of file predicate.c.
References SERIALIZABLEXACT::earliestOutConflictCommit, SERIALIZABLEXACT::finishedLink, InvalidSerCommitSeqNo, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), offsetof, OldSerXidAdd(), ReleaseOneSerializableXact(), SERIALIZABLEXACT::SeqNo, SerializableFinishedListLock, SHMQueueDelete(), SHMQueueEmpty(), SHMQueueNext(), SxactHasConflictOut, SxactIsReadOnly, SERIALIZABLEXACT::topXid, and TransactionIdIsValid.
Referenced by GetSerializableTransactionSnapshotInt().
{
SERIALIZABLEXACT *sxact;
LWLockAcquire(SerializableFinishedListLock, LW_EXCLUSIVE);
/*
* This function is only called if there are no sxact slots available.
* Some of them must belong to old, already-finished transactions, so
* there should be something in FinishedSerializableTransactions list that
* we can summarize. However, there's a race condition: while we were not
* holding any locks, a transaction might have ended and cleaned up all
* the finished sxact entries already, freeing up their sxact slots. In
* that case, we have nothing to do here. The caller will find one of the
* slots released by the other backend when it retries.
*/
if (SHMQueueEmpty(FinishedSerializableTransactions))
{
LWLockRelease(SerializableFinishedListLock);
return;
}
/*
* Grab the first sxact off the finished list -- this will be the earliest
* commit. Remove it from the list.
*/
sxact = (SERIALIZABLEXACT *)
SHMQueueNext(FinishedSerializableTransactions,
FinishedSerializableTransactions,
offsetof(SERIALIZABLEXACT, finishedLink));
SHMQueueDelete(&(sxact->finishedLink));
/* Add to SLRU summary information. */
if (TransactionIdIsValid(sxact->topXid) && !SxactIsReadOnly(sxact))
OldSerXidAdd(sxact->topXid, SxactHasConflictOut(sxact)
? sxact->SeqNo.earliestOutConflictCommit : InvalidSerCommitSeqNo);
/* Summarize and release the detail. */
ReleaseOneSerializableXact(sxact, false, true);
LWLockRelease(SerializableFinishedListLock);
}
| void TransferPredicateLocksToHeapRelation | ( | Relation | relation | ) |
Definition at line 3012 of file predicate.c.
References DropAllPredicateLocksFromTable().
Referenced by ATRewriteTable(), cluster_rel(), index_drop(), and reindex_index().
{
DropAllPredicateLocksFromTable(relation, true);
}
| static bool TransferPredicateLocksToNewTarget | ( | PREDICATELOCKTARGETTAG | oldtargettag, | |
| PREDICATELOCKTARGETTAG | newtargettag, | |||
| bool | removeOld | |||
| ) | [static] |
Definition at line 2600 of file predicate.c.
References Assert, PREDICATELOCK::commitSeqNo, DeleteLockTarget(), HASH_ENTER_NULL, HASH_FIND, HASH_REMOVE, hash_search_with_hash_value(), InvalidSerCommitSeqNo, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockHeldByMe(), LWLockRelease(), PREDICATELOCKTAG::myTarget, PREDICATELOCKTAG::myXact, NULL, offsetof, PredicateLockHashCodeFromTargetHashCode, PredicateLockHashPartitionLock, SERIALIZABLEXACT::predicateLocks, PREDICATELOCKTARGET::predicateLocks, PredicateLockTargetTagHashCode, RemoveScratchTarget(), RemoveTargetIfNoLongerUsed(), RestoreScratchTarget(), SerializablePredicateLockListLock, SerializableXactHashLock, SHMQueueDelete(), SHMQueueEmpty(), SHMQueueInit(), SHMQueueInsertBefore(), SHMQueueNext(), PREDICATELOCK::tag, PREDICATELOCK::targetLink, and PREDICATELOCK::xactLink.
Referenced by PredicateLockPageSplit().
{
uint32 oldtargettaghash;
LWLockId oldpartitionLock;
PREDICATELOCKTARGET *oldtarget;
uint32 newtargettaghash;
LWLockId newpartitionLock;
bool found;
bool outOfShmem = false;
Assert(LWLockHeldByMe(SerializablePredicateLockListLock));
oldtargettaghash = PredicateLockTargetTagHashCode(&oldtargettag);
newtargettaghash = PredicateLockTargetTagHashCode(&newtargettag);
oldpartitionLock = PredicateLockHashPartitionLock(oldtargettaghash);
newpartitionLock = PredicateLockHashPartitionLock(newtargettaghash);
if (removeOld)
{
/*
* Remove the dummy entry to give us scratch space, so we know we'll
* be able to create the new lock target.
*/
RemoveScratchTarget(false);
}
/*
* We must get the partition locks in ascending sequence to avoid
* deadlocks. If old and new partitions are the same, we must request the
* lock only once.
*/
if (oldpartitionLock < newpartitionLock)
{
LWLockAcquire(oldpartitionLock,
(removeOld ? LW_EXCLUSIVE : LW_SHARED));
LWLockAcquire(newpartitionLock, LW_EXCLUSIVE);
}
else if (oldpartitionLock > newpartitionLock)
{
LWLockAcquire(newpartitionLock, LW_EXCLUSIVE);
LWLockAcquire(oldpartitionLock,
(removeOld ? LW_EXCLUSIVE : LW_SHARED));
}
else
LWLockAcquire(newpartitionLock, LW_EXCLUSIVE);
/*
* Look for the old target. If not found, that's OK; no predicate locks
* are affected, so we can just clean up and return. If it does exist,
* walk its list of predicate locks and move or copy them to the new
* target.
*/
oldtarget = hash_search_with_hash_value(PredicateLockTargetHash,
&oldtargettag,
oldtargettaghash,
HASH_FIND, NULL);
if (oldtarget)
{
PREDICATELOCKTARGET *newtarget;
PREDICATELOCK *oldpredlock;
PREDICATELOCKTAG newpredlocktag;
newtarget = hash_search_with_hash_value(PredicateLockTargetHash,
&newtargettag,
newtargettaghash,
HASH_ENTER_NULL, &found);
if (!newtarget)
{
/* Failed to allocate due to insufficient shmem */
outOfShmem = true;
goto exit;
}
/* If we created a new entry, initialize it */
if (!found)
SHMQueueInit(&(newtarget->predicateLocks));
newpredlocktag.myTarget = newtarget;
/*
* Loop through all the locks on the old target, replacing them with
* locks on the new target.
*/
oldpredlock = (PREDICATELOCK *)
SHMQueueNext(&(oldtarget->predicateLocks),
&(oldtarget->predicateLocks),
offsetof(PREDICATELOCK, targetLink));
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
while (oldpredlock)
{
SHM_QUEUE *predlocktargetlink;
PREDICATELOCK *nextpredlock;
PREDICATELOCK *newpredlock;
SerCommitSeqNo oldCommitSeqNo = oldpredlock->commitSeqNo;
predlocktargetlink = &(oldpredlock->targetLink);
nextpredlock = (PREDICATELOCK *)
SHMQueueNext(&(oldtarget->predicateLocks),
predlocktargetlink,
offsetof(PREDICATELOCK, targetLink));
newpredlocktag.myXact = oldpredlock->tag.myXact;
if (removeOld)
{
SHMQueueDelete(&(oldpredlock->xactLink));
SHMQueueDelete(&(oldpredlock->targetLink));
hash_search_with_hash_value
(PredicateLockHash,
&oldpredlock->tag,
PredicateLockHashCodeFromTargetHashCode(&oldpredlock->tag,
oldtargettaghash),
HASH_REMOVE, &found);
Assert(found);
}
newpredlock = (PREDICATELOCK *)
hash_search_with_hash_value(PredicateLockHash,
&newpredlocktag,
PredicateLockHashCodeFromTargetHashCode(&newpredlocktag,
newtargettaghash),
HASH_ENTER_NULL,
&found);
if (!newpredlock)
{
/* Out of shared memory. Undo what we've done so far. */
LWLockRelease(SerializableXactHashLock);
DeleteLockTarget(newtarget, newtargettaghash);
outOfShmem = true;
goto exit;
}
if (!found)
{
SHMQueueInsertBefore(&(newtarget->predicateLocks),
&(newpredlock->targetLink));
SHMQueueInsertBefore(&(newpredlocktag.myXact->predicateLocks),
&(newpredlock->xactLink));
newpredlock->commitSeqNo = oldCommitSeqNo;
}
else
{
if (newpredlock->commitSeqNo < oldCommitSeqNo)
newpredlock->commitSeqNo = oldCommitSeqNo;
}
Assert(newpredlock->commitSeqNo != 0);
Assert((newpredlock->commitSeqNo == InvalidSerCommitSeqNo)
|| (newpredlock->tag.myXact == OldCommittedSxact));
oldpredlock = nextpredlock;
}
LWLockRelease(SerializableXactHashLock);
if (removeOld)
{
Assert(SHMQueueEmpty(&oldtarget->predicateLocks));
RemoveTargetIfNoLongerUsed(oldtarget, oldtargettaghash);
}
}
exit:
/* Release partition locks in reverse order of acquisition. */
if (oldpartitionLock < newpartitionLock)
{
LWLockRelease(newpartitionLock);
LWLockRelease(oldpartitionLock);
}
else if (oldpartitionLock > newpartitionLock)
{
LWLockRelease(oldpartitionLock);
LWLockRelease(newpartitionLock);
}
else
LWLockRelease(newpartitionLock);
if (removeOld)
{
/* We shouldn't run out of memory if we're moving locks */
Assert(!outOfShmem);
/* Put the scrach entry back */
RestoreScratchTarget(false);
}
return !outOfShmem;
}
| static bool XidIsConcurrent | ( | TransactionId | xid | ) | [static] |
Definition at line 3825 of file predicate.c.
References Assert, GetTopTransactionIdIfAny(), GetTransactionSnapshot(), i, TransactionIdEquals, TransactionIdFollowsOrEquals(), TransactionIdIsValid, TransactionIdPrecedes(), SnapshotData::xcnt, SnapshotData::xip, SnapshotData::xmax, and SnapshotData::xmin.
Referenced by CheckForSerializableConflictOut().
{
Snapshot snap;
uint32 i;
Assert(TransactionIdIsValid(xid));
Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny()));
snap = GetTransactionSnapshot();
if (TransactionIdPrecedes(xid, snap->xmin))
return false;
if (TransactionIdFollowsOrEquals(xid, snap->xmax))
return true;
for (i = 0; i < snap->xcnt; i++)
{
if (xid == snap->xip[i])
return true;
}
return false;
}
SHM_QUEUE* FinishedSerializableTransactions [static] |
Definition at line 377 of file predicate.c.
HTAB* LocalPredicateLockHash = NULL [static] |
Definition at line 392 of file predicate.c.
Definition at line 351 of file predicate.c.
Referenced by GetSerializableTransactionSnapshotInt(), and PredicateLockPromotionThreshold().
SERIALIZABLEXACT* MySerializableXact = InvalidSerializableXact [static] |
Definition at line 399 of file predicate.c.
bool MyXactDidWrite = false [static] |
Definition at line 400 of file predicate.c.
Referenced by CheckForSerializableConflictIn(), CheckTableForSerializableConflictIn(), GetSerializableTransactionSnapshotInt(), PostPrepare_PredicateLocks(), PredicateLockTwoPhaseFinish(), and ReleasePredicateLocks().
SERIALIZABLEXACT* OldCommittedSxact [static] |
Definition at line 347 of file predicate.c.
OldSerXidControl oldSerXidControl [static] |
Definition at line 339 of file predicate.c.
SlruCtlData OldSerXidSlruCtlData [static] |
Definition at line 305 of file predicate.c.
HTAB* PredicateLockHash [static] |
Definition at line 376 of file predicate.c.
HTAB* PredicateLockTargetHash [static] |
Definition at line 375 of file predicate.c.
PredXactList PredXact [static] |
Definition at line 362 of file predicate.c.
RWConflictPoolHeader RWConflictPool [static] |
Definition at line 368 of file predicate.c.
int ScratchPartitionLock [static] |
Definition at line 386 of file predicate.c.
Referenced by InitPredicateLocks(), RemoveScratchTarget(), and RestoreScratchTarget().
const PREDICATELOCKTARGETTAG ScratchTargetTag = {0, 0, 0, 0, 0} [static] |
Definition at line 384 of file predicate.c.
uint32 ScratchTargetTagHash [static] |
Definition at line 385 of file predicate.c.
Referenced by InitPredicateLocks(), RemoveScratchTarget(), and RestoreScratchTarget().
HTAB* SerializableXidHash [static] |
Definition at line 374 of file predicate.c.
1.7.1