/* * add_vars_to_targetlist * For each variable appearing in the list, add it to the owning * relation's targetlist if not already present, and mark the variable * as being needed for the indicated join (or for final output if * where_needed includes "relation 0"). * * The list may also contain PlaceHolderVars. These don't necessarily * have a single owning relation; we keep their attr_needed info in * root->placeholder_list instead. */ void add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed) { ListCell *temp; Assert(!bms_is_empty(where_needed)); foreach(temp, vars) { Node *node = (Node *) lfirst(temp); if (IsA(node, Var)) { Var *var = (Var *) node; RelOptInfo *rel = find_base_rel(root, var->varno); int attno = var->varattno; Assert(attno >= rel->min_attr && attno <= rel->max_attr); attno -= rel->min_attr; if (rel->attr_needed[attno] == NULL) { /* Variable not yet requested, so add to reltargetlist */ /* XXX is copyObject necessary here? */ rel->reltargetlist = lappend(rel->reltargetlist, copyObject(var)); } rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno], where_needed); } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderInfo *phinfo = find_placeholder_info(root, phv); phinfo->ph_needed = bms_add_members(phinfo->ph_needed, where_needed); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); }
/* * optimize_minmax_aggregates - check for optimizing MIN/MAX via indexes * * This checks to see if we can replace MIN/MAX aggregate functions by * subqueries of the form * (SELECT col FROM tab WHERE ... ORDER BY col ASC/DESC LIMIT 1) * Given a suitable index on tab.col, this can be much faster than the * generic scan-all-the-rows plan. * * We are passed the preprocessed tlist, and the best path * devised for computing the input of a standard Agg node. If we are able * to optimize all the aggregates, and the result is estimated to be cheaper * than the generic aggregate method, then generate and return a Plan that * does it that way. Otherwise, return NULL. */ Plan * optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path) { Query *parse = root->parse; FromExpr *jtnode; RangeTblRef *rtr; RangeTblEntry *rte; RelOptInfo *rel; List *aggs_list; ListCell *l; Cost total_cost; Path agg_p; Plan *plan; Node *hqual; QualCost tlist_cost; /* Nothing to do if query has no aggregates */ if (!parse->hasAggs) return NULL; Assert(!parse->setOperations); /* shouldn't get here if a setop */ Assert(parse->rowMarks == NIL); /* nor if FOR UPDATE */ /* * Reject unoptimizable cases. * * We don't handle GROUP BY, because our current implementations of * grouping require looking at all the rows anyway, and so there's not * much point in optimizing MIN/MAX. */ if (parse->groupClause) return NULL; /* * We also restrict the query to reference exactly one table, since join * conditions can't be handled reasonably. (We could perhaps handle a * query containing cartesian-product joins, but it hardly seems worth the * trouble.) However, the single real table could be buried in several * levels of FromExpr. */ jtnode = parse->jointree; while (IsA(jtnode, FromExpr)) { if (list_length(jtnode->fromlist) != 1) return NULL; jtnode = linitial(jtnode->fromlist); } if (!IsA(jtnode, RangeTblRef)) return NULL; rtr = (RangeTblRef *) jtnode; rte = rt_fetch(rtr->rtindex, parse->rtable); if (rte->rtekind != RTE_RELATION || rte->inh) return NULL; rel = find_base_rel(root, rtr->rtindex); /* * Since this optimization is not applicable all that often, we want to * fall out before doing very much work if possible. Therefore we do the * work in several passes. The first pass scans the tlist and HAVING qual * to find all the aggregates and verify that each of them is a MIN/MAX * aggregate. If that succeeds, the second pass looks at each aggregate * to see if it is optimizable; if so we make an IndexPath describing how * we would scan it. (We do not try to optimize if only some aggs are * optimizable, since that means we'll have to scan all the rows anyway.) * If that succeeds, we have enough info to compare costs against the * generic implementation. Only if that test passes do we build a Plan. */ /* Pass 1: find all the aggregates */ aggs_list = NIL; if (find_minmax_aggs_walker((Node *) tlist, &aggs_list)) return NULL; if (find_minmax_aggs_walker(parse->havingQual, &aggs_list)) return NULL; /* Pass 2: see if each one is optimizable */ total_cost = 0; foreach(l, aggs_list) { MinMaxAggInfo *info = (MinMaxAggInfo *) lfirst(l); if (!build_minmax_path(root, rel, info)) return NULL; total_cost += info->pathcost; }