diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c
index 620414a1ed..50f59ff0b9 100644
--- a/src/backend/executor/nodeRecursiveunion.c
+++ b/src/backend/executor/nodeRecursiveunion.c
@@ -104,7 +104,9 @@ ExecRecursiveUnion(PlanState *pstate)
 			/* Each non-duplicate tuple goes to the working table ... */
 			tuplestore_puttupleslot(node->working_table, slot);
 			/* ... and to the caller */
-			return slot;
+			if (!node->iterating) {
+				return slot;
+			}
 		}
 		node->recursing = true;
 	}
@@ -116,8 +118,33 @@ ExecRecursiveUnion(PlanState *pstate)
 		if (TupIsNull(slot))
 		{
 			/* Done if there's nothing in the intermediate table */
-			if (node->intermediate_empty)
+			if (node->intermediate_empty) {
+				if (!node->iterating) {
+					return NULL;
+				}
+
+				if (!TupIsNull(pstate->ps_ResultTupleSlot)) {
+					tuplestore_select_read_pointer(node->working_table, 1);
+					(void) tuplestore_gettupleslot(node->working_table, true, true, pstate->ps_ResultTupleSlot);
+					tuplestore_select_read_pointer(node->working_table, 0);
+					return pstate->ps_ResultTupleSlot;
+				} else {
+					tuplestore_alloc_read_pointer(node->working_table, 0);
+
+					tuplestore_rescan(node->working_table);
+
+					tuplestore_copy_read_pointer(node->working_table, 0, 1);
+					tuplestore_select_read_pointer(node->working_table, 1);
+
+					tuplestore_gettupleslot(node->working_table, true, true, pstate->ps_ResultTupleSlot);
+					tuplestore_select_read_pointer(node->working_table, 0);
+					return pstate->ps_ResultTupleSlot;
+				}
+
+				 return slot;
+
 				break;
+			}
 
 			/* done with old working table ... */
 			tuplestore_end(node->working_table);
@@ -153,10 +180,16 @@ ExecRecursiveUnion(PlanState *pstate)
 		node->intermediate_empty = false;
 		tuplestore_puttupleslot(node->intermediate_table, slot);
 		/* ... and return it */
-		return slot;
+		if (!node->iterating) {
+			return slot;
+		}
 	}
 
-	return NULL;
+	if (node->iterating) {
+		return pstate->ps_ResultTupleSlot;
+	} else {
+		return NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
@@ -188,6 +221,7 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
 
 	/* initialize processing state */
 	rustate->recursing = false;
+	rustate->iterating = node->iterative;
 	rustate->intermediate_empty = true;
 	rustate->working_table = tuplestore_begin_heap(false, false, work_mem);
 	rustate->intermediate_table = tuplestore_begin_heap(false, false, work_mem);
@@ -233,6 +267,11 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
 	 */
 	ExecInitResultTypeTL(&rustate->ps);
 
+	if (rustate->iterating) {
+		/* XXX FIX */
+		rustate->ps.ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable, NULL, &TTSOpsMinimalTuple);
+	}
+
 	/*
 	 * Initialize result tuple type.  (Note: we have to set up the result type
 	 * before initializing child nodes, because nodeWorktablescan.c expects it
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 491452ae2d..89ccc82fd6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2556,6 +2556,7 @@ _copyWithClause(const WithClause *from)
 
 	COPY_NODE_FIELD(ctes);
 	COPY_SCALAR_FIELD(recursive);
+	COPY_SCALAR_FIELD(iterative);
 	COPY_LOCATION_FIELD(location);
 
 	return newnode;
@@ -2599,6 +2600,7 @@ _copyCommonTableExpr(const CommonTableExpr *from)
 	COPY_NODE_FIELD(ctequery);
 	COPY_LOCATION_FIELD(location);
 	COPY_SCALAR_FIELD(cterecursive);
+	COPY_SCALAR_FIELD(cteiterative);
 	COPY_SCALAR_FIELD(cterefcount);
 	COPY_NODE_FIELD(ctecolnames);
 	COPY_NODE_FIELD(ctecoltypes);
@@ -3066,6 +3068,7 @@ _copyQuery(const Query *from)
 	COPY_SCALAR_FIELD(hasSubLinks);
 	COPY_SCALAR_FIELD(hasDistinctOn);
 	COPY_SCALAR_FIELD(hasRecursive);
+	COPY_SCALAR_FIELD(hasIterative);
 	COPY_SCALAR_FIELD(hasModifyingCTE);
 	COPY_SCALAR_FIELD(hasForUpdate);
 	COPY_SCALAR_FIELD(hasRowSecurity);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bb1565467d..e6da740d6d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -468,6 +468,7 @@ _outRecursiveUnion(StringInfo str, const RecursiveUnion *node)
 	WRITE_OID_ARRAY(dupOperators, node->numCols);
 	WRITE_OID_ARRAY(dupCollations, node->numCols);
 	WRITE_LONG_FIELD(numGroups);
+	WRITE_BOOL_FIELD(iterative);
 }
 
 static void
@@ -2254,6 +2255,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
 	WRITE_BOOL_FIELD(hasHavingQual);
 	WRITE_BOOL_FIELD(hasPseudoConstantQuals);
 	WRITE_BOOL_FIELD(hasRecursion);
+	WRITE_BOOL_FIELD(hasIteration);
 	WRITE_INT_FIELD(wt_param_id);
 	WRITE_BITMAPSET_FIELD(curOuterRels);
 	WRITE_NODE_FIELD(curOuterParams);
@@ -2942,6 +2944,7 @@ _outQuery(StringInfo str, const Query *node)
 	WRITE_BOOL_FIELD(hasSubLinks);
 	WRITE_BOOL_FIELD(hasDistinctOn);
 	WRITE_BOOL_FIELD(hasRecursive);
+	WRITE_BOOL_FIELD(hasIterative);
 	WRITE_BOOL_FIELD(hasModifyingCTE);
 	WRITE_BOOL_FIELD(hasForUpdate);
 	WRITE_BOOL_FIELD(hasRowSecurity);
@@ -3042,6 +3045,7 @@ _outWithClause(StringInfo str, const WithClause *node)
 
 	WRITE_NODE_FIELD(ctes);
 	WRITE_BOOL_FIELD(recursive);
+	WRITE_BOOL_FIELD(iterative);
 	WRITE_LOCATION_FIELD(location);
 }
 
@@ -3056,6 +3060,7 @@ _outCommonTableExpr(StringInfo str, const CommonTableExpr *node)
 	WRITE_NODE_FIELD(ctequery);
 	WRITE_LOCATION_FIELD(location);
 	WRITE_BOOL_FIELD(cterecursive);
+	WRITE_BOOL_FIELD(cteiterative);
 	WRITE_INT_FIELD(cterefcount);
 	WRITE_NODE_FIELD(ctecolnames);
 	WRITE_NODE_FIELD(ctecoltypes);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 644e0f5fc7..2333b7f2bf 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -260,6 +260,7 @@ _readQuery(void)
 	READ_BOOL_FIELD(hasSubLinks);
 	READ_BOOL_FIELD(hasDistinctOn);
 	READ_BOOL_FIELD(hasRecursive);
+	READ_BOOL_FIELD(hasIterative);
 	READ_BOOL_FIELD(hasModifyingCTE);
 	READ_BOOL_FIELD(hasForUpdate);
 	READ_BOOL_FIELD(hasRowSecurity);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 255f56b827..26d215b6ec 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2302,7 +2302,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	/* Generate a subroot and Paths for the subquery */
 	rel->subroot = subquery_planner(root->glob, subquery,
 									root,
-									false, tuple_fraction);
+									false, tuple_fraction, false);
 
 	/* Isolate the params needed by this specific subplan */
 	rel->subplan_params = root->plan_params;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 9941dfe65e..7586edc6c7 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -214,7 +214,8 @@ static RecursiveUnion *make_recursive_union(List *tlist,
 											Plan *righttree,
 											int wtParam,
 											List *distinctList,
-											long numGroups);
+											long numGroups,
+											bool iterate);
 static BitmapAnd *make_bitmap_and(List *bitmapplans);
 static BitmapOr *make_bitmap_or(List *bitmapplans);
 static NestLoop *make_nestloop(List *tlist,
@@ -2588,7 +2589,8 @@ create_recursiveunion_plan(PlannerInfo *root, RecursiveUnionPath *best_path)
 								rightplan,
 								best_path->wtParam,
 								best_path->distinctList,
-								numGroups);
+								numGroups,
+								best_path->iterative);
 
 	copy_generic_path_info(&plan->plan, (Path *) best_path);
 
@@ -5549,7 +5551,8 @@ make_recursive_union(List *tlist,
 					 Plan *righttree,
 					 int wtParam,
 					 List *distinctList,
-					 long numGroups)
+					 long numGroups,
+					 bool iterate)
 {
 	RecursiveUnion *node = makeNode(RecursiveUnion);
 	Plan	   *plan = &node->plan;
@@ -5595,6 +5598,7 @@ make_recursive_union(List *tlist,
 		node->dupCollations = dupCollations;
 	}
 	node->numGroups = numGroups;
+	node->iterative = iterate;
 
 	return node;
 }
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index e664eb18c0..c191325bf2 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -315,6 +315,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 	glob->lastPlanNodeId = 0;
 	glob->transientPlan = false;
 	glob->dependsOnRole = false;
+	glob->iterativePlan = parse->hasIterative;
 
 	/*
 	 * Assess whether it's feasible to use parallel mode for this query. We
@@ -403,7 +404,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 
 	/* primary planning entry point (may recurse for subqueries) */
 	root = subquery_planner(glob, parse, NULL,
-							false, tuple_fraction);
+							false, tuple_fraction, parse->hasIterative);
 
 	/* Select best Path and turn it into a Plan */
 	final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
@@ -596,7 +597,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 PlannerInfo *
 subquery_planner(PlannerGlobal *glob, Query *parse,
 				 PlannerInfo *parent_root,
-				 bool hasRecursion, double tuple_fraction)
+				 bool hasRecursion, double tuple_fraction, bool hasIteration)
 {
 	PlannerInfo *root;
 	List	   *newWithCheckOptions;
@@ -630,6 +631,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->qual_security_level = 0;
 	root->inhTargetKind = INHKIND_NONE;
 	root->hasRecursion = hasRecursion;
+	root->hasIteration = hasIteration;
 	if (hasRecursion)
 		root->wt_param_id = assign_special_exec_param(root);
 	else
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index b02fcb9bfe..272334ea5b 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -219,7 +219,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
 	/* Generate Paths for the subquery */
 	subroot = subquery_planner(root->glob, subquery,
 							   root,
-							   false, tuple_fraction);
+							   false, tuple_fraction, subquery->hasIterative);
 
 	/* Isolate the params needed by this specific subplan */
 	plan_params = root->plan_params;
@@ -266,7 +266,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
 			/* Generate Paths for the ANY subquery; we'll need all rows */
 			subroot = subquery_planner(root->glob, subquery,
 									   root,
-									   false, 0.0);
+									   false, 0.0, false);
 
 			/* Isolate the params needed by this specific subplan */
 			plan_params = root->plan_params;
@@ -917,7 +917,7 @@ SS_process_ctes(PlannerInfo *root)
 		 */
 		subroot = subquery_planner(root->glob, subquery,
 								   root,
-								   cte->cterecursive, 0.0);
+								   cte->cterecursive, 0.0, (cte->cteiterative || root->hasIteration));
 
 		/*
 		 * Since the current query level doesn't yet contain any RTEs, it
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 951aed80e7..b8eedbcc47 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -239,7 +239,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 		subroot = rel->subroot = subquery_planner(root->glob, subquery,
 												  root,
 												  false,
-												  root->tuple_fraction);
+												  root->tuple_fraction,
+												  root->hasIteration);
 
 		/*
 		 * It should not be possible for the primitive query to contain any
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index e991385059..f5db21f344 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -3422,6 +3422,7 @@ create_recursiveunion_path(PlannerInfo *root,
 	pathnode->distinctList = distinctList;
 	pathnode->wtParam = wtParam;
 	pathnode->numGroups = numGroups;
+	pathnode->iterative = (root->hasIteration || root->parse->hasIterative);
 
 	cost_recursive_union(&pathnode->path, leftpath, rightpath);
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index eee9c33637..6d21f7dc39 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -406,6 +406,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	if (stmt->withClause)
 	{
 		qry->hasRecursive = stmt->withClause->recursive;
+		qry->hasIterative = stmt->withClause->iterative;
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 	}
@@ -492,6 +493,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	if (stmt->withClause)
 	{
 		qry->hasRecursive = stmt->withClause->recursive;
+		qry->hasIterative = stmt->withClause->iterative;
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 	}
@@ -1199,6 +1201,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	if (stmt->withClause)
 	{
 		qry->hasRecursive = stmt->withClause->recursive;
+		qry->hasIterative = stmt->withClause->iterative;
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 	}
@@ -1360,6 +1363,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 	if (stmt->withClause)
 	{
 		qry->hasRecursive = stmt->withClause->recursive;
+		qry->hasIterative = stmt->withClause->iterative;
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 	}
@@ -1644,6 +1648,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	if (withClause)
 	{
 		qry->hasRecursive = withClause->recursive;
+		qry->hasIterative = withClause->iterative;
 		qry->cteList = transformWithClause(pstate, withClause);
 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 	}
@@ -2239,6 +2244,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	if (stmt->withClause)
 	{
 		qry->hasRecursive = stmt->withClause->recursive;
+		qry->hasIterative = stmt->withClause->iterative;
 		qry->cteList = transformWithClause(pstate, stmt->withClause);
 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 	}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3c78f2d1b5..fc14c06fdf 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -662,7 +662,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P INCLUDE
 	INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
 	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
-	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
+	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION ITERATIVE
 
 	JOIN
 
@@ -11559,6 +11559,7 @@ with_clause:
 				$$ = makeNode(WithClause);
 				$$->ctes = $2;
 				$$->recursive = false;
+				$$->iterative = false;
 				$$->location = @1;
 			}
 		| WITH_LA cte_list
@@ -11566,6 +11567,7 @@ with_clause:
 				$$ = makeNode(WithClause);
 				$$->ctes = $2;
 				$$->recursive = false;
+				$$->iterative = false;
 				$$->location = @1;
 			}
 		| WITH RECURSIVE cte_list
@@ -11573,6 +11575,15 @@ with_clause:
 				$$ = makeNode(WithClause);
 				$$->ctes = $3;
 				$$->recursive = true;
+				$$->iterative = false;
+				$$->location = @1;
+			}
+		| WITH ITERATIVE cte_list
+			{
+				$$ = makeNode(WithClause);
+				$$->ctes = $3;
+				$$->recursive = true;
+				$$->iterative = true;
 				$$->location = @1;
 			}
 		;
@@ -15387,6 +15398,7 @@ unreserved_keyword:
 			| INSTEAD
 			| INVOKER
 			| ISOLATION
+			| ITERATIVE
 			| KEY
 			| LABEL
 			| LANGUAGE
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 1fca7485ca..cb696220e6 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -135,6 +135,7 @@ transformWithClause(ParseState *pstate, WithClause *withClause)
 		}
 
 		cte->cterecursive = false;
+		cte->cteiterative = withClause->iterative;
 		cte->cterefcount = 0;
 
 		if (!IsA(cte->ctequery, SelectStmt))
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 4fee043bb2..088265ba02 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1266,6 +1266,7 @@ typedef struct MergeAppendState
  *		RecursiveUnionState is used for performing a recursive union.
  *
  *		recursing			T when we're done scanning the non-recursive term
+ *		iterating			T when we're done scanning the non-recursive term
  *		intermediate_empty	T if intermediate_table is currently empty
  *		working_table		working table (to be scanned by recursive term)
  *		intermediate_table	current recursive output (next generation of WT)
@@ -1275,6 +1276,7 @@ typedef struct RecursiveUnionState
 {
 	PlanState	ps;				/* its first field is NodeTag */
 	bool		recursing;
+	bool		iterating;
 	bool		intermediate_empty;
 	Tuplestorestate *working_table;
 	Tuplestorestate *intermediate_table;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5e1ffafb91..551e62c1c1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -128,6 +128,7 @@ typedef struct Query
 	bool		hasSubLinks;	/* has subquery SubLink */
 	bool		hasDistinctOn;	/* distinctClause is from DISTINCT ON */
 	bool		hasRecursive;	/* WITH RECURSIVE was specified */
+	bool		hasIterative;	/* WITH ITERATIVE was specified */
 	bool		hasModifyingCTE;	/* has INSERT/UPDATE/DELETE in WITH */
 	bool		hasForUpdate;	/* FOR [KEY] UPDATE/SHARE was specified */
 	bool		hasRowSecurity; /* rewriter has applied some RLS policy */
@@ -1396,7 +1397,8 @@ typedef struct WithClause
 {
 	NodeTag		type;
 	List	   *ctes;			/* list of CommonTableExprs */
-	bool		recursive;		/* true = WITH RECURSIVE */
+	bool		recursive;		/* true = WITH RECURSIVE (or ITERATIVE) */
+	bool		iterative;		/* true = WITH ITERATIVE */
 	int			location;		/* token location, or -1 if unknown */
 } WithClause;
 
@@ -1455,6 +1457,7 @@ typedef struct CommonTableExpr
 	int			location;		/* token location, or -1 if unknown */
 	/* These fields are set during parse analysis: */
 	bool		cterecursive;	/* is this CTE actually recursive? */
+	bool		cteiterative;	/* is this CTE iterative? */
 	int			cterefcount;	/* number of RTEs referencing this CTE
 								 * (excluding internal self-references) */
 	List	   *ctecolnames;	/* list of output column names */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 1c83772d62..e8df896ffc 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -147,6 +147,7 @@ typedef struct PlannerGlobal
 	char		maxParallelHazard;	/* worst PROPARALLEL hazard level */
 
 	PartitionDirectory partition_directory; /* partition descriptors */
+	bool iterativePlan;
 } PlannerGlobal;
 
 /* macro for fetching the Plan associated with a SubPlan node */
@@ -348,6 +349,7 @@ struct PlannerInfo
 	bool		hasPseudoConstantQuals; /* true if any RestrictInfo has
 										 * pseudoconstant = true */
 	bool		hasRecursion;	/* true if planning a recursive WITH item */
+	bool		hasIteration;	/* true if planning an iteration-optimized recursive WITH item */
 
 	/* These fields are used only when hasRecursion is true: */
 	int			wt_param_id;	/* PARAM_EXEC ID for the work table */
@@ -1787,6 +1789,7 @@ typedef struct RecursiveUnionPath
 	List	   *distinctList;	/* SortGroupClauses identifying target cols */
 	int			wtParam;		/* ID of Param representing work table */
 	double		numGroups;		/* estimated number of groups in input */
+	bool		iterative;		/* WITH ITERATIVE */
 } RecursiveUnionPath;
 
 /*
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 55f363f70c..b58f82f82b 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -303,6 +303,7 @@ typedef struct RecursiveUnion
 	Oid		   *dupOperators;	/* equality operators to compare with */
 	Oid		   *dupCollations;
 	long		numGroups;		/* estimated number of groups in input */
+	bool		iterative;		/* WITH ITERATIVE */
 } RecursiveUnion;
 
 /* ----------------
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index beb7dbbcbe..2521bc79a2 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -44,7 +44,9 @@ extern PlannedStmt *standard_planner(Query *parse, const char *query_string,
 
 extern PlannerInfo *subquery_planner(PlannerGlobal *glob, Query *parse,
 									 PlannerInfo *parent_root,
-									 bool hasRecursion, double tuple_fraction);
+									 bool hasRecursion, double tuple_fraction, bool hasIterate);
+
+extern bool is_dummy_plan(Plan *plan);
 
 extern RowMarkType select_rowmark_type(RangeTblEntry *rte,
 									   LockClauseStrength strength);
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 08f22ce211..8a76c43b9b 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -220,6 +220,7 @@ PG_KEYWORD("invoker", INVOKER, UNRESERVED_KEYWORD)
 PG_KEYWORD("is", IS, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD)
+PG_KEYWORD("iterative", ITERATIVE, UNRESERVED_KEYWORD)
 PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD)
 PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD)
