cnf'ify cleanup
authorBruce Momjian
Sun, 4 Oct 1998 03:30:56 +0000 (03:30 +0000)
committerBruce Momjian
Sun, 4 Oct 1998 03:30:56 +0000 (03:30 +0000)
src/backend/optimizer/prep/prepqual.c

index a1f2f309247df27a89d6100c9328456b9c455200..59ebbfd1cc9a20efb804d8069f48bbdd2c21c8e7 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.10 1998/09/01 03:23:43 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.11 1998/10/04 03:30:56 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,7 +32,7 @@ static Expr *push_nots(Expr *qual);
 static Expr *normalize(Expr *qual);
 static List *or_normalize(List *orlist);
 static List *distribute_args(List *item, List *args);
-static List *qualcleanup(Expr *qual);
+static List *qual_cleanup(Expr *qual);
 static List *remove_ands(Expr *qual);
 static List *remove_duplicates(List *list);
 
@@ -52,7 +52,7 @@ static List *remove_duplicates(List *list);
 
 
 /*
- * cnfify--
+ * cnfify
  *   Convert a qualification to conjunctive normal form by applying
  *   successive normalizations.
  *
@@ -73,7 +73,7 @@ cnfify(Expr *qual, bool removeAndFlag)
    {
        newqual = find_nots(pull_args(qual));
        newqual = normalize(pull_args(newqual));
-       newqual = (Expr *) qualcleanup(pull_args(newqual));
+       newqual = (Expr *) qual_cleanup(pull_args(newqual));
        newqual = pull_args(newqual);;
 
        if (removeAndFlag)
@@ -84,21 +84,22 @@ cnfify(Expr *qual, bool removeAndFlag)
                newqual = (Expr *) remove_ands(make_andclause(lcons(newqual, NIL)));
        }
    }
-   else if (qual != NULL)
-       newqual = (Expr *) lcons(qual, NIL);
 
    return (List *) (newqual);
 }
 
 /*
- * pull-args--
- *   Given a qualification, eliminate nested 'and' and 'or' clauses.
+ * find_nots
+ *   Traverse the qualification, looking for 'not's to take care of.
+ *   For 'not' clauses, remove the 'not' and push it down to the clauses'
+ *   descendants.
+ *   For all other clause types, simply recurse.
  *
  * Returns the modified qualification.
  *
  */
 static Expr *
-pull_args(Expr *qual)
+find_nots(Expr *qual)
 {
    if (qual == NULL)
        return NULL;
@@ -106,8 +107,8 @@ pull_args(Expr *qual)
    if (is_opclause((Node *) qual))
    {
        return (make_clause(qual->opType, qual->oper,
-                           lcons(pull_args((Expr *) get_leftop(qual)),
-                            lcons(pull_args((Expr *) get_rightop(qual)),
+                           lcons(find_nots((Expr *) get_leftop(qual)),
+                            lcons(find_nots((Expr *) get_rightop(qual)),
                                   NIL))));
    }
    else if (and_clause((Node *) qual))
@@ -116,8 +117,9 @@ pull_args(Expr *qual)
        List       *t_list = NIL;
 
        foreach(temp, qual->args)
-           t_list = lappend(t_list, pull_args(lfirst(temp)));
-       return make_andclause(pull_ands(t_list));
+           t_list = lappend(t_list, find_nots(lfirst(temp)));
+
+       return make_andclause(t_list);
    }
    else if (or_clause((Node *) qual))
    {
@@ -125,75 +127,149 @@ pull_args(Expr *qual)
        List       *t_list = NIL;
 
        foreach(temp, qual->args)
-           t_list = lappend(t_list, pull_args(lfirst(temp)));
-       return make_orclause(pull_ors(t_list));
+           t_list = lappend(t_list, find_nots(lfirst(temp)));
+       return make_orclause(t_list);
    }
    else if (not_clause((Node *) qual))
-       return make_notclause(pull_args(get_notclausearg(qual)));
+       return push_nots(get_notclausearg(qual));
    else
        return qual;
 }
 
 /*
- * pull-ors--
- *   Pull the arguments of an 'or' clause nested within another 'or'
- *   clause up into the argument list of the parent.
+ * normalize
+ *   Given a qualification tree with the 'not's pushed down, convert it
+ *   to a tree in CNF by repeatedly applying the rule:
+ *             ("OR" A ("AND" B C))  => ("AND" ("OR" A B) ("OR" A C))
+ *   bottom-up.
+ *   Note that 'or' clauses will always be turned into 'and' clauses.
+ *
+ * Returns the modified qualification.
  *
- * Returns the modified list.
  */
-static List *
-pull_ors(List *orlist)
+static Expr *
+normalize(Expr *qual)
 {
-   if (orlist == NIL)
-       return NIL;
+   if (qual == NULL)
+       return NULL;
 
-   if (or_clause(lfirst(orlist)))
+   if (is_opclause((Node *) qual))
    {
-       List       *args = ((Expr *) lfirst(orlist))->args;
+       Expr       *expr = (Expr *) qual;
 
-       return (pull_ors(nconc(copyObject((Node *) args),
-                              copyObject((Node *) lnext(orlist)))));
+       return (make_clause(expr->opType, expr->oper,
+                           lcons(normalize((Expr *) get_leftop(qual)),
+                            lcons(normalize((Expr *) get_rightop(qual)),
+                                  NIL))));
    }
+   else if (and_clause((Node *) qual))
+   {
+       List       *temp = NIL;
+       List       *t_list = NIL;
+
+       foreach(temp, qual->args)
+           t_list = lappend(t_list, normalize(lfirst(temp)));
+       return make_andclause(t_list);
+   }
+   else if (or_clause((Node *) qual))
+   {
+       /* XXX - let form, maybe incorrect */
+       List       *orlist = NIL;
+       List       *temp = NIL;
+       bool        has_andclause = FALSE;
+
+       foreach(temp, qual->args)
+           orlist = lappend(orlist, normalize(lfirst(temp)));
+       foreach(temp, orlist)
+       {
+           if (and_clause(lfirst(temp)))
+           {
+               has_andclause = TRUE;
+               break;
+           }
+       }
+       if (has_andclause == TRUE)
+           return make_andclause(or_normalize(orlist));
+       else
+           return make_orclause(orlist);
+
+   }
+   else if (not_clause((Node *) qual))
+       return make_notclause(normalize(get_notclausearg(qual)));
    else
-       return lcons(lfirst(orlist), pull_ors(lnext(orlist)));
+       return qual;
 }
 
 /*
- * pull-ands--
- *   Pull the arguments of an 'and' clause nested within another 'and'
- *   clause up into the argument list of the parent.
+ * qual_cleanup
+ *   Fix up a qualification by removing duplicate entries (left over from
+ *   normalization), and by removing 'and' and 'or' clauses which have only
+ *   one valid expr (e.g., ("AND" A) => A).
+ *
+ * Returns the modified qualfication.
  *
- * Returns the modified list.
  */
 static List *
-pull_ands(List *andlist)
+qual_cleanup(Expr *qual)
 {
-   if (andlist == NIL)
+   if (qual == NULL)
        return NIL;
 
-   if (and_clause(lfirst(andlist)))
+   if (is_opclause((Node *) qual))
    {
-       List       *args = ((Expr *) lfirst(andlist))->args;
+       return ((List *) make_clause(qual->opType, qual->oper,
+                           lcons(qual_cleanup((Expr *) get_leftop(qual)),
+                          lcons(qual_cleanup((Expr *) get_rightop(qual)),
+                                NIL))));
+   }
+   else if (and_clause((Node *) qual))
+   {
+       List       *temp = NIL;
+       List       *t_list = NIL;
+       List       *new_and_args = NIL;
 
-       return (pull_ands(nconc(copyObject((Node *) args),
-                               copyObject((Node *) lnext(andlist)))));
+       foreach(temp, qual->args)
+           t_list = lappend(t_list, qual_cleanup(lfirst(temp)));
+
+       new_and_args = remove_duplicates(t_list);
+
+       if (length(new_and_args) > 1)
+           return (List *) make_andclause(new_and_args);
+       else
+           return lfirst(new_and_args);
    }
+   else if (or_clause((Node *) qual))
+   {
+       List       *temp = NIL;
+       List       *t_list = NIL;
+       List       *new_or_args = NIL;
+
+       foreach(temp, qual->args)
+           t_list = lappend(t_list, qual_cleanup(lfirst(temp)));
+
+       new_or_args = remove_duplicates(t_list);
+
+
+       if (length(new_or_args) > 1)
+           return (List *) make_orclause(new_or_args);
+       else
+           return lfirst(new_or_args);
+   }
+   else if (not_clause((Node *) qual))
+       return (List *) make_notclause((Expr *) qual_cleanup((Expr *) get_notclausearg(qual)));
    else
-       return lcons(lfirst(andlist), pull_ands(lnext(andlist)));
+       return (List *) qual;
 }
 
 /*
- * find-nots--
- *   Traverse the qualification, looking for 'not's to take care of.
- *   For 'not' clauses, remove the 'not' and push it down to the clauses'
- *   descendants.
- *   For all other clause types, simply recurse.
+ * pull_args
+ *   Given a qualification, eliminate nested 'and' and 'or' clauses.
  *
  * Returns the modified qualification.
  *
  */
 static Expr *
-find_nots(Expr *qual)
+pull_args(Expr *qual)
 {
    if (qual == NULL)
        return NULL;
@@ -201,8 +277,8 @@ find_nots(Expr *qual)
    if (is_opclause((Node *) qual))
    {
        return (make_clause(qual->opType, qual->oper,
-                           lcons(find_nots((Expr *) get_leftop(qual)),
-                            lcons(find_nots((Expr *) get_rightop(qual)),
+                           lcons(pull_args((Expr *) get_leftop(qual)),
+                            lcons(pull_args((Expr *) get_rightop(qual)),
                                   NIL))));
    }
    else if (and_clause((Node *) qual))
@@ -211,9 +287,8 @@ find_nots(Expr *qual)
        List       *t_list = NIL;
 
        foreach(temp, qual->args)
-           t_list = lappend(t_list, find_nots(lfirst(temp)));
-
-       return make_andclause(t_list);
+           t_list = lappend(t_list, pull_args(lfirst(temp)));
+       return make_andclause(pull_ands(t_list));
    }
    else if (or_clause((Node *) qual))
    {
@@ -221,17 +296,65 @@ find_nots(Expr *qual)
        List       *t_list = NIL;
 
        foreach(temp, qual->args)
-           t_list = lappend(t_list, find_nots(lfirst(temp)));
-       return make_orclause(t_list);
+           t_list = lappend(t_list, pull_args(lfirst(temp)));
+       return make_orclause(pull_ors(t_list));
    }
    else if (not_clause((Node *) qual))
-       return push_nots(get_notclausearg(qual));
+       return make_notclause(pull_args(get_notclausearg(qual)));
    else
        return qual;
 }
 
 /*
- * push-nots--
+ * pull_ors
+ *   Pull the arguments of an 'or' clause nested within another 'or'
+ *   clause up into the argument list of the parent.
+ *
+ * Returns the modified list.
+ */
+static List *
+pull_ors(List *orlist)
+{
+   if (orlist == NIL)
+       return NIL;
+
+   if (or_clause(lfirst(orlist)))
+   {
+       List       *args = ((Expr *) lfirst(orlist))->args;
+
+       return (pull_ors(nconc(copyObject((Node *) args),
+                              copyObject((Node *) lnext(orlist)))));
+   }
+   else
+       return lcons(lfirst(orlist), pull_ors(lnext(orlist)));
+}
+
+/*
+ * pull_ands
+ *   Pull the arguments of an 'and' clause nested within another 'and'
+ *   clause up into the argument list of the parent.
+ *
+ * Returns the modified list.
+ */
+static List *
+pull_ands(List *andlist)
+{
+   if (andlist == NIL)
+       return NIL;
+
+   if (and_clause(lfirst(andlist)))
+   {
+       List       *args = ((Expr *) lfirst(andlist))->args;
+
+       return (pull_ands(nconc(copyObject((Node *) args),
+                               copyObject((Node *) lnext(andlist)))));
+   }
+   else
+       return lcons(lfirst(andlist), pull_ands(lnext(andlist)));
+}
+
+/*
+ * push_nots
  *   Negate the descendants of a 'not' clause.
  *
  * Returns the modified qualification.
@@ -308,71 +431,7 @@ push_nots(Expr *qual)
 }
 
 /*
- * normalize--
- *   Given a qualification tree with the 'not's pushed down, convert it
- *   to a tree in CNF by repeatedly applying the rule:
- *             ("OR" A ("AND" B C))  => ("AND" ("OR" A B) ("OR" A C))
- *   bottom-up.
- *   Note that 'or' clauses will always be turned into 'and' clauses.
- *
- * Returns the modified qualification.
- *
- */
-static Expr *
-normalize(Expr *qual)
-{
-   if (qual == NULL)
-       return NULL;
-
-   if (is_opclause((Node *) qual))
-   {
-       Expr       *expr = (Expr *) qual;
-
-       return (make_clause(expr->opType, expr->oper,
-                           lcons(normalize((Expr *) get_leftop(qual)),
-                            lcons(normalize((Expr *) get_rightop(qual)),
-                                  NIL))));
-   }
-   else if (and_clause((Node *) qual))
-   {
-       List       *temp = NIL;
-       List       *t_list = NIL;
-
-       foreach(temp, qual->args)
-           t_list = lappend(t_list, normalize(lfirst(temp)));
-       return make_andclause(t_list);
-   }
-   else if (or_clause((Node *) qual))
-   {
-       /* XXX - let form, maybe incorrect */
-       List       *orlist = NIL;
-       List       *temp = NIL;
-       bool        has_andclause = FALSE;
-
-       foreach(temp, qual->args)
-           orlist = lappend(orlist, normalize(lfirst(temp)));
-       foreach(temp, orlist)
-       {
-           if (and_clause(lfirst(temp)))
-           {
-               has_andclause = TRUE;
-               break;
-           }
-       }
-       if (has_andclause == TRUE)
-           return make_andclause(or_normalize(orlist));
-       else
-           return make_orclause(orlist);
-
-   }
-   else if (not_clause((Node *) qual))
-       return make_notclause(normalize(get_notclausearg(qual)));
-   else
-       return qual;
-}
-
-/*
- * or-normalize--
+ * or_normalize
  *   Given a list of exprs which are 'or'ed together, distribute any
  *   'and' clauses.
  *
@@ -409,7 +468,7 @@ or_normalize(List *orlist)
 }
 
 /*
- * distribute-args--
+ * distribute_args
  *   Create new 'or' clauses by or'ing 'item' with each element of 'args'.
  *   E.g.: (distribute-args A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
  *
@@ -438,69 +497,7 @@ distribute_args(List *item, List *args)
 }
 
 /*
- * qualcleanup--
- *   Fix up a qualification by removing duplicate entries (left over from
- *   normalization), and by removing 'and' and 'or' clauses which have only
- *   one valid expr (e.g., ("AND" A) => A).
- *
- * Returns the modified qualfication.
- *
- */
-static List *
-qualcleanup(Expr *qual)
-{
-   if (qual == NULL)
-       return NIL;
-
-   if (is_opclause((Node *) qual))
-   {
-       return ((List *) make_clause(qual->opType, qual->oper,
-                           lcons(qualcleanup((Expr *) get_leftop(qual)),
-                          lcons(qualcleanup((Expr *) get_rightop(qual)),
-                                NIL))));
-   }
-   else if (and_clause((Node *) qual))
-   {
-       List       *temp = NIL;
-       List       *t_list = NIL;
-       List       *new_and_args = NIL;
-
-       foreach(temp, qual->args)
-           t_list = lappend(t_list, qualcleanup(lfirst(temp)));
-
-       new_and_args = remove_duplicates(t_list);
-
-       if (length(new_and_args) > 1)
-           return (List *) make_andclause(new_and_args);
-       else
-           return lfirst(new_and_args);
-   }
-   else if (or_clause((Node *) qual))
-   {
-       List       *temp = NIL;
-       List       *t_list = NIL;
-       List       *new_or_args = NIL;
-
-       foreach(temp, qual->args)
-           t_list = lappend(t_list, qualcleanup(lfirst(temp)));
-
-       new_or_args = remove_duplicates(t_list);
-
-
-       if (length(new_or_args) > 1)
-           return (List *) make_orclause(new_or_args);
-       else
-           return lfirst(new_or_args);
-   }
-   else if (not_clause((Node *) qual))
-       return (List *) make_notclause((Expr *) qualcleanup((Expr *) get_notclausearg(qual)));
-
-   else
-       return (List *) qual;
-}
-
-/*
- * remove-ands--
+ * remove_ands
  *   Remove the explicit "AND"s from the qualification:
  *             ("AND" A B) => (A B)
  *
@@ -543,12 +540,9 @@ remove_ands(Expr *qual)
        return (List *) qual;
 }
 
-/*****************************************************************************
- *
- *
- *
- *****************************************************************************/
-
+/*
+ * remove_duplicates
+ */
 static List *
 remove_duplicates(List *list)
 {