equivclass.c

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

C
1,975
字号
/*------------------------------------------------------------------------- * * equivclass.c *	  Routines for managing EquivalenceClasses * * See src/backend/optimizer/README for discussion of EquivalenceClasses. * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.9.2.1 2008/03/31 16:59:33 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/skey.h"#include "optimizer/clauses.h"#include "optimizer/cost.h"#include "optimizer/paths.h"#include "optimizer/planmain.h"#include "optimizer/prep.h"#include "optimizer/var.h"#include "utils/lsyscache.h"static EquivalenceMember *add_eq_member(EquivalenceClass *ec,			  Expr *expr, Relids relids,			  bool is_child, Oid datatype);static void generate_base_implied_equalities_const(PlannerInfo *root,									   EquivalenceClass *ec);static void generate_base_implied_equalities_no_const(PlannerInfo *root,										  EquivalenceClass *ec);static void generate_base_implied_equalities_broken(PlannerInfo *root,										EquivalenceClass *ec);static List *generate_join_implied_equalities_normal(PlannerInfo *root,										EquivalenceClass *ec,										RelOptInfo *joinrel,										RelOptInfo *outer_rel,										RelOptInfo *inner_rel);static List *generate_join_implied_equalities_broken(PlannerInfo *root,										EquivalenceClass *ec,										RelOptInfo *joinrel,										RelOptInfo *outer_rel,										RelOptInfo *inner_rel);static Oid select_equality_operator(EquivalenceClass *ec,						 Oid lefttype, Oid righttype);static RestrictInfo *create_join_clause(PlannerInfo *root,				   EquivalenceClass *ec, Oid opno,				   EquivalenceMember *leftem,				   EquivalenceMember *rightem,				   EquivalenceClass *parent_ec);static bool reconsider_outer_join_clause(PlannerInfo *root,							 RestrictInfo *rinfo,							 bool outer_on_left);static bool reconsider_full_join_clause(PlannerInfo *root,							RestrictInfo *rinfo);/* * process_equivalence *	  The given clause has a mergejoinable operator and can be applied without *	  any delay by an outer join, so its two sides can be considered equal *	  anywhere they are both computable; moreover that equality can be *	  extended transitively.  Record this knowledge in the EquivalenceClass *	  data structure.  Returns TRUE if successful, FALSE if not (in which *	  case caller should treat the clause as ordinary, not an equivalence). * * If below_outer_join is true, then the clause was found below the nullable * side of an outer join, so its sides might validly be both NULL rather than * strictly equal.	We can still deduce equalities in such cases, but we take * care to mark an EquivalenceClass if it came from any such clauses.  Also, * we have to check that both sides are either pseudo-constants or strict * functions of Vars, else they might not both go to NULL above the outer * join.  (This is the reason why we need a failure return.  It's more * convenient to check this case here than at the call sites...) * * Note: constructing merged EquivalenceClasses is a standard UNION-FIND * problem, for which there exist better data structures than simple lists. * If this code ever proves to be a bottleneck then it could be sped up --- * but for now, simple is beautiful. * * Note: this is only called during planner startup, not during GEQO * exploration, so we need not worry about whether we're in the right * memory context. */boolprocess_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,					bool below_outer_join){	Expr	   *clause = restrictinfo->clause;	Oid			opno,				item1_type,				item2_type;	Expr	   *item1;	Expr	   *item2;	Relids		item1_relids,				item2_relids;	List	   *opfamilies;	EquivalenceClass *ec1,			   *ec2;	EquivalenceMember *em1,			   *em2;	ListCell   *lc1;	/* Extract info from given clause */	Assert(is_opclause(clause));	opno = ((OpExpr *) clause)->opno;	item1 = (Expr *) get_leftop(clause);	item2 = (Expr *) get_rightop(clause);	item1_relids = restrictinfo->left_relids;	item2_relids = restrictinfo->right_relids;	/*	 * If below outer join, check for strictness, else reject.	 */	if (below_outer_join)	{		if (!bms_is_empty(item1_relids) &&			contain_nonstrict_functions((Node *) item1))			return false;		/* LHS is non-strict but not constant */		if (!bms_is_empty(item2_relids) &&			contain_nonstrict_functions((Node *) item2))			return false;		/* RHS is non-strict but not constant */	}	/*	 * We use the declared input types of the operator, not exprType() of the	 * inputs, as the nominal datatypes for opfamily lookup.  This presumes	 * that btree operators are always registered with amoplefttype and	 * amoprighttype equal to their declared input types.  We will need this	 * info anyway to build EquivalenceMember nodes, and by extracting it now	 * we can use type comparisons to short-circuit some equal() tests.	 */	op_input_types(opno, &item1_type, &item2_type);	opfamilies = restrictinfo->mergeopfamilies;	/*	 * Sweep through the existing EquivalenceClasses looking for matches to	 * item1 and item2.  These are the possible outcomes:	 *	 * 1. We find both in the same EC.	The equivalence is already known, so	 * there's nothing to do.	 *	 * 2. We find both in different ECs.  Merge the two ECs together.	 *	 * 3. We find just one.  Add the other to its EC.	 *	 * 4. We find neither.	Make a new, two-entry EC.	 *	 * Note: since all ECs are built through this process, it's impossible	 * that we'd match an item in more than one existing EC.  It is possible	 * to match more than once within an EC, if someone fed us something silly	 * like "WHERE X=X".  (However, we can't simply discard such clauses,	 * since they should fail when X is null; so we will build a 2-member EC	 * to ensure the correct restriction clause gets generated.  Hence there	 * is no shortcut here for item1 and item2 equal.)	 */	ec1 = ec2 = NULL;	em1 = em2 = NULL;	foreach(lc1, root->eq_classes)	{		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);		ListCell   *lc2;		/* Never match to a volatile EC */		if (cur_ec->ec_has_volatile)			continue;		/*		 * A "match" requires matching sets of btree opfamilies.  Use of		 * equal() for this test has implications discussed in the comments		 * for get_mergejoin_opfamilies().		 */		if (!equal(opfamilies, cur_ec->ec_opfamilies))			continue;		foreach(lc2, cur_ec->ec_members)		{			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);			Assert(!cur_em->em_is_child);		/* no children yet */			/*			 * If below an outer join, don't match constants: they're not as			 * constant as they look.			 */			if ((below_outer_join || cur_ec->ec_below_outer_join) &&				cur_em->em_is_const)				continue;			if (!ec1 &&				item1_type == cur_em->em_datatype &&				equal(item1, cur_em->em_expr))			{				ec1 = cur_ec;				em1 = cur_em;				if (ec2)					break;			}			if (!ec2 &&				item2_type == cur_em->em_datatype &&				equal(item2, cur_em->em_expr))			{				ec2 = cur_ec;				em2 = cur_em;				if (ec1)					break;			}		}		if (ec1 && ec2)			break;	}	/* Sweep finished, what did we find? */	if (ec1 && ec2)	{		/* If case 1, nothing to do, except add to sources */		if (ec1 == ec2)		{			ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);			ec1->ec_below_outer_join |= below_outer_join;			/* mark the RI as usable with this pair of EMs */			/* NB: can't set left_ec/right_ec until merging is finished */			restrictinfo->left_em = em1;			restrictinfo->right_em = em2;			return true;		}		/*		 * Case 2: need to merge ec1 and ec2.  We add ec2's items to ec1, then		 * set ec2's ec_merged link to point to ec1 and remove ec2 from the		 * eq_classes list.  We cannot simply delete ec2 because that could		 * leave dangling pointers in existing PathKeys.  We leave it behind		 * with a link so that the merged EC can be found.		 */		ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);		ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);		ec1->ec_derives = list_concat(ec1->ec_derives, ec2->ec_derives);		ec1->ec_relids = bms_join(ec1->ec_relids, ec2->ec_relids);		ec1->ec_has_const |= ec2->ec_has_const;		/* can't need to set has_volatile */		ec1->ec_below_outer_join |= ec2->ec_below_outer_join;		ec2->ec_merged = ec1;		root->eq_classes = list_delete_ptr(root->eq_classes, ec2);		/* just to avoid debugging confusion w/ dangling pointers: */		ec2->ec_members = NIL;		ec2->ec_sources = NIL;		ec2->ec_derives = NIL;		ec2->ec_relids = NULL;		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);		ec1->ec_below_outer_join |= below_outer_join;		/* mark the RI as usable with this pair of EMs */		restrictinfo->left_em = em1;		restrictinfo->right_em = em2;	}	else if (ec1)	{		/* Case 3: add item2 to ec1 */		em2 = add_eq_member(ec1, item2, item2_relids, false, item2_type);		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);		ec1->ec_below_outer_join |= below_outer_join;		/* mark the RI as usable with this pair of EMs */		restrictinfo->left_em = em1;		restrictinfo->right_em = em2;	}	else if (ec2)	{		/* Case 3: add item1 to ec2 */		em1 = add_eq_member(ec2, item1, item1_relids, false, item1_type);		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);		ec2->ec_below_outer_join |= below_outer_join;		/* mark the RI as usable with this pair of EMs */		restrictinfo->left_em = em1;		restrictinfo->right_em = em2;	}	else	{		/* Case 4: make a new, two-entry EC */		EquivalenceClass *ec = makeNode(EquivalenceClass);		ec->ec_opfamilies = opfamilies;		ec->ec_members = NIL;		ec->ec_sources = list_make1(restrictinfo);		ec->ec_derives = NIL;		ec->ec_relids = NULL;		ec->ec_has_const = false;		ec->ec_has_volatile = false;		ec->ec_below_outer_join = below_outer_join;		ec->ec_broken = false;		ec->ec_sortref = 0;		ec->ec_merged = NULL;		em1 = add_eq_member(ec, item1, item1_relids, false, item1_type);		em2 = add_eq_member(ec, item2, item2_relids, false, item2_type);		root->eq_classes = lappend(root->eq_classes, ec);		/* mark the RI as usable with this pair of EMs */		restrictinfo->left_em = em1;		restrictinfo->right_em = em2;	}	return true;}/* * add_eq_member - build a new EquivalenceMember and add it to an EC */static EquivalenceMember *add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,			  bool is_child, Oid datatype){	EquivalenceMember *em = makeNode(EquivalenceMember);	em->em_expr = expr;	em->em_relids = relids;	em->em_is_const = false;	em->em_is_child = is_child;	em->em_datatype = datatype;	if (bms_is_empty(relids))	{		/*		 * No Vars, assume it's a pseudoconstant.  This is correct for entries		 * generated from process_equivalence(), because a WHERE clause can't		 * contain aggregates or SRFs, and non-volatility was checked before		 * process_equivalence() ever got called.  But		 * get_eclass_for_sort_expr() has to work harder.  We put the tests		 * there not here to save cycles in the equivalence case.		 */		Assert(!is_child);		em->em_is_const = true;		ec->ec_has_const = true;		/* it can't affect ec_relids */	}	else if (!is_child)			/* child members don't add to ec_relids */	{		ec->ec_relids = bms_add_members(ec->ec_relids, relids);	}	ec->ec_members = lappend(ec->ec_members, em);	return em;}/* * get_eclass_for_sort_expr *	  Given an expression and opfamily info, find an existing equivalence *	  class it is a member of; if none, build a new single-member *	  EquivalenceClass for it. * * sortref is the SortGroupRef of the originating SortClause, if any, * or zero if not. * * This can be used safely both before and after EquivalenceClass merging; * since it never causes merging it does not invalidate any existing ECs * or PathKeys. * * Note: opfamilies must be chosen consistently with the way * process_equivalence() would do; that is, generated from a mergejoinable * equality operator.  Else we might fail to detect valid equivalences, * generating poor (but not incorrect) plans. */EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,						 Expr *expr,						 Oid expr_datatype,						 List *opfamilies,						 Index sortref){	EquivalenceClass *newec;	EquivalenceMember *newem;	ListCell   *lc1;	MemoryContext oldcontext;	/*	 * Scan through the existing EquivalenceClasses for a match	 */	foreach(lc1, root->eq_classes)	{		EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);		ListCell   *lc2;		/* Never match to a volatile EC */		if (cur_ec->ec_has_volatile)			continue;		if (!equal(opfamilies, cur_ec->ec_opfamilies))			continue;

⌨️ 快捷键说明

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