clauses.c

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

C
2,294
字号
 * * Returns true if any nonstrict construct is found --- ie, anything that * could produce non-NULL output with a NULL input. * * The idea here is that the caller has verified that the expression contains * one or more Var or Param nodes (as appropriate for the caller's need), and * now wishes to prove that the expression result will be NULL if any of these * inputs is NULL.	If we return false, then the proof succeeded. */boolcontain_nonstrict_functions(Node *clause){	return contain_nonstrict_functions_walker(clause, NULL);}static boolcontain_nonstrict_functions_walker(Node *node, void *context){	if (node == NULL)		return false;	if (IsA(node, Aggref))	{		/* an aggregate could return non-null with null input */		return true;	}	if (IsA(node, ArrayRef))	{		/* array assignment is nonstrict, but subscripting is strict */		if (((ArrayRef *) node)->refassgnexpr != NULL)			return true;		/* else fall through to check args */	}	if (IsA(node, FuncExpr))	{		FuncExpr   *expr = (FuncExpr *) node;		if (!func_strict(expr->funcid))			return true;		/* else fall through to check args */	}	if (IsA(node, OpExpr))	{		OpExpr	   *expr = (OpExpr *) node;		set_opfuncid(expr);		if (!func_strict(expr->opfuncid))			return true;		/* else fall through to check args */	}	if (IsA(node, DistinctExpr))	{		/* IS DISTINCT FROM is inherently non-strict */		return true;	}	if (IsA(node, ScalarArrayOpExpr))	{		ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;		if (!is_strict_saop(expr, false))			return true;		/* else fall through to check args */	}	if (IsA(node, BoolExpr))	{		BoolExpr   *expr = (BoolExpr *) node;		switch (expr->boolop)		{			case AND_EXPR:			case OR_EXPR:				/* AND, OR are inherently non-strict */				return true;			default:				break;		}	}	if (IsA(node, SubLink))	{		/* In some cases a sublink might be strict, but in general not */		return true;	}	if (IsA(node, SubPlan))		return true;	/* ArrayCoerceExpr is strict at the array level, regardless of elemfunc */	if (IsA(node, FieldStore))		return true;	if (IsA(node, CaseExpr))		return true;	if (IsA(node, ArrayExpr))		return true;	if (IsA(node, RowExpr))		return true;	if (IsA(node, RowCompareExpr))		return true;	if (IsA(node, CoalesceExpr))		return true;	if (IsA(node, MinMaxExpr))		return true;	if (IsA(node, XmlExpr))		return true;	if (IsA(node, NullIfExpr))		return true;	if (IsA(node, NullTest))		return true;	if (IsA(node, BooleanTest))		return true;	return expression_tree_walker(node, contain_nonstrict_functions_walker,								  context);}/* * find_nonnullable_rels *		Determine which base rels are forced nonnullable by given clause. * * Returns the set of all Relids that are referenced in the clause in such * a way that the clause cannot possibly return TRUE if any of these Relids * is an all-NULL row.	(It is OK to err on the side of conservatism; hence * the analysis here is simplistic.) * * The semantics here are subtly different from contain_nonstrict_functions: * that function is concerned with NULL results from arbitrary expressions, * but here we assume that the input is a Boolean expression, and wish to * see if NULL inputs will provably cause a FALSE-or-NULL result.  We expect * the expression to have been AND/OR flattened and converted to implicit-AND * format. * * top_level is TRUE while scanning top-level AND/OR structure; here, showing * the result is either FALSE or NULL is good enough.  top_level is FALSE when * we have descended below a NOT or a strict function: now we must be able to * prove that the subexpression goes to NULL. * * We don't use expression_tree_walker here because we don't want to descend * through very many kinds of nodes; only the ones we can be sure are strict. */Relidsfind_nonnullable_rels(Node *clause){	return find_nonnullable_rels_walker(clause, true);}static Relidsfind_nonnullable_rels_walker(Node *node, bool top_level){	Relids		result = NULL;	ListCell   *l;	if (node == NULL)		return NULL;	if (IsA(node, Var))	{		Var		   *var = (Var *) node;		if (var->varlevelsup == 0)			result = bms_make_singleton(var->varno);	}	else if (IsA(node, List))	{		/*		 * At top level, we are examining an implicit-AND list: if any of the		 * arms produces FALSE-or-NULL then the result is FALSE-or-NULL. If		 * not at top level, we are examining the arguments of a strict		 * function: if any of them produce NULL then the result of the		 * function must be NULL.  So in both cases, the set of nonnullable		 * rels is the union of those found in the arms, and we pass down the		 * top_level flag unmodified.		 */		foreach(l, (List *) node)		{			result = bms_join(result,							  find_nonnullable_rels_walker(lfirst(l),														   top_level));		}	}	else if (IsA(node, FuncExpr))	{		FuncExpr   *expr = (FuncExpr *) node;		if (func_strict(expr->funcid))			result = find_nonnullable_rels_walker((Node *) expr->args, false);	}	else if (IsA(node, OpExpr))	{		OpExpr	   *expr = (OpExpr *) node;		set_opfuncid(expr);		if (func_strict(expr->opfuncid))			result = find_nonnullable_rels_walker((Node *) expr->args, false);	}	else if (IsA(node, ScalarArrayOpExpr))	{		ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;		if (is_strict_saop(expr, true))			result = find_nonnullable_rels_walker((Node *) expr->args, false);	}	else if (IsA(node, BoolExpr))	{		BoolExpr   *expr = (BoolExpr *) node;		switch (expr->boolop)		{			case AND_EXPR:				/* At top level we can just recurse (to the List case) */				if (top_level)				{					result = find_nonnullable_rels_walker((Node *) expr->args,														  top_level);					break;				}				/*				 * Below top level, even if one arm produces NULL, the result				 * could be FALSE (hence not NULL).  However, if *all* the				 * arms produce NULL then the result is NULL, so we can take				 * the intersection of the sets of nonnullable rels, just as				 * for OR.	Fall through to share code.				 */				/* FALL THRU */			case OR_EXPR:				/*				 * OR is strict if all of its arms are, so we can take the				 * intersection of the sets of nonnullable rels for each arm.				 * This works for both values of top_level.				 */				foreach(l, expr->args)				{					Relids		subresult;					subresult = find_nonnullable_rels_walker(lfirst(l),															 top_level);					if (result == NULL) /* first subresult? */						result = subresult;					else						result = bms_int_members(result, subresult);					/*					 * If the intersection is empty, we can stop looking. This					 * also justifies the test for first-subresult above.					 */					if (bms_is_empty(result))						break;				}				break;			case NOT_EXPR:				/* NOT will return null if its arg is null */				result = find_nonnullable_rels_walker((Node *) expr->args,													  false);				break;			default:				elog(ERROR, "unrecognized boolop: %d", (int) expr->boolop);				break;		}	}	else if (IsA(node, RelabelType))	{		RelabelType *expr = (RelabelType *) node;		result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);	}	else if (IsA(node, CoerceViaIO))	{		/* not clear this is useful, but it can't hurt */		CoerceViaIO *expr = (CoerceViaIO *) node;		result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);	}	else if (IsA(node, ArrayCoerceExpr))	{		/* ArrayCoerceExpr is strict at the array level */		ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;		result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);	}	else if (IsA(node, ConvertRowtypeExpr))	{		/* not clear this is useful, but it can't hurt */		ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node;		result = find_nonnullable_rels_walker((Node *) expr->arg, top_level);	}	else if (IsA(node, NullTest))	{		/* IS NOT NULL can be considered strict, but only at top level */		NullTest   *expr = (NullTest *) node;		if (top_level && expr->nulltesttype == IS_NOT_NULL)			result = find_nonnullable_rels_walker((Node *) expr->arg, false);	}	else if (IsA(node, BooleanTest))	{		/* Boolean tests that reject NULL are strict at top level */		BooleanTest *expr = (BooleanTest *) node;		if (top_level &&			(expr->booltesttype == IS_TRUE ||			 expr->booltesttype == IS_FALSE ||			 expr->booltesttype == IS_NOT_UNKNOWN))			result = find_nonnullable_rels_walker((Node *) expr->arg, false);	}	return result;}/* * Can we treat a ScalarArrayOpExpr as strict? * * If "falseOK" is true, then a "false" result can be considered strict, * else we need to guarantee an actual NULL result for NULL input. * * "foo op ALL array" is strict if the op is strict *and* we can prove * that the array input isn't an empty array.  We can check that * for the cases of an array constant and an ARRAY[] construct. * * "foo op ANY array" is strict in the falseOK sense if the op is strict. * If not falseOK, the test is the same as for "foo op ALL array". */static boolis_strict_saop(ScalarArrayOpExpr *expr, bool falseOK){	Node	   *rightop;	/* The contained operator must be strict. */	set_sa_opfuncid(expr);	if (!func_strict(expr->opfuncid))		return false;	/* If ANY and falseOK, that's all we need to check. */	if (expr->useOr && falseOK)		return true;	/* Else, we have to see if the array is provably non-empty. */	Assert(list_length(expr->args) == 2);	rightop = (Node *) lsecond(expr->args);	if (rightop && IsA(rightop, Const))	{		Datum		arraydatum = ((Const *) rightop)->constvalue;		bool		arrayisnull = ((Const *) rightop)->constisnull;		ArrayType  *arrayval;		int			nitems;		if (arrayisnull)			return false;		arrayval = DatumGetArrayTypeP(arraydatum);		nitems = ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval));		if (nitems > 0)			return true;	}	else if (rightop && IsA(rightop, ArrayExpr))	{		ArrayExpr  *arrayexpr = (ArrayExpr *) rightop;		if (arrayexpr->elements != NIL && !arrayexpr->multidims)			return true;	}	return false;}/***************************************************************************** *		Check for "pseudo-constant" clauses *****************************************************************************//* * is_pseudo_constant_clause *	  Detect whether an expression is "pseudo constant", ie, it contains no *	  variables of the current query level and no uses of volatile functions. *	  Such an expr is not necessarily a true constant: it can still contain *	  Params and outer-level Vars, not to mention functions whose results *	  may vary from one statement to the next.	However, the expr's value *	  will be constant over any one scan of the current query, so it can be *	  used as, eg, an indexscan key. * * CAUTION: this function omits to test for one very important class of * not-constant expressions, namely aggregates (Aggrefs).  In current usage * this is only applied to WHERE clauses and so a check for Aggrefs would be * a waste of cycles; but be sure to also check contain_agg_clause() if you * want to know about pseudo-constness in other contexts. */boolis_pseudo_constant_clause(Node *clause){	/*	 * We could implement this check in one recursive scan.  But since the	 * check for volatile functions is both moderately expensive and unlikely	 * to fail, it seems better to look for Vars first and only check for	 * volatile functions if we find no Vars.	 */	if (!contain_var_clause(clause) &&		!contain_volatile_functions(clause))		return true;	return false;}/* * is_pseudo_constant_clause_relids *	  Same as above, except caller already has available the var membership *	  of the expression; this lets us avoid the contain_var_clause() scan. */boolis_pseudo_constant_clause_relids(Node *clause, Relids relids){	if (bms_is_empty(relids) &&		!contain_volatile_functions(clause))		return true;	return false;}/***************************************************************************** *		Tests on clauses of queries * * Possibly this code should go someplace else, since this isn't quite the * same meaning of "clause" as is used elsewhere in this module.  But I can't * think of a better place for it... *****************************************************************************//* * Test whether a query uses DISTINCT ON, ie, has a distinct-list that is * not the same as the set of output columns. */boolhas_distinct_on_clause(Query *query){	ListCell   *l;	/* Is there a DISTINCT clause at all? */	if (query->distinctClause == NIL)		return false;	/*	 * If the DISTINCT list contains all the nonjunk targetlist items, and	 * nothing else (ie, no junk tlist items), then it's a simple DISTINCT,	 * else it's DISTINCT ON.  We do not require the lists to be in the same	 * order (since the parser may have adjusted the DISTINCT clause ordering	 * to agree with ORDER BY).  Furthermore, a non-DISTINCT junk tlist item	 * that is in the sortClause is also evidence of DISTINCT ON, since we	 * don't allow ORDER BY on junk tlist items when plain DISTINCT is used.	 *	 * This code assumes that the DISTINCT list is valid, ie, all its entries	 * match some entry of the tlist.	 */	foreach(l, query->targetList)	{		TargetEntry *tle = (TargetEntry *) lfirst(l);		if (tle->ressortgroupref == 0)		{			if (tle->resjunk)				continue;		/* we can ignore unsorted junk cols */			return true;		/* definitely not in DISTINCT list */		}		if (targetIsInSortList(tle, InvalidOid, query->distinctClause))		{			if (tle->resjunk)				return true;	/* junk TLE in DISTINCT means DISTINCT ON */			/* else this TLE is okay, keep looking */		}		else		{			/* This TLE is not in DISTINCT list */

⌨️ 快捷键说明

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