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 + -
显示快捷键?