Commit 56c88772 authored by Tom Lane's avatar Tom Lane

Turns out that my recent elimination of the 'redundant' flatten_andors()

code in prepqual.c had a small drawback: the flatten_andors code was
able to cope with deeply nested AND/OR structures (like 10000 ORs in
a row), whereas eval_const_expressions tends to recurse until it
overruns the stack.  Revise eval_const_expressions so that it doesn't
choke on deeply nested ANDs or ORs.
parent 25434e32
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.195 2005/04/14 21:44:09 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.196 2005/04/23 04:42:53 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -73,8 +73,10 @@ static bool set_coercionform_dontcare_walker(Node *node, void *context); ...@@ -73,8 +73,10 @@ static bool set_coercionform_dontcare_walker(Node *node, void *context);
static Node *eval_const_expressions_mutator(Node *node, static Node *eval_const_expressions_mutator(Node *node,
eval_const_expressions_context *context); eval_const_expressions_context *context);
static List *simplify_or_arguments(List *args, static List *simplify_or_arguments(List *args,
eval_const_expressions_context *context,
bool *haveNull, bool *forceTrue); bool *haveNull, bool *forceTrue);
static List *simplify_and_arguments(List *args, static List *simplify_and_arguments(List *args,
eval_const_expressions_context *context,
bool *haveNull, bool *forceFalse); bool *haveNull, bool *forceFalse);
static Expr *simplify_boolean_equality(List *args); static Expr *simplify_boolean_equality(List *args);
static Expr *simplify_function(Oid funcid, Oid result_type, List *args, static Expr *simplify_function(Oid funcid, Oid result_type, List *args,
...@@ -1466,16 +1468,6 @@ eval_const_expressions_mutator(Node *node, ...@@ -1466,16 +1468,6 @@ eval_const_expressions_mutator(Node *node,
if (IsA(node, BoolExpr)) if (IsA(node, BoolExpr))
{ {
BoolExpr *expr = (BoolExpr *) node; BoolExpr *expr = (BoolExpr *) node;
List *args;
/*
* Reduce constants in the BoolExpr's arguments. We know args is
* either NIL or a List node, so we can call
* expression_tree_mutator directly rather than recursing to self.
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
eval_const_expressions_mutator,
(void *) context);
switch (expr->boolop) switch (expr->boolop)
{ {
...@@ -1485,7 +1477,7 @@ eval_const_expressions_mutator(Node *node, ...@@ -1485,7 +1477,7 @@ eval_const_expressions_mutator(Node *node,
bool haveNull = false; bool haveNull = false;
bool forceTrue = false; bool forceTrue = false;
newargs = simplify_or_arguments(args, newargs = simplify_or_arguments(expr->args, context,
&haveNull, &forceTrue); &haveNull, &forceTrue);
if (forceTrue) if (forceTrue)
return makeBoolConst(true, false); return makeBoolConst(true, false);
...@@ -1506,7 +1498,7 @@ eval_const_expressions_mutator(Node *node, ...@@ -1506,7 +1498,7 @@ eval_const_expressions_mutator(Node *node,
bool haveNull = false; bool haveNull = false;
bool forceFalse = false; bool forceFalse = false;
newargs = simplify_and_arguments(args, newargs = simplify_and_arguments(expr->args, context,
&haveNull, &forceFalse); &haveNull, &forceFalse);
if (forceFalse) if (forceFalse)
return makeBoolConst(false, false); return makeBoolConst(false, false);
...@@ -1522,25 +1514,31 @@ eval_const_expressions_mutator(Node *node, ...@@ -1522,25 +1514,31 @@ eval_const_expressions_mutator(Node *node,
return (Node *) make_andclause(newargs); return (Node *) make_andclause(newargs);
} }
case NOT_EXPR: case NOT_EXPR:
Assert(list_length(args) == 1);
if (IsA(linitial(args), Const))
{
Const *const_input = (Const *) linitial(args);
/* NOT NULL => NULL */
if (const_input->constisnull)
return makeBoolConst(false, true);
/* otherwise pretty easy */
return makeBoolConst(!DatumGetBool(const_input->constvalue),
false);
}
else if (not_clause((Node *) linitial(args)))
{ {
/* Cancel NOT/NOT */ Node *arg;
return (Node *) get_notclausearg((Expr *) linitial(args));
Assert(list_length(expr->args) == 1);
arg = eval_const_expressions_mutator(linitial(expr->args),
context);
if (IsA(arg, Const))
{
Const *const_input = (Const *) arg;
/* NOT NULL => NULL */
if (const_input->constisnull)
return makeBoolConst(false, true);
/* otherwise pretty easy */
return makeBoolConst(!DatumGetBool(const_input->constvalue),
false);
}
else if (not_clause(arg))
{
/* Cancel NOT/NOT */
return (Node *) get_notclausearg((Expr *) arg);
}
/* Else we still need a NOT node */
return (Node *) make_notclause((Expr *) arg);
} }
/* Else we still need a NOT node */
return (Node *) make_notclause((Expr *) linitial(args));
default: default:
elog(ERROR, "unrecognized boolop: %d", elog(ERROR, "unrecognized boolop: %d",
(int) expr->boolop); (int) expr->boolop);
...@@ -1869,33 +1867,87 @@ eval_const_expressions_mutator(Node *node, ...@@ -1869,33 +1867,87 @@ eval_const_expressions_mutator(Node *node,
} }
/* /*
* Subroutine for eval_const_expressions: scan the arguments of an OR clause * Subroutine for eval_const_expressions: process arguments of an OR clause
*
* This includes flattening of nested ORs as well as recursion to
* eval_const_expressions to simplify the OR arguments.
* *
* OR arguments are handled as follows: * After simplification, OR arguments are handled as follows:
* non constant: keep * non constant: keep
* FALSE: drop (does not affect result) * FALSE: drop (does not affect result)
* TRUE: force result to TRUE * TRUE: force result to TRUE
* NULL: keep only one * NULL: keep only one
* We must keep one NULL input because ExecEvalOr returns NULL when no input * We must keep one NULL input because ExecEvalOr returns NULL when no input
* is TRUE and at least one is NULL. * is TRUE and at least one is NULL. We don't actually include the NULL
* * here, that's supposed to be done by the caller.
* This is split out as a subroutine so that we can recurse to fold sub-ORs
* into the upper OR clause, thereby ensuring that nested ORs are flattened.
* *
* The output arguments *haveNull and *forceTrue must be initialized FALSE * 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, * by the caller. They will be set TRUE if a null constant or true constant,
* respectively, is detected anywhere in the argument list. * respectively, is detected anywhere in the argument list.
*/ */
static List * static List *
simplify_or_arguments(List *args, bool *haveNull, bool *forceTrue) simplify_or_arguments(List *args,
eval_const_expressions_context *context,
bool *haveNull, bool *forceTrue)
{ {
List *newargs = NIL; List *newargs = NIL;
ListCell *larg; List *unprocessed_args;
foreach(larg, args) /*
* Since the parser considers OR to be a binary operator, long OR lists
* become deeply nested expressions. We must flatten these into long
* argument lists of a single OR operator. To avoid blowing out the stack
* with recursion of eval_const_expressions, we resort to some tenseness
* here: we keep a list of not-yet-processed inputs, and handle flattening
* of nested ORs by prepending to the to-do list instead of recursing.
*/
unprocessed_args = list_copy(args);
while (unprocessed_args)
{ {
Node *arg = (Node *) lfirst(larg); Node *arg = (Node *) linitial(unprocessed_args);
unprocessed_args = list_delete_first(unprocessed_args);
/* flatten nested ORs as per above comment */
if (or_clause(arg))
{
List *subargs = list_copy(((BoolExpr *) arg)->args);
/* overly tense code to avoid leaking unused list header */
if (!unprocessed_args)
unprocessed_args = subargs;
else
{
List *oldhdr = unprocessed_args;
unprocessed_args = list_concat(subargs, unprocessed_args);
pfree(oldhdr);
}
continue;
}
/* If it's not an OR, simplify it */
arg = eval_const_expressions_mutator(arg, context);
/*
* It is unlikely but not impossible for simplification of a
* non-OR clause to produce an OR. Recheck, but don't be
* too tense about it since it's not a mainstream case.
* In particular we don't worry about const-simplifying
* the input twice.
*/
if (or_clause(arg))
{
List *subargs = list_copy(((BoolExpr *) arg)->args);
unprocessed_args = list_concat(subargs, unprocessed_args);
continue;
}
/*
* OK, we have a const-simplified non-OR argument. Process it
* per comments above.
*/
if (IsA(arg, Const)) if (IsA(arg, Const))
{ {
Const *const_input = (Const *) arg; Const *const_input = (Const *) arg;
...@@ -1914,48 +1966,91 @@ simplify_or_arguments(List *args, bool *haveNull, bool *forceTrue) ...@@ -1914,48 +1966,91 @@ simplify_or_arguments(List *args, bool *haveNull, bool *forceTrue)
return NIL; return NIL;
} }
/* otherwise, we can drop the constant-false input */ /* otherwise, we can drop the constant-false input */
continue;
} }
else if (or_clause(arg))
{ /* else emit the simplified arg into the result list */
newargs = list_concat(newargs, newargs = lappend(newargs, arg);
simplify_or_arguments(((BoolExpr *) arg)->args,
haveNull, forceTrue));
}
else
newargs = lappend(newargs, arg);
} }
return newargs; return newargs;
} }
/* /*
* Subroutine for eval_const_expressions: scan the arguments of an AND clause * Subroutine for eval_const_expressions: process arguments of an AND clause
* *
* AND arguments are handled as follows: * This includes flattening of nested ANDs as well as recursion to
* eval_const_expressions to simplify the AND arguments.
*
* After simplification, AND arguments are handled as follows:
* non constant: keep * non constant: keep
* TRUE: drop (does not affect result) * TRUE: drop (does not affect result)
* FALSE: force result to FALSE * FALSE: force result to FALSE
* NULL: keep only one * NULL: keep only one
* We must keep one NULL input because ExecEvalAnd returns NULL when no input * We must keep one NULL input because ExecEvalAnd returns NULL when no input
* is FALSE and at least one is NULL. * is FALSE and at least one is NULL. We don't actually include the NULL
* * here, that's supposed to be done by the caller.
* This is split out as a subroutine so that we can recurse to fold sub-ANDs
* into the upper AND clause, thereby ensuring that nested ANDs are flattened.
* *
* The output arguments *haveNull and *forceFalse must be initialized FALSE * 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, * by the caller. They will be set TRUE if a null constant or false constant,
* respectively, is detected anywhere in the argument list. * respectively, is detected anywhere in the argument list.
*/ */
static List * static List *
simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse) simplify_and_arguments(List *args,
eval_const_expressions_context *context,
bool *haveNull, bool *forceFalse)
{ {
List *newargs = NIL; List *newargs = NIL;
ListCell *larg; List *unprocessed_args;
foreach(larg, args) /* See comments in simplify_or_arguments */
unprocessed_args = list_copy(args);
while (unprocessed_args)
{ {
Node *arg = (Node *) lfirst(larg); Node *arg = (Node *) linitial(unprocessed_args);
unprocessed_args = list_delete_first(unprocessed_args);
/* flatten nested ANDs as per above comment */
if (and_clause(arg))
{
List *subargs = list_copy(((BoolExpr *) arg)->args);
/* overly tense code to avoid leaking unused list header */
if (!unprocessed_args)
unprocessed_args = subargs;
else
{
List *oldhdr = unprocessed_args;
unprocessed_args = list_concat(subargs, unprocessed_args);
pfree(oldhdr);
}
continue;
}
/* If it's not an AND, simplify it */
arg = eval_const_expressions_mutator(arg, context);
/*
* It is unlikely but not impossible for simplification of a
* non-AND clause to produce an AND. Recheck, but don't be
* too tense about it since it's not a mainstream case.
* In particular we don't worry about const-simplifying
* the input twice.
*/
if (and_clause(arg))
{
List *subargs = list_copy(((BoolExpr *) arg)->args);
unprocessed_args = list_concat(subargs, unprocessed_args);
continue;
}
/*
* OK, we have a const-simplified non-AND argument. Process it
* per comments above.
*/
if (IsA(arg, Const)) if (IsA(arg, Const))
{ {
Const *const_input = (Const *) arg; Const *const_input = (Const *) arg;
...@@ -1974,15 +2069,11 @@ simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse) ...@@ -1974,15 +2069,11 @@ simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse)
return NIL; return NIL;
} }
/* otherwise, we can drop the constant-true input */ /* otherwise, we can drop the constant-true input */
continue;
} }
else if (and_clause(arg))
{ /* else emit the simplified arg into the result list */
newargs = list_concat(newargs, newargs = lappend(newargs, arg);
simplify_and_arguments(((BoolExpr *) arg)->args,
haveNull, forceFalse));
}
else
newargs = lappend(newargs, arg);
} }
return newargs; return newargs;
......
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