Get rid of ReferentialIntegritySnapshotOverride by extending Executor API
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 25 Sep 2003 18:58:36 +0000 (18:58 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 25 Sep 2003 18:58:36 +0000 (18:58 +0000)
to allow es_snapshot to be set to SnapshotNow rather than a query snapshot.
This solves a bug reported by Wade Klaver, wherein triggers fired as a
result of RI cascade updates could misbehave.

16 files changed:
src/backend/commands/explain.c
src/backend/commands/trigger.c
src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/backend/executor/functions.c
src/backend/executor/nodeSubplan.c
src/backend/executor/nodeSubqueryscan.c
src/backend/executor/spi.c
src/backend/tcop/pquery.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/time/tqual.c
src/include/access/valid.h
src/include/executor/executor.h
src/include/executor/spi.h
src/include/nodes/execnodes.h
src/include/utils/tqual.h

index d1305fde31ee6ea34c6deb69d6a57ca966dc5748..79e1baf318ecccde54364f6b39e965a08e247c53 100644 (file)
@@ -207,7 +207,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
        gettimeofday(&starttime, NULL);
 
        /* call ExecutorStart to prepare the plan for execution */
-       ExecutorStart(queryDesc, !stmt->analyze);
+       ExecutorStart(queryDesc, false, !stmt->analyze);
 
        /* Execute the plan for statistics if asked for */
        if (stmt->analyze)
index dba53cc937723adf69509d6f5e7eed92a662c599..0394a934e2e931fc160d9804f841467e8ec9765c 100644 (file)
@@ -1862,12 +1862,6 @@ DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
        if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple)
                heap_freetuple(rettuple);
 
-       /*
-        * Might have been a referential integrity constraint trigger. Reset
-        * the snapshot overriding flag.
-        */
-       ReferentialIntegritySnapshotOverride = false;
-
        /*
         * Release buffers
         */
index 8a9c5afb4a3af0dae40c14bd6d993bce3e9b954d..655a2e1fbb173ce2dcd02476c003ed34011f0110 100644 (file)
@@ -104,6 +104,9 @@ static void EvalPlanQualStop(evalPlanQual *epq);
  * field of the QueryDesc is filled in to describe the tuples that will be
  * returned, and the internal fields (estate and planstate) are set up.
  *
+ * If useSnapshotNow is true, run the query with SnapshotNow time qual rules
+ * instead of the normal use of QuerySnapshot.
+ *
  * If explainOnly is true, we are not actually intending to run the plan,
  * only to set up for EXPLAIN; so skip unwanted side-effects.
  *
@@ -112,7 +115,7 @@ static void EvalPlanQualStop(evalPlanQual *epq);
  * ----------------------------------------------------------------
  */
 void
-ExecutorStart(QueryDesc *queryDesc, bool explainOnly)
+ExecutorStart(QueryDesc *queryDesc, bool useSnapshotNow, bool explainOnly)
 {
        EState     *estate;
        MemoryContext oldcontext;
@@ -154,7 +157,16 @@ ExecutorStart(QueryDesc *queryDesc, bool explainOnly)
         * the life of this query, even if it outlives the current command and
         * current snapshot.
         */
-       estate->es_snapshot = CopyQuerySnapshot();
+       if (useSnapshotNow)
+       {
+               estate->es_snapshot = SnapshotNow;
+               estate->es_snapshot_cid = GetCurrentCommandId();
+       }
+       else
+       {
+               estate->es_snapshot = CopyQuerySnapshot();
+               estate->es_snapshot_cid = estate->es_snapshot->curcid;
+       }
 
        /*
         * Initialize the plan state tree
@@ -1106,7 +1118,7 @@ lnext:    ;
 
                                        tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
                                        test = heap_mark4update(erm->relation, &tuple, &buffer,
-                                                                                       estate->es_snapshot->curcid);
+                                                                                       estate->es_snapshot_cid);
                                        ReleaseBuffer(buffer);
                                        switch (test)
                                        {
@@ -1266,7 +1278,7 @@ ExecSelect(TupleTableSlot *slot,
        if (estate->es_into_relation_descriptor != NULL)
        {
                heap_insert(estate->es_into_relation_descriptor, tuple,
-                                       estate->es_snapshot->curcid);
+                                       estate->es_snapshot_cid);
                IncrAppended();
        }
 
@@ -1342,7 +1354,7 @@ ExecInsert(TupleTableSlot *slot,
         * insert the tuple
         */
        newId = heap_insert(resultRelationDesc, tuple,
-                                               estate->es_snapshot->curcid);
+                                               estate->es_snapshot_cid);
 
        IncrAppended();
        (estate->es_processed)++;
@@ -1394,7 +1406,7 @@ ExecDelete(TupleTableSlot *slot,
                bool            dodelete;
 
                dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid,
-                                                                               estate->es_snapshot->curcid);
+                                                                               estate->es_snapshot_cid);
 
                if (!dodelete)                  /* "do nothing" */
                        return;
@@ -1406,7 +1418,7 @@ ExecDelete(TupleTableSlot *slot,
 ldelete:;
        result = heap_delete(resultRelationDesc, tupleid,
                                                 &ctid,
-                                                estate->es_snapshot->curcid,
+                                                estate->es_snapshot_cid,
                                                 true /* wait for commit */);
        switch (result)
        {
@@ -1505,7 +1517,7 @@ ExecUpdate(TupleTableSlot *slot,
 
                newtuple = ExecBRUpdateTriggers(estate, resultRelInfo,
                                                                                tupleid, tuple,
-                                                                               estate->es_snapshot->curcid);
+                                                                               estate->es_snapshot_cid);
 
                if (newtuple == NULL)   /* "do nothing" */
                        return;
@@ -1541,7 +1553,7 @@ lreplace:;
         */
        result = heap_update(resultRelationDesc, tupleid, tuple,
                                                 &ctid,
-                                                estate->es_snapshot->curcid,
+                                                estate->es_snapshot_cid,
                                                 true /* wait for commit */);
        switch (result)
        {
@@ -2027,6 +2039,7 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
         */
        epqstate->es_direction = ForwardScanDirection;
        epqstate->es_snapshot = estate->es_snapshot;
+       epqstate->es_snapshot_cid = estate->es_snapshot_cid;
        epqstate->es_range_table = estate->es_range_table;
        epqstate->es_result_relations = estate->es_result_relations;
        epqstate->es_num_result_relations = estate->es_num_result_relations;
index d06fd9f3501a72e4b349254518809721b50bdd58..97df06ea0141e30228af17f7beb299fd4bbbf924 100644 (file)
@@ -178,6 +178,7 @@ CreateExecutorState(void)
         */
        estate->es_direction = ForwardScanDirection;
        estate->es_snapshot = SnapshotNow;
+       estate->es_snapshot_cid = FirstCommandId;
        estate->es_range_table = NIL;
 
        estate->es_result_relations = NULL;
index 1d37be83777f0bb3b658dafdcdf887d58f316e55..64e997b32efc5549d468bc5ffdd7a109ea45cf7e 100644 (file)
@@ -291,7 +291,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
 
        /* Utility commands don't need Executor. */
        if (es->qd->operation != CMD_UTILITY)
-               ExecutorStart(es->qd, false);
+               ExecutorStart(es->qd, false, false);
 
        es->status = F_EXEC_RUN;
 }
index f5903d302aaaeba708ba215ceae01d5a8a49ca92..4d571d738cc3b9ee6873387578a9dcba4ba78be5 100644 (file)
@@ -709,6 +709,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
        sp_estate->es_tupleTable =
                ExecCreateTupleTable(ExecCountSlotsNode(subplan->plan) + 10);
        sp_estate->es_snapshot = estate->es_snapshot;
+       sp_estate->es_snapshot_cid = estate->es_snapshot_cid;
        sp_estate->es_instrument = estate->es_instrument;
 
        /*
index f2767d3cba42abb844a6133043d0c596f05380af..64c561dd7112650c2f113744a71038142aa16d9f 100644 (file)
@@ -177,6 +177,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate)
        sp_estate->es_tupleTable =
                ExecCreateTupleTable(ExecCountSlotsNode(node->subplan) + 10);
        sp_estate->es_snapshot = estate->es_snapshot;
+       sp_estate->es_snapshot_cid = estate->es_snapshot_cid;
        sp_estate->es_instrument = estate->es_instrument;
 
        /*
index 135a848aa9a584de61849fbbdf5c015d718d7d91..2aea3423c79ae93b62850b4930465b42dab736d0 100644 (file)
@@ -32,10 +32,12 @@ static int  _SPI_connected = -1;
 static int     _SPI_curid = -1;
 
 static int     _SPI_execute(const char *src, int tcount, _SPI_plan *plan);
-static int     _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount);
+static int     _SPI_pquery(QueryDesc *queryDesc, bool runit,
+                                               bool useSnapshotNow, int tcount);
 
 static int _SPI_execute_plan(_SPI_plan *plan,
-                                 Datum *Values, const char *Nulls, int tcount);
+                                                        Datum *Values, const char *Nulls,
+                                                        bool useSnapshotNow, int tcount);
 
 static void _SPI_cursor_operation(Portal portal, bool forward, int count,
                                          DestReceiver *dest);
@@ -236,7 +238,33 @@ SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
        if (res < 0)
                return res;
 
-       res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
+       res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, false, tcount);
+
+       _SPI_end_call(true);
+       return res;
+}
+
+/*
+ * SPI_execp_now -- identical to SPI_execp, except that we use SnapshotNow
+ * instead of the normal QuerySnapshot.  This is currently not documented
+ * in spi.sgml because it is only intended for use by RI triggers.
+ */
+int
+SPI_execp_now(void *plan, Datum *Values, const char *Nulls, int tcount)
+{
+       int                     res;
+
+       if (plan == NULL || tcount < 0)
+               return SPI_ERROR_ARGUMENT;
+
+       if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
+               return SPI_ERROR_PARAM;
+
+       res = _SPI_begin_call(true);
+       if (res < 0)
+               return res;
+
+       res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, true, tcount);
 
        _SPI_end_call(true);
        return res;
@@ -1068,7 +1096,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
                        {
                                qdesc = CreateQueryDesc(queryTree, planTree, dest,
                                                                                NULL, false);
-                               res = _SPI_pquery(qdesc, true,
+                               res = _SPI_pquery(qdesc, true, false,
                                                                  queryTree->canSetTag ? tcount : 0);
                                if (res < 0)
                                        return res;
@@ -1078,7 +1106,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
                        {
                                qdesc = CreateQueryDesc(queryTree, planTree, dest,
                                                                                NULL, false);
-                               res = _SPI_pquery(qdesc, false, 0);
+                               res = _SPI_pquery(qdesc, false, false, 0);
                                if (res < 0)
                                        return res;
                        }
@@ -1096,7 +1124,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
 
 static int
 _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
-                                 int tcount)
+                                 bool useSnapshotNow, int tcount)
 {
        List       *query_list_list = plan->qtlist;
        List       *plan_list = plan->ptlist;
@@ -1167,7 +1195,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
                        {
                                qdesc = CreateQueryDesc(queryTree, planTree, dest,
                                                                                paramLI, false);
-                               res = _SPI_pquery(qdesc, true,
+                               res = _SPI_pquery(qdesc, true, useSnapshotNow,
                                                                  queryTree->canSetTag ? tcount : 0);
                                if (res < 0)
                                        return res;
@@ -1180,7 +1208,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
 }
 
 static int
-_SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
+_SPI_pquery(QueryDesc *queryDesc, bool runit, bool useSnapshotNow, int tcount)
 {
        int                     operation = queryDesc->operation;
        int                     res;
@@ -1217,7 +1245,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
                ResetUsage();
 #endif
 
-       ExecutorStart(queryDesc, false);
+       ExecutorStart(queryDesc, useSnapshotNow, false);
 
        ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
 
index 29eafe39a585df1c45247aedad53558f1cc43b61..51fd65b9eeff6c4b1a2758c94562200bf39db6d8 100644 (file)
@@ -131,7 +131,7 @@ ProcessQuery(Query *parsetree,
        /*
         * Call ExecStart to prepare the plan for execution
         */
-       ExecutorStart(queryDesc, false);
+       ExecutorStart(queryDesc, false, false);
 
        /*
         * Run the plan to completion.
@@ -269,7 +269,7 @@ PortalStart(Portal portal, ParamListInfo params)
                        /*
                         * Call ExecStart to prepare the plan for execution
                         */
-                       ExecutorStart(queryDesc, false);
+                       ExecutorStart(queryDesc, false, false);
 
                        /*
                         * This tells PortalCleanup to shut down the executor
index 3a70991d6e329a5070c809cb1e43cd608e93bf48..15931d2b277f2f1f5cf3eea4f58c00510b01e7e0 100644 (file)
@@ -187,8 +187,6 @@ RI_FKey_check(PG_FUNCTION_ARGS)
        int                     i;
        int                     match_type;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -627,8 +625,6 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
        int                     i;
        int                     match_type;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -807,8 +803,6 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
        int                     i;
        int                     match_type;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -995,8 +989,6 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
        void       *qplan;
        int                     i;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -1159,8 +1151,6 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
        int                     i;
        int                     j;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -1349,8 +1339,6 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
        void       *qplan;
        int                     i;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -1520,8 +1508,6 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
        void       *qplan;
        int                     i;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -1694,8 +1680,6 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
        void       *qplan;
        int                     i;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -1868,8 +1852,6 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
        int                     match_type;
        bool            use_cached_query;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -2083,8 +2065,6 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
        RI_QueryKey qkey;
        void       *qplan;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -2296,8 +2276,6 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
        void       *qplan;
        int                     match_type;
 
-       ReferentialIntegritySnapshotOverride = true;
-
        /*
         * Check that this is a valid trigger call on the right time and
         * event.
@@ -2936,15 +2914,19 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan,
         */
        limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
 
-       /* Run the plan */
-       spi_result = SPI_execp(qplan, vals, nulls, limit);
+       /*
+        * Run the plan, using SnapshotNow time qual rules so that we can see
+        * all committed tuples, even those committed after our own transaction
+        * or query started.
+        */
+       spi_result = SPI_execp_now(qplan, vals, nulls, limit);
 
        /* Restore UID */
        SetUserId(save_uid);
 
        /* Check result */
        if (spi_result < 0)
-               elog(ERROR, "SPI_execp failed");
+               elog(ERROR, "SPI_execp_now returned %d", spi_result);
 
        if (expect_OK >= 0 && spi_result != expect_OK)
                ri_ReportViolation(qkey, constrname ? constrname : "",
index 2c055acd0ec8b5c25e18268b3e5474a5ef8a2b5a..3ffb62aaeaeabf1d969d10e6d993299f57eba00c 100644 (file)
@@ -39,8 +39,6 @@ Snapshot      SerializableSnapshot = NULL;
 TransactionId RecentXmin = InvalidTransactionId;
 TransactionId RecentGlobalXmin = InvalidTransactionId;
 
-bool           ReferentialIntegritySnapshotOverride = false;
-
 
 /*
  * HeapTupleSatisfiesItself
@@ -665,10 +663,6 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 bool
 HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 {
-       /* XXX this is horribly ugly: */
-       if (ReferentialIntegritySnapshotOverride)
-               return HeapTupleSatisfiesNow(tuple);
-
        if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
        {
                if (tuple->t_infomask & HEAP_XMIN_INVALID)
@@ -978,9 +972,6 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 void
 SetQuerySnapshot(void)
 {
-       /* Initialize snapshot overriding to false */
-       ReferentialIntegritySnapshotOverride = false;
-
        /* 1st call in xaction? */
        if (SerializableSnapshot == NULL)
        {
index 01cacee1e12cbb1fd87074291d7da94f6864c62e..57d5a367884dd3a71b75f4f24d04f6087876bbe5 100644 (file)
@@ -93,7 +93,7 @@ do \
                                                   relation, \
                                                   buffer, \
                                                   disk_page, \
-                                                  seeself, \
+                                                  snapshot, \
                                                   nKeys, \
                                                   key, \
                                                   res) \
@@ -112,7 +112,7 @@ do \
                { \
                        uint16  _infomask = (tuple)->t_data->t_infomask; \
                        \
-                       (res) = HeapTupleSatisfiesVisibility((tuple), (seeself)); \
+                       (res) = HeapTupleSatisfiesVisibility((tuple), (snapshot)); \
                        if ((tuple)->t_data->t_infomask != _infomask) \
                                SetBufferCommitInfoNeedsSave(buffer); \
                } \
index 1aff650472c8c61d882831fd04433769c280104c..d119da9e550fc76100424b48d7eadf0e1a82be35 100644 (file)
@@ -85,7 +85,8 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
 /*
  * prototypes from functions in execMain.c
  */
-extern void ExecutorStart(QueryDesc *queryDesc, bool explainOnly);
+extern void ExecutorStart(QueryDesc *queryDesc, bool useSnapshotNow,
+                                                 bool explainOnly);
 extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
                        ScanDirection direction, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc);
index 2fb5b9f55c73886714ca9f744afebe2526d1f075..aa94d53fa8ad83b62c9c47ffd6668d098b7854b7 100644 (file)
@@ -84,6 +84,8 @@ extern void SPI_pop(void);
 extern int     SPI_exec(const char *src, int tcount);
 extern int SPI_execp(void *plan, Datum *values, const char *Nulls,
                  int tcount);
+extern int SPI_execp_now(void *plan, Datum *values, const char *Nulls,
+                 int tcount);
 extern void *SPI_prepare(const char *src, int nargs, Oid *argtypes);
 extern void *SPI_saveplan(void *plan);
 extern int     SPI_freeplan(void *plan);
index 6a5dabd053f89dc1136f54daad6bc58ad52a75a1..534e5050677a40da9c9479953d506daf864ad00a 100644 (file)
@@ -286,6 +286,7 @@ typedef struct EState
        /* Basic state for all query types: */
        ScanDirection es_direction; /* current scan direction */
        Snapshot        es_snapshot;    /* time qual to use */
+       CommandId       es_snapshot_cid;        /* CommandId component of time qual */
        List       *es_range_table; /* List of RangeTableEntrys */
 
        /* Info about target table for insert/update/delete queries: */
index 77f118bc27c10d0afc49a930555f2802e4ae935c..3d96894738e06fde89cff461eeaa21ceac7a68fa 100644 (file)
@@ -44,14 +44,6 @@ extern DLLIMPORT Snapshot SerializableSnapshot;
 extern TransactionId RecentXmin;
 extern TransactionId RecentGlobalXmin;
 
-extern bool ReferentialIntegritySnapshotOverride;
-
-#define IsSnapshotNow(snapshot)                ((Snapshot) (snapshot) == SnapshotNow)
-#define IsSnapshotSelf(snapshot)       ((Snapshot) (snapshot) == SnapshotSelf)
-#define IsSnapshotAny(snapshot)                ((Snapshot) (snapshot) == SnapshotAny)
-#define IsSnapshotToast(snapshot)      ((Snapshot) (snapshot) == SnapshotToast)
-#define IsSnapshotDirty(snapshot)      ((Snapshot) (snapshot) == SnapshotDirty)
-
 
 /*
  * HeapTupleSatisfiesVisibility
@@ -62,19 +54,19 @@ extern bool ReferentialIntegritySnapshotOverride;
  *             Beware of multiple evaluations of snapshot argument.
  */
 #define HeapTupleSatisfiesVisibility(tuple, snapshot) \
-(IsSnapshotNow(snapshot) ? \
+((snapshot) == SnapshotNow ? \
        HeapTupleSatisfiesNow((tuple)->t_data) \
 : \
-       (IsSnapshotSelf(snapshot) ? \
+       ((snapshot) == SnapshotSelf ? \
                HeapTupleSatisfiesItself((tuple)->t_data) \
        : \
-               (IsSnapshotAny(snapshot) ? \
+               ((snapshot) == SnapshotAny ? \
                        true \
                : \
-                       (IsSnapshotToast(snapshot) ? \
+                       ((snapshot) == SnapshotToast ? \
                                HeapTupleSatisfiesToast((tuple)->t_data) \
                        : \
-                               (IsSnapshotDirty(snapshot) ? \
+                               ((snapshot) == SnapshotDirty ? \
                                        HeapTupleSatisfiesDirty((tuple)->t_data) \
                                : \
                                        HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \