summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/prep/prepqual.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/prep/prepqual.c')
-rw-r--r--src/backend/optimizer/prep/prepqual.c186
1 files changed, 74 insertions, 112 deletions
diff --git a/src/backend/optimizer/prep/prepqual.c b/src/backend/optimizer/prep/prepqual.c
index 1bff3509e26..186de4c40ac 100644
--- a/src/backend/optimizer/prep/prepqual.c
+++ b/src/backend/optimizer/prep/prepqual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.39 2003/11/29 19:51:51 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.40 2003/12/28 21:57:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,7 +20,7 @@
#include "optimizer/prep.h"
#include "utils/lsyscache.h"
-static Expr *flatten_andors(Expr *qual);
+static Node *flatten_andors_mutator(Node *node, void *context);
static void flatten_andors_and_walker(FastList *out_list, List *andlist);
static void flatten_andors_or_walker(FastList *out_list, List *orlist);
static List *pull_ands(List *andlist);
@@ -61,8 +61,7 @@ static void count_bool_nodes(Expr *qual, double *nodes,
*
* canonicalize_qual() does "smart" conversion to either CNF or DNF, per
* the above considerations, while cnfify() and dnfify() simply perform
- * the demanded transformation. The latter two may become dead code
- * eventually.
+ * the demanded transformation. The latter two are presently dead code.
*****************************************************************************/
@@ -72,11 +71,6 @@ static void count_bool_nodes(Expr *qual, double *nodes,
*
* Returns the modified qualification.
*
- * If 'removeAndFlag' is true then it removes explicit AND at the top level,
- * producing a list of implicitly-ANDed conditions. Otherwise, a regular
- * boolean expression is returned. Since most callers pass 'true', we
- * prefer to declare the result as List *, not Expr *.
- *
* XXX This code could be much smarter, at the cost of also being slower,
* if we tried to compute selectivities and/or see whether there are
* actually indexes to support an indexscan implementation of a DNF qual.
@@ -85,8 +79,8 @@ static void count_bool_nodes(Expr *qual, double *nodes,
* implement. For now, though, we just try to avoid doing anything
* quite as stupid as unconditionally converting to CNF was...
*/
-List *
-canonicalize_qual(Expr *qual, bool removeAndFlag)
+Expr *
+canonicalize_qual(Expr *qual)
{
Expr *newqual;
double nodes,
@@ -95,23 +89,23 @@ canonicalize_qual(Expr *qual, bool removeAndFlag)
bool cnfok,
dnfok;
+ /* Quick exit for empty qual */
if (qual == NULL)
- return NIL;
+ return NULL;
/*
* Flatten AND and OR groups throughout the tree. This improvement is
* always worthwhile, so do it unconditionally.
*/
- qual = flatten_andors(qual);
+ newqual = (Expr *) flatten_andors((Node *) qual);
/*
* Push down NOTs. We do this only in the top-level boolean
* expression, without examining arguments of operators/functions.
- * Even so, it might not be a win if we are unable to find negators
- * for all the operators involved; perhaps we should compare before-
- * and-after tree sizes?
+ * The main reason for doing this is to expose as much top-level AND/OR
+ * structure as we can, so there's no point in descending further.
*/
- newqual = find_nots(qual);
+ newqual = find_nots(newqual);
/*
* Choose whether to convert to CNF, or DNF, or leave well enough
@@ -175,13 +169,10 @@ canonicalize_qual(Expr *qual, bool removeAndFlag)
newqual = qual_cleanup(find_ands(newqual));
}
- /* Convert to implicit-AND list if requested */
- if (removeAndFlag)
- newqual = (Expr *) make_ands_implicit(newqual);
-
- return (List *) newqual;
+ return newqual;
}
+#ifdef NOT_USED
/*
* cnfify
* Convert a qualification to conjunctive normal form by applying
@@ -223,6 +214,7 @@ cnfify(Expr *qual, bool removeAndFlag)
return (List *) newqual;
}
+#endif
#ifdef NOT_USED
/*
@@ -281,50 +273,46 @@ dnfify(Expr *qual)
/*--------------------
* flatten_andors
- * Given a qualification, simplify nested AND/OR clauses into flat
- * AND/OR clauses with more arguments.
+ * Given an expression tree, simplify nested AND/OR clauses into flat
+ * AND/OR clauses with more arguments. The entire tree is processed.
*
- * Returns the rebuilt expr (note original list structure is not touched).
+ * Returns the rebuilt expr (note original structure is not touched).
*--------------------
*/
-static Expr *
-flatten_andors(Expr *qual)
+Node *
+flatten_andors(Node *node)
{
- if (qual == NULL)
- return NULL;
+ return flatten_andors_mutator(node, NULL);
+}
- if (and_clause((Node *) qual))
+static Node *
+flatten_andors_mutator(Node *node, void *context)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, BoolExpr))
{
- FastList out_list;
+ BoolExpr *bexpr = (BoolExpr *) node;
- FastListInit(&out_list);
- flatten_andors_and_walker(&out_list, ((BoolExpr *) qual)->args);
- return make_andclause(FastListValue(&out_list));
- }
- else if (or_clause((Node *) qual))
- {
- FastList out_list;
+ if (bexpr->boolop == AND_EXPR)
+ {
+ FastList out_list;
- FastListInit(&out_list);
- flatten_andors_or_walker(&out_list, ((BoolExpr *) qual)->args);
- return make_orclause(FastListValue(&out_list));
- }
- else if (not_clause((Node *) qual))
- return make_notclause(flatten_andors(get_notclausearg(qual)));
- else if (is_opclause(qual))
- {
- OpExpr *opexpr = (OpExpr *) qual;
- Expr *left = (Expr *) get_leftop(qual);
- Expr *right = (Expr *) get_rightop(qual);
-
- return make_opclause(opexpr->opno,
- opexpr->opresulttype,
- opexpr->opretset,
- flatten_andors(left),
- flatten_andors(right));
+ FastListInit(&out_list);
+ flatten_andors_and_walker(&out_list, bexpr->args);
+ return (Node *) make_andclause(FastListValue(&out_list));
+ }
+ if (bexpr->boolop == OR_EXPR)
+ {
+ FastList out_list;
+
+ FastListInit(&out_list);
+ flatten_andors_or_walker(&out_list, bexpr->args);
+ return (Node *) make_orclause(FastListValue(&out_list));
+ }
+ /* else it's a NOT clause, fall through */
}
- else
- return qual;
+ return expression_tree_mutator(node, flatten_andors_mutator, context);
}
static void
@@ -334,9 +322,9 @@ flatten_andors_and_walker(FastList *out_list, List *andlist)
foreach(arg, andlist)
{
- Expr *subexpr = (Expr *) lfirst(arg);
+ Node *subexpr = (Node *) lfirst(arg);
- if (and_clause((Node *) subexpr))
+ if (and_clause(subexpr))
flatten_andors_and_walker(out_list, ((BoolExpr *) subexpr)->args);
else
FastAppend(out_list, flatten_andors(subexpr));
@@ -350,9 +338,9 @@ flatten_andors_or_walker(FastList *out_list, List *orlist)
foreach(arg, orlist)
{
- Expr *subexpr = (Expr *) lfirst(arg);
+ Node *subexpr = (Node *) lfirst(arg);
- if (or_clause((Node *) subexpr))
+ if (or_clause(subexpr))
flatten_andors_or_walker(out_list, ((BoolExpr *) subexpr)->args);
else
FastAppend(out_list, flatten_andors(subexpr));
@@ -383,9 +371,9 @@ pull_ands_walker(FastList *out_list, List *andlist)
foreach(arg, andlist)
{
- Expr *subexpr = (Expr *) lfirst(arg);
+ Node *subexpr = (Node *) lfirst(arg);
- if (and_clause((Node *) subexpr))
+ if (and_clause(subexpr))
pull_ands_walker(out_list, ((BoolExpr *) subexpr)->args);
else
FastAppend(out_list, subexpr);
@@ -416,9 +404,9 @@ pull_ors_walker(FastList *out_list, List *orlist)
foreach(arg, orlist)
{
- Expr *subexpr = (Expr *) lfirst(arg);
+ Node *subexpr = (Node *) lfirst(arg);
- if (or_clause((Node *) subexpr))
+ if (or_clause(subexpr))
pull_ors_walker(out_list, ((BoolExpr *) subexpr)->args);
else
FastAppend(out_list, subexpr);
@@ -427,9 +415,10 @@ pull_ors_walker(FastList *out_list, List *orlist)
/*
* find_nots
- * Traverse the qualification, looking for 'NOT's to take care of.
- * For 'NOT' clauses, apply push_not() to try to push down the 'NOT'.
- * For all other clause types, simply recurse.
+ * Traverse the qualification, looking for NOTs to take care of.
+ * For NOT clauses, apply push_nots() to try to push down the NOT.
+ * For AND and OR clause types, simply recurse. Otherwise stop
+ * recursing (we do not worry about structure below the top AND/OR tree).
*
* Returns the modified qualification. AND/OR flatness is preserved.
*/
@@ -439,21 +428,6 @@ find_nots(Expr *qual)
if (qual == NULL)
return NULL;
-#ifdef NOT_USED
- /* recursing into operator expressions is probably not worth it. */
- if (is_opclause(qual))
- {
- OpExpr *opexpr = (OpExpr *) qual;
- Expr *left = (Expr *) get_leftop(qual);
- Expr *right = (Expr *) get_rightop(qual);
-
- return make_opclause(opexpr->opno,
- opexpr->opresulttype,
- opexpr->opretset,
- find_nots(left),
- find_nots(right));
- }
-#endif
if (and_clause((Node *) qual))
{
FastList t_list;
@@ -482,7 +456,7 @@ find_nots(Expr *qual)
/*
* push_nots
- * Push down a 'NOT' as far as possible.
+ * Push down a NOT as far as possible.
*
* Input is an expression to be negated (e.g., the argument of a NOT clause).
* Returns a new qual equivalent to the negation of the given qual.
@@ -496,7 +470,7 @@ push_nots(Expr *qual)
/*
* Negate an operator clause if possible: ("NOT" (< A B)) => (> A B)
- * Otherwise, retain the clause as it is (the 'not' can't be pushed
+ * Otherwise, retain the clause as it is (the NOT can't be pushed
* down any farther).
*/
if (is_opclause(qual))
@@ -543,16 +517,15 @@ push_nots(Expr *qual)
else if (not_clause((Node *) qual))
{
/*
- * Another 'not' cancels this 'not', so eliminate the 'not' and
- * stop negating this branch. But search the subexpression for
- * more 'not's to simplify.
+ * Another NOT cancels this NOT, so eliminate the NOT and
+ * stop negating this branch.
*/
- return find_nots(get_notclausearg(qual));
+ return get_notclausearg(qual);
}
else
{
/*
- * We don't know how to negate anything else, place a 'not' at
+ * We don't know how to negate anything else, place a NOT at
* this level.
*/
return make_notclause(qual);
@@ -561,12 +534,13 @@ push_nots(Expr *qual)
/*
* find_ors
- * Given a qualification tree with the 'not's pushed down, convert it
+ * Given a qualification tree with the NOTs 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))
*
* Note that 'or' clauses will always be turned into 'and' clauses
- * if they contain any 'and' subclauses.
+ * if they contain any 'and' subclauses. Also, we do not descend
+ * below the top-level AND/OR structure.
*
* Returns the modified qualification. AND/OR flatness is preserved.
*/
@@ -576,7 +550,6 @@ find_ors(Expr *qual)
if (qual == NULL)
return NULL;
- /* We used to recurse into opclauses here, but I see no reason to... */
if (and_clause((Node *) qual))
{
List *andlist = NIL;
@@ -595,8 +568,6 @@ find_ors(Expr *qual)
orlist = lappend(orlist, find_ors(lfirst(temp)));
return or_normalize(pull_ors(orlist));
}
- else if (not_clause((Node *) qual))
- return make_notclause(find_ors(get_notclausearg(qual)));
else
return qual;
}
@@ -688,12 +659,13 @@ or_normalize(List *orlist)
/*
* find_ands
- * Given a qualification tree with the 'not's pushed down, convert it
+ * Given a qualification tree with the NOTs pushed down, convert it
* to a tree in DNF by repeatedly applying the rule:
* ("AND" A ("OR" B C)) => ("OR" ("AND" A B) ("AND" A C))
*
* Note that 'and' clauses will always be turned into 'or' clauses
- * if they contain any 'or' subclauses.
+ * if they contain any 'or' subclauses. Also, we do not descend
+ * below the top-level AND/OR structure.
*
* Returns the modified qualification. AND/OR flatness is preserved.
*/
@@ -703,7 +675,6 @@ find_ands(Expr *qual)
if (qual == NULL)
return NULL;
- /* We used to recurse into opclauses here, but I see no reason to... */
if (or_clause((Node *) qual))
{
List *orlist = NIL;
@@ -722,8 +693,6 @@ find_ands(Expr *qual)
andlist = lappend(andlist, find_ands(lfirst(temp)));
return and_normalize(pull_ands(andlist));
}
- else if (not_clause((Node *) qual))
- return make_notclause(find_ands(get_notclausearg(qual)));
else
return qual;
}
@@ -860,8 +829,6 @@ qual_cleanup(Expr *qual)
else
return lfirst(orlist);
}
- else if (not_clause((Node *) qual))
- return make_notclause(qual_cleanup(get_notclausearg(qual)));
else
return qual;
}
@@ -889,14 +856,14 @@ remove_duplicates(List *list)
/*
* count_bool_nodes
* Support for heuristics in canonicalize_qual(): count the
- * number of nodes that are inputs to the top level AND/OR/NOT
+ * number of nodes that are inputs to the top level AND/OR
* part of a qual tree, and estimate how many nodes will appear
* in the CNF'ified or DNF'ified equivalent of the expression.
*
- * This is just an approximate calculation; it doesn't deal with NOTs
- * very well, and of course it cannot detect possible simplifications
- * from eliminating duplicate subclauses. The idea is just to cheaply
- * determine whether CNF will be markedly worse than DNF or vice versa.
+ * This is just an approximate calculation; it cannot detect possible
+ * simplifications from eliminating duplicate subclauses. The idea is just to
+ * cheaply determine whether CNF will be markedly worse than DNF or vice
+ * versa.
*
* The counts/estimates are represented as doubles to avoid risk of overflow.
*/
@@ -953,11 +920,6 @@ count_bool_nodes(Expr *qual,
if (*cnfnodes < *dnfnodes)
*cnfnodes = *dnfnodes;
}
- else if (not_clause((Node *) qual))
- {
- count_bool_nodes(get_notclausearg(qual),
- nodes, cnfnodes, dnfnodes);
- }
else if (contain_subplans((Node *) qual))
{
/*