aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/plan/subselect.c337
1 files changed, 200 insertions, 137 deletions
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 7170d727ef0..2eab2d3d6d4 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);
}