*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.233 2007/02/02 00:02:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.234 2007/02/16 23:32:08 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
* the expression to have been AND/OR flattened and converted to implicit-AND
* format.
*
- * 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. We can descend through the top level of implicit AND'ing,
- * but not through any explicit ANDs (or ORs) below that, since those are not
- * strict constructs. The List case handles the top-level implicit AND list
- * as well as lists of arguments to strict operators/functions.
+ * 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.
*/
Relids
find_nonnullable_rels(Node *clause)
find_nonnullable_rels_walker(Node *node, bool top_level)
{
Relids result = NULL;
+ ListCell *l;
if (node == NULL)
return NULL;
}
else if (IsA(node, List))
{
- ListCell *l;
-
+ /*
+ * 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,
{
BoolExpr *expr = (BoolExpr *) node;
- /* NOT is strict, others are not */
- if (expr->boolop == NOT_EXPR)
- result = find_nonnullable_rels_walker((Node *) expr->args, false);
+ 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))
{
}
else if (IsA(node, NullTest))
{
+ /* IS NOT NULL can be considered strict, but only at top level */
NullTest *expr = (NullTest *) node;
- /*
- * IS NOT NULL can be considered strict, but only at top level; else
- * we might have something like NOT (x IS NOT NULL).
- */
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;
- /*
- * Appropriate boolean tests are strict at top level.
- */
if (top_level &&
(expr->booltesttype == IS_TRUE ||
expr->booltesttype == IS_FALSE ||