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 100644
index fedbfab..00334c7
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** subquery_planner(PlannerGlobal *glob, Qu
*** 359,364 ****
--- 359,369 ----
  		pull_up_sublinks(root);
  
  	/*
+ 	 * Try to subsitute CTEs with subqueries.
+ 	 */
+ 	substitute_ctes_with_subqueries(root);
+ 
+ 	/*
  	 * Scan the rangetable for set-returning functions, and inline them if
  	 * possible (producing subqueries that might get pulled up next).
  	 * Recursion issues here are handled in the same way as for SubLinks.
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
new file mode 100644
index 9bf1c66..e9242a4
*** a/src/backend/optimizer/prep/prepjointree.c
--- b/src/backend/optimizer/prep/prepjointree.c
***************
*** 31,41 ****
  #include "optimizer/prep.h"
  #include "optimizer/subselect.h"
  #include "optimizer/tlist.h"
  #include "parser/parse_relation.h"
  #include "parser/parsetree.h"
  #include "rewrite/rewriteManip.h"
  
- 
  typedef struct pullup_replace_vars_context
  {
  	PlannerInfo *root;
--- 31,41 ----
  #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"
  
  typedef struct pullup_replace_vars_context
  {
  	PlannerInfo *root;
*************** pull_up_sublinks_qual_recurse(PlannerInf
*** 551,556 ****
--- 551,590 ----
  }
  
  /*
+  * substitute_ctes_with_subqueries
+  *		Attempt to sustitute CTEs with subqueries.
+  *
+  */
+ 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	   *funcquery;
+ 
+ 			/* Check safety of expansion, and expand if possible */
+ 			funcquery = substitute_cte_with_subquery(root, rte);
+ 			if (funcquery)
+ 			{
+ 				/* Successful expansion, replace the rtable entry */
+ 				rte->rtekind = RTE_SUBQUERY;
+ 				rte->subquery = funcquery;
+ 				rte->functions = NIL;
+ 			}
+ 		}
+ 	}
+ }
+ 
+ /*
   * inline_set_returning_functions
   *		Attempt to "inline" set-returning functions in the FROM clause.
   *
*************** pull_up_simple_subquery(PlannerInfo *roo
*** 921,926 ****
--- 955,965 ----
  		pull_up_sublinks(subroot);
  
  	/*
+ 	 * Try to subsitute CTEs with subqueries.
+ 	 */
+ 	substitute_ctes_with_subqueries(subroot);
+ 
+ 	/*
  	 * Similarly, inline any set-returning functions in its rangetable.
  	 */
  	inline_set_returning_functions(subroot);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
new file mode 100644
index c72dbef..db9ec7b
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** evaluate_expr(Expr *expr, Oid result_typ
*** 4677,4682 ****
--- 4677,4710 ----
  							  resultTypByVal);
  }
  
+ /*
+  * substitute_cte_with_subquery
+  *		Attempt to sustitute a CTE with a subquery.
+  *
+  */
+ Query *
+ substitute_cte_with_subquery(PlannerInfo *root, RangeTblEntry *rte)
+ {
+ 	ListCell   *lc;
+  
+ 	Assert(rte->rtekind == RTE_CTE);
+ 
+ 	/*
+ 	 * Lookup current CTE by name
+ 	 */
+ 	foreach(lc, root->parse->cteList)
+ 	{
+ 		CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+ 
+ 		if (strcmp(cte->ctename, rte->ctename) == 0)
+ 		{
+ 			/* TODO: check validity of this CTE */
+ 			return (Query *)cte->ctequery;
+ 		}
+ 	}
+ 
+ 	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 100644
index 7a55a49..380d2e4
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
*************** static struct config_bool ConfigureNames
*** 892,897 ****
--- 892,906 ----
  		NULL, NULL, NULL
  	},
  	{
+ 		{"enable_cte_subquery", PGC_USERSET, QUERY_TUNING_METHOD,
+ 			gettext_noop("Enables the planner's subsituting 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);
