diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 7170d727ef0c2bce5f3281cf837a9c9dbef205e6..2eab2d3d6d48a17291bb6e3f2e2ae0eb709de18f 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -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); }