if (!RelationUsesLocalBuffers(scan->rs_base.rs_rd) &&
                scan->rs_nblocks > NBuffers / 4)
        {
-               allow_strat = scan->rs_base.rs_allow_strat;
-               allow_sync = scan->rs_base.rs_allow_sync;
+               allow_strat = (scan->rs_base.rs_flags & SO_ALLOW_STRAT) != 0;
+               allow_sync = (scan->rs_base.rs_flags & SO_ALLOW_SYNC) != 0;
        }
        else
                allow_strat = allow_sync = false;
        if (scan->rs_base.rs_parallel != NULL)
        {
                /* For parallel scan, believe whatever ParallelTableScanDesc says. */
-               scan->rs_base.rs_syncscan = scan->rs_base.rs_parallel->phs_syncscan;
+               if (scan->rs_base.rs_parallel->phs_syncscan)
+                       scan->rs_base.rs_flags |= SO_ALLOW_SYNC;
+               else
+                       scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
        }
        else if (keep_startblock)
        {
                 * so that rewinding a cursor doesn't generate surprising results.
                 * Reset the active syncscan setting, though.
                 */
-               scan->rs_base.rs_syncscan = (allow_sync && synchronize_seqscans);
+               if (allow_sync && synchronize_seqscans)
+                       scan->rs_base.rs_flags |= SO_ALLOW_SYNC;
+               else
+                       scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
        }
        else if (allow_sync && synchronize_seqscans)
        {
-               scan->rs_base.rs_syncscan = true;
+               scan->rs_base.rs_flags |= SO_ALLOW_SYNC;
                scan->rs_startblock = ss_get_location(scan->rs_base.rs_rd, scan->rs_nblocks);
        }
        else
        {
-               scan->rs_base.rs_syncscan = false;
+               scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
                scan->rs_startblock = 0;
        }
 
                memcpy(scan->rs_base.rs_key, key, scan->rs_base.rs_nkeys * sizeof(ScanKeyData));
 
        /*
-        * Currently, we don't have a stats counter for bitmap heap scans (but the
-        * underlying bitmap index scans will be counted) or sample scans (we only
-        * update stats for tuple fetches there)
+        * Currently, we only have a stats counter for sequential heap scans (but
+        * e.g for bitmap scans the underlying bitmap index scans will be counted,
+        * and for sample scans we update stats for tuple fetches).
         */
-       if (!scan->rs_base.rs_bitmapscan && !scan->rs_base.rs_samplescan)
+       if (scan->rs_base.rs_flags & SO_TYPE_SEQSCAN)
                pgstat_count_heap_scan(scan->rs_base.rs_rd);
 }
 
        HeapScanDesc scan = (HeapScanDesc) sscan;
 
        Assert(!scan->rs_inited);       /* else too late to change */
-       Assert(!scan->rs_base.rs_syncscan); /* else rs_startblock is significant */
+       /* else rs_startblock is significant */
+       Assert(!(scan->rs_base.rs_flags & SO_ALLOW_SYNC));
 
        /* Check startBlk is valid (but allow case of zero blocks...) */
        Assert(startBlk == 0 || startBlk < scan->rs_nblocks);
                                                                           RBM_NORMAL, scan->rs_strategy);
        scan->rs_cblock = page;
 
-       if (!scan->rs_base.rs_pageatatime)
+       if (!(scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE))
                return;
 
        buffer = scan->rs_cbuf;
                         * time, and much more likely that we'll just bollix things for
                         * forward scanners.
                         */
-                       scan->rs_base.rs_syncscan = false;
+                       scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
                        /* start from last page of the scan */
                        if (scan->rs_startblock > 0)
                                page = scan->rs_startblock - 1;
                         * a little bit backwards on every invocation, which is confusing.
                         * We don't guarantee any specific ordering in general, though.
                         */
-                       if (scan->rs_base.rs_syncscan)
+                       if (scan->rs_base.rs_flags & SO_ALLOW_SYNC)
                                ss_report_location(scan->rs_base.rs_rd, page);
                }
 
                         * time, and much more likely that we'll just bollix things for
                         * forward scanners.
                         */
-                       scan->rs_base.rs_syncscan = false;
+                       scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
                        /* start from last page of the scan */
                        if (scan->rs_startblock > 0)
                                page = scan->rs_startblock - 1;
                         * a little bit backwards on every invocation, which is confusing.
                         * We don't guarantee any specific ordering in general, though.
                         */
-                       if (scan->rs_base.rs_syncscan)
+                       if (scan->rs_base.rs_flags & SO_ALLOW_SYNC)
                                ss_report_location(scan->rs_base.rs_rd, page);
                }
 
 heap_beginscan(Relation relation, Snapshot snapshot,
                           int nkeys, ScanKey key,
                           ParallelTableScanDesc parallel_scan,
-                          bool allow_strat,
-                          bool allow_sync,
-                          bool allow_pagemode,
-                          bool is_bitmapscan,
-                          bool is_samplescan,
-                          bool temp_snap)
+                          uint32 flags)
 {
        HeapScanDesc scan;
 
        scan->rs_base.rs_rd = relation;
        scan->rs_base.rs_snapshot = snapshot;
        scan->rs_base.rs_nkeys = nkeys;
-       scan->rs_base.rs_bitmapscan = is_bitmapscan;
-       scan->rs_base.rs_samplescan = is_samplescan;
-       scan->rs_strategy = NULL;       /* set in initscan */
-       scan->rs_base.rs_allow_strat = allow_strat;
-       scan->rs_base.rs_allow_sync = allow_sync;
-       scan->rs_base.rs_temp_snap = temp_snap;
+       scan->rs_base.rs_flags = flags;
        scan->rs_base.rs_parallel = parallel_scan;
+       scan->rs_strategy = NULL;       /* set in initscan */
 
        /*
-        * we can use page-at-a-time mode if it's an MVCC-safe snapshot
+        * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
         */
-       scan->rs_base.rs_pageatatime =
-               allow_pagemode && snapshot && IsMVCCSnapshot(snapshot);
+       if (!(snapshot && IsMVCCSnapshot(snapshot)))
+               scan->rs_base.rs_flags &= ~SO_ALLOW_PAGEMODE;
 
        /*
-        * For a seqscan in a serializable transaction, acquire a predicate lock
-        * on the entire relation. This is required not only to lock all the
-        * matching tuples, but also to conflict with new insertions into the
-        * table. In an indexscan, we take page locks on the index pages covering
-        * the range specified in the scan qual, but in a heap scan there is
-        * nothing more fine-grained to lock. A bitmap scan is a different story,
-        * there we have already scanned the index and locked the index pages
-        * covering the predicate. But in that case we still have to lock any
-        * matching heap tuples.
+        * For seqscan and sample scans in a serializable transaction, acquire a
+        * predicate lock on the entire relation. This is required not only to
+        * lock all the matching tuples, but also to conflict with new insertions
+        * into the table. In an indexscan, we take page locks on the index pages
+        * covering the range specified in the scan qual, but in a heap scan there
+        * is nothing more fine-grained to lock. A bitmap scan is a different
+        * story, there we have already scanned the index and locked the index
+        * pages covering the predicate. But in that case we still have to lock
+        * any matching heap tuples. For sample scan we could optimize the locking
+        * to be at least page-level granularity, but we'd need to add per-tuple
+        * locking for that.
         */
-       if (!is_bitmapscan)
+       if (scan->rs_base.rs_flags & (SO_TYPE_SEQSCAN | SO_TYPE_SAMPLESCAN))
+       {
+               /*
+                * Ensure a missing snapshot is noticed reliably, even if the
+                * isolation mode means predicate locking isn't performed (and
+                * therefore the snapshot isn't used here).
+                */
+               Assert(snapshot);
                PredicateLockRelation(relation, snapshot);
+       }
 
        /* we only need to set this up once */
        scan->rs_ctup.t_tableOid = RelationGetRelid(relation);
 
        if (set_params)
        {
-               scan->rs_base.rs_allow_strat = allow_strat;
-               scan->rs_base.rs_allow_sync = allow_sync;
-               scan->rs_base.rs_pageatatime =
-                       allow_pagemode && IsMVCCSnapshot(scan->rs_base.rs_snapshot);
+               if (allow_strat)
+                       scan->rs_base.rs_flags |= SO_ALLOW_STRAT;
+               else
+                       scan->rs_base.rs_flags &= ~SO_ALLOW_STRAT;
+
+               if (allow_sync)
+                       scan->rs_base.rs_flags |= SO_ALLOW_SYNC;
+               else
+                       scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
+
+               if (allow_pagemode && scan->rs_base.rs_snapshot &&
+                       IsMVCCSnapshot(scan->rs_base.rs_snapshot))
+                       scan->rs_base.rs_flags |= SO_ALLOW_PAGEMODE;
+               else
+                       scan->rs_base.rs_flags &= ~SO_ALLOW_PAGEMODE;
        }
 
        /*
        if (scan->rs_strategy != NULL)
                FreeAccessStrategy(scan->rs_strategy);
 
-       if (scan->rs_base.rs_temp_snap)
+       if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
                UnregisterSnapshot(scan->rs_base.rs_snapshot);
 
        pfree(scan);
 
        HEAPDEBUG_1;                            /* heap_getnext( info ) */
 
-       if (scan->rs_base.rs_pageatatime)
+       if (scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
                heapgettup_pagemode(scan, direction,
                                                        scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
        else
 
        HEAPAMSLOTDEBUG_1;                      /* heap_getnextslot( info ) */
 
-       if (scan->rs_base.rs_pageatatime)
-               heapgettup_pagemode(scan, direction,
-                                                       scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
+       if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
+               heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
        else
-               heapgettup(scan, direction, scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
+               heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
 
        if (scan->rs_ctup.t_data == NULL)
        {
 
                         * a little bit backwards on every invocation, which is confusing.
                         * We don't guarantee any specific ordering in general, though.
                         */
-                       if (scan->rs_syncscan)
+                       if (scan->rs_flags & SO_ALLOW_SYNC)
                                ss_report_location(scan->rs_rd, blockno);
 
                        if (blockno == hscan->rs_startblock)
        HeapScanDesc hscan = (HeapScanDesc) scan;
        TsmRoutine *tsm = scanstate->tsmroutine;
        BlockNumber blockno = hscan->rs_cblock;
-       bool            pagemode = scan->rs_pageatatime;
+       bool            pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
 
        Page            page;
        bool            all_visible;
 {
        HeapScanDesc hscan = (HeapScanDesc) scan;
 
-       if (scan->rs_pageatatime)
+       if (scan->rs_flags & SO_ALLOW_PAGEMODE)
        {
                /*
                 * In pageatatime mode, heapgetpage() already did visibility checks,
 
 TableScanDesc
 table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
 {
+       uint32          flags = SO_TYPE_SEQSCAN |
+               SO_ALLOW_STRAT | SO_ALLOW_SYNC | SO_ALLOW_PAGEMODE | SO_TEMP_SNAPSHOT;
        Oid                     relid = RelationGetRelid(relation);
        Snapshot        snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
 
        return relation->rd_tableam->scan_begin(relation, snapshot, nkeys, key,
-                                                                                       NULL, true, true, true, false,
-                                                                                       false, true);
+                                                                                       NULL, flags);
 }
 
 void
 
        RegisterSnapshot(snapshot);
        scan->rs_snapshot = snapshot;
-       scan->rs_temp_snap = true;
+       scan->rs_flags |= SO_TEMP_SNAPSHOT;
 }
 
 
 table_beginscan_parallel(Relation relation, ParallelTableScanDesc parallel_scan)
 {
        Snapshot        snapshot;
+       uint32          flags = SO_TYPE_SEQSCAN |
+               SO_ALLOW_STRAT | SO_ALLOW_SYNC | SO_ALLOW_PAGEMODE;
 
        Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
 
                snapshot = RestoreSnapshot((char *) parallel_scan +
                                                                   parallel_scan->phs_snapshot_off);
                RegisterSnapshot(snapshot);
+               flags |= SO_TEMP_SNAPSHOT;
        }
        else
        {
        }
 
        return relation->rd_tableam->scan_begin(relation, snapshot, 0, NULL,
-                                                                                       parallel_scan, true, true, true,
-                                                                                       false, false,
-                                                                                       !parallel_scan->phs_snapshot_any);
+                                                                                       parallel_scan, flags);
 }
 
 
 
 extern TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
                           int nkeys, ScanKey key,
                           ParallelTableScanDesc parallel_scan,
-                          bool allow_strat,
-                          bool allow_sync,
-                          bool allow_pagemode,
-                          bool is_bitmapscan,
-                          bool is_samplescan,
-                          bool temp_snap);
+                          uint32 flags);
 extern void heap_setscanlimits(TableScanDesc scan, BlockNumber startBlk,
                                   BlockNumber endBlk);
 extern void heapgetpage(TableScanDesc scan, BlockNumber page);
 
        struct SnapshotData *rs_snapshot;       /* snapshot to see */
        int                     rs_nkeys;               /* number of scan keys */
        struct ScanKeyData *rs_key; /* array of scan key descriptors */
-       bool            rs_bitmapscan;  /* true if this is really a bitmap scan */
-       bool            rs_samplescan;  /* true if this is really a sample scan */
-       bool            rs_pageatatime; /* verify visibility page-at-a-time? */
-       bool            rs_allow_strat; /* allow or disallow use of access strategy */
-       bool            rs_allow_sync;  /* allow or disallow use of syncscan */
-       bool            rs_temp_snap;   /* unregister snapshot at scan end? */
-       bool            rs_syncscan;    /* report location to syncscan logic? */
+
+       /*
+        * Information about type and behaviour of the scan, a bitmask of members
+        * of the ScanOptions enum (see tableam.h).
+        */
+       uint32          rs_flags;
 
        struct ParallelTableScanDescData *rs_parallel;  /* parallel scan
                                                                                                         * information */
 
 struct VacuumParams;
 struct ValidateIndexState;
 
+/*
+ * Bitmask values for the flags argument to the scan_begin callback.
+ */
+typedef enum ScanOptions
+{
+       /* one of SO_TYPE_* may be specified */
+       SO_TYPE_SEQSCAN         = 1 << 0,
+       SO_TYPE_BITMAPSCAN      = 1 << 1,
+       SO_TYPE_SAMPLESCAN      = 1 << 2,
+       SO_TYPE_ANALYZE         = 1 << 3,
+
+       /* several of SO_ALLOW_* may be specified */
+       /* allow or disallow use of access strategy */
+       SO_ALLOW_STRAT          = 1 << 4,
+       /* report location to syncscan logic? */
+       SO_ALLOW_SYNC           = 1 << 5,
+       /* verify visibility page-at-a-time? */
+       SO_ALLOW_PAGEMODE       = 1 << 6,
+
+       /* unregister snapshot at scan end? */
+       SO_TEMP_SNAPSHOT        = 1 << 7
+} ScanOptions;
 
 /*
  * Result codes for table_{update,delete,lock_tuple}, and for visibility
        TM_WouldBlock
 } TM_Result;
 
-
 /*
  * When table_update, table_delete, or table_lock_tuple fail because the target
  * tuple is already outdated, they fill in this struct to provide information
         * parallelscan_initialize(), and has to be for the same relation. Will
         * only be set coming from table_beginscan_parallel().
         *
-        * allow_{strat, sync, pagemode} specify whether a scan strategy,
-        * synchronized scans, or page mode may be used (although not every AM
-        * will support those).
-        *
-        * is_{bitmapscan, samplescan} specify whether the scan is intended to
-        * support those types of scans.
-        *
-        * if temp_snap is true, the snapshot will need to be deallocated at
-        * scan_end.
+        * `flags` is a bitmask indicating the type of scan (ScanOptions's
+        * SO_TYPE_*, currently only one may be specified), options controlling
+        * the scan's behaviour (ScanOptions's SO_ALLOW_*, several may be
+        * specified, an AM may ignore unsupported ones) and whether the snapshot
+        * needs to be deallocated at scan_end (ScanOptions's SO_TEMP_SNAPSHOT).
         */
        TableScanDesc (*scan_begin) (Relation rel,
                                                                 Snapshot snapshot,
                                                                 int nkeys, struct ScanKeyData *key,
                                                                 ParallelTableScanDesc pscan,
-                                                                bool allow_strat,
-                                                                bool allow_sync,
-                                                                bool allow_pagemode,
-                                                                bool is_bitmapscan,
-                                                                bool is_samplescan,
-                                                                bool temp_snap);
+                                                                uint32 flags);
 
        /*
         * Release resources and deallocate scan. If TableScanDesc.temp_snap,
 table_beginscan(Relation rel, Snapshot snapshot,
                                int nkeys, struct ScanKeyData *key)
 {
-       return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL,
-                                                                          true, true, true, false, false, false);
+       uint32          flags = SO_TYPE_SEQSCAN |
+               SO_ALLOW_STRAT | SO_ALLOW_SYNC | SO_ALLOW_PAGEMODE;
+
+       return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL, flags);
 }
 
 /*
                                          int nkeys, struct ScanKeyData *key,
                                          bool allow_strat, bool allow_sync)
 {
-       return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL,
-                                                                          allow_strat, allow_sync, true,
-                                                                          false, false, false);
+       uint32          flags = SO_TYPE_SEQSCAN | SO_ALLOW_PAGEMODE;
+
+       if (allow_strat)
+               flags |= SO_ALLOW_STRAT;
+       if (allow_sync)
+               flags |= SO_ALLOW_SYNC;
+
+       return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL, flags);
 }
 
 /*
 table_beginscan_bm(Relation rel, Snapshot snapshot,
                                   int nkeys, struct ScanKeyData *key)
 {
-       return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL,
-                                                                          false, false, true, true, false, false);
+       uint32          flags = SO_TYPE_BITMAPSCAN | SO_ALLOW_PAGEMODE;
+
+       return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL, flags);
 }
 
 /*
                                                 bool allow_strat, bool allow_sync,
                                                 bool allow_pagemode)
 {
-       return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL,
-                                                                          allow_strat, allow_sync, allow_pagemode,
-                                                                          false, true, false);
+       uint32          flags = SO_TYPE_SAMPLESCAN;
+
+       if (allow_strat)
+               flags |= SO_ALLOW_STRAT;
+       if (allow_sync)
+               flags |= SO_ALLOW_SYNC;
+       if (allow_pagemode)
+               flags |= SO_ALLOW_PAGEMODE;
+
+       return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL, flags);
 }
 
 /*
 static inline TableScanDesc
 table_beginscan_analyze(Relation rel)
 {
-       return rel->rd_tableam->scan_begin(rel, NULL, 0, NULL, NULL,
-                                                                          true, false, true,
-                                                                          false, true, false);
+       uint32          flags = SO_TYPE_ANALYZE;
+
+       return rel->rd_tableam->scan_begin(rel, NULL, 0, NULL, NULL, flags);
 }
 
 /*
 
 VACUUM (SKIP_LOCKED) vactst;
 VACUUM (SKIP_LOCKED, FULL) vactst;
 ANALYZE (SKIP_LOCKED) vactst;
+-- ensure VACUUM and ANALYZE don't have a problem with serializable
+SET default_transaction_isolation = serializable;
+VACUUM vactst;
+ANALYZE vactst;
+RESET default_transaction_isolation;
+BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+ANALYZE vactst;
+COMMIT;
 DROP TABLE vaccluster;
 DROP TABLE vactst;
 DROP TABLE vacparted;
 
 VACUUM (SKIP_LOCKED, FULL) vactst;
 ANALYZE (SKIP_LOCKED) vactst;
 
+-- ensure VACUUM and ANALYZE don't have a problem with serializable
+SET default_transaction_isolation = serializable;
+VACUUM vactst;
+ANALYZE vactst;
+RESET default_transaction_isolation;
+BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+ANALYZE vactst;
+COMMIT;
+
 DROP TABLE vaccluster;
 DROP TABLE vactst;
 DROP TABLE vacparted;