equivclass.c

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

C
1,975
字号
		foreach(lc2, cur_ec->ec_members)		{			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);			/*			 * If below an outer join, don't match constants: they're not as			 * constant as they look.			 */			if (cur_ec->ec_below_outer_join &&				cur_em->em_is_const)				continue;			if (expr_datatype == cur_em->em_datatype &&				equal(expr, cur_em->em_expr))				return cur_ec;	/* Match! */		}	}	/*	 * No match, so build a new single-member EC	 *	 * Here, we must be sure that we construct the EC in the right context. We	 * can assume, however, that the passed expr is long-lived.	 */	oldcontext = MemoryContextSwitchTo(root->planner_cxt);	newec = makeNode(EquivalenceClass);	newec->ec_opfamilies = list_copy(opfamilies);	newec->ec_members = NIL;	newec->ec_sources = NIL;	newec->ec_derives = NIL;	newec->ec_relids = NULL;	newec->ec_has_const = false;	newec->ec_has_volatile = contain_volatile_functions((Node *) expr);	newec->ec_below_outer_join = false;	newec->ec_broken = false;	newec->ec_sortref = sortref;	newec->ec_merged = NULL;	newem = add_eq_member(newec, expr, pull_varnos((Node *) expr),						  false, expr_datatype);	/*	 * add_eq_member doesn't check for volatile functions, set-returning	 * functions, or aggregates, but such could appear in sort expressions; so	 * we have to check whether its const-marking was correct.	 */	if (newec->ec_has_const)	{		if (newec->ec_has_volatile ||			expression_returns_set((Node *) expr) ||			contain_agg_clause((Node *) expr))		{			newec->ec_has_const = false;			newem->em_is_const = false;		}	}	root->eq_classes = lappend(root->eq_classes, newec);	MemoryContextSwitchTo(oldcontext);	return newec;}/* * generate_base_implied_equalities *	  Generate any restriction clauses that we can deduce from equivalence *	  classes. * * When an EC contains pseudoconstants, our strategy is to generate * "member = const1" clauses where const1 is the first constant member, for * every other member (including other constants).	If we are able to do this * then we don't need any "var = var" comparisons because we've successfully * constrained all the vars at their points of creation.  If we fail to * generate any of these clauses due to lack of cross-type operators, we fall * back to the "ec_broken" strategy described below.  (XXX if there are * multiple constants of different types, it's possible that we might succeed * in forming all the required clauses if we started from a different const * member; but this seems a sufficiently hokey corner case to not be worth * spending lots of cycles on.) * * For ECs that contain no pseudoconstants, we generate derived clauses * "member1 = member2" for each pair of members belonging to the same base * relation (actually, if there are more than two for the same base relation, * we only need enough clauses to link each to each other).  This provides * the base case for the recursion: each row emitted by a base relation scan * will constrain all computable members of the EC to be equal.  As each * join path is formed, we'll add additional derived clauses on-the-fly * to maintain this invariant (see generate_join_implied_equalities). * * If the opfamilies used by the EC do not provide complete sets of cross-type * equality operators, it is possible that we will fail to generate a clause * that must be generated to maintain the invariant.  (An example: given * "WHERE a.x = b.y AND b.y = a.z", the scheme breaks down if we cannot * generate "a.x = a.z" as a restriction clause for A.)  In this case we mark * the EC "ec_broken" and fall back to regurgitating its original source * RestrictInfos at appropriate times.	We do not try to retract any derived * clauses already generated from the broken EC, so the resulting plan could * be poor due to bad selectivity estimates caused by redundant clauses.  But * the correct solution to that is to fix the opfamilies ... * * Equality clauses derived by this function are passed off to * process_implied_equality (in plan/initsplan.c) to be inserted into the * restrictinfo datastructures.  Note that this must be called after initial * scanning of the quals and before Path construction begins. * * We make no attempt to avoid generating duplicate RestrictInfos here: we * don't search ec_sources for matches, nor put the created RestrictInfos * into ec_derives.  Doing so would require some slightly ugly changes in * initsplan.c's API, and there's no real advantage, because the clauses * generated here can't duplicate anything we will generate for joins anyway. */voidgenerate_base_implied_equalities(PlannerInfo *root){	ListCell   *lc;	Index		rti;	foreach(lc, root->eq_classes)	{		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);		Assert(ec->ec_merged == NULL);	/* else shouldn't be in list */		Assert(!ec->ec_broken); /* not yet anyway... */		/* Single-member ECs won't generate any deductions */		if (list_length(ec->ec_members) <= 1)			continue;		if (ec->ec_has_const)			generate_base_implied_equalities_const(root, ec);		else			generate_base_implied_equalities_no_const(root, ec);		/* Recover if we failed to generate required derived clauses */		if (ec->ec_broken)			generate_base_implied_equalities_broken(root, ec);	}	/*	 * This is also a handy place to mark base rels (which should all exist by	 * now) with flags showing whether they have pending eclass joins.	 */	for (rti = 1; rti < root->simple_rel_array_size; rti++)	{		RelOptInfo *brel = root->simple_rel_array[rti];		if (brel == NULL)			continue;		brel->has_eclass_joins = has_relevant_eclass_joinclause(root, brel);	}}/* * generate_base_implied_equalities when EC contains pseudoconstant(s) */static voidgenerate_base_implied_equalities_const(PlannerInfo *root,									   EquivalenceClass *ec){	EquivalenceMember *const_em = NULL;	ListCell   *lc;	/*	 * In the trivial case where we just had one "var = const" clause,	 * push the original clause back into the main planner machinery.  There	 * is nothing to be gained by doing it differently, and we save the	 * effort to re-build and re-analyze an equality clause that will be	 * exactly equivalent to the old one.	 */	if (list_length(ec->ec_members) == 2 &&		list_length(ec->ec_sources) == 1)	{		RestrictInfo *restrictinfo = (RestrictInfo *) linitial(ec->ec_sources);		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)		{			distribute_restrictinfo_to_rels(root, restrictinfo);			return;		}	}	/* Find the constant member to use */	foreach(lc, ec->ec_members)	{		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);		if (cur_em->em_is_const)		{			const_em = cur_em;			break;		}	}	Assert(const_em != NULL);	/* Generate a derived equality against each other member */	foreach(lc, ec->ec_members)	{		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);		Oid			eq_op;		Assert(!cur_em->em_is_child);	/* no children yet */		if (cur_em == const_em)			continue;		eq_op = select_equality_operator(ec,										 cur_em->em_datatype,										 const_em->em_datatype);		if (!OidIsValid(eq_op))		{			/* failed... */			ec->ec_broken = true;			break;		}		process_implied_equality(root, eq_op,								 cur_em->em_expr, const_em->em_expr,								 ec->ec_relids,								 ec->ec_below_outer_join,								 cur_em->em_is_const);	}}/* * generate_base_implied_equalities when EC contains no pseudoconstants */static voidgenerate_base_implied_equalities_no_const(PlannerInfo *root,										  EquivalenceClass *ec){	EquivalenceMember **prev_ems;	ListCell   *lc;	/*	 * We scan the EC members once and track the last-seen member for each	 * base relation.  When we see another member of the same base relation,	 * we generate "prev_mem = cur_mem".  This results in the minimum number	 * of derived clauses, but it's possible that it will fail when a	 * different ordering would succeed.  XXX FIXME: use a UNION-FIND	 * algorithm similar to the way we build merged ECs.  (Use a list-of-lists	 * for each rel.)	 */	prev_ems = (EquivalenceMember **)		palloc0(root->simple_rel_array_size * sizeof(EquivalenceMember *));	foreach(lc, ec->ec_members)	{		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);		int			relid;		Assert(!cur_em->em_is_child);	/* no children yet */		if (bms_membership(cur_em->em_relids) != BMS_SINGLETON)			continue;		relid = bms_singleton_member(cur_em->em_relids);		Assert(relid < root->simple_rel_array_size);		if (prev_ems[relid] != NULL)		{			EquivalenceMember *prev_em = prev_ems[relid];			Oid			eq_op;			eq_op = select_equality_operator(ec,											 prev_em->em_datatype,											 cur_em->em_datatype);			if (!OidIsValid(eq_op))			{				/* failed... */				ec->ec_broken = true;				break;			}			process_implied_equality(root, eq_op,									 prev_em->em_expr, cur_em->em_expr,									 ec->ec_relids,									 ec->ec_below_outer_join,									 false);		}		prev_ems[relid] = cur_em;	}	pfree(prev_ems);	/*	 * We also have to make sure that all the Vars used in the member clauses	 * will be available at any join node we might try to reference them at.	 * For the moment we force all the Vars to be available at all join nodes	 * for this eclass.  Perhaps this could be improved by doing some	 * pre-analysis of which members we prefer to join, but it's no worse than	 * what happened in the pre-8.3 code.	 */	foreach(lc, ec->ec_members)	{		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);		List	   *vars = pull_var_clause((Node *) cur_em->em_expr, false);		add_vars_to_targetlist(root, vars, ec->ec_relids);		list_free(vars);	}}/* * generate_base_implied_equalities cleanup after failure * * What we must do here is push any zero- or one-relation source RestrictInfos * of the EC back into the main restrictinfo datastructures.  Multi-relation * clauses will be regurgitated later by generate_join_implied_equalities(). * (We do it this way to maintain continuity with the case that ec_broken * becomes set only after we've gone up a join level or two.) */static voidgenerate_base_implied_equalities_broken(PlannerInfo *root,										EquivalenceClass *ec){	ListCell   *lc;	foreach(lc, ec->ec_sources)	{		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);		if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)			distribute_restrictinfo_to_rels(root, restrictinfo);	}}/* * generate_join_implied_equalities *	  Generate any join clauses that we can deduce from equivalence classes. * * At a join node, we must enforce restriction clauses sufficient to ensure * that all equivalence-class members computable at that node are equal. * Since the set of clauses to enforce can vary depending on which subset * relations are the inputs, we have to compute this afresh for each join * path pair.  Hence a fresh List of RestrictInfo nodes is built and passed * back on each call. * * The results are sufficient for use in merge, hash, and plain nestloop join * methods.  We do not worry here about selecting clauses that are optimal * for use in a nestloop-with-inner-indexscan join, however.  indxpath.c makes * its own selections of clauses to use, and if the ones we pick here are * redundant with those, the extras will be eliminated in createplan.c. * * Because the same join clauses are likely to be needed multiple times as * we consider different join paths, we avoid generating multiple copies: * whenever we select a particular pair of EquivalenceMembers to join, * we check to see if the pair matches any original clause (in ec_sources) * or previously-built clause (in ec_derives).	This saves memory and allows * re-use of information cached in RestrictInfos. */List *generate_join_implied_equalities(PlannerInfo *root,								 RelOptInfo *joinrel,								 RelOptInfo *outer_rel,								 RelOptInfo *inner_rel){	List	   *result = NIL;	ListCell   *lc;	foreach(lc, root->eq_classes)	{		EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);		List	   *sublist = NIL;		/* ECs containing consts do not need any further enforcement */		if (ec->ec_has_const)			continue;		/* Single-member ECs won't generate any deductions */		if (list_length(ec->ec_members) <= 1)			continue;		/* We can quickly ignore any that don't overlap the join, too */		if (!bms_overlap(ec->ec_relids, joinrel->relids))			continue;		if (!ec->ec_broken)			sublist = generate_join_implied_equalities_normal(root,															  ec,															  joinrel,															  outer_rel,															  inner_rel);		/* Recover if we failed to generate required derived clauses */		if (ec->ec_broken)			sublist = generate_join_implied_equalities_broken(root,															  ec,															  joinrel,															  outer_rel,															  inner_rel);		result = list_concat(result, sublist);	}	return result;}

⌨️ 快捷键说明

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