mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
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.
This commit is contained in:
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* 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_volatile_functions_walker(Node *node, void *context);
|
||||||
static bool contain_nonstrict_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 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,
|
static Expr *simplify_function(Oid funcid, Oid result_type, List *args,
|
||||||
bool allow_inline, List *active_fns);
|
bool allow_inline, List *active_fns);
|
||||||
static Expr *evaluate_function(Oid funcid, Oid result_type, List *args,
|
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.
|
* Variant of make_andclause for ANDing two qual conditions together.
|
||||||
* Qual conditions have the property that a NULL nodetree is interpreted
|
* Qual conditions have the property that a NULL nodetree is interpreted
|
||||||
* as 'true'.
|
* 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 *
|
Node *
|
||||||
make_and_qual(Node *qual1, Node *qual2)
|
make_and_qual(Node *qual1, Node *qual2)
|
||||||
@ -1210,7 +1217,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
|||||||
{
|
{
|
||||||
BoolExpr *expr = (BoolExpr *) node;
|
BoolExpr *expr = (BoolExpr *) node;
|
||||||
List *args;
|
List *args;
|
||||||
Const *const_input;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reduce constants in the BoolExpr's arguments. We know args is
|
* 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:
|
case OR_EXPR:
|
||||||
{
|
{
|
||||||
/*----------
|
List *newargs;
|
||||||
* 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;
|
|
||||||
bool haveNull = false;
|
bool haveNull = false;
|
||||||
bool forceTrue = false;
|
bool forceTrue = false;
|
||||||
|
|
||||||
FastListInit(&newargs);
|
newargs = simplify_or_arguments(args,
|
||||||
foreach(arg, args)
|
&haveNull, &forceTrue);
|
||||||
{
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
if (forceTrue)
|
if (forceTrue)
|
||||||
return MAKEBOOLCONST(true, false);
|
return MAKEBOOLCONST(true, false);
|
||||||
if (haveNull)
|
if (haveNull)
|
||||||
FastAppend(&newargs, MAKEBOOLCONST(false, true));
|
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
|
||||||
/* If all the inputs are FALSE, result is FALSE */
|
/* If all the inputs are FALSE, result is FALSE */
|
||||||
if (FastListValue(&newargs) == NIL)
|
if (newargs == NIL)
|
||||||
return MAKEBOOLCONST(false, false);
|
return MAKEBOOLCONST(false, false);
|
||||||
/* If only one nonconst-or-NULL input, it's the result */
|
/* If only one nonconst-or-NULL input, it's the result */
|
||||||
if (lnext(FastListValue(&newargs)) == NIL)
|
if (lnext(newargs) == NIL)
|
||||||
return (Node *) lfirst(FastListValue(&newargs));
|
return (Node *) lfirst(newargs);
|
||||||
/* Else we still need an OR node */
|
/* Else we still need an OR node */
|
||||||
return (Node *) make_orclause(FastListValue(&newargs));
|
return (Node *) make_orclause(newargs);
|
||||||
}
|
}
|
||||||
case AND_EXPR:
|
case AND_EXPR:
|
||||||
{
|
{
|
||||||
/*----------
|
List *newargs;
|
||||||
* 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;
|
|
||||||
bool haveNull = false;
|
bool haveNull = false;
|
||||||
bool forceFalse = false;
|
bool forceFalse = false;
|
||||||
|
|
||||||
FastListInit(&newargs);
|
newargs = simplify_and_arguments(args,
|
||||||
foreach(arg, args)
|
&haveNull, &forceFalse);
|
||||||
{
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
if (forceFalse)
|
if (forceFalse)
|
||||||
return MAKEBOOLCONST(false, false);
|
return MAKEBOOLCONST(false, false);
|
||||||
if (haveNull)
|
if (haveNull)
|
||||||
FastAppend(&newargs, MAKEBOOLCONST(false, true));
|
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
|
||||||
/* If all the inputs are TRUE, result is TRUE */
|
/* If all the inputs are TRUE, result is TRUE */
|
||||||
if (FastListValue(&newargs) == NIL)
|
if (newargs == NIL)
|
||||||
return MAKEBOOLCONST(true, false);
|
return MAKEBOOLCONST(true, false);
|
||||||
/* If only one nonconst-or-NULL input, it's the result */
|
/* If only one nonconst-or-NULL input, it's the result */
|
||||||
if (lnext(FastListValue(&newargs)) == NIL)
|
if (lnext(newargs) == NIL)
|
||||||
return (Node *) lfirst(FastListValue(&newargs));
|
return (Node *) lfirst(newargs);
|
||||||
/* Else we still need an AND node */
|
/* Else we still need an AND node */
|
||||||
return (Node *) make_andclause(FastListValue(&newargs));
|
return (Node *) make_andclause(newargs);
|
||||||
}
|
}
|
||||||
case NOT_EXPR:
|
case NOT_EXPR:
|
||||||
Assert(length(args) == 1);
|
Assert(length(args) == 1);
|
||||||
if (IsA(lfirst(args), Const))
|
if (IsA(lfirst(args), Const))
|
||||||
{
|
{
|
||||||
const_input = (Const *) lfirst(args);
|
Const *const_input = (Const *) lfirst(args);
|
||||||
|
|
||||||
/* NOT NULL => NULL */
|
/* NOT NULL => NULL */
|
||||||
if (const_input->constisnull)
|
if (const_input->constisnull)
|
||||||
return MAKEBOOLCONST(false, true);
|
return MAKEBOOLCONST(false, true);
|
||||||
@ -1341,8 +1284,13 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
|||||||
return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
|
return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
|
||||||
false);
|
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 */
|
/* Else we still need a NOT node */
|
||||||
return (Node *) make_notclause(lfirst(args));
|
return (Node *) make_notclause((Expr *) lfirst(args));
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized boolop: %d",
|
elog(ERROR, "unrecognized boolop: %d",
|
||||||
(int) expr->boolop);
|
(int) expr->boolop);
|
||||||
@ -1579,6 +1527,128 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
|||||||
(void *) 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
|
* Subroutine for eval_const_expressions: try to simplify a function call
|
||||||
* (which might originally have been an operator; we don't care)
|
* (which might originally have been an operator; we don't care)
|
||||||
|
Reference in New Issue
Block a user