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/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c new file mode 100644 index 401ba5b..58698ab *** 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 ---- *************** inline_set_returning_functions(PlannerIn *** 577,583 **** { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); ! if (rte->rtekind == RTE_FUNCTION) { Query *funcquery; --- 577,599 ---- { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); ! if (rte->rtekind == RTE_CTE) ! { ! Query *subquery; ! ! if (!enable_cte_subquery) ! continue; ! ! /* 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; ! } ! } ! else if (rte->rtekind == RTE_FUNCTION) { Query *funcquery; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c new file mode 100644 index c72dbef..274c0bb *** a/src/backend/optimizer/util/clauses.c --- b/src/backend/optimizer/util/clauses.c *************** evaluate_expr(Expr *expr, Oid result_typ *** 4677,4682 **** --- 4677,4728 ---- 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; + + 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((Node *)query)) + continue; + + 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 100644 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);