1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* prepqual.c
|
1999-09-12 20:08:17 +02:00
|
|
|
* Routines for preprocessing qualification expressions
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2004-08-29 06:13:13 +02:00
|
|
|
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2004-09-01 20:04:32 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepqual.c,v 1.47 2004/09/01 18:04:32 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-10-31 11:59:42 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "postgres.h"
|
|
|
|
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "nodes/makefuncs.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "optimizer/clauses.h"
|
|
|
|
#include "optimizer/prep.h"
|
|
|
|
#include "utils/lsyscache.h"
|
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
|
2003-12-28 22:57:37 +01:00
|
|
|
static Node *flatten_andors_mutator(Node *node, void *context);
|
1997-09-08 23:56:23 +02:00
|
|
|
static List *pull_ands(List *andlist);
|
2003-05-29 00:32:50 +02:00
|
|
|
static List *pull_ors(List *orlist);
|
1997-09-08 23:56:23 +02:00
|
|
|
static Expr *find_nots(Expr *qual);
|
|
|
|
static Expr *push_nots(Expr *qual);
|
2003-12-30 22:49:19 +01:00
|
|
|
static Expr *find_duplicate_ors(Expr *qual);
|
|
|
|
static Expr *process_duplicate_ors(List *orlist);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
1999-09-13 02:17:25 +02:00
|
|
|
/*
|
|
|
|
* canonicalize_qual
|
2003-12-30 22:49:19 +01:00
|
|
|
* Convert a qualification expression to the most useful form.
|
1999-09-13 02:17:25 +02:00
|
|
|
*
|
2003-12-30 22:49:19 +01:00
|
|
|
* The name of this routine is a holdover from a time when it would try to
|
|
|
|
* force the expression into canonical AND-of-ORs or OR-of-ANDs form.
|
|
|
|
* Eventually, we recognized that that had more theoretical purity than
|
|
|
|
* actual usefulness, and so now the transformation doesn't involve any
|
|
|
|
* notion of reaching a canonical form.
|
1999-09-13 02:17:25 +02:00
|
|
|
*
|
2003-12-30 22:49:19 +01:00
|
|
|
* Returns the modified qualification.
|
1999-09-13 02:17:25 +02:00
|
|
|
*/
|
2003-12-28 22:57:37 +01:00
|
|
|
Expr *
|
|
|
|
canonicalize_qual(Expr *qual)
|
1999-09-13 02:17:25 +02:00
|
|
|
{
|
2000-01-28 04:22:36 +01:00
|
|
|
Expr *newqual;
|
1999-09-13 02:17:25 +02:00
|
|
|
|
2003-12-28 22:57:37 +01:00
|
|
|
/* Quick exit for empty qual */
|
1999-09-13 02:17:25 +02:00
|
|
|
if (qual == NULL)
|
2003-12-28 22:57:37 +01:00
|
|
|
return NULL;
|
1999-09-13 02:17:25 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2003-12-30 22:49:19 +01:00
|
|
|
* Flatten AND and OR groups throughout the expression tree.
|
1999-09-13 02:17:25 +02:00
|
|
|
*/
|
2003-12-28 22:57:37 +01:00
|
|
|
newqual = (Expr *) flatten_andors((Node *) qual);
|
2000-01-28 04:22:36 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Push down NOTs. We do this only in the top-level boolean
|
2004-08-29 07:07:03 +02:00
|
|
|
* expression, without examining arguments of operators/functions. The
|
|
|
|
* main reason for doing this is to expose as much top-level AND/OR
|
2003-12-28 22:57:37 +01:00
|
|
|
* structure as we can, so there's no point in descending further.
|
1999-09-13 02:17:25 +02:00
|
|
|
*/
|
2003-12-28 22:57:37 +01:00
|
|
|
newqual = find_nots(newqual);
|
1999-09-13 02:17:25 +02:00
|
|
|
|
|
|
|
/*
|
2003-12-30 22:49:19 +01:00
|
|
|
* Pull up redundant subclauses in OR-of-AND trees. Again, we do this
|
|
|
|
* only within the top-level AND/OR structure.
|
2000-01-28 04:22:36 +01:00
|
|
|
*/
|
2003-12-30 22:49:19 +01:00
|
|
|
newqual = find_duplicate_ors(newqual);
|
1999-09-13 02:17:25 +02:00
|
|
|
|
2003-12-28 22:57:37 +01:00
|
|
|
return newqual;
|
1999-09-13 02:17:25 +02:00
|
|
|
}
|
|
|
|
|
1999-09-12 20:08:17 +02:00
|
|
|
|
|
|
|
/*--------------------
|
|
|
|
* The parser regards AND and OR as purely binary operators, so a qual like
|
|
|
|
* (A = 1) OR (A = 2) OR (A = 3) ...
|
|
|
|
* will produce a nested parsetree
|
|
|
|
* (OR (A = 1) (OR (A = 2) (OR (A = 3) ...)))
|
|
|
|
* In reality, the optimizer and executor regard AND and OR as n-argument
|
|
|
|
* operators, so this tree can be flattened to
|
|
|
|
* (OR (A = 1) (A = 2) (A = 3) ...)
|
|
|
|
* which is the responsibility of the routines below.
|
|
|
|
*
|
|
|
|
* flatten_andors() does the basic transformation with no initial assumptions.
|
|
|
|
* pull_ands() and pull_ors() are used to maintain flatness of the AND/OR
|
|
|
|
* tree after local transformations that might introduce nested AND/ORs.
|
|
|
|
*--------------------
|
|
|
|
*/
|
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
/*
|
1999-09-12 20:08:17 +02:00
|
|
|
* flatten_andors
|
2003-12-28 22:57:37 +01:00
|
|
|
* Given an expression tree, simplify nested AND/OR clauses into flat
|
|
|
|
* AND/OR clauses with more arguments. The entire tree is processed.
|
1999-09-12 20:08:17 +02:00
|
|
|
*
|
2003-12-28 22:57:37 +01:00
|
|
|
* Returns the rebuilt expr (note original structure is not touched).
|
2003-12-30 22:49:19 +01:00
|
|
|
*
|
|
|
|
* This is exported so that other modules can perform the part of
|
|
|
|
* canonicalize_qual processing that applies to entire trees, rather
|
|
|
|
* than just the top-level boolean expressions.
|
1999-09-12 20:08:17 +02:00
|
|
|
*/
|
2003-12-28 22:57:37 +01:00
|
|
|
Node *
|
|
|
|
flatten_andors(Node *node)
|
1999-09-12 20:08:17 +02:00
|
|
|
{
|
2003-12-28 22:57:37 +01:00
|
|
|
return flatten_andors_mutator(node, NULL);
|
|
|
|
}
|
1999-09-12 20:08:17 +02:00
|
|
|
|
2003-12-28 22:57:37 +01:00
|
|
|
static Node *
|
|
|
|
flatten_andors_mutator(Node *node, void *context)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (IsA(node, BoolExpr))
|
1999-09-12 20:08:17 +02:00
|
|
|
{
|
2003-12-28 22:57:37 +01:00
|
|
|
BoolExpr *bexpr = (BoolExpr *) node;
|
2003-05-29 00:32:50 +02:00
|
|
|
|
2003-12-28 22:57:37 +01:00
|
|
|
if (bexpr->boolop == AND_EXPR)
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *out_list = NIL;
|
|
|
|
ListCell *arg;
|
|
|
|
|
|
|
|
foreach(arg, bexpr->args)
|
|
|
|
{
|
|
|
|
Node *subexpr = flatten_andors((Node *) lfirst(arg));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: we can destructively concat the subexpression's
|
|
|
|
* arglist because we know the recursive invocation of
|
|
|
|
* flatten_andors will have built a new arglist not shared
|
2004-08-29 07:07:03 +02:00
|
|
|
* with any other expr. Otherwise we'd need a list_copy
|
|
|
|
* here.
|
2004-06-01 06:47:46 +02:00
|
|
|
*/
|
|
|
|
if (and_clause(subexpr))
|
|
|
|
out_list = list_concat(out_list,
|
|
|
|
((BoolExpr *) subexpr)->args);
|
|
|
|
else
|
|
|
|
out_list = lappend(out_list, subexpr);
|
|
|
|
}
|
|
|
|
return (Node *) make_andclause(out_list);
|
2003-12-28 22:57:37 +01:00
|
|
|
}
|
|
|
|
if (bexpr->boolop == OR_EXPR)
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *out_list = NIL;
|
|
|
|
ListCell *arg;
|
|
|
|
|
|
|
|
foreach(arg, bexpr->args)
|
|
|
|
{
|
|
|
|
Node *subexpr = flatten_andors((Node *) lfirst(arg));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: we can destructively concat the subexpression's
|
|
|
|
* arglist because we know the recursive invocation of
|
|
|
|
* flatten_andors will have built a new arglist not shared
|
2004-08-29 07:07:03 +02:00
|
|
|
* with any other expr. Otherwise we'd need a list_copy
|
|
|
|
* here.
|
2004-06-01 06:47:46 +02:00
|
|
|
*/
|
|
|
|
if (or_clause(subexpr))
|
|
|
|
out_list = list_concat(out_list,
|
|
|
|
((BoolExpr *) subexpr)->args);
|
|
|
|
else
|
|
|
|
out_list = lappend(out_list, subexpr);
|
|
|
|
}
|
|
|
|
return (Node *) make_orclause(out_list);
|
2003-12-28 22:57:37 +01:00
|
|
|
}
|
|
|
|
/* else it's a NOT clause, fall through */
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2003-12-28 22:57:37 +01:00
|
|
|
return expression_tree_mutator(node, flatten_andors_mutator, context);
|
1999-09-12 20:08:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pull_ands
|
2003-05-29 00:32:50 +02:00
|
|
|
* Recursively flatten nested AND clauses into a single and-clause list.
|
1999-09-12 20:08:17 +02:00
|
|
|
*
|
2003-05-29 00:32:50 +02:00
|
|
|
* Input is the arglist of an AND clause.
|
|
|
|
* Returns the rebuilt arglist (note original list structure is not touched).
|
1999-09-12 20:08:17 +02:00
|
|
|
*/
|
|
|
|
static List *
|
|
|
|
pull_ands(List *andlist)
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *out_list = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *arg;
|
1999-09-12 20:08:17 +02:00
|
|
|
|
|
|
|
foreach(arg, andlist)
|
|
|
|
{
|
2003-12-28 22:57:37 +01:00
|
|
|
Node *subexpr = (Node *) lfirst(arg);
|
1999-09-12 20:08:17 +02:00
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
/*
|
|
|
|
* Note: we can destructively concat the subexpression's arglist
|
|
|
|
* because we know the recursive invocation of pull_ands will have
|
|
|
|
* built a new arglist not shared with any other expr. Otherwise
|
|
|
|
* we'd need a list_copy here.
|
|
|
|
*/
|
2003-12-28 22:57:37 +01:00
|
|
|
if (and_clause(subexpr))
|
2004-06-01 06:47:46 +02:00
|
|
|
out_list = list_concat(out_list,
|
2004-08-29 07:07:03 +02:00
|
|
|
pull_ands(((BoolExpr *) subexpr)->args));
|
2003-05-29 00:32:50 +02:00
|
|
|
else
|
2004-06-01 06:47:46 +02:00
|
|
|
out_list = lappend(out_list, subexpr);
|
2003-05-29 00:32:50 +02:00
|
|
|
}
|
2004-06-01 06:47:46 +02:00
|
|
|
return out_list;
|
2003-05-29 00:32:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pull_ors
|
|
|
|
* Recursively flatten nested OR clauses into a single or-clause list.
|
|
|
|
*
|
|
|
|
* Input is the arglist of an OR clause.
|
|
|
|
* Returns the rebuilt arglist (note original list structure is not touched).
|
|
|
|
*/
|
|
|
|
static List *
|
|
|
|
pull_ors(List *orlist)
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *out_list = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *arg;
|
2003-05-29 00:32:50 +02:00
|
|
|
|
|
|
|
foreach(arg, orlist)
|
|
|
|
{
|
2003-12-28 22:57:37 +01:00
|
|
|
Node *subexpr = (Node *) lfirst(arg);
|
2003-05-29 00:32:50 +02:00
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
/*
|
|
|
|
* Note: we can destructively concat the subexpression's arglist
|
|
|
|
* because we know the recursive invocation of pull_ors will have
|
|
|
|
* built a new arglist not shared with any other expr. Otherwise
|
|
|
|
* we'd need a list_copy here.
|
|
|
|
*/
|
2003-12-28 22:57:37 +01:00
|
|
|
if (or_clause(subexpr))
|
2004-06-01 06:47:46 +02:00
|
|
|
out_list = list_concat(out_list,
|
2004-08-29 07:07:03 +02:00
|
|
|
pull_ors(((BoolExpr *) subexpr)->args));
|
1999-09-12 20:08:17 +02:00
|
|
|
else
|
2004-06-01 06:47:46 +02:00
|
|
|
out_list = lappend(out_list, subexpr);
|
1999-09-12 20:08:17 +02:00
|
|
|
}
|
2004-06-01 06:47:46 +02:00
|
|
|
return out_list;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1998-10-04 05:30:56 +02:00
|
|
|
* find_nots
|
2003-12-28 22:57:37 +01:00
|
|
|
* Traverse the qualification, looking for NOTs to take care of.
|
|
|
|
* For NOT clauses, apply push_nots() to try to push down the NOT.
|
2004-08-29 07:07:03 +02:00
|
|
|
* For AND and OR clause types, simply recurse. Otherwise stop
|
2003-12-28 22:57:37 +01:00
|
|
|
* recursing (we do not worry about structure below the top AND/OR tree).
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2000-04-12 19:17:23 +02:00
|
|
|
* Returns the modified qualification. AND/OR flatness is preserved.
|
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
|
|
|
|
1999-09-07 05:47:06 +02:00
|
|
|
if (and_clause((Node *) qual))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *t_list = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
foreach(temp, ((BoolExpr *) qual)->args)
|
2004-06-01 06:47:46 +02:00
|
|
|
t_list = lappend(t_list, find_nots(lfirst(temp)));
|
|
|
|
return make_andclause(pull_ands(t_list));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (or_clause((Node *) qual))
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *t_list = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
foreach(temp, ((BoolExpr *) qual)->args)
|
2004-06-01 06:47:46 +02:00
|
|
|
t_list = lappend(t_list, find_nots(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 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
|
|
|
}
|
|
|
|
|
1999-09-07 05:47:06 +02:00
|
|
|
/*
|
|
|
|
* push_nots
|
2003-12-28 22:57:37 +01:00
|
|
|
* Push down a NOT as far as possible.
|
1999-09-07 05:47:06 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
static Expr *
|
|
|
|
push_nots(Expr *qual)
|
|
|
|
{
|
|
|
|
if (qual == NULL)
|
2000-04-12 19:17:23 +02:00
|
|
|
return make_notclause(qual); /* XXX is this right? Or
|
|
|
|
* possible? */
|
1999-09-07 05:47:06 +02:00
|
|
|
|
|
|
|
/*
|
2004-09-01 20:04:32 +02:00
|
|
|
* Negate an operator clause if possible: (NOT (< A B)) => (>= A B)
|
2004-08-29 07:07:03 +02:00
|
|
|
* Otherwise, retain the clause as it is (the NOT can't be pushed down
|
|
|
|
* any farther).
|
1999-09-07 05:47:06 +02:00
|
|
|
*/
|
2002-12-12 16:49:42 +01:00
|
|
|
if (is_opclause(qual))
|
1999-09-07 05:47:06 +02:00
|
|
|
{
|
2002-12-12 16:49:42 +01:00
|
|
|
OpExpr *opexpr = (OpExpr *) qual;
|
|
|
|
Oid negator = get_negator(opexpr->opno);
|
1999-09-07 05:47:06 +02:00
|
|
|
|
|
|
|
if (negator)
|
2002-12-12 16:49:42 +01:00
|
|
|
return make_opclause(negator,
|
|
|
|
opexpr->opresulttype,
|
|
|
|
opexpr->opretset,
|
|
|
|
(Expr *) get_leftop(qual),
|
|
|
|
(Expr *) get_rightop(qual));
|
1999-09-07 05:47:06 +02:00
|
|
|
else
|
|
|
|
return make_notclause(qual);
|
|
|
|
}
|
|
|
|
else if (and_clause((Node *) qual))
|
|
|
|
{
|
1999-09-12 20:08:17 +02:00
|
|
|
/*--------------------
|
|
|
|
* Apply DeMorgan's Laws:
|
2003-12-30 22:49:19 +01:00
|
|
|
* (NOT (AND A B)) => (OR (NOT A) (NOT B))
|
|
|
|
* (NOT (OR A B)) => (AND (NOT A) (NOT B))
|
1999-09-12 20:08:17 +02:00
|
|
|
* i.e., swap AND for OR and negate all the subclauses.
|
|
|
|
*--------------------
|
1999-09-07 05:47:06 +02:00
|
|
|
*/
|
2004-06-01 06:47:46 +02:00
|
|
|
List *t_list = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp;
|
1999-09-07 05:47:06 +02:00
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
foreach(temp, ((BoolExpr *) qual)->args)
|
2004-06-01 06:47:46 +02:00
|
|
|
t_list = lappend(t_list, push_nots(lfirst(temp)));
|
|
|
|
return make_orclause(pull_ors(t_list));
|
1999-09-07 05:47:06 +02:00
|
|
|
}
|
|
|
|
else if (or_clause((Node *) qual))
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *t_list = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp;
|
1999-09-07 05:47:06 +02:00
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
foreach(temp, ((BoolExpr *) qual)->args)
|
2004-06-01 06:47:46 +02:00
|
|
|
t_list = lappend(t_list, push_nots(lfirst(temp)));
|
|
|
|
return make_andclause(pull_ands(t_list));
|
1999-09-07 05:47:06 +02:00
|
|
|
}
|
|
|
|
else if (not_clause((Node *) qual))
|
|
|
|
{
|
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* Another NOT cancels this NOT, so eliminate the NOT and stop
|
|
|
|
* negating this branch.
|
1999-09-07 05:47:06 +02:00
|
|
|
*/
|
2003-12-28 22:57:37 +01:00
|
|
|
return get_notclausearg(qual);
|
1999-09-07 05:47:06 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* We don't know how to negate anything else, place a NOT at this
|
|
|
|
* level.
|
1999-09-07 05:47:06 +02:00
|
|
|
*/
|
|
|
|
return make_notclause(qual);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
|
|
|
|
/*--------------------
|
|
|
|
* The following code attempts to apply the inverse OR distributive law:
|
|
|
|
* ((A AND B) OR (A AND C)) => (A AND (B OR C))
|
|
|
|
* That is, locate OR clauses in which every subclause contains an
|
|
|
|
* identical term, and pull out the duplicated terms.
|
1998-10-04 05:30:56 +02:00
|
|
|
*
|
2003-12-30 22:49:19 +01:00
|
|
|
* This may seem like a fairly useless activity, but it turns out to be
|
|
|
|
* applicable to many machine-generated queries, and there are also queries
|
2004-08-29 07:07:03 +02:00
|
|
|
* in some of the TPC benchmarks that need it. This was in fact almost the
|
2003-12-30 22:49:19 +01:00
|
|
|
* sole useful side-effect of the old prepqual code that tried to force
|
|
|
|
* the query into canonical AND-of-ORs form: the canonical equivalent of
|
|
|
|
* ((A AND B) OR (A AND C))
|
|
|
|
* is
|
|
|
|
* ((A OR A) AND (A OR C) AND (B OR A) AND (B OR C))
|
|
|
|
* which the code was able to simplify to
|
|
|
|
* (A AND (A OR C) AND (B OR A) AND (B OR C))
|
|
|
|
* thus successfully extracting the common condition A --- but at the cost
|
|
|
|
* of cluttering the qual with many redundant clauses.
|
|
|
|
*--------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* find_duplicate_ors
|
|
|
|
* Given a qualification tree with the NOTs pushed down, search for
|
|
|
|
* OR clauses to which the inverse OR distributive law might apply.
|
|
|
|
* Only the top-level AND/OR structure is searched.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2004-08-29 07:07:03 +02:00
|
|
|
* Returns the modified qualification. AND/OR flatness is preserved.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-10-04 05:30:56 +02:00
|
|
|
static Expr *
|
2003-12-30 22:49:19 +01:00
|
|
|
find_duplicate_ors(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
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
if (or_clause((Node *) qual))
|
1998-10-04 05:30:56 +02:00
|
|
|
{
|
2003-12-30 22:49:19 +01:00
|
|
|
List *orlist = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp;
|
1998-10-04 05:30:56 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
/* Recurse */
|
2002-12-12 16:49:42 +01:00
|
|
|
foreach(temp, ((BoolExpr *) qual)->args)
|
2003-12-30 22:49:19 +01:00
|
|
|
orlist = lappend(orlist, find_duplicate_ors(lfirst(temp)));
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
/*
|
|
|
|
* Don't need pull_ors() since this routine will never introduce
|
|
|
|
* an OR where there wasn't one before.
|
|
|
|
*/
|
|
|
|
return process_duplicate_ors(orlist);
|
1998-10-04 05:30:56 +02:00
|
|
|
}
|
2003-12-30 22:49:19 +01:00
|
|
|
else if (and_clause((Node *) qual))
|
1998-10-04 05:30:56 +02:00
|
|
|
{
|
2003-12-30 22:49:19 +01:00
|
|
|
List *andlist = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp;
|
1998-10-04 05:30:56 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
/* Recurse */
|
2002-12-12 16:49:42 +01:00
|
|
|
foreach(temp, ((BoolExpr *) qual)->args)
|
2003-12-30 22:49:19 +01:00
|
|
|
andlist = lappend(andlist, find_duplicate_ors(lfirst(temp)));
|
|
|
|
/* Flatten any ANDs introduced just below here */
|
|
|
|
andlist = pull_ands(andlist);
|
|
|
|
/* The AND list can't get shorter, so result is always an AND */
|
|
|
|
return make_andclause(andlist);
|
1998-10-04 05:30:56 +02:00
|
|
|
}
|
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
|
|
|
/*
|
2003-12-30 22:49:19 +01:00
|
|
|
* process_duplicate_ors
|
|
|
|
* Given a list of exprs which are ORed together, try to apply
|
|
|
|
* the inverse OR distributive law.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-09-12 20:08:17 +02:00
|
|
|
* Returns the resulting expression (could be an AND clause, an OR
|
|
|
|
* clause, or maybe even a single subexpression).
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1999-09-12 20:08:17 +02:00
|
|
|
static Expr *
|
2003-12-30 22:49:19 +01:00
|
|
|
process_duplicate_ors(List *orlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-12-30 22:49:19 +01:00
|
|
|
List *reference = NIL;
|
|
|
|
int num_subclauses = 0;
|
|
|
|
List *winners;
|
|
|
|
List *neworlist;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-12 20:08:17 +02:00
|
|
|
if (orlist == NIL)
|
|
|
|
return NULL; /* probably can't happen */
|
2004-08-29 07:07:03 +02:00
|
|
|
if (list_length(orlist) == 1) /* single-expression OR (can this
|
|
|
|
* happen?) */
|
2004-05-26 06:41:50 +02:00
|
|
|
return linitial(orlist);
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-09-12 20:08:17 +02:00
|
|
|
/*
|
2003-12-30 22:49:19 +01:00
|
|
|
* Choose the shortest AND clause as the reference list --- obviously,
|
2004-08-29 07:07:03 +02:00
|
|
|
* any subclause not in this clause isn't in all the clauses. If we
|
|
|
|
* find a clause that's not an AND, we can treat it as a one-element
|
|
|
|
* AND clause, which necessarily wins as shortest.
|
1999-09-12 20:08:17 +02:00
|
|
|
*/
|
|
|
|
foreach(temp, orlist)
|
1998-10-04 05:30:56 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
Expr *clause = (Expr *) lfirst(temp);
|
1998-10-04 05:30:56 +02:00
|
|
|
|
1999-09-12 20:08:17 +02:00
|
|
|
if (and_clause((Node *) clause))
|
|
|
|
{
|
2003-12-30 22:49:19 +01:00
|
|
|
List *subclauses = ((BoolExpr *) clause)->args;
|
2004-05-31 01:40:41 +02:00
|
|
|
int nclauses = list_length(subclauses);
|
1998-10-04 05:30:56 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
if (reference == NIL || nclauses < num_subclauses)
|
1999-09-12 20:08:17 +02:00
|
|
|
{
|
2003-12-30 22:49:19 +01:00
|
|
|
reference = subclauses;
|
1999-09-12 20:08:17 +02:00
|
|
|
num_subclauses = nclauses;
|
|
|
|
}
|
|
|
|
}
|
2003-12-30 22:49:19 +01:00
|
|
|
else
|
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
reference = list_make1(clause);
|
2003-12-30 22:49:19 +01:00
|
|
|
break;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-10-04 05:30:56 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2003-12-30 22:49:19 +01:00
|
|
|
* Just in case, eliminate any duplicates in the reference list.
|
1999-09-12 20:08:17 +02:00
|
|
|
*/
|
2004-05-31 01:40:41 +02:00
|
|
|
reference = list_union(NIL, reference);
|
1998-10-04 05:30:56 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
/*
|
|
|
|
* Check each element of the reference list to see if it's in all the
|
2004-08-29 07:07:03 +02:00
|
|
|
* OR clauses. Build a new list of winning clauses.
|
2003-12-30 22:49:19 +01:00
|
|
|
*/
|
|
|
|
winners = NIL;
|
|
|
|
foreach(temp, reference)
|
1999-09-12 20:08:17 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
Expr *refclause = (Expr *) lfirst(temp);
|
2003-12-30 22:49:19 +01:00
|
|
|
bool win = true;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *temp2;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
foreach(temp2, orlist)
|
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
Expr *clause = (Expr *) lfirst(temp2);
|
1999-09-07 05:47:06 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
if (and_clause((Node *) clause))
|
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
if (!list_member(((BoolExpr *) clause)->args, refclause))
|
2003-12-30 22:49:19 +01:00
|
|
|
{
|
|
|
|
win = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!equal(refclause, clause))
|
|
|
|
{
|
|
|
|
win = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-09-07 05:47:06 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
if (win)
|
|
|
|
winners = lappend(winners, refclause);
|
1999-09-07 05:47:06 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
/*
|
|
|
|
* If no winners, we can't transform the OR
|
|
|
|
*/
|
|
|
|
if (winners == NIL)
|
|
|
|
return make_orclause(orlist);
|
1998-10-04 05:30:56 +02:00
|
|
|
|
1999-09-12 20:08:17 +02:00
|
|
|
/*
|
2003-12-30 22:49:19 +01:00
|
|
|
* Generate new OR list consisting of the remaining sub-clauses.
|
|
|
|
*
|
2004-08-29 07:07:03 +02:00
|
|
|
* If any clause degenerates to empty, then we have a situation like (A
|
|
|
|
* AND B) OR (A), which can be reduced to just A --- that is, the
|
2003-12-30 22:49:19 +01:00
|
|
|
* additional conditions in other arms of the OR are irrelevant.
|
|
|
|
*
|
2004-05-31 01:40:41 +02:00
|
|
|
* Note that because we use list_difference, any multiple occurrences of
|
2004-08-29 07:07:03 +02:00
|
|
|
* a winning clause in an AND sub-clause will be removed
|
|
|
|
* automatically.
|
1999-09-12 20:08:17 +02:00
|
|
|
*/
|
2003-12-30 22:49:19 +01:00
|
|
|
neworlist = NIL;
|
|
|
|
foreach(temp, orlist)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
Expr *clause = (Expr *) lfirst(temp);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
if (and_clause((Node *) clause))
|
1999-09-07 05:47:06 +02:00
|
|
|
{
|
2003-12-30 22:49:19 +01:00
|
|
|
List *subclauses = ((BoolExpr *) clause)->args;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-05-31 01:40:41 +02:00
|
|
|
subclauses = list_difference(subclauses, winners);
|
2003-12-30 22:49:19 +01:00
|
|
|
if (subclauses != NIL)
|
1999-09-12 20:08:17 +02:00
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
if (list_length(subclauses) == 1)
|
2004-05-26 06:41:50 +02:00
|
|
|
neworlist = lappend(neworlist, linitial(subclauses));
|
2003-12-30 22:49:19 +01:00
|
|
|
else
|
|
|
|
neworlist = lappend(neworlist, make_andclause(subclauses));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
neworlist = NIL; /* degenerate case, see above */
|
2003-12-30 22:49:19 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
if (!list_member(winners, clause))
|
2003-12-30 22:49:19 +01:00
|
|
|
neworlist = lappend(neworlist, clause);
|
|
|
|
else
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
neworlist = NIL; /* degenerate case, see above */
|
2003-12-30 22:49:19 +01:00
|
|
|
break;
|
1999-09-12 20:08:17 +02:00
|
|
|
}
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* Append reduced OR to the winners list, if it's not degenerate,
|
|
|
|
* handling the special case of one element correctly (can that really
|
|
|
|
* happen?). Also be careful to maintain AND/OR flatness in case we
|
|
|
|
* pulled up a sub-sub-OR-clause.
|
1999-09-12 20:08:17 +02:00
|
|
|
*/
|
2003-12-30 22:49:19 +01:00
|
|
|
if (neworlist != NIL)
|
1999-09-13 02:17:25 +02:00
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
if (list_length(neworlist) == 1)
|
2004-05-26 06:41:50 +02:00
|
|
|
winners = lappend(winners, linitial(neworlist));
|
1999-09-13 02:17:25 +02:00
|
|
|
else
|
2003-12-30 22:49:19 +01:00
|
|
|
winners = lappend(winners, make_orclause(pull_ors(neworlist)));
|
1999-09-13 02:17:25 +02:00
|
|
|
}
|
|
|
|
|
2003-12-30 22:49:19 +01:00
|
|
|
/*
|
|
|
|
* And return the constructed AND clause, again being wary of a single
|
|
|
|
* element and AND/OR flatness.
|
|
|
|
*/
|
2004-05-31 01:40:41 +02:00
|
|
|
if (list_length(winners) == 1)
|
2004-05-26 06:41:50 +02:00
|
|
|
return (Expr *) linitial(winners);
|
1999-09-13 02:17:25 +02:00
|
|
|
else
|
2003-12-30 22:49:19 +01:00
|
|
|
return make_andclause(pull_ands(winners));
|
1999-09-13 02:17:25 +02:00
|
|
|
}
|