1 /*-------------------------------------------------------------------------
4 * Routines for preprocessing the parse tree qualification
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.10 1998/09/01 03:23:43 momjian Exp $
12 *-------------------------------------------------------------------------
14 #include <sys/types.h>
18 #include "nodes/pg_list.h"
19 #include "nodes/makefuncs.h"
21 #include "optimizer/internal.h"
22 #include "optimizer/clauses.h"
23 #include "optimizer/prep.h"
25 #include "utils/lsyscache.h"
27 static Expr *pull_args(Expr *qual);
28 static List *pull_ors(List *orlist);
29 static List *pull_ands(List *andlist);
30 static Expr *find_nots(Expr *qual);
31 static Expr *push_nots(Expr *qual);
32 static Expr *normalize(Expr *qual);
33 static List *or_normalize(List *orlist);
34 static List *distribute_args(List *item, List *args);
35 static List *qualcleanup(Expr *qual);
36 static List *remove_ands(Expr *qual);
37 static List *remove_duplicates(List *list);
39 /*****************************************************************************
41 * CNF CONVERSION ROUTINES
44 * The basic algorithms for normalizing the qualification are taken
45 * from ingres/source/qrymod/norml.c
47 * Remember that the initial qualification may consist of ARBITRARY
48 * combinations of clauses. In addition, before this routine is called,
49 * the qualification will contain explicit "AND"s.
51 *****************************************************************************/
56 * Convert a qualification to conjunctive normal form by applying
57 * successive normalizations.
59 * Returns the modified qualification with an extra level of nesting.
61 * If 'removeAndFlag' is true then it removes the explicit ANDs.
63 * NOTE: this routine is called by the planner (removeAndFlag = true)
64 * and from the rule manager (removeAndFlag = false).
68 cnfify(Expr *qual, bool removeAndFlag)
74 newqual = find_nots(pull_args(qual));
75 newqual = normalize(pull_args(newqual));
76 newqual = (Expr *) qualcleanup(pull_args(newqual));
77 newqual = pull_args(newqual);;
81 if (and_clause((Node *) newqual))
82 newqual = (Expr *) remove_ands(newqual);
84 newqual = (Expr *) remove_ands(make_andclause(lcons(newqual, NIL)));
87 else if (qual != NULL)
88 newqual = (Expr *) lcons(qual, NIL);
90 return (List *) (newqual);
95 * Given a qualification, eliminate nested 'and' and 'or' clauses.
97 * Returns the modified qualification.
101 pull_args(Expr *qual)
106 if (is_opclause((Node *) qual))
108 return (make_clause(qual->opType, qual->oper,
109 lcons(pull_args((Expr *) get_leftop(qual)),
110 lcons(pull_args((Expr *) get_rightop(qual)),
113 else if (and_clause((Node *) qual))
118 foreach(temp, qual->args)
119 t_list = lappend(t_list, pull_args(lfirst(temp)));
120 return make_andclause(pull_ands(t_list));
122 else if (or_clause((Node *) qual))
127 foreach(temp, qual->args)
128 t_list = lappend(t_list, pull_args(lfirst(temp)));
129 return make_orclause(pull_ors(t_list));
131 else if (not_clause((Node *) qual))
132 return make_notclause(pull_args(get_notclausearg(qual)));
139 * Pull the arguments of an 'or' clause nested within another 'or'
140 * clause up into the argument list of the parent.
142 * Returns the modified list.
145 pull_ors(List *orlist)
150 if (or_clause(lfirst(orlist)))
152 List *args = ((Expr *) lfirst(orlist))->args;
154 return (pull_ors(nconc(copyObject((Node *) args),
155 copyObject((Node *) lnext(orlist)))));
158 return lcons(lfirst(orlist), pull_ors(lnext(orlist)));
163 * Pull the arguments of an 'and' clause nested within another 'and'
164 * clause up into the argument list of the parent.
166 * Returns the modified list.
169 pull_ands(List *andlist)
174 if (and_clause(lfirst(andlist)))
176 List *args = ((Expr *) lfirst(andlist))->args;
178 return (pull_ands(nconc(copyObject((Node *) args),
179 copyObject((Node *) lnext(andlist)))));
182 return lcons(lfirst(andlist), pull_ands(lnext(andlist)));
187 * Traverse the qualification, looking for 'not's to take care of.
188 * For 'not' clauses, remove the 'not' and push it down to the clauses'
190 * For all other clause types, simply recurse.
192 * Returns the modified qualification.
196 find_nots(Expr *qual)
201 if (is_opclause((Node *) qual))
203 return (make_clause(qual->opType, qual->oper,
204 lcons(find_nots((Expr *) get_leftop(qual)),
205 lcons(find_nots((Expr *) get_rightop(qual)),
208 else if (and_clause((Node *) qual))
213 foreach(temp, qual->args)
214 t_list = lappend(t_list, find_nots(lfirst(temp)));
216 return make_andclause(t_list);
218 else if (or_clause((Node *) qual))
223 foreach(temp, qual->args)
224 t_list = lappend(t_list, find_nots(lfirst(temp)));
225 return make_orclause(t_list);
227 else if (not_clause((Node *) qual))
228 return push_nots(get_notclausearg(qual));
235 * Negate the descendants of a 'not' clause.
237 * Returns the modified qualification.
241 push_nots(Expr *qual)
247 * Negate an operator clause if possible: ("NOT" (< A B)) => (> A B)
248 * Otherwise, retain the clause as it is (the 'not' can't be pushed
251 if (is_opclause((Node *) qual))
253 Oper *oper = (Oper *) ((Expr *) qual)->oper;
254 Oid negator = get_negator(oper->opno);
258 Oper *op = (Oper *) makeOper(negator,
263 op->op_fcache = (FunctionCache *) NULL;
265 (make_opclause(op, get_leftop(qual), get_rightop(qual)));
268 return make_notclause(qual);
270 else if (and_clause((Node *) qual))
274 * Apply DeMorgan's Laws: ("NOT" ("AND" A B)) => ("OR" ("NOT" A)
275 * ("NOT" B)) ("NOT" ("OR" A B)) => ("AND" ("NOT" A) ("NOT" B))
276 * i.e., continue negating down through the clause's descendants.
281 foreach(temp, qual->args)
282 t_list = lappend(t_list, push_nots(lfirst(temp)));
283 return make_orclause(t_list);
285 else if (or_clause((Node *) qual))
290 foreach(temp, qual->args)
291 t_list = lappend(t_list, push_nots(lfirst(temp)));
292 return make_andclause(t_list);
294 else if (not_clause((Node *) qual))
297 * Another 'not' cancels this 'not', so eliminate the 'not' and
298 * stop negating this branch.
300 return find_nots(get_notclausearg(qual));
304 * We don't know how to negate anything else, place a 'not' at
307 return make_notclause(qual);
312 * Given a qualification tree with the 'not's pushed down, convert it
313 * to a tree in CNF by repeatedly applying the rule:
314 * ("OR" A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
316 * Note that 'or' clauses will always be turned into 'and' clauses.
318 * Returns the modified qualification.
322 normalize(Expr *qual)
327 if (is_opclause((Node *) qual))
329 Expr *expr = (Expr *) qual;
331 return (make_clause(expr->opType, expr->oper,
332 lcons(normalize((Expr *) get_leftop(qual)),
333 lcons(normalize((Expr *) get_rightop(qual)),
336 else if (and_clause((Node *) qual))
341 foreach(temp, qual->args)
342 t_list = lappend(t_list, normalize(lfirst(temp)));
343 return make_andclause(t_list);
345 else if (or_clause((Node *) qual))
347 /* XXX - let form, maybe incorrect */
350 bool has_andclause = FALSE;
352 foreach(temp, qual->args)
353 orlist = lappend(orlist, normalize(lfirst(temp)));
354 foreach(temp, orlist)
356 if (and_clause(lfirst(temp)))
358 has_andclause = TRUE;
362 if (has_andclause == TRUE)
363 return make_andclause(or_normalize(orlist));
365 return make_orclause(orlist);
368 else if (not_clause((Node *) qual))
369 return make_notclause(normalize(get_notclausearg(qual)));
376 * Given a list of exprs which are 'or'ed together, distribute any
379 * Returns the modified list.
383 or_normalize(List *orlist)
385 List *distributable = NIL;
386 List *new_orlist = NIL;
392 foreach(temp, orlist)
394 if (and_clause(lfirst(temp)))
395 distributable = lfirst(temp);
398 new_orlist = LispRemove(distributable, orlist);
403 (or_normalize(lcons(distribute_args(lfirst(new_orlist),
404 ((Expr *) distributable)->args),
405 lnext(new_orlist))));
413 * Create new 'or' clauses by or'ing 'item' with each element of 'args'.
414 * E.g.: (distribute-args A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
416 * Returns an 'and' clause.
420 distribute_args(List *item, List *args)
432 n_list = or_normalize(pull_ors(lcons(item,
433 lcons(lfirst(temp), NIL))));
434 or_list = (List *) make_orclause(n_list);
435 t_list = lappend(t_list, or_list);
437 return (List *) make_andclause(t_list);
442 * Fix up a qualification by removing duplicate entries (left over from
443 * normalization), and by removing 'and' and 'or' clauses which have only
444 * one valid expr (e.g., ("AND" A) => A).
446 * Returns the modified qualfication.
450 qualcleanup(Expr *qual)
455 if (is_opclause((Node *) qual))
457 return ((List *) make_clause(qual->opType, qual->oper,
458 lcons(qualcleanup((Expr *) get_leftop(qual)),
459 lcons(qualcleanup((Expr *) get_rightop(qual)),
462 else if (and_clause((Node *) qual))
466 List *new_and_args = NIL;
468 foreach(temp, qual->args)
469 t_list = lappend(t_list, qualcleanup(lfirst(temp)));
471 new_and_args = remove_duplicates(t_list);
473 if (length(new_and_args) > 1)
474 return (List *) make_andclause(new_and_args);
476 return lfirst(new_and_args);
478 else if (or_clause((Node *) qual))
482 List *new_or_args = NIL;
484 foreach(temp, qual->args)
485 t_list = lappend(t_list, qualcleanup(lfirst(temp)));
487 new_or_args = remove_duplicates(t_list);
490 if (length(new_or_args) > 1)
491 return (List *) make_orclause(new_or_args);
493 return lfirst(new_or_args);
495 else if (not_clause((Node *) qual))
496 return (List *) make_notclause((Expr *) qualcleanup((Expr *) get_notclausearg(qual)));
499 return (List *) qual;
504 * Remove the explicit "AND"s from the qualification:
505 * ("AND" A B) => (A B)
511 remove_ands(Expr *qual)
517 if (is_opclause((Node *) qual))
519 return ((List *) make_clause(qual->opType, qual->oper,
520 lcons(remove_ands((Expr *) get_leftop(qual)),
521 lcons(remove_ands((Expr *) get_rightop(qual)),
524 else if (and_clause((Node *) qual))
528 foreach(temp, qual->args)
529 t_list = lappend(t_list, remove_ands(lfirst(temp)));
532 else if (or_clause((Node *) qual))
536 foreach(temp, qual->args)
537 t_list = lappend(t_list, remove_ands(lfirst(temp)));
538 return (List *) make_orclause((List *) t_list);
540 else if (not_clause((Node *) qual))
541 return (List *) make_notclause((Expr *) remove_ands((Expr *) get_notclausearg(qual)));
543 return (List *) qual;
546 /*****************************************************************************
550 *****************************************************************************/
553 remove_duplicates(List *list)
558 bool there_exists_duplicate = false;
560 if (length(list) == 1)
569 if (equal(lfirst(i), lfirst(j)))
570 there_exists_duplicate = true;
572 if (!there_exists_duplicate)
573 result = lappend(result, lfirst(i));
575 there_exists_duplicate = false;