planagg.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 625 行 · 第 1/2 页
C
625 行
* cheat a bit by not bothering with a RestrictInfo node for the notnull * test --- predicate_implied_by() won't care. */ allquals = list_concat(list_make1(ntest), rel->baserestrictinfo); foreach(l, rel->indexlist) { IndexOptInfo *index = (IndexOptInfo *) lfirst(l); ScanDirection indexscandir = NoMovementScanDirection; int indexcol; int prevcol; List *restrictclauses; IndexPath *new_path; Cost new_cost; bool found_clause; /* Ignore non-btree indexes */ if (index->relam != BTREE_AM_OID) continue; /* * Ignore partial indexes that do not match the query --- unless their * predicates can be proven from the baserestrict list plus the IS NOT * NULL test. In that case we can use them. */ if (index->indpred != NIL && !index->predOK && !predicate_implied_by(index->indpred, allquals)) continue; /* * Look for a match to one of the index columns. (In a stupidly * designed index, there could be multiple matches, but we only care * about the first one.) */ for (indexcol = 0; indexcol < index->ncolumns; indexcol++) { indexscandir = match_agg_to_index_col(info, index, indexcol); if (!ScanDirectionIsNoMovement(indexscandir)) break; } if (ScanDirectionIsNoMovement(indexscandir)) continue; /* * If the match is not at the first index column, we have to verify * that there are "x = something" restrictions on all the earlier * index columns. Since we'll need the restrictclauses list anyway to * build the path, it's convenient to extract that first and then look * through it for the equality restrictions. */ restrictclauses = group_clauses_by_indexkey(index, index->rel->baserestrictinfo, NIL, NULL, SAOP_FORBID, &found_clause); if (list_length(restrictclauses) < indexcol) continue; /* definitely haven't got enough */ for (prevcol = 0; prevcol < indexcol; prevcol++) { List *rinfos = (List *) list_nth(restrictclauses, prevcol); ListCell *ll; foreach(ll, rinfos) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(ll); int strategy; /* Could be an IS_NULL test, if so ignore */ if (!is_opclause(rinfo->clause)) continue; strategy = get_op_opfamily_strategy(((OpExpr *) rinfo->clause)->opno, index->opfamily[prevcol]); if (strategy == BTEqualStrategyNumber) break; } if (ll == NULL) break; /* none are Equal for this index col */ } if (prevcol < indexcol) continue; /* didn't find all Equal clauses */ /* * Build the access path. We don't bother marking it with pathkeys. */ new_path = create_index_path(root, index, restrictclauses, NIL, indexscandir, NULL); /* * Estimate actual cost of fetching just one row. */ if (new_path->rows > 1.0) new_cost = new_path->path.startup_cost + (new_path->path.total_cost - new_path->path.startup_cost) * 1.0 / new_path->rows; else new_cost = new_path->path.total_cost; /* * Keep if first or if cheaper than previous best. */ if (best_path == NULL || new_cost < best_cost) { best_path = new_path; best_cost = new_cost; if (ScanDirectionIsForward(indexscandir)) best_nulls_first = index->nulls_first[indexcol]; else best_nulls_first = !index->nulls_first[indexcol]; } } info->path = best_path; info->pathcost = best_cost; info->nulls_first = best_nulls_first; return (best_path != NULL);}/* * match_agg_to_index_col * Does an aggregate match an index column? * * It matches if its argument is equal to the index column's data and its * sortop is either the forward or reverse sort operator for the column. * * We return ForwardScanDirection if match the forward sort operator, * BackwardScanDirection if match the reverse sort operator, * and NoMovementScanDirection if there's no match. */static ScanDirectionmatch_agg_to_index_col(MinMaxAggInfo *info, IndexOptInfo *index, int indexcol){ ScanDirection result; /* Check for operator match first (cheaper) */ if (info->aggsortop == index->fwdsortop[indexcol]) result = ForwardScanDirection; else if (info->aggsortop == index->revsortop[indexcol]) result = BackwardScanDirection; else return NoMovementScanDirection; /* Check for data match */ if (!match_index_to_operand((Node *) info->target, indexcol, index)) return NoMovementScanDirection; return result;}/* * Construct a suitable plan for a converted aggregate query */static voidmake_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info){ PlannerInfo subroot; Query *subparse; Plan *plan; Plan *iplan; TargetEntry *tle; SortClause *sortcl; /* * Generate a suitably modified query. Much of the work here is probably * unnecessary in the normal case, but we want to make it look good if * someone tries to EXPLAIN the result. */ memcpy(&subroot, root, sizeof(PlannerInfo)); subroot.parse = subparse = (Query *) copyObject(root->parse); subroot.init_plans = NIL; subparse->commandType = CMD_SELECT; subparse->resultRelation = 0; subparse->returningList = NIL; subparse->utilityStmt = NULL; subparse->intoClause = NULL; subparse->hasAggs = false; subparse->groupClause = NIL; subparse->havingQual = NULL; subparse->distinctClause = NIL; subroot.hasHavingQual = false; /* single tlist entry that is the aggregate target */ tle = makeTargetEntry(copyObject(info->target), 1, pstrdup("agg_target"), false); subparse->targetList = list_make1(tle); /* set up the appropriate ORDER BY entry */ sortcl = makeNode(SortClause); sortcl->tleSortGroupRef = assignSortGroupRef(tle, subparse->targetList); sortcl->sortop = info->aggsortop; sortcl->nulls_first = info->nulls_first; subparse->sortClause = list_make1(sortcl); /* set up LIMIT 1 */ subparse->limitOffset = NULL; subparse->limitCount = (Node *) makeConst(INT8OID, -1, sizeof(int64), Int64GetDatum(1), false, false /* not by val */ ); /* * Generate the plan for the subquery. We already have a Path for the * basic indexscan, but we have to convert it to a Plan and attach a LIMIT * node above it. * * Also we must add a "WHERE target IS NOT NULL" restriction to the * indexscan, to be sure we don't return a NULL, which'd be contrary to * the standard behavior of MIN/MAX. XXX ideally this should be done * earlier, so that the selectivity of the restriction could be included * in our cost estimates. But that looks painful, and in most cases the * fraction of NULLs isn't high enough to change the decision. * * The NOT NULL qual has to go on the actual indexscan; create_plan might * have stuck a gating Result atop that, if there were any pseudoconstant * quals. * * We can skip adding the NOT NULL qual if it's redundant with either an * already-given WHERE condition, or a clause of the index predicate. */ plan = create_plan(&subroot, (Path *) info->path); plan->targetlist = copyObject(subparse->targetList); if (IsA(plan, Result)) iplan = plan->lefttree; else iplan = plan; Assert(IsA(iplan, IndexScan)); if (!list_member(iplan->qual, info->notnulltest) && !list_member(info->path->indexinfo->indpred, info->notnulltest)) iplan->qual = lcons(info->notnulltest, iplan->qual); plan = (Plan *) make_limit(plan, subparse->limitOffset, subparse->limitCount, 0, 1); /* * Convert the plan into an InitPlan, and make a Param for its result. */ info->param = SS_make_initplan_from_plan(&subroot, plan, exprType((Node *) tle->expr), -1); /* * Make sure the InitPlan gets into the outer list. It has to appear * after any other InitPlans it might depend on, too (see comments in * ExecReScan). */ root->init_plans = list_concat(root->init_plans, subroot.init_plans);}/* * Replace original aggregate calls with subplan output Params */static Node *replace_aggs_with_params_mutator(Node *node, List **context){ if (node == NULL) return NULL; if (IsA(node, Aggref)) { Aggref *aggref = (Aggref *) node; ListCell *l; Expr *curTarget = linitial(aggref->args); foreach(l, *context) { MinMaxAggInfo *info = (MinMaxAggInfo *) lfirst(l); if (info->aggfnoid == aggref->aggfnoid && equal(info->target, curTarget)) return (Node *) info->param; } elog(ERROR, "failed to re-find aggregate info record"); } Assert(!IsA(node, SubLink)); return expression_tree_mutator(node, replace_aggs_with_params_mutator, (void *) context);}/* * Get the OID of the sort operator, if any, associated with an aggregate. * Returns InvalidOid if there is no such operator. */static Oidfetch_agg_sort_op(Oid aggfnoid){ HeapTuple aggTuple; Form_pg_aggregate aggform; Oid aggsortop; /* fetch aggregate entry from pg_aggregate */ aggTuple = SearchSysCache(AGGFNOID, ObjectIdGetDatum(aggfnoid), 0, 0, 0); if (!HeapTupleIsValid(aggTuple)) return InvalidOid; aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); aggsortop = aggform->aggsortop; ReleaseSysCache(aggTuple); return aggsortop;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?