1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* prepqual.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines for preprocessing the parse tree qualification
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1999-02-14 00:22:53 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.13 1999/02/13 23:16:37 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-10-31 11:59:42 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "nodes/pg_list.h"
|
|
|
|
#include "nodes/makefuncs.h"
|
|
|
|
|
|
|
|
#include "optimizer/internal.h"
|
|
|
|
#include "optimizer/clauses.h"
|
|
|
|
#include "optimizer/prep.h"
|
|
|
|
|
|
|
|
#include "utils/lsyscache.h"
|
|
|
|
|
1997-09-08 23:56:23 +02:00
|
|
|
static Expr *pull_args(Expr *qual);
|
|
|
|
static List *pull_ors(List *orlist);
|
|
|
|
static List *pull_ands(List *andlist);
|
|
|
|
static Expr *find_nots(Expr *qual);
|
|
|
|
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);
|
1998-10-04 05:30:56 +02:00
|
|
|
static List *qual_cleanup(Expr *qual);
|
1997-09-08 23:56:23 +02:00
|
|
|
static List *remove_ands(Expr *qual);
|
|
|
|
static List *remove_duplicates(List *list);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* CNF CONVERSION ROUTINES
|
|
|
|
*
|
|
|
|
* NOTES:
|
|
|
|
* The basic algorithms for normalizing the qualification are taken
|
|
|
|
* from ingres/source/qrymod/norml.c
|
|
|
|
*
|
|
|
|
* Remember that the initial qualification may consist of ARBITRARY
|
|
|
|
* combinations of clauses. In addition, before this routine is called,
|
|
|
|
* the qualification will contain explicit "AND"s.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* cnfify
|
1997-09-07 07:04:48 +02:00
|
|
|
* Convert a qualification to conjunctive normal form by applying
|
|
|
|
* successive normalizations.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns the modified qualification with an extra level of nesting.
|
|
|
|
*
|
|
|
|
* If 'removeAndFlag' is true then it removes the explicit ANDs.
|
|
|
|
*
|
|
|
|
* NOTE: this routine is called by the planner (removeAndFlag = true)
|
1997-09-07 07:04:48 +02:00
|
|
|
* and from the rule manager (removeAndFlag = false).
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
1997-09-08 23:56:23 +02:00
|
|
|
cnfify(Expr *qual, bool removeAndFlag)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Expr *newqual = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (qual != NULL)
|
|
|
|
{
|
|
|
|
newqual = find_nots(pull_args(qual));
|
|
|
|
newqual = normalize(pull_args(newqual));
|
1998-10-04 05:30:56 +02:00
|
|
|
newqual = (Expr *) qual_cleanup(pull_args(newqual));
|
1997-09-07 07:04:48 +02:00
|
|
|
newqual = pull_args(newqual);;
|
|
|
|
|
|
|
|
if (removeAndFlag)
|
|
|
|
{
|
|
|
|
if (and_clause((Node *) newqual))
|
|
|
|
newqual = (Expr *) remove_ands(newqual);
|
|
|
|
else
|
|
|
|
newqual = (Expr *) remove_ands(make_andclause(lcons(newqual, NIL)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (List *) (newqual);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* 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.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns the modified qualification.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static Expr *
|
1998-10-04 05:30:56 +02:00
|
|
|
find_nots(Expr *qual)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (qual == NULL)
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (is_opclause((Node *) qual))
|
|
|
|
{
|
|
|
|
return (make_clause(qual->opType, qual->oper,
|
1998-10-04 05:30:56 +02:00
|
|
|
lcons(find_nots((Expr *) get_leftop(qual)),
|
|
|
|
lcons(find_nots((Expr *) get_rightop(qual)),
|
1997-09-07 07:04:48 +02:00
|
|
|
NIL))));
|
|
|
|
}
|
|
|
|
else if (and_clause((Node *) qual))
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp = NIL;
|
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, qual->args)
|
1998-10-04 05:30:56 +02:00
|
|
|
t_list = lappend(t_list, find_nots(lfirst(temp)));
|
|
|
|
|
|
|
|
return make_andclause(t_list);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (or_clause((Node *) qual))
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp = NIL;
|
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, qual->args)
|
1998-10-04 05:30:56 +02:00
|
|
|
t_list = lappend(t_list, find_nots(lfirst(temp)));
|
|
|
|
return make_orclause(t_list);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (not_clause((Node *) qual))
|
1998-10-04 05:30:56 +02:00
|
|
|
return push_nots(get_notclausearg(qual));
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return qual;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* 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.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-10-04 05:30:56 +02:00
|
|
|
static Expr *
|
|
|
|
normalize(Expr *qual)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-10-04 05:30:56 +02:00
|
|
|
if (qual == NULL)
|
|
|
|
return NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-10-04 05:30:56 +02:00
|
|
|
if (is_opclause((Node *) qual))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-10-04 05:30:56 +02:00
|
|
|
Expr *expr = (Expr *) qual;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-10-04 05:30:56 +02:00
|
|
|
return (make_clause(expr->opType, expr->oper,
|
|
|
|
lcons(normalize((Expr *) get_leftop(qual)),
|
|
|
|
lcons(normalize((Expr *) get_rightop(qual)),
|
|
|
|
NIL))));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-10-04 05:30:56 +02:00
|
|
|
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)));
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-10-04 05:30:56 +02:00
|
|
|
return qual;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* 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.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1998-10-04 05:30:56 +02:00
|
|
|
qual_cleanup(Expr *qual)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1998-10-04 05:30:56 +02:00
|
|
|
if (qual == NULL)
|
1998-09-01 05:29:17 +02:00
|
|
|
return NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-10-04 05:30:56 +02:00
|
|
|
if (is_opclause((Node *) qual))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-10-04 05:30:56 +02:00
|
|
|
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;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-10-04 05:30:56 +02:00
|
|
|
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);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-10-04 05:30:56 +02:00
|
|
|
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)));
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-10-04 05:30:56 +02:00
|
|
|
return (List *) qual;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* pull_args
|
|
|
|
* Given a qualification, eliminate nested 'and' and 'or' clauses.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns the modified qualification.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static Expr *
|
1998-10-04 05:30:56 +02:00
|
|
|
pull_args(Expr *qual)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (qual == NULL)
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (is_opclause((Node *) qual))
|
|
|
|
{
|
|
|
|
return (make_clause(qual->opType, qual->oper,
|
1998-10-04 05:30:56 +02:00
|
|
|
lcons(pull_args((Expr *) get_leftop(qual)),
|
|
|
|
lcons(pull_args((Expr *) get_rightop(qual)),
|
1997-09-07 07:04:48 +02:00
|
|
|
NIL))));
|
|
|
|
}
|
|
|
|
else if (and_clause((Node *) qual))
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp = NIL;
|
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, qual->args)
|
1998-10-04 05:30:56 +02:00
|
|
|
t_list = lappend(t_list, pull_args(lfirst(temp)));
|
|
|
|
return make_andclause(pull_ands(t_list));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (or_clause((Node *) qual))
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp = NIL;
|
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, qual->args)
|
1998-10-04 05:30:56 +02:00
|
|
|
t_list = lappend(t_list, pull_args(lfirst(temp)));
|
|
|
|
return make_orclause(pull_ors(t_list));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (not_clause((Node *) qual))
|
1998-10-04 05:30:56 +02:00
|
|
|
return make_notclause(pull_args(get_notclausearg(qual)));
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return qual;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* 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
|
1997-09-07 07:04:48 +02:00
|
|
|
* Negate the descendants of a 'not' clause.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns the modified qualification.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static Expr *
|
1997-09-08 23:56:23 +02:00
|
|
|
push_nots(Expr *qual)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (qual == NULL)
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Negate an operator clause if possible: ("NOT" (< A B)) => (> A B)
|
|
|
|
* Otherwise, retain the clause as it is (the 'not' can't be pushed
|
|
|
|
* down any farther).
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
if (is_opclause((Node *) qual))
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Oper *oper = (Oper *) ((Expr *) qual)->oper;
|
|
|
|
Oid negator = get_negator(oper->opno);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (negator)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Oper *op = (Oper *) makeOper(negator,
|
|
|
|
InvalidOid,
|
|
|
|
oper->opresulttype,
|
|
|
|
0, NULL);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
op->op_fcache = (FunctionCache *) NULL;
|
1999-02-03 22:18:02 +01:00
|
|
|
return (make_opclause(op, get_leftop(qual), get_rightop(qual)));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return make_notclause(qual);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (and_clause((Node *) qual))
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply DeMorgan's Laws: ("NOT" ("AND" A B)) => ("OR" ("NOT" A)
|
|
|
|
* ("NOT" B)) ("NOT" ("OR" A B)) => ("AND" ("NOT" A) ("NOT" B))
|
|
|
|
* i.e., continue negating down through the clause's descendants.
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp = NIL;
|
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, qual->args)
|
|
|
|
t_list = lappend(t_list, push_nots(lfirst(temp)));
|
1998-09-01 05:29:17 +02:00
|
|
|
return make_orclause(t_list);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (or_clause((Node *) qual))
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp = NIL;
|
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, qual->args)
|
|
|
|
t_list = lappend(t_list, push_nots(lfirst(temp)));
|
1998-09-01 05:29:17 +02:00
|
|
|
return make_andclause(t_list);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (not_clause((Node *) qual))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Another 'not' cancels this 'not', so eliminate the 'not' and
|
|
|
|
* stop negating this branch.
|
|
|
|
*/
|
1998-09-01 05:29:17 +02:00
|
|
|
return find_nots(get_notclausearg(qual));
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't know how to negate anything else, place a 'not' at
|
|
|
|
* this level.
|
|
|
|
*/
|
1998-09-01 05:29:17 +02:00
|
|
|
return make_notclause(qual);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* or_normalize
|
1997-09-07 07:04:48 +02:00
|
|
|
* Given a list of exprs which are 'or'ed together, distribute any
|
|
|
|
* 'and' clauses.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns the modified list.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1997-09-08 23:56:23 +02:00
|
|
|
or_normalize(List *orlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *distributable = NIL;
|
|
|
|
List *new_orlist = NIL;
|
|
|
|
List *temp = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (orlist == NIL)
|
|
|
|
return NIL;
|
|
|
|
|
|
|
|
foreach(temp, orlist)
|
|
|
|
{
|
|
|
|
if (and_clause(lfirst(temp)))
|
|
|
|
distributable = lfirst(temp);
|
|
|
|
}
|
|
|
|
if (distributable)
|
|
|
|
new_orlist = LispRemove(distributable, orlist);
|
|
|
|
|
|
|
|
if (new_orlist)
|
|
|
|
{
|
1999-02-03 22:18:02 +01:00
|
|
|
return (or_normalize(lcons(distribute_args(lfirst(new_orlist),
|
1997-09-07 07:04:48 +02:00
|
|
|
((Expr *) distributable)->args),
|
|
|
|
lnext(new_orlist))));
|
|
|
|
}
|
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return orlist;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* distribute_args
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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))
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns an 'and' clause.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1997-09-08 23:56:23 +02:00
|
|
|
distribute_args(List *item, List *args)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *or_list = NIL;
|
|
|
|
List *n_list = NIL;
|
|
|
|
List *temp = NIL;
|
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (args == NULL)
|
1998-09-01 05:29:17 +02:00
|
|
|
return item;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, args)
|
|
|
|
{
|
|
|
|
n_list = or_normalize(pull_ors(lcons(item,
|
|
|
|
lcons(lfirst(temp), NIL))));
|
|
|
|
or_list = (List *) make_orclause(n_list);
|
|
|
|
t_list = lappend(t_list, or_list);
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return (List *) make_andclause(t_list);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* remove_ands
|
1997-09-07 07:04:48 +02:00
|
|
|
* Remove the explicit "AND"s from the qualification:
|
|
|
|
* ("AND" A B) => (A B)
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* RETURNS : qual
|
|
|
|
* MODIFIES: qual
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1997-09-08 23:56:23 +02:00
|
|
|
remove_ands(Expr *qual)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (qual == NULL)
|
1998-09-01 05:29:17 +02:00
|
|
|
return NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
if (is_opclause((Node *) qual))
|
|
|
|
{
|
|
|
|
return ((List *) make_clause(qual->opType, qual->oper,
|
|
|
|
lcons(remove_ands((Expr *) get_leftop(qual)),
|
|
|
|
lcons(remove_ands((Expr *) get_rightop(qual)),
|
|
|
|
NIL))));
|
|
|
|
}
|
|
|
|
else if (and_clause((Node *) qual))
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, qual->args)
|
|
|
|
t_list = lappend(t_list, remove_ands(lfirst(temp)));
|
1998-09-01 05:29:17 +02:00
|
|
|
return t_list;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (or_clause((Node *) qual))
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, qual->args)
|
|
|
|
t_list = lappend(t_list, remove_ands(lfirst(temp)));
|
1998-09-01 05:29:17 +02:00
|
|
|
return (List *) make_orclause((List *) t_list);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (not_clause((Node *) qual))
|
1998-09-01 05:29:17 +02:00
|
|
|
return (List *) make_notclause((Expr *) remove_ands((Expr *) get_notclausearg(qual)));
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return (List *) qual;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-10-04 05:30:56 +02:00
|
|
|
/*
|
|
|
|
* remove_duplicates
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1997-09-08 23:56:23 +02:00
|
|
|
remove_duplicates(List *list)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *i;
|
|
|
|
List *j;
|
|
|
|
List *result = NIL;
|
|
|
|
bool there_exists_duplicate = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (length(list) == 1)
|
1998-09-01 05:29:17 +02:00
|
|
|
return list;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(i, list)
|
|
|
|
{
|
|
|
|
if (i != NIL)
|
|
|
|
{
|
|
|
|
foreach(j, lnext(i))
|
|
|
|
{
|
|
|
|
if (equal(lfirst(i), lfirst(j)))
|
|
|
|
there_exists_duplicate = true;
|
|
|
|
}
|
|
|
|
if (!there_exists_duplicate)
|
|
|
|
result = lappend(result, lfirst(i));
|
|
|
|
|
|
|
|
there_exists_duplicate = false;
|
|
|
|
}
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|