indxpath.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,070 行 · 第 1/5 页

C
2,070
字号
		default:			return;	}	/*	 * If there are no indexable joinclauses for this rel, exit quickly.	 */	if (bms_is_empty(rel->index_outer_relids))		return;	/*	 * Otherwise, we have to do path selection in the main planning context,	 * so that any created path can be safely attached to the rel's cache of	 * best inner paths.  (This is not currently an issue for normal planning,	 * but it is an issue for GEQO planning.)	 */	oldcontext = MemoryContextSwitchTo(root->planner_cxt);	/*	 * Intersect the given outer relids with index_outer_relids to find the	 * set of outer relids actually relevant for this rel. If there are none,	 * again we can fail immediately.	 */	outer_relids = bms_intersect(rel->index_outer_relids, outer_rel->relids);	if (bms_is_empty(outer_relids))	{		bms_free(outer_relids);		MemoryContextSwitchTo(oldcontext);		return;	}	/*	 * Look to see if we already computed the result for this set of relevant	 * outerrels.  (We include the isouterjoin status in the cache lookup key	 * for safety.	In practice I suspect this is not necessary because it	 * should always be the same for a given combination of rels.)	 *	 * NOTE: because we cache on outer_relids rather than outer_rel->relids,	 * we will report the same paths and hence path cost for joins with	 * different sets of irrelevant rels on the outside.  Now that cost_index	 * is sensitive to outer_rel->rows, this is not really right.  However the	 * error is probably not large.  Is it worth establishing a separate cache	 * entry for each distinct outer_rel->relids set to get this right?	 */	foreach(l, rel->index_inner_paths)	{		info = (InnerIndexscanInfo *) lfirst(l);		if (bms_equal(info->other_relids, outer_relids) &&			info->isouterjoin == isouterjoin)		{			bms_free(outer_relids);			MemoryContextSwitchTo(oldcontext);			*cheapest_startup = info->cheapest_startup_innerpath;			*cheapest_total = info->cheapest_total_innerpath;			return;		}	}	/*	 * Find all the relevant restriction and join clauses.	 *	 * Note: because we include restriction clauses, we will find indexscans	 * that could be plain indexscans, ie, they don't require the join context	 * at all.	This may seem redundant, but we need to include those scans in	 * the input given to choose_bitmap_and() to be sure we find optimal AND	 * combinations of join and non-join scans.  Also, even if the "best inner	 * indexscan" is just a plain indexscan, it will have a different cost	 * estimate because of cache effects.	 */	clause_list = find_clauses_for_join(root, rel, outer_relids, isouterjoin);	/*	 * Find all the index paths that are usable for this join, except for	 * stuff involving OR and ScalarArrayOpExpr clauses.	 */	indexpaths = find_usable_indexes(root, rel,									 clause_list, NIL,									 false, outer_rel,									 SAOP_FORBID);	/*	 * Generate BitmapOrPaths for any suitable OR-clauses present in the	 * clause list.	 */	bitindexpaths = generate_bitmap_or_paths(root, rel,											 clause_list, NIL,											 outer_rel);	/*	 * Likewise, generate paths using ScalarArrayOpExpr clauses; these can't	 * be simple indexscans but they can be used in bitmap scans.	 */	bitindexpaths = list_concat(bitindexpaths,								find_saop_paths(root, rel,												clause_list, NIL,												false, outer_rel));	/*	 * Include the regular index paths in bitindexpaths.	 */	bitindexpaths = list_concat(bitindexpaths, list_copy(indexpaths));	/*	 * If we found anything usable, generate a BitmapHeapPath for the most	 * promising combination of bitmap index paths.	 */	if (bitindexpaths != NIL)	{		Path	   *bitmapqual;		BitmapHeapPath *bpath;		bitmapqual = choose_bitmap_and(root, rel, bitindexpaths, outer_rel);		bpath = create_bitmap_heap_path(root, rel, bitmapqual, outer_rel);		indexpaths = lappend(indexpaths, bpath);	}	/*	 * Now choose the cheapest members of indexpaths.	 */	if (indexpaths != NIL)	{		*cheapest_startup = *cheapest_total = (Path *) linitial(indexpaths);		for_each_cell(l, lnext(list_head(indexpaths)))		{			Path	   *path = (Path *) lfirst(l);			if (compare_path_costs(path, *cheapest_startup, STARTUP_COST) < 0)				*cheapest_startup = path;			if (compare_path_costs(path, *cheapest_total, TOTAL_COST) < 0)				*cheapest_total = path;		}	}	/* Cache the results --- whether positive or negative */	info = makeNode(InnerIndexscanInfo);	info->other_relids = outer_relids;	info->isouterjoin = isouterjoin;	info->cheapest_startup_innerpath = *cheapest_startup;	info->cheapest_total_innerpath = *cheapest_total;	rel->index_inner_paths = lcons(info, rel->index_inner_paths);	MemoryContextSwitchTo(oldcontext);}/* * find_clauses_for_join *	  Generate a list of clauses that are potentially useful for *	  scanning rel as the inner side of a nestloop join. * * We consider both join and restriction clauses.  Any joinclause that uses * only otherrels in the specified outer_relids is fair game.  But there must * be at least one such joinclause in the final list, otherwise we return NIL * indicating that there isn't any potential win here. */static List *find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,					  Relids outer_relids, bool isouterjoin){	List	   *clause_list = NIL;	Relids		join_relids;	ListCell   *l;	/*	 * Look for joinclauses that are usable with given outer_relids.  Note	 * we'll take anything that's applicable to the join whether it has	 * anything to do with an index or not; since we're only building a list,	 * it's not worth filtering more finely here.	 */	join_relids = bms_union(rel->relids, outer_relids);	foreach(l, rel->joininfo)	{		RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);		/* Can't use pushed-down join clauses in outer join */		if (isouterjoin && rinfo->is_pushed_down)			continue;		if (!bms_is_subset(rinfo->required_relids, join_relids))			continue;		clause_list = lappend(clause_list, rinfo);	}	bms_free(join_relids);	/*	 * Also check to see if any EquivalenceClasses can produce a relevant	 * joinclause.	Since all such clauses are effectively pushed-down, this	 * doesn't apply to outer joins.	 */	if (!isouterjoin && rel->has_eclass_joins)		clause_list = list_concat(clause_list,								  find_eclass_clauses_for_index_join(root,																	 rel,															  outer_relids));	/* If no join clause was matched then forget it, per comments above */	if (clause_list == NIL)		return NIL;	/* We can also use any plain restriction clauses for the rel */	clause_list = list_concat(list_copy(rel->baserestrictinfo), clause_list);	return clause_list;}/**************************************************************************** *				----  PATH CREATION UTILITIES  ---- ****************************************************************************//* * flatten_clausegroups_list *	  Given a list of lists of RestrictInfos, flatten it to a list *	  of RestrictInfos. * * This is used to flatten out the result of group_clauses_by_indexkey() * to produce an indexclauses list.  The original list structure mustn't * be altered, but it's OK to share copies of the underlying RestrictInfos. */List *flatten_clausegroups_list(List *clausegroups){	List	   *allclauses = NIL;	ListCell   *l;	foreach(l, clausegroups)		allclauses = list_concat(allclauses, list_copy((List *) lfirst(l)));	return allclauses;}/**************************************************************************** *				----  ROUTINES TO CHECK OPERANDS  ---- ****************************************************************************//* * match_index_to_operand() *	  Generalized test for a match between an index's key *	  and the operand on one side of a restriction or join clause. * * operand: the nodetree to be compared to the index * indexcol: the column number of the index (counting from 0) * index: the index of interest */boolmatch_index_to_operand(Node *operand,					   int indexcol,					   IndexOptInfo *index){	int			indkey;	/*	 * Ignore any RelabelType node above the operand.	This is needed to be	 * able to apply indexscanning in binary-compatible-operator cases. Note:	 * we can assume there is at most one RelabelType node;	 * eval_const_expressions() will have simplified if more than one.	 */	if (operand && IsA(operand, RelabelType))		operand = (Node *) ((RelabelType *) operand)->arg;	indkey = index->indexkeys[indexcol];	if (indkey != 0)	{		/*		 * Simple index column; operand must be a matching Var.		 */		if (operand && IsA(operand, Var) &&			index->rel->relid == ((Var *) operand)->varno &&			indkey == ((Var *) operand)->varattno)			return true;	}	else	{		/*		 * Index expression; find the correct expression.  (This search could		 * be avoided, at the cost of complicating all the callers of this		 * routine; doesn't seem worth it.)		 */		ListCell   *indexpr_item;		int			i;		Node	   *indexkey;		indexpr_item = list_head(index->indexprs);		for (i = 0; i < indexcol; i++)		{			if (index->indexkeys[i] == 0)			{				if (indexpr_item == NULL)					elog(ERROR, "wrong number of index expressions");				indexpr_item = lnext(indexpr_item);			}		}		if (indexpr_item == NULL)			elog(ERROR, "wrong number of index expressions");		indexkey = (Node *) lfirst(indexpr_item);		/*		 * Does it match the operand?  Again, strip any relabeling.		 */		if (indexkey && IsA(indexkey, RelabelType))			indexkey = (Node *) ((RelabelType *) indexkey)->arg;		if (equal(indexkey, operand))			return true;	}	return false;}/**************************************************************************** *			----  ROUTINES FOR "SPECIAL" INDEXABLE OPERATORS  ---- ****************************************************************************//*---------- * These routines handle special optimization of operators that can be * used with index scans even though they are not known to the executor's * indexscan machinery.  The key idea is that these operators allow us * to derive approximate indexscan qual clauses, such that any tuples * that pass the operator clause itself must also satisfy the simpler * indexscan condition(s).	Then we can use the indexscan machinery * to avoid scanning as much of the table as we'd otherwise have to, * while applying the original operator as a qpqual condition to ensure * we deliver only the tuples we want.	(In essence, we're using a regular * index as if it were a lossy index.) * * An example of what we're doing is *			textfield LIKE 'abc%' * from which we can generate the indexscanable conditions *			textfield >= 'abc' AND textfield < 'abd' * which allow efficient scanning of an index on textfield. * (In reality, character set and collation issues make the transformation * from LIKE to indexscan limits rather harder than one might think ... * but that's the basic idea.) * * Another thing that we do with this machinery is to provide special * smarts for "boolean" indexes (that is, indexes on boolean columns * that support boolean equality).	We can transform a plain reference * to the indexkey into "indexkey = true", or "NOT indexkey" into * "indexkey = false", so as to make the expression indexable using the * regular index operators.  (As of Postgres 8.1, we must do this here * because constant simplification does the reverse transformation; * without this code there'd be no way to use such an index at all.) * * Three routines are provided here: * * match_special_index_operator() is just an auxiliary function for * match_clause_to_indexcol(); after the latter fails to recognize a * restriction opclause's operator as a member of an index's opfamily, * it asks match_special_index_operator() whether the clause should be * considered an indexqual anyway. * * match_boolean_index_clause() similarly detects clauses that can be * converted into boolean equality operators. * * expand_indexqual_conditions() converts a list of lists of RestrictInfo * nodes (with implicit AND semantics across list elements) into * a list of clauses that the executor can actually handle.  For operators * that are members of the index's opfamily this transformation is a no-op, * but clauses recognized by match_special_index_operator() or * match_boolean_index_clause() must be converted into one or more "regular" * indexqual conditions. *---------- *//* * match_boolean_index_clause *	  Recognize restriction clauses that can be matched to a boolean index. * * This should be called only when IsBooleanOpfamily() recognizes the * index's operator family.  We check to see if the clause matches the * index's key. */static boolmatch_boolean_index_clause(Node *clause,						   int indexcol,						   IndexOptInfo *index){	/* Direct match? */	if (match_index_to_operand(clause, indexcol, index))		return true;	/* NOT clause? */	if (not_clause(clause))	{		if (match_index_to_operand((Node *) get_notclausearg((Expr *) clause),								   indexcol, index))			return true;	}	/*	 * Since we only consider clauses at top level of WHERE, we can convert	 * indexkey IS TRUE and indexkey IS FALSE to index searches as well. The	 * different meaning for NULL isn't important.	 */	else if (clause && IsA(clause, BooleanTest))	{		BooleanTest *btest = (BooleanTest *) clause;		if (btest->booltesttype == IS_TRUE ||			btest->booltesttype == IS_FALSE)			if (match_index_to_operand((Node *) btest->arg,									   indexcol, index))				return true;	}	return false;}/* * match_special_index_operator *	  Recognize restriction clauses that can be used to generate *	  additional indexscanable qualifications. * * The g

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?