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.
REL7_0_PATCHES
Tom Lane 27 years ago
parent 95d3d468ce
commit 40f6524161
  1. 173
      src/backend/executor/execQual.c
  2. 23
      src/backend/nodes/equalfuncs.c
  3. 35
      src/backend/optimizer/plan/planmain.c
  4. 39
      src/backend/optimizer/plan/planner.c
  5. 421
      src/backend/optimizer/util/clauses.c
  6. 1600
      src/include/catalog/pg_proc.h
  7. 4
      src/include/optimizer/clauses.h

@ -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;
}

@ -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;

@ -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;
/*
* 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)
{
/* should copy be made ? */
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);
qual = canonicalize_qual((Expr *) qual, true);
#ifdef OPTIMIZER_DEBUG
printf("After canonicalize_qual()\n");
pprint(qual);
#endif
/*
* 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);

@ -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");
}
}
/*

@ -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

@ -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) (),

Loading…
Cancel
Save