Implement constant-expression simplification per Bernard

Frankpitt, plus some improvements from yours truly.  The simplifier depends
on the proiscachable field of pg_proc to tell it whether a function is
safe to pre-evaluate --- things like nextval() are not, for example.
Update pg_proc.h to contain reasonable cacheability information; as of
6.5.* hardly any functions were marked cacheable.  I may have erred too
far in the other direction; see recent mail to pghackers for more info.
This update does not force an initdb, exactly, but you won't see much
benefit from the simplifier until you do one.
This commit is contained in:
Tom Lane 1999-09-26 02:28:44 +00:00
parent 95d3d468ce
commit 40f6524161
7 changed files with 1359 additions and 938 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.60 1999/09/24 00:24:23 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.61 1999/09/26 02:28:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -956,8 +956,8 @@ ExecEvalFunc(Expr *funcClause,
static Datum
ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
{
Datum expr_value;
Node *clause;
Datum expr_value;
bool isDone;
clause = lfirst(notclause->args);
@ -995,67 +995,47 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
List *clauses;
List *clause;
bool isDone;
bool IsNull;
Datum const_value = 0;
bool AnyNull;
Datum clause_value;
IsNull = false;
clauses = orExpr->args;
AnyNull = false;
/*
* we use three valued logic functions here... we evaluate each of the
* clauses in turn, as soon as one is true we return that value. If
* none is true and none of the clauses evaluate to NULL we return
* the value of the last clause evaluated (which should be false) with
* *isNull set to false else if none is true and at least one clause
* evaluated to NULL we set *isNull flag to true -
* If any of the clauses is TRUE, the OR result is TRUE regardless
* of the states of the rest of the clauses, so we can stop evaluating
* and return TRUE immediately. If none are TRUE and one or more is
* NULL, we return NULL; otherwise we return FALSE. This makes sense
* when you interpret NULL as "don't know": if we have a TRUE then the
* OR is TRUE even if we aren't sure about some of the other inputs.
* If all the known inputs are FALSE, but we have one or more "don't
* knows", then we have to report that we "don't know" what the OR's
* result should be --- perhaps one of the "don't knows" would have been
* TRUE if we'd known its value. Only when all the inputs are known
* to be FALSE can we state confidently that the OR's result is FALSE.
*/
foreach(clause, clauses)
{
/*
* We don't iterate over sets in the quals, so pass in an isDone
* flag, but ignore it.
*/
const_value = ExecEvalExpr((Node *) lfirst(clause),
econtext,
isNull,
&isDone);
clause_value = ExecEvalExpr((Node *) lfirst(clause),
econtext,
isNull,
&isDone);
/*
* if the expression evaluates to null, then we remember it in the
* local IsNull flag, if none of the clauses are true then we need
* to set *isNull to true again.
* if we have a non-null true result, then return it.
*/
if (*isNull)
{
IsNull = *isNull;
/*
* Many functions don't (or can't!) check if an argument is
* NULL or NOT_NULL and may return TRUE (1) with *isNull TRUE
* (an_int4_column <> 1: int4ne returns TRUE for NULLs). Not
* having time to fix the function manager I want to fix OR:
* if we had 'x <> 1 OR x isnull' then when x is NULL TRUE was
* returned by the 'x <> 1' clause ... but ExecQualClause says
* that the qualification should *fail* if isnull is TRUE for
* any value returned by ExecEvalExpr. So, force this rule
* here: if isnull is TRUE then the clause failed. Note:
* nullvalue() & nonnullvalue() always sets isnull to FALSE
* for NULLs. - vadim 09/22/97
*/
const_value = 0;
}
/*
* if we have a true result, then we return it.
*/
if (DatumGetInt32(const_value) != 0)
return const_value;
AnyNull = true; /* remember we got a null */
else if (DatumGetInt32(clause_value) != 0)
return clause_value;
}
/* IsNull is true if at least one clause evaluated to NULL */
*isNull = IsNull;
return const_value;
/* AnyNull is true if at least one clause evaluated to NULL */
*isNull = AnyNull;
return (Datum) false;
}
/* ----------------------------------------------------------------
@ -1067,49 +1047,43 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
{
List *clauses;
List *clause;
Datum const_value = 0;
bool isDone;
bool IsNull;
IsNull = false;
bool AnyNull;
Datum clause_value;
clauses = andExpr->args;
AnyNull = false;
/*
* we evaluate each of the clauses in turn, as soon as one is false we
* return that value. If none are false or NULL then we return the
* value of the last clause evaluated, which should be true.
* If any of the clauses is FALSE, the AND result is FALSE regardless
* of the states of the rest of the clauses, so we can stop evaluating
* and return FALSE immediately. If none are FALSE and one or more is
* NULL, we return NULL; otherwise we return TRUE. This makes sense
* when you interpret NULL as "don't know", using the same sort of
* reasoning as for OR, above.
*/
foreach(clause, clauses)
{
/*
* We don't iterate over sets in the quals, so pass in an isDone
* flag, but ignore it.
*/
const_value = ExecEvalExpr((Node *) lfirst(clause),
econtext,
isNull,
&isDone);
clause_value = ExecEvalExpr((Node *) lfirst(clause),
econtext,
isNull,
&isDone);
/*
* if the expression evaluates to null, then we remember it in
* IsNull, if none of the clauses after this evaluates to false we
* will have to set *isNull to true again.
* if we have a non-null false result, then return it.
*/
if (*isNull)
IsNull = *isNull;
/*
* if we have a false result, then we return it, since the
* conjunction must be false.
*/
if (DatumGetInt32(const_value) == 0)
return const_value;
AnyNull = true; /* remember we got a null */
else if (DatumGetInt32(clause_value) == 0)
return clause_value;
}
*isNull = IsNull;
return const_value;
/* AnyNull is true if at least one clause evaluated to NULL */
*isNull = AnyNull;
return (Datum) (! AnyNull);
}
/* ----------------------------------------------------------------
@ -1126,7 +1100,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
{
List *clauses;
List *clause;
Datum const_value = 0;
Datum clause_value;
bool isDone;
clauses = caseExpr->args;
@ -1144,37 +1118,35 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
* We don't iterate over sets in the quals, so pass in an isDone
* flag, but ignore it.
*/
const_value = ExecEvalExpr((Node *) wclause->expr,
econtext,
isNull,
&isDone);
clause_value = ExecEvalExpr(wclause->expr,
econtext,
isNull,
&isDone);
/*
* if we have a true test, then we return the result, since the
* case statement is satisfied. A NULL result from the test is
* not considered true.
*/
if (DatumGetInt32(const_value) != 0 && ! *isNull)
if (DatumGetInt32(clause_value) != 0 && ! *isNull)
{
const_value = ExecEvalExpr((Node *) wclause->result,
econtext,
isNull,
&isDone);
return (Datum) const_value;
return ExecEvalExpr(wclause->result,
econtext,
isNull,
&isDone);
}
}
if (caseExpr->defresult)
{
const_value = ExecEvalExpr((Node *) caseExpr->defresult,
econtext,
isNull,
&isDone);
return ExecEvalExpr(caseExpr->defresult,
econtext,
isNull,
&isDone);
}
else
*isNull = true;
return const_value;
*isNull = true;
return (Datum) 0;
}
/* ----------------------------------------------------------------
@ -1357,7 +1329,6 @@ bool
ExecQual(List *qual, ExprContext *econtext)
{
List *clause;
bool result;
/*
* debugging stuff
@ -1378,27 +1349,17 @@ ExecQual(List *qual, ExprContext *econtext)
* a "qual" is a list of clauses. To evaluate the qual, we evaluate
* each of the clauses in the list.
*
* ExecQualClause returns true when we know the qualification *failed* so
* we just pass each clause in qual to it until we know the qual
* ExecQualClause returns true when we know the qualification *failed*
* so we just pass each clause in qual to it until we know the qual
* failed or there are no more clauses.
*/
result = false;
foreach(clause, qual)
{
result = ExecQualClause((Node *) lfirst(clause), econtext);
if (result == true)
break;
if (ExecQualClause((Node *) lfirst(clause), econtext))
return false; /* qual failed, so return false */
}
/*
* if result is true, then it means a clause failed so we return
* false. if result is false then it means no clause failed so we
* return true.
*/
if (result == true)
return false;
return true;
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.48 1999/08/21 03:48:57 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.49 1999/09/26 02:28:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -223,6 +223,22 @@ _equalAggref(Aggref *a, Aggref *b)
return true;
}
static bool
_equalSubLink(SubLink *a, SubLink *b)
{
if (a->subLinkType != b->subLinkType)
return false;
if (a->useor != b->useor)
return false;
if (!equal(a->lefthand, b->lefthand))
return false;
if (!equal(a->oper, b->oper))
return false;
if (!equal(a->subselect, b->subselect))
return false;
return true;
}
static bool
_equalArray(Array *a, Array *b)
{
@ -393,7 +409,7 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
if (a->plan_id != b->plan_id)
return false;
if (!equal(a->sublink->oper, b->sublink->oper))
if (!equal(a->sublink, b->sublink))
return false;
return true;
@ -713,6 +729,9 @@ equal(void *a, void *b)
case T_Aggref:
retval = _equalAggref(a, b);
break;
case T_SubLink:
retval = _equalSubLink(a, b);
break;
case T_Func:
retval = _equalFunc(a, b);
break;

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.44 1999/09/13 00:17:25 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.45 1999/09/26 02:28:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -66,24 +66,41 @@ query_planner(Query *root,
List *level_tlist;
Plan *subplan;
if (PlannerQueryLevel > 1)
{
/* should copy be made ? */
tlist = (List *) SS_replace_correlation_vars((Node *) tlist);
qual = (List *) SS_replace_correlation_vars((Node *) qual);
}
if (root->hasSubLinks)
qual = (List *) SS_process_sublinks((Node *) qual);
/*
* Simplify constant expressions in both targetlist and qual.
*
* Note that at this point the qual has not yet been converted to
* implicit-AND form, so we can apply eval_const_expressions directly.
* Also note that we need to do this before SS_process_sublinks,
* because that routine inserts bogus "Const" nodes.
*/
tlist = (List *) eval_const_expressions((Node *) tlist);
qual = (List *) eval_const_expressions((Node *) qual);
/*
* Canonicalize the qual, and convert it to implicit-AND format.
*/
qual = canonicalize_qual((Expr *) qual, true);
#ifdef OPTIMIZER_DEBUG
printf("After canonicalize_qual()\n");
pprint(qual);
#endif
/* Replace uplevel vars with Param nodes */
if (PlannerQueryLevel > 1)
{
tlist = (List *) SS_replace_correlation_vars((Node *) tlist);
qual = (List *) SS_replace_correlation_vars((Node *) qual);
}
/* Expand SubLinks to SubPlans */
if (root->hasSubLinks)
qual = (List *) SS_process_sublinks((Node *) qual);
/*
* Pull out any non-variable qualifications so these can be put in the
* topmost result node.
* topmost result node. (Any *really* non-variable quals will probably
* have been optimized away by eval_const_expressions(). What we're
* looking for here is quals that depend only on outer-level vars...)
*/
qual = pull_constant_clauses(qual, &constant_qual);

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.68 1999/09/18 19:07:00 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.69 1999/09/26 02:28:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -301,7 +301,28 @@ union_planner(Query *parse)
*/
if (parse->havingQual)
{
List *ql;
/*--------------------
* Require the havingQual to contain at least one aggregate function
* (else it could have been done as a WHERE constraint). This check
* used to be much stricter, requiring an aggregate in each clause of
* the CNF-ified qual. However, that's probably overly anal-retentive.
* We now do it first so that we will not complain if there is an
* aggregate but it gets optimized away by eval_const_expressions().
* The agg itself is never const, of course, but consider
* SELECT ... HAVING xyz OR (COUNT(*) > 1)
* where xyz reduces to constant true in a particular query.
* We probably should not refuse this query.
*--------------------
*/
if (pull_agg_clause(parse->havingQual) == NIL)
elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
/* Simplify constant expressions in havingQual */
parse->havingQual = eval_const_expressions(parse->havingQual);
/* Convert the havingQual to implicit-AND normal form */
parse->havingQual = (Node *)
canonicalize_qual((Expr *) parse->havingQual, true);
/* Replace uplevel Vars with Params */
if (PlannerQueryLevel > 1)
@ -323,20 +344,6 @@ union_planner(Query *parse)
parse->targetList))
elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT");
}
/* convert the havingQual to implicit-AND normal form */
parse->havingQual = (Node *)
canonicalize_qual((Expr *) parse->havingQual, true);
/*
* Require an aggregate function to appear in each clause of the
* havingQual (else it could have been done as a WHERE constraint).
*/
foreach(ql, (List *) parse->havingQual)
{
if (pull_agg_clause(lfirst(ql)) == NIL)
elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
}
}
/*

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.51 1999/09/09 02:35:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.52 1999/09/26 02:28:33 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -19,6 +19,9 @@
#include "postgres.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
@ -26,7 +29,9 @@
#include "optimizer/internal.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_type.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
typedef struct {
@ -38,6 +43,7 @@ static bool pull_agg_clause_walker(Node *node, List **listptr);
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
check_subplans_for_ungrouped_vars_context *context);
static int is_single_func(Node *node);
static Node *eval_const_expressions_mutator (Node *node, void *context);
Expr *
@ -211,7 +217,7 @@ make_orclause(List *orclauses)
{
Expr *expr = makeNode(Expr);
expr->typeOid = InvalidOid; /* assume type checking done */
expr->typeOid = BOOLOID;
expr->opType = OR_EXPR;
expr->oper = NULL;
expr->args = orclauses;
@ -247,7 +253,7 @@ make_notclause(Expr *notclause)
{
Expr *expr = makeNode(Expr);
expr->typeOid = InvalidOid; /* assume type checking done */
expr->typeOid = BOOLOID;
expr->opType = NOT_EXPR;
expr->oper = NULL;
expr->args = lcons(notclause, NIL);
@ -296,7 +302,7 @@ make_andclause(List *andclauses)
{
Expr *expr = makeNode(Expr);
expr->typeOid = InvalidOid; /* assume type checking done */
expr->typeOid = BOOLOID;
expr->opType = AND_EXPR;
expr->oper = NULL;
expr->args = andclauses;
@ -762,6 +768,413 @@ CommuteClause(Expr *clause)
}
/*--------------------
* eval_const_expressions
*
* Reduce any recognizably constant subexpressions of the given
* expression tree, for example "2 + 2" => "4". More interestingly,
* we can reduce certain boolean expressions even when they contain
* non-constant subexpressions: "x OR true" => "true" no matter what
* the subexpression x is. (XXX We assume that no such subexpression
* will have important side-effects, which is not necessarily a good
* assumption in the presence of user-defined functions; do we need a
* pg_proc flag that prevents discarding the execution of a function?)
*
* We do understand that certain functions may deliver non-constant
* results even with constant inputs, "nextval()" being the classic
* example. Functions that are not marked "proiscachable" in pg_proc
* will not be pre-evaluated here, although we will reduce their
* arguments as far as possible. Functions that are the arguments
* of Iter nodes are also not evaluated.
*
* We assume that the tree has already been type-checked and contains
* only operators and functions that are reasonable to try to execute.
*
* This routine should be invoked before converting sublinks to subplans
* (subselect.c's SS_process_sublinks()). The converted form contains
* bogus "Const" nodes that are actually placeholders where the executor
* will insert values from the inner plan, and obviously we mustn't try
* to reduce the expression as though these were really constants.
* As a safeguard, if we happen to find an already-converted SubPlan node,
* we will return it unchanged rather than recursing into it.
*--------------------
*/
Node *
eval_const_expressions(Node *node)
{
/* no context or special setup needed, so away we go... */
return eval_const_expressions_mutator(node, NULL);
}
/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
#define MAKEBOOLCONST(val,isnull) \
((Node *) makeConst(BOOLOID, 1, (Datum) (val), \
(isnull), true, false, false))
static Node *
eval_const_expressions_mutator (Node *node, void *context)
{
if (node == NULL)
return NULL;
if (IsA(node, Expr))
{
Expr *expr = (Expr *) node;
List *args;
Const *const_input;
Expr *newexpr;
/*
* Reduce constants in the Expr'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->opType)
{
case OP_EXPR:
case FUNC_EXPR:
{
/*
* For an operator or function, we cannot simplify
* unless all the inputs are constants. (XXX possible
* future improvement: if the op/func is strict and
* at least one input is NULL, we could simplify to NULL.
* But we do not currently have any way to know if the
* op/func is strict or not. For now, a NULL input is
* treated the same as any other constant node.)
*/
bool args_all_const = true;
List *arg;
Oid funcid;
Oid result_typeid;
HeapTuple func_tuple;
Form_pg_proc funcform;
Type resultType;
Datum const_val;
bool const_is_null;
bool isDone;
foreach(arg, args)
{
if (! IsA(lfirst(arg), Const))
{
args_all_const = false;
break;
}
}
if (! args_all_const)
break;
/*
* Get the function procedure's OID and look to see
* whether it is marked proiscachable.
*/
if (expr->opType == OP_EXPR)
{
Oper *oper = (Oper *) expr->oper;
replace_opid(oper);
funcid = oper->opid;
result_typeid = oper->opresulttype;
}
else
{
Func *func = (Func *) expr->oper;
funcid = func->funcid;
result_typeid = func->functype;
}
/* Someday lsyscache.c might provide a function for this */
func_tuple = SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "Function OID %u does not exist", funcid);
funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
if (! funcform->proiscachable)
break;
/*
* Also check to make sure it doesn't return a set.
*
* XXX would it be better to take the result type from the
* pg_proc tuple, rather than the Oper or Func node?
*/
if (funcform->proretset)
break;
/*
* OK, looks like we can simplify this operator/function.
* We use the executor's routine ExecEvalExpr() to avoid
* duplication of code and ensure we get the same result
* as the executor would get.
*
* The only setup needed here is the replace_opid()
* that we already did for the OP_EXPR case.
*
* It is OK to pass econtext = NULL because none of the
* ExecEvalExpr() code used in this situation will use
* econtext. That might seem fortuitous, but it's not
* so unreasonable --- a constant expression does not
* depend on context, by definition, n'est ce pas?
*/
const_val = ExecEvalExpr((Node *) expr, NULL,
&const_is_null, &isDone);
Assert(isDone); /* if this isn't set, we blew it... */
/*
* Make the constant result node.
*/
resultType = typeidType(result_typeid);
return (Node *) makeConst(result_typeid, typeLen(resultType),
const_val, const_is_null,
typeByVal(resultType),
false, false);
}
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.
*/
List *newargs = NIL;
List *arg;
bool haveNull = false;
bool forceTrue = false;
foreach(arg, args)
{
if (! IsA(lfirst(arg), Const))
{
newargs = lappend(newargs, lfirst(arg));
continue;
}
const_input = (Const *) lfirst(arg);
if (const_input->constisnull)
haveNull = true;
else if (DatumGetInt32(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)
return MAKEBOOLCONST(true, false);
if (haveNull)
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
/* If all the inputs are FALSE, result is FALSE */
if (newargs == NIL)
return MAKEBOOLCONST(false, false);
/* If only one nonconst-or-NULL input, it's the result */
if (lnext(newargs) == NIL)
return (Node *) lfirst(newargs);
/* Else we still need an OR node */
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.
*/
List *newargs = NIL;
List *arg;
bool haveNull = false;
bool forceFalse = false;
foreach(arg, args)
{
if (! IsA(lfirst(arg), Const))
{
newargs = lappend(newargs, lfirst(arg));
continue;
}
const_input = (Const *) lfirst(arg);
if (const_input->constisnull)
haveNull = true;
else if (! DatumGetInt32(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)
return MAKEBOOLCONST(false, false);
if (haveNull)
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
/* If all the inputs are TRUE, result is TRUE */
if (newargs == NIL)
return MAKEBOOLCONST(true, false);
/* If only one nonconst-or-NULL input, it's the result */
if (lnext(newargs) == NIL)
return (Node *) lfirst(newargs);
/* Else we still need an AND node */
return (Node *) make_andclause(newargs);
}
case NOT_EXPR:
Assert(length(args) == 1);
if (! IsA(lfirst(args), Const))
break;
const_input = (Const *) lfirst(args);
/* NOT NULL => NULL */
if (const_input->constisnull)
return MAKEBOOLCONST(false, true);
/* otherwise pretty easy */
return MAKEBOOLCONST(! DatumGetInt32(const_input->constvalue),
false);
case SUBPLAN_EXPR:
/*
* Safety measure per notes at head of this routine:
* return a SubPlan unchanged. Too late to do anything
* with it. The arglist simplification above was wasted
* work (the list probably only contains Var nodes anyway).
*/
return (Node *) expr;
default:
elog(ERROR, "eval_const_expressions: unexpected opType %d",
(int) expr->opType);
break;
}
/*
* If we break out of the above switch on opType, then the
* expression cannot be simplified any further, so build
* and return a replacement Expr node using the
* possibly-simplified arguments and the original oper node.
* Can't use make_clause() here because we want to be sure
* the typeOid field is preserved...
*/
newexpr = makeNode(Expr);
newexpr->typeOid = expr->typeOid;
newexpr->opType = expr->opType;
newexpr->oper = expr->oper;
newexpr->args = args;
return (Node *) newexpr;
}
if (IsA(node, CaseExpr))
{
/*
* CASE expressions can be simplified if there are constant condition
* clauses:
* FALSE (or NULL): drop the alternative
* TRUE: drop all remaining alternatives
* If the first non-FALSE alternative is a constant TRUE, we can
* simplify the entire CASE to that alternative's expression.
* If there are no non-FALSE alternatives, we simplify the entire
* CASE to the default result (ELSE result).
*/
CaseExpr *caseexpr = (CaseExpr *) node;
CaseExpr *newcase;
List *newargs = NIL;
Node *defresult;
Const *const_input;
List *arg;
foreach(arg, caseexpr->args)
{
/* Simplify this alternative's condition and result */
CaseWhen *casewhen = (CaseWhen *)
expression_tree_mutator((Node *) lfirst(arg),
eval_const_expressions_mutator,
(void *) context);
Assert(IsA(casewhen, CaseWhen));
if (casewhen->expr == NULL ||
! IsA(casewhen->expr, Const))
{
newargs = lappend(newargs, casewhen);
continue;
}
const_input = (Const *) casewhen->expr;
if (const_input->constisnull ||
! DatumGetInt32(const_input->constvalue))
continue; /* drop alternative with FALSE condition */
/*
* Found a TRUE condition. If it's the first (un-dropped)
* alternative, the CASE reduces to just this alternative.
*/
if (newargs == NIL)
return casewhen->result;
/*
* Otherwise, add it to the list, and drop all the rest.
*/
newargs = lappend(newargs, casewhen);
break;
}
/* Simplify the default result */
defresult = eval_const_expressions_mutator(caseexpr->defresult,
context);
/* If no non-FALSE alternatives, CASE reduces to the default result */
if (newargs == NIL)
return defresult;
/* Otherwise we need a new CASE node */
newcase = makeNode(CaseExpr);
newcase->casetype = caseexpr->casetype;
newcase->arg = NULL;
newcase->args = newargs;
newcase->defresult = defresult;
return (Node *) newcase;
}
if (IsA(node, Iter))
{
/*
* The argument of an Iter is normally a function call.
* We must not try to eliminate the function, but we
* can try to simplify its arguments. If, by chance,
* the arg is NOT a function then we go ahead and try to
* simplify it (by falling into expression_tree_mutator).
* Is that the right thing?
*/
Iter *iter = (Iter *) node;
if (is_funcclause(iter->iterexpr))
{
Expr *func = (Expr *) iter->iterexpr;
Expr *newfunc;
Iter *newiter;
newfunc = makeNode(Expr);
newfunc->typeOid = func->typeOid;
newfunc->opType = func->opType;
newfunc->oper = func->oper;
newfunc->args = (List *)
expression_tree_mutator((Node *) func->args,
eval_const_expressions_mutator,
(void *) context);
newiter = makeNode(Iter);
newiter->iterexpr = (Node *) newfunc;
newiter->itertype = iter->itertype;
return (Node *) newiter;
}
}
/*
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged
* but try to simplify its arguments (if any) using this routine.
* For example: we cannot eliminate an ArrayRef node, but we
* might be able to simplify constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
(void *) context);
}
/*
* Standard expression-tree walking support
*

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: clauses.h,v 1.29 1999/08/22 20:14:56 tgl Exp $
* $Id: clauses.h,v 1.30 1999/09/26 02:28:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -52,6 +52,8 @@ extern void get_rels_atts(Node *clause, int *relid1,
AttrNumber *attno1, int *relid2, AttrNumber *attno2);
extern void CommuteClause(Expr *clause);
extern Node *eval_const_expressions(Node *node);
extern bool expression_tree_walker(Node *node, bool (*walker) (),
void *context);
extern Node *expression_tree_mutator(Node *node, Node * (*mutator) (),