prepqual.c

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

C
979
字号
push_nots(Expr *qual){	if (qual == NULL)		return make_notclause(qual);	/* XXX is this right?  Or										 * possible? */	/*	 * Negate an operator clause if possible: ("NOT" (< A B)) => (> A B)	 * Otherwise, retain the clause as it is (the 'not' can't be pushed	 * down any farther).	 */	if (is_opclause(qual))	{		OpExpr	   *opexpr = (OpExpr *) qual;		Oid			negator = get_negator(opexpr->opno);		if (negator)			return make_opclause(negator,								 opexpr->opresulttype,								 opexpr->opretset,								 (Expr *) get_leftop(qual),								 (Expr *) get_rightop(qual));		else			return make_notclause(qual);	}	else if (and_clause((Node *) qual))	{		/*--------------------		 * Apply DeMorgan's Laws:		 *		("NOT" ("AND" A B)) => ("OR" ("NOT" A) ("NOT" B))		 *		("NOT" ("OR" A B))	=> ("AND" ("NOT" A) ("NOT" B))		 * i.e., swap AND for OR and negate all the subclauses.		 *--------------------		 */		FastList	t_list;		List	   *temp;		FastListInit(&t_list);		foreach(temp, ((BoolExpr *) qual)->args)			FastAppend(&t_list, push_nots(lfirst(temp)));		return make_orclause(pull_ors(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, push_nots(lfirst(temp)));		return make_andclause(pull_ands(FastListValue(&t_list)));	}	else if (not_clause((Node *) qual))	{		/*		 * Another 'not' cancels this 'not', so eliminate the 'not' and		 * stop negating this branch.  But search the subexpression for		 * more 'not's to simplify.		 */		return find_nots(get_notclausearg(qual));	}	else	{		/*		 * We don't know how to negate anything else, place a 'not' at		 * this level.		 */		return make_notclause(qual);	}}/* * find_ors *	  Given a qualification tree with the 'not's pushed down, convert it *	  to a tree in CNF by repeatedly applying the rule: *				("OR" A ("AND" B C))  => ("AND" ("OR" A B) ("OR" A C)) * *	  Note that 'or' clauses will always be turned into 'and' clauses *	  if they contain any 'and' subclauses. * * Returns the modified qualification.	AND/OR flatness is preserved. */static Expr *find_ors(Expr *qual){	if (qual == NULL)		return NULL;	/* We used to recurse into opclauses here, but I see no reason to... */	if (and_clause((Node *) qual))	{		List	   *andlist = NIL;		List	   *temp;		foreach(temp, ((BoolExpr *) qual)->args)			andlist = lappend(andlist, find_ors(lfirst(temp)));		return make_andclause(pull_ands(andlist));	}	else if (or_clause((Node *) qual))	{		List	   *orlist = NIL;		List	   *temp;		foreach(temp, ((BoolExpr *) qual)->args)			orlist = lappend(orlist, find_ors(lfirst(temp)));		return or_normalize(pull_ors(orlist));	}	else if (not_clause((Node *) qual))		return make_notclause(find_ors(get_notclausearg(qual)));	else		return qual;}/* * or_normalize *	  Given a list of exprs which are 'or'ed together, try to apply *	  the distributive law *				("OR" A ("AND" B C))  => ("AND" ("OR" A B) ("OR" A C)) *	  to convert the top-level OR clause to a top-level AND clause. * * Returns the resulting expression (could be an AND clause, an OR * clause, or maybe even a single subexpression). */static Expr *or_normalize(List *orlist){	Expr	   *distributable = NULL;	int			num_subclauses = 1;	List	   *andclauses = NIL;	List	   *temp;	if (orlist == NIL)		return NULL;			/* probably can't happen */	if (lnext(orlist) == NIL)		return lfirst(orlist);	/* single-expression OR (can this happen?) */	/*	 * If we have a choice of AND clauses, pick the one with the most	 * subclauses.	Because we initialized num_subclauses = 1, any AND	 * clauses with only one arg will be ignored as useless.	 */	foreach(temp, orlist)	{		Expr	   *clause = lfirst(temp);		if (and_clause((Node *) clause))		{			int			nclauses = length(((BoolExpr *) clause)->args);			if (nclauses > num_subclauses)			{				distributable = clause;				num_subclauses = nclauses;			}		}	}	/* if there's no suitable AND clause, we can't transform the OR */	if (!distributable)		return make_orclause(orlist);	/*	 * Caution: lremove destructively modifies the input orlist. This	 * should be OK, since or_normalize is only called with freshly	 * constructed lists that are not referenced elsewhere.	 */	orlist = lremove(distributable, orlist);	foreach(temp, ((BoolExpr *) distributable)->args)	{		Expr	   *andclause = lfirst(temp);		List	   *neworlist;		/*		 * We are going to insert the orlist into multiple places in the		 * result expression.  For most expression types, it'd be OK to		 * just have multiple links to the same subtree, but this fails		 * badly for SubLinks (and perhaps other cases?).  For safety, we		 * make a distinct copy for each place the orlist is inserted.		 */		if (lnext(temp) == NIL)			neworlist = orlist; /* can use original tree at the end */		else			neworlist = copyObject(orlist);		/*		 * pull_ors is needed here in case andclause has a top-level OR.		 * Then we recursively apply or_normalize, since there might be an		 * AND subclause in the resulting OR-list.		 */		andclause = or_normalize(pull_ors(lcons(andclause, neworlist)));		andclauses = lappend(andclauses, andclause);	}	/* pull_ands is needed in case any sub-or_normalize succeeded */	return make_andclause(pull_ands(andclauses));}/* * find_ands *	  Given a qualification tree with the 'not's pushed down, convert it *	  to a tree in DNF by repeatedly applying the rule: *				("AND" A ("OR" B C))  => ("OR" ("AND" A B) ("AND" A C)) * *	  Note that 'and' clauses will always be turned into 'or' clauses *	  if they contain any 'or' subclauses. * * Returns the modified qualification.	AND/OR flatness is preserved. */static Expr *find_ands(Expr *qual){	if (qual == NULL)		return NULL;	/* We used to recurse into opclauses here, but I see no reason to... */	if (or_clause((Node *) qual))	{		List	   *orlist = NIL;		List	   *temp;		foreach(temp, ((BoolExpr *) qual)->args)			orlist = lappend(orlist, find_ands(lfirst(temp)));		return make_orclause(pull_ors(orlist));	}	else if (and_clause((Node *) qual))	{		List	   *andlist = NIL;		List	   *temp;		foreach(temp, ((BoolExpr *) qual)->args)			andlist = lappend(andlist, find_ands(lfirst(temp)));		return and_normalize(pull_ands(andlist));	}	else if (not_clause((Node *) qual))		return make_notclause(find_ands(get_notclausearg(qual)));	else		return qual;}/* * and_normalize *	  Given a list of exprs which are 'and'ed together, try to apply *	  the distributive law *				("AND" A ("OR" B C))  => ("OR" ("AND" A B) ("AND" A C)) *	  to convert the top-level AND clause to a top-level OR clause. * * Returns the resulting expression (could be an AND clause, an OR * clause, or maybe even a single subexpression). */static Expr *and_normalize(List *andlist){	Expr	   *distributable = NULL;	int			num_subclauses = 1;	List	   *orclauses = NIL;	List	   *temp;	if (andlist == NIL)		return NULL;			/* probably can't happen */	if (lnext(andlist) == NIL)		return lfirst(andlist); /* single-expression AND (can this								 * happen?) */	/*	 * If we have a choice of OR clauses, pick the one with the most	 * subclauses.	Because we initialized num_subclauses = 1, any OR	 * clauses with only one arg will be ignored as useless.	 */	foreach(temp, andlist)	{		Expr	   *clause = lfirst(temp);		if (or_clause((Node *) clause))		{			int			nclauses = length(((BoolExpr *) clause)->args);			if (nclauses > num_subclauses)			{				distributable = clause;				num_subclauses = nclauses;			}		}	}	/* if there's no suitable OR clause, we can't transform the AND */	if (!distributable)		return make_andclause(andlist);	/*	 * Caution: lremove destructively modifies the input andlist. This	 * should be OK, since and_normalize is only called with freshly	 * constructed lists that are not referenced elsewhere.	 */	andlist = lremove(distributable, andlist);	foreach(temp, ((BoolExpr *) distributable)->args)	{		Expr	   *orclause = lfirst(temp);		List	   *newandlist;		/*		 * We are going to insert the andlist into multiple places in the		 * result expression.  For most expression types, it'd be OK to		 * just have multiple links to the same subtree, but this fails		 * badly for SubLinks (and perhaps other cases?).  For safety, we		 * make a distinct copy for each place the andlist is inserted.		 */		if (lnext(temp) == NIL)			newandlist = andlist;		/* can use original tree at the										 * end */		else			newandlist = copyObject(andlist);		/*		 * pull_ands is needed here in case orclause has a top-level AND.		 * Then we recursively apply and_normalize, since there might be		 * an OR subclause in the resulting AND-list.		 */		orclause = and_normalize(pull_ands(lcons(orclause, newandlist)));		orclauses = lappend(orclauses, orclause);	}	/* pull_ors is needed in case any sub-and_normalize succeeded */	return make_orclause(pull_ors(orclauses));}/* * qual_cleanup *	  Fix up a qualification by removing duplicate entries (which could be *	  created during normalization, if identical subexpressions from different *	  parts of the tree are brought together).	Also, check for AND and OR *	  clauses with only one remaining subexpression, and simplify. * * Returns the modified qualification. */static Expr *qual_cleanup(Expr *qual){	if (qual == NULL)		return NULL;	if (and_clause((Node *) qual))	{		List	   *andlist = NIL;		List	   *temp;		foreach(temp, ((BoolExpr *) qual)->args)			andlist = lappend(andlist, qual_cleanup(lfirst(temp)));		andlist = remove_duplicates(pull_ands(andlist));		if (length(andlist) > 1)			return make_andclause(andlist);		else			return lfirst(andlist);	}	else if (or_clause((Node *) qual))	{		List	   *orlist = NIL;		List	   *temp;		foreach(temp, ((BoolExpr *) qual)->args)			orlist = lappend(orlist, qual_cleanup(lfirst(temp)));		orlist = remove_duplicates(pull_ors(orlist));		if (length(orlist) > 1)			return make_orclause(orlist);		else			return lfirst(orlist);	}	else if (not_clause((Node *) qual))		return make_notclause(qual_cleanup(get_notclausearg(qual)));	else		return qual;}/* * remove_duplicates */static List *remove_duplicates(List *list){	List	   *result = NIL;	List	   *i;	if (length(list) <= 1)		return list;	foreach(i, list)	{		if (!member(lfirst(i), result))			result = lappend(result, lfirst(i));	}	return result;}/* * count_bool_nodes *		Support for heuristics in canonicalize_qual(): count the *		number of nodes that are inputs to the top level AND/OR/NOT *		part of a qual tree, and estimate how many nodes will appear *		in the CNF'ified or DNF'ified equivalent of the expression. * * This is just an approximate calculation; it doesn't deal with NOTs * very well, and of course it cannot detect possible simplifications * from eliminating duplicate subclauses.  The idea is just to cheaply * determine whether CNF will be markedly worse than DNF or vice versa. * * The counts/estimates are represented as doubles to avoid risk of overflow. */static voidcount_bool_nodes(Expr *qual,				 double *nodes,				 double *cnfnodes,				 double *dnfnodes){	List	   *temp;	double		subnodes,				subcnfnodes,				subdnfnodes;	if (and_clause((Node *) qual))	{		*nodes = *cnfnodes = 0.0;		*dnfnodes = 1.0;		/* DNF nodes will be product of sub-counts */		foreach(temp, ((BoolExpr *) qual)->args)		{			count_bool_nodes(lfirst(temp),							 &subnodes, &subcnfnodes, &subdnfnodes);			*nodes += subnodes;			*cnfnodes += subcnfnodes;			*dnfnodes *= subdnfnodes;		}		/*		 * we could get dnfnodes < cnfnodes here, if all the sub-nodes are		 * simple ones with count 1.  Make sure dnfnodes isn't too small.		 */		if (*dnfnodes < *cnfnodes)			*dnfnodes = *cnfnodes;	}	else if (or_clause((Node *) qual))	{		*nodes = *dnfnodes = 0.0;		*cnfnodes = 1.0;		/* CNF nodes will be product of sub-counts */		foreach(temp, ((BoolExpr *) qual)->args)		{			count_bool_nodes(lfirst(temp),							 &subnodes, &subcnfnodes, &subdnfnodes);			*nodes += subnodes;			*cnfnodes *= subcnfnodes;			*dnfnodes += subdnfnodes;		}		/*		 * we could get cnfnodes < dnfnodes here, if all the sub-nodes are		 * simple ones with count 1.  Make sure cnfnodes isn't too small.		 */		if (*cnfnodes < *dnfnodes)			*cnfnodes = *dnfnodes;	}	else if (not_clause((Node *) qual))	{		count_bool_nodes(get_notclausearg(qual),						 nodes, cnfnodes, dnfnodes);	}	else if (contain_subplans((Node *) qual))	{		/*		 * charge extra for subexpressions containing sub-SELECTs, to		 * discourage us from rearranging them in a way that might		 * generate N copies of a subselect rather than one.  The magic		 * constant here interacts with the "4x maximum growth" heuristic		 * in canonicalize_qual().		 */		*nodes = 1.0;		*cnfnodes = *dnfnodes = 25.0;	}	else	{		/* anything else counts 1 for my purposes */		*nodes = *cnfnodes = *dnfnodes = 1.0;	}}

⌨️ 快捷键说明

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