816 lines
20 KiB
C
816 lines
20 KiB
C
|
/*-------------------------------------------------------------------------
|
||
|
*
|
||
|
* 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)));
|
||
|
}
|