/*------------------------------------------------------------------------- * * subselect.c-- * *------------------------------------------------------------------------- */ #include "postgres.h" #include "catalog/pg_type.h" #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "nodes/parsenodes.h" #include "nodes/relation.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/subselect.h" #include "optimizer/planner.h" #include "optimizer/planmain.h" #include "optimizer/internal.h" #include "optimizer/paths.h" #include "optimizer/clauses.h" #include "optimizer/keys.h" #include "optimizer/tlist.h" #include "optimizer/var.h" #include "optimizer/cost.h" int PlannerQueryLevel; /* level of current query */ List *PlannerVarParam; /* correlation Vars to Param mapper */ List *PlannerParamVar; /* to get Var from Param->paramid */ List *PlannerInitPlan; /* init subplans for current query */ int PlannerPlanId; static int _new_param (Var *var, int varlevel) { List *last; int i = 0; if ( PlannerParamVar == NULL ) last = PlannerParamVar = makeNode(List); else { for (last = PlannerParamVar; ; ) { i++; if ( lnext(last) == NULL ) break; last = lnext(last); } lnext(last) = makeNode(List); last = lnext(last); } lnext(last) = NULL; lfirst(last) = makeVar (var->varno, var->varattno, var->vartype, var->vartypmod, varlevel, var->varnoold, var->varoattno); return (i); } static Param* _replace_var (Var *var) { List **rt = (List**) nth (var->varlevelsup, PlannerVarParam); List *vpe = rt[var->varno - 1]; Param *retval; int i; if ( vpe == NULL ) { vpe = rt[var->varno - 1] = makeNode(List); lfirsti(vpe) = -1; lnext(vpe) = NULL; } for (i = 1; ; i++) { if ( i == var->varattno ) break; if ( lnext(vpe) == NULL ) { lnext(vpe) = makeNode(List); vpe = lnext(vpe); lfirsti(vpe) = -1; lnext(vpe) = NULL; } else vpe = lnext(vpe); } if ( (i = lfirsti(vpe)) < 0 ) /* parameter is not assigned */ { i = _new_param (var, PlannerQueryLevel - var->varlevelsup); } retval = makeNode(Param); retval->paramkind = PARAM_EXEC; retval->paramid = (AttrNumber) i; retval->paramtype = var->vartype; return (retval); } static Node* _make_subplan (SubLink *slink) { SubPlan *node = makeNode (SubPlan); Plan *plan; List *lst; Node *result; List *saved_ip = PlannerInitPlan; PlannerInitPlan = NULL; PlannerQueryLevel++; /* we becomes child */ node->plan = plan = union_planner ((Query*) slink->subselect); /* * Assign subPlan, extParam and locParam to plan nodes. * At the moment, SS_finalize_plan doesn't handle initPlan-s * and so we assigne them to the topmost plan node and take * care about its extParam too. */ (void) SS_finalize_plan (plan); plan->initPlan = PlannerInitPlan; /* Get extParam from InitPlan-s */ foreach (lst, PlannerInitPlan) { List *lp; foreach (lp, ((SubPlan*) lfirst (lst))->plan->extParam) { if ( !intMember (lfirsti(lp), plan->extParam) ) plan->extParam = lappendi (plan->extParam, lfirsti(lp)); } } /* and now we are parent again */ PlannerInitPlan = saved_ip; PlannerQueryLevel--; node->plan_id = PlannerPlanId++; node->rtable = ((Query*) slink->subselect)->rtable; node->sublink = slink; slink->subselect = NULL; /* cool ?! */ /* make parParam list */ foreach (lst, plan->extParam) { Var *var = nth (lfirsti(lst), PlannerParamVar); if ( var->varlevelsup == PlannerQueryLevel ) node->parParam = lappendi (node->parParam, lfirsti(lst)); } /* * Un-correlated or undirect correlated plans of EXISTS or EXPR * types can be used as initPlans... */ if ( node->parParam == NULL && slink->subLinkType == EXPR_SUBLINK ) { int i = 0; /* transform right side of all sublink Oper-s into Param */ foreach (lst, slink->oper) { List *rside = lnext(((Expr*) lfirst(lst))->args); TargetEntry *te = nth (i, plan->targetlist); Var *var = makeVar (0, 0, te->resdom->restype, te->resdom->restypmod, PlannerQueryLevel, 0, 0); Param *prm = makeNode(Param); prm->paramkind = PARAM_EXEC; prm->paramid = (AttrNumber) _new_param (var, PlannerQueryLevel); prm->paramtype = var->vartype; lfirst(rside) = prm; node->setParam = lappendi (node->setParam, prm->paramid); pfree (var); i++; } PlannerInitPlan = lappend (PlannerInitPlan, node); if ( i > 1 ) result = (Node*) ((slink->useor) ? make_orclause (slink->oper) : make_andclause (slink->oper)); else result = (Node*) lfirst (slink->oper); } else if ( node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK ) { Var *var = makeVar (0, 0, BOOLOID, -1, PlannerQueryLevel, 0, 0); Param *prm = makeNode(Param); prm->paramkind = PARAM_EXEC; prm->paramid = (AttrNumber) _new_param (var, PlannerQueryLevel); prm->paramtype = var->vartype; node->setParam = lappendi (node->setParam, prm->paramid); pfree (var); PlannerInitPlan = lappend (PlannerInitPlan, node); result = (Node*) prm; } else /* make expression of SUBPLAN type */ { Expr *expr = makeNode (Expr); List *args = NULL; int i = 0; expr->typeOid = BOOLOID; expr->opType = SUBPLAN_EXPR; expr->oper = (Node*) node; /* * Make expr->args from parParam. Left sides of sublink Oper-s * are handled by optimizer directly... * Also, transform right side of sublink Oper-s into Const. */ foreach (lst, node->parParam) { Var *var = nth (lfirsti (lst), PlannerParamVar); var = (Var*) copyObject (var); var->varlevelsup = 0; args = lappend (args, var); } foreach (lst, slink->oper) { List *rside = lnext(((Expr*) lfirst(lst))->args); TargetEntry *te = nth (i, plan->targetlist); Const *con = makeConst (te->resdom->restype, 0, 0, true, 0, 0, 0); lfirst(rside) = con; i++; } expr->args = args; result = (Node*) expr; } return (result); } static List * set_unioni (List *l1, List *l2) { if (l1 == NULL) return (l2); if (l2 == NULL) return (l1); return (nconc (l1, set_differencei (l2, l1))); } static List * _finalize_primnode (void *expr, List **subplan) { List *result = NULL; if ( expr == NULL ) return (NULL); 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; 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, Aggreg)) return (_finalize_primnode (((Aggreg *) 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)) return (_finalize_primnode (((TargetEntry*) expr)->expr, subplan)); else if (is_subplan (expr)) { List *lst; *subplan = lappend (*subplan, ((Expr*) expr)->oper); foreach (lst, ((SubPlan*) ((Expr*) expr)->oper)->plan->extParam) { Var *var = nth (lfirsti(lst), PlannerParamVar); if ( var->varlevelsup < PlannerQueryLevel && !intMember (lfirsti(lst), result) ) result = lappendi (result, lfirsti(lst)); } } else elog (ERROR, "_finalize_primnode: can't handle node %d", nodeTag (expr)); return (result); } Node * SS_replace_correlation_vars (Node *expr) { if ( expr == NULL ) return (NULL); if (IsA (expr, List)) { List *le; foreach (le, (List*) expr) lfirst(le) = SS_replace_correlation_vars ((Node*) lfirst(le)); } else if (IsA (expr, Var)) { if ( ((Var*) expr)->varlevelsup > 0 ) { Assert (((Var*) expr)->varlevelsup < PlannerQueryLevel); 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*) SS_replace_correlation_vars ((Node*) ((Expr *) expr)->args); else if (IsA (expr, Aggreg)) ((Aggreg *) expr)->target = SS_replace_correlation_vars ((Node*) ((Aggreg *) expr)->target); 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)->refassgnexpr = SS_replace_correlation_vars (((ArrayRef *) expr)->refassgnexpr); } else if (IsA (expr, TargetEntry)) ((TargetEntry*) expr)->expr = SS_replace_correlation_vars ((Node*) ((TargetEntry*) expr)->expr); else if (IsA (expr, SubLink)) { List *le; foreach (le, ((SubLink*) expr)->oper) /* left sides only */ { List *oparg = ((Expr*) lfirst (le))->args; lfirst (oparg) = (List*) SS_replace_correlation_vars ((Node*) lfirst (oparg)); } ((SubLink*) expr)->lefthand = (List*) SS_replace_correlation_vars ((Node*) ((SubLink*) expr)->lefthand); } else elog (NOTICE, "SS_replace_correlation_vars: can't handle node %d", nodeTag (expr)); return (expr); } Node* SS_process_sublinks (Node *expr) { if ( expr == NULL ) return (NULL); 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)) ((Expr *) expr)->args = (List*) SS_process_sublinks ((Node*) ((Expr *) expr)->args); else if (IsA (expr, SubLink)) /* got it! */ expr = _make_subplan ((SubLink*) expr); return (expr); } List* SS_finalize_plan (Plan *plan) { List *extParam = NULL; List *locParam = NULL; List *subPlan = NULL; List *param_list; List *lst; if ( plan == NULL ) return (NULL); param_list = _finalize_primnode (plan->targetlist, &subPlan); Assert (subPlan == NULL); switch (nodeTag(plan)) { case T_Result: param_list = set_unioni (param_list, _finalize_primnode (((Result*) plan)->resconstantqual, &subPlan)); break; case T_Append: foreach (lst, ((Append*) plan)->unionplans) param_list = set_unioni (param_list, 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); break; case T_MergeJoin: param_list = set_unioni (param_list, _finalize_primnode (((MergeJoin*) plan)->mergeclauses, &subPlan)); Assert (subPlan == NULL); break; case T_HashJoin: param_list = set_unioni (param_list, _finalize_primnode (((HashJoin*) plan)->hashclauses, &subPlan)); Assert (subPlan == NULL); break; case T_Hash: param_list = set_unioni (param_list, _finalize_primnode (((Hash*) plan)->hashkey, &subPlan)); Assert (subPlan == NULL); break; case T_Agg: param_list = set_unioni (param_list, _finalize_primnode (((Agg*) plan)->aggs, &subPlan)); Assert (subPlan == NULL); break; case T_SeqScan: case T_NestLoop: case T_Material: case T_Sort: case T_Unique: case T_Group: break; default: 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)); foreach (lst, param_list) { Var *var = nth (lfirsti(lst), PlannerParamVar); 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"); else { Assert (var->varno == 0 && var->varattno == 0); locParam = lappendi (locParam, lfirsti(lst)); } } plan->extParam = extParam; plan->locParam = locParam; plan->subPlan = subPlan; return (param_list); } List *SS_pull_subplan (void *expr); List * SS_pull_subplan (void *expr) { List *result = NULL; if ( expr == NULL || single_node(expr) ) return (NULL); if (IsA (expr, List)) { List *le; 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 (((Expr*) expr)->args)); else if (IsA (expr, Aggreg)) return (SS_pull_subplan (((Aggreg *) expr)->target)); else if (IsA (expr, ArrayRef)) { result = SS_pull_subplan (((ArrayRef*) expr)->refupperindexpr); result = nconc (result, SS_pull_subplan (((ArrayRef *) expr)->reflowerindexpr)); result = nconc (result, SS_pull_subplan (((ArrayRef *) expr)->refexpr)); result = nconc (result, SS_pull_subplan (((ArrayRef *) expr)->refassgnexpr)); } 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); }