diff options
Diffstat (limited to 'src/backend/utils/adt/selfuncs.c')
-rw-r--r-- | src/backend/utils/adt/selfuncs.c | 134 |
1 files changed, 99 insertions, 35 deletions
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 4ea5415f204..35c9e3c86fe 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5363,7 +5363,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Handle a simple Var for examine_variable * * This is split out as a subroutine so that we can recurse to deal with - * Vars referencing subqueries. + * Vars referencing subqueries (either sub-SELECT-in-FROM or CTE style). * * We already filled in all the fields of *vardata except for the stats tuple. */ @@ -5497,13 +5497,19 @@ examine_simple_variable(PlannerInfo *root, Var *var, vardata->acl_ok = true; } } - else if (rte->rtekind == RTE_SUBQUERY && !rte->inh) + else if ((rte->rtekind == RTE_SUBQUERY && !rte->inh) || + (rte->rtekind == RTE_CTE && !rte->self_reference)) { /* - * Plain subquery (not one that was converted to an appendrel). + * Plain subquery (not one that was converted to an appendrel) or + * non-recursive CTE. In either case, we can try to find out what the + * Var refers to within the subquery. We skip this for appendrel and + * recursive-CTE cases because any column stats we did find would + * likely not be very relevant. */ - Query *subquery = rte->subquery; - RelOptInfo *rel; + PlannerInfo *subroot; + Query *subquery; + List *subtlist; TargetEntry *ste; /* @@ -5513,6 +5519,85 @@ examine_simple_variable(PlannerInfo *root, Var *var, return; /* + * Otherwise, find the subquery's planner subroot. + */ + if (rte->rtekind == RTE_SUBQUERY) + { + RelOptInfo *rel; + + /* + * Fetch RelOptInfo for subquery. Note that we don't change the + * rel returned in vardata, since caller expects it to be a rel of + * the caller's query level. Because we might already be + * recursing, we can't use that rel pointer either, but have to + * look up the Var's rel afresh. + */ + rel = find_base_rel(root, var->varno); + + subroot = rel->subroot; + } + else + { + /* CTE case is more difficult */ + PlannerInfo *cteroot; + Index levelsup; + int ndx; + int plan_id; + ListCell *lc; + + /* + * Find the referenced CTE, and locate the subroot previously made + * for it. + */ + levelsup = rte->ctelevelsup; + cteroot = root; + while (levelsup-- > 0) + { + cteroot = cteroot->parent_root; + if (!cteroot) /* shouldn't happen */ + elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename); + } + + /* + * Note: cte_plan_ids can be shorter than cteList, if we are still + * working on planning the CTEs (ie, this is a side-reference from + * another CTE). So we mustn't use forboth here. + */ + ndx = 0; + foreach(lc, cteroot->parse->cteList) + { + CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); + + if (strcmp(cte->ctename, rte->ctename) == 0) + break; + ndx++; + } + if (lc == NULL) /* shouldn't happen */ + elog(ERROR, "could not find CTE \"%s\"", rte->ctename); + if (ndx >= list_length(cteroot->cte_plan_ids)) + elog(ERROR, "could not find plan for CTE \"%s\"", rte->ctename); + plan_id = list_nth_int(cteroot->cte_plan_ids, ndx); + if (plan_id <= 0) + elog(ERROR, "no plan was made for CTE \"%s\"", rte->ctename); + subroot = list_nth(root->glob->subroots, plan_id - 1); + } + + /* If the subquery hasn't been planned yet, we have to punt */ + if (subroot == NULL) + return; + Assert(IsA(subroot, PlannerInfo)); + + /* + * We must use the subquery parsetree as mangled by the planner, not + * the raw version from the RTE, because we need a Var that will refer + * to the subroot's live RelOptInfos. For instance, if any subquery + * pullup happened during planning, Vars in the targetlist might have + * gotten replaced, and we need to see the replacement expressions. + */ + subquery = subroot->parse; + Assert(IsA(subquery, Query)); + + /* * Punt if subquery uses set operations or GROUP BY, as these will * mash underlying columns' stats beyond recognition. (Set ops are * particularly nasty; if we forged ahead, we would return stats @@ -5525,33 +5610,12 @@ examine_simple_variable(PlannerInfo *root, Var *var, subquery->groupingSets) return; - /* - * OK, fetch RelOptInfo for subquery. Note that we don't change the - * rel returned in vardata, since caller expects it to be a rel of the - * caller's query level. Because we might already be recursing, we - * can't use that rel pointer either, but have to look up the Var's - * rel afresh. - */ - rel = find_base_rel(root, var->varno); - - /* If the subquery hasn't been planned yet, we have to punt */ - if (rel->subroot == NULL) - return; - Assert(IsA(rel->subroot, PlannerInfo)); - - /* - * Switch our attention to the subquery as mangled by the planner. It - * was okay to look at the pre-planning version for the tests above, - * but now we need a Var that will refer to the subroot's live - * RelOptInfos. For instance, if any subquery pullup happened during - * planning, Vars in the targetlist might have gotten replaced, and we - * need to see the replacement expressions. - */ - subquery = rel->subroot->parse; - Assert(IsA(subquery, Query)); - /* Get the subquery output expression referenced by the upper Var */ - ste = get_tle_by_resno(subquery->targetList, var->varattno); + if (subquery->returningList) + subtlist = subquery->returningList; + else + subtlist = subquery->targetList; + ste = get_tle_by_resno(subtlist, var->varattno); if (ste == NULL || ste->resjunk) elog(ERROR, "subquery %s does not have attribute %d", rte->eref->aliasname, var->varattno); @@ -5599,16 +5663,16 @@ examine_simple_variable(PlannerInfo *root, Var *var, * if the underlying column is unique, the subquery may have * joined to other tables in a way that creates duplicates. */ - examine_simple_variable(rel->subroot, var, vardata); + examine_simple_variable(subroot, var, vardata); } } else { /* - * Otherwise, the Var comes from a FUNCTION, VALUES, or CTE RTE. (We - * won't see RTE_JOIN here because join alias Vars have already been + * Otherwise, the Var comes from a FUNCTION or VALUES RTE. (We won't + * see RTE_JOIN here because join alias Vars have already been * flattened.) There's not much we can do with function outputs, but - * maybe someday try to be smarter about VALUES and/or CTEs. + * maybe someday try to be smarter about VALUES. */ } } |