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);