aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/time/tqual.c
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
committerMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
commitd31084e9d1118b25fd16580d9d8c2924b5740dff (patch)
tree3179e66307d54df9c7b966543550e601eb55e668 /src/backend/utils/time/tqual.c
downloadpostgresql-PG95-1_01.tar.gz
postgresql-PG95-1_01.zip
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/utils/time/tqual.c')
-rw-r--r--src/backend/utils/time/tqual.c815
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)));
+}