query-bits.patch

text/plain

Filename: query-bits.patch
Type: text/plain
Part: 0
Message: Re: Change the bool member of the Query structure to bits

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
contrib/postgres_fdw/postgres_fdw.c 4 4
src/backend/commands/view.c 1 1
src/backend/executor/functions.c 4 4
src/backend/optimizer/path/allpaths.c 11 11
src/backend/optimizer/plan/analyzejoins.c 4 4
src/backend/optimizer/plan/planagg.c 4 4
src/backend/optimizer/plan/planner.c 43 39
src/backend/optimizer/plan/setrefs.c 1 1
src/backend/optimizer/plan/subselect.c 6 6
src/backend/optimizer/prep/prepjointree.c 17 13
src/backend/optimizer/prep/prepunion.c 1 1
src/backend/optimizer/util/clauses.c 5 5
src/backend/optimizer/util/var.c 5 4
src/backend/parser/analyze.c 53 51
src/backend/parser/parse_cte.c 1 1
src/backend/parser/parse_merge.c 5 5
src/backend/rewrite/rewriteDefine.c 2 2
src/backend/rewrite/rewriteHandler.c 47 43
src/backend/rewrite/rewriteManip.c 14 13
src/backend/rewrite/rewriteSearchCycle.c 2 2
src/backend/tcop/postgres.c 1 1
src/backend/tcop/pquery.c 3 3
src/backend/utils/adt/ruleutils.c 5 5
src/backend/utils/cache/plancache.c 3 3
src/include/nodes/parsenodes.h 55 25
src/include/rewrite/rewriteManip.h 2 2
src/pl/plpgsql/src/pl_exec.c 4 4
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 142dcfc995..a28d625147 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -3341,7 +3341,7 @@ estimate_path_cost_size(PlannerInfo *root,
 
 			/* Collect statistics about aggregates for estimating costs. */
 			MemSet(&aggcosts, 0, sizeof(AggClauseCosts));
-			if (root->parse->hasAggs)
+			if (QueryHasAggs(root->parse))
 			{
 				get_agg_clause_costs(root, AGGSPLIT_SIMPLE, &aggcosts);
 			}
@@ -6762,7 +6762,7 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	Cost		total_cost;
 
 	/* Nothing to be done, if there is no grouping or aggregation required. */
-	if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
+	if (!parse->groupClause && !parse->groupingSets && !QueryHasAggs(parse) &&
 		!root->hasHavingQual)
 		return;
 
@@ -6859,7 +6859,7 @@ add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	Assert(parse->sortClause);
 
 	/* We don't support cases where there are any SRFs in the targetlist */
-	if (parse->hasTargetSRFs)
+	if (QueryHasTargetSRFs(parse))
 		return;
 
 	/* Save the input_rel as outerrel in fpinfo */
@@ -7007,7 +7007,7 @@ add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel,
 		return;
 
 	/* We don't support cases where there are any SRFs in the targetlist */
-	if (parse->hasTargetSRFs)
+	if (QueryHasTargetSRFs(parse))
 		return;
 
 	/* Save the input_rel as outerrel in fpinfo */
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index dce898c751..e328bd9242 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -398,7 +398,7 @@ DefineView(ViewStmt *stmt, const char *queryString,
 	 * DefineQueryRewrite(), but that function will complain about a bogus ON
 	 * SELECT rule, and we'd rather the message complain about a view.
 	 */
-	if (viewParse->hasModifyingCTE)
+	if (QueryHasModifyingCTE(viewParse))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("views must not contain data-modifying statements in WITH")));
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 0f811fd2fc..cdc94f082d 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -488,7 +488,7 @@ init_execution_state(List *queryTree_list,
 				/* Utility commands require no planning. */
 				stmt = makeNode(PlannedStmt);
 				stmt->commandType = CMD_UTILITY;
-				stmt->canSetTag = queryTree->canSetTag;
+				stmt->canSetTag = QueryCanSetTag(queryTree);
 				stmt->utilityStmt = queryTree->utilityStmt;
 				stmt->stmt_location = queryTree->stmt_location;
 				stmt->stmt_len = queryTree->stmt_len;
@@ -540,7 +540,7 @@ init_execution_state(List *queryTree_list,
 			newes->stmt = stmt;
 			newes->qd = NULL;
 
-			if (queryTree->canSetTag)
+			if (QueryCanSetTag(queryTree))
 				lasttages = newes;
 
 			preves = newes;
@@ -1650,7 +1650,7 @@ check_sql_fn_retval(List *queryTreeLists,
 		{
 			Query	   *q = lfirst_node(Query, lc2);
 
-			if (q->canSetTag)
+			if (QueryCanSetTag(q))
 			{
 				parse = q;
 				parse_cell = lc2;
@@ -1934,7 +1934,7 @@ tlist_coercion_finished:
 		newquery = makeNode(Query);
 		newquery->commandType = CMD_SELECT;
 		newquery->querySource = parse->querySource;
-		newquery->canSetTag = true;
+		QuerySetFlag(newquery, CAN_SET_TAG);
 		newquery->targetList = upper_tlist;
 
 		/* We need a moderately realistic colnames list for the subquery RTE */
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index d404fbf262..bd359fd636 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2595,7 +2595,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 					 * so then it might be useful to use for the WindowAgg's
 					 * runCondition.
 					 */
-					if (!subquery->hasWindowFuncs ||
+					if (!QueryHasWindowFuncs(subquery) ||
 						check_and_push_window_quals(subquery, rte, rti, clause,
 													&run_cond_attrs))
 					{
@@ -2633,7 +2633,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	 * we'd better tell the subquery to plan for full retrieval. (XXX This
 	 * could probably be made more intelligent ...)
 	 */
-	if (parse->hasAggs ||
+	if (QueryHasAggs(parse) ||
 		parse->groupClause ||
 		parse->groupingSets ||
 		root->hasHavingQual ||
@@ -3604,8 +3604,8 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
 
 	/* Check points 3, 4, and 5 */
 	if (subquery->distinctClause ||
-		subquery->hasWindowFuncs ||
-		subquery->hasTargetSRFs)
+		QueryHasWindowFuncs(subquery) ||
+		QueryHasTargetSRFs(subquery))
 		safetyInfo->unsafeVolatile = true;
 
 	/*
@@ -3726,7 +3726,7 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
 			continue;			/* ignore resjunk columns */
 
 		/* Functions returning sets are unsafe (point 1) */
-		if (subquery->hasTargetSRFs &&
+		if (QueryHasTargetSRFs(subquery) &&
 			(safetyInfo->unsafeFlags[tle->resno] &
 			 UNSAFE_HAS_SET_FUNC) == 0 &&
 			expression_returns_set((Node *) tle->expr))
@@ -3745,7 +3745,7 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
 		}
 
 		/* If subquery uses DISTINCT ON, check point 3 */
-		if (subquery->hasDistinctOn &&
+		if (QueryHasDistinctOn(subquery) &&
 			(safetyInfo->unsafeFlags[tle->resno] &
 			 UNSAFE_NOTIN_DISTINCTON_CLAUSE) == 0 &&
 			!targetIsInSortList(tle, InvalidOid, subquery->distinctClause))
@@ -3756,7 +3756,7 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
 		}
 
 		/* If subquery uses window functions, check point 4 */
-		if (subquery->hasWindowFuncs &&
+		if (QueryHasWindowFuncs(subquery) &&
 			(safetyInfo->unsafeFlags[tle->resno] &
 			 UNSAFE_NOTIN_DISTINCTON_CLAUSE) == 0 &&
 			!targetIsInAllPartitionLists(tle, subquery))
@@ -3985,14 +3985,14 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
 		qual = ReplaceVarsFromTargetList(qual, rti, 0, rte,
 										 subquery->targetList,
 										 REPLACEVARS_REPORT_ERROR, 0,
-										 &subquery->hasSubLinks);
+										 &subquery->flags);
 
 		/*
 		 * Now attach the qual to the proper place: normally WHERE, but if the
 		 * subquery uses grouping or aggregation, put it in HAVING (since the
 		 * qual really refers to the group-result rows).
 		 */
-		if (subquery->hasAggs || subquery->groupClause || subquery->groupingSets || subquery->havingQual)
+		if (QueryHasAggs(subquery) || subquery->groupClause || subquery->groupingSets || subquery->havingQual)
 			subquery->havingQual = make_and_qual(subquery->havingQual, qual);
 		else
 			subquery->jointree->quals =
@@ -4086,7 +4086,7 @@ remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel,
 	 * If subquery has regular DISTINCT (not DISTINCT ON), we're wasting our
 	 * time: all its output columns must be used in the distinctClause.
 	 */
-	if (subquery->distinctClause && !subquery->hasDistinctOn)
+	if (subquery->distinctClause && !QueryHasDistinctOn(subquery))
 		return;
 
 	/*
@@ -4147,7 +4147,7 @@ remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel,
 		 * If it contains a set-returning function, we can't remove it since
 		 * that could change the number of rows returned by the subquery.
 		 */
-		if (subquery->hasTargetSRFs &&
+		if (QueryHasTargetSRFs(subquery) &&
 			expression_returns_set(texpr))
 			continue;
 
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index e494acd51a..618fed25fb 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1010,14 +1010,14 @@ bool
 query_supports_distinctness(Query *query)
 {
 	/* SRFs break distinctness except with DISTINCT, see below */
-	if (query->hasTargetSRFs && query->distinctClause == NIL)
+	if (QueryHasTargetSRFs(query) && query->distinctClause == NIL)
 		return false;
 
 	/* check for features we can prove distinctness with */
 	if (query->distinctClause != NIL ||
 		query->groupClause != NIL ||
 		query->groupingSets != NIL ||
-		query->hasAggs ||
+		QueryHasAggs(query) ||
 		query->havingQual ||
 		query->setOperations)
 		return true;
@@ -1081,7 +1081,7 @@ query_is_distinct_for(Query *query, List *colnos, List *opids)
 	 * columns, it would be safe because they'd be expanded before grouping.
 	 * But it doesn't currently seem worth the effort to check for that.)
 	 */
-	if (query->hasTargetSRFs)
+	if (QueryHasTargetSRFs(query))
 		return false;
 
 	/*
@@ -1131,7 +1131,7 @@ query_is_distinct_for(Query *query, List *colnos, List *opids)
 		 * If we have no GROUP BY, but do have aggregates or HAVING, then the
 		 * result is at most one row so it's surely unique, for any operators.
 		 */
-		if (query->hasAggs || query->havingQual)
+		if (QueryHasAggs(query) || query->havingQual)
 			return true;
 	}
 
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 700c0b7ac7..fe5daaa332 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -84,7 +84,7 @@ preprocess_minmax_aggregates(PlannerInfo *root)
 	Assert(root->minmax_aggs == NIL);
 
 	/* Nothing to do if query has no aggregates */
-	if (!parse->hasAggs)
+	if (!QueryHasAggs(parse))
 		return;
 
 	Assert(!parse->setOperations);	/* shouldn't get here if a setop */
@@ -98,7 +98,7 @@ preprocess_minmax_aggregates(PlannerInfo *root)
 	 * so there's not much point in optimizing MIN/MAX.
 	 */
 	if (parse->groupClause || list_length(parse->groupingSets) > 1 ||
-		parse->hasWindowFuncs)
+		QueryHasWindowFuncs(parse))
 		return;
 
 	/*
@@ -379,8 +379,8 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	parse->havingQual = NULL;
 	subroot->hasHavingQual = false;
 	parse->distinctClause = NIL;
-	parse->hasDistinctOn = false;
-	parse->hasAggs = false;
+	QueryClearFlag(parse, HAS_DISTINCT_ON);
+	QueryClearFlag(parse, HAS_AGGS);
 
 	/* Build "target IS NOT NULL" expression */
 	ntest = makeNode(NullTest);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index be4e182869..a18832507f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -348,7 +348,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 	if ((cursorOptions & CURSOR_OPT_PARALLEL_OK) != 0 &&
 		IsUnderPostmaster &&
 		parse->commandType == CMD_SELECT &&
-		!parse->hasModifyingCTE &&
+		!QueryHasModifyingCTE(parse) &&
 		max_parallel_workers_per_gather > 0 &&
 		!IsParallelWorker())
 	{
@@ -540,8 +540,8 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 	result->commandType = parse->commandType;
 	result->queryId = parse->queryId;
 	result->hasReturning = (parse->returningList != NIL);
-	result->hasModifyingCTE = parse->hasModifyingCTE;
-	result->canSetTag = parse->canSetTag;
+	result->hasModifyingCTE = QueryHasModifyingCTE(parse);
+	result->canSetTag = QueryCanSetTag(parse);
 	result->transientPlan = glob->transientPlan;
 	result->dependsOnRole = glob->dependsOnRole;
 	result->parallelModeNeeded = glob->parallelModeNeeded;
@@ -707,7 +707,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	 * into subqueries; if we pull up any subqueries below, their SubLinks are
 	 * processed just before pulling them up.
 	 */
-	if (parse->hasSubLinks)
+	if (QueryHasSubLinks(parse))
 		pull_up_sublinks(root);
 
 	/*
@@ -834,8 +834,12 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 							  EXPRKIND_TARGET);
 
 	/* Constant-folding might have removed all set-returning functions */
-	if (parse->hasTargetSRFs)
-		parse->hasTargetSRFs = expression_returns_set((Node *) parse->targetList);
+	if (QueryHasTargetSRFs(parse))
+	{
+		QueryCondSetFlag(parse,
+					expression_returns_set((Node *) parse->targetList),
+					HAS_TARGET_SRFS);
+	}
 
 	newWithCheckOptions = NIL;
 	foreach(l, parse->withCheckOptions)
@@ -1189,7 +1193,7 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
 	}
 
 	/* Expand SubLinks to SubPlans */
-	if (root->parse->hasSubLinks)
+	if (QueryHasSubLinks(root->parse))
 		expr = SS_process_sublinks(root, expr, (kind == EXPRKIND_QUAL));
 
 	/*
@@ -1368,7 +1372,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 			is_parallel_safe(root, (Node *) final_target->exprs);
 
 		/* The setop result tlist couldn't contain any SRFs */
-		Assert(!parse->hasTargetSRFs);
+		Assert(!QueryHasTargetSRFs(parse));
 		final_targets = final_targets_contain_srfs = NIL;
 
 		/*
@@ -1446,7 +1450,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		 * pathtargets, else some copies of the Aggref nodes might escape
 		 * being marked.
 		 */
-		if (parse->hasAggs)
+		if (QueryHasAggs(parse))
 		{
 			preprocess_aggrefs(root, (Node *) root->processed_tlist);
 			preprocess_aggrefs(root, (Node *) parse->havingQual);
@@ -1458,7 +1462,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		 * too.)  Note that they could all have been eliminated by constant
 		 * folding, in which case we don't need to do any more work.
 		 */
-		if (parse->hasWindowFuncs)
+		if (QueryHasWindowFuncs(parse))
 		{
 			wflists = find_window_functions((Node *) root->processed_tlist,
 											list_length(parse->windowClause));
@@ -1474,7 +1478,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 				activeWindows = select_active_windows(root, wflists);
 			}
 			else
-				parse->hasWindowFuncs = false;
+				QueryClearFlag(parse, HAS_WINDOW_FUNCS);
 		}
 
 		/*
@@ -1483,7 +1487,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		 * that is needed in MIN/MAX-optimizable cases will have to be
 		 * duplicated in planagg.c.
 		 */
-		if (parse->hasAggs)
+		if (QueryHasAggs(parse))
 			preprocess_minmax_aggregates(root);
 
 		/*
@@ -1495,9 +1499,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		if (parse->groupClause ||
 			parse->groupingSets ||
 			parse->distinctClause ||
-			parse->hasAggs ||
-			parse->hasWindowFuncs ||
-			parse->hasTargetSRFs ||
+			QueryHasAggs(parse) ||
+			QueryHasWindowFuncs(parse) ||
+			QueryHasTargetSRFs(parse) ||
 			root->hasHavingQual)
 			root->limit_tuples = -1.0;
 		else
@@ -1573,7 +1577,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		 * should emit grouping_target.
 		 */
 		have_grouping = (parse->groupClause || parse->groupingSets ||
-						 parse->hasAggs || root->hasHavingQual);
+						 QueryHasAggs(parse) || root->hasHavingQual);
 		if (have_grouping)
 		{
 			scanjoin_target = make_group_input_target(root, final_target);
@@ -1592,7 +1596,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		 * each of the named targets with a SRF-free version, and remember the
 		 * list of additional projection steps we need to add afterwards.
 		 */
-		if (parse->hasTargetSRFs)
+		if (QueryHasTargetSRFs(parse))
 		{
 			/* final_target doesn't recompute any SRFs in sort_input_target */
 			split_pathtarget_at_srfs(root, final_target, sort_input_target,
@@ -1664,7 +1668,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 												grouping_target_parallel_safe,
 												gset_data);
 			/* Fix things up if grouping_target contains SRFs */
-			if (parse->hasTargetSRFs)
+			if (QueryHasTargetSRFs(parse))
 				adjust_paths_for_srfs(root, current_rel,
 									  grouping_targets,
 									  grouping_targets_contain_srfs);
@@ -1684,7 +1688,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 											  wflists,
 											  activeWindows);
 			/* Fix things up if sort_input_target contains SRFs */
-			if (parse->hasTargetSRFs)
+			if (QueryHasTargetSRFs(parse))
 				adjust_paths_for_srfs(root, current_rel,
 									  sort_input_targets,
 									  sort_input_targets_contain_srfs);
@@ -1718,7 +1722,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 										   have_postponed_srfs ? -1.0 :
 										   limit_tuples);
 		/* Fix things up if final_target contains SRFs */
-		if (parse->hasTargetSRFs)
+		if (QueryHasTargetSRFs(parse))
 			adjust_paths_for_srfs(root, current_rel,
 								  final_targets,
 								  final_targets_contain_srfs);
@@ -1955,7 +1959,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 				create_modifytable_path(root, final_rel,
 										path,
 										parse->commandType,
-										parse->canSetTag,
+										QueryCanSetTag(parse),
 										parse->resultRelation,
 										rootRelation,
 										root->partColsUpdated,
@@ -3573,7 +3577,7 @@ get_number_of_groups(PlannerInfo *root,
 		/* Empty grouping sets ... one result row for each one */
 		dNumGroups = list_length(parse->groupingSets);
 	}
-	else if (parse->hasAggs || root->hasHavingQual)
+	else if (QueryHasAggs(parse) || root->hasHavingQual)
 	{
 		/* Plain aggregation, one result row */
 		dNumGroups = 1;
@@ -3776,7 +3780,7 @@ is_degenerate_grouping(PlannerInfo *root)
 	Query	   *parse = root->parse;
 
 	return (root->hasHavingQual || parse->groupingSets) &&
-		!parse->hasAggs && parse->groupClause == NIL;
+		!QueryHasAggs(parse) && parse->groupClause == NIL;
 }
 
 /*
@@ -4665,7 +4669,7 @@ create_partial_distinct_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	parse = root->parse;
 
 	/* can't do parallel DISTINCT ON */
-	if (parse->hasDistinctOn)
+	if (QueryHasDistinctOn(parse))
 		return;
 
 	partial_distinct_rel = fetch_upper_rel(root, UPPERREL_PARTIAL_DISTINCT,
@@ -4855,7 +4859,7 @@ create_final_distinct_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	bool		allow_hash;
 
 	/* Estimate number of distinct rows there will be */
-	if (parse->groupClause || parse->groupingSets || parse->hasAggs ||
+	if (parse->groupClause || parse->groupingSets || QueryHasAggs(parse) ||
 		root->hasHavingQual)
 	{
 		/*
@@ -4902,7 +4906,7 @@ create_final_distinct_paths(PlannerInfo *root, RelOptInfo *input_rel,
 		ListCell   *lc;
 		double		limittuples = root->distinct_pathkeys == NIL ? 1.0 : -1.0;
 
-		if (parse->hasDistinctOn &&
+		if (QueryHasDistinctOn(parse) &&
 			list_length(root->distinct_pathkeys) <
 			list_length(root->sort_pathkeys))
 			needed_pathkeys = root->sort_pathkeys;
@@ -5011,7 +5015,7 @@ create_final_distinct_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	 */
 	if (distinct_rel->pathlist == NIL)
 		allow_hash = true;		/* we have no alternatives */
-	else if (parse->hasDistinctOn || !enable_hashagg)
+	else if (QueryHasDistinctOn(parse) || !enable_hashagg)
 		allow_hash = false;		/* policy-based decision not to hash */
 	else
 		allow_hash = true;		/* default */
@@ -5839,7 +5843,7 @@ make_window_input_target(PlannerInfo *root,
 	int			i;
 	ListCell   *lc;
 
-	Assert(root->parse->hasWindowFuncs);
+	Assert(QueryHasWindowFuncs(root->parse));
 
 	/*
 	 * Collect the sortgroupref numbers of window PARTITION/ORDER BY clauses
@@ -6123,7 +6127,7 @@ make_sort_input_target(PlannerInfo *root,
 			 * Check for SRF or volatile functions.  Check the SRF case first
 			 * because we must know whether we have any postponed SRFs.
 			 */
-			if (parse->hasTargetSRFs &&
+			if (QueryHasTargetSRFs(parse) &&
 				expression_returns_set((Node *) expr))
 			{
 				/* We'll decide below whether these are postponable */
@@ -6163,7 +6167,7 @@ make_sort_input_target(PlannerInfo *root,
 		{
 			/* For sortgroupref cols, just check if any contain SRFs */
 			if (!have_srf_sortcols &&
-				parse->hasTargetSRFs &&
+				QueryHasTargetSRFs(parse) &&
 				expression_returns_set((Node *) expr))
 				have_srf_sortcols = true;
 		}
@@ -6845,7 +6849,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
 												path, true, can_hash,
 												gd, agg_costs, dNumGroups);
 				}
-				else if (parse->hasAggs)
+				else if (QueryHasAggs(parse))
 				{
 					/*
 					 * We have aggregation, possibly with plain GROUP BY. Make
@@ -6920,7 +6924,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
 					if (path == NULL)
 						continue;
 
-					if (parse->hasAggs)
+					if (QueryHasAggs(parse))
 						add_path(grouped_rel, (Path *)
 								 create_agg_path(root,
 												 grouped_rel,
@@ -7105,7 +7109,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 		 */
 		MemSet(agg_partial_costs, 0, sizeof(AggClauseCosts));
 		MemSet(agg_final_costs, 0, sizeof(AggClauseCosts));
-		if (parse->hasAggs)
+		if (QueryHasAggs(parse))
 		{
 			/* partial phase */
 			get_agg_clause_costs(root, AGGSPLIT_INITIAL_SERIAL,
@@ -7136,7 +7140,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	if (can_sort && cheapest_total_path != NULL)
 	{
 		/* This should have been checked previously */
-		Assert(parse->hasAggs || parse->groupClause);
+		Assert(QueryHasAggs(parse) || parse->groupClause);
 
 		/*
 		 * Use any available suitably-sorted path as input, and also consider
@@ -7171,7 +7175,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 				if (path == NULL)
 					continue;
 
-				if (parse->hasAggs)
+				if (QueryHasAggs(parse))
 					add_path(partially_grouped_rel, (Path *)
 							 create_agg_path(root,
 											 partially_grouped_rel,
@@ -7228,7 +7232,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 				if (path == NULL)
 					continue;
 
-				if (parse->hasAggs)
+				if (QueryHasAggs(parse))
 					add_partial_path(partially_grouped_rel, (Path *)
 									 create_agg_path(root,
 													 partially_grouped_rel,
@@ -7258,7 +7262,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	if (can_hash && cheapest_total_path != NULL)
 	{
 		/* Checked above */
-		Assert(parse->hasAggs || parse->groupClause);
+		Assert(QueryHasAggs(parse) || parse->groupClause);
 
 		add_path(partially_grouped_rel, (Path *)
 				 create_agg_path(root,
@@ -7401,7 +7405,7 @@ can_partial_agg(PlannerInfo *root)
 {
 	Query	   *parse = root->parse;
 
-	if (!parse->hasAggs && parse->groupClause == NIL)
+	if (!QueryHasAggs(parse) && parse->groupClause == NIL)
 	{
 		/*
 		 * We don't know how to do parallel aggregation unless we have either
@@ -7560,7 +7564,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 	 * cheapest-path fields, which is a good thing because they're bogus right
 	 * now.)
 	 */
-	if (root->parse->hasTargetSRFs)
+	if (QueryHasTargetSRFs(root->parse))
 		adjust_paths_for_srfs(root, rel,
 							  scanjoin_targets,
 							  scanjoin_targets_contain_srfs);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 22a1fa29f3..3c45431d84 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -3600,7 +3600,7 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
 		}
 
 		/* Remember if any Query has RLS quals applied by rewriter */
-		if (query->hasRowSecurity)
+		if (QueryHasRowSecurity(query))
 			context->glob->dependsOnRole = true;
 
 		/* Collect relation OIDs in this Query's rtable */
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 47e14723d2..e65a1a1740 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1562,11 +1562,11 @@ simplify_EXISTS_query(PlannerInfo *root, Query *query)
 	 */
 	if (query->commandType != CMD_SELECT ||
 		query->setOperations ||
-		query->hasAggs ||
+		QueryHasAggs(query) ||
 		query->groupingSets ||
-		query->hasWindowFuncs ||
-		query->hasTargetSRFs ||
-		query->hasModifyingCTE ||
+		QueryHasWindowFuncs(query) ||
+		QueryHasTargetSRFs(query) ||
+		QueryHasModifyingCTE(query) ||
 		query->havingQual ||
 		query->limitOffset ||
 		query->rowMarks)
@@ -1618,7 +1618,7 @@ simplify_EXISTS_query(PlannerInfo *root, Query *query)
 	query->windowClause = NIL;
 	query->distinctClause = NIL;
 	query->sortClause = NIL;
-	query->hasDistinctOn = false;
+	QueryClearFlag(query, HAS_DISTINCT_ON);
 
 	return true;
 }
@@ -1779,7 +1779,7 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
 	if (contain_vars_of_level((Node *) newWhere, 1) ||
 		contain_vars_of_level((Node *) rightargs, 1))
 		return NULL;
-	if (root->parse->hasAggs &&
+	if (QueryHasAggs(root->parse) &&
 		(contain_aggs_of_level((Node *) newWhere, 1) ||
 		 contain_aggs_of_level((Node *) rightargs, 1)))
 		return NULL;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index aa83dd3636..5b85aa8ff9 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -49,7 +49,7 @@ typedef struct pullup_replace_vars_context
 	RangeTblEntry *target_rte;	/* RTE of subquery */
 	Relids		relids;			/* relids within subquery, as numbered after
 								 * pullup (set only if target_rte->lateral) */
-	bool	   *outer_hasSubLinks;	/* -> outer query's hasSubLinks */
+	int		   *outer_flags;	/* -> outer query's flags */
 	int			varno;			/* varno of subquery */
 	bool		wrap_non_vars;	/* do we need all non-Var outputs to be PHVs? */
 	Node	  **rv_cache;		/* cache for results with PHVs */
@@ -168,7 +168,7 @@ transform_MERGE_to_join(Query *parse)
 	 * outer join so that we process all unmatched tuples from the source
 	 * relation.  If none exist, we can use an inner join.
 	 */
-	if (parse->mergeUseOuterJoin)
+	if (QueryMergeUseOuterJoin(parse))
 		jointype = JOIN_RIGHT;
 	else
 		jointype = JOIN_INNER;
@@ -1028,7 +1028,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	 * Pull up any SubLinks within the subquery's quals, so that we don't
 	 * leave unoptimized SubLinks behind.
 	 */
-	if (subquery->hasSubLinks)
+	if (QueryHasSubLinks(subquery))
 		pull_up_sublinks(subroot);
 
 	/*
@@ -1119,7 +1119,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 												  true, true);
 	else						/* won't need relids */
 		rvcontext.relids = NULL;
-	rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
+	rvcontext.outer_flags = &parse->flags;
 	rvcontext.varno = varno;
 	/* this flag will be set below, if needed */
 	rvcontext.wrap_non_vars = false;
@@ -1260,10 +1260,14 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	 * and VALUES RTEs copied up from the subquery.  So it's necessary to copy
 	 * subquery->hasSubLinks anyway.  Perhaps this can be improved someday.
 	 */
-	parse->hasSubLinks |= subquery->hasSubLinks;
+	QueryCondSetFlag(parse,
+			QueryHasSubLinks(parse) || QueryHasSubLinks(subquery),
+			HAS_SUB_LINKS);
 
 	/* If subquery had any RLS conditions, now main query does too */
-	parse->hasRowSecurity |= subquery->hasRowSecurity;
+	QueryCondSetFlag(parse,
+			QueryHasRowSecurity(parse) || QueryHasRowSecurity(subquery),
+			HAS_ROW_SECURITY);
 
 	/*
 	 * subquery won't be pulled up if it hasAggs, hasWindowFuncs, or
@@ -1512,9 +1516,9 @@ is_simple_subquery(PlannerInfo *root, Query *subquery, RangeTblEntry *rte,
 	 * that case the locking was originally declared in the upper query
 	 * anyway.
 	 */
-	if (subquery->hasAggs ||
-		subquery->hasWindowFuncs ||
-		subquery->hasTargetSRFs ||
+	if (QueryHasAggs(subquery) ||
+		QueryHasWindowFuncs(subquery) ||
+		QueryHasTargetSRFs(subquery) ||
 		subquery->groupClause ||
 		subquery->groupingSets ||
 		subquery->havingQual ||
@@ -1522,7 +1526,7 @@ is_simple_subquery(PlannerInfo *root, Query *subquery, RangeTblEntry *rte,
 		subquery->distinctClause ||
 		subquery->limitOffset ||
 		subquery->limitCount ||
-		subquery->hasForUpdate ||
+		QueryHasForUpdate(subquery) ||
 		subquery->cteList)
 		return false;
 
@@ -1665,7 +1669,7 @@ pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
 	rvcontext.targetlist = tlist;
 	rvcontext.target_rte = rte;
 	rvcontext.relids = NULL;
-	rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
+	rvcontext.outer_flags = &parse->flags;
 	rvcontext.varno = varno;
 	rvcontext.wrap_non_vars = false;
 	/* initialize cache array with indexes 0 .. length(tlist) */
@@ -1826,7 +1830,7 @@ pull_up_constant_function(PlannerInfo *root, Node *jtnode,
 	 */
 	rvcontext.relids = NULL;
 
-	rvcontext.outer_hasSubLinks = &parse->hasSubLinks;
+	rvcontext.outer_flags = &parse->flags;
 	rvcontext.varno = ((RangeTblRef *) jtnode)->rtindex;
 	/* this flag will be set below, if needed */
 	rvcontext.wrap_non_vars = false;
@@ -2302,7 +2306,7 @@ pullup_replace_vars(Node *expr, pullup_replace_vars_context *context)
 								 context->varno, 0,
 								 pullup_replace_vars_callback,
 								 (void *) context,
-								 context->outer_hasSubLinks);
+								 context->outer_flags);
 }
 
 static Node *
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index c939b5a45f..354aae9464 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -340,7 +340,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 		{
 			if (subquery->groupClause || subquery->groupingSets ||
 				subquery->distinctClause ||
-				subroot->hasHavingQual || subquery->hasAggs)
+				subroot->hasHavingQual || QueryHasAggs(subquery))
 				*pNumGroups = subpath->rows;
 			else
 				*pNumGroups = estimate_num_groups(subroot,
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index edc25d712e..3d513194a3 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4672,10 +4672,10 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 	 */
 	if (!IsA(querytree, Query) ||
 		querytree->commandType != CMD_SELECT ||
-		querytree->hasAggs ||
-		querytree->hasWindowFuncs ||
-		querytree->hasTargetSRFs ||
-		querytree->hasSubLinks ||
+		QueryHasAggs(querytree) ||
+		QueryHasWindowFuncs(querytree) ||
+		QueryHasTargetSRFs(querytree) ||
+		QueryHasSubLinks(querytree) ||
 		querytree->cteList ||
 		querytree->rtable ||
 		querytree->jointree->fromlist ||
@@ -5298,7 +5298,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 	 * We must also notice if the inserted query adds a dependency on the
 	 * calling role due to RLS quals.
 	 */
-	if (querytree->hasRowSecurity)
+	if (QueryHasRowSecurity(querytree))
 		root->glob->dependsOnRole = true;
 
 	return querytree;
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 844fc30978..4e1d2a5e1c 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -756,9 +756,9 @@ flatten_join_alias_vars(PlannerInfo *root, Query *query, Node *node)
 	context.query = query;
 	context.sublevels_up = 0;
 	/* flag whether join aliases could possibly contain SubLinks */
-	context.possible_sublink = query->hasSubLinks;
+	context.possible_sublink = QueryHasSubLinks(query);
 	/* if hasSubLinks is already true, no need to work hard */
-	context.inserted_sublink = query->hasSubLinks;
+	context.inserted_sublink = QueryHasSubLinks(query);
 
 	return flatten_join_alias_vars_mutator(node, &context);
 }
@@ -881,12 +881,13 @@ flatten_join_alias_vars_mutator(Node *node,
 
 		context->sublevels_up++;
 		save_inserted_sublink = context->inserted_sublink;
-		context->inserted_sublink = ((Query *) node)->hasSubLinks;
+		context->inserted_sublink = QueryHasSubLinks(((Query *) node));
 		newnode = query_tree_mutator((Query *) node,
 									 flatten_join_alias_vars_mutator,
 									 (void *) context,
 									 QTW_IGNORE_JOINALIASES);
-		newnode->hasSubLinks |= context->inserted_sublink;
+		if (context->inserted_sublink)
+			QuerySetFlag(newnode, HAS_SUB_LINKS);
 		context->inserted_sublink = save_inserted_sublink;
 		context->sublevels_up--;
 		return (Node *) newnode;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index dbdf6bf896..7b67016d36 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -420,7 +420,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
 
 	/* Mark as original query until we learn differently */
 	result->querySource = QSRC_ORIGINAL;
-	result->canSetTag = true;
+	QuerySetFlag(result, CAN_SET_TAG);
 
 	return result;
 }
@@ -519,9 +519,9 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
 	{
-		qry->hasRecursive = stmt->withClause->recursive;
+		QueryCondSetFlag(qry, stmt->withClause->recursive, HAS_RECURSIVE);
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
-		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
+		QueryCondSetFlag(qry, pstate->p_hasModifyingCTE, HAS_MODIFYING_CTE);
 	}
 
 	/* set up range table with just the result rel */
@@ -560,10 +560,10 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	qry->rteperminfos = pstate->p_rteperminfos;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
-	qry->hasSubLinks = pstate->p_hasSubLinks;
-	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
-	qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
-	qry->hasAggs = pstate->p_hasAggs;
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
+	QueryCondSetFlag(qry, pstate->p_hasWindowFuncs, HAS_WINDOW_FUNCS);
+	QueryCondSetFlag(qry, pstate->p_hasTargetSRFs, HAS_TARGET_SRFS);
+	QueryCondSetFlag(qry, pstate->p_hasAggs, HAS_AGGS);
 
 	assign_query_collations(pstate, qry);
 
@@ -607,9 +607,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
 	{
-		qry->hasRecursive = stmt->withClause->recursive;
+		QueryCondSetFlag(qry, stmt->withClause->recursive, HAS_RECURSIVE);
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
-		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
+		QueryCondSetFlag(qry, pstate->p_hasModifyingCTE, HAS_MODIFYING_CTE);
 	}
 
 	qry->override = stmt->override;
@@ -987,8 +987,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	qry->rteperminfos = pstate->p_rteperminfos;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
-	qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
-	qry->hasSubLinks = pstate->p_hasSubLinks;
+	QueryCondSetFlag(qry, pstate->p_hasTargetSRFs, HAS_TARGET_SRFS);
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
 
 	assign_query_collations(pstate, qry);
 
@@ -1335,9 +1335,9 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
 	{
-		qry->hasRecursive = stmt->withClause->recursive;
+		QueryCondSetFlag(qry, stmt->withClause->recursive, HAS_RECURSIVE);
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
-		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
+		QueryCondSetFlag(qry, pstate->p_hasModifyingCTE, HAS_MODIFYING_CTE);
 	}
 
 	/* Complain if we get called from someplace where INTO is not allowed */
@@ -1396,7 +1396,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	if (stmt->distinctClause == NIL)
 	{
 		qry->distinctClause = NIL;
-		qry->hasDistinctOn = false;
+		QueryClearFlag(qry, HAS_DISTINCT_ON);
 	}
 	else if (linitial(stmt->distinctClause) == NULL)
 	{
@@ -1405,7 +1405,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 													  &qry->targetList,
 													  qry->sortClause,
 													  false);
-		qry->hasDistinctOn = false;
+		QueryClearFlag(qry, HAS_DISTINCT_ON);
 	}
 	else
 	{
@@ -1414,7 +1414,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 														stmt->distinctClause,
 														&qry->targetList,
 														qry->sortClause);
-		qry->hasDistinctOn = true;
+		QuerySetFlag(qry, HAS_DISTINCT_ON);
 	}
 
 	/* transform LIMIT */
@@ -1439,10 +1439,11 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->rteperminfos = pstate->p_rteperminfos;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
-	qry->hasSubLinks = pstate->p_hasSubLinks;
-	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
-	qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
-	qry->hasAggs = pstate->p_hasAggs;
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
+	QueryCondSetFlag(qry, pstate->p_hasWindowFuncs, HAS_WINDOW_FUNCS);
+	QueryCondSetFlag(qry, pstate->p_hasTargetSRFs, HAS_TARGET_SRFS);
+	QueryCondSetFlag(qry, pstate->p_hasAggs, HAS_AGGS);
+
 
 	foreach(l, stmt->lockingClause)
 	{
@@ -1498,9 +1499,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
 	{
-		qry->hasRecursive = stmt->withClause->recursive;
+		QueryCondSetFlag(qry, stmt->withClause->recursive, HAS_RECURSIVE);
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
-		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
+		QueryCondSetFlag(qry, pstate->p_hasModifyingCTE, HAS_MODIFYING_CTE);
 	}
 
 	/*
@@ -1668,7 +1669,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 	qry->rteperminfos = pstate->p_rteperminfos;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
-	qry->hasSubLinks = pstate->p_hasSubLinks;
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
 
 	assign_query_collations(pstate, qry);
 
@@ -1765,9 +1766,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	/* Process the WITH clause independently of all else */
 	if (withClause)
 	{
-		qry->hasRecursive = withClause->recursive;
+		QueryCondSetFlag(qry, withClause->recursive, HAS_RECURSIVE);
 		qry->cteList = transformWithClause(pstate, withClause);
-		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
+		QueryCondSetFlag(qry, pstate->p_hasModifyingCTE, HAS_MODIFYING_CTE);
 	}
 
 	/*
@@ -1915,10 +1916,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->rteperminfos = pstate->p_rteperminfos;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
-	qry->hasSubLinks = pstate->p_hasSubLinks;
-	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
-	qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
-	qry->hasAggs = pstate->p_hasAggs;
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
+	QueryCondSetFlag(qry, pstate->p_hasWindowFuncs, HAS_WINDOW_FUNCS);
+	QueryCondSetFlag(qry, pstate->p_hasTargetSRFs, HAS_TARGET_SRFS);
+	QueryCondSetFlag(qry, pstate->p_hasAggs, HAS_AGGS);
 
 	foreach(l, lockingClause)
 	{
@@ -2379,7 +2380,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt)
 	Query	   *qry = makeNode(Query);
 
 	qry->commandType = CMD_SELECT;
-	qry->isReturn = true;
+	QuerySetFlag(qry, IS_RETURN);
 
 	qry->targetList = list_make1(makeTargetEntry((Expr *) transformExpr(pstate, stmt->returnval, EXPR_KIND_SELECT_TARGET),
 												 1, NULL, false));
@@ -2389,10 +2390,10 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt)
 	qry->rtable = pstate->p_rtable;
 	qry->rteperminfos = pstate->p_rteperminfos;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
-	qry->hasSubLinks = pstate->p_hasSubLinks;
-	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
-	qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
-	qry->hasAggs = pstate->p_hasAggs;
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
+	QueryCondSetFlag(qry, pstate->p_hasWindowFuncs, HAS_WINDOW_FUNCS);
+	QueryCondSetFlag(qry, pstate->p_hasTargetSRFs, HAS_TARGET_SRFS);
+	QueryCondSetFlag(qry, pstate->p_hasAggs, HAS_AGGS);
 
 	assign_query_collations(pstate, qry);
 
@@ -2417,9 +2418,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
 	{
-		qry->hasRecursive = stmt->withClause->recursive;
+		QueryCondSetFlag(qry, stmt->withClause->recursive, HAS_RECURSIVE);
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
-		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
+		QueryCondSetFlag(qry, pstate->p_hasModifyingCTE, HAS_MODIFYING_CTE);
 	}
 
 	qry->resultRelation = setTargetTable(pstate, stmt->relation,
@@ -2457,8 +2458,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	qry->rteperminfos = pstate->p_rteperminfos;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
-	qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
-	qry->hasSubLinks = pstate->p_hasSubLinks;
+	QueryCondSetFlag(qry, pstate->p_hasTargetSRFs, HAS_TARGET_SRFS);
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
+
 
 	assign_query_collations(pstate, qry);
 
@@ -2781,7 +2783,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
 	if (sstmt->distinctClause == NIL)
 	{
 		qry->distinctClause = NIL;
-		qry->hasDistinctOn = false;
+		QueryClearFlag(qry, HAS_DISTINCT_ON);
 	}
 	else if (linitial(sstmt->distinctClause) == NULL)
 	{
@@ -2790,7 +2792,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
 													  &qry->targetList,
 													  qry->sortClause,
 													  false);
-		qry->hasDistinctOn = false;
+		QueryClearFlag(qry, HAS_DISTINCT_ON);
 	}
 	else
 	{
@@ -2799,7 +2801,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
 														sstmt->distinctClause,
 														&qry->targetList,
 														qry->sortClause);
-		qry->hasDistinctOn = true;
+		QuerySetFlag(qry, HAS_DISTINCT_ON);
 	}
 
 	/* transform LIMIT */
@@ -2820,10 +2822,10 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
 	qry->rteperminfos = pstate->p_rteperminfos;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
-	qry->hasSubLinks = pstate->p_hasSubLinks;
-	qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
-	qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
-	qry->hasAggs = pstate->p_hasAggs;
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
+	QueryCondSetFlag(qry, pstate->p_hasWindowFuncs, HAS_WINDOW_FUNCS);
+	QueryCondSetFlag(qry, pstate->p_hasTargetSRFs, HAS_TARGET_SRFS);
+	QueryCondSetFlag(qry, pstate->p_hasAggs, HAS_AGGS);
 
 	foreach(l, sstmt->lockingClause)
 	{
@@ -2887,7 +2889,7 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 	 * allowed, but the semantics of when the updates occur might be
 	 * surprising.)
 	 */
-	if (query->hasModifyingCTE)
+	if (QueryHasModifyingCTE(query))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH")));
@@ -3014,7 +3016,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
 		 * materialized view. It's not sufficiently clear what the user would
 		 * want to happen if the MV is refreshed or incrementally maintained.
 		 */
-		if (query->hasModifyingCTE)
+		if (QueryHasModifyingCTE(query))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("materialized views must not use data-modifying statements in WITH")));
@@ -3254,21 +3256,21 @@ CheckSelectLocking(Query *qry, LockClauseStrength strength)
 		  translator: %s is a SQL row locking clause such as FOR UPDATE */
 				 errmsg("%s is not allowed with HAVING clause",
 						LCS_asString(strength))));
-	if (qry->hasAggs)
+	if (QueryHasAggs(qry))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 		/*------
 		  translator: %s is a SQL row locking clause such as FOR UPDATE */
 				 errmsg("%s is not allowed with aggregate functions",
 						LCS_asString(strength))));
-	if (qry->hasWindowFuncs)
+	if (QueryHasWindowFuncs(qry))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 		/*------
 		  translator: %s is a SQL row locking clause such as FOR UPDATE */
 				 errmsg("%s is not allowed with window functions",
 						LCS_asString(strength))));
-	if (qry->hasTargetSRFs)
+	if (QueryHasTargetSRFs(qry))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 		/*------
@@ -3523,7 +3525,7 @@ applyLockingClause(Query *qry, Index rtindex,
 
 	/* If it's an explicit clause, make sure hasForUpdate gets set */
 	if (!pushedDown)
-		qry->hasForUpdate = true;
+		QuerySetFlag(qry, HAS_FOR_UPDATE);
 
 	/* Check for pre-existing entry for same rtindex */
 	if ((rc = get_parse_rowmark(qry, rtindex)) != NULL)
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 3c88c9abba..5719b437cb 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -346,7 +346,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
 	 * matters for data-modifying statements, for which the flag will be
 	 * propagated to the ModifyTable plan node.)
 	 */
-	query->canSetTag = false;
+	QueryClearFlag(query, CAN_SET_TAG);
 
 	if (!cte->cterecursive)
 	{
diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c
index 5f6a683ab9..bdecfb2246 100644
--- a/src/backend/parser/parse_merge.c
+++ b/src/backend/parser/parse_merge.c
@@ -108,7 +108,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 	Assert(pstate->p_ctenamespace == NIL);
 
 	qry->commandType = CMD_MERGE;
-	qry->hasRecursive = false;
+	QueryClearFlag(qry, HAS_RECURSIVE);
 
 	/* process the WITH clause independently of all else */
 	if (stmt->withClause)
@@ -119,7 +119,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 					 errmsg("WITH RECURSIVE is not supported for MERGE statement")));
 
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
-		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
+		QueryCondSetFlag(qry, pstate->p_hasModifyingCTE, HAS_MODIFYING_CTE);
 	}
 
 	/*
@@ -259,7 +259,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 
 		/* Use an outer join if any INSERT actions exist in the command. */
 		if (action->commandType == CMD_INSERT)
-			qry->mergeUseOuterJoin = true;
+			QuerySetFlag(qry, MERGE_USE_OUTERJOIN);
 
 		/*
 		 * Set namespace for the specific action. This must be done before
@@ -393,8 +393,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 	/* RETURNING could potentially be added in the future, but not in SQL std */
 	qry->returningList = NULL;
 
-	qry->hasTargetSRFs = false;
-	qry->hasSubLinks = pstate->p_hasSubLinks;
+	QueryClearFlag(qry, HAS_TARGET_SRFS);
+	QueryCondSetFlag(qry, pstate->p_hasSubLinks, HAS_SUB_LINKS);
 
 	assign_query_collations(pstate, qry);
 
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index b449244a53..e0ed3f6148 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -350,7 +350,7 @@ DefineQueryRewrite(const char *rulename,
 		/*
 		 * ... it cannot contain data-modifying WITH ...
 		 */
-		if (query->hasModifyingCTE)
+		if (QueryHasModifyingCTE(query))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("rules on SELECT must not contain data-modifying statements in WITH")));
@@ -685,7 +685,7 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid)
 	}
 
 	/* If there are sublinks, search for them and process their RTEs */
-	if (qry->hasSubLinks)
+	if (QueryHasSubLinks(qry))
 		query_tree_walker(qry, setRuleCheckAsUser_walker, (void *) &userid,
 						  QTW_IGNORE_RC_SUBQUERIES);
 }
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index f60b34deb6..42b23a6d84 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -292,7 +292,7 @@ AcquireRewriteLocks(Query *parsetree,
 	 * Recurse into sublink subqueries, too.  But we already did the ones in
 	 * the rtable and cteList.
 	 */
-	if (parsetree->hasSubLinks)
+	if (QueryHasSubLinks(parsetree))
 		query_tree_walker(parsetree, acquireLocksOnSubLinks, &context,
 						  QTW_IGNORE_RC_SUBQUERIES);
 }
@@ -457,7 +457,7 @@ rewriteRuleAction(Query *parsetree,
 	 * There could have been some SubLinks in parsetree's rtable, in which
 	 * case we'd better mark the sub_action correctly.
 	 */
-	if (parsetree->hasSubLinks && !sub_action->hasSubLinks)
+	if (QueryHasSubLinks(parsetree) && !QueryHasSubLinks(sub_action))
 	{
 		foreach(lc, parsetree->rtable)
 		{
@@ -466,28 +466,28 @@ rewriteRuleAction(Query *parsetree,
 			switch (rte->rtekind)
 			{
 				case RTE_RELATION:
-					sub_action->hasSubLinks =
-						checkExprHasSubLink((Node *) rte->tablesample);
+					QueryCondSetFlag(sub_action,
+						checkExprHasSubLink((Node *) rte->tablesample), HAS_SUB_LINKS);
 					break;
 				case RTE_FUNCTION:
-					sub_action->hasSubLinks =
-						checkExprHasSubLink((Node *) rte->functions);
+					QueryCondSetFlag(sub_action,
+						checkExprHasSubLink((Node *) rte->functions), HAS_SUB_LINKS);
 					break;
 				case RTE_TABLEFUNC:
-					sub_action->hasSubLinks =
-						checkExprHasSubLink((Node *) rte->tablefunc);
+					QueryCondSetFlag(sub_action,
+						checkExprHasSubLink((Node *) rte->tablefunc), HAS_SUB_LINKS);
 					break;
 				case RTE_VALUES:
-					sub_action->hasSubLinks =
-						checkExprHasSubLink((Node *) rte->values_lists);
+					QueryCondSetFlag(sub_action,
+						checkExprHasSubLink((Node *) rte->values_lists), HAS_SUB_LINKS);
 					break;
 				default:
 					/* other RTE types don't contain bare expressions */
 					break;
 			}
-			sub_action->hasSubLinks |=
-				checkExprHasSubLink((Node *) rte->securityQuals);
-			if (sub_action->hasSubLinks)
+			if (checkExprHasSubLink((Node *) rte->securityQuals))
+				QuerySetFlag(sub_action, HAS_SUB_LINKS);
+			if (QueryHasSubLinks(sub_action))
 				break;			/* no need to keep scanning rtable */
 		}
 	}
@@ -499,7 +499,8 @@ rewriteRuleAction(Query *parsetree,
 	 * this is a no-op because RLS conditions aren't added till later, but it
 	 * seems like good future-proofing to do this anyway.)
 	 */
-	sub_action->hasRowSecurity |= parsetree->hasRowSecurity;
+	if(QueryHasRowSecurity(parsetree))
+		QuerySetFlag(sub_action, HAS_ROW_SECURITY);
 
 	/*
 	 * Each rule action's jointree should be the main parsetree's jointree
@@ -546,9 +547,9 @@ rewriteRuleAction(Query *parsetree,
 			 * There could have been some SubLinks in newjointree, in which
 			 * case we'd better mark the sub_action correctly.
 			 */
-			if (parsetree->hasSubLinks && !sub_action->hasSubLinks)
-				sub_action->hasSubLinks =
-					checkExprHasSubLink((Node *) newjointree);
+			if (QueryHasSubLinks(parsetree) && !QueryHasSubLinks(sub_action))
+				QueryCondSetFlag(sub_action,
+								 checkExprHasSubLink((Node *) newjointree), HAS_SUB_LINKS);
 		}
 	}
 
@@ -590,8 +591,10 @@ rewriteRuleAction(Query *parsetree,
 		sub_action->cteList = list_concat(sub_action->cteList,
 										  copyObject(parsetree->cteList));
 		/* ... and don't forget about the associated flags */
-		sub_action->hasRecursive |= parsetree->hasRecursive;
-		sub_action->hasModifyingCTE |= parsetree->hasModifyingCTE;
+		if (QueryHasRecursive(parsetree))
+			QuerySetFlag(sub_action, HAS_RECURSIVE);
+		if (QueryHasModifyingCTE(parsetree))
+			QuerySetFlag(sub_action, HAS_MODIFYING_CTE);
 
 		/*
 		 * If rule_action is different from sub_action (i.e., the rule action
@@ -605,7 +608,7 @@ rewriteRuleAction(Query *parsetree,
 		 * have to increment ctelevelsup in RTEs and SubLinks copied from the
 		 * original query.  For now, it doesn't seem worth the trouble.
 		 */
-		if (sub_action->hasModifyingCTE && rule_action != sub_action)
+		if (QueryHasModifyingCTE(sub_action) && rule_action != sub_action)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("INSERT ... SELECT rule actions are not supported for queries having data-modifying statements in WITH")));
@@ -672,15 +675,16 @@ rewriteRuleAction(Query *parsetree,
 									  rule_action->returningList,
 									  REPLACEVARS_REPORT_ERROR,
 									  0,
-									  &rule_action->hasSubLinks);
+									  &rule_action->flags);
 
 		/*
 		 * There could have been some SubLinks in parsetree's returningList,
 		 * in which case we'd better mark the rule_action correctly.
 		 */
-		if (parsetree->hasSubLinks && !rule_action->hasSubLinks)
-			rule_action->hasSubLinks =
-				checkExprHasSubLink((Node *) rule_action->returningList);
+		if (QueryHasSubLinks(parsetree) && !QueryHasSubLinks(rule_action))
+			QueryCondSetFlag(rule_action,
+							 checkExprHasSubLink((Node *) rule_action->returningList),
+							 HAS_SUB_LINKS);
 	}
 
 	return rule_action;
@@ -2154,7 +2158,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
 	 * Recurse into sublink subqueries, too.  But we already did the ones in
 	 * the rtable and cteList.
 	 */
-	if (parsetree->hasSubLinks)
+	if (QueryHasSubLinks(parsetree))
 		query_tree_walker(parsetree, fireRIRonSubLink, (void *) activeRIRs,
 						  QTW_IGNORE_RC_SUBQUERIES);
 
@@ -2253,9 +2257,9 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
 		 * applies, or if the new quals had sublinks.
 		 */
 		if (hasRowSecurity)
-			parsetree->hasRowSecurity = true;
+			QuerySetFlag(parsetree, HAS_ROW_SECURITY);
 		if (hasSubLinks)
-			parsetree->hasSubLinks = true;
+			QuerySetFlag(parsetree, HAS_SUB_LINKS);
 
 		table_close(rel, NoLock);
 	}
@@ -2311,7 +2315,7 @@ CopyAndAddInvertedQual(Query *parsetree,
 											 REPLACEVARS_CHANGE_VARNO :
 											 REPLACEVARS_SUBSTITUTE_NULL,
 											 rt_index,
-											 &parsetree->hasSubLinks);
+											 &parsetree->flags);
 	/* And attach the fixed qual */
 	AddInvertedQual(parsetree, new_qual);
 
@@ -2419,7 +2423,7 @@ fireRules(Query *parsetree,
 											returning_flag);
 
 			rule_action->querySource = qsrc;
-			rule_action->canSetTag = false; /* might change later */
+			QueryClearFlag(rule_action, CAN_SET_TAG); /* might change later */
 
 			results = lappend(results, rule_action);
 		}
@@ -2622,13 +2626,13 @@ view_query_is_auto_updatable(Query *viewquery, bool check_cols)
 	 * These restrictions ensure that each row of the view corresponds to a
 	 * unique row in the underlying base relation.
 	 */
-	if (viewquery->hasAggs)
+	if (QueryHasAggs(viewquery))
 		return gettext_noop("Views that return aggregate functions are not automatically updatable.");
 
-	if (viewquery->hasWindowFuncs)
+	if (QueryHasWindowFuncs(viewquery))
 		return gettext_noop("Views that return window functions are not automatically updatable.");
 
-	if (viewquery->hasTargetSRFs)
+	if (QueryHasTargetSRFs(viewquery))
 		return gettext_noop("Views that return set-returning functions are not automatically updatable.");
 
 	/*
@@ -3212,7 +3216,7 @@ rewriteTargetView(Query *parsetree, Relation view)
 	 * be any subqueries in the range table or CTEs, so we can skip those, as
 	 * in AcquireRewriteLocks.
 	 */
-	if (viewquery->hasSubLinks)
+	if (QueryHasSubLinks(viewquery))
 	{
 		acquireLocksOnSubLinks_context context;
 
@@ -3473,7 +3477,7 @@ rewriteTargetView(Query *parsetree, Relation view)
 									  tmp_tlist,
 									  REPLACEVARS_REPORT_ERROR,
 									  0,
-									  &parsetree->hasSubLinks);
+									  &parsetree->flags);
 	}
 
 	/*
@@ -3524,8 +3528,8 @@ rewriteTargetView(Query *parsetree, Relation view)
 			 * Make sure that the query is marked correctly if the added qual
 			 * has sublinks.
 			 */
-			if (!parsetree->hasSubLinks)
-				parsetree->hasSubLinks = checkExprHasSubLink(viewqual);
+			if (!QueryHasSubLinks(parsetree))
+				QueryCondSetFlag(parsetree, checkExprHasSubLink(viewqual), HAS_SUB_LINKS);
 		}
 		else
 			AddQual(parsetree, (Node *) viewqual);
@@ -3596,9 +3600,9 @@ rewriteTargetView(Query *parsetree, Relation view)
 				 * case the same qual will have already been added, and this
 				 * check will already have been done.
 				 */
-				if (!parsetree->hasSubLinks &&
+				if (!QueryHasSubLinks(parsetree) &&
 					parsetree->commandType != CMD_UPDATE)
-					parsetree->hasSubLinks = checkExprHasSubLink(wco->qual);
+					QueryCondSetFlag(parsetree, checkExprHasSubLink(wco->qual), HAS_SUB_LINKS);
 			}
 		}
 	}
@@ -3670,7 +3674,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
 						 errmsg("DO INSTEAD NOTIFY rules are not supported for data-modifying statements in WITH")));
 			}
 			/* WITH queries should never be canSetTag */
-			Assert(!ctequery->canSetTag);
+			Assert(!QueryCanSetTag(ctequery));
 			/* Push the single Query back into the CTE node */
 			cte->ctequery = (Node *) ctequery;
 		}
@@ -4211,7 +4215,7 @@ QueryRewrite(Query *parsetree)
 	 * This function is only applied to top-level original queries
 	 */
 	Assert(parsetree->querySource == QSRC_ORIGINAL);
-	Assert(parsetree->canSetTag);
+	Assert(QueryCanSetTag(parsetree));
 
 	/*
 	 * Step 1
@@ -4265,7 +4269,7 @@ QueryRewrite(Query *parsetree)
 
 		if (query->querySource == QSRC_ORIGINAL)
 		{
-			Assert(query->canSetTag);
+			Assert(QueryCanSetTag(query));
 			Assert(!foundOriginalQuery);
 			foundOriginalQuery = true;
 #ifndef USE_ASSERT_CHECKING
@@ -4274,7 +4278,7 @@ QueryRewrite(Query *parsetree)
 		}
 		else
 		{
-			Assert(!query->canSetTag);
+			Assert(!QueryCanSetTag(query));
 			if (query->commandType == origCmdType &&
 				(query->querySource == QSRC_INSTEAD_RULE ||
 				 query->querySource == QSRC_QUAL_INSTEAD_RULE))
@@ -4283,7 +4287,7 @@ QueryRewrite(Query *parsetree)
 	}
 
 	if (!foundOriginalQuery && lastInstead != NULL)
-		lastInstead->canSetTag = true;
+		QuerySetFlag(lastInstead, CAN_SET_TAG);
 
 	return results;
 }
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 76c97a5b28..7fb2859b8f 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -1102,8 +1102,8 @@ AddQual(Query *parsetree, Node *qual)
 	 * Make sure query is marked correctly if added qual has sublinks. Need
 	 * not search qual when query is already marked.
 	 */
-	if (!parsetree->hasSubLinks)
-		parsetree->hasSubLinks = checkExprHasSubLink(copy);
+	if (!QueryHasSubLinks(parsetree))
+		QueryCondSetFlag(parsetree, checkExprHasSubLink(copy), HAS_SUB_LINKS);
 }
 
 
@@ -1334,7 +1334,7 @@ Node *
 replace_rte_variables(Node *node, int target_varno, int sublevels_up,
 					  replace_rte_variables_callback callback,
 					  void *callback_arg,
-					  bool *outer_hasSubLinks)
+					  int *outer_flags)
 {
 	Node	   *result;
 	replace_rte_variables_context context;
@@ -1349,9 +1349,9 @@ replace_rte_variables(Node *node, int target_varno, int sublevels_up,
 	 * detect new sublinks because the query already has some.
 	 */
 	if (node && IsA(node, Query))
-		context.inserted_sublink = ((Query *) node)->hasSubLinks;
-	else if (outer_hasSubLinks)
-		context.inserted_sublink = *outer_hasSubLinks;
+		context.inserted_sublink = QueryHasSubLinks((Query *) node);
+	else if (outer_flags)
+		context.inserted_sublink = QueryFlagIsSet(*outer_flags, HAS_SUB_LINKS);
 	else
 		context.inserted_sublink = false;
 
@@ -1367,9 +1367,9 @@ replace_rte_variables(Node *node, int target_varno, int sublevels_up,
 	if (context.inserted_sublink)
 	{
 		if (result && IsA(result, Query))
-			((Query *) result)->hasSubLinks = true;
-		else if (outer_hasSubLinks)
-			*outer_hasSubLinks = true;
+			QuerySetFlag(((Query *) result), HAS_SUB_LINKS);
+		else if (outer_flags)
+			QueryFlagSet(*outer_flags, HAS_SUB_LINKS);
 		else
 			elog(ERROR, "replace_rte_variables inserted a SubLink, but has noplace to record it");
 	}
@@ -1428,12 +1428,13 @@ replace_rte_variables_mutator(Node *node,
 
 		context->sublevels_up++;
 		save_inserted_sublink = context->inserted_sublink;
-		context->inserted_sublink = ((Query *) node)->hasSubLinks;
+		context->inserted_sublink = QueryHasSubLinks((Query *) node);
 		newnode = query_tree_mutator((Query *) node,
 									 replace_rte_variables_mutator,
 									 (void *) context,
 									 0);
-		newnode->hasSubLinks |= context->inserted_sublink;
+		if (context->inserted_sublink)
+			QuerySetFlag(newnode, HAS_SUB_LINKS);
 		context->inserted_sublink = save_inserted_sublink;
 		context->sublevels_up--;
 		return (Node *) newnode;
@@ -1764,7 +1765,7 @@ ReplaceVarsFromTargetList(Node *node,
 						  List *targetlist,
 						  ReplaceVarsNoMatchOption nomatch_option,
 						  int nomatch_varno,
-						  bool *outer_hasSubLinks)
+						  int *outer_flags)
 {
 	ReplaceVarsFromTargetList_context context;
 
@@ -1776,5 +1777,5 @@ ReplaceVarsFromTargetList(Node *node,
 	return replace_rte_variables(node, target_varno, sublevels_up,
 								 ReplaceVarsFromTargetList_callback,
 								 (void *) &context,
-								 outer_hasSubLinks);
+								 outer_flags);
 }
diff --git a/src/backend/rewrite/rewriteSearchCycle.c b/src/backend/rewrite/rewriteSearchCycle.c
index 911a901808..d3cd02c4e0 100644
--- a/src/backend/rewrite/rewriteSearchCycle.c
+++ b/src/backend/rewrite/rewriteSearchCycle.c
@@ -278,7 +278,7 @@ rewriteSearchAndCycle(CommonTableExpr *cte)
 	 */
 	newq1 = makeNode(Query);
 	newq1->commandType = CMD_SELECT;
-	newq1->canSetTag = true;
+	QuerySetFlag(newq1, CAN_SET_TAG);
 
 	newrte = makeNode(RangeTblEntry);
 	newrte->rtekind = RTE_SUBQUERY;
@@ -365,7 +365,7 @@ rewriteSearchAndCycle(CommonTableExpr *cte)
 	 */
 	newq2 = makeNode(Query);
 	newq2->commandType = CMD_SELECT;
-	newq2->canSetTag = true;
+	QuerySetFlag(newq2, CAN_SET_TAG);
 
 	newrte = makeNode(RangeTblEntry);
 	newrte->rtekind = RTE_SUBQUERY;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 2c63b7875a..18100128cc 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -984,7 +984,7 @@ pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions,
 			/* Utility commands require no planning. */
 			stmt = makeNode(PlannedStmt);
 			stmt->commandType = CMD_UTILITY;
-			stmt->canSetTag = query->canSetTag;
+			stmt->canSetTag = QueryCanSetTag(query);
 			stmt->utilityStmt = query->utilityStmt;
 			stmt->stmt_location = query->stmt_location;
 			stmt->stmt_len = query->stmt_len;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0c45fcf318..329fc05ac4 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -225,11 +225,11 @@ ChoosePortalStrategy(List *stmts)
 		{
 			Query	   *query = (Query *) stmt;
 
-			if (query->canSetTag)
+			if (QueryCanSetTag(query))
 			{
 				if (query->commandType == CMD_SELECT)
 				{
-					if (query->hasModifyingCTE)
+					if (QueryHasModifyingCTE(query))
 						return PORTAL_ONE_MOD_WITH;
 					else
 						return PORTAL_ONE_SELECT;
@@ -283,7 +283,7 @@ ChoosePortalStrategy(List *stmts)
 		{
 			Query	   *query = (Query *) stmt;
 
-			if (query->canSetTag)
+			if (QueryCanSetTag(query))
 			{
 				if (++nSetTag > 1)
 					return PORTAL_MULTI_QUERY;	/* no need to look further */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index a928a8c55d..daec49c286 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -5562,7 +5562,7 @@ get_with_clause(Query *query, deparse_context *context)
 		appendStringInfoChar(buf, ' ');
 	}
 
-	if (query->hasRecursive)
+	if (QueryHasRecursive(query))
 		sep = "WITH RECURSIVE ";
 	else
 		sep = "WITH ";
@@ -5763,7 +5763,7 @@ get_select_query_def(Query *query, deparse_context *context,
 	}
 
 	/* Add FOR [KEY] UPDATE/SHARE clauses if present */
-	if (query->hasForUpdate)
+	if (QueryHasForUpdate(query))
 	{
 		foreach(l, query->rowMarks)
 		{
@@ -5916,7 +5916,7 @@ get_basic_select_query(Query *query, deparse_context *context,
 	/*
 	 * Build up the query string - first we say SELECT
 	 */
-	if (query->isReturn)
+	if (QueryIsReturn(query))
 		appendStringInfoString(buf, "RETURN");
 	else
 		appendStringInfoString(buf, "SELECT");
@@ -5924,7 +5924,7 @@ get_basic_select_query(Query *query, deparse_context *context,
 	/* Add the DISTINCT clause if given */
 	if (query->distinctClause != NIL)
 	{
-		if (query->hasDistinctOn)
+		if (QueryHasDistinctOn(query))
 		{
 			appendStringInfoString(buf, " DISTINCT ON (");
 			sep = "";
@@ -6898,7 +6898,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
 	 * entries.
 	 */
 	ma_sublinks = NIL;
-	if (query->hasSubLinks)		/* else there can't be any */
+	if (QueryHasSubLinks(query))		/* else there can't be any */
 	{
 		foreach(l, targetList)
 		{
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 5194cbf2cc..fcf371622a 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -1384,7 +1384,7 @@ CachedPlanAllowsSimpleValidityCheck(CachedPlanSource *plansource,
 
 		if (query->commandType == CMD_UTILITY)
 			return false;
-		if (query->rtable || query->cteList || query->hasSubLinks)
+		if (query->rtable || query->cteList || QueryHasSubLinks(query))
 			return false;
 	}
 
@@ -1760,7 +1760,7 @@ QueryListGetPrimaryStmt(List *stmts)
 	{
 		Query	   *stmt = lfirst_node(Query, lc);
 
-		if (stmt->canSetTag)
+		if (QueryCanSetTag(stmt))
 			return stmt;
 	}
 	return NULL;
@@ -1907,7 +1907,7 @@ ScanQueryForLocks(Query *parsetree, bool acquire)
 	 * Recurse into sublink subqueries, too.  But we already did the ones in
 	 * the rtable and cteList.
 	 */
-	if (parsetree->hasSubLinks)
+	if (QueryHasSubLinks(parsetree))
 	{
 		query_tree_walker(parsetree, ScanQueryWalker,
 						  (void *) &acquire,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d5b08ded44..ecaefe9b1a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -129,9 +129,6 @@ typedef struct Query
 	 */
 	uint64		queryId pg_node_attr(equal_ignore, query_jumble_ignore, read_write_ignore, read_as(0));
 
-	/* do I set the command result tag? */
-	bool		canSetTag pg_node_attr(query_jumble_ignore);
-
 	Node	   *utilityStmt;	/* non-null if commandType == CMD_UTILITY */
 
 	/*
@@ -141,26 +138,7 @@ typedef struct Query
 	 */
 	int			resultRelation pg_node_attr(query_jumble_ignore);
 
-	/* has aggregates in tlist or havingQual */
-	bool		hasAggs pg_node_attr(query_jumble_ignore);
-	/* has window functions in tlist */
-	bool		hasWindowFuncs pg_node_attr(query_jumble_ignore);
-	/* has set-returning functions in tlist */
-	bool		hasTargetSRFs pg_node_attr(query_jumble_ignore);
-	/* has subquery SubLink */
-	bool		hasSubLinks pg_node_attr(query_jumble_ignore);
-	/* distinctClause is from DISTINCT ON */
-	bool		hasDistinctOn pg_node_attr(query_jumble_ignore);
-	/* WITH RECURSIVE was specified */
-	bool		hasRecursive pg_node_attr(query_jumble_ignore);
-	/* has INSERT/UPDATE/DELETE in WITH */
-	bool		hasModifyingCTE pg_node_attr(query_jumble_ignore);
-	/* FOR [KEY] UPDATE/SHARE was specified */
-	bool		hasForUpdate pg_node_attr(query_jumble_ignore);
-	/* rewriter has applied some RLS policy */
-	bool		hasRowSecurity pg_node_attr(query_jumble_ignore);
-	/* is a RETURN statement */
-	bool		isReturn pg_node_attr(query_jumble_ignore);
+	int			flags pg_node_attr(query_jumble_ignore);
 
 	List	   *cteList;		/* WITH list (of CommonTableExpr's) */
 
@@ -175,8 +153,6 @@ typedef struct Query
 								 * also USING clause for MERGE */
 
 	List	   *mergeActionList;	/* list of actions for MERGE (only) */
-	/* whether to use outer join */
-	bool		mergeUseOuterJoin pg_node_attr(query_jumble_ignore);
 
 	List	   *targetList;		/* target list (of TargetEntry) */
 
@@ -230,6 +206,60 @@ typedef struct Query
 	int			stmt_len pg_node_attr(query_jumble_ignore);
 } Query;
 
+/* do I set the command result tag? */
+#define QUERY_CAN_SET_TAG				(1)
+/* has aggregates in tlist or havingQual */
+#define QUERY_HAS_AGGS					(1 << 1)
+/* has window functions in tlist */
+#define QUERY_HAS_WINDOW_FUNCS			(1 << 2)
+/* has set-returning functions in tlist */
+#define QUERY_HAS_TARGET_SRFS			(1 << 3)
+/* has subquery SubLink */
+#define QUERY_HAS_SUB_LINKS				(1 << 4)
+/* distinctClause is from DISTINCT ON */
+#define QUERY_HAS_DISTINCT_ON			(1 << 5)
+/* WITH RECURSIVE was specified */
+#define QUERY_HAS_RECURSIVE				(1 << 6)
+/* has INSERT/UPDATE/DELETE in WITH */
+#define QUERY_HAS_MODIFYING_CTE			(1 << 7)
+/* FOR [KEY] UPDATE/SHARE was specified */
+#define QUERY_HAS_FOR_UPDATE			(1 << 8)
+/* rewriter has applied some RLS policy */
+#define QUERY_HAS_ROW_SECURITY			(1 << 9)
+/* is a RETURN statement */
+#define QUERY_IS_RETURN					(1 << 10)
+/* whether to use outer join */
+#define QUERY_MERGE_USE_OUTERJOIN		(1 << 11)
+
+#define	QueryCanSetTag(query)			((query)->flags & QUERY_CAN_SET_TAG)
+#define	QueryHasAggs(query)				((query)->flags & QUERY_HAS_AGGS)
+#define	QueryHasWindowFuncs(query)		((query)->flags & QUERY_HAS_WINDOW_FUNCS)
+#define	QueryHasTargetSRFs(query)		((query)->flags & QUERY_HAS_TARGET_SRFS)
+#define	QueryHasSubLinks(query)			((query)->flags & QUERY_HAS_SUB_LINKS)
+#define	QueryHasDistinctOn(query)		((query)->flags & QUERY_HAS_DISTINCT_ON)
+#define	QueryHasRecursive(query)		((query)->flags & QUERY_HAS_RECURSIVE)
+#define	QueryHasModifyingCTE(query)		((query)->flags & QUERY_HAS_MODIFYING_CTE)
+#define	QueryHasForUpdate(query)		((query)->flags & QUERY_HAS_FOR_UPDATE)
+#define	QueryHasRowSecurity(query)		((query)->flags & QUERY_HAS_ROW_SECURITY)
+#define	QueryIsReturn(query)			((query)->flags & QUERY_IS_RETURN)
+#define	QueryMergeUseOuterJoin(query)	((query)->flags & QUERY_MERGE_USE_OUTERJOIN)
+
+#define	QueryFlagSet(flags, fname)		((flags) |= (QUERY_##fname))
+#define	QuerySetFlag(query, fname)		QueryFlagSet((query)->flags, fname)
+
+#define	QueryFlagIsSet(flags, fname)	((flags) & (QUERY_##fname))
+
+#define	QueryFlagClear(flags, fname)	((flags) &= ~(QUERY_##fname))
+#define	QueryClearFlag(query, fname)	QueryFlagClear((query)->flags, fname)
+
+#define	QueryCondSetFlag(query, cond, pname) \
+	{ \
+		if (cond) \
+			QuerySetFlag(query, pname); \
+		else \
+			QueryClearFlag(query, pname); \
+	}
+
 
 /****************************************************************************
  *	Supporting data structures for Parse Trees
diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h
index ac6d2049e8..d04443ef97 100644
--- a/src/include/rewrite/rewriteManip.h
+++ b/src/include/rewrite/rewriteManip.h
@@ -76,7 +76,7 @@ extern Node *replace_rte_variables(Node *node,
 								   int target_varno, int sublevels_up,
 								   replace_rte_variables_callback callback,
 								   void *callback_arg,
-								   bool *outer_hasSubLinks);
+								   int *outer_flags);
 extern Node *replace_rte_variables_mutator(Node *node,
 										   replace_rte_variables_context *context);
 
@@ -91,6 +91,6 @@ extern Node *ReplaceVarsFromTargetList(Node *node,
 									   List *targetlist,
 									   ReplaceVarsNoMatchOption nomatch_option,
 									   int nomatch_varno,
-									   bool *outer_hasSubLinks);
+									   int *outer_flags);
 
 #endif							/* REWRITEMANIP_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 6d1691340c..e7f3652863 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8020,10 +8020,10 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
 	 * inlining a SQL function; otherwise, inlining could change our
 	 * conclusion about whether an expression is simple, which we don't want.)
 	 */
-	if (query->hasAggs ||
-		query->hasWindowFuncs ||
-		query->hasTargetSRFs ||
-		query->hasSubLinks ||
+	if (QueryHasAggs(query) ||
+		QueryHasWindowFuncs(query) ||
+		QueryHasTargetSRFs(query) ||
+		QueryHasSubLinks(query) ||
 		query->cteList ||
 		query->jointree->fromlist ||
 		query->jointree->quals ||