mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Ye-old pgindent run. Same 4-space tabs.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.64 2000/04/04 01:21:46 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.65 2000/04/12 17:15:24 momjian Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -46,9 +46,9 @@ static bool pull_agg_clause_walker(Node *node, List **listptr);
|
||||
static bool contain_subplans_walker(Node *node, void *context);
|
||||
static bool pull_subplans_walker(Node *node, List **listptr);
|
||||
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
Query *context);
|
||||
static int is_single_func(Node *node);
|
||||
static Node *eval_const_expressions_mutator (Node *node, void *context);
|
||||
Query *context);
|
||||
static int is_single_func(Node *node);
|
||||
static Node *eval_const_expressions_mutator(Node *node, void *context);
|
||||
static Expr *simplify_op_or_func(Expr *expr, List *args);
|
||||
|
||||
|
||||
@@ -340,18 +340,19 @@ make_ands_explicit(List *andclauses)
|
||||
List *
|
||||
make_ands_implicit(Expr *clause)
|
||||
{
|
||||
|
||||
/*
|
||||
* NB: because the parser sets the qual field to NULL in a query that
|
||||
* has no WHERE clause, we must consider a NULL input clause as TRUE,
|
||||
* even though one might more reasonably think it FALSE. Grumble.
|
||||
* If this causes trouble, consider changing the parser's behavior.
|
||||
* even though one might more reasonably think it FALSE. Grumble. If
|
||||
* this causes trouble, consider changing the parser's behavior.
|
||||
*/
|
||||
if (clause == NULL)
|
||||
return NIL; /* NULL -> NIL list == TRUE */
|
||||
else if (and_clause((Node *) clause))
|
||||
return clause->args;
|
||||
else if (IsA(clause, Const) &&
|
||||
! ((Const *) clause)->constisnull &&
|
||||
!((Const *) clause)->constisnull &&
|
||||
DatumGetInt32(((Const *) clause)->constvalue))
|
||||
return NIL; /* constant TRUE input -> NIL list */
|
||||
else
|
||||
@@ -381,7 +382,8 @@ contain_agg_clause_walker(Node *node, void *context)
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Aggref))
|
||||
return true; /* abort the tree traversal and return true */
|
||||
return true; /* abort the tree traversal and return
|
||||
* true */
|
||||
return expression_tree_walker(node, contain_agg_clause_walker, context);
|
||||
}
|
||||
|
||||
@@ -411,12 +413,14 @@ pull_agg_clause_walker(Node *node, List **listptr)
|
||||
if (IsA(node, Aggref))
|
||||
{
|
||||
*listptr = lappend(*listptr, node);
|
||||
|
||||
/*
|
||||
* Complain if the aggregate's argument contains any aggregates;
|
||||
* nested agg functions are semantically nonsensical.
|
||||
*/
|
||||
if (contain_agg_clause(((Aggref *) node)->target))
|
||||
elog(ERROR, "Aggregate function calls may not be nested");
|
||||
|
||||
/*
|
||||
* Having checked that, we need not recurse into the argument.
|
||||
*/
|
||||
@@ -454,7 +458,8 @@ contain_subplans_walker(Node *node, void *context)
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (is_subplan(node) || IsA(node, SubLink))
|
||||
return true; /* abort the tree traversal and return true */
|
||||
return true; /* abort the tree traversal and return
|
||||
* true */
|
||||
return expression_tree_walker(node, contain_subplans_walker, context);
|
||||
}
|
||||
|
||||
@@ -462,7 +467,7 @@ contain_subplans_walker(Node *node, void *context)
|
||||
* pull_subplans
|
||||
* Recursively pulls all subplans from an expression tree.
|
||||
*
|
||||
* Returns list of subplan nodes found. Note the nodes themselves are not
|
||||
* Returns list of subplan nodes found. Note the nodes themselves are not
|
||||
* copied, only referenced.
|
||||
*/
|
||||
List *
|
||||
@@ -507,7 +512,11 @@ void
|
||||
check_subplans_for_ungrouped_vars(Node *clause,
|
||||
Query *query)
|
||||
{
|
||||
/* No special setup needed; context for walker is just the Query pointer */
|
||||
|
||||
/*
|
||||
* No special setup needed; context for walker is just the Query
|
||||
* pointer
|
||||
*/
|
||||
check_subplans_for_ungrouped_vars_walker(clause, query);
|
||||
}
|
||||
|
||||
@@ -517,17 +526,19 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* We can ignore Vars other than in subplan args lists,
|
||||
* since the parser already checked 'em.
|
||||
* We can ignore Vars other than in subplan args lists, since the
|
||||
* parser already checked 'em.
|
||||
*/
|
||||
if (is_subplan(node))
|
||||
{
|
||||
|
||||
/*
|
||||
* The args list of the subplan node represents attributes from
|
||||
* outside passed into the sublink.
|
||||
*/
|
||||
List *t;
|
||||
List *t;
|
||||
|
||||
foreach(t, ((Expr *) node)->args)
|
||||
{
|
||||
@@ -539,10 +550,10 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
/*
|
||||
* We do not care about args that are not local variables;
|
||||
* params or outer-level vars are not our responsibility to
|
||||
* check. (The outer-level query passing them to us needs
|
||||
* to worry, instead.)
|
||||
* check. (The outer-level query passing them to us needs to
|
||||
* worry, instead.)
|
||||
*/
|
||||
if (! IsA(thisarg, Var))
|
||||
if (!IsA(thisarg, Var))
|
||||
continue;
|
||||
var = (Var *) thisarg;
|
||||
if (var->varlevelsup > 0)
|
||||
@@ -554,8 +565,8 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
contained_in_group_clause = false;
|
||||
foreach(gl, context->groupClause)
|
||||
{
|
||||
GroupClause *gcl = lfirst(gl);
|
||||
Node *groupexpr;
|
||||
GroupClause *gcl = lfirst(gl);
|
||||
Node *groupexpr;
|
||||
|
||||
groupexpr = get_sortgroupclause_expr(gcl,
|
||||
context->targetList);
|
||||
@@ -569,14 +580,14 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
if (!contained_in_group_clause)
|
||||
{
|
||||
/* Found an ungrouped argument. Complain. */
|
||||
RangeTblEntry *rte;
|
||||
char *attname;
|
||||
RangeTblEntry *rte;
|
||||
char *attname;
|
||||
|
||||
Assert(var->varno > 0 &&
|
||||
var->varno <= length(context->rtable));
|
||||
rte = rt_fetch(var->varno, context->rtable);
|
||||
attname = get_attname(rte->relid, var->varattno);
|
||||
if (! attname)
|
||||
if (!attname)
|
||||
elog(ERROR, "cache lookup of attribute %d in relation %u failed",
|
||||
var->varattno, rte->relid);
|
||||
elog(ERROR, "Sub-SELECT uses un-GROUPed attribute %s.%s from outer query",
|
||||
@@ -585,7 +596,7 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
}
|
||||
}
|
||||
return expression_tree_walker(node,
|
||||
check_subplans_for_ungrouped_vars_walker,
|
||||
check_subplans_for_ungrouped_vars_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
@@ -697,7 +708,7 @@ NumRelids(Node *clause)
|
||||
* is referenced in the clause). The routine checks that the
|
||||
* expression is of the form (var op something) or (something op var)
|
||||
* where the var is an attribute of the specified relation, or
|
||||
* a function of a var of the specified relation. If so, it
|
||||
* a function of a var of the specified relation. If so, it
|
||||
* returns the following info:
|
||||
* the found relation number (same as targetrelid unless that is 0)
|
||||
* the found var number (or InvalidAttrNumber if a function)
|
||||
@@ -707,7 +718,7 @@ NumRelids(Node *clause)
|
||||
* specifically 0 for the relid and attno, 0 for the constant value.
|
||||
*
|
||||
* Note that negative attno values are *not* invalid, but represent
|
||||
* system attributes such as OID. It's sufficient to check for relid=0
|
||||
* system attributes such as OID. It's sufficient to check for relid=0
|
||||
* to determine whether the routine succeeded.
|
||||
*/
|
||||
void
|
||||
@@ -785,15 +796,13 @@ default_results:
|
||||
*flag |= SEL_CONSTANT;
|
||||
}
|
||||
else
|
||||
{
|
||||
*constval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* is_single_func
|
||||
* If the given expression is a function of a single relation,
|
||||
* return the relation number; else return 0
|
||||
* If the given expression is a function of a single relation,
|
||||
* return the relation number; else return 0
|
||||
*/
|
||||
static int
|
||||
is_single_func(Node *node)
|
||||
@@ -804,7 +813,7 @@ is_single_func(Node *node)
|
||||
|
||||
if (length(varnos) == 1)
|
||||
{
|
||||
int funcvarno = lfirsti(varnos);
|
||||
int funcvarno = lfirsti(varnos);
|
||||
|
||||
freeList(varnos);
|
||||
return funcvarno;
|
||||
@@ -922,7 +931,7 @@ CommuteClause(Expr *clause)
|
||||
* 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
|
||||
* 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?)
|
||||
@@ -954,7 +963,7 @@ eval_const_expressions(Node *node)
|
||||
}
|
||||
|
||||
static Node *
|
||||
eval_const_expressions_mutator (Node *node, void *context)
|
||||
eval_const_expressions_mutator(Node *node, void *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
@@ -963,21 +972,22 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
Expr *expr = (Expr *) node;
|
||||
List *args;
|
||||
Const *const_input;
|
||||
Expr *newexpr;
|
||||
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.
|
||||
* 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,
|
||||
eval_const_expressions_mutator,
|
||||
(void *) context);
|
||||
|
||||
switch (expr->opType)
|
||||
{
|
||||
case OP_EXPR:
|
||||
case FUNC_EXPR:
|
||||
|
||||
/*
|
||||
* Code for op/func case is pretty bulky, so split it out
|
||||
* as a separate function.
|
||||
@@ -985,123 +995,131 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
newexpr = simplify_op_or_func(expr, args);
|
||||
if (newexpr) /* successfully simplified it */
|
||||
return (Node *) newexpr;
|
||||
/* else fall out to build new Expr node with simplified args */
|
||||
|
||||
/*
|
||||
* else fall out to build new Expr node with simplified
|
||||
* args
|
||||
*/
|
||||
break;
|
||||
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))
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
newargs = lappend(newargs, lfirst(arg));
|
||||
continue;
|
||||
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 */
|
||||
}
|
||||
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);
|
||||
}
|
||||
/*
|
||||
* 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))
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
newargs = lappend(newargs, lfirst(arg));
|
||||
continue;
|
||||
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 */
|
||||
}
|
||||
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);
|
||||
}
|
||||
/*
|
||||
* 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))
|
||||
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),
|
||||
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
|
||||
* 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).
|
||||
* work (the list probably only contains Var nodes
|
||||
* anyway).
|
||||
*/
|
||||
return (Node *) expr;
|
||||
default:
|
||||
@@ -1112,25 +1130,26 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
|
||||
/*
|
||||
* 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...
|
||||
* 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;
|
||||
newexpr->typeOid = expr->typeOid;
|
||||
newexpr->opType = expr->opType;
|
||||
newexpr->oper = expr->oper;
|
||||
newexpr->args = args;
|
||||
return (Node *) newexpr;
|
||||
}
|
||||
if (IsA(node, RelabelType))
|
||||
{
|
||||
|
||||
/*
|
||||
* If we can simplify the input to a constant, then we don't need
|
||||
* the RelabelType node anymore: just change the type field of
|
||||
* the Const node. Otherwise, copy the RelabelType node.
|
||||
* the RelabelType node anymore: just change the type field of the
|
||||
* Const node. Otherwise, copy the RelabelType node.
|
||||
*/
|
||||
RelabelType *relabel = (RelabelType *) node;
|
||||
Node *arg;
|
||||
@@ -1138,13 +1157,15 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
arg = eval_const_expressions_mutator(relabel->arg, context);
|
||||
if (arg && IsA(arg, Const))
|
||||
{
|
||||
Const *con = (Const *) arg;
|
||||
Const *con = (Const *) arg;
|
||||
|
||||
con->consttype = relabel->resulttype;
|
||||
|
||||
/*
|
||||
* relabel's resulttypmod is discarded, which is OK for now;
|
||||
* if the type actually needs a runtime length coercion then
|
||||
* there should be a function call to do it just above this node.
|
||||
* there should be a function call to do it just above this
|
||||
* node.
|
||||
*/
|
||||
return (Node *) con;
|
||||
}
|
||||
@@ -1160,15 +1181,15 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
}
|
||||
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).
|
||||
* 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;
|
||||
@@ -1181,26 +1202,29 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
{
|
||||
/* Simplify this alternative's condition and result */
|
||||
CaseWhen *casewhen = (CaseWhen *)
|
||||
expression_tree_mutator((Node *) lfirst(arg),
|
||||
eval_const_expressions_mutator,
|
||||
(void *) context);
|
||||
expression_tree_mutator((Node *) lfirst(arg),
|
||||
eval_const_expressions_mutator,
|
||||
(void *) context);
|
||||
|
||||
Assert(IsA(casewhen, CaseWhen));
|
||||
if (casewhen->expr == NULL ||
|
||||
! IsA(casewhen->expr, Const))
|
||||
!IsA(casewhen->expr, Const))
|
||||
{
|
||||
newargs = lappend(newargs, casewhen);
|
||||
continue;
|
||||
}
|
||||
const_input = (Const *) casewhen->expr;
|
||||
if (const_input->constisnull ||
|
||||
! DatumGetInt32(const_input->constvalue))
|
||||
!DatumGetInt32(const_input->constvalue))
|
||||
continue; /* drop alternative with FALSE condition */
|
||||
|
||||
/*
|
||||
* Found a TRUE condition. If it's the first (un-dropped)
|
||||
* 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.
|
||||
*/
|
||||
@@ -1211,7 +1235,11 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
/* Simplify the default result */
|
||||
defresult = eval_const_expressions_mutator(caseexpr->defresult,
|
||||
context);
|
||||
/* If no non-FALSE alternatives, CASE reduces to the default result */
|
||||
|
||||
/*
|
||||
* If no non-FALSE alternatives, CASE reduces to the default
|
||||
* result
|
||||
*/
|
||||
if (newargs == NIL)
|
||||
return defresult;
|
||||
/* Otherwise we need a new CASE node */
|
||||
@@ -1224,21 +1252,21 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
}
|
||||
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?
|
||||
* 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;
|
||||
Expr *func = (Expr *) iter->iterexpr;
|
||||
Expr *newfunc;
|
||||
Iter *newiter;
|
||||
|
||||
newfunc = makeNode(Expr);
|
||||
newfunc->typeOid = func->typeOid;
|
||||
@@ -1254,12 +1282,13 @@ eval_const_expressions_mutator (Node *node, void *context)
|
||||
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.
|
||||
* 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);
|
||||
@@ -1289,31 +1318,32 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
HeapTuple func_tuple;
|
||||
Form_pg_proc funcform;
|
||||
Type resultType;
|
||||
Expr *newexpr;
|
||||
Expr *newexpr;
|
||||
Datum const_val;
|
||||
bool const_is_null;
|
||||
bool isDone;
|
||||
|
||||
/*
|
||||
* 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.)
|
||||
* 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.)
|
||||
*/
|
||||
foreach(arg, args)
|
||||
{
|
||||
if (! IsA(lfirst(arg), Const))
|
||||
if (!IsA(lfirst(arg), Const))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the function procedure's OID and look to see
|
||||
* whether it is marked proiscachable.
|
||||
* 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;
|
||||
Oper *oper = (Oper *) expr->oper;
|
||||
|
||||
replace_opid(oper); /* OK to scribble on input to this extent */
|
||||
funcid = oper->opid;
|
||||
@@ -1321,7 +1351,7 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
}
|
||||
else
|
||||
{
|
||||
Func *func = (Func *) expr->oper;
|
||||
Func *func = (Func *) expr->oper;
|
||||
|
||||
funcid = func->funcid;
|
||||
result_typeid = func->functype;
|
||||
@@ -1333,21 +1363,23 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
if (!HeapTupleIsValid(func_tuple))
|
||||
elog(ERROR, "Function OID %u does not exist", funcid);
|
||||
funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
||||
if (! funcform->proiscachable)
|
||||
if (!funcform->proiscachable)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Also check to make sure it doesn't return a set.
|
||||
*/
|
||||
if (funcform->proretset)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Build a new Expr node containing the already-simplified arguments.
|
||||
* The only other setup needed here is the replace_opid() that we already
|
||||
* Build a new Expr node containing the already-simplified arguments. The
|
||||
* only other setup needed here is the replace_opid() that we already
|
||||
* did for the OP_EXPR case.
|
||||
*/
|
||||
newexpr = makeNode(Expr);
|
||||
@@ -1355,21 +1387,23 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
newexpr->opType = expr->opType;
|
||||
newexpr->oper = expr->oper;
|
||||
newexpr->args = args;
|
||||
|
||||
/*
|
||||
* 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?
|
||||
* 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 *) newexpr, NULL,
|
||||
&const_is_null, &isDone);
|
||||
Assert(isDone); /* if this isn't set, we blew it... */
|
||||
pfree(newexpr);
|
||||
|
||||
/*
|
||||
* Make the constant result node.
|
||||
*
|
||||
* XXX would it be better to take the result type from the
|
||||
* pg_proc tuple, rather than the Oper or Func node?
|
||||
* XXX would it be better to take the result type from the pg_proc tuple,
|
||||
* rather than the Oper or Func node?
|
||||
*/
|
||||
resultType = typeidType(result_typeid);
|
||||
return (Expr *) makeConst(result_typeid, typeLen(resultType),
|
||||
@@ -1426,8 +1460,8 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
*
|
||||
* The walker routine should return "false" to continue the tree walk, or
|
||||
* "true" to abort the walk and immediately return "true" to the top-level
|
||||
* caller. This can be used to short-circuit the traversal if the walker
|
||||
* has found what it came for. "false" is returned to the top-level caller
|
||||
* caller. This can be used to short-circuit the traversal if the walker
|
||||
* has found what it came for. "false" is returned to the top-level caller
|
||||
* iff no invocation of the walker returned "true".
|
||||
*
|
||||
* The node types handled by expression_tree_walker include all those
|
||||
@@ -1454,16 +1488,16 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
*/
|
||||
|
||||
bool
|
||||
expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
||||
expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
||||
{
|
||||
List *temp;
|
||||
|
||||
/*
|
||||
* The walker has already visited the current node,
|
||||
* and so we need only recurse into any sub-nodes it has.
|
||||
* The walker has already visited the current node, and so we need
|
||||
* only recurse into any sub-nodes it has.
|
||||
*
|
||||
* We assume that the walker is not interested in List nodes per se,
|
||||
* so when we expect a List we just recurse directly to self without
|
||||
* We assume that the walker is not interested in List nodes per se, so
|
||||
* when we expect a List we just recurse directly to self without
|
||||
* bothering to call the walker.
|
||||
*/
|
||||
if (node == NULL)
|
||||
@@ -1478,7 +1512,7 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
||||
break;
|
||||
case T_Expr:
|
||||
{
|
||||
Expr *expr = (Expr *) node;
|
||||
Expr *expr = (Expr *) node;
|
||||
|
||||
if (expr->opType == SUBPLAN_EXPR)
|
||||
{
|
||||
@@ -1500,6 +1534,7 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
||||
case T_ArrayRef:
|
||||
{
|
||||
ArrayRef *aref = (ArrayRef *) node;
|
||||
|
||||
/* recurse directly for upper/lower array index lists */
|
||||
if (expression_tree_walker((Node *) aref->refupperindexpr,
|
||||
walker, context))
|
||||
@@ -1519,10 +1554,12 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
||||
case T_CaseExpr:
|
||||
{
|
||||
CaseExpr *caseexpr = (CaseExpr *) node;
|
||||
|
||||
/* we assume walker doesn't care about CaseWhens, either */
|
||||
foreach(temp, caseexpr->args)
|
||||
{
|
||||
CaseWhen *when = (CaseWhen *) lfirst(temp);
|
||||
|
||||
Assert(IsA(when, CaseWhen));
|
||||
if (walker(when->expr, context))
|
||||
return true;
|
||||
@@ -1538,12 +1575,14 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
||||
break;
|
||||
case T_SubLink:
|
||||
{
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
|
||||
/* If the SubLink has already been processed by subselect.c,
|
||||
* it will have lefthand=NIL, and we only need to look at
|
||||
* the oper list. Otherwise we only need to look at lefthand
|
||||
* (the Oper nodes in the oper list are deemed uninteresting).
|
||||
/*
|
||||
* If the SubLink has already been processed by
|
||||
* subselect.c, it will have lefthand=NIL, and we only
|
||||
* need to look at the oper list. Otherwise we only need
|
||||
* to look at lefthand (the Oper nodes in the oper list
|
||||
* are deemed uninteresting).
|
||||
*/
|
||||
if (sublink->lefthand)
|
||||
return walker((Node *) sublink->lefthand, context);
|
||||
@@ -1628,18 +1667,19 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
||||
*/
|
||||
|
||||
Node *
|
||||
expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
|
||||
expression_tree_mutator(Node *node, Node *(*mutator) (), void *context)
|
||||
{
|
||||
|
||||
/*
|
||||
* The mutator has already decided not to modify the current node,
|
||||
* but we must call the mutator for any sub-nodes.
|
||||
* The mutator has already decided not to modify the current node, but
|
||||
* we must call the mutator for any sub-nodes.
|
||||
*/
|
||||
|
||||
#define FLATCOPY(newnode, node, nodetype) \
|
||||
( (newnode) = makeNode(nodetype), \
|
||||
memcpy((newnode), (node), sizeof(nodetype)) )
|
||||
|
||||
#define CHECKFLATCOPY(newnode, node, nodetype) \
|
||||
#define CHECKFLATCOPY(newnode, node, nodetype) \
|
||||
( AssertMacro(IsA((node), nodetype)), \
|
||||
(newnode) = makeNode(nodetype), \
|
||||
memcpy((newnode), (node), sizeof(nodetype)) )
|
||||
@@ -1659,31 +1699,41 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
|
||||
return (Node *) copyObject(node);
|
||||
case T_Expr:
|
||||
{
|
||||
Expr *expr = (Expr *) node;
|
||||
Expr *newnode;
|
||||
Expr *expr = (Expr *) node;
|
||||
Expr *newnode;
|
||||
|
||||
FLATCOPY(newnode, expr, Expr);
|
||||
|
||||
if (expr->opType == SUBPLAN_EXPR)
|
||||
{
|
||||
SubLink *oldsublink = ((SubPlan *) expr->oper)->sublink;
|
||||
SubPlan *newsubplan;
|
||||
SubLink *oldsublink = ((SubPlan *) expr->oper)->sublink;
|
||||
SubPlan *newsubplan;
|
||||
|
||||
/* flat-copy the oper node, which is a SubPlan */
|
||||
CHECKFLATCOPY(newsubplan, expr->oper, SubPlan);
|
||||
newnode->oper = (Node *) newsubplan;
|
||||
/* likewise its SubLink node */
|
||||
CHECKFLATCOPY(newsubplan->sublink, oldsublink, SubLink);
|
||||
/* transform args list (params to be passed to subplan) */
|
||||
|
||||
/*
|
||||
* transform args list (params to be passed to
|
||||
* subplan)
|
||||
*/
|
||||
MUTATE(newnode->args, expr->args, List *);
|
||||
/* transform sublink's oper list as well */
|
||||
MUTATE(newsubplan->sublink->oper, oldsublink->oper, List*);
|
||||
/* but not the subplan itself, which is referenced as-is */
|
||||
MUTATE(newsubplan->sublink->oper, oldsublink->oper, List *);
|
||||
|
||||
/*
|
||||
* but not the subplan itself, which is referenced
|
||||
* as-is
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
/* for other Expr node types, just transform args list,
|
||||
* linking to original oper node (OK?)
|
||||
|
||||
/*
|
||||
* for other Expr node types, just transform args
|
||||
* list, linking to original oper node (OK?)
|
||||
*/
|
||||
MUTATE(newnode->args, expr->args, List *);
|
||||
}
|
||||
@@ -1692,8 +1742,8 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
|
||||
break;
|
||||
case T_Aggref:
|
||||
{
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
Aggref *newnode;
|
||||
Aggref *aggref = (Aggref *) node;
|
||||
Aggref *newnode;
|
||||
|
||||
FLATCOPY(newnode, aggref, Aggref);
|
||||
MUTATE(newnode->target, aggref->target, Node *);
|
||||
@@ -1702,8 +1752,8 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
|
||||
break;
|
||||
case T_Iter:
|
||||
{
|
||||
Iter *iter = (Iter *) node;
|
||||
Iter *newnode;
|
||||
Iter *iter = (Iter *) node;
|
||||
Iter *newnode;
|
||||
|
||||
FLATCOPY(newnode, iter, Iter);
|
||||
MUTATE(newnode->iterexpr, iter->iterexpr, Node *);
|
||||
@@ -1763,12 +1813,14 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
|
||||
break;
|
||||
case T_SubLink:
|
||||
{
|
||||
/* A "bare" SubLink (note we will not come here if we found
|
||||
* a SUBPLAN_EXPR node above it). Transform the lefthand side,
|
||||
* but not the oper list nor the subquery.
|
||||
|
||||
/*
|
||||
* A "bare" SubLink (note we will not come here if we
|
||||
* found a SUBPLAN_EXPR node above it). Transform the
|
||||
* lefthand side, but not the oper list nor the subquery.
|
||||
*/
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
SubLink *newnode;
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
SubLink *newnode;
|
||||
|
||||
FLATCOPY(newnode, sublink, SubLink);
|
||||
MUTATE(newnode->lefthand, sublink->lefthand, List *);
|
||||
@@ -1777,9 +1829,12 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
|
||||
break;
|
||||
case T_List:
|
||||
{
|
||||
/* We assume the mutator isn't interested in the list nodes
|
||||
* per se, so just invoke it on each list element.
|
||||
* NOTE: this would fail badly on a list with integer elements!
|
||||
|
||||
/*
|
||||
* We assume the mutator isn't interested in the list
|
||||
* nodes per se, so just invoke it on each list element.
|
||||
* NOTE: this would fail badly on a list with integer
|
||||
* elements!
|
||||
*/
|
||||
List *resultlist = NIL;
|
||||
List *temp;
|
||||
@@ -1795,9 +1850,13 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
|
||||
break;
|
||||
case T_TargetEntry:
|
||||
{
|
||||
/* We mutate the expression, but not the resdom, by default. */
|
||||
TargetEntry *targetentry = (TargetEntry *) node;
|
||||
TargetEntry *newnode;
|
||||
|
||||
/*
|
||||
* We mutate the expression, but not the resdom, by
|
||||
* default.
|
||||
*/
|
||||
TargetEntry *targetentry = (TargetEntry *) node;
|
||||
TargetEntry *newnode;
|
||||
|
||||
FLATCOPY(newnode, targetentry, TargetEntry);
|
||||
MUTATE(newnode->expr, targetentry->expr, Node *);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.62 2000/03/22 22:08:35 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.63 2000/04/12 17:15:24 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -42,6 +42,7 @@ compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
|
||||
return -1;
|
||||
if (path1->startup_cost > path2->startup_cost)
|
||||
return +1;
|
||||
|
||||
/*
|
||||
* If paths have the same startup cost (not at all unlikely),
|
||||
* order them by total cost.
|
||||
@@ -57,6 +58,7 @@ compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
|
||||
return -1;
|
||||
if (path1->total_cost > path2->total_cost)
|
||||
return +1;
|
||||
|
||||
/*
|
||||
* If paths have the same total cost, order them by startup cost.
|
||||
*/
|
||||
@@ -172,7 +174,8 @@ set_cheapest(RelOptInfo *parent_rel)
|
||||
void
|
||||
add_path(RelOptInfo *parent_rel, Path *new_path)
|
||||
{
|
||||
bool accept_new = true; /* unless we find a superior old path */
|
||||
bool accept_new = true; /* unless we find a superior old
|
||||
* path */
|
||||
List *p1_prev = NIL;
|
||||
List *p1;
|
||||
|
||||
@@ -184,36 +187,39 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
|
||||
foreach(p1, parent_rel->pathlist)
|
||||
{
|
||||
Path *old_path = (Path *) lfirst(p1);
|
||||
bool remove_old = false; /* unless new proves superior */
|
||||
bool remove_old = false; /* unless new proves superior */
|
||||
int costcmp;
|
||||
|
||||
costcmp = compare_path_costs(new_path, old_path, TOTAL_COST);
|
||||
|
||||
/*
|
||||
* If the two paths compare differently for startup and total cost,
|
||||
* then we want to keep both, and we can skip the (much slower)
|
||||
* comparison of pathkeys. If they compare the same, proceed with
|
||||
* the pathkeys comparison. Note this test relies on the fact that
|
||||
* compare_path_costs will only return 0 if both costs are equal
|
||||
* (and, therefore, there's no need to call it twice in that case).
|
||||
* If the two paths compare differently for startup and total
|
||||
* cost, then we want to keep both, and we can skip the (much
|
||||
* slower) comparison of pathkeys. If they compare the same,
|
||||
* proceed with the pathkeys comparison. Note this test relies on
|
||||
* the fact that compare_path_costs will only return 0 if both
|
||||
* costs are equal (and, therefore, there's no need to call it
|
||||
* twice in that case).
|
||||
*/
|
||||
if (costcmp == 0 ||
|
||||
costcmp == compare_path_costs(new_path, old_path, STARTUP_COST))
|
||||
costcmp == compare_path_costs(new_path, old_path, STARTUP_COST))
|
||||
{
|
||||
switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys))
|
||||
{
|
||||
case PATHKEYS_EQUAL:
|
||||
if (costcmp < 0)
|
||||
remove_old = true; /* new dominates old */
|
||||
remove_old = true; /* new dominates old */
|
||||
else
|
||||
accept_new = false; /* old equals or dominates new */
|
||||
accept_new = false; /* old equals or dominates
|
||||
* new */
|
||||
break;
|
||||
case PATHKEYS_BETTER1:
|
||||
if (costcmp <= 0)
|
||||
remove_old = true; /* new dominates old */
|
||||
remove_old = true; /* new dominates old */
|
||||
break;
|
||||
case PATHKEYS_BETTER2:
|
||||
if (costcmp >= 0)
|
||||
accept_new = false; /* old dominates new */
|
||||
accept_new = false; /* old dominates new */
|
||||
break;
|
||||
case PATHKEYS_DIFFERENT:
|
||||
/* keep both paths, since they have different ordering */
|
||||
@@ -241,7 +247,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
|
||||
* scanning the pathlist; we will not add new_path, and we assume
|
||||
* new_path cannot dominate any other elements of the pathlist.
|
||||
*/
|
||||
if (! accept_new)
|
||||
if (!accept_new)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -315,12 +321,14 @@ create_index_path(Query *root,
|
||||
if (pathnode->path.pathkeys == NIL)
|
||||
{
|
||||
/* No ordering available from index, is that OK? */
|
||||
if (! ScanDirectionIsNoMovement(indexscandir))
|
||||
if (!ScanDirectionIsNoMovement(indexscandir))
|
||||
elog(ERROR, "create_index_path: failed to create ordered index scan");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The index is ordered, and build_index_pathkeys defaulted to
|
||||
|
||||
/*
|
||||
* The index is ordered, and build_index_pathkeys defaulted to
|
||||
* forward scan, so make sure we mark the pathnode properly.
|
||||
*/
|
||||
if (ScanDirectionIsNoMovement(indexscandir))
|
||||
@@ -341,11 +349,11 @@ create_index_path(Query *root,
|
||||
pathnode->indexscandir = indexscandir;
|
||||
|
||||
/*
|
||||
* This routine is only used to generate "standalone" indexpaths,
|
||||
* not nestloop inner indexpaths. So joinrelids is always NIL
|
||||
* and the number of rows is the same as the parent rel's estimate.
|
||||
* This routine is only used to generate "standalone" indexpaths, not
|
||||
* nestloop inner indexpaths. So joinrelids is always NIL and the
|
||||
* number of rows is the same as the parent rel's estimate.
|
||||
*/
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->rows = rel->rows;
|
||||
|
||||
cost_index(&pathnode->path, root, rel, index, indexquals, false);
|
||||
@@ -359,20 +367,23 @@ create_index_path(Query *root,
|
||||
* pathnode.
|
||||
*
|
||||
*/
|
||||
TidPath *
|
||||
TidPath *
|
||||
create_tidscan_path(RelOptInfo *rel, List *tideval)
|
||||
{
|
||||
TidPath *pathnode = makeNode(TidPath);
|
||||
TidPath *pathnode = makeNode(TidPath);
|
||||
|
||||
pathnode->path.pathtype = T_TidScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.pathkeys = NIL;
|
||||
pathnode->tideval = copyObject(tideval); /* is copy really necessary? */
|
||||
pathnode->tideval = copyObject(tideval); /* is copy really
|
||||
* necessary? */
|
||||
pathnode->unjoined_relids = NIL;
|
||||
|
||||
cost_tidscan(&pathnode->path, rel, tideval);
|
||||
/* divide selectivity for each clause to get an equal selectivity
|
||||
* as IndexScan does OK ?
|
||||
|
||||
/*
|
||||
* divide selectivity for each clause to get an equal selectivity as
|
||||
* IndexScan does OK ?
|
||||
*/
|
||||
|
||||
return pathnode;
|
||||
@@ -485,7 +496,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
|
||||
* 'innerdisbursion' is an estimate of the disbursion of the inner hash key
|
||||
*
|
||||
*/
|
||||
HashPath *
|
||||
HashPath *
|
||||
create_hashjoin_path(RelOptInfo *joinrel,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.49 2000/02/18 09:30:09 inoue Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.50 2000/04/12 17:15:24 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -50,7 +50,7 @@ relation_info(Query *root, Index relid,
|
||||
Form_pg_class relation;
|
||||
|
||||
relationTuple = SearchSysCacheTuple(RELOID,
|
||||
ObjectIdGetDatum(relationObjectId),
|
||||
ObjectIdGetDatum(relationObjectId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(relationTuple))
|
||||
elog(ERROR, "relation_info: Relation %u not found",
|
||||
@@ -81,7 +81,7 @@ find_secondary_indexes(Query *root, Index relid)
|
||||
Oid indrelid = getrelid(relid, root->rtable);
|
||||
Relation relation;
|
||||
HeapScanDesc scan;
|
||||
ScanKeyData indexKey;
|
||||
ScanKeyData indexKey;
|
||||
HeapTuple indexTuple;
|
||||
|
||||
/* Scan pg_index for tuples describing indexes of this rel */
|
||||
@@ -97,27 +97,28 @@ find_secondary_indexes(Query *root, Index relid)
|
||||
|
||||
while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
IndexOptInfo *info = makeNode(IndexOptInfo);
|
||||
int i;
|
||||
Relation indexRelation;
|
||||
Oid relam;
|
||||
uint16 amorderstrategy;
|
||||
Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
IndexOptInfo *info = makeNode(IndexOptInfo);
|
||||
int i;
|
||||
Relation indexRelation;
|
||||
Oid relam;
|
||||
uint16 amorderstrategy;
|
||||
|
||||
/*
|
||||
* Need to make these arrays large enough to be sure there is a
|
||||
* terminating 0 at the end of each one.
|
||||
*/
|
||||
info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS+1));
|
||||
info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS+1));
|
||||
info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS+1));
|
||||
info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
|
||||
info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1));
|
||||
info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
|
||||
|
||||
/* Extract info from the pg_index tuple */
|
||||
info->indexoid = index->indexrelid;
|
||||
info->indproc = index->indproc; /* functional index ?? */
|
||||
if (VARSIZE(&index->indpred) != 0) /* partial index ?? */
|
||||
info->indproc = index->indproc; /* functional index ?? */
|
||||
if (VARSIZE(&index->indpred) != 0) /* partial index ?? */
|
||||
{
|
||||
char *predString = fmgr(F_TEXTOUT, &index->indpred);
|
||||
|
||||
info->indpred = (List *) stringToNode(predString);
|
||||
pfree(predString);
|
||||
}
|
||||
@@ -143,26 +144,25 @@ find_secondary_indexes(Query *root, Index relid)
|
||||
index_close(indexRelation);
|
||||
|
||||
/*
|
||||
* Fetch the ordering operators associated with the index,
|
||||
* if any.
|
||||
* Fetch the ordering operators associated with the index, if any.
|
||||
*/
|
||||
MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS+1));
|
||||
MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1));
|
||||
if (amorderstrategy != 0)
|
||||
{
|
||||
for (i = 0; i < INDEX_MAX_KEYS && index->indclass[i]; i++)
|
||||
{
|
||||
HeapTuple amopTuple;
|
||||
Form_pg_amop amop;
|
||||
HeapTuple amopTuple;
|
||||
Form_pg_amop amop;
|
||||
|
||||
amopTuple =
|
||||
SearchSysCacheTuple(AMOPSTRATEGY,
|
||||
ObjectIdGetDatum(relam),
|
||||
ObjectIdGetDatum(index->indclass[i]),
|
||||
ObjectIdGetDatum(index->indclass[i]),
|
||||
UInt16GetDatum(amorderstrategy),
|
||||
0);
|
||||
if (!HeapTupleIsValid(amopTuple))
|
||||
elog(ERROR, "find_secondary_indexes: no amop %u %u %d",
|
||||
relam, index->indclass[i], (int) amorderstrategy);
|
||||
relam, index->indclass[i], (int) amorderstrategy);
|
||||
amop = (Form_pg_amop) GETSTRUCT(amopTuple);
|
||||
info->ordering[i] = amop->amopopr;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.25 2000/02/18 23:47:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.26 2000/04/12 17:15:24 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -24,15 +24,15 @@
|
||||
|
||||
static List *new_join_tlist(List *tlist, int first_resdomno);
|
||||
static List *build_joinrel_restrictlist(RelOptInfo *joinrel,
|
||||
RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel);
|
||||
RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel);
|
||||
static void build_joinrel_joinlist(RelOptInfo *joinrel,
|
||||
RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel);
|
||||
RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel);
|
||||
static List *subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
|
||||
List *joininfo_list);
|
||||
List *joininfo_list);
|
||||
static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
||||
List *joininfo_list);
|
||||
List *joininfo_list);
|
||||
|
||||
|
||||
/*
|
||||
@@ -50,7 +50,10 @@ get_base_rel(Query *root, int relid)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(baserels);
|
||||
|
||||
/* We know length(rel->relids) == 1 for all members of base_rel_list */
|
||||
/*
|
||||
* We know length(rel->relids) == 1 for all members of
|
||||
* base_rel_list
|
||||
*/
|
||||
if (lfirsti(rel->relids) == relid)
|
||||
return rel;
|
||||
}
|
||||
@@ -75,18 +78,20 @@ get_base_rel(Query *root, int relid)
|
||||
|
||||
if (relid < 0)
|
||||
{
|
||||
|
||||
/*
|
||||
* If the relation is a materialized relation, assume
|
||||
* constants for sizes.
|
||||
* If the relation is a materialized relation, assume constants
|
||||
* for sizes.
|
||||
*/
|
||||
rel->pages = _NONAME_RELATION_PAGES_;
|
||||
rel->tuples = _NONAME_RELATION_TUPLES_;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* Otherwise, retrieve relation statistics from the
|
||||
* system catalogs.
|
||||
* Otherwise, retrieve relation statistics from the system
|
||||
* catalogs.
|
||||
*/
|
||||
relation_info(root, relid,
|
||||
&rel->indexed, &rel->pages, &rel->tuples);
|
||||
@@ -162,6 +167,7 @@ get_join_rel(Query *root,
|
||||
|
||||
if (joinrel)
|
||||
{
|
||||
|
||||
/*
|
||||
* Yes, so we only need to figure the restrictlist for this
|
||||
* particular pair of component relations.
|
||||
@@ -198,13 +204,13 @@ get_join_rel(Query *root,
|
||||
* of the outer and inner join relations and then merging the results
|
||||
* together.
|
||||
*
|
||||
* NOTE: the tlist order for a join rel will depend on which pair
|
||||
* of outer and inner rels we first try to build it from. But the
|
||||
* NOTE: the tlist order for a join rel will depend on which pair of
|
||||
* outer and inner rels we first try to build it from. But the
|
||||
* contents should be the same regardless.
|
||||
*
|
||||
* XXX someday: consider pruning vars from the join's targetlist
|
||||
* if they are needed only to evaluate restriction clauses of this
|
||||
* join, and will never be accessed at higher levels of the plantree.
|
||||
* XXX someday: consider pruning vars from the join's targetlist if they
|
||||
* are needed only to evaluate restriction clauses of this join, and
|
||||
* will never be accessed at higher levels of the plantree.
|
||||
*/
|
||||
new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1);
|
||||
new_inner_tlist = new_join_tlist(inner_rel->targetlist,
|
||||
@@ -212,9 +218,9 @@ get_join_rel(Query *root,
|
||||
joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
|
||||
|
||||
/*
|
||||
* Construct restrict and join clause lists for the new joinrel.
|
||||
* (The caller might or might not need the restrictlist, but
|
||||
* I need it anyway for set_joinrel_size_estimates().)
|
||||
* Construct restrict and join clause lists for the new joinrel. (The
|
||||
* caller might or might not need the restrictlist, but I need it
|
||||
* anyway for set_joinrel_size_estimates().)
|
||||
*/
|
||||
restrictlist = build_joinrel_restrictlist(joinrel, outer_rel, inner_rel);
|
||||
if (restrictlist_ptr)
|
||||
@@ -246,7 +252,7 @@ get_join_rel(Query *root,
|
||||
*
|
||||
* XXX the above comment refers to code that is long dead and gone;
|
||||
* we don't keep track of joinlists for individual targetlist entries
|
||||
* anymore. For now, all vars present in either input tlist will be
|
||||
* anymore. For now, all vars present in either input tlist will be
|
||||
* emitted in the join's tlist.
|
||||
*
|
||||
* 'tlist' is the target list of one of the join relations
|
||||
@@ -286,16 +292,16 @@ new_join_tlist(List *tlist,
|
||||
* the join lists need only be computed once for any join RelOptInfo.
|
||||
* The join lists are fully determined by the set of rels making up the
|
||||
* joinrel, so we should get the same results (up to ordering) from any
|
||||
* candidate pair of sub-relations. But the restriction list is whatever
|
||||
* candidate pair of sub-relations. But the restriction list is whatever
|
||||
* is not handled in the sub-relations, so it depends on which
|
||||
* sub-relations are considered.
|
||||
*
|
||||
* If a join clause from an input relation refers to base rels still not
|
||||
* present in the joinrel, then it is still a join clause for the joinrel;
|
||||
* we put it into an appropriate JoinInfo list for the joinrel. Otherwise,
|
||||
* we put it into an appropriate JoinInfo list for the joinrel. Otherwise,
|
||||
* the clause is now a restrict clause for the joined relation, and we
|
||||
* return it to the caller of build_joinrel_restrictlist() to be stored in
|
||||
* join paths made from this pair of sub-relations. (It will not need to
|
||||
* join paths made from this pair of sub-relations. (It will not need to
|
||||
* be considered further up the join tree.)
|
||||
*
|
||||
* 'joinrel' is a join relation node
|
||||
@@ -304,11 +310,11 @@ new_join_tlist(List *tlist,
|
||||
*
|
||||
* build_joinrel_restrictlist() returns a list of relevant restrictinfos,
|
||||
* whereas build_joinrel_joinlist() stores its results in the joinrel's
|
||||
* joininfo lists. One or the other must accept each given clause!
|
||||
* joininfo lists. One or the other must accept each given clause!
|
||||
*
|
||||
* NB: Formerly, we made deep(!) copies of each input RestrictInfo to pass
|
||||
* up to the join relation. I believe this is no longer necessary, because
|
||||
* RestrictInfo nodes are no longer context-dependent. Instead, just include
|
||||
* RestrictInfo nodes are no longer context-dependent. Instead, just include
|
||||
* the original nodes in the lists made for the join relation.
|
||||
*/
|
||||
static List *
|
||||
@@ -316,9 +322,10 @@ build_joinrel_restrictlist(RelOptInfo *joinrel,
|
||||
RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel)
|
||||
{
|
||||
|
||||
/*
|
||||
* We must eliminate duplicates, since we will see the
|
||||
* same clauses arriving from both input relations...
|
||||
* We must eliminate duplicates, since we will see the same clauses
|
||||
* arriving from both input relations...
|
||||
*/
|
||||
return LispUnion(subbuild_joinrel_restrictlist(joinrel,
|
||||
outer_rel->joininfo),
|
||||
@@ -348,6 +355,7 @@ subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
|
||||
|
||||
if (is_subseti(joininfo->unjoined_relids, joinrel->relids))
|
||||
{
|
||||
|
||||
/*
|
||||
* Clauses in this JoinInfo list become restriction clauses
|
||||
* for the joinrel, since they refer to no outside rels.
|
||||
@@ -360,9 +368,10 @@ subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* These clauses are still join clauses at this level,
|
||||
* so we ignore them in this routine.
|
||||
* These clauses are still join clauses at this level, so we
|
||||
* ignore them in this routine.
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -385,18 +394,20 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
||||
joinrel->relids);
|
||||
if (new_unjoined_relids == NIL)
|
||||
{
|
||||
|
||||
/*
|
||||
* Clauses in this JoinInfo list become restriction clauses
|
||||
* for the joinrel, since they refer to no outside rels.
|
||||
* So we can ignore them in this routine.
|
||||
* for the joinrel, since they refer to no outside rels. So we
|
||||
* can ignore them in this routine.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* These clauses are still join clauses at this level,
|
||||
* so find or make the appropriate JoinInfo item for the joinrel,
|
||||
* and add the clauses to it (eliminating duplicates).
|
||||
* These clauses are still join clauses at this level, so find
|
||||
* or make the appropriate JoinInfo item for the joinrel, and
|
||||
* add the clauses to it (eliminating duplicates).
|
||||
*/
|
||||
JoinInfo *new_joininfo;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.43 2000/01/27 18:11:34 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.44 2000/04/12 17:15:24 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -27,7 +27,7 @@
|
||||
/*
|
||||
* tlistentry_member
|
||||
* Finds the (first) member of the given tlist whose expression is
|
||||
* equal() to the given expression. Result is NULL if no such member.
|
||||
* equal() to the given expression. Result is NULL if no such member.
|
||||
*/
|
||||
TargetEntry *
|
||||
tlistentry_member(Node *node, List *targetlist)
|
||||
@@ -36,7 +36,7 @@ tlistentry_member(Node *node, List *targetlist)
|
||||
|
||||
foreach(temp, targetlist)
|
||||
{
|
||||
TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
|
||||
TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
|
||||
|
||||
if (equal(node, tlentry->expr))
|
||||
return tlentry;
|
||||
@@ -87,12 +87,12 @@ tlist_member(Node *node, List *targetlist)
|
||||
void
|
||||
add_var_to_tlist(RelOptInfo *rel, Var *var)
|
||||
{
|
||||
if (! tlistentry_member((Node *) var, rel->targetlist))
|
||||
if (!tlistentry_member((Node *) var, rel->targetlist))
|
||||
{
|
||||
/* XXX is copyObject necessary here? */
|
||||
rel->targetlist = lappend(rel->targetlist,
|
||||
create_tl_element((Var *) copyObject(var),
|
||||
length(rel->targetlist) + 1));
|
||||
create_tl_element((Var *) copyObject(var),
|
||||
length(rel->targetlist) + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ add_to_flat_tlist(List *tlist, List *vars)
|
||||
{
|
||||
Var *var = lfirst(v);
|
||||
|
||||
if (! tlistentry_member((Node *) var, tlist))
|
||||
if (!tlistentry_member((Node *) var, tlist))
|
||||
{
|
||||
Resdom *r;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.25 2000/01/26 05:56:40 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.26 2000/04/12 17:15:24 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -20,7 +20,8 @@
|
||||
#include "optimizer/var.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
List *varlist;
|
||||
bool includeUpperVars;
|
||||
} pull_var_clause_context;
|
||||
@@ -28,7 +29,7 @@ typedef struct {
|
||||
static bool pull_varnos_walker(Node *node, List **listptr);
|
||||
static bool contain_var_clause_walker(Node *node, void *context);
|
||||
static bool pull_var_clause_walker(Node *node,
|
||||
pull_var_clause_context *context);
|
||||
pull_var_clause_context *context);
|
||||
|
||||
|
||||
/*
|
||||
@@ -54,7 +55,8 @@ pull_varnos_walker(Node *node, List **listptr)
|
||||
return false;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
Var *var = (Var *) node;
|
||||
|
||||
if (var->varlevelsup == 0 && !intMember(var->varno, *listptr))
|
||||
*listptr = lconsi(var->varno, *listptr);
|
||||
return false;
|
||||
@@ -83,7 +85,8 @@ contain_var_clause_walker(Node *node, void *context)
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
if (((Var *) node)->varlevelsup == 0)
|
||||
return true; /* abort the tree traversal and return true */
|
||||
return true; /* abort the tree traversal and return
|
||||
* true */
|
||||
return false;
|
||||
}
|
||||
return expression_tree_walker(node, contain_var_clause_walker, context);
|
||||
@@ -94,7 +97,7 @@ contain_var_clause_walker(Node *node, void *context)
|
||||
* Recursively pulls all var nodes from an expression clause.
|
||||
*
|
||||
* Upper-level vars (with varlevelsup > 0) are included only
|
||||
* if includeUpperVars is true. Most callers probably want
|
||||
* if includeUpperVars is true. Most callers probably want
|
||||
* to ignore upper-level vars.
|
||||
*
|
||||
* Returns list of varnodes found. Note the varnodes themselves are not
|
||||
@@ -103,7 +106,7 @@ contain_var_clause_walker(Node *node, void *context)
|
||||
List *
|
||||
pull_var_clause(Node *clause, bool includeUpperVars)
|
||||
{
|
||||
pull_var_clause_context context;
|
||||
pull_var_clause_context context;
|
||||
|
||||
context.varlist = NIL;
|
||||
context.includeUpperVars = includeUpperVars;
|
||||
|
||||
Reference in New Issue
Block a user