diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
new file mode 100644
index 7069f60..7b49816
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** bool		enable_nestloop = true;
*** 118,123 ****
--- 118,124 ----
  bool		enable_material = true;
  bool		enable_mergejoin = true;
  bool		enable_hashjoin = true;
+ bool		enable_cte_subquery = true;
  
  typedef struct
  {
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
new file mode 100755
index d598c1b..32fe18b
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** subquery_planner(PlannerGlobal *glob, Qu
*** 365,374 ****
  
  	/*
  	 * If there is a WITH list, process each WITH query and build an initplan
! 	 * SubPlan structure for it.
  	 */
  	if (parse->cteList)
  		SS_process_ctes(root);
  
  	/*
  	 * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try
--- 365,378 ----
  
  	/*
  	 * If there is a WITH list, process each WITH query and build an initplan
! 	 * SubPlan structure for it. Before we process ctes, try to subsitute with 
! 	 * subqueries to benefits from global optimization.
  	 */
  	if (parse->cteList)
+ 	{
+ 		substitute_ctes_with_subqueries(root);
  		SS_process_ctes(root);
+ 	}
  
  	/*
  	 * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
new file mode 100755
index 401ba5b..e8c8ee2
*** a/src/backend/optimizer/prep/prepjointree.c
--- b/src/backend/optimizer/prep/prepjointree.c
***************
*** 31,36 ****
--- 31,37 ----
  #include "optimizer/prep.h"
  #include "optimizer/subselect.h"
  #include "optimizer/tlist.h"
+ #include "optimizer/cost.h"
  #include "parser/parse_relation.h"
  #include "parser/parsetree.h"
  #include "rewrite/rewriteManip.h"
*************** static void fix_append_rel_relids(List *
*** 116,122 ****
  					  Relids subrelids);
  static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
  
- 
  /*
   * pull_up_sublinks
   *		Attempt to pull up ANY and EXISTS SubLinks to be treated as
--- 117,122 ----
*************** pull_up_sublinks_qual_recurse(PlannerInf
*** 551,556 ****
--- 551,595 ----
  }
  
  /*
+  * substitute_ctes_with_subqueries
+  *		Attempt to sustitute CTEs with subqueries in the FROM clause.
+  *
+  *  The spec does not specify the optimization boundary of a CTE, so we are
+  *  free to expand it as a subquery to enable it to participate in the global 
+  *  optimization.
+  * 
+  *  There are some restrictions when we can't do that, and see the processor
+  *  function for details. 
+  */
+ void
+ substitute_ctes_with_subqueries(PlannerInfo *root)
+ {
+ 	ListCell   *rt;
+ 
+ 	if (!enable_cte_subquery)
+ 		return;
+ 
+ 	foreach(rt, root->parse->rtable)
+ 	{
+ 		RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+ 
+ 		if (rte->rtekind == RTE_CTE)
+ 		{
+ 			Query	   *subquery;
+ 
+ 			/* Check safety of expansion, and expand if possible */
+ 			subquery = substitute_cte_with_subquery(root, rte);
+ 			if (subquery)
+ 			{
+ 				/* Successful expansion, replace the rtable entry */
+ 				rte->rtekind = RTE_SUBQUERY;
+ 				rte->subquery = subquery;
+ 			}
+ 		}
+ 	}
+ }
+ 
+ /*
   * inline_set_returning_functions
   *		Attempt to "inline" set-returning functions in the FROM clause.
   *
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
new file mode 100755
index c72dbef..4c8b2af
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** evaluate_expr(Expr *expr, Oid result_typ
*** 4677,4682 ****
--- 4677,4731 ----
  							  resultTypByVal);
  }
  
+ /*
+  * substitute_cte_with_subquery
+  *		Attempt to sustitute a CTE with a subquery.
+  *
+  */
+ Query *
+ substitute_cte_with_subquery(PlannerInfo *root, RangeTblEntry *rte)
+ {
+ 	List		*cteList;
+ 	ListCell	*lc;
+ 	int levelsup;
+  
+ 	Assert(rte->rtekind == RTE_CTE);
+ 
+ 	/*
+ 	 * Lookup matching RTE by name in current level's CTE list
+ 	 */
+ 	cteList = root->parse->cteList;
+ 	foreach(lc, cteList)
+ 	{
+ 		CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+ 		Query *query = (Query *)cte->ctequery;
+ 
+ 		/* Not interested in unused CTE */
+ 		if (!cte->cterefcount)
+ 			continue;
+ 
+ 		/* Do not convert non-select, with-recursive CTE */
+ 		if (query->commandType != CMD_SELECT || cte->cterecursive)
+ 			continue;
+ 
+ 		if (!strcmp(cte->ctename, rte->ctename))
+ 		{
+ 			/*
+ 			 * Expanding volatile function could cause multiple evaluation of  the
+ 			 * function, so we can't do it. Also, volatile check is expensive so arrange 
+ 			 * it after name check. 
+ 			 */
+ 			if (contain_volatile_functions(query))
+ 				continue;
+ 
+ 			/* decrease the refcount as current RTE does not need it */
+ 			cte->cterefcount --;
+ 			return query;
+ 		}
+ 	}
+ 
+ 	return NULL;
+ }
  
  /*
   * inline_set_returning_function
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
new file mode 100755
index b3dac51..7f4e2bd
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
*************** static struct config_bool ConfigureNames
*** 873,878 ****
--- 873,887 ----
  		NULL, NULL, NULL
  	},
  	{
+ 		{"enable_cte_subquery", PGC_USERSET, QUERY_TUNING_METHOD,
+ 			gettext_noop("Enables the planner subsitutes CTEs with subqueries."),
+ 			NULL
+ 		},
+ 		&enable_cte_subquery,
+ 		true,
+ 		NULL, NULL, NULL
+ 	},
+ 	{
  		{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
  			gettext_noop("Enables genetic query optimization."),
  			gettext_noop("This algorithm attempts to do planning without "
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
new file mode 100644
index 3d04ac2..27a7227
*** a/src/include/optimizer/clauses.h
--- b/src/include/optimizer/clauses.h
*************** extern Node *estimate_expression_value(P
*** 84,88 ****
--- 84,89 ----
  
  extern Query *inline_set_returning_function(PlannerInfo *root,
  							  RangeTblEntry *rte);
+ extern Query *substitute_cte_with_subquery(PlannerInfo *root, RangeTblEntry *rte);
  
  #endif   /* CLAUSES_H */
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
new file mode 100644
index dd43e45..58a146b
*** a/src/include/optimizer/cost.h
--- b/src/include/optimizer/cost.h
*************** extern bool enable_nestloop;
*** 61,66 ****
--- 61,67 ----
  extern bool enable_material;
  extern bool enable_mergejoin;
  extern bool enable_hashjoin;
+ extern bool enable_cte_subquery;
  extern int	constraint_exclusion;
  
  extern double clamp_row_est(double nrows);
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
new file mode 100644
index 7b8c0a9..ed43dd0
*** a/src/include/optimizer/prep.h
--- b/src/include/optimizer/prep.h
***************
*** 23,28 ****
--- 23,29 ----
   */
  extern void pull_up_sublinks(PlannerInfo *root);
  extern void inline_set_returning_functions(PlannerInfo *root);
+ extern void substitute_ctes_with_subqueries(PlannerInfo *root);
  extern void pull_up_subqueries(PlannerInfo *root);
  extern void flatten_simple_union_all(PlannerInfo *root);
  extern void reduce_outer_joins(PlannerInfo *root);
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
new file mode 100644
index 6dabe50..7c926da
*** a/src/test/regress/expected/rangefuncs.out
--- b/src/test/regress/expected/rangefuncs.out
*************** SELECT name, setting FROM pg_settings WH
*** 2,7 ****
--- 2,8 ----
           name         | setting 
  ----------------------+---------
   enable_bitmapscan    | on
+  enable_cte_subquery  | on
   enable_hashagg       | on
   enable_hashjoin      | on
   enable_indexonlyscan | on
*************** SELECT name, setting FROM pg_settings WH
*** 12,18 ****
   enable_seqscan       | on
   enable_sort          | on
   enable_tidscan       | on
! (11 rows)
  
  CREATE TABLE foo2(fooid int, f2 int);
  INSERT INTO foo2 VALUES(1, 11);
--- 13,19 ----
   enable_seqscan       | on
   enable_sort          | on
   enable_tidscan       | on
! (12 rows)
  
  CREATE TABLE foo2(fooid int, f2 int);
  INSERT INTO foo2 VALUES(1, 11);
