]> granicus.if.org Git - postgresql/commitdiff
Improve has_nullable_targetlist() to allow strict functions of simple
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 10 Jan 2004 18:13:53 +0000 (18:13 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 10 Jan 2004 18:13:53 +0000 (18:13 +0000)
variables, not just simple variables.  This was foreseen in the original
coding of this routine, but not implemented until now.  Responds to
performance gripe from Laurent Perez.

src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/util/clauses.c

index 90485171d9786c62796452aa803fefcb47c1875d..033aaba494e0436cd432d615558dc2f3f52675e4 100644 (file)
@@ -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;
 }
index b2b2d7d02bbeab2dfaf82c85e25b17e305e03456..6ce171e2f137fa89be1a0775de2285c5fcb782fb 100644 (file)
@@ -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);
 }