#include "storage/lmgr.h"
 #include "utils/memutils.h"
 #include "utils/snapmgr.h"
+#include "utils/tqual.h"
 
 
 PG_MODULE_MAGIC;
 
 #include "storage/procarray.h"
 #include "storage/smgr.h"
 #include "utils/rel.h"
+#include "utils/snapmgr.h"
+#include "utils/tqual.h"
 
 PG_MODULE_MAGIC;
 
 
 #include "utils/rel.h"
 #include "utils/sortsupport.h"
 #include "utils/tuplesort.h"
+#include "utils/tqual.h"
 
 
 /* Magic numbers for parallel state sharing */
 
 SnapBuildFreeSnapshot(Snapshot snap)
 {
    /* make sure we don't get passed an external snapshot */
-   Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+   Assert(snap->snapshot_type == SNAPSHOT_HISTORIC_MVCC);
 
    /* make sure nobody modified our snapshot */
    Assert(snap->curcid == FirstCommandId);
 SnapBuildSnapDecRefcount(Snapshot snap)
 {
    /* make sure we don't get passed an external snapshot */
-   Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC);
+   Assert(snap->snapshot_type == SNAPSHOT_HISTORIC_MVCC);
 
    /* make sure nobody modified our snapshot */
    Assert(snap->curcid == FirstCommandId);
 
    snapshot = MemoryContextAllocZero(builder->context, ssize);
 
-   snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC;
+   snapshot->snapshot_type = SNAPSHOT_HISTORIC_MVCC;
 
    /*
     * We misuse the original meaning of SnapshotData's xip and subxip fields
 
  * These SnapshotData structs are static to simplify memory allocation
  * (see the hack in GetSnapshotData to avoid repeated malloc/free).
  */
-static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
-static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
-SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CurrentSnapshotData = {SNAPSHOT_MVCC};
+static SnapshotData SecondarySnapshotData = {SNAPSHOT_MVCC};
+SnapshotData CatalogSnapshotData = {SNAPSHOT_MVCC};
 
 /* Pointers to valid snapshots */
 static Snapshot CurrentSnapshot = NULL;
    Size        size;
 
    Assert(snap != InvalidSnapshot);
-   Assert(snap->satisfies == HeapTupleSatisfiesMVCC);
+   Assert(snap->snapshot_type == SNAPSHOT_MVCC);
 
    /* We allocate any XID arrays needed in the same palloc block. */
    size = add_size(sizeof(SerializedSnapshotData),
 
    /* Copy all required fields */
    snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
-   snapshot->satisfies = HeapTupleSatisfiesMVCC;
+   snapshot->snapshot_type = SNAPSHOT_MVCC;
    snapshot->xmin = serialized_snapshot.xmin;
    snapshot->xmax = serialized_snapshot.xmax;
    snapshot->xip = NULL;
 
 
 
 /* Static variables representing various special snapshot semantics */
-SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
-SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
+SnapshotData SnapshotSelfData = {SNAPSHOT_SELF};
+SnapshotData SnapshotAnyData = {SNAPSHOT_ANY};
 
 
 /*
  * HeapTupleSatisfiesSelf
  *     True iff heap tuple is valid "for itself".
  *
- * Here, we consider the effects of:
- *     all committed transactions (as of the current instant)
- *     previous commands of this transaction
- *     changes made by the current command
+ * See SNAPSHOT_MVCC's definition for the intended behaviour.
  *
  * Note:
  *     Assumes heap tuple is valid.
  *         (Xmax != my-transaction &&          the row was deleted by another transaction
  *          Xmax is not committed)))           that has not been committed
  */
-bool
+static bool
 HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
 {
    HeapTupleHeader tuple = htup->t_data;
  * HeapTupleSatisfiesAny
  *     Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
  */
-bool
+static bool
 HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer)
 {
    return true;
  * HeapTupleSatisfiesToast
  *     True iff heap tuple is valid as a TOAST row.
  *
+ * See SNAPSHOT_TOAST's definition for the intended behaviour.
+ *
  * This is a simplified version that only checks for VACUUM moving conditions.
  * It's appropriate for TOAST usage because TOAST really doesn't want to do
  * its own time qual checks; if you can see the main table row that contains
  * Among other things, this means you can't do UPDATEs of rows in a TOAST
  * table.
  */
-bool
+static bool
 HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
                        Buffer buffer)
 {
  * HeapTupleSatisfiesDirty
  *     True iff heap tuple is valid including effects of open transactions.
  *
- * Here, we consider the effects of:
- *     all committed and in-progress transactions (as of the current instant)
- *     previous commands of this transaction
- *     changes made by the current command
+ * See SNAPSHOT_DIRTY's definition for the intended behaviour.
  *
  * This is essentially like HeapTupleSatisfiesSelf as far as effects of
  * the current transaction and committed/aborted xacts are concerned.
  * on the insertion without aborting the whole transaction, the associated
  * token is also returned in snapshot->speculativeToken.
  */
-bool
+static bool
 HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
                        Buffer buffer)
 {
  * HeapTupleSatisfiesMVCC
  *     True iff heap tuple is valid for the given MVCC snapshot.
  *
- * Here, we consider the effects of:
- *     all transactions committed as of the time of the given snapshot
- *     previous commands of this transaction
- *
- * Does _not_ include:
- *     transactions shown as in-progress by the snapshot
- *     transactions started after the snapshot was taken
- *     changes made by the current command
+ * See SNAPSHOT_MVCC's definition for the intended behaviour.
  *
  * Notice that here, we will not update the tuple status hint bits if the
  * inserting/deleting transaction is still running according to our snapshot,
  * inserting/deleting transaction was still running --- which was more cycles
  * and more contention on the PGXACT array.
  */
-bool
+static bool
 HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
                       Buffer buffer)
 {
  * True if tuple might be visible to some transaction; false if it's
  * surely dead to everyone, ie, vacuumable.
  *
- * This is an interface to HeapTupleSatisfiesVacuum that meets the
- * SnapshotSatisfiesFunc API, so it can be used through a Snapshot.
+ * See SNAPSHOT_TOAST's definition for the intended behaviour.
+ *
+ * This is an interface to HeapTupleSatisfiesVacuum that's callable via
+ * HeapTupleSatisfiesSnapshot, so it can be used through a Snapshot.
  * snapshot->xmin must have been set up with the xmin horizon to use.
  */
-bool
+static bool
 HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
                                Buffer buffer)
 {
  * dangerous to do so as the semantics of doing so during timetravel are more
  * complicated than when dealing "only" with the present.
  */
-bool
+static bool
 HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
                               Buffer buffer)
 {
    else
        return true;
 }
+
+/*
+ * HeapTupleSatisfiesVisibility
+ *     True iff heap tuple satisfies a time qual.
+ *
+ * Notes:
+ * Assumes heap tuple is valid, and buffer at least share locked.
+ *
+ * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
+ * if so, the indicated buffer is marked dirty.
+ */
+bool
+HeapTupleSatisfiesVisibility(HeapTuple tup, Snapshot snapshot, Buffer buffer)
+{
+   switch (snapshot->snapshot_type)
+   {
+       case SNAPSHOT_MVCC:
+           return HeapTupleSatisfiesMVCC(tup, snapshot, buffer);
+           break;
+       case SNAPSHOT_SELF:
+           return HeapTupleSatisfiesSelf(tup, snapshot, buffer);
+           break;
+       case SNAPSHOT_ANY:
+           return HeapTupleSatisfiesAny(tup, snapshot, buffer);
+           break;
+       case SNAPSHOT_TOAST:
+           return HeapTupleSatisfiesToast(tup, snapshot, buffer);
+           break;
+       case SNAPSHOT_DIRTY:
+           return HeapTupleSatisfiesDirty(tup, snapshot, buffer);
+           break;
+       case SNAPSHOT_HISTORIC_MVCC:
+           return HeapTupleSatisfiesHistoricMVCC(tup, snapshot, buffer);
+           break;
+       case SNAPSHOT_NON_VACUUMABLE:
+           return HeapTupleSatisfiesNonVacuumable(tup, snapshot, buffer);
+           break;
+   }
+
+   return false;               /* keep compiler quiet */
+}
 
 #include "storage/relfilenode.h"
 #include "utils/relcache.h"
 #include "utils/snapmgr.h"
-#include "utils/tqual.h"
 
 typedef void *Block;
 
 
    if (old_snapshot_threshold >= 0
        && (snapshot) != NULL
-       && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC
-           || (snapshot)->satisfies == HeapTupleSatisfiesToast)
+       && ((snapshot)->snapshot_type == SNAPSHOT_MVCC
+           || (snapshot)->snapshot_type == SNAPSHOT_TOAST)
        && !XLogRecPtrIsInvalid((snapshot)->lsn)
        && PageGetLSN(page) > (snapshot)->lsn)
        TestForOldSnapshot_impl(snapshot, relation);
 
 #include "storage/buf.h"
 
 
+/*
+ * The different snapshot types.  We use SnapshotData structures to represent
+ * both "regular" (MVCC) snapshots and "special" snapshots that have non-MVCC
+ * semantics.  The specific semantics of a snapshot are encoded by its type.
+ *
+ * The behaviour of each type of snapshot should be documented alongside its
+ * enum value, best in terms that are not specific to an individual table AM.
+ *
+ * The reason the snapshot type rather than a callback as it used to be is
+ * that that allows to use the same snapshot for different table AMs without
+ * having one callback per AM.
+ */
+typedef enum SnapshotType
+{
+   /*-------------------------------------------------------------------------
+    * A tuple is visible iff the tuple is valid for the given MVCC snapshot.
+    *
+    * Here, we consider the effects of:
+    * - all transactions committed as of the time of the given snapshot
+    * - previous commands of this transaction
+    *
+    * Does _not_ include:
+    * - transactions shown as in-progress by the snapshot
+    * - transactions started after the snapshot was taken
+    * - changes made by the current command
+    * -------------------------------------------------------------------------
+    */
+   SNAPSHOT_MVCC = 0,
+
+   /*-------------------------------------------------------------------------
+    * A tuple is visible iff the tuple is valid including effects of open
+    * transactions.
+    *
+    * Here, we consider the effects of:
+    * - all committed and in-progress transactions (as of the current instant)
+    * - previous commands of this transaction
+    * - changes made by the current command
+    * -------------------------------------------------------------------------
+    */
+   SNAPSHOT_SELF,
+
+   /*
+    * Any tuple is visible.
+    */
+   SNAPSHOT_ANY,
+
+   /*
+    * A tuple is visible iff the tuple tuple is valid as a TOAST row.
+    */
+   SNAPSHOT_TOAST,
+
+   /*-------------------------------------------------------------------------
+    * A tuple is visible iff the tuple is valid including effects of open
+    * transactions.
+    *
+    * Here, we consider the effects of:
+    * - all committed and in-progress transactions (as of the current instant)
+    * - previous commands of this transaction
+    * - changes made by the current command
+    * -------------------------------------------------------------------------
+    */
+   SNAPSHOT_DIRTY,
+
+   /*
+    * A tuple is visible iff it follows the rules of SNAPSHOT_MVCC, but
+    * supports being called in timetravel context (for decoding catalog
+    * contents in the context of logical decoding).
+    */
+   SNAPSHOT_HISTORIC_MVCC,
+
+   /*
+    * A tuple is visible iff the tuple might be visible to some transaction;
+    * false if it's surely dead to everyone, ie, vacuumable.
+    *
+    * Snapshot.xmin must have been set up with the xmin horizon to use.
+    */
+   SNAPSHOT_NON_VACUUMABLE
+} SnapshotType;
+
 typedef struct SnapshotData *Snapshot;
 
 #define InvalidSnapshot        ((Snapshot) NULL)
 
-/*
- * We use SnapshotData structures to represent both "regular" (MVCC)
- * snapshots and "special" snapshots that have non-MVCC semantics.
- * The specific semantics of a snapshot are encoded by the "satisfies"
- * function.
- */
-typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup,
-                                      Snapshot snapshot, Buffer buffer);
-
 /*
  * Struct representing all kind of possible snapshots.
  *
  */
 typedef struct SnapshotData
 {
-   SnapshotSatisfiesFunc satisfies;    /* tuple test function */
+   SnapshotType snapshot_type; /* type of snapshot */
 
    /*
     * The remaining fields are used only for MVCC snapshots, and are normally
 
 
 /* This macro encodes the knowledge of which snapshots are MVCC-safe */
 #define IsMVCCSnapshot(snapshot)  \
-   ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \
-    (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC)
+   ((snapshot)->snapshot_type == SNAPSHOT_MVCC || \
+    (snapshot)->snapshot_type == SNAPSHOT_HISTORIC_MVCC)
 
-/*
- * HeapTupleSatisfiesVisibility
- *     True iff heap tuple satisfies a time qual.
- *
- * Notes:
- * Assumes heap tuple is valid.
- * Beware of multiple evaluations of snapshot argument.
- * Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
- * if so, the indicated buffer is marked dirty.
- */
-#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
-   ((*(snapshot)->satisfies) (tuple, snapshot, buffer))
+extern bool HeapTupleSatisfiesVisibility(HeapTuple stup, Snapshot snapshot,
+                            Buffer buffer);
 
 /* Result codes for HeapTupleSatisfiesVacuum */
 typedef enum
    HEAPTUPLE_DELETE_IN_PROGRESS    /* deleting xact is still in progress */
 } HTSV_Result;
 
-/* These are the "satisfies" test routines for the various snapshot types */
-extern bool HeapTupleSatisfiesMVCC(HeapTuple htup,
-                      Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesSelf(HeapTuple htup,
-                      Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesAny(HeapTuple htup,
-                     Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTuple htup,
-                       Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
-                       Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
-                               Snapshot snapshot, Buffer buffer);
-extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
-                              Snapshot snapshot, Buffer buffer);
-
 /* Special "satisfies" routines with different APIs */
 extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
                         CommandId curcid, Buffer buffer);
  * local variable of type SnapshotData, and initialize it with this macro.
  */
 #define InitDirtySnapshot(snapshotdata)  \
-   ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
+   ((snapshotdata).snapshot_type = SNAPSHOT_DIRTY)
 
 /*
  * Similarly, some initialization is required for a NonVacuumable snapshot.
  * The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin).
  */
 #define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon)  \
-   ((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \
+   ((snapshotdata).snapshot_type = SNAPSHOT_NON_VACUUMABLE, \
     (snapshotdata).xmin = (xmin_horizon))
 
 /*
  * to set lsn and whenTaken correctly to support snapshot_too_old.
  */
 #define InitToastSnapshot(snapshotdata, l, w)  \
-   ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \
+   ((snapshotdata).snapshot_type = SNAPSHOT_TOAST, \
     (snapshotdata).lsn = (l),                  \
     (snapshotdata).whenTaken = (w))
 
 
 SnapBuildState
 Snapshot
 SnapshotData
-SnapshotSatisfiesFunc
+SnapshotType
 SockAddr
 Sort
 SortBy