clausesel.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 564 行 · 第 1/2 页

C
564
字号
/*------------------------------------------------------------------------- * * clausesel.c *	  Routines to compute clause selectivities * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.60 2003/08/04 02:40:00 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/cost.h"#include "optimizer/plancat.h"#include "optimizer/restrictinfo.h"#include "parser/parsetree.h"#include "utils/fmgroids.h"#include "utils/lsyscache.h"#include "utils/selfuncs.h"/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */#define MAKEBOOLCONST(val,isnull) \	((Node *) makeConst(BOOLOID, 1, (Datum) (val), (isnull), true))/* * Data structure for accumulating info about possible range-query * clause pairs in clauselist_selectivity. */typedef struct RangeQueryClause{	struct RangeQueryClause *next;		/* next in linked list */	Node	   *var;			/* The common variable of the clauses */	bool		have_lobound;	/* found a low-bound clause yet? */	bool		have_hibound;	/* found a high-bound clause yet? */	Selectivity lobound;		/* Selectivity of a var > something clause */	Selectivity hibound;		/* Selectivity of a var < something clause */} RangeQueryClause;static void addRangeClause(RangeQueryClause **rqlist, Node *clause,			   bool varonleft, bool isLTsel, Selectivity s2);/**************************************************************************** *		ROUTINES TO COMPUTE SELECTIVITIES ****************************************************************************//* * restrictlist_selectivity - *	  Compute the selectivity of an implicitly-ANDed list of RestrictInfo *	  clauses. * * This is the same as clauselist_selectivity except for the representation * of the clause list. */Selectivityrestrictlist_selectivity(Query *root,						 List *restrictinfo_list,						 int varRelid,						 JoinType jointype){	List	   *clauselist = get_actual_clauses(restrictinfo_list);	Selectivity result;	result = clauselist_selectivity(root, clauselist, varRelid, jointype);	freeList(clauselist);	return result;}/* * clauselist_selectivity - *	  Compute the selectivity of an implicitly-ANDed list of boolean *	  expression clauses.  The list can be empty, in which case 1.0 *	  must be returned. * * See clause_selectivity() for the meaning of the additional parameters. * * Our basic approach is to take the product of the selectivities of the * subclauses.	However, that's only right if the subclauses have independent * probabilities, and in reality they are often NOT independent.  So, * we want to be smarter where we can. * Currently, the only extra smarts we have is to recognize "range queries", * such as "x > 34 AND x < 42".  Clauses are recognized as possible range * query components if they are restriction opclauses whose operators have * scalarltsel() or scalargtsel() as their restriction selectivity estimator. * We pair up clauses of this form that refer to the same variable.  An * unpairable clause of this kind is simply multiplied into the selectivity * product in the normal way.  But when we find a pair, we know that the * selectivities represent the relative positions of the low and high bounds * within the column's range, so instead of figuring the selectivity as * hisel * losel, we can figure it as hisel + losel - 1.  (To visualize this, * see that hisel is the fraction of the range below the high bound, while * losel is the fraction above the low bound; so hisel can be interpreted * directly as a 0..1 value but we need to convert losel to 1-losel before * interpreting it as a value.	Then the available range is 1-losel to hisel. * However, this calculation double-excludes nulls, so really we need * hisel + losel + null_frac - 1.) * If the calculation yields zero or negative, however, we chicken out and * use a default estimate; that probably means that one or both * selectivities is a default estimate rather than an actual range value. * Of course this is all very dependent on the behavior of * scalarltsel/scalargtsel; perhaps some day we can generalize the approach. */Selectivityclauselist_selectivity(Query *root,					   List *clauses,					   int varRelid,					   JoinType jointype){	Selectivity s1 = 1.0;	RangeQueryClause *rqlist = NULL;	List	   *clist;	/*	 * Initial scan over clauses.  Anything that doesn't look like a	 * potential rangequery clause gets multiplied into s1 and forgotten.	 * Anything that does gets inserted into an rqlist entry.	 */	foreach(clist, clauses)	{		Node	   *clause = (Node *) lfirst(clist);		Selectivity s2;		/*		 * See if it looks like a restriction clause with a pseudoconstant		 * on one side.  (Anything more complicated than that might not		 * behave in the simple way we are expecting.)		 *		 * NB: for consistency of results, this fragment of code had better		 * match what clause_selectivity() would do in the cases it		 * handles.		 */		if (is_opclause(clause) &&			(varRelid != 0 || NumRelids(clause) == 1))		{			OpExpr	   *expr = (OpExpr *) clause;			if (length(expr->args) == 2)			{				bool		varonleft = true;				if (is_pseudo_constant_clause(lsecond(expr->args)) ||					(varonleft = false,					 is_pseudo_constant_clause(lfirst(expr->args))))				{					Oid			opno = expr->opno;					RegProcedure oprrest = get_oprrest(opno);					s2 = restriction_selectivity(root, opno,												 expr->args, varRelid);					/*					 * If we reach here, we have computed the same result					 * that clause_selectivity would, so we can just use					 * s2 if it's the wrong oprrest.  But if it's the					 * right oprrest, add the clause to rqlist for later					 * processing.					 */					switch (oprrest)					{						case F_SCALARLTSEL:							addRangeClause(&rqlist, clause,										   varonleft, true, s2);							break;						case F_SCALARGTSEL:							addRangeClause(&rqlist, clause,										   varonleft, false, s2);							break;						default:							/* Just merge the selectivity in generically */							s1 = s1 * s2;							break;					}					continue;	/* drop to loop bottom */				}			}		}		/* Not the right form, so treat it generically. */		s2 = clause_selectivity(root, clause, varRelid, jointype);		s1 = s1 * s2;	}	/*	 * Now scan the rangequery pair list.	 */	while (rqlist != NULL)	{		RangeQueryClause *rqnext;		if (rqlist->have_lobound && rqlist->have_hibound)		{			/* Successfully matched a pair of range clauses */			Selectivity s2 = rqlist->hibound + rqlist->lobound - 1.0;			/* Adjust for double-exclusion of NULLs */			s2 += nulltestsel(root, IS_NULL, rqlist->var, varRelid);			/*			 * A zero or slightly negative s2 should be converted into a			 * small positive value; we probably are dealing with a very			 * tight range and got a bogus result due to roundoff errors.			 * However, if s2 is very negative, then we probably have			 * default selectivity estimates on one or both sides of the			 * range.  In that case, insert a not-so-wildly-optimistic			 * default estimate.			 */			if (s2 <= 0.0)			{				if (s2 < -0.01)				{					/*					 * No data available --- use a default estimate that					 * is small, but not real small.					 */					s2 = 0.005;				}				else				{					/*					 * It's just roundoff error; use a small positive					 * value					 */					s2 = 1.0e-10;				}			}			/* Merge in the selectivity of the pair of clauses */			s1 *= s2;		}		else		{			/* Only found one of a pair, merge it in generically */			if (rqlist->have_lobound)				s1 *= rqlist->lobound;			else				s1 *= rqlist->hibound;		}		/* release storage and advance */		rqnext = rqlist->next;		pfree(rqlist);		rqlist = rqnext;	}	return s1;}/* * addRangeClause --- add a new range clause for clauselist_selectivity * * Here is where we try to match up pairs of range-query clauses */static voidaddRangeClause(RangeQueryClause **rqlist, Node *clause,			   bool varonleft, bool isLTsel, Selectivity s2){	RangeQueryClause *rqelem;	Node	   *var;	bool		is_lobound;	if (varonleft)	{		var = get_leftop((Expr *) clause);		is_lobound = !isLTsel;	/* x < something is high bound */	}	else	{		var = get_rightop((Expr *) clause);		is_lobound = isLTsel;	/* something < x is low bound */	}	for (rqelem = *rqlist; rqelem; rqelem = rqelem->next)	{		/*

⌨️ 快捷键说明

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