Commit 3f8bc47d authored by Bruce Momjian's avatar Bruce Momjian

cnf'ify cleanup

parent 2d69fd90
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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); ...@@ -32,7 +32,7 @@ static Expr *push_nots(Expr *qual);
static Expr *normalize(Expr *qual); static Expr *normalize(Expr *qual);
static List *or_normalize(List *orlist); static List *or_normalize(List *orlist);
static List *distribute_args(List *item, List *args); 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_ands(Expr *qual);
static List *remove_duplicates(List *list); static List *remove_duplicates(List *list);
...@@ -52,7 +52,7 @@ 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 * Convert a qualification to conjunctive normal form by applying
* successive normalizations. * successive normalizations.
* *
...@@ -73,7 +73,7 @@ cnfify(Expr *qual, bool removeAndFlag) ...@@ -73,7 +73,7 @@ cnfify(Expr *qual, bool removeAndFlag)
{ {
newqual = find_nots(pull_args(qual)); newqual = find_nots(pull_args(qual));
newqual = normalize(pull_args(newqual)); newqual = normalize(pull_args(newqual));
newqual = (Expr *) qualcleanup(pull_args(newqual)); newqual = (Expr *) qual_cleanup(pull_args(newqual));
newqual = pull_args(newqual);; newqual = pull_args(newqual);;
if (removeAndFlag) if (removeAndFlag)
...@@ -84,21 +84,22 @@ cnfify(Expr *qual, bool removeAndFlag) ...@@ -84,21 +84,22 @@ cnfify(Expr *qual, bool removeAndFlag)
newqual = (Expr *) remove_ands(make_andclause(lcons(newqual, NIL))); newqual = (Expr *) remove_ands(make_andclause(lcons(newqual, NIL)));
} }
} }
else if (qual != NULL)
newqual = (Expr *) lcons(qual, NIL);
return (List *) (newqual); return (List *) (newqual);
} }
/* /*
* pull-args-- * find_nots
* Given a qualification, eliminate nested 'and' and 'or' clauses. * 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. * Returns the modified qualification.
* *
*/ */
static Expr * static Expr *
pull_args(Expr *qual) find_nots(Expr *qual)
{ {
if (qual == NULL) if (qual == NULL)
return NULL; return NULL;
...@@ -106,8 +107,8 @@ pull_args(Expr *qual) ...@@ -106,8 +107,8 @@ pull_args(Expr *qual)
if (is_opclause((Node *) qual)) if (is_opclause((Node *) qual))
{ {
return (make_clause(qual->opType, qual->oper, return (make_clause(qual->opType, qual->oper,
lcons(pull_args((Expr *) get_leftop(qual)), lcons(find_nots((Expr *) get_leftop(qual)),
lcons(pull_args((Expr *) get_rightop(qual)), lcons(find_nots((Expr *) get_rightop(qual)),
NIL)))); NIL))));
} }
else if (and_clause((Node *) qual)) else if (and_clause((Node *) qual))
...@@ -116,8 +117,9 @@ pull_args(Expr *qual) ...@@ -116,8 +117,9 @@ pull_args(Expr *qual)
List *t_list = NIL; List *t_list = NIL;
foreach(temp, qual->args) foreach(temp, qual->args)
t_list = lappend(t_list, pull_args(lfirst(temp))); t_list = lappend(t_list, find_nots(lfirst(temp)));
return make_andclause(pull_ands(t_list));
return make_andclause(t_list);
} }
else if (or_clause((Node *) qual)) else if (or_clause((Node *) qual))
{ {
...@@ -125,75 +127,149 @@ pull_args(Expr *qual) ...@@ -125,75 +127,149 @@ pull_args(Expr *qual)
List *t_list = NIL; List *t_list = NIL;
foreach(temp, qual->args) foreach(temp, qual->args)
t_list = lappend(t_list, pull_args(lfirst(temp))); t_list = lappend(t_list, find_nots(lfirst(temp)));
return make_orclause(pull_ors(t_list)); return make_orclause(t_list);
} }
else if (not_clause((Node *) qual)) else if (not_clause((Node *) qual))
return make_notclause(pull_args(get_notclausearg(qual))); return push_nots(get_notclausearg(qual));
else else
return qual; return qual;
} }
/* /*
* pull-ors-- * normalize
* Pull the arguments of an 'or' clause nested within another 'or' * Given a qualification tree with the 'not's pushed down, convert it
* clause up into the argument list of the parent. * 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 * static Expr *
pull_ors(List *orlist) normalize(Expr *qual)
{ {
if (orlist == NIL) if (qual == NULL)
return NIL; 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), return (make_clause(expr->opType, expr->oper,
copyObject((Node *) lnext(orlist))))); 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 else
return lcons(lfirst(orlist), pull_ors(lnext(orlist))); return make_orclause(orlist);
}
else if (not_clause((Node *) qual))
return make_notclause(normalize(get_notclausearg(qual)));
else
return qual;
} }
/* /*
* pull-ands-- * qual_cleanup
* Pull the arguments of an 'and' clause nested within another 'and' * Fix up a qualification by removing duplicate entries (left over from
* clause up into the argument list of the parent. * 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 * static List *
pull_ands(List *andlist) qual_cleanup(Expr *qual)
{ {
if (andlist == NIL) if (qual == NULL)
return NIL; 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), foreach(temp, qual->args)
copyObject((Node *) lnext(andlist))))); 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 else
return lcons(lfirst(andlist), pull_ands(lnext(andlist))); return lfirst(new_or_args);
}
else if (not_clause((Node *) qual))
return (List *) make_notclause((Expr *) qual_cleanup((Expr *) get_notclausearg(qual)));
else
return (List *) qual;
} }
/* /*
* find-nots-- * pull_args
* Traverse the qualification, looking for 'not's to take care of. * Given a qualification, eliminate nested 'and' and 'or' clauses.
* 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. * Returns the modified qualification.
* *
*/ */
static Expr * static Expr *
find_nots(Expr *qual) pull_args(Expr *qual)
{ {
if (qual == NULL) if (qual == NULL)
return NULL; return NULL;
...@@ -201,8 +277,8 @@ find_nots(Expr *qual) ...@@ -201,8 +277,8 @@ find_nots(Expr *qual)
if (is_opclause((Node *) qual)) if (is_opclause((Node *) qual))
{ {
return (make_clause(qual->opType, qual->oper, return (make_clause(qual->opType, qual->oper,
lcons(find_nots((Expr *) get_leftop(qual)), lcons(pull_args((Expr *) get_leftop(qual)),
lcons(find_nots((Expr *) get_rightop(qual)), lcons(pull_args((Expr *) get_rightop(qual)),
NIL)))); NIL))));
} }
else if (and_clause((Node *) qual)) else if (and_clause((Node *) qual))
...@@ -211,9 +287,8 @@ find_nots(Expr *qual) ...@@ -211,9 +287,8 @@ find_nots(Expr *qual)
List *t_list = NIL; List *t_list = NIL;
foreach(temp, qual->args) foreach(temp, qual->args)
t_list = lappend(t_list, find_nots(lfirst(temp))); t_list = lappend(t_list, pull_args(lfirst(temp)));
return make_andclause(pull_ands(t_list));
return make_andclause(t_list);
} }
else if (or_clause((Node *) qual)) else if (or_clause((Node *) qual))
{ {
...@@ -221,17 +296,65 @@ find_nots(Expr *qual) ...@@ -221,17 +296,65 @@ find_nots(Expr *qual)
List *t_list = NIL; List *t_list = NIL;
foreach(temp, qual->args) foreach(temp, qual->args)
t_list = lappend(t_list, find_nots(lfirst(temp))); t_list = lappend(t_list, pull_args(lfirst(temp)));
return make_orclause(t_list); return make_orclause(pull_ors(t_list));
} }
else if (not_clause((Node *) qual)) else if (not_clause((Node *) qual))
return push_nots(get_notclausearg(qual)); return make_notclause(pull_args(get_notclausearg(qual)));
else else
return qual; 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. * Negate the descendants of a 'not' clause.
* *
* Returns the modified qualification. * Returns the modified qualification.
...@@ -308,71 +431,7 @@ push_nots(Expr *qual) ...@@ -308,71 +431,7 @@ push_nots(Expr *qual)
} }
/* /*
* normalize-- * or_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--
* Given a list of exprs which are 'or'ed together, distribute any * Given a list of exprs which are 'or'ed together, distribute any
* 'and' clauses. * 'and' clauses.
* *
...@@ -409,7 +468,7 @@ or_normalize(List *orlist) ...@@ -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'. * 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)) * 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) ...@@ -438,69 +497,7 @@ distribute_args(List *item, List *args)
} }
/* /*
* qualcleanup-- * remove_ands
* 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 the explicit "AND"s from the qualification: * Remove the explicit "AND"s from the qualification:
* ("AND" A B) => (A B) * ("AND" A B) => (A B)
* *
...@@ -543,12 +540,9 @@ remove_ands(Expr *qual) ...@@ -543,12 +540,9 @@ remove_ands(Expr *qual)
return (List *) qual; return (List *) qual;
} }
/***************************************************************************** /*
* * remove_duplicates
* */
*
*****************************************************************************/
static List * static List *
remove_duplicates(List *list) remove_duplicates(List *list)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment