Commit 8aea617c authored by Tom Lane's avatar Tom Lane

Several routines failed to cope with CASE expressions, and

indeed some of 'em were missing support for more node types than that...
parent d72168d6
......@@ -6,7 +6,7 @@
* Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.18 1999/06/21 01:20:57 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.19 1999/07/15 01:52:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -292,108 +292,129 @@ set_unioni(List *l1, List *l2)
return nconc(l1, set_differencei(l2, l1));
}
static List *
_finalize_primnode(void *expr, List **subplan)
{
List *result = NULL;
typedef struct finalize_primnode_results {
List *subplans; /* List of subplans found in expr */
List *paramids; /* List of PARAM_EXEC paramids found */
} finalize_primnode_results;
if (expr == NULL)
return NULL;
static bool finalize_primnode_walker(Node *node,
finalize_primnode_results *results);
if (IsA(expr, Param))
{
if (((Param *) expr)->paramkind == PARAM_EXEC)
return lconsi(((Param *) expr)->paramid, (List *) NULL);
}
else if (single_node(expr))
return NULL;
else if (IsA(expr, List))
{
List *le;
static void
finalize_primnode(Node *expr, finalize_primnode_results *results)
{
results->subplans = NIL; /* initialize */
results->paramids = NIL;
(void) finalize_primnode_walker(expr, results);
}
foreach(le, (List *) expr)
result = set_unioni(result,
_finalize_primnode(lfirst(le), subplan));
}
else if (IsA(expr, Iter))
return _finalize_primnode(((Iter *) expr)->iterexpr, subplan);
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
not_clause(expr) || is_funcclause(expr))
return _finalize_primnode(((Expr *) expr)->args, subplan);
else if (IsA(expr, Aggref))
return _finalize_primnode(((Aggref *) expr)->target, subplan);
else if (IsA(expr, ArrayRef))
static bool
finalize_primnode_walker(Node *node,
finalize_primnode_results *results)
{
if (node == NULL)
return false;
if (IsA(node, Param))
{
result = _finalize_primnode(((ArrayRef *) expr)->refupperindexpr, subplan);
result = set_unioni(result,
_finalize_primnode(((ArrayRef *) expr)->reflowerindexpr, subplan));
result = set_unioni(result,
_finalize_primnode(((ArrayRef *) expr)->refexpr, subplan));
result = set_unioni(result,
_finalize_primnode(((ArrayRef *) expr)->refassgnexpr, subplan));
if (((Param *) node)->paramkind == PARAM_EXEC)
{
int paramid = (int) ((Param *) node)->paramid;
if (! intMember(paramid, results->paramids))
results->paramids = lconsi(paramid, results->paramids);
}
return false; /* no more to do here */
}
else if (IsA(expr, TargetEntry))
return _finalize_primnode(((TargetEntry *) expr)->expr, subplan);
else if (is_subplan(expr))
if (is_subplan(node))
{
SubPlan *subplan = (SubPlan *) ((Expr *) node)->oper;
List *lst;
*subplan = lappend(*subplan, ((Expr *) expr)->oper);
foreach(lst, ((SubPlan *) ((Expr *) expr)->oper)->plan->extParam)
/* Add subplan to subplans list */
results->subplans = lappend(results->subplans, subplan);
/* Check extParam list for params to add to paramids */
foreach(lst, subplan->plan->extParam)
{
Var *var = nth(lfirsti(lst), PlannerParamVar);
int paramid = lfirsti(lst);
Var *var = nth(paramid, PlannerParamVar);
/* note varlevelsup is absolute level number */
if (var->varlevelsup < PlannerQueryLevel &&
!intMember(lfirsti(lst), result))
result = lappendi(result, lfirsti(lst));
! intMember(paramid, results->paramids))
results->paramids = lconsi(paramid, results->paramids);
}
/* XXX We do NOT allow expression_tree_walker to examine the args
* passed to the subplan. Is that correct??? It's what the
* old code did, but it seems mighty bogus... tgl 7/14/99
*/
return false; /* don't recurse into subplan args */
}
else
elog(ERROR, "_finalize_primnode: can't handle node %d", nodeTag(expr));
return result;
return expression_tree_walker(node, finalize_primnode_walker,
(void *) results);
}
/* Replace correlation vars (uplevel vars) with Params. */
/* XXX should replace this with use of a generalized tree rebuilder,
* designed along the same lines as expression_tree_walker.
* Not done yet.
*/
Node *
SS_replace_correlation_vars(Node *expr)
{
if (expr == NULL)
return NULL;
if (IsA(expr, List))
if (IsA(expr, Var))
{
if (((Var *) expr)->varlevelsup > 0)
expr = (Node *) _replace_var((Var *) expr);
}
else if (single_node(expr))
return expr;
else if (IsA(expr, List))
{
List *le;
foreach(le, (List *) expr)
lfirst(le) = SS_replace_correlation_vars((Node *) lfirst(le));
}
else if (IsA(expr, Var))
else if (IsA(expr, Expr))
{
if (((Var *) expr)->varlevelsup > 0)
expr = (Node *) _replace_var((Var *) expr);
}
else if (IsA(expr, Iter))
((Iter *) expr)->iterexpr = SS_replace_correlation_vars(((Iter *) expr)->iterexpr);
else if (single_node(expr))
return expr;
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
not_clause(expr) || is_funcclause(expr))
/* XXX do we need to do anything special with subplans? */
((Expr *) expr)->args = (List *)
SS_replace_correlation_vars((Node *) ((Expr *) expr)->args);
}
else if (IsA(expr, Aggref))
((Aggref *) expr)->target = SS_replace_correlation_vars((Node *) ((Aggref *) expr)->target);
((Aggref *) expr)->target = SS_replace_correlation_vars(((Aggref *) expr)->target);
else if (IsA(expr, Iter))
((Iter *) expr)->iterexpr = SS_replace_correlation_vars(((Iter *) expr)->iterexpr);
else if (IsA(expr, ArrayRef))
{
((ArrayRef *) expr)->refupperindexpr = (List *)
SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refupperindexpr);
((ArrayRef *) expr)->reflowerindexpr = (List *)
SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->reflowerindexpr);
((ArrayRef *) expr)->refexpr = SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refexpr);
((ArrayRef *) expr)->refexpr = SS_replace_correlation_vars(((ArrayRef *) expr)->refexpr);
((ArrayRef *) expr)->refassgnexpr = SS_replace_correlation_vars(((ArrayRef *) expr)->refassgnexpr);
}
else if (IsA(expr, CaseExpr))
{
CaseExpr *caseexpr = (CaseExpr *) expr;
List *le;
foreach(le, caseexpr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(le);
Assert(IsA(when, CaseWhen));
when->expr = SS_replace_correlation_vars(when->expr);
when->result = SS_replace_correlation_vars(when->result);
}
/* caseexpr->arg should be null, but we'll check it anyway */
caseexpr->arg = SS_replace_correlation_vars(caseexpr->arg);
caseexpr->defresult = SS_replace_correlation_vars(caseexpr->defresult);
}
else if (IsA(expr, TargetEntry))
((TargetEntry *) expr)->expr = SS_replace_correlation_vars((Node *) ((TargetEntry *) expr)->expr);
((TargetEntry *) expr)->expr = SS_replace_correlation_vars(((TargetEntry *) expr)->expr);
else if (IsA(expr, SubLink))
{
List *le;
......@@ -409,30 +430,76 @@ SS_replace_correlation_vars(Node *expr)
SS_replace_correlation_vars((Node *) ((SubLink *) expr)->lefthand);
}
else
elog(NOTICE, "SS_replace_correlation_vars: can't handle node %d",
elog(ERROR, "SS_replace_correlation_vars: can't handle node %d",
nodeTag(expr));
return expr;
}
/* Replace sublinks by subplans in the given expression */
/* XXX should replace this with use of a generalized tree rebuilder,
* designed along the same lines as expression_tree_walker.
* Not done yet.
*/
Node *
SS_process_sublinks(Node *expr)
{
if (expr == NULL)
return NULL;
if (IsA(expr, List))
if (IsA(expr, SubLink))
{
expr = _make_subplan((SubLink *) expr);
}
else if (single_node(expr))
return expr;
else if (IsA(expr, List))
{
List *le;
foreach(le, (List *) expr)
lfirst(le) = SS_process_sublinks((Node *) lfirst(le));
}
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
not_clause(expr) || is_funcclause(expr))
else if (IsA(expr, Expr))
{
/* We should never see a subplan node here, since this is the
* routine that makes 'em in the first place. No need to check.
*/
((Expr *) expr)->args = (List *)
SS_process_sublinks((Node *) ((Expr *) expr)->args);
else if (IsA(expr, SubLink))/* got it! */
expr = _make_subplan((SubLink *) expr);
}
else if (IsA(expr, Aggref))
((Aggref *) expr)->target = SS_process_sublinks(((Aggref *) expr)->target);
else if (IsA(expr, Iter))
((Iter *) expr)->iterexpr = SS_process_sublinks(((Iter *) expr)->iterexpr);
else if (IsA(expr, ArrayRef))
{
((ArrayRef *) expr)->refupperindexpr = (List *)
SS_process_sublinks((Node *) ((ArrayRef *) expr)->refupperindexpr);
((ArrayRef *) expr)->reflowerindexpr = (List *)
SS_process_sublinks((Node *) ((ArrayRef *) expr)->reflowerindexpr);
((ArrayRef *) expr)->refexpr = SS_process_sublinks(((ArrayRef *) expr)->refexpr);
((ArrayRef *) expr)->refassgnexpr = SS_process_sublinks(((ArrayRef *) expr)->refassgnexpr);
}
else if (IsA(expr, CaseExpr))
{
CaseExpr *caseexpr = (CaseExpr *) expr;
List *le;
foreach(le, caseexpr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(le);
Assert(IsA(when, CaseWhen));
when->expr = SS_process_sublinks(when->expr);
when->result = SS_process_sublinks(when->result);
}
/* caseexpr->arg should be null, but we'll check it anyway */
caseexpr->arg = SS_process_sublinks(caseexpr->arg);
caseexpr->defresult = SS_process_sublinks(caseexpr->defresult);
}
else
elog(ERROR, "SS_process_sublinks: can't handle node %d",
nodeTag(expr));
return expr;
}
......@@ -440,60 +507,65 @@ SS_process_sublinks(Node *expr)
List *
SS_finalize_plan(Plan *plan)
{
List *extParam = NULL;
List *locParam = NULL;
List *subPlan = NULL;
List *param_list;
List *extParam = NIL;
List *locParam = NIL;
finalize_primnode_results results;
List *lst;
if (plan == NULL)
return NULL;
param_list = _finalize_primnode(plan->targetlist, &subPlan);
Assert(subPlan == NULL);
/* Find params in targetlist, make sure there are no subplans there */
finalize_primnode((Node *) plan->targetlist, &results);
Assert(results.subplans == NIL);
/* From here on, we invoke finalize_primnode_walker not finalize_primnode,
* so that results.paramids lists are automatically merged together and
* we don't have to do it the hard way. But when recursing to self,
* we do have to merge the lists. Oh well.
*/
switch (nodeTag(plan))
{
case T_Result:
param_list = set_unioni(param_list,
_finalize_primnode(((Result *) plan)->resconstantqual, &subPlan));
/* subPlan is NOT necessarily NULL here ... */
finalize_primnode_walker(((Result *) plan)->resconstantqual,
&results);
/* results.subplans is NOT necessarily empty here ... */
break;
case T_Append:
foreach(lst, ((Append *) plan)->appendplans)
param_list = set_unioni(param_list,
SS_finalize_plan((Plan *) lfirst(lst)));
results.paramids = set_unioni(results.paramids,
SS_finalize_plan((Plan *) lfirst(lst)));
break;
case T_IndexScan:
param_list = set_unioni(param_list,
_finalize_primnode(((IndexScan *) plan)->indxqual, &subPlan));
Assert(subPlan == NULL);
finalize_primnode_walker((Node *) ((IndexScan *) plan)->indxqual,
&results);
Assert(results.subplans == NIL);
break;
case T_MergeJoin:
param_list = set_unioni(param_list,
_finalize_primnode(((MergeJoin *) plan)->mergeclauses, &subPlan));
Assert(subPlan == NULL);
finalize_primnode_walker((Node *) ((MergeJoin *) plan)->mergeclauses,
&results);
Assert(results.subplans == NIL);
break;
case T_HashJoin:
param_list = set_unioni(param_list,
_finalize_primnode(((HashJoin *) plan)->hashclauses, &subPlan));
Assert(subPlan == NULL);
finalize_primnode_walker((Node *) ((HashJoin *) plan)->hashclauses,
&results);
Assert(results.subplans == NIL);
break;
case T_Hash:
param_list = set_unioni(param_list,
_finalize_primnode(((Hash *) plan)->hashkey, &subPlan));
Assert(subPlan == NULL);
finalize_primnode_walker((Node *) ((Hash *) plan)->hashkey,
&results);
Assert(results.subplans == NIL);
break;
case T_Agg:
param_list = set_unioni(param_list,
_finalize_primnode(((Agg *) plan)->aggs, &subPlan));
Assert(subPlan == NULL);
finalize_primnode_walker((Node *) ((Agg *) plan)->aggs,
&results);
Assert(results.subplans == NIL);
break;
case T_SeqScan:
......@@ -503,16 +575,24 @@ SS_finalize_plan(Plan *plan)
case T_Unique:
case T_Group:
break;
default:
elog(ERROR, "SS_finalize_plan: node %d unsupported", nodeTag(plan));
elog(ERROR, "SS_finalize_plan: node %d unsupported",
nodeTag(plan));
return NULL;
}
param_list = set_unioni(param_list, _finalize_primnode(plan->qual, &subPlan));
param_list = set_unioni(param_list, SS_finalize_plan(plan->lefttree));
param_list = set_unioni(param_list, SS_finalize_plan(plan->righttree));
finalize_primnode_walker((Node *) plan->qual, &results);
/* subplans are OK in the qual... */
results.paramids = set_unioni(results.paramids,
SS_finalize_plan(plan->lefttree));
results.paramids = set_unioni(results.paramids,
SS_finalize_plan(plan->righttree));
/* Now we have all the paramids and subplans */
foreach(lst, param_list)
foreach(lst, results.paramids)
{
Var *var = nth(lfirsti(lst), PlannerParamVar);
......@@ -520,7 +600,7 @@ SS_finalize_plan(Plan *plan)
if (var->varlevelsup < PlannerQueryLevel)
extParam = lappendi(extParam, lfirsti(lst));
else if (var->varlevelsup > PlannerQueryLevel)
elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan' variable");
elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan's variable");
else
{
Assert(var->varno == 0 && var->varattno == 0);
......@@ -530,52 +610,35 @@ SS_finalize_plan(Plan *plan)
plan->extParam = extParam;
plan->locParam = locParam;
plan->subPlan = subPlan;
return param_list;
plan->subPlan = results.subplans;
return results.paramids;
}
/* Construct a list of all subplans found within the given node tree */
static bool SS_pull_subplan_walker(Node *node, List **listptr);
List *
SS_pull_subplan(Node *expr)
{
List *result = NULL;
if (expr == NULL || single_node(expr))
return NULL;
List *result = NIL;
if (IsA(expr, List))
{
List *le;
SS_pull_subplan_walker(expr, &result);
return result;
}
foreach(le, (List *) expr)
result = nconc(result, SS_pull_subplan(lfirst(le)));
}
else if (IsA(expr, Iter))
return SS_pull_subplan(((Iter *) expr)->iterexpr);
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
not_clause(expr) || is_funcclause(expr))
return SS_pull_subplan((Node *) ((Expr *) expr)->args);
else if (IsA(expr, Aggref))
return SS_pull_subplan(((Aggref *) expr)->target);
else if (IsA(expr, ArrayRef))
static bool
SS_pull_subplan_walker(Node *node, List **listptr)
{
if (node == NULL)
return false;
if (is_subplan(node))
{
result = SS_pull_subplan((Node *) ((ArrayRef *) expr)->refupperindexpr);
result = nconc(result,
SS_pull_subplan((Node *) ((ArrayRef *) expr)->reflowerindexpr));
result = nconc(result,
SS_pull_subplan(((ArrayRef *) expr)->refexpr));
result = nconc(result,
SS_pull_subplan(((ArrayRef *) expr)->refassgnexpr));
*listptr = lappend(*listptr, ((Expr *) node)->oper);
/* XXX original code did not examine args to subplan, is this right? */
return false;
}
else if (IsA(expr, TargetEntry))
return SS_pull_subplan(((TargetEntry *) expr)->expr);
else if (is_subplan(expr))
return lcons(((Expr *) expr)->oper, NULL);
else
elog(ERROR, "SS_pull_subplan: can't handle node %d", nodeTag(expr));
return result;
return expression_tree_walker(node, SS_pull_subplan_walker,
(void *) listptr);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment