Commit 5590be0a authored by Tom Lane's avatar Tom Lane

Preserve AND/OR flatness during eval_const_expressions(). This seems a

useful improvement in any case, and it keeps the new logic for restrictinfo
structures happy.  Per report from Kris Jurka.
parent 1c15812c
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.161 2004/01/10 18:13:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.162 2004/01/12 20:48:15 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -61,6 +61,10 @@ static bool contain_mutable_functions_walker(Node *node, void *context);
static bool contain_volatile_functions_walker(Node *node, void *context);
static bool contain_nonstrict_functions_walker(Node *node, void *context);
static Node *eval_const_expressions_mutator(Node *node, List *active_fns);
static List *simplify_or_arguments(List *args,
bool *haveNull, bool *forceTrue);
static List *simplify_and_arguments(List *args,
bool *haveNull, bool *forceFalse);
static Expr *simplify_function(Oid funcid, Oid result_type, List *args,
bool allow_inline, List *active_fns);
static Expr *evaluate_function(Oid funcid, Oid result_type, List *args,
......@@ -249,6 +253,9 @@ make_andclause(List *andclauses)
* Variant of make_andclause for ANDing two qual conditions together.
* Qual conditions have the property that a NULL nodetree is interpreted
* as 'true'.
*
* NB: this makes no attempt to preserve AND/OR flatness; so it should not
* be used on a qual that has already been run through prepqual.c.
*/
Node *
make_and_qual(Node *qual1, Node *qual2)
......@@ -1210,7 +1217,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
{
BoolExpr *expr = (BoolExpr *) node;
List *args;
Const *const_input;
/*
* Reduce constants in the BoolExpr's arguments. We know args is
......@@ -1225,115 +1231,52 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
{
case OR_EXPR:
{
/*----------
* OR arguments are handled as follows:
* non constant: keep
* FALSE: drop (does not affect result)
* TRUE: force result to TRUE
* NULL: keep only one
* We keep one NULL input because ExecEvalOr returns NULL
* when no input is TRUE and at least one is NULL.
*----------
*/
FastList newargs;
List *arg;
List *newargs;
bool haveNull = false;
bool forceTrue = false;
FastListInit(&newargs);
foreach(arg, args)
{
if (!IsA(lfirst(arg), Const))
{
FastAppend(&newargs, lfirst(arg));
continue;
}
const_input = (Const *) lfirst(arg);
if (const_input->constisnull)
haveNull = true;
else if (DatumGetBool(const_input->constvalue))
forceTrue = true;
/* otherwise, we can drop the constant-false input */
}
/*
* We could return TRUE before falling out of the
* loop, but this coding method will be easier to
* adapt if we ever add a notion of non-removable
* functions. We'd need to check all the inputs for
* non-removability.
*/
newargs = simplify_or_arguments(args,
&haveNull, &forceTrue);
if (forceTrue)
return MAKEBOOLCONST(true, false);
if (haveNull)
FastAppend(&newargs, MAKEBOOLCONST(false, true));
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
/* If all the inputs are FALSE, result is FALSE */
if (FastListValue(&newargs) == NIL)
if (newargs == NIL)
return MAKEBOOLCONST(false, false);
/* If only one nonconst-or-NULL input, it's the result */
if (lnext(FastListValue(&newargs)) == NIL)
return (Node *) lfirst(FastListValue(&newargs));
if (lnext(newargs) == NIL)
return (Node *) lfirst(newargs);
/* Else we still need an OR node */
return (Node *) make_orclause(FastListValue(&newargs));
return (Node *) make_orclause(newargs);
}
case AND_EXPR:
{
/*----------
* AND arguments are handled as follows:
* non constant: keep
* TRUE: drop (does not affect result)
* FALSE: force result to FALSE
* NULL: keep only one
* We keep one NULL input because ExecEvalAnd returns NULL
* when no input is FALSE and at least one is NULL.
*----------
*/
FastList newargs;
List *arg;
List *newargs;
bool haveNull = false;
bool forceFalse = false;
FastListInit(&newargs);
foreach(arg, args)
{
if (!IsA(lfirst(arg), Const))
{
FastAppend(&newargs, lfirst(arg));
continue;
}
const_input = (Const *) lfirst(arg);
if (const_input->constisnull)
haveNull = true;
else if (!DatumGetBool(const_input->constvalue))
forceFalse = true;
/* otherwise, we can drop the constant-true input */
}
/*
* We could return FALSE before falling out of the
* loop, but this coding method will be easier to
* adapt if we ever add a notion of non-removable
* functions. We'd need to check all the inputs for
* non-removability.
*/
newargs = simplify_and_arguments(args,
&haveNull, &forceFalse);
if (forceFalse)
return MAKEBOOLCONST(false, false);
if (haveNull)
FastAppend(&newargs, MAKEBOOLCONST(false, true));
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
/* If all the inputs are TRUE, result is TRUE */
if (FastListValue(&newargs) == NIL)
if (newargs == NIL)
return MAKEBOOLCONST(true, false);
/* If only one nonconst-or-NULL input, it's the result */
if (lnext(FastListValue(&newargs)) == NIL)
return (Node *) lfirst(FastListValue(&newargs));
if (lnext(newargs) == NIL)
return (Node *) lfirst(newargs);
/* Else we still need an AND node */
return (Node *) make_andclause(FastListValue(&newargs));
return (Node *) make_andclause(newargs);
}
case NOT_EXPR:
Assert(length(args) == 1);
if (IsA(lfirst(args), Const))
{
const_input = (Const *) lfirst(args);
Const *const_input = (Const *) lfirst(args);
/* NOT NULL => NULL */
if (const_input->constisnull)
return MAKEBOOLCONST(false, true);
......@@ -1341,8 +1284,13 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
false);
}
else if (not_clause((Node *) lfirst(args)))
{
/* Cancel NOT/NOT */
return (Node *) get_notclausearg((Expr *) lfirst(args));
}
/* Else we still need a NOT node */
return (Node *) make_notclause(lfirst(args));
return (Node *) make_notclause((Expr *) lfirst(args));
default:
elog(ERROR, "unrecognized boolop: %d",
(int) expr->boolop);
......@@ -1579,6 +1527,128 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
(void *) active_fns);
}
/*
* Subroutine for eval_const_expressions: scan the arguments of an OR clause
*
* OR arguments are handled as follows:
* non constant: keep
* FALSE: drop (does not affect result)
* TRUE: force result to TRUE
* NULL: keep only one
* We must keep one NULL input because ExecEvalOr returns NULL when no input
* is TRUE and at least one is NULL.
*
* This is split out as a subroutine so that we can recurse to fold sub-ORs
* into the upper OR clause, thereby preserving AND/OR flatness.
*
* The output arguments *haveNull and *forceTrue must be initialized FALSE
* by the caller. They will be set TRUE if a null constant or true constant,
* respectively, is detected anywhere in the argument list.
*/
static List *
simplify_or_arguments(List *args, bool *haveNull, bool *forceTrue)
{
List *newargs = NIL;
List *larg;
foreach(larg, args)
{
Node *arg = (Node *) lfirst(larg);
if (IsA(arg, Const))
{
Const *const_input = (Const *) arg;
if (const_input->constisnull)
*haveNull = true;
else if (DatumGetBool(const_input->constvalue))
{
*forceTrue = true;
/*
* Once we detect a TRUE result we can just exit the loop
* immediately. However, if we ever add a notion of
* non-removable functions, we'd need to keep scanning.
*/
return NIL;
}
/* otherwise, we can drop the constant-false input */
}
else if (or_clause(arg))
{
newargs = nconc(newargs,
simplify_or_arguments(((BoolExpr *) arg)->args,
haveNull, forceTrue));
}
else
{
newargs = lappend(newargs, arg);
}
}
return newargs;
}
/*
* Subroutine for eval_const_expressions: scan the arguments of an AND clause
*
* AND arguments are handled as follows:
* non constant: keep
* TRUE: drop (does not affect result)
* FALSE: force result to FALSE
* NULL: keep only one
* We must keep one NULL input because ExecEvalAnd returns NULL when no input
* is FALSE and at least one is NULL.
*
* This is split out as a subroutine so that we can recurse to fold sub-ANDs
* into the upper AND clause, thereby preserving AND/OR flatness.
*
* The output arguments *haveNull and *forceFalse must be initialized FALSE
* by the caller. They will be set TRUE if a null constant or false constant,
* respectively, is detected anywhere in the argument list.
*/
static List *
simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse)
{
List *newargs = NIL;
List *larg;
foreach(larg, args)
{
Node *arg = (Node *) lfirst(larg);
if (IsA(arg, Const))
{
Const *const_input = (Const *) arg;
if (const_input->constisnull)
*haveNull = true;
else if (!DatumGetBool(const_input->constvalue))
{
*forceFalse = true;
/*
* Once we detect a FALSE result we can just exit the loop
* immediately. However, if we ever add a notion of
* non-removable functions, we'd need to keep scanning.
*/
return NIL;
}
/* otherwise, we can drop the constant-true input */
}
else if (and_clause(arg))
{
newargs = nconc(newargs,
simplify_and_arguments(((BoolExpr *) arg)->args,
haveNull, forceFalse));
}
else
{
newargs = lappend(newargs, arg);
}
}
return newargs;
}
/*
* Subroutine for eval_const_expressions: try to simplify a function call
* (which might originally have been an operator; we don't care)
......
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