diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 90485171d9..033aaba494 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.15 2004/01/10 00:30:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.16 2004/01/10 18:13:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -439,11 +439,13 @@ is_simple_subquery(Query *subquery) /* * has_nullable_targetlist * Check a subquery in the range table to see if all the non-junk - * targetlist items are simple variables (and, hence, will correctly - * go to NULL when examined above the point of an outer join). + * targetlist items are simple variables or strict functions of simple + * variables (and, hence, will correctly go to NULL when examined above + * the point of an outer join). * - * A possible future extension is to accept strict functions of simple - * variables, eg, "x + 1". + * NOTE: it would be correct (and useful) to ignore output columns that aren't + * actually referenced by the enclosing query ... but we do not have that + * information available at this point. */ static bool has_nullable_targetlist(Query *subquery) @@ -458,11 +460,15 @@ has_nullable_targetlist(Query *subquery) if (tle->resdom->resjunk) continue; - /* Okay if tlist item is a simple Var */ - if (tle->expr && IsA(tle->expr, Var)) - continue; + /* Must contain a Var of current level */ + if (!contain_vars_of_level((Node *) tle->expr, 0)) + return false; - return false; + /* Must not contain any non-strict constructs */ + if (contain_nonstrict_functions((Node *) tle->expr)) + return false; + + /* This one's OK, keep scanning */ } return true; } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index b2b2d7d02b..6ce171e2f1 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.160 2004/01/05 18:04:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.161 2004/01/10 18:13:53 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -697,10 +697,10 @@ contain_volatile_functions_walker(Node *node, void *context) * Returns true if any nonstrict construct is found --- ie, anything that * could produce non-NULL output with a NULL input. * - * XXX we do not examine sub-selects to see if they contain uses of - * nonstrict functions. It's not real clear if that is correct or not... - * for the current usage it does not matter, since inline_function() - * rejects cases with sublinks. + * 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. */ bool contain_nonstrict_functions(Node *clause) @@ -713,6 +713,11 @@ contain_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, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; @@ -745,16 +750,25 @@ contain_nonstrict_functions_walker(Node *node, void *context) switch (expr->boolop) { - case OR_EXPR: case AND_EXPR: - /* OR, AND are inherently non-strict */ + 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; if (IsA(node, CaseExpr)) return true; + if (IsA(node, CaseWhen)) + return true; /* NB: ArrayExpr might someday be nonstrict */ if (IsA(node, CoalesceExpr)) return true; @@ -764,18 +778,6 @@ contain_nonstrict_functions_walker(Node *node, void *context) return true; if (IsA(node, BooleanTest)) return true; - if (IsA(node, SubLink)) - { - SubLink *sublink = (SubLink *) node; - List *opid; - - foreach(opid, sublink->operOids) - { - if (!op_strict(lfirsto(opid))) - return true; - } - /* else fall through to check args */ - } return expression_tree_walker(node, contain_nonstrict_functions_walker, context); }