prepqual.c

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

C
979
字号
/*------------------------------------------------------------------------- * * prepqual.c *	  Routines for preprocessing qualification expressions * * 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/prep/prepqual.c,v 1.38 2003/08/08 21:41:52 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/prep.h"#include "utils/lsyscache.h"static Expr *flatten_andors(Expr *qual);static void flatten_andors_and_walker(FastList *out_list, List *andlist);static void flatten_andors_or_walker(FastList *out_list, List *orlist);static List *pull_ands(List *andlist);static void pull_ands_walker(FastList *out_list, List *andlist);static List *pull_ors(List *orlist);static void pull_ors_walker(FastList *out_list, List *orlist);static Expr *find_nots(Expr *qual);static Expr *push_nots(Expr *qual);static Expr *find_ors(Expr *qual);static Expr *or_normalize(List *orlist);static Expr *find_ands(Expr *qual);static Expr *and_normalize(List *andlist);static Expr *qual_cleanup(Expr *qual);static List *remove_duplicates(List *list);static void count_bool_nodes(Expr *qual, double *nodes,				 double *cnfnodes, double *dnfnodes);/***************************************************************************** * *		CNF/DNF CONVERSION ROUTINES * *		These routines convert an arbitrary boolean expression into *		conjunctive normal form or disjunctive normal form. * *		Normalization is only carried out in the top AND/OR/NOT portion *		of the given tree; we do not attempt to normalize boolean expressions *		that may appear as arguments of operators or functions in the tree. * *		Query qualifications (WHERE clauses) are ordinarily transformed into *		CNF, ie, AND-of-ORs form, because then the optimizer can use any one *		of the independent AND clauses as a filtering qualification.  However, *		quals that are naturally expressed as OR-of-ANDs can suffer an *		exponential growth in size in this transformation, so we also consider *		converting to DNF (OR-of-ANDs), and we may also leave well enough alone *		if both transforms cause unreasonable growth.  The OR-of-ANDs format *		is useful for indexscan implementation, so we prefer that format when *		there is just one relation involved. * *		canonicalize_qual() does "smart" conversion to either CNF or DNF, per *		the above considerations, while cnfify() and dnfify() simply perform *		the demanded transformation.  The latter two may become dead code *		eventually. *****************************************************************************//* * canonicalize_qual *	  Convert a qualification to the most useful normalized form. * * Returns the modified qualification. * * If 'removeAndFlag' is true then it removes explicit AND at the top level, * producing a list of implicitly-ANDed conditions.  Otherwise, a regular * boolean expression is returned.	Since most callers pass 'true', we * prefer to declare the result as List *, not Expr *. * * XXX This code could be much smarter, at the cost of also being slower, * if we tried to compute selectivities and/or see whether there are * actually indexes to support an indexscan implementation of a DNF qual. * We could even try converting the CNF clauses that mention a single * relation into a single DNF clause to see if that looks cheaper to * implement.  For now, though, we just try to avoid doing anything * quite as stupid as unconditionally converting to CNF was... */List *canonicalize_qual(Expr *qual, bool removeAndFlag){	Expr	   *newqual;	double		nodes,				cnfnodes,				dnfnodes;	bool		cnfok,				dnfok;	if (qual == NULL)		return NIL;	/*	 * Flatten AND and OR groups throughout the tree. This improvement is	 * always worthwhile, so do it unconditionally.	 */	qual = flatten_andors(qual);	/*	 * Push down NOTs.	We do this only in the top-level boolean	 * expression, without examining arguments of operators/functions.	 * Even so, it might not be a win if we are unable to find negators	 * for all the operators involved; perhaps we should compare before-	 * and-after tree sizes?	 */	newqual = find_nots(qual);	/*	 * Choose whether to convert to CNF, or DNF, or leave well enough	 * alone.	 *	 * We make an approximate estimate of the number of bottom-level nodes	 * that will appear in the CNF and DNF forms of the query.	 */	count_bool_nodes(newqual, &nodes, &cnfnodes, &dnfnodes);	/*	 * First heuristic is to forget about *both* normal forms if there are	 * a huge number of terms in the qual clause.  This would only happen	 * with machine-generated queries, presumably; and most likely such a	 * query is already in either CNF or DNF.	 */	cnfok = dnfok = true;	if (nodes >= 500.0)		cnfok = dnfok = false;	/*	 * Second heuristic is to forget about either CNF or DNF if it shows	 * unreasonable growth compared to the original form of the qual,	 * where we define "unreasonable" a tad arbitrarily as 4x more	 * operators.	 */	if (cnfnodes >= 4.0 * nodes)		cnfok = false;	if (dnfnodes >= 4.0 * nodes)		dnfok = false;	/*	 * Third heuristic is to prefer DNF if top level is already an OR, and	 * only one relation is mentioned, and DNF is no larger than the CNF	 * representation.	(Pretty shaky; can we improve on this?)	 */	if (cnfok && dnfok && dnfnodes <= cnfnodes &&		or_clause((Node *) newqual) &&		NumRelids((Node *) newqual) == 1)		cnfok = false;	/*	 * Otherwise, we prefer CNF.	 *	 * XXX obviously, these rules could be improved upon.	 */	if (cnfok)	{		/*		 * Normalize into conjunctive normal form, and clean up the		 * result.		 */		newqual = qual_cleanup(find_ors(newqual));	}	else if (dnfok)	{		/*		 * Normalize into disjunctive normal form, and clean up the		 * result.		 */		newqual = qual_cleanup(find_ands(newqual));	}	/* Convert to implicit-AND list if requested */	if (removeAndFlag)		newqual = (Expr *) make_ands_implicit(newqual);	return (List *) newqual;}/* * cnfify *	  Convert a qualification to conjunctive normal form by applying *	  successive normalizations. * * Returns the modified qualification. * * If 'removeAndFlag' is true then it removes explicit AND at the top level, * producing a list of implicitly-ANDed conditions.  Otherwise, a regular * boolean expression is returned.	Since most callers pass 'true', we * prefer to declare the result as List *, not Expr *. */List *cnfify(Expr *qual, bool removeAndFlag){	Expr	   *newqual;	if (qual == NULL)		return NIL;	/*	 * Flatten AND and OR groups throughout the tree. This improvement is	 * always worthwhile.	 */	newqual = flatten_andors(qual);	/*	 * Push down NOTs.	We do this only in the top-level boolean	 * expression, without examining arguments of operators/functions.	 */	newqual = find_nots(newqual);	/* Normalize into conjunctive normal form. */	newqual = find_ors(newqual);	/* Clean up the result. */	newqual = qual_cleanup(newqual);	if (removeAndFlag)		newqual = (Expr *) make_ands_implicit(newqual);	return (List *) newqual;}#ifdef NOT_USED/* * dnfify *	  Convert a qualification to disjunctive normal form by applying *	  successive normalizations. * * Returns the modified qualification. * * We do not offer a 'removeOrFlag' in this case; the usages are * different. */static Expr *dnfify(Expr *qual){	Expr	   *newqual;	if (qual == NULL)		return NULL;	/*	 * Flatten AND and OR groups throughout the tree. This improvement is	 * always worthwhile.	 */	newqual = flatten_andors(qual);	/*	 * Push down NOTs.	We do this only in the top-level boolean	 * expression, without examining arguments of operators/functions.	 */	newqual = find_nots(newqual);	/* Normalize into disjunctive normal form. */	newqual = find_ands(newqual);	/* Clean up the result. */	newqual = qual_cleanup(newqual);	return newqual;}#endif/*-------------------- * The parser regards AND and OR as purely binary operators, so a qual like *		(A = 1) OR (A = 2) OR (A = 3) ... * will produce a nested parsetree *		(OR (A = 1) (OR (A = 2) (OR (A = 3) ...))) * In reality, the optimizer and executor regard AND and OR as n-argument * operators, so this tree can be flattened to *		(OR (A = 1) (A = 2) (A = 3) ...) * which is the responsibility of the routines below. * * flatten_andors() does the basic transformation with no initial assumptions. * pull_ands() and pull_ors() are used to maintain flatness of the AND/OR * tree after local transformations that might introduce nested AND/ORs. *-------------------- *//*-------------------- * flatten_andors *	  Given a qualification, simplify nested AND/OR clauses into flat *	  AND/OR clauses with more arguments. * * Returns the rebuilt expr (note original list structure is not touched). *-------------------- */static Expr *flatten_andors(Expr *qual){	if (qual == NULL)		return NULL;	if (and_clause((Node *) qual))	{		FastList	out_list;		FastListInit(&out_list);		flatten_andors_and_walker(&out_list, ((BoolExpr *) qual)->args);		return make_andclause(FastListValue(&out_list));	}	else if (or_clause((Node *) qual))	{		FastList	out_list;		FastListInit(&out_list);		flatten_andors_or_walker(&out_list, ((BoolExpr *) qual)->args);		return make_orclause(FastListValue(&out_list));	}	else if (not_clause((Node *) qual))		return make_notclause(flatten_andors(get_notclausearg(qual)));	else if (is_opclause(qual))	{		OpExpr	   *opexpr = (OpExpr *) qual;		Expr	   *left = (Expr *) get_leftop(qual);		Expr	   *right = (Expr *) get_rightop(qual);		return make_opclause(opexpr->opno,							 opexpr->opresulttype,							 opexpr->opretset,							 flatten_andors(left),							 flatten_andors(right));	}	else		return qual;}static voidflatten_andors_and_walker(FastList *out_list, List *andlist){	List	   *arg;	foreach(arg, andlist)	{		Expr	   *subexpr = (Expr *) lfirst(arg);		if (and_clause((Node *) subexpr))			flatten_andors_and_walker(out_list, ((BoolExpr *) subexpr)->args);		else			FastAppend(out_list, flatten_andors(subexpr));	}}static voidflatten_andors_or_walker(FastList *out_list, List *orlist){	List	   *arg;	foreach(arg, orlist)	{		Expr	   *subexpr = (Expr *) lfirst(arg);		if (or_clause((Node *) subexpr))			flatten_andors_or_walker(out_list, ((BoolExpr *) subexpr)->args);		else			FastAppend(out_list, flatten_andors(subexpr));	}}/* * pull_ands *	  Recursively flatten nested AND clauses into a single and-clause list. * * Input is the arglist of an AND clause. * Returns the rebuilt arglist (note original list structure is not touched). */static List *pull_ands(List *andlist){	FastList	out_list;	FastListInit(&out_list);	pull_ands_walker(&out_list, andlist);	return FastListValue(&out_list);}static voidpull_ands_walker(FastList *out_list, List *andlist){	List	   *arg;	foreach(arg, andlist)	{		Expr	   *subexpr = (Expr *) lfirst(arg);		if (and_clause((Node *) subexpr))			pull_ands_walker(out_list, ((BoolExpr *) subexpr)->args);		else			FastAppend(out_list, subexpr);	}}/* * pull_ors *	  Recursively flatten nested OR clauses into a single or-clause list. * * Input is the arglist of an OR clause. * Returns the rebuilt arglist (note original list structure is not touched). */static List *pull_ors(List *orlist){	FastList	out_list;	FastListInit(&out_list);	pull_ors_walker(&out_list, orlist);	return FastListValue(&out_list);}static voidpull_ors_walker(FastList *out_list, List *orlist){	List	   *arg;	foreach(arg, orlist)	{		Expr	   *subexpr = (Expr *) lfirst(arg);		if (or_clause((Node *) subexpr))			pull_ors_walker(out_list, ((BoolExpr *) subexpr)->args);		else			FastAppend(out_list, subexpr);	}}/* * find_nots *	  Traverse the qualification, looking for 'NOT's to take care of. *	  For 'NOT' clauses, apply push_not() to try to push down the 'NOT'. *	  For all other clause types, simply recurse. * * Returns the modified qualification.	AND/OR flatness is preserved. */static Expr *find_nots(Expr *qual){	if (qual == NULL)		return NULL;#ifdef NOT_USED	/* recursing into operator expressions is probably not worth it. */	if (is_opclause(qual))	{		OpExpr	   *opexpr = (OpExpr *) qual;		Expr	   *left = (Expr *) get_leftop(qual);		Expr	   *right = (Expr *) get_rightop(qual);		return make_opclause(opexpr->opno,							 opexpr->opresulttype,							 opexpr->opretset,							 find_nots(left),							 find_nots(right));	}#endif	if (and_clause((Node *) qual))	{		FastList	t_list;		List	   *temp;		FastListInit(&t_list);		foreach(temp, ((BoolExpr *) qual)->args)			FastAppend(&t_list, find_nots(lfirst(temp)));		return make_andclause(pull_ands(FastListValue(&t_list)));	}	else if (or_clause((Node *) qual))	{		FastList	t_list;		List	   *temp;		FastListInit(&t_list);		foreach(temp, ((BoolExpr *) qual)->args)			FastAppend(&t_list, find_nots(lfirst(temp)));		return make_orclause(pull_ors(FastListValue(&t_list)));	}	else if (not_clause((Node *) qual))		return push_nots(get_notclausearg(qual));	else		return qual;}/* * push_nots *	  Push down a 'NOT' as far as possible. * * Input is an expression to be negated (e.g., the argument of a NOT clause). * Returns a new qual equivalent to the negation of the given qual. */static Expr *

⌨️ 快捷键说明

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