Preserve AND/OR flatness during eval_const_expressions(). This seems a
authorTom Lane
Mon, 12 Jan 2004 20:48:15 +0000 (20:48 +0000)
committerTom Lane
Mon, 12 Jan 2004 20:48:15 +0000 (20:48 +0000)
useful improvement in any case, and it keeps the new logic for restrictinfo
structures happy.  Per report from Kris Jurka.

src/backend/optimizer/util/clauses.c

index 6ce171e2f137fa89be1a0775de2285c5fcb782fb..a5faff26cfdaa762bc5d300a9c9b11e41351999a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.161 2004/01/10 18:13:53 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.162 2004/01/12 20:48:15 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -61,6 +61,10 @@ static bool contain_mutable_functions_walker(Node *node, void *context);
 static bool contain_volatile_functions_walker(Node *node, void *context);
 static bool contain_nonstrict_functions_walker(Node *node, void *context);
 static Node *eval_const_expressions_mutator(Node *node, List *active_fns);
+static List *simplify_or_arguments(List *args,
+                                  bool *haveNull, bool *forceTrue);
+static List *simplify_and_arguments(List *args,
+                                   bool *haveNull, bool *forceFalse);
 static Expr *simplify_function(Oid funcid, Oid result_type, List *args,
                  bool allow_inline, List *active_fns);
 static Expr *evaluate_function(Oid funcid, Oid result_type, List *args,
@@ -249,6 +253,9 @@ make_andclause(List *andclauses)
  * Variant of make_andclause for ANDing two qual conditions together.
  * Qual conditions have the property that a NULL nodetree is interpreted
  * as 'true'.
+ *
+ * NB: this makes no attempt to preserve AND/OR flatness; so it should not
+ * be used on a qual that has already been run through prepqual.c.
  */
 Node *
 make_and_qual(Node *qual1, Node *qual2)
@@ -1210,7 +1217,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
    {
        BoolExpr   *expr = (BoolExpr *) node;
        List       *args;
-       Const      *const_input;
 
        /*
         * Reduce constants in the BoolExpr's arguments.  We know args is
@@ -1225,115 +1231,52 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
        {
            case OR_EXPR:
                {
-                   /*----------
-                    * OR arguments are handled as follows:
-                    *  non constant: keep
-                    *  FALSE: drop (does not affect result)
-                    *  TRUE: force result to TRUE
-                    *  NULL: keep only one
-                    * We keep one NULL input because ExecEvalOr returns NULL
-                    * when no input is TRUE and at least one is NULL.
-                    *----------
-                    */
-                   FastList    newargs;
-                   List       *arg;
+                   List       *newargs;
                    bool        haveNull = false;
                    bool        forceTrue = false;
 
-                   FastListInit(&newargs);
-                   foreach(arg, args)
-                   {
-                       if (!IsA(lfirst(arg), Const))
-                       {
-                           FastAppend(&newargs, lfirst(arg));
-                           continue;
-                       }
-                       const_input = (Const *) lfirst(arg);
-                       if (const_input->constisnull)
-                           haveNull = true;
-                       else if (DatumGetBool(const_input->constvalue))
-                           forceTrue = true;
-                       /* otherwise, we can drop the constant-false input */
-                   }
-
-                   /*
-                    * We could return TRUE before falling out of the
-                    * loop, but this coding method will be easier to
-                    * adapt if we ever add a notion of non-removable
-                    * functions. We'd need to check all the inputs for
-                    * non-removability.
-                    */
+                   newargs = simplify_or_arguments(args,
+                                                   &haveNull, &forceTrue);
                    if (forceTrue)
                        return MAKEBOOLCONST(true, false);
                    if (haveNull)
-                       FastAppend(&newargs, MAKEBOOLCONST(false, true));
+                       newargs = lappend(newargs, MAKEBOOLCONST(false, true));
                    /* If all the inputs are FALSE, result is FALSE */
-                   if (FastListValue(&newargs) == NIL)
+                   if (newargs == NIL)
                        return MAKEBOOLCONST(false, false);
                    /* If only one nonconst-or-NULL input, it's the result */
-                   if (lnext(FastListValue(&newargs)) == NIL)
-                       return (Node *) lfirst(FastListValue(&newargs));
+                   if (lnext(newargs) == NIL)
+                       return (Node *) lfirst(newargs);
                    /* Else we still need an OR node */
-                   return (Node *) make_orclause(FastListValue(&newargs));
+                   return (Node *) make_orclause(newargs);
                }
            case AND_EXPR:
                {
-                   /*----------
-                    * AND arguments are handled as follows:
-                    *  non constant: keep
-                    *  TRUE: drop (does not affect result)
-                    *  FALSE: force result to FALSE
-                    *  NULL: keep only one
-                    * We keep one NULL input because ExecEvalAnd returns NULL
-                    * when no input is FALSE and at least one is NULL.
-                    *----------
-                    */
-                   FastList    newargs;
-                   List       *arg;
+                   List       *newargs;
                    bool        haveNull = false;
                    bool        forceFalse = false;
 
-                   FastListInit(&newargs);
-                   foreach(arg, args)
-                   {
-                       if (!IsA(lfirst(arg), Const))
-                       {
-                           FastAppend(&newargs, lfirst(arg));
-                           continue;
-                       }
-                       const_input = (Const *) lfirst(arg);
-                       if (const_input->constisnull)
-                           haveNull = true;
-                       else if (!DatumGetBool(const_input->constvalue))
-                           forceFalse = true;
-                       /* otherwise, we can drop the constant-true input */
-                   }
-
-                   /*
-                    * We could return FALSE before falling out of the
-                    * loop, but this coding method will be easier to
-                    * adapt if we ever add a notion of non-removable
-                    * functions. We'd need to check all the inputs for
-                    * non-removability.
-                    */
+                   newargs = simplify_and_arguments(args,
+                                                    &haveNull, &forceFalse);
                    if (forceFalse)
                        return MAKEBOOLCONST(false, false);
                    if (haveNull)
-                       FastAppend(&newargs, MAKEBOOLCONST(false, true));
+                       newargs = lappend(newargs, MAKEBOOLCONST(false, true));
                    /* If all the inputs are TRUE, result is TRUE */
-                   if (FastListValue(&newargs) == NIL)
+                   if (newargs == NIL)
                        return MAKEBOOLCONST(true, false);
                    /* If only one nonconst-or-NULL input, it's the result */
-                   if (lnext(FastListValue(&newargs)) == NIL)
-                       return (Node *) lfirst(FastListValue(&newargs));
+                   if (lnext(newargs) == NIL)
+                       return (Node *) lfirst(newargs);
                    /* Else we still need an AND node */
-                   return (Node *) make_andclause(FastListValue(&newargs));
+                   return (Node *) make_andclause(newargs);
                }
            case NOT_EXPR:
                Assert(length(args) == 1);
                if (IsA(lfirst(args), Const))
                {
-                   const_input = (Const *) lfirst(args);
+                   Const *const_input = (Const *) lfirst(args);
+
                    /* NOT NULL => NULL */
                    if (const_input->constisnull)
                        return MAKEBOOLCONST(false, true);
@@ -1341,8 +1284,13 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
                    return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
                                         false);
                }
+               else if (not_clause((Node *) lfirst(args)))
+               {
+                   /* Cancel NOT/NOT */
+                   return (Node *) get_notclausearg((Expr *) lfirst(args));
+               }
                /* Else we still need a NOT node */
-               return (Node *) make_notclause(lfirst(args));
+               return (Node *) make_notclause((Expr *) lfirst(args));
            default:
                elog(ERROR, "unrecognized boolop: %d",
                     (int) expr->boolop);
@@ -1579,6 +1527,128 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
                                   (void *) active_fns);
 }
 
+/*
+ * Subroutine for eval_const_expressions: scan the arguments of an OR clause
+ *
+ * OR arguments are handled as follows:
+ *     non constant: keep
+ *     FALSE: drop (does not affect result)
+ *     TRUE: force result to TRUE
+ *     NULL: keep only one
+ * We must keep one NULL input because ExecEvalOr returns NULL when no input
+ * is TRUE and at least one is NULL.
+ *
+ * This is split out as a subroutine so that we can recurse to fold sub-ORs
+ * into the upper OR clause, thereby preserving AND/OR flatness.
+ *
+ * The output arguments *haveNull and *forceTrue must be initialized FALSE
+ * by the caller.  They will be set TRUE if a null constant or true constant,
+ * respectively, is detected anywhere in the argument list.
+ */
+static List *
+simplify_or_arguments(List *args, bool *haveNull, bool *forceTrue)
+{
+   List       *newargs = NIL;
+   List       *larg;
+
+   foreach(larg, args)
+   {
+       Node   *arg = (Node *) lfirst(larg);
+
+       if (IsA(arg, Const))
+       {
+           Const  *const_input = (Const *) arg;
+
+           if (const_input->constisnull)
+               *haveNull = true;
+           else if (DatumGetBool(const_input->constvalue))
+           {
+               *forceTrue = true;
+               /*
+                * Once we detect a TRUE result we can just exit the loop
+                * immediately.  However, if we ever add a notion of
+                * non-removable functions, we'd need to keep scanning.
+                */
+               return NIL;
+           }
+           /* otherwise, we can drop the constant-false input */
+       }
+       else if (or_clause(arg))
+       {
+           newargs = nconc(newargs,
+                           simplify_or_arguments(((BoolExpr *) arg)->args,
+                                                 haveNull, forceTrue));
+       }
+       else
+       {
+           newargs = lappend(newargs, arg);
+       }
+   }
+
+   return newargs;
+}
+
+/*
+ * Subroutine for eval_const_expressions: scan the arguments of an AND clause
+ *
+ * AND arguments are handled as follows:
+ *     non constant: keep
+ *     TRUE: drop (does not affect result)
+ *     FALSE: force result to FALSE
+ *     NULL: keep only one
+ * We must keep one NULL input because ExecEvalAnd returns NULL when no input
+ * is FALSE and at least one is NULL.
+ *
+ * This is split out as a subroutine so that we can recurse to fold sub-ANDs
+ * into the upper AND clause, thereby preserving AND/OR flatness.
+ *
+ * The output arguments *haveNull and *forceFalse must be initialized FALSE
+ * by the caller.  They will be set TRUE if a null constant or false constant,
+ * respectively, is detected anywhere in the argument list.
+ */
+static List *
+simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse)
+{
+   List       *newargs = NIL;
+   List       *larg;
+
+   foreach(larg, args)
+   {
+       Node   *arg = (Node *) lfirst(larg);
+
+       if (IsA(arg, Const))
+       {
+           Const  *const_input = (Const *) arg;
+
+           if (const_input->constisnull)
+               *haveNull = true;
+           else if (!DatumGetBool(const_input->constvalue))
+           {
+               *forceFalse = true;
+               /*
+                * Once we detect a FALSE result we can just exit the loop
+                * immediately.  However, if we ever add a notion of
+                * non-removable functions, we'd need to keep scanning.
+                */
+               return NIL;
+           }
+           /* otherwise, we can drop the constant-true input */
+       }
+       else if (and_clause(arg))
+       {
+           newargs = nconc(newargs,
+                           simplify_and_arguments(((BoolExpr *) arg)->args,
+                                                  haveNull, forceFalse));
+       }
+       else
+       {
+           newargs = lappend(newargs, arg);
+       }
+   }
+
+   return newargs;
+}
+
 /*
  * Subroutine for eval_const_expressions: try to simplify a function call
  * (which might originally have been an operator; we don't care)