diff options
Diffstat (limited to 'src/backend/utils/time/tqual.c')
-rw-r--r-- | src/backend/utils/time/tqual.c | 815 |
1 files changed, 815 insertions, 0 deletions
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c new file mode 100644 index 00000000000..dd115b69885 --- /dev/null +++ b/src/backend/utils/time/tqual.c @@ -0,0 +1,815 @@ +/*------------------------------------------------------------------------- + * + * tqual.c-- + * POSTGRES time qualification code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +/* #define TQUALDEBUG 1 */ + +#include "postgres.h" + +#include "access/htup.h" +#include "access/xact.h" +#include "storage/bufmgr.h" +#include "access/transam.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/nabstime.h" + +#include "utils/tqual.h" + +/* + * TimeQualMode -- + * Mode indicator for treatment of time qualifications. + */ +typedef uint16 TimeQualMode; + +#define TimeQualAt 0x1 +#define TimeQualNewer 0x2 +#define TimeQualOlder 0x4 +#define TimeQualAll 0x8 + +#define TimeQualMask 0xf + +#define TimeQualEvery 0x0 +#define TimeQualRange (TimeQualNewer | TimeQualOlder) +#define TimeQualAllAt (TimeQualAt | TimeQualAll) + +typedef struct TimeQualData { + AbsoluteTime start; + AbsoluteTime end; + TimeQualMode mode; +} TimeQualData; + +typedef TimeQualData *InternalTimeQual; + +static TimeQualData SelfTimeQualData; +TimeQual SelfTimeQual = (Pointer)&SelfTimeQualData; + +extern bool PostgresIsInitialized; + +/* + * XXX Transaction system override hacks start here + */ +#ifndef GOODAMI + +static TransactionId HeapSpecialTransactionId = InvalidTransactionId; +static CommandId HeapSpecialCommandId = FirstCommandId; + +void +setheapoverride(bool on) +{ + if (on) { + TransactionIdStore(GetCurrentTransactionId(), + &HeapSpecialTransactionId); + HeapSpecialCommandId = GetCurrentCommandId(); + } else { + HeapSpecialTransactionId = InvalidTransactionId; + } +} + +/* static */ +bool +heapisoverride() +{ + if (!TransactionIdIsValid(HeapSpecialTransactionId)) { + return (false); + } + + if (!TransactionIdEquals(GetCurrentTransactionId(), + HeapSpecialTransactionId) || + GetCurrentCommandId() != HeapSpecialCommandId) { + HeapSpecialTransactionId = InvalidTransactionId; + + return (false); + } + return (true); +} + +#endif /* !defined(GOODAMI) */ +/* + * XXX Transaction system override hacks end here + */ + +static bool HeapTupleSatisfiesItself(HeapTuple tuple); +static bool HeapTupleSatisfiesNow(HeapTuple tuple); +static bool HeapTupleSatisfiesSnapshotInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual); +static bool HeapTupleSatisfiesUpperBoundedInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual); +static bool HeapTupleSatisfiesUpperUnboundedInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual); + + + +/* + * TimeQualIsValid -- + * True iff time qualification is valid. + */ +bool +TimeQualIsValid(TimeQual qual) +{ + bool hasStartTime; + + if (!PointerIsValid(qual) || qual == SelfTimeQual) { + return (true); + } + + if (((InternalTimeQual)qual)->mode & ~TimeQualMask) { + return (false); + } + + if (((InternalTimeQual)qual)->mode & TimeQualAt) { + return (AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->start)); + } + + hasStartTime = false; + + if (((InternalTimeQual)qual)->mode & TimeQualNewer) { + if (!AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->start)) { + return (false); + } + hasStartTime = true; + } + + if (((InternalTimeQual)qual)->mode & TimeQualOlder) { + if (!AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->end)) { + return (false); + } + if (hasStartTime) { + return ((bool)!AbsoluteTimeIsBefore( + ((InternalTimeQual)qual)->end, + ((InternalTimeQual)qual)->start)); + } + } + return (true); +} + +/* + * TimeQualIsLegal -- + * True iff time qualification is legal. + * I.e., true iff time qualification does not intersects the future, + * relative to the transaction start time. + * + * Note: + * Assumes time qualification is valid. + */ +bool +TimeQualIsLegal(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (true); + } + + /* TimeQualAt */ + if (((InternalTimeQual)qual)->mode & TimeQualAt) { + AbsoluteTime a, b; + + a = ((InternalTimeQual)qual)->start; + b = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsAfter(a, b)) + return (false); + else + return (true); + } + + /* TimeQualOlder or TimeQualRange */ + if (((InternalTimeQual)qual)->mode & TimeQualOlder) { + AbsoluteTime a, b; + + a = ((InternalTimeQual)qual)->end; + b = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsAfter(a, b)) + return (false); + else + return (true); + } + + /* TimeQualNewer */ + if (((InternalTimeQual)qual)->mode & TimeQualNewer) { + AbsoluteTime a, b; + + a = ((InternalTimeQual)qual)->start; + b = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsAfter(a, b)) + return (false); + else + return (true); + } + + /* TimeQualEvery */ + return (true); +} + +/* + * TimeQualIncludesNow -- + * True iff time qualification includes "now." + * + * Note: + * Assumes time qualification is valid. + */ +bool +TimeQualIncludesNow(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (true); + } + + if (((InternalTimeQual)qual)->mode & TimeQualAt) { + return (false); + } + if (((InternalTimeQual)qual)->mode & TimeQualOlder && + !AbsoluteTimeIsAfter( + ((InternalTimeQual)qual)->end, + GetCurrentTransactionStartTime())) { + + return (false); + } + return (true); +} + +/* + * TimeQualIncludesPast -- + * True iff time qualification includes some time in the past. + * + * Note: + * Assumes time qualification is valid. + * XXX may not be needed? + */ +bool +TimeQualIncludesPast(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (false); + } + + /* otherwise, must check archive (setting locks as appropriate) */ + return (true); +} + +/* + * TimeQualIsSnapshot -- + * True iff time qualification is a snapshot qualification. + * + * Note: + * Assumes time qualification is valid. + */ +bool +TimeQualIsSnapshot(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (false); + } + + return ((bool)!!(((InternalTimeQual)qual)->mode & TimeQualAt)); +} + +/* + * TimeQualIsRanged -- + * True iff time qualification is a ranged qualification. + * + * Note: + * Assumes time qualification is valid. + */ +bool +TimeQualIsRanged(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (false); + } + + return ((bool)!(((InternalTimeQual)qual)->mode & TimeQualAt)); +} + +/* + * TimeQualIndicatesDisableValidityChecking -- + * True iff time qualification indicates validity checking should be + * disabled. + * + * Note: + * XXX This should not be implemented since this does not make sense. + */ +bool +TimeQualIndicatesDisableValidityChecking(TimeQual qual) +{ + Assert (TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (false); + } + + if (((InternalTimeQual)qual)->mode & TimeQualAll) { + return (true); + } + return (false); +} + +/* + * TimeQualGetSnapshotTime -- + * Returns time for a snapshot time qual. + * + * Note: + * Assumes time qual is valid snapshot time qual. + */ +AbsoluteTime +TimeQualGetSnapshotTime(TimeQual qual) +{ + Assert(TimeQualIsSnapshot(qual)); + + return (((InternalTimeQual)qual)->start); +} + +/* + * TimeQualGetStartTime -- + * Returns start time for a ranged time qual. + * + * Note: + * Assumes time qual is valid ranged time qual. + */ +AbsoluteTime +TimeQualGetStartTime(TimeQual qual) +{ + Assert(TimeQualIsRanged(qual)); + + return (((InternalTimeQual)qual)->start); +} + +/* + * TimeQualGetEndTime -- + * Returns end time for a ranged time qual. + * + * Note: + * Assumes time qual is valid ranged time qual. + */ +AbsoluteTime +TimeQualGetEndTime(TimeQual qual) +{ + Assert(TimeQualIsRanged(qual)); + + return (((InternalTimeQual)qual)->end); +} + +/* + * TimeFormSnapshotTimeQual -- + * Returns snapshot time qual for a time. + * + * Note: + * Assumes time is valid. + */ +TimeQual +TimeFormSnapshotTimeQual(AbsoluteTime time) +{ + InternalTimeQual qual; + + Assert(AbsoluteTimeIsBackwardCompatiblyValid(time)); + + qual = (InternalTimeQual)palloc(sizeof *qual); + + qual->start = time; + qual->end = INVALID_ABSTIME; + qual->mode = TimeQualAt; + + return ((TimeQual)qual); +} + +/* + * TimeFormRangedTimeQual -- + * Returns ranged time qual for a pair of times. + * + * Note: + * If start time is invalid, it is regarded as the epoch. + * If end time is invalid, it is regarded as "now." + * Assumes start time is before (or the same as) end time. + */ +TimeQual +TimeFormRangedTimeQual(AbsoluteTime startTime, + AbsoluteTime endTime) +{ + InternalTimeQual qual; + + qual = (InternalTimeQual)palloc(sizeof *qual); + + qual->start = startTime; + qual->end = endTime; + qual->mode = TimeQualEvery; + + if (AbsoluteTimeIsBackwardCompatiblyValid(startTime)) { + qual->mode |= TimeQualNewer; + } + if (AbsoluteTimeIsBackwardCompatiblyValid(endTime)) { + qual->mode |= TimeQualOlder; + } + + return ((TimeQual)qual); +} + +/* + * HeapTupleSatisfiesTimeQual -- + * True iff heap tuple satsifies a time qual. + * + * Note: + * Assumes heap tuple is valid. + * Assumes time qual is valid. + * XXX Many of the checks may be simplified and still remain correct. + * XXX Partial answers to the checks may be cached in an ItemId. + */ +bool +HeapTupleSatisfiesTimeQual(HeapTuple tuple, TimeQual qual) +{ +/* extern TransactionId AmiTransactionId; */ + + Assert(HeapTupleIsValid(tuple)); + Assert(TimeQualIsValid(qual)); + + if (TransactionIdEquals(tuple->t_xmax, AmiTransactionId)) + return(false); + + if (qual == SelfTimeQual || heapisoverride()) { + return (HeapTupleSatisfiesItself(tuple)); + } + + if (qual == NowTimeQual) { + return (HeapTupleSatisfiesNow(tuple)); + } + + if (!TimeQualIsLegal(qual)) { + elog(WARN, "HeapTupleSatisfiesTimeQual: illegal time qual"); + } + + if (TimeQualIndicatesDisableValidityChecking(qual)) { + elog(WARN, "HeapTupleSatisfiesTimeQual: no disabled validity checking (yet)"); + } + + if (TimeQualIsSnapshot(qual)) { + return (HeapTupleSatisfiesSnapshotInternalTimeQual(tuple, + (InternalTimeQual)qual)); + } + + if (TimeQualIncludesNow(qual)) { + return (HeapTupleSatisfiesUpperUnboundedInternalTimeQual(tuple, + (InternalTimeQual)qual)); + } + + return (HeapTupleSatisfiesUpperBoundedInternalTimeQual(tuple, + (InternalTimeQual)qual)); +} + +/* + * HeapTupleSatisfiesItself -- + * True iff heap tuple is valid for "itself." + * + * Note: + * Assumes heap tuple is valid. + */ +/* + * The satisfaction of "itself" requires the following: + * + * ((Xmin == my-transaction && (Xmax is null [|| Xmax != my-transaction)]) + * || + * + * (Xmin is committed && + * (Xmax is null || (Xmax != my-transaction && Xmax is not committed))) + */ +static bool +HeapTupleSatisfiesItself(HeapTuple tuple) +{ + /* + * XXX Several evil casts are made in this routine. Casting XID to be + * TransactionId works only because TransactionId->data is the first + * (and only) field of the structure. + */ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) && + !TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + } + /* the tuple was inserted validly */ + + if (AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) { + return (false); + } + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) { + return (false); + } + + return ((bool)!TransactionIdDidCommit((TransactionId)tuple->t_xmax)); +} + +/* + * HeapTupleSatisfiesNow -- + * True iff heap tuple is valid "now." + * + * Note: + * Assumes heap tuple is valid. + */ +/* + * The satisfaction of "now" requires the following: + * + * ((Xmin == my-transaction && Cmin != my-command && + * (Xmax is null || (Xmax == my-transaction && Cmax != my-command))) + * || + * + * (Xmin is committed && + * (Xmax is null || (Xmax == my-transaction && Cmax == my-command) || + * (Xmax is not committed && Xmax != my-transaction)))) + * + * mao says 17 march 1993: the tests in this routine are correct; + * if you think they're not, you're wrong, and you should think + * about it again. i know, it happened to me. we don't need to + * check commit time against the start time of this transaction + * because 2ph locking protects us from doing the wrong thing. + * if you mess around here, you'll break serializability. the only + * problem with this code is that it does the wrong thing for system + * catalog updates, because the catalogs aren't subject to 2ph, so + * the serializability guarantees we provide don't extend to xacts + * that do catalog accesses. this is unfortunate, but not critical. + */ +static bool +HeapTupleSatisfiesNow(HeapTuple tuple) +{ + if (AMI_OVERRIDE) + return true; + /* + * If the transaction system isn't yet initialized, then we assume + * that transactions committed. We only look at system catalogs + * during startup, so this is less awful than it seems, but it's + * still pretty awful. + */ + + if (!PostgresIsInitialized) + return ((bool)(TransactionIdIsValid((TransactionId)tuple->t_xmin) && + !TransactionIdIsValid((TransactionId)tuple->t_xmax))); + + /* + * XXX Several evil casts are made in this routine. Casting XID to be + * TransactionId works only because TransactionId->data is the first + * (and only) field of the structure. + */ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) + && CommandIdIsCurrentCommandId(tuple->t_cmin)) { + + return (false); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) + && !CommandIdIsCurrentCommandId(tuple->t_cmin)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + Assert(TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)); + + if (CommandIdIsCurrentCommandId(tuple->t_cmax)) { + return (true); + } + } + + /* + * this call is VERY expensive - requires a log table lookup. + */ + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + } + + /* by here, the inserting transaction has committed */ + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) { + return (false); + } + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmax)) { + return (true); + } + + /* by here, deleting transaction has committed */ + return (false); +} + +/* + * HeapTupleSatisfiesSnapshotInternalTimeQual -- + * True iff heap tuple is valid at the snapshot time qualification. + * + * Note: + * Assumes heap tuple is valid. + * Assumes internal time qualification is valid snapshot qualification. + */ +/* + * The satisfaction of Rel[T] requires the following: + * + * (Xmin is committed && Tmin <= T && + * (Xmax is null || (Xmax is not committed && Xmax != my-transaction) || + * Tmax >= T)) + */ +static bool +HeapTupleSatisfiesSnapshotInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual) +{ + /* + * XXX Several evil casts are made in this routine. Casting XID to be + * TransactionId works only because TransactionId->data is the first + * (and only) field of the structure. + */ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + + tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin); + } + + if (AbsoluteTimeIsBefore(TimeQualGetSnapshotTime((TimeQual)qual), tuple->t_tmin)) { + return (false); + } + /* the tuple was inserted validly before the snapshot time */ + + if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax) || + !TransactionIdDidCommit((TransactionId)tuple->t_xmax)) { + + return (true); + } + + tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax); + } + + return ((bool) + AbsoluteTimeIsAfter(tuple->t_tmax, + TimeQualGetSnapshotTime((TimeQual)qual))); +} + +/* + * HeapTupleSatisfiesUpperBoundedInternalTimeQual -- + * True iff heap tuple is valid within a upper bounded time qualification. + * + * Note: + * Assumes heap tuple is valid. + * Assumes time qualification is valid ranged qualification with fixed + * upper bound. + */ +/* + * The satisfaction of [T1,T2] requires the following: + * + * (Xmin is committed && Tmin <= T2 && + * (Xmax is null || (Xmax is not committed && Xmax != my-transaction) || + * T1 is null || Tmax >= T1)) + */ +static bool +HeapTupleSatisfiesUpperBoundedInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual) +{ + /* + * XXX Several evil casts are made in this routine. Casting XID to be + * TransactionId works only because TransactionId->data is the first + * (and only) field of the structure. + */ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + + tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin); + } + + if (AbsoluteTimeIsBefore(TimeQualGetEndTime((TimeQual)qual), tuple->t_tmin)) { + return (false); + } + /* the tuple was inserted validly before the range end */ + + if (!AbsoluteTimeIsBackwardCompatiblyValid(TimeQualGetStartTime((TimeQual)qual))) { + return (true); + } + + if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax) || + !TransactionIdDidCommit((TransactionId)tuple->t_xmax)) { + + return (true); + } + + tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax); + } + + return ((bool)AbsoluteTimeIsAfter(tuple->t_tmax, + TimeQualGetStartTime((TimeQual)qual))); +} + +/* + * HeapTupleSatisfiesUpperUnboundedInternalTimeQual -- + * True iff heap tuple is valid within a upper bounded time qualification. + * + * Note: + * Assumes heap tuple is valid. + * Assumes time qualification is valid ranged qualification with no + * upper bound. + */ +/* + * The satisfaction of [T1,] requires the following: + * + * ((Xmin == my-transaction && Cmin != my-command && + * (Xmax is null || (Xmax == my-transaction && Cmax != my-command))) + * || + * + * (Xmin is committed && + * (Xmax is null || (Xmax == my-transaction && Cmax == my-command) || + * (Xmax is not committed && Xmax != my-transaction) || + * T1 is null || Tmax >= T1))) + */ +static bool +HeapTupleSatisfiesUpperUnboundedInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual) +{ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) && + CommandIdIsCurrentCommandId(tuple->t_cmin)) { + + return (false); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) && + !CommandIdIsCurrentCommandId(tuple->t_cmin)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + Assert(TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)); + + return ((bool) !CommandIdIsCurrentCommandId(tuple->t_cmax)); + } + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + + tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin); + } + /* the tuple was inserted validly */ + + if (!AbsoluteTimeIsBackwardCompatiblyValid(TimeQualGetStartTime((TimeQual)qual))) { + return (true); + } + + if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) { + return (CommandIdIsCurrentCommandId(tuple->t_cmin)); + } + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmax)) { + return (true); + } + + tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax); + } + + return ((bool)AbsoluteTimeIsAfter(tuple->t_tmax, + TimeQualGetStartTime((TimeQual)qual))); +} |