diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 7ab15ac63e1020dd00b3ef27950615dc108b795b..7e10b8a530deb2850938c254e80eae9fe09d4cd9 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -205,7 +205,7 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	 * The Param slot associated with the CTE query is used to hold a pointer
 	 * to the CteState of the first CteScan node that initializes for this
 	 * CTE.  This node will be the one that holds the shared state for all the
-	 * CTEs.
+	 * CTEs, particularly the shared tuplestore.
 	 */
 	prmdata = &(estate->es_param_exec_vals[node->cteParam]);
 	Assert(prmdata->execPlan == NULL);
@@ -294,7 +294,10 @@ ExecEndCteScan(CteScanState *node)
 	 * If I am the leader, free the tuplestore.
 	 */
 	if (node->leader == node)
+	{
 		tuplestore_end(node->cte_table);
+		node->cte_table = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
@@ -312,26 +315,26 @@ ExecReScanCteScan(CteScanState *node)
 
 	ExecScanReScan(&node->ss);
 
-	if (node->leader == node)
+	/*
+	 * Clear the tuplestore if a new scan of the underlying CTE is required.
+	 * This implicitly resets all the tuplestore's read pointers.  Note that
+	 * multiple CTE nodes might redundantly clear the tuplestore; that's OK,
+	 * and not unduly expensive.  We'll stop taking this path as soon as
+	 * somebody has attempted to read something from the underlying CTE
+	 * (thereby causing its chgParam to be cleared).
+	 */
+	if (node->leader->cteplanstate->chgParam != NULL)
 	{
-		/*
-		 * The leader is responsible for clearing the tuplestore if a new scan
-		 * of the underlying CTE is required.
-		 */
-		if (node->cteplanstate->chgParam != NULL)
-		{
-			tuplestore_clear(tuplestorestate);
-			node->eof_cte = false;
-		}
-		else
-		{
-			tuplestore_select_read_pointer(tuplestorestate, node->readptr);
-			tuplestore_rescan(tuplestorestate);
-		}
+		tuplestore_clear(tuplestorestate);
+		node->leader->eof_cte = false;
 	}
 	else
 	{
-		/* Not leader, so just rewind my own pointer */
+		/*
+		 * Else, just rewind my own pointer.  Either the underlying CTE
+		 * doesn't need a rescan (and we can re-read what's in the tuplestore
+		 * now), or somebody else already took care of it.
+		 */
 		tuplestore_select_read_pointer(tuplestorestate, node->readptr);
 		tuplestore_rescan(tuplestorestate);
 	}
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index a491b2ca61f82bee3978a1b2bf262bc646fe91bd..a7491bf6b93038b8b23368516d0681a563fa9831 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -1209,6 +1209,81 @@ SELECT * FROM outermost;
 ERROR:  recursive reference to query "outermost" must not appear within a subquery
 LINE 2:   WITH innermost as (SELECT 2 FROM outermost) 
                                            ^
+--
+-- Test CTEs read in non-initialization orders
+--
+WITH RECURSIVE
+  tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
+  iter (id_key, row_type, link) AS (
+      SELECT 0, 'base', 17
+    UNION ALL (
+      WITH remaining(id_key, row_type, link, min) AS (
+        SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
+        FROM tab INNER JOIN iter USING (link)
+        WHERE tab.id_key > iter.id_key
+      ),
+      first_remaining AS (
+        SELECT id_key, row_type, link
+        FROM remaining
+        WHERE id_key=min
+      ),
+      effect AS (
+        SELECT tab.id_key, 'new'::text, tab.link
+        FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
+        WHERE e.row_type = 'false'
+      )
+      SELECT * FROM first_remaining
+      UNION ALL SELECT * FROM effect
+    )
+  )
+SELECT * FROM iter;
+ id_key | row_type | link 
+--------+----------+------
+      0 | base     |   17
+      1 | true     |   17
+      2 | true     |   17
+      3 | true     |   17
+      4 | true     |   17
+      5 | true     |   17
+      6 | true     |   17
+(7 rows)
+
+WITH RECURSIVE
+  tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
+  iter (id_key, row_type, link) AS (
+      SELECT 0, 'base', 17
+    UNION (
+      WITH remaining(id_key, row_type, link, min) AS (
+        SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
+        FROM tab INNER JOIN iter USING (link)
+        WHERE tab.id_key > iter.id_key
+      ),
+      first_remaining AS (
+        SELECT id_key, row_type, link
+        FROM remaining
+        WHERE id_key=min
+      ),
+      effect AS (
+        SELECT tab.id_key, 'new'::text, tab.link
+        FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
+        WHERE e.row_type = 'false'
+      )
+      SELECT * FROM first_remaining
+      UNION ALL SELECT * FROM effect
+    )
+  )
+SELECT * FROM iter;
+ id_key | row_type | link 
+--------+----------+------
+      0 | base     |   17
+      1 | true     |   17
+      2 | true     |   17
+      3 | true     |   17
+      4 | true     |   17
+      5 | true     |   17
+      6 | true     |   17
+(7 rows)
+
 --
 -- Data-modifying statements in WITH
 --
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index 22fdddc4eeafa2de36c1eaf57b20ec0d44d27df3..684c8f13db1a692c78ba4b87355a03377bec83c0 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -574,6 +574,62 @@ WITH RECURSIVE outermost(x) AS (
 )
 SELECT * FROM outermost;
 
+--
+-- Test CTEs read in non-initialization orders
+--
+
+WITH RECURSIVE
+  tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
+  iter (id_key, row_type, link) AS (
+      SELECT 0, 'base', 17
+    UNION ALL (
+      WITH remaining(id_key, row_type, link, min) AS (
+        SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
+        FROM tab INNER JOIN iter USING (link)
+        WHERE tab.id_key > iter.id_key
+      ),
+      first_remaining AS (
+        SELECT id_key, row_type, link
+        FROM remaining
+        WHERE id_key=min
+      ),
+      effect AS (
+        SELECT tab.id_key, 'new'::text, tab.link
+        FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
+        WHERE e.row_type = 'false'
+      )
+      SELECT * FROM first_remaining
+      UNION ALL SELECT * FROM effect
+    )
+  )
+SELECT * FROM iter;
+
+WITH RECURSIVE
+  tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
+  iter (id_key, row_type, link) AS (
+      SELECT 0, 'base', 17
+    UNION (
+      WITH remaining(id_key, row_type, link, min) AS (
+        SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
+        FROM tab INNER JOIN iter USING (link)
+        WHERE tab.id_key > iter.id_key
+      ),
+      first_remaining AS (
+        SELECT id_key, row_type, link
+        FROM remaining
+        WHERE id_key=min
+      ),
+      effect AS (
+        SELECT tab.id_key, 'new'::text, tab.link
+        FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
+        WHERE e.row_type = 'false'
+      )
+      SELECT * FROM first_remaining
+      UNION ALL SELECT * FROM effect
+    )
+  )
+SELECT * FROM iter;
+
 --
 -- Data-modifying statements in WITH
 --