Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions | Variables

predicate.h File Reference

#include "utils/relcache.h"
#include "utils/snapshot.h"
Include dependency graph for predicate.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Defines

#define NUM_OLDSERXID_BUFFERS   16

Functions

void InitPredicateLocks (void)
Size PredicateLockShmemSize (void)
void CheckPointPredicate (void)
bool PageIsPredicateLocked (Relation relation, BlockNumber blkno)
Snapshot GetSerializableTransactionSnapshot (Snapshot snapshot)
void SetSerializableTransactionSnapshot (Snapshot snapshot, TransactionId sourcexid)
void RegisterPredicateLockingXid (TransactionId xid)
void PredicateLockRelation (Relation relation, Snapshot snapshot)
void PredicateLockPage (Relation relation, BlockNumber blkno, Snapshot snapshot)
void PredicateLockTuple (Relation relation, HeapTuple tuple, Snapshot snapshot)
void PredicateLockPageSplit (Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
void PredicateLockPageCombine (Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
void TransferPredicateLocksToHeapRelation (Relation relation)
void ReleasePredicateLocks (bool isCommit)
void CheckForSerializableConflictOut (bool valid, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
void CheckForSerializableConflictIn (Relation relation, HeapTuple tuple, Buffer buffer)
void CheckTableForSerializableConflictIn (Relation relation)
void PreCommit_CheckForSerializationFailure (void)
void AtPrepare_PredicateLocks (void)
void PostPrepare_PredicateLocks (TransactionId xid)
void PredicateLockTwoPhaseFinish (TransactionId xid, bool isCommit)
void predicatelock_twophase_recover (TransactionId xid, uint16 info, void *recdata, uint32 len)

Variables

int max_predicate_locks_per_xact

Define Documentation

#define NUM_OLDSERXID_BUFFERS   16

Definition at line 28 of file predicate.h.

Referenced by OldSerXidInit(), and PredicateLockShmemSize().


Function Documentation

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);
}

void CheckForSerializableConflictIn ( Relation  relation,
HeapTuple  tuple,
Buffer  buffer 
)

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  valid,
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);
}

Snapshot GetSerializableTransactionSnapshot ( Snapshot  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);
}

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);
}

bool PageIsPredicateLocked ( Relation  relation,
BlockNumber  blkno 
)
void PostPrepare_PredicateLocks ( TransactionId  xid  ) 
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);
}

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);
    }
}

void PredicateLockPage ( Relation  relation,
BlockNumber  blkno,
Snapshot  snapshot 
)
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);
}

void PredicateLockRelation ( Relation  relation,
Snapshot  snapshot 
)
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;
}

void PredicateLockTuple ( Relation  relation,
HeapTuple  tuple,
Snapshot  snapshot 
)

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);
}

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;
    }
}

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);
}

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);
}


Variable Documentation