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 @@ ...@@ -6,7 +6,7 @@
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -292,108 +292,129 @@ set_unioni(List *l1, List *l2)
return nconc(l1, set_differencei(l2, l1)); return nconc(l1, set_differencei(l2, l1));
} }
static List * typedef struct finalize_primnode_results {
_finalize_primnode(void *expr, List **subplan) List *subplans; /* List of subplans found in expr */
{ List *paramids; /* List of PARAM_EXEC paramids found */
List *result = NULL; } finalize_primnode_results;
if (expr == NULL) static bool finalize_primnode_walker(Node *node,
return NULL; finalize_primnode_results *results);
static void
finalize_primnode(Node *expr, finalize_primnode_results *results)
{
results->subplans = NIL; /* initialize */
results->paramids = NIL;
(void) finalize_primnode_walker(expr, results);
}
if (IsA(expr, Param)) static bool
finalize_primnode_walker(Node *node,
finalize_primnode_results *results)
{
if (node == NULL)
return false;
if (IsA(node, Param))
{ {
if (((Param *) expr)->paramkind == PARAM_EXEC) if (((Param *) node)->paramkind == PARAM_EXEC)
return lconsi(((Param *) expr)->paramid, (List *) NULL);
}
else if (single_node(expr))
return NULL;
else if (IsA(expr, List))
{ {
List *le; int paramid = (int) ((Param *) node)->paramid;
foreach(le, (List *) expr) if (! intMember(paramid, results->paramids))
result = set_unioni(result, results->paramids = lconsi(paramid, results->paramids);
_finalize_primnode(lfirst(le), subplan));
} }
else if (IsA(expr, Iter)) return false; /* no more to do here */
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))
{
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));
} }
else if (IsA(expr, TargetEntry)) if (is_subplan(node))
return _finalize_primnode(((TargetEntry *) expr)->expr, subplan);
else if (is_subplan(expr))
{ {
SubPlan *subplan = (SubPlan *) ((Expr *) node)->oper;
List *lst; List *lst;
*subplan = lappend(*subplan, ((Expr *) expr)->oper); /* Add subplan to subplans list */
foreach(lst, ((SubPlan *) ((Expr *) expr)->oper)->plan->extParam) 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 */ /* note varlevelsup is absolute level number */
if (var->varlevelsup < PlannerQueryLevel && if (var->varlevelsup < PlannerQueryLevel &&
!intMember(lfirsti(lst), result)) ! intMember(paramid, results->paramids))
result = lappendi(result, lfirsti(lst)); 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 return expression_tree_walker(node, finalize_primnode_walker,
elog(ERROR, "_finalize_primnode: can't handle node %d", nodeTag(expr)); (void *) results);
return result;
} }
/* 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 * Node *
SS_replace_correlation_vars(Node *expr) SS_replace_correlation_vars(Node *expr)
{ {
if (expr == NULL) if (expr == NULL)
return 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; List *le;
foreach(le, (List *) expr) foreach(le, (List *) expr)
lfirst(le) = SS_replace_correlation_vars((Node *) lfirst(le)); lfirst(le) = SS_replace_correlation_vars((Node *) lfirst(le));
} }
else if (IsA(expr, Var)) else if (IsA(expr, Expr))
{ {
if (((Var *) expr)->varlevelsup > 0) /* XXX do we need to do anything special with subplans? */
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))
((Expr *) expr)->args = (List *) ((Expr *) expr)->args = (List *)
SS_replace_correlation_vars((Node *) ((Expr *) expr)->args); SS_replace_correlation_vars((Node *) ((Expr *) expr)->args);
}
else if (IsA(expr, Aggref)) 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)) else if (IsA(expr, ArrayRef))
{ {
((ArrayRef *) expr)->refupperindexpr = (List *) ((ArrayRef *) expr)->refupperindexpr = (List *)
SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refupperindexpr); SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refupperindexpr);
((ArrayRef *) expr)->reflowerindexpr = (List *) ((ArrayRef *) expr)->reflowerindexpr = (List *)
SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->reflowerindexpr); 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); ((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)) 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)) else if (IsA(expr, SubLink))
{ {
List *le; List *le;
...@@ -409,30 +430,76 @@ SS_replace_correlation_vars(Node *expr) ...@@ -409,30 +430,76 @@ SS_replace_correlation_vars(Node *expr)
SS_replace_correlation_vars((Node *) ((SubLink *) expr)->lefthand); SS_replace_correlation_vars((Node *) ((SubLink *) expr)->lefthand);
} }
else 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)); nodeTag(expr));
return 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 * Node *
SS_process_sublinks(Node *expr) SS_process_sublinks(Node *expr)
{ {
if (expr == NULL) if (expr == NULL)
return 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; List *le;
foreach(le, (List *) expr) foreach(le, (List *) expr)
lfirst(le) = SS_process_sublinks((Node *) lfirst(le)); lfirst(le) = SS_process_sublinks((Node *) lfirst(le));
} }
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) || else if (IsA(expr, Expr))
not_clause(expr) || is_funcclause(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 *) ((Expr *) expr)->args = (List *)
SS_process_sublinks((Node *) ((Expr *) expr)->args); 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; return expr;
} }
...@@ -440,60 +507,65 @@ SS_process_sublinks(Node *expr) ...@@ -440,60 +507,65 @@ SS_process_sublinks(Node *expr)
List * List *
SS_finalize_plan(Plan *plan) SS_finalize_plan(Plan *plan)
{ {
List *extParam = NULL; List *extParam = NIL;
List *locParam = NULL; List *locParam = NIL;
List *subPlan = NULL; finalize_primnode_results results;
List *param_list;
List *lst; List *lst;
if (plan == NULL) if (plan == NULL)
return NULL; return NULL;
param_list = _finalize_primnode(plan->targetlist, &subPlan); /* Find params in targetlist, make sure there are no subplans there */
Assert(subPlan == NULL); 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)) switch (nodeTag(plan))
{ {
case T_Result: case T_Result:
param_list = set_unioni(param_list, finalize_primnode_walker(((Result *) plan)->resconstantqual,
_finalize_primnode(((Result *) plan)->resconstantqual, &subPlan)); &results);
/* subPlan is NOT necessarily NULL here ... */ /* results.subplans is NOT necessarily empty here ... */
break; break;
case T_Append: case T_Append:
foreach(lst, ((Append *) plan)->appendplans) foreach(lst, ((Append *) plan)->appendplans)
param_list = set_unioni(param_list, results.paramids = set_unioni(results.paramids,
SS_finalize_plan((Plan *) lfirst(lst))); SS_finalize_plan((Plan *) lfirst(lst)));
break; break;
case T_IndexScan: case T_IndexScan:
param_list = set_unioni(param_list, finalize_primnode_walker((Node *) ((IndexScan *) plan)->indxqual,
_finalize_primnode(((IndexScan *) plan)->indxqual, &subPlan)); &results);
Assert(subPlan == NULL); Assert(results.subplans == NIL);
break; break;
case T_MergeJoin: case T_MergeJoin:
param_list = set_unioni(param_list, finalize_primnode_walker((Node *) ((MergeJoin *) plan)->mergeclauses,
_finalize_primnode(((MergeJoin *) plan)->mergeclauses, &subPlan)); &results);
Assert(subPlan == NULL); Assert(results.subplans == NIL);
break; break;
case T_HashJoin: case T_HashJoin:
param_list = set_unioni(param_list, finalize_primnode_walker((Node *) ((HashJoin *) plan)->hashclauses,
_finalize_primnode(((HashJoin *) plan)->hashclauses, &subPlan)); &results);
Assert(subPlan == NULL); Assert(results.subplans == NIL);
break; break;
case T_Hash: case T_Hash:
param_list = set_unioni(param_list, finalize_primnode_walker((Node *) ((Hash *) plan)->hashkey,
_finalize_primnode(((Hash *) plan)->hashkey, &subPlan)); &results);
Assert(subPlan == NULL); Assert(results.subplans == NIL);
break; break;
case T_Agg: case T_Agg:
param_list = set_unioni(param_list, finalize_primnode_walker((Node *) ((Agg *) plan)->aggs,
_finalize_primnode(((Agg *) plan)->aggs, &subPlan)); &results);
Assert(subPlan == NULL); Assert(results.subplans == NIL);
break; break;
case T_SeqScan: case T_SeqScan:
...@@ -503,16 +575,24 @@ SS_finalize_plan(Plan *plan) ...@@ -503,16 +575,24 @@ SS_finalize_plan(Plan *plan)
case T_Unique: case T_Unique:
case T_Group: case T_Group:
break; break;
default: default:
elog(ERROR, "SS_finalize_plan: node %d unsupported", nodeTag(plan)); elog(ERROR, "SS_finalize_plan: node %d unsupported",
nodeTag(plan));
return NULL; return NULL;
} }
param_list = set_unioni(param_list, _finalize_primnode(plan->qual, &subPlan)); finalize_primnode_walker((Node *) plan->qual, &results);
param_list = set_unioni(param_list, SS_finalize_plan(plan->lefttree)); /* subplans are OK in the qual... */
param_list = set_unioni(param_list, SS_finalize_plan(plan->righttree));
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); Var *var = nth(lfirsti(lst), PlannerParamVar);
...@@ -520,7 +600,7 @@ SS_finalize_plan(Plan *plan) ...@@ -520,7 +600,7 @@ SS_finalize_plan(Plan *plan)
if (var->varlevelsup < PlannerQueryLevel) if (var->varlevelsup < PlannerQueryLevel)
extParam = lappendi(extParam, lfirsti(lst)); extParam = lappendi(extParam, lfirsti(lst));
else if (var->varlevelsup > PlannerQueryLevel) 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 else
{ {
Assert(var->varno == 0 && var->varattno == 0); Assert(var->varno == 0 && var->varattno == 0);
...@@ -530,52 +610,35 @@ SS_finalize_plan(Plan *plan) ...@@ -530,52 +610,35 @@ SS_finalize_plan(Plan *plan)
plan->extParam = extParam; plan->extParam = extParam;
plan->locParam = locParam; plan->locParam = locParam;
plan->subPlan = subPlan; plan->subPlan = results.subplans;
return param_list;
return results.paramids;
} }
/* Construct a list of all subplans found within the given node tree */ /* Construct a list of all subplans found within the given node tree */
static bool SS_pull_subplan_walker(Node *node, List **listptr);
List * List *
SS_pull_subplan(Node *expr) SS_pull_subplan(Node *expr)
{ {
List *result = NULL; List *result = NIL;
if (expr == NULL || single_node(expr))
return NULL;
if (IsA(expr, List)) SS_pull_subplan_walker(expr, &result);
{ return result;
List *le; }
foreach(le, (List *) expr) static bool
result = nconc(result, SS_pull_subplan(lfirst(le))); SS_pull_subplan_walker(Node *node, List **listptr)
} {
else if (IsA(expr, Iter)) if (node == NULL)
return SS_pull_subplan(((Iter *) expr)->iterexpr); return false;
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) || if (is_subplan(node))
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))
{ {
result = SS_pull_subplan((Node *) ((ArrayRef *) expr)->refupperindexpr); *listptr = lappend(*listptr, ((Expr *) node)->oper);
result = nconc(result, /* XXX original code did not examine args to subplan, is this right? */
SS_pull_subplan((Node *) ((ArrayRef *) expr)->reflowerindexpr)); return false;
result = nconc(result,
SS_pull_subplan(((ArrayRef *) expr)->refexpr));
result = nconc(result,
SS_pull_subplan(((ArrayRef *) expr)->refassgnexpr));
} }
else if (IsA(expr, TargetEntry)) return expression_tree_walker(node, SS_pull_subplan_walker,
return SS_pull_subplan(((TargetEntry *) expr)->expr); (void *) listptr);
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;
} }
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