From a67881969747cbbe95cbd50bd3daad831b8b417a Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon, 14 Dec 2020 00:26:44 -0600
Subject: [PATCH 2/4] Also do ClusterOpt and VacuumOpt

---
 src/backend/access/heap/vacuumlazy.c |   8 +-
 src/backend/commands/analyze.c       |  15 ++--
 src/backend/commands/cluster.c       |  26 +++---
 src/backend/commands/vacuum.c        | 128 +++++++++++++--------------
 src/backend/postmaster/autovacuum.c  |  14 +--
 src/include/commands/cluster.h       |   8 +-
 src/include/commands/vacuum.h        |  25 +++---
 7 files changed, 110 insertions(+), 114 deletions(-)

diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 25f2d5df1b..3c8b22fd63 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -456,7 +456,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
 		starttime = GetCurrentTimestamp();
 	}
 
-	if (params->options & VACOPT_VERBOSE)
+	if (params->options.VACOPT_VERBOSE)
 		elevel = INFO;
 	else
 		elevel = DEBUG2;
@@ -484,7 +484,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
 											   xidFullScanLimit);
 	aggressive |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid,
 											  mxactFullScanLimit);
-	if (params->options & VACOPT_DISABLE_PAGE_SKIPPING)
+	if (params->options.VACOPT_DISABLE_PAGE_SKIPPING)
 		aggressive = true;
 
 	vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats));
@@ -902,7 +902,7 @@ lazy_scan_heap(Relation onerel, VacuumParams *params, LVRelStats *vacrelstats,
 	 * be replayed on any hot standby, where it can be disruptive.
 	 */
 	next_unskippable_block = 0;
-	if ((params->options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
+	if (!params->options.VACOPT_DISABLE_PAGE_SKIPPING)
 	{
 		while (next_unskippable_block < nblocks)
 		{
@@ -960,7 +960,7 @@ lazy_scan_heap(Relation onerel, VacuumParams *params, LVRelStats *vacrelstats,
 		{
 			/* Time to advance next_unskippable_block */
 			next_unskippable_block++;
-			if ((params->options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
+			if (!params->options.VACOPT_DISABLE_PAGE_SKIPPING)
 			{
 				while (next_unskippable_block < nblocks)
 				{
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 8af12b5c6b..2e266391fb 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -124,9 +124,10 @@ analyze_rel(Oid relid, RangeVar *relation,
 	int			elevel;
 	AcquireSampleRowsFunc acquirefunc = NULL;
 	BlockNumber relpages = 0;
+	VacuumOption	newoptions;
 
 	/* Select logging level */
-	if (params->options & VACOPT_VERBOSE)
+	if (params->options.VACOPT_VERBOSE)
 		elevel = INFO;
 	else
 		elevel = DEBUG2;
@@ -148,7 +149,9 @@ analyze_rel(Oid relid, RangeVar *relation,
 	 *
 	 * Make sure to generate only logs for ANALYZE in this case.
 	 */
-	onerel = vacuum_open_relation(relid, relation, params->options & ~(VACOPT_VACUUM),
+	newoptions = params->options;
+	newoptions.VACOPT_VACUUM = false;
+	onerel = vacuum_open_relation(relid, relation, &newoptions,
 								  params->log_min_duration >= 0,
 								  ShareUpdateExclusiveLock);
 
@@ -166,7 +169,7 @@ analyze_rel(Oid relid, RangeVar *relation,
 	 */
 	if (!vacuum_is_relation_owner(RelationGetRelid(onerel),
 								  onerel->rd_rel,
-								  params->options & VACOPT_ANALYZE))
+								  &newoptions))
 	{
 		relation_close(onerel, ShareUpdateExclusiveLock);
 		return;
@@ -238,7 +241,7 @@ analyze_rel(Oid relid, RangeVar *relation,
 	else
 	{
 		/* No need for a WARNING if we already complained during VACUUM */
-		if (!(params->options & VACOPT_VACUUM))
+		if (!params->options.VACOPT_VACUUM)
 			ereport(WARNING,
 					(errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
 							RelationGetRelationName(onerel))));
@@ -624,7 +627,7 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
 	 * VACUUM ANALYZE, don't overwrite the accurate count already inserted by
 	 * VACUUM.
 	 */
-	if (!inh && !(params->options & VACOPT_VACUUM))
+	if (!inh && !params->options.VACOPT_VACUUM)
 	{
 		for (ind = 0; ind < nindexes; ind++)
 		{
@@ -655,7 +658,7 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
 							  (va_cols == NIL));
 
 	/* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup */
-	if (!(params->options & VACOPT_VACUUM))
+	if (!params->options.VACOPT_VACUUM)
 	{
 		for (ind = 0; ind < nindexes; ind++)
 		{
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 272723e050..591878207d 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -103,8 +103,7 @@ void
 cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
 {
 	ListCell   *lc;
-	int			options = 0;
-	bool		verbose = false;
+	ClusterOption		options = {false};
 
 	/* Parse option list */
 	foreach(lc, stmt->params)
@@ -112,7 +111,7 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
 		DefElem    *opt = (DefElem *) lfirst(lc);
 
 		if (strcmp(opt->defname, "verbose") == 0)
-			verbose = defGetBoolean(opt);
+			options.CLUOPT_VERBOSE = defGetBoolean(opt);
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
@@ -121,8 +120,6 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
 					 parser_errposition(pstate, opt->location)));
 	}
 
-	options = (verbose ? CLUOPT_VERBOSE : 0);
-
 	if (stmt->relation != NULL)
 	{
 		/* This is the single-relation case. */
@@ -192,7 +189,7 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
 		table_close(rel, NoLock);
 
 		/* Do the job. */
-		cluster_rel(tableOid, indexOid, options);
+		cluster_rel(tableOid, indexOid, &options);
 	}
 	else
 	{
@@ -234,14 +231,15 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
 		foreach(rv, rvs)
 		{
 			RelToCluster *rvtc = (RelToCluster *) lfirst(rv);
+			ClusterOption newoptions = options;
+			newoptions.CLUOPT_RECHECK = true;
 
 			/* Start a new transaction for each relation. */
 			StartTransactionCommand();
 			/* functions in indexes may want a snapshot set */
 			PushActiveSnapshot(GetTransactionSnapshot());
 			/* Do the job. */
-			cluster_rel(rvtc->tableOid, rvtc->indexOid,
-						options | CLUOPT_RECHECK);
+			cluster_rel(rvtc->tableOid, rvtc->indexOid, &newoptions);
 			PopActiveSnapshot();
 			CommitTransactionCommand();
 		}
@@ -272,11 +270,9 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
  * and error messages should refer to the operation as VACUUM not CLUSTER.
  */
 void
-cluster_rel(Oid tableOid, Oid indexOid, int options)
+cluster_rel(Oid tableOid, Oid indexOid, ClusterOption *options)
 {
 	Relation	OldHeap;
-	bool		verbose = ((options & CLUOPT_VERBOSE) != 0);
-	bool		recheck = ((options & CLUOPT_RECHECK) != 0);
 
 	/* Check for user-requested abort. */
 	CHECK_FOR_INTERRUPTS();
@@ -312,7 +308,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options)
 	 * *must* skip the one on indisclustered since it would reject an attempt
 	 * to cluster a not-previously-clustered index.
 	 */
-	if (recheck)
+	if (options->CLUOPT_RECHECK)
 	{
 		/* Check that the user still owns the relation */
 		if (!pg_class_ownercheck(tableOid, GetUserId()))
@@ -396,7 +392,9 @@ cluster_rel(Oid tableOid, Oid indexOid, int options)
 
 	/* Check heap and index are valid to cluster on */
 	if (OidIsValid(indexOid))
-		check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
+		check_index_is_clusterable(OldHeap, indexOid, options->CLUOPT_RECHECK,
+				AccessExclusiveLock);
+
 
 	/*
 	 * Quietly ignore the request if this is a materialized view which has not
@@ -422,7 +420,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options)
 	TransferPredicateLocksToHeapRelation(OldHeap);
 
 	/* rebuild_relation does all the dirty work */
-	rebuild_relation(OldHeap, indexOid, verbose);
+	rebuild_relation(OldHeap, indexOid, options->CLUOPT_VERBOSE);
 
 	/* NB: rebuild_relation does table_close() on OldHeap */
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 98270a1049..3774a18ff8 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -78,8 +78,8 @@ pg_atomic_uint32 *VacuumActiveNWorkers = NULL;
 int			VacuumCostBalanceLocal = 0;
 
 /* non-export function prototypes */
-static List *expand_vacuum_rel(VacuumRelation *vrel, int options);
-static List *get_all_vacuum_rels(int options);
+static List *expand_vacuum_rel(VacuumRelation *vrel, VacuumOption *options);
+static List *get_all_vacuum_rels(VacuumOption *options);
 static void vac_truncate_clog(TransactionId frozenXID,
 							  MultiXactId minMulti,
 							  TransactionId lastSaneFrozenXid,
@@ -97,13 +97,7 @@ static VacOptTernaryValue get_vacopt_ternary_value(DefElem *def);
 void
 ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 {
-	VacuumParams params;
-	bool		verbose = false;
-	bool		skip_locked = false;
-	bool		analyze = false;
-	bool		freeze = false;
-	bool		full = false;
-	bool		disable_page_skipping = false;
+	VacuumParams params = {.options={false} };
 	ListCell   *lc;
 
 	/* Set default value */
@@ -120,9 +114,9 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 
 		/* Parse common options for VACUUM and ANALYZE */
 		if (strcmp(opt->defname, "verbose") == 0)
-			verbose = defGetBoolean(opt);
+			params.options.VACOPT_VERBOSE = defGetBoolean(opt);
 		else if (strcmp(opt->defname, "skip_locked") == 0)
-			skip_locked = defGetBoolean(opt);
+			params.options.VACOPT_SKIP_LOCKED = defGetBoolean(opt);
 		else if (!vacstmt->is_vacuumcmd)
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
@@ -131,16 +125,18 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 
 		/* Parse options available on VACUUM */
 		else if (strcmp(opt->defname, "analyze") == 0)
-			analyze = defGetBoolean(opt);
+			params.options.VACOPT_ANALYZE = defGetBoolean(opt);
 		else if (strcmp(opt->defname, "freeze") == 0)
-			freeze = defGetBoolean(opt);
+			params.options.VACOPT_FREEZE = defGetBoolean(opt);
 		else if (strcmp(opt->defname, "full") == 0)
-			full = defGetBoolean(opt);
+			params.options.VACOPT_FULL = defGetBoolean(opt);
 		else if (strcmp(opt->defname, "disable_page_skipping") == 0)
-			disable_page_skipping = defGetBoolean(opt);
+			params.options.VACOPT_DISABLE_PAGE_SKIPPING = defGetBoolean(opt);
 		else if (strcmp(opt->defname, "index_cleanup") == 0)
+// XXX
 			params.index_cleanup = get_vacopt_ternary_value(opt);
 		else if (strcmp(opt->defname, "truncate") == 0)
+// XXX
 			params.truncate = get_vacopt_ternary_value(opt);
 		else if (strcmp(opt->defname, "parallel") == 0)
 		{
@@ -181,23 +177,18 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 					 parser_errposition(pstate, opt->location)));
 	}
 
-	/* Set vacuum options */
-	params.options =
-		(vacstmt->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE) |
-		(verbose ? VACOPT_VERBOSE : 0) |
-		(skip_locked ? VACOPT_SKIP_LOCKED : 0) |
-		(analyze ? VACOPT_ANALYZE : 0) |
-		(freeze ? VACOPT_FREEZE : 0) |
-		(full ? VACOPT_FULL : 0) |
-		(disable_page_skipping ? VACOPT_DISABLE_PAGE_SKIPPING : 0);
+	if (vacstmt->is_vacuumcmd)
+		params.options.VACOPT_VACUUM = true;
+	else
+		params.options.VACOPT_ANALYZE = true;
 
 	/* sanity checks on options */
-	Assert(params.options & (VACOPT_VACUUM | VACOPT_ANALYZE));
-	Assert((params.options & VACOPT_VACUUM) ||
-		   !(params.options & (VACOPT_FULL | VACOPT_FREEZE)));
-	Assert(!(params.options & VACOPT_SKIPTOAST));
+	Assert(params.options.VACOPT_VACUUM || params.options.VACOPT_ANALYZE);
+	Assert(params.options.VACOPT_VACUUM ||
+		   !(params.options.VACOPT_FULL || params.options.VACOPT_FREEZE));
+	Assert(!params.options.VACOPT_SKIPTOAST);
 
-	if ((params.options & VACOPT_FULL) && params.nworkers > 0)
+	if (params.options.VACOPT_FULL && params.nworkers > 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("VACUUM FULL cannot be performed in parallel")));
@@ -205,7 +196,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 	/*
 	 * Make sure VACOPT_ANALYZE is specified if any column lists are present.
 	 */
-	if (!(params.options & VACOPT_ANALYZE))
+	if (!params.options.VACOPT_ANALYZE)
 	{
 		ListCell   *lc;
 
@@ -224,7 +215,7 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 	 * All freeze ages are zero if the FREEZE option is given; otherwise pass
 	 * them as -1 which means to use the default values.
 	 */
-	if (params.options & VACOPT_FREEZE)
+	if (params.options.VACOPT_FREEZE)
 	{
 		params.freeze_min_age = 0;
 		params.freeze_table_age = 0;
@@ -280,7 +271,7 @@ vacuum(List *relations, VacuumParams *params,
 
 	Assert(params != NULL);
 
-	stmttype = (params->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
+	stmttype = params->options.VACOPT_VACUUM ? "VACUUM" : "ANALYZE";
 
 	/*
 	 * We cannot run VACUUM inside a user transaction block; if we were inside
@@ -290,7 +281,7 @@ vacuum(List *relations, VacuumParams *params,
 	 *
 	 * ANALYZE (without VACUUM) can run either way.
 	 */
-	if (params->options & VACOPT_VACUUM)
+	if (params->options.VACOPT_VACUUM)
 	{
 		PreventInTransactionBlock(isTopLevel, stmttype);
 		in_outer_xact = false;
@@ -312,8 +303,8 @@ vacuum(List *relations, VacuumParams *params,
 	/*
 	 * Sanity check DISABLE_PAGE_SKIPPING option.
 	 */
-	if ((params->options & VACOPT_FULL) != 0 &&
-		(params->options & VACOPT_DISABLE_PAGE_SKIPPING) != 0)
+	if (params->options.VACOPT_FULL &&
+		params->options.VACOPT_DISABLE_PAGE_SKIPPING)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL")));
@@ -322,7 +313,7 @@ vacuum(List *relations, VacuumParams *params,
 	 * Send info about dead objects to the statistics collector, unless we are
 	 * in autovacuum --- autovacuum.c does this for itself.
 	 */
-	if ((params->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
+	if (params->options.VACOPT_VACUUM && !IsAutoVacuumWorkerProcess())
 		pgstat_vacuum_stat();
 
 	/*
@@ -363,7 +354,7 @@ vacuum(List *relations, VacuumParams *params,
 			List	   *sublist;
 			MemoryContext old_context;
 
-			sublist = expand_vacuum_rel(vrel, params->options);
+			sublist = expand_vacuum_rel(vrel, &params->options);
 			old_context = MemoryContextSwitchTo(vac_context);
 			newrels = list_concat(newrels, sublist);
 			MemoryContextSwitchTo(old_context);
@@ -371,7 +362,7 @@ vacuum(List *relations, VacuumParams *params,
 		relations = newrels;
 	}
 	else
-		relations = get_all_vacuum_rels(params->options);
+		relations = get_all_vacuum_rels(&params->options);
 
 	/*
 	 * Decide whether we need to start/commit our own transactions.
@@ -387,11 +378,11 @@ vacuum(List *relations, VacuumParams *params,
 	 * transaction block, and also in an autovacuum worker, use own
 	 * transactions so we can release locks sooner.
 	 */
-	if (params->options & VACOPT_VACUUM)
+	if (params->options.VACOPT_VACUUM)
 		use_own_xacts = true;
 	else
 	{
-		Assert(params->options & VACOPT_ANALYZE);
+		Assert(params->options.VACOPT_ANALYZE);
 		if (IsAutoVacuumWorkerProcess())
 			use_own_xacts = true;
 		else if (in_outer_xact)
@@ -444,13 +435,13 @@ vacuum(List *relations, VacuumParams *params,
 		{
 			VacuumRelation *vrel = lfirst_node(VacuumRelation, cur);
 
-			if (params->options & VACOPT_VACUUM)
+			if (params->options.VACOPT_VACUUM)
 			{
 				if (!vacuum_rel(vrel->oid, vrel->relation, params))
 					continue;
 			}
 
-			if (params->options & VACOPT_ANALYZE)
+			if (params->options.VACOPT_ANALYZE)
 			{
 				/*
 				 * If using separate xacts, start one for analyze. Otherwise,
@@ -504,7 +495,7 @@ vacuum(List *relations, VacuumParams *params,
 		StartTransactionCommand();
 	}
 
-	if ((params->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
+	if (params->options.VACOPT_VACUUM && !IsAutoVacuumWorkerProcess())
 	{
 		/*
 		 * Update pg_database.datfrozenxid, and truncate pg_xact if possible.
@@ -530,11 +521,11 @@ vacuum(List *relations, VacuumParams *params,
  * ANALYZE.
  */
 bool
-vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, int options)
+vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, VacuumOption *options)
 {
 	char	   *relname;
 
-	Assert((options & (VACOPT_VACUUM | VACOPT_ANALYZE)) != 0);
+	Assert(options->VACOPT_VACUUM || options->VACOPT_ANALYZE);
 
 	/*
 	 * Check permissions.
@@ -553,7 +544,7 @@ vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, int options)
 
 	relname = NameStr(reltuple->relname);
 
-	if ((options & VACOPT_VACUUM) != 0)
+	if (options->VACOPT_VACUUM)
 	{
 		if (reltuple->relisshared)
 			ereport(WARNING,
@@ -576,7 +567,7 @@ vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, int options)
 		return false;
 	}
 
-	if ((options & VACOPT_ANALYZE) != 0)
+	if (options->VACOPT_ANALYZE)
 	{
 		if (reltuple->relisshared)
 			ereport(WARNING,
@@ -604,14 +595,14 @@ vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, int options)
  * or locked, a log is emitted if possible.
  */
 Relation
-vacuum_open_relation(Oid relid, RangeVar *relation, int options,
+vacuum_open_relation(Oid relid, RangeVar *relation, VacuumOption *options,
 					 bool verbose, LOCKMODE lmode)
 {
 	Relation	onerel;
 	bool		rel_lock = true;
 	int			elevel;
 
-	Assert((options & (VACOPT_VACUUM | VACOPT_ANALYZE)) != 0);
+	Assert(options->VACOPT_VACUUM || options->VACOPT_ANALYZE);
 
 	/*
 	 * Open the relation and get the appropriate lock on it.
@@ -622,7 +613,7 @@ vacuum_open_relation(Oid relid, RangeVar *relation, int options,
 	 * If we've been asked not to wait for the relation lock, acquire it first
 	 * in non-blocking mode, before calling try_relation_open().
 	 */
-	if (!(options & VACOPT_SKIP_LOCKED))
+	if (!options->VACOPT_SKIP_LOCKED)
 		onerel = try_relation_open(relid, lmode);
 	else if (ConditionalLockRelationOid(relid, lmode))
 		onerel = try_relation_open(relid, NoLock);
@@ -662,7 +653,7 @@ vacuum_open_relation(Oid relid, RangeVar *relation, int options,
 	else
 		return NULL;
 
-	if ((options & VACOPT_VACUUM) != 0)
+	if (options->VACOPT_VACUUM)
 	{
 		if (!rel_lock)
 			ereport(elevel,
@@ -683,7 +674,7 @@ vacuum_open_relation(Oid relid, RangeVar *relation, int options,
 		return NULL;
 	}
 
-	if ((options & VACOPT_ANALYZE) != 0)
+	if (options->VACOPT_ANALYZE)
 	{
 		if (!rel_lock)
 			ereport(elevel,
@@ -716,7 +707,7 @@ vacuum_open_relation(Oid relid, RangeVar *relation, int options,
  * are made in vac_context.
  */
 static List *
-expand_vacuum_rel(VacuumRelation *vrel, int options)
+expand_vacuum_rel(VacuumRelation *vrel, VacuumOption *options)
 {
 	List	   *vacrels = NIL;
 	MemoryContext oldcontext;
@@ -748,7 +739,7 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
 		 * below, as well as find_all_inheritors's expectation that the caller
 		 * holds some lock on the starting relation.
 		 */
-		rvr_opts = (options & VACOPT_SKIP_LOCKED) ? RVR_SKIP_LOCKED : 0;
+		rvr_opts = (options->VACOPT_SKIP_LOCKED) ? RVR_SKIP_LOCKED : 0;
 		relid = RangeVarGetRelidExtended(vrel->relation,
 										 AccessShareLock,
 										 rvr_opts,
@@ -760,7 +751,7 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
 		 */
 		if (!OidIsValid(relid))
 		{
-			if (options & VACOPT_VACUUM)
+			if (options->VACOPT_VACUUM)
 				ereport(WARNING,
 						(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
 						 errmsg("skipping vacuum of \"%s\" --- lock not available",
@@ -855,7 +846,7 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
  * the current database.  The list is built in vac_context.
  */
 static List *
-get_all_vacuum_rels(int options)
+get_all_vacuum_rels(VacuumOption *options)
 {
 	List	   *vacrels = NIL;
 	Relation	pgclass;
@@ -1722,13 +1713,14 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
 	Oid			save_userid;
 	int			save_sec_context;
 	int			save_nestlevel;
+	VacuumOption	newoptions;
 
 	Assert(params != NULL);
 
 	/* Begin a transaction for vacuuming this relation */
 	StartTransactionCommand();
 
-	if (!(params->options & VACOPT_FULL))
+	if (!params->options.VACOPT_FULL)
 	{
 		/*
 		 * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
@@ -1778,11 +1770,11 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
 	 * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either
 	 * way, we can be sure that no other backend is vacuuming the same table.
 	 */
-	lmode = (params->options & VACOPT_FULL) ?
+	lmode = (params->options.VACOPT_FULL) ?
 		AccessExclusiveLock : ShareUpdateExclusiveLock;
 
 	/* open the relation and get the appropriate lock on it */
-	onerel = vacuum_open_relation(relid, relation, params->options,
+	onerel = vacuum_open_relation(relid, relation, &params->options,
 								  params->log_min_duration >= 0, lmode);
 
 	/* leave if relation could not be opened or locked */
@@ -1801,9 +1793,11 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
 	 * changed in-between.  Make sure to only generate logs for VACUUM in this
 	 * case.
 	 */
+	newoptions = params->options;
+	newoptions.VACOPT_ANALYZE = false;
 	if (!vacuum_is_relation_owner(RelationGetRelid(onerel),
 								  onerel->rd_rel,
-								  params->options & VACOPT_VACUUM))
+								  &newoptions))
 	{
 		relation_close(onerel, lmode);
 		PopActiveSnapshot();
@@ -1895,7 +1889,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
 	 * us to process it.  In VACUUM FULL, though, the toast table is
 	 * automatically rebuilt by cluster_rel so we shouldn't recurse to it.
 	 */
-	if (!(params->options & VACOPT_SKIPTOAST) && !(params->options & VACOPT_FULL))
+	if (!params->options.VACOPT_SKIPTOAST && !params->options.VACOPT_FULL)
 		toast_relid = onerel->rd_rel->reltoastrelid;
 	else
 		toast_relid = InvalidOid;
@@ -1914,19 +1908,19 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
 	/*
 	 * Do the actual work --- either FULL or "lazy" vacuum
 	 */
-	if (params->options & VACOPT_FULL)
+	if (params->options.VACOPT_FULL)
 	{
-		int			cluster_options = 0;
+		ClusterOption	cluster_options = {
+			.CLUOPT_VERBOSE = params->options.VACOPT_VERBOSE,
+			/* Other members initialized to false/0/NULL */
+		};
 
 		/* close relation before vacuuming, but hold lock until commit */
 		relation_close(onerel, NoLock);
 		onerel = NULL;
 
-		if ((params->options & VACOPT_VERBOSE) != 0)
-			cluster_options |= CLUOPT_VERBOSE;
-
 		/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
-		cluster_rel(relid, InvalidOid, cluster_options);
+		cluster_rel(relid, InvalidOid, &cluster_options);
 	}
 	else
 		table_relation_vacuum(onerel, params, vac_strategy);
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 7e28944d2f..a1599ccb77 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2505,7 +2505,7 @@ do_autovacuum(void)
 			 * next table in our list.
 			 */
 			HOLD_INTERRUPTS();
-			if (tab->at_params.options & VACOPT_VACUUM)
+			if (tab->at_params.options.VACOPT_VACUUM)
 				errcontext("automatic vacuum of table \"%s.%s.%s\"",
 						   tab->at_datname, tab->at_nspname, tab->at_relname);
 			else
@@ -2919,10 +2919,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		tab = palloc(sizeof(autovac_table));
 		tab->at_relid = relid;
 		tab->at_sharedrel = classForm->relisshared;
-		tab->at_params.options = VACOPT_SKIPTOAST |
-			(dovacuum ? VACOPT_VACUUM : 0) |
-			(doanalyze ? VACOPT_ANALYZE : 0) |
-			(!wraparound ? VACOPT_SKIP_LOCKED : 0);
+		tab->at_params.options.VACOPT_SKIPTOAST = true;
+		tab->at_params.options.VACOPT_VACUUM = dovacuum;
+		tab->at_params.options.VACOPT_ANALYZE = doanalyze;
+		tab->at_params.options.VACOPT_SKIP_LOCKED = !wraparound;
 		tab->at_params.index_cleanup = VACOPT_TERNARY_DEFAULT;
 		tab->at_params.truncate = VACOPT_TERNARY_DEFAULT;
 		/* As of now, we don't support parallel vacuum for autovacuum */
@@ -3250,10 +3250,10 @@ autovac_report_activity(autovac_table *tab)
 	int			len;
 
 	/* Report the command and possible options */
-	if (tab->at_params.options & VACOPT_VACUUM)
+	if (tab->at_params.options.VACOPT_VACUUM)
 		snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
 				 "autovacuum: VACUUM%s",
-				 tab->at_params.options & VACOPT_ANALYZE ? " ANALYZE" : "");
+				 tab->at_params.options.VACOPT_ANALYZE ? " ANALYZE" : "");
 	else
 		snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
 				 "autovacuum: ANALYZE");
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index 7cfb37c9b2..5111941a41 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -20,14 +20,14 @@
 
 
 /* options for CLUSTER */
-typedef enum ClusterOption
+typedef struct ClusterOption
 {
-	CLUOPT_RECHECK = 1 << 0,	/* recheck relation state */
-	CLUOPT_VERBOSE = 1 << 1		/* print progress info */
+	bool	CLUOPT_RECHECK;		/* recheck relation state */
+	bool	CLUOPT_VERBOSE;		/* print progress info */
 } ClusterOption;
 
 extern void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel);
-extern void cluster_rel(Oid tableOid, Oid indexOid, int options);
+extern void cluster_rel(Oid tableOid, Oid indexOid, ClusterOption *options);
 extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
 									   bool recheck, LOCKMODE lockmode);
 extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal);
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index a4cd721400..15327643ba 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -174,16 +174,16 @@ typedef struct VacAttrStats
 	int			rowstride;
 } VacAttrStats;
 
-typedef enum VacuumOption
+typedef struct VacuumOption
 {
-	VACOPT_VACUUM = 1 << 0,		/* do VACUUM */
-	VACOPT_ANALYZE = 1 << 1,	/* do ANALYZE */
-	VACOPT_VERBOSE = 1 << 2,	/* print progress info */
-	VACOPT_FREEZE = 1 << 3,		/* FREEZE option */
-	VACOPT_FULL = 1 << 4,		/* FULL (non-concurrent) vacuum */
-	VACOPT_SKIP_LOCKED = 1 << 5,	/* skip if cannot get lock */
-	VACOPT_SKIPTOAST = 1 << 6,	/* don't process the TOAST table, if any */
-	VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7	/* don't skip any pages */
+	bool VACOPT_VACUUM;			/* do VACUUM */
+	bool VACOPT_ANALYZE;		/* do ANALYZE */
+	bool VACOPT_VERBOSE;		/* print progress info */
+	bool VACOPT_FREEZE;			/* FREEZE option */
+	bool VACOPT_FULL;			/* FULL (non-concurrent) vacuum */
+	bool VACOPT_SKIP_LOCKED;	/* skip if cannot get lock */
+	bool VACOPT_SKIPTOAST;		/* don't process the TOAST table, if any */
+	bool VACOPT_DISABLE_PAGE_SKIPPING;	/* don't skip any pages */
 } VacuumOption;
 
 /*
@@ -207,7 +207,7 @@ typedef enum VacOptTernaryValue
  */
 typedef struct VacuumParams
 {
-	int			options;		/* bitmask of VacuumOption */
+	VacuumOption		options;		/* bitmask of VacuumOption */
 	int			freeze_min_age; /* min freeze age, -1 to use default */
 	int			freeze_table_age;	/* age at which to scan whole table */
 	int			multixact_freeze_min_age;	/* min multixact freeze age, -1 to
@@ -275,9 +275,10 @@ extern void vacuum_set_xid_limits(Relation rel,
 extern void vac_update_datfrozenxid(void);
 extern void vacuum_delay_point(void);
 extern bool vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple,
-									 int options);
+									 VacuumOption *options);
 extern Relation vacuum_open_relation(Oid relid, RangeVar *relation,
-									 int options, bool verbose, LOCKMODE lmode);
+									 VacuumOption *options, bool verbose,
+									 LOCKMODE lmode);
 
 /* in commands/analyze.c */
 extern void analyze_rel(Oid relid, RangeVar *relation,
-- 
2.17.0

