diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 3 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 106 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 69 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planagg.c | 6 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 47 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 1044 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 118 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 35 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 4 |
10 files changed, 736 insertions, 700 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 6c6e80071c1..7aa2bd7e6fb 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.160 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.161 2007/02/22 22:00:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -521,6 +521,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, root->query_level + 1, tuple_fraction, &subroot); + rel->subrtable = subroot->parse->rtable; /* Copy number of output rows from subplan */ rel->tuples = rel->subplan->plan_rows; diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 422ef923222..3dbb3bd802d 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -54,7 +54,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.177 2007/01/22 20:00:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.178 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -107,11 +107,16 @@ bool enable_nestloop = true; bool enable_mergejoin = true; bool enable_hashjoin = true; +typedef struct +{ + PlannerInfo *root; + QualCost total; +} cost_qual_eval_context; static MergeScanSelCache *cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey); -static bool cost_qual_eval_walker(Node *node, QualCost *total); +static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context); static Selectivity approx_selectivity(PlannerInfo *root, List *quals, JoinType jointype); static Selectivity join_in_selectivity(JoinPath *path, PlannerInfo *root); @@ -362,7 +367,7 @@ cost_index(IndexPath *path, PlannerInfo *root, { QualCost index_qual_cost; - cost_qual_eval(&index_qual_cost, indexQuals); + cost_qual_eval(&index_qual_cost, indexQuals, root); /* any startup cost still has to be paid ... */ cpu_per_tuple -= index_qual_cost.per_tuple; } @@ -855,7 +860,7 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel) Assert(rte->rtekind == RTE_FUNCTION); /* Estimate costs of executing the function expression */ - cost_qual_eval_node(&exprcost, rte->funcexpr); + cost_qual_eval_node(&exprcost, rte->funcexpr, root); startup_cost += exprcost.startup; cpu_per_tuple = exprcost.per_tuple; @@ -1241,7 +1246,7 @@ cost_nestloop(NestPath *path, PlannerInfo *root) ntuples = outer_path_rows * inner_path_rows * joininfactor; /* CPU costs */ - cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo); + cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root); startup_cost += restrict_qual_cost.startup; cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple; run_cost += cpu_per_tuple * ntuples; @@ -1301,8 +1306,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root) */ merge_selec = approx_selectivity(root, mergeclauses, path->jpath.jointype); - cost_qual_eval(&merge_qual_cost, mergeclauses); - cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo); + cost_qual_eval(&merge_qual_cost, mergeclauses, root); + cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root); qp_qual_cost.startup -= merge_qual_cost.startup; qp_qual_cost.per_tuple -= merge_qual_cost.per_tuple; @@ -1587,8 +1592,8 @@ cost_hashjoin(HashPath *path, PlannerInfo *root) */ hash_selec = approx_selectivity(root, hashclauses, path->jpath.jointype); - cost_qual_eval(&hash_qual_cost, hashclauses); - cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo); + cost_qual_eval(&hash_qual_cost, hashclauses, root); + cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root); qp_qual_cost.startup -= hash_qual_cost.startup; qp_qual_cost.per_tuple -= hash_qual_cost.per_tuple; @@ -1756,12 +1761,14 @@ cost_hashjoin(HashPath *path, PlannerInfo *root) * and a per-evaluation component. */ void -cost_qual_eval(QualCost *cost, List *quals) +cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root) { + cost_qual_eval_context context; ListCell *l; - cost->startup = 0; - cost->per_tuple = 0; + context.root = root; + context.total.startup = 0; + context.total.per_tuple = 0; /* We don't charge any cost for the implicit ANDing at top level ... */ @@ -1769,8 +1776,10 @@ cost_qual_eval(QualCost *cost, List *quals) { Node *qual = (Node *) lfirst(l); - cost_qual_eval_walker(qual, cost); + cost_qual_eval_walker(qual, &context); } + + *cost = context.total; } /* @@ -1778,15 +1787,21 @@ cost_qual_eval(QualCost *cost, List *quals) * As above, for a single RestrictInfo or expression. */ void -cost_qual_eval_node(QualCost *cost, Node *qual) +cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root) { - cost->startup = 0; - cost->per_tuple = 0; - cost_qual_eval_walker(qual, cost); + cost_qual_eval_context context; + + context.root = root; + context.total.startup = 0; + context.total.per_tuple = 0; + + cost_qual_eval_walker(qual, &context); + + *cost = context.total; } static bool -cost_qual_eval_walker(Node *node, QualCost *total) +cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) { if (node == NULL) return false; @@ -1803,18 +1818,19 @@ cost_qual_eval_walker(Node *node, QualCost *total) if (rinfo->eval_cost.startup < 0) { - rinfo->eval_cost.startup = 0; - rinfo->eval_cost.per_tuple = 0; + cost_qual_eval_context locContext; + + locContext.root = context->root; + locContext.total.startup = 0; + locContext.total.per_tuple = 0; /* * For an OR clause, recurse into the marked-up tree so that * we set the eval_cost for contained RestrictInfos too. */ if (rinfo->orclause) - cost_qual_eval_walker((Node *) rinfo->orclause, - &rinfo->eval_cost); + cost_qual_eval_walker((Node *) rinfo->orclause, &locContext); else - cost_qual_eval_walker((Node *) rinfo->clause, - &rinfo->eval_cost); + cost_qual_eval_walker((Node *) rinfo->clause, &locContext); /* * If the RestrictInfo is marked pseudoconstant, it will be tested * only once, so treat its cost as all startup cost. @@ -1822,12 +1838,13 @@ cost_qual_eval_walker(Node *node, QualCost *total) if (rinfo->pseudoconstant) { /* count one execution during startup */ - rinfo->eval_cost.startup += rinfo->eval_cost.per_tuple; - rinfo->eval_cost.per_tuple = 0; + locContext.total.startup += locContext.total.per_tuple; + locContext.total.per_tuple = 0; } + rinfo->eval_cost = locContext.total; } - total->startup += rinfo->eval_cost.startup; - total->per_tuple += rinfo->eval_cost.per_tuple; + context->total.startup += rinfo->eval_cost.startup; + context->total.per_tuple += rinfo->eval_cost.per_tuple; /* do NOT recurse into children */ return false; } @@ -1849,8 +1866,8 @@ cost_qual_eval_walker(Node *node, QualCost *total) */ if (IsA(node, FuncExpr)) { - total->per_tuple += get_func_cost(((FuncExpr *) node)->funcid) * - cpu_operator_cost; + context->total.per_tuple += + get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost; } else if (IsA(node, OpExpr) || IsA(node, DistinctExpr) || @@ -1858,8 +1875,8 @@ cost_qual_eval_walker(Node *node, QualCost *total) { /* rely on struct equivalence to treat these all alike */ set_opfuncid((OpExpr *) node); - total->per_tuple += get_func_cost(((OpExpr *) node)->opfuncid) * - cpu_operator_cost; + context->total.per_tuple += + get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost; } else if (IsA(node, ScalarArrayOpExpr)) { @@ -1871,7 +1888,7 @@ cost_qual_eval_walker(Node *node, QualCost *total) Node *arraynode = (Node *) lsecond(saop->args); set_sa_opfuncid(saop); - total->per_tuple += get_func_cost(saop->opfuncid) * + context->total.per_tuple += get_func_cost(saop->opfuncid) * cpu_operator_cost * estimate_array_length(arraynode) * 0.5; } else if (IsA(node, RowCompareExpr)) @@ -1884,7 +1901,7 @@ cost_qual_eval_walker(Node *node, QualCost *total) { Oid opid = lfirst_oid(lc); - total->per_tuple += get_func_cost(get_opcode(opid)) * + context->total.per_tuple += get_func_cost(get_opcode(opid)) * cpu_operator_cost; } } @@ -1905,7 +1922,7 @@ cost_qual_eval_walker(Node *node, QualCost *total) * subplan by hashing. */ SubPlan *subplan = (SubPlan *) node; - Plan *plan = subplan->plan; + Plan *plan = planner_subplan_get_plan(context->root, subplan); if (subplan->useHashTable) { @@ -1915,7 +1932,7 @@ cost_qual_eval_walker(Node *node, QualCost *total) * cpu_operator_cost per tuple for the work of loading the * hashtable, too. */ - total->startup += plan->total_cost + + context->total.startup += plan->total_cost + cpu_operator_cost * plan->plan_rows; /* @@ -1941,20 +1958,21 @@ cost_qual_eval_walker(Node *node, QualCost *total) if (subplan->subLinkType == EXISTS_SUBLINK) { /* we only need to fetch 1 tuple */ - total->per_tuple += plan_run_cost / plan->plan_rows; + context->total.per_tuple += plan_run_cost / plan->plan_rows; } else if (subplan->subLinkType == ALL_SUBLINK || subplan->subLinkType == ANY_SUBLINK) { /* assume we need 50% of the tuples */ - total->per_tuple += 0.50 * plan_run_cost; + context->total.per_tuple += 0.50 * plan_run_cost; /* also charge a cpu_operator_cost per row examined */ - total->per_tuple += 0.50 * plan->plan_rows * cpu_operator_cost; + context->total.per_tuple += + 0.50 * plan->plan_rows * cpu_operator_cost; } else { /* assume we need all tuples */ - total->per_tuple += plan_run_cost; + context->total.per_tuple += plan_run_cost; } /* @@ -1967,15 +1985,15 @@ cost_qual_eval_walker(Node *node, QualCost *total) if (subplan->parParam == NIL && (IsA(plan, Sort) || IsA(plan, Material))) - total->startup += plan->startup_cost; + context->total.startup += plan->startup_cost; else - total->per_tuple += plan->startup_cost; + context->total.per_tuple += plan->startup_cost; } } /* recurse into children */ return expression_tree_walker(node, cost_qual_eval_walker, - (void *) total); + (void *) context); } @@ -2042,7 +2060,7 @@ set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel) rel->rows = clamp_row_est(nrows); - cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo); + cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root); set_rel_width(root, rel); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 4d3d926a167..dbfa2c4e58e 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.225 2007/02/19 02:23:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.226 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -128,12 +128,12 @@ static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, * create_plan * Creates the access plan for a query by tracing backwards through the * desired chain of pathnodes, starting at the node 'best_path'. For - * every pathnode found: - * (1) Create a corresponding plan node containing appropriate id, - * target list, and qualification information. - * (2) Modify qual clauses of join nodes so that subplan attributes are - * referenced using relative values. - * (3) Target lists are not modified, but will be in setrefs.c. + * every pathnode found, we create a corresponding plan node containing + * appropriate id, target list, and qualification information. + * + * The tlists and quals in the plan tree are still in planner format, + * ie, Vars still correspond to the parser's numbering. This will be + * fixed later by setrefs.c. * * best_path is the best access path * @@ -421,7 +421,8 @@ create_gating_plan(PlannerInfo *root, Plan *plan, List *quals) if (!pseudoconstants) return plan; - return (Plan *) make_result((List *) copyObject(plan->targetlist), + return (Plan *) make_result(root, + plan->targetlist, (Node *) pseudoconstants, plan); } @@ -519,7 +520,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) if (best_path->subpaths == NIL) { /* Generate a Result plan with constant-FALSE gating qual */ - return (Plan *) make_result(tlist, + return (Plan *) make_result(root, + tlist, (Node *) list_make1(makeBoolConst(false, false)), NULL); @@ -559,7 +561,7 @@ create_result_plan(PlannerInfo *root, ResultPath *best_path) quals = order_qual_clauses(root, best_path->quals); - return make_result(tlist, (Node *) quals, NULL); + return make_result(root, tlist, (Node *) quals, NULL); } /* @@ -682,7 +684,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) * node to help it along. */ if (!is_projection_capable_plan(subplan)) - subplan = (Plan *) make_result(newtlist, NULL, subplan); + subplan = (Plan *) make_result(root, newtlist, NULL, subplan); else subplan->targetlist = newtlist; } @@ -1065,12 +1067,6 @@ create_bitmap_scan_plan(PlannerInfo *root, */ bitmapqualorig = list_difference_ptr(bitmapqualorig, qpqual); - /* - * Copy the finished bitmapqualorig to make sure we have an independent - * copy --- needed in case there are subplans in the index quals - */ - bitmapqualorig = copyObject(bitmapqualorig); - /* Finally ready to build the plan node */ scan_plan = make_bitmap_heapscan(tlist, qpqual, @@ -1333,7 +1329,8 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path, scan_plan = make_subqueryscan(tlist, scan_clauses, scan_relid, - best_path->parent->subplan); + best_path->parent->subplan, + best_path->parent->subrtable); copy_path_costsize(&scan_plan->scan.plan, best_path); @@ -2115,7 +2112,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) Node *clause = (Node *) lfirst(lc); QualCost qcost; - cost_qual_eval_node(&qcost, clause); + cost_qual_eval_node(&qcost, clause, root); items[i].clause = clause; items[i].cost = qcost.per_tuple; i++; @@ -2326,7 +2323,8 @@ SubqueryScan * make_subqueryscan(List *qptlist, List *qpqual, Index scanrelid, - Plan *subplan) + Plan *subplan, + List *subrtable) { SubqueryScan *node = makeNode(SubqueryScan); Plan *plan = &node->scan.plan; @@ -2345,6 +2343,7 @@ make_subqueryscan(List *qptlist, plan->righttree = NULL; node->scan.scanrelid = scanrelid; node->subplan = subplan; + node->subrtable = subrtable; return node; } @@ -2524,7 +2523,7 @@ make_hash(Plan *lefttree) * plan; this only affects EXPLAIN display not decisions. */ plan->startup_cost = plan->total_cost; - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -2583,7 +2582,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, lefttree->plan_width); plan->startup_cost = sort_path.startup_cost; plan->total_cost = sort_path.total_cost; - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -2741,10 +2740,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys) * Do we need to insert a Result node? */ if (!is_projection_capable_plan(lefttree)) - { - tlist = copyObject(tlist); - lefttree = (Plan *) make_result(tlist, NULL, lefttree); - } + lefttree = (Plan *) make_result(root, tlist, NULL, lefttree); /* * Add resjunk entry to input's tlist @@ -2905,7 +2901,7 @@ make_material(Plan *lefttree) Plan *plan = &node->plan; /* cost should be inserted by caller */ - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -2996,12 +2992,12 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, */ if (qual) { - cost_qual_eval(&qual_cost, qual); + cost_qual_eval(&qual_cost, qual, root); plan->startup_cost += qual_cost.startup; plan->total_cost += qual_cost.startup; plan->total_cost += qual_cost.per_tuple * plan->plan_rows; } - cost_qual_eval(&qual_cost, tlist); + cost_qual_eval(&qual_cost, tlist, root); plan->startup_cost += qual_cost.startup; plan->total_cost += qual_cost.startup; plan->total_cost += qual_cost.per_tuple * plan->plan_rows; @@ -3059,12 +3055,12 @@ make_group(PlannerInfo *root, */ if (qual) { - cost_qual_eval(&qual_cost, qual); + cost_qual_eval(&qual_cost, qual, root); plan->startup_cost += qual_cost.startup; plan->total_cost += qual_cost.startup; plan->total_cost += qual_cost.per_tuple * plan->plan_rows; } - cost_qual_eval(&qual_cost, tlist); + cost_qual_eval(&qual_cost, tlist, root); plan->startup_cost += qual_cost.startup; plan->total_cost += qual_cost.startup; plan->total_cost += qual_cost.per_tuple * plan->plan_rows; @@ -3108,7 +3104,7 @@ make_unique(Plan *lefttree, List *distinctList) * has a better idea. */ - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -3174,7 +3170,7 @@ make_setop(SetOpCmd cmd, Plan *lefttree, if (plan->plan_rows < 1) plan->plan_rows = 1; - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -3272,7 +3268,7 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount, plan->plan_rows = 1; } - plan->targetlist = copyObject(lefttree->targetlist); + plan->targetlist = lefttree->targetlist; plan->qual = NIL; plan->lefttree = lefttree; plan->righttree = NULL; @@ -3293,7 +3289,8 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount, * cost. In either case, tlist eval cost is not to be included here. */ Result * -make_result(List *tlist, +make_result(PlannerInfo *root, + List *tlist, Node *resconstantqual, Plan *subplan) { @@ -3312,7 +3309,7 @@ make_result(List *tlist, { QualCost qual_cost; - cost_qual_eval(&qual_cost, (List *) resconstantqual); + cost_qual_eval(&qual_cost, (List *) resconstantqual, root); /* resconstantqual is evaluated once at startup */ plan->startup_cost += qual_cost.startup + qual_cost.per_tuple; plan->total_cost += qual_cost.startup + qual_cost.per_tuple; diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index 406cc9dd496..5411072b8db 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.28 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.29 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -188,10 +188,10 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path) /* * Generate the output plan --- basically just a Result */ - plan = (Plan *) make_result(tlist, hqual, NULL); + plan = (Plan *) make_result(root, tlist, hqual, NULL); /* Account for evaluation cost of the tlist (make_result did the rest) */ - cost_qual_eval(&tlist_cost, tlist); + cost_qual_eval(&tlist_cost, tlist, root); plan->startup_cost += tlist_cost.startup; plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 3bb603a0f61..b45288dc5b8 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.214 2007/02/20 17:32:15 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.215 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -88,6 +88,8 @@ planner(Query *parse, bool isCursor, int cursorOptions, double tuple_fraction; PlannerInfo *root; Plan *top_plan; + ListCell *lp, + *lr; /* * Set up global state for this planner invocation. This data is needed @@ -99,7 +101,9 @@ planner(Query *parse, bool isCursor, int cursorOptions, glob->boundParams = boundParams; glob->paramlist = NIL; - glob->next_plan_id = 0; + glob->subplans = NIL; + glob->subrtables = NIL; + glob->finalrtable = NIL; /* Determine what fraction of the plan is likely to be scanned */ if (isCursor) @@ -132,7 +136,17 @@ planner(Query *parse, bool isCursor, int cursorOptions, } /* final cleanup of the plan */ - top_plan = set_plan_references(top_plan, parse->rtable); + Assert(glob->finalrtable == NIL); + top_plan = set_plan_references(glob, top_plan, root->parse->rtable); + /* ... and the subplans (both regular subplans and initplans) */ + Assert(list_length(glob->subplans) == list_length(glob->subrtables)); + forboth(lp, glob->subplans, lr, glob->subrtables) + { + Plan *subplan = (Plan *) lfirst(lp); + List *subrtable = (List *) lfirst(lr); + + lfirst(lp) = set_plan_references(glob, subplan, subrtable); + } /* build the PlannedStmt result */ result = makeNode(PlannedStmt); @@ -140,9 +154,10 @@ planner(Query *parse, bool isCursor, int cursorOptions, result->commandType = parse->commandType; result->canSetTag = parse->canSetTag; result->planTree = top_plan; - result->rtable = parse->rtable; + result->rtable = glob->finalrtable; result->resultRelations = root->resultRelations; result->into = parse->into; + result->subplans = glob->subplans; result->returningLists = root->returningLists; result->rowMarks = parse->rowMarks; result->nParamExec = list_length(glob->paramlist); @@ -182,7 +197,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, Index level, double tuple_fraction, PlannerInfo **subroot) { - int saved_plan_id = glob->next_plan_id; + int num_old_subplans = list_length(glob->subplans); PlannerInfo *root; Plan *plan; List *newHaving; @@ -384,7 +399,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, * initPlan list and extParam/allParam sets for plan nodes, and attach the * initPlans to the top plan node. */ - if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1) + if (list_length(glob->subplans) != num_old_subplans || + root->query_level > 1) SS_finalize_plan(root, plan); /* Return internal info if caller wants it */ @@ -621,7 +637,8 @@ inheritance_planner(PlannerInfo *root) * If we managed to exclude every child rel, return a dummy plan */ if (subplans == NIL) - return (Plan *) make_result(tlist, + return (Plan *) make_result(root, + tlist, (Node *) list_make1(makeBoolConst(false, false)), NULL); @@ -639,6 +656,10 @@ inheritance_planner(PlannerInfo *root) */ parse->rtable = rtable; + /* Suppress Append if there's only one surviving child rel */ + if (list_length(subplans) == 1) + return (Plan *) linitial(subplans); + return (Plan *) make_append(subplans, true, tlist); } @@ -897,7 +918,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ if (!is_projection_capable_plan(result_plan)) { - result_plan = (Plan *) make_result(sub_tlist, NULL, + result_plan = (Plan *) make_result(root, + sub_tlist, + NULL, result_plan); } else @@ -928,7 +951,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * tuples) --- so make_agg() and make_group() are responsible * for computing the added cost. */ - cost_qual_eval(&tlist_cost, sub_tlist); + cost_qual_eval(&tlist_cost, sub_tlist, root); result_plan->startup_cost += tlist_cost.startup; result_plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple * result_plan->plan_rows; @@ -1051,7 +1074,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * this routine to avoid having to generate the plan in the * first place. */ - result_plan = (Plan *) make_result(tlist, + result_plan = (Plan *) make_result(root, + tlist, parse->havingQual, NULL); } @@ -1110,6 +1134,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) { List *rlist; + Assert(parse->resultRelation); rlist = set_returning_clause_references(parse->returningList, result_plan, parse->resultRelation); @@ -1544,7 +1569,7 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, * pass down only c,d,a+b, but it's not really worth the trouble to * eliminate simple var references from the subplan. We will avoid doing * the extra computation to recompute a+b at the outer level; see - * replace_vars_with_subplan_refs() in setrefs.c.) + * fix_upper_expr() in setrefs.c.) * * If we are grouping or aggregating, *and* there are no non-Var grouping * expressions, then the returned tlist is effectively dummy; we do not diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 3d9f5486bcc..aaa383742e7 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.130 2007/02/19 02:23:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.131 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,46 +42,58 @@ typedef struct typedef struct { + int rtoffset; +} fix_scan_expr_context; + +typedef struct +{ indexed_tlist *outer_itlist; indexed_tlist *inner_itlist; Index acceptable_rel; -} join_references_context; + int rtoffset; +} fix_join_expr_context; typedef struct { indexed_tlist *subplan_itlist; Index subvarno; -} replace_vars_with_subplan_refs_context; + int rtoffset; +} fix_upper_expr_context; + +#define fix_scan_list(lst, rtoffset) \ + ((List *) fix_scan_expr((Node *) (lst), rtoffset)) -static Plan *set_subqueryscan_references(SubqueryScan *plan, List *rtable); +static Plan *set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset); +static Plan *set_subqueryscan_references(PlannerGlobal *glob, + SubqueryScan *plan, + int rtoffset); static bool trivial_subqueryscan(SubqueryScan *plan); -static void adjust_plan_varnos(Plan *plan, int rtoffset); -static void adjust_expr_varnos(Node *node, int rtoffset); -static bool adjust_expr_varnos_walker(Node *node, int *context); -static void fix_expr_references(Plan *plan, Node *node); -static bool fix_expr_references_walker(Node *node, void *context); -static void set_join_references(Join *join); +static Node *fix_scan_expr(Node *node, int rtoffset); +static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context); +static void set_join_references(Join *join, int rtoffset); static void set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist); -static void set_uppernode_references(Plan *plan, Index subvarno); +static void set_upper_references(Plan *plan, Index subvarno, int rtoffset); static indexed_tlist *build_tlist_index(List *tlist); static Var *search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, - Index newvarno); + Index newvarno, + int rtoffset); static Var *search_indexed_tlist_for_non_var(Node *node, indexed_tlist *itlist, Index newvarno); -static List *join_references(List *clauses, - indexed_tlist *outer_itlist, - indexed_tlist *inner_itlist, - Index acceptable_rel); -static Node *join_references_mutator(Node *node, - join_references_context *context); -static Node *replace_vars_with_subplan_refs(Node *node, - indexed_tlist *subplan_itlist, - Index subvarno); -static Node *replace_vars_with_subplan_refs_mutator(Node *node, - replace_vars_with_subplan_refs_context *context); +static List *fix_join_expr(List *clauses, + indexed_tlist *outer_itlist, + indexed_tlist *inner_itlist, + Index acceptable_rel, int rtoffset); +static Node *fix_join_expr_mutator(Node *node, + fix_join_expr_context *context); +static Node *fix_upper_expr(Node *node, + indexed_tlist *subplan_itlist, + Index subvarno, + int rtoffset); +static Node *fix_upper_expr_mutator(Node *node, + fix_upper_expr_context *context); static bool fix_opfuncids_walker(Node *node, void *context); @@ -96,34 +108,87 @@ static bool fix_opfuncids_walker(Node *node, void *context); * * This is the final processing pass of the planner/optimizer. The plan * tree is complete; we just have to adjust some representational details - * for the convenience of the executor. We update Vars in upper plan nodes - * to refer to the outputs of their subplans, and we compute regproc OIDs - * for operators (ie, we look up the function that implements each op). + * for the convenience of the executor: + * + * 1. We flatten the various subquery rangetables into a single list, and + * zero out RangeTblEntry fields that are not useful to the executor. + * + * 2. We adjust Vars in scan nodes to be consistent with the flat rangetable. + * + * 3. We adjust Vars in upper plan nodes to refer to the outputs of their + * subplans. + * + * 4. We compute regproc OIDs for operators (ie, we look up the function + * that implements each op). * * We also perform one final optimization step, which is to delete * SubqueryScan plan nodes that aren't doing anything useful (ie, have * no qual and a no-op targetlist). The reason for doing this last is that * it can't readily be done before set_plan_references, because it would - * break set_uppernode_references: the Vars in the subquery's top tlist - * won't match up with the Vars in the outer plan tree. The SubqueryScan + * break set_upper_references: the Vars in the subquery's top tlist + * wouldn't match up with the Vars in the outer plan tree. The SubqueryScan * serves a necessary function as a buffer between outer query and subquery - * variable numbering ... but the executor doesn't care about that, only the - * planner. + * variable numbering ... but after we've flattened the rangetable this is + * no longer a problem, since there's only one rtindex namespace. * * set_plan_references recursively traverses the whole plan tree. * + * Inputs: + * glob: global data for planner run + * plan: the topmost node of the plan + * rtable: the rangetable for the current subquery + * * The return value is normally the same Plan node passed in, but can be * different when the passed-in Plan is a SubqueryScan we decide isn't needed. * - * Note: to delete a SubqueryScan, we have to renumber Vars in its child nodes - * and append the modified subquery rangetable to the outer rangetable. - * Therefore "rtable" is an in/out argument and really should be declared - * "List **". But in the interest of notational simplicity we don't do that. - * (Since rtable can't be NIL if there's a SubqueryScan, the list header - * address won't change when we append a subquery rangetable.) + * The flattened rangetable entries are appended to glob->finalrtable. + * + * Notice that we modify Plan nodes in-place, but use expression_tree_mutator + * to process targetlist and qual expressions. We can assume that the Plan + * nodes were just built by the planner and are not multiply referenced, but + * it's not so safe to assume that for expression tree nodes. */ Plan * -set_plan_references(Plan *plan, List *rtable) +set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable) +{ + int rtoffset = list_length(glob->finalrtable); + ListCell *lc; + + /* + * In the flat rangetable, we zero out substructure pointers that are + * not needed by the executor; this reduces the storage space and + * copying cost for cached plans. + */ + foreach(lc, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + RangeTblEntry *newrte; + + /* flat copy to duplicate all the scalar fields */ + newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry)); + memcpy(newrte, rte, sizeof(RangeTblEntry)); + + /* zap unneeded sub-structure (we keep only the eref Alias) */ + newrte->subquery = NULL; + newrte->funcexpr = NULL; + newrte->funccoltypes = NIL; + newrte->funccoltypmods = NIL; + newrte->values_lists = NIL; + newrte->joinaliasvars = NIL; + newrte->alias = NULL; + + glob->finalrtable = lappend(glob->finalrtable, newrte); + } + + /* Now fix the Plan tree */ + return set_plan_refs(glob, plan, rtoffset); +} + +/* + * set_plan_refs: recurse through the Plan nodes of a single subquery level + */ +static Plan * +set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) { ListCell *l; @@ -136,73 +201,109 @@ set_plan_references(Plan *plan, List *rtable) switch (nodeTag(plan)) { case T_SeqScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); + { + SeqScan *splan = (SeqScan *) plan; + + splan->scanrelid += rtoffset; + splan->plan.targetlist = + fix_scan_list(splan->plan.targetlist, rtoffset); + splan->plan.qual = + fix_scan_list(splan->plan.qual, rtoffset); + } break; case T_IndexScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, - (Node *) ((IndexScan *) plan)->indexqual); - fix_expr_references(plan, - (Node *) ((IndexScan *) plan)->indexqualorig); + { + IndexScan *splan = (IndexScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->indexqual = + fix_scan_list(splan->indexqual, rtoffset); + splan->indexqualorig = + fix_scan_list(splan->indexqualorig, rtoffset); + } break; case T_BitmapIndexScan: - /* no need to fix targetlist and qual */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - fix_expr_references(plan, - (Node *) ((BitmapIndexScan *) plan)->indexqual); - fix_expr_references(plan, - (Node *) ((BitmapIndexScan *) plan)->indexqualorig); + { + BitmapIndexScan *splan = (BitmapIndexScan *) plan; + + splan->scan.scanrelid += rtoffset; + /* no need to fix targetlist and qual */ + Assert(splan->scan.plan.targetlist == NIL); + Assert(splan->scan.plan.qual == NIL); + splan->indexqual = + fix_scan_list(splan->indexqual, rtoffset); + splan->indexqualorig = + fix_scan_list(splan->indexqualorig, rtoffset); + } break; case T_BitmapHeapScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, - (Node *) ((BitmapHeapScan *) plan)->bitmapqualorig); + { + BitmapHeapScan *splan = (BitmapHeapScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->bitmapqualorig = + fix_scan_list(splan->bitmapqualorig, rtoffset); + } break; case T_TidScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, (Node *) ((TidScan *) plan)->tidquals); + { + TidScan *splan = (TidScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->tidquals = + fix_scan_list(splan->tidquals, rtoffset); + } break; case T_SubqueryScan: /* Needs special treatment, see comments below */ - return set_subqueryscan_references((SubqueryScan *) plan, rtable); + return set_subqueryscan_references(glob, + (SubqueryScan *) plan, + rtoffset); case T_FunctionScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, ((FunctionScan *) plan)->funcexpr); + { + FunctionScan *splan = (FunctionScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->funcexpr = + fix_scan_expr(splan->funcexpr, rtoffset); + } break; case T_ValuesScan: - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, - (Node *) ((ValuesScan *) plan)->values_lists); + { + ValuesScan *splan = (ValuesScan *) plan; + + splan->scan.scanrelid += rtoffset; + splan->scan.plan.targetlist = + fix_scan_list(splan->scan.plan.targetlist, rtoffset); + splan->scan.plan.qual = + fix_scan_list(splan->scan.plan.qual, rtoffset); + splan->values_lists = + fix_scan_list(splan->values_lists, rtoffset); + } break; + case T_NestLoop: - set_join_references((Join *) plan); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); - break; case T_MergeJoin: - set_join_references((Join *) plan); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); - fix_expr_references(plan, - (Node *) ((MergeJoin *) plan)->mergeclauses); - break; case T_HashJoin: - set_join_references((Join *) plan); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual); - fix_expr_references(plan, - (Node *) ((HashJoin *) plan)->hashclauses); + set_join_references((Join *) plan, rtoffset); break; + case T_Hash: case T_Material: case T_Sort: @@ -211,75 +312,113 @@ set_plan_references(Plan *plan, List *rtable) /* * These plan types don't actually bother to evaluate their - * targetlists (because they just return their unmodified input - * tuples). The optimizer is lazy about creating really valid - * targetlists for them --- it tends to just put in a pointer to - * the child plan node's tlist. Hence, we leave the tlist alone. - * In particular, we do not want to process subplans in the tlist, - * since we will likely end up reprocessing subplans that also - * appear in lower levels of the plan tree! - * + * targetlists, because they just return their unmodified input + * tuples. Even though the targetlist won't be used by the + * executor, we fix it up for possible use by EXPLAIN (not to + * mention ease of debugging --- wrong varnos are very confusing). + */ + plan->targetlist = + fix_scan_list(plan->targetlist, rtoffset); + /* * Since these plan types don't check quals either, we should not * find any qual expression attached to them. */ Assert(plan->qual == NIL); break; case T_Limit: - - /* - * Like the plan types above, Limit doesn't evaluate its tlist or - * quals. It does have live expressions for limit/offset, - * however. - */ - Assert(plan->qual == NIL); - fix_expr_references(plan, ((Limit *) plan)->limitOffset); - fix_expr_references(plan, ((Limit *) plan)->limitCount); + { + Limit *splan = (Limit *) plan; + + /* + * Like the plan types above, Limit doesn't evaluate its tlist + * or quals. It does have live expressions for limit/offset, + * however. + */ + splan->plan.targetlist = + fix_scan_list(splan->plan.targetlist, rtoffset); + Assert(splan->plan.qual == NIL); + splan->limitOffset = + fix_scan_expr(splan->limitOffset, rtoffset); + splan->limitCount = + fix_scan_expr(splan->limitCount, rtoffset); + } break; case T_Agg: case T_Group: - set_uppernode_references(plan, (Index) 0); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); + set_upper_references(plan, (Index) 0, rtoffset); break; case T_Result: - - /* - * Result may or may not have a subplan; no need to fix up subplan - * references if it hasn't got one... - * - * XXX why does Result use a different subvarno from Agg/Group? - */ - if (plan->lefttree != NULL) - set_uppernode_references(plan, (Index) OUTER); - fix_expr_references(plan, (Node *) plan->targetlist); - fix_expr_references(plan, (Node *) plan->qual); - fix_expr_references(plan, ((Result *) plan)->resconstantqual); + { + Result *splan = (Result *) plan; + + /* + * Result may or may not have a subplan; if not, it's more + * like a scan node than an upper node. + * + * XXX why does Result use a different subvarno from Agg/Group? + */ + if (splan->plan.lefttree != NULL) + set_upper_references(plan, (Index) OUTER, rtoffset); + else + { + splan->plan.targetlist = + fix_scan_list(splan->plan.targetlist, rtoffset); + splan->plan.qual = + fix_scan_list(splan->plan.qual, rtoffset); + } + /* resconstantqual can't contain any subplan variable refs */ + splan->resconstantqual = + fix_scan_expr(splan->resconstantqual, rtoffset); + } break; case T_Append: - - /* - * Append, like Sort et al, doesn't actually evaluate its - * targetlist or check quals, and we haven't bothered to give it - * its own tlist copy. So, don't fix targetlist/qual. But do - * recurse into child plans. - */ - Assert(plan->qual == NIL); - foreach(l, ((Append *) plan)->appendplans) - lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable); + { + Append *splan = (Append *) plan; + + /* + * Append, like Sort et al, doesn't actually evaluate its + * targetlist or check quals. + */ + splan->plan.targetlist = + fix_scan_list(splan->plan.targetlist, rtoffset); + Assert(splan->plan.qual == NIL); + foreach(l, splan->appendplans) + { + lfirst(l) = set_plan_refs(glob, + (Plan *) lfirst(l), + rtoffset); + } + } break; case T_BitmapAnd: - /* BitmapAnd works like Append, but has no tlist */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - foreach(l, ((BitmapAnd *) plan)->bitmapplans) - lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable); + { + BitmapAnd *splan = (BitmapAnd *) plan; + + /* BitmapAnd works like Append, but has no tlist */ + Assert(splan->plan.targetlist == NIL); + Assert(splan->plan.qual == NIL); + foreach(l, splan->bitmapplans) + { + lfirst(l) = set_plan_refs(glob, + (Plan *) lfirst(l), + rtoffset); + } + } break; case T_BitmapOr: - /* BitmapOr works like Append, but has no tlist */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - foreach(l, ((BitmapOr *) plan)->bitmapplans) - lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable); + { + BitmapOr *splan = (BitmapOr *) plan; + + /* BitmapOr works like Append, but has no tlist */ + Assert(splan->plan.targetlist == NIL); + Assert(splan->plan.qual == NIL); + foreach(l, splan->bitmapplans) + { + lfirst(l) = set_plan_refs(glob, + (Plan *) lfirst(l), + rtoffset); + } + } break; default: elog(ERROR, "unrecognized node type: %d", @@ -288,25 +427,15 @@ set_plan_references(Plan *plan, List *rtable) } /* - * Now recurse into child plans and initplans, if any + * Now recurse into child plans, if any * * NOTE: it is essential that we recurse into child plans AFTER we set * subplan references in this plan's tlist and quals. If we did the * reference-adjustments bottom-up, then we would fail to match this * plan's var nodes against the already-modified nodes of the children. - * Fortunately, that consideration doesn't apply to SubPlan nodes; else - * we'd need two passes over the expression trees. */ - plan->lefttree = set_plan_references(plan->lefttree, rtable); - plan->righttree = set_plan_references(plan->righttree, rtable); - - foreach(l, plan->initPlan) - { - SubPlan *sp = (SubPlan *) lfirst(l); - - Assert(IsA(sp, SubPlan)); - sp->plan = set_plan_references(sp->plan, sp->rtable); - } + plan->lefttree = set_plan_refs(glob, plan->lefttree, rtoffset); + plan->righttree = set_plan_refs(glob, plan->righttree, rtoffset); return plan; } @@ -319,54 +448,29 @@ set_plan_references(Plan *plan, List *rtable) * to do the normal processing on it. */ static Plan * -set_subqueryscan_references(SubqueryScan *plan, List *rtable) +set_subqueryscan_references(PlannerGlobal *glob, + SubqueryScan *plan, + int rtoffset) { Plan *result; - RangeTblEntry *rte; - ListCell *l; /* First, recursively process the subplan */ - rte = rt_fetch(plan->scan.scanrelid, rtable); - Assert(rte->rtekind == RTE_SUBQUERY); - plan->subplan = set_plan_references(plan->subplan, - rte->subquery->rtable); - - /* - * We have to process any initplans too; set_plan_references can't do it - * for us because of the possibility of double-processing. - */ - foreach(l, plan->scan.plan.initPlan) - { - SubPlan *sp = (SubPlan *) lfirst(l); + plan->subplan = set_plan_references(glob, plan->subplan, plan->subrtable); - Assert(IsA(sp, SubPlan)); - sp->plan = set_plan_references(sp->plan, sp->rtable); - } + /* subrtable is no longer needed in the plan tree */ + plan->subrtable = NIL; if (trivial_subqueryscan(plan)) { /* - * We can omit the SubqueryScan node and just pull up the subplan. We - * have to merge its rtable into the outer rtable, which means - * adjusting varnos throughout the subtree. + * We can omit the SubqueryScan node and just pull up the subplan. */ - int rtoffset = list_length(rtable); - List *sub_rtable; ListCell *lp, *lc; - sub_rtable = copyObject(rte->subquery->rtable); - rtable = list_concat(rtable, sub_rtable); - - /* - * we have to copy the subplan to make sure there are no duplicately - * linked nodes in it, else adjust_plan_varnos might increment some - * varnos twice - */ - result = copyObject(plan->subplan); - - adjust_plan_varnos(result, rtoffset); + result = plan->subplan; + /* We have to be sure we don't lose any initplans */ result->initPlan = list_concat(plan->scan.plan.initPlan, result->initPlan); @@ -391,14 +495,17 @@ set_subqueryscan_references(SubqueryScan *plan, List *rtable) /* * Keep the SubqueryScan node. We have to do the processing that * set_plan_references would otherwise have done on it. Notice we do - * not do set_uppernode_references() here, because a SubqueryScan will + * not do set_upper_references() here, because a SubqueryScan will * always have been created with correct references to its subplan's * outputs to begin with. */ - result = (Plan *) plan; + plan->scan.scanrelid += rtoffset; + plan->scan.plan.targetlist = + fix_scan_list(plan->scan.plan.targetlist, rtoffset); + plan->scan.plan.qual = + fix_scan_list(plan->scan.plan.qual, rtoffset); - fix_expr_references(result, (Node *) result->targetlist); - fix_expr_references(result, (Node *) result->qual); + result = (Plan *) plan; } return result; @@ -463,261 +570,72 @@ trivial_subqueryscan(SubqueryScan *plan) } /* - * adjust_plan_varnos - * Offset varnos and other rangetable indexes in a plan tree by rtoffset. - */ -static void -adjust_plan_varnos(Plan *plan, int rtoffset) -{ - ListCell *l; - - if (plan == NULL) - return; - - /* - * Plan-type-specific fixes - */ - switch (nodeTag(plan)) - { - case T_SeqScan: - ((SeqScan *) plan)->scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - break; - case T_IndexScan: - ((IndexScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqual, - rtoffset); - adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqualorig, - rtoffset); - break; - case T_BitmapIndexScan: - ((BitmapIndexScan *) plan)->scan.scanrelid += rtoffset; - /* no need to fix targetlist and qual */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqual, - rtoffset); - adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqualorig, - rtoffset); - break; - case T_BitmapHeapScan: - ((BitmapHeapScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig, - rtoffset); - break; - case T_TidScan: - ((TidScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((TidScan *) plan)->tidquals, - rtoffset); - break; - case T_SubqueryScan: - ((SubqueryScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - /* we should not recurse into the subquery! */ - break; - case T_FunctionScan: - ((FunctionScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos(((FunctionScan *) plan)->funcexpr, - rtoffset); - break; - case T_ValuesScan: - ((ValuesScan *) plan)->scan.scanrelid += rtoffset; - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((ValuesScan *) plan)->values_lists, - rtoffset); - break; - case T_NestLoop: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset); - break; - case T_MergeJoin: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset); - adjust_expr_varnos((Node *) ((MergeJoin *) plan)->mergeclauses, - rtoffset); - break; - case T_HashJoin: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset); - adjust_expr_varnos((Node *) ((HashJoin *) plan)->hashclauses, - rtoffset); - break; - case T_Hash: - case T_Material: - case T_Sort: - case T_Unique: - case T_SetOp: - - /* - * Even though the targetlist won't be used by the executor, we - * fix it up for possible use by EXPLAIN (not to mention ease of - * debugging --- wrong varnos are very confusing). - */ - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - Assert(plan->qual == NIL); - break; - case T_Limit: - - /* - * Like the plan types above, Limit doesn't evaluate its tlist or - * quals. It does have live expressions for limit/offset, - * however. - */ - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - Assert(plan->qual == NIL); - adjust_expr_varnos(((Limit *) plan)->limitOffset, rtoffset); - adjust_expr_varnos(((Limit *) plan)->limitCount, rtoffset); - break; - case T_Agg: - case T_Group: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - break; - case T_Result: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - adjust_expr_varnos((Node *) plan->qual, rtoffset); - adjust_expr_varnos(((Result *) plan)->resconstantqual, rtoffset); - break; - case T_Append: - adjust_expr_varnos((Node *) plan->targetlist, rtoffset); - Assert(plan->qual == NIL); - foreach(l, ((Append *) plan)->appendplans) - adjust_plan_varnos((Plan *) lfirst(l), rtoffset); - break; - case T_BitmapAnd: - /* BitmapAnd works like Append, but has no tlist */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - foreach(l, ((BitmapAnd *) plan)->bitmapplans) - adjust_plan_varnos((Plan *) lfirst(l), rtoffset); - break; - case T_BitmapOr: - /* BitmapOr works like Append, but has no tlist */ - Assert(plan->targetlist == NIL); - Assert(plan->qual == NIL); - foreach(l, ((BitmapOr *) plan)->bitmapplans) - adjust_plan_varnos((Plan *) lfirst(l), rtoffset); - break; - default: - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(plan)); - break; - } - - /* - * Now recurse into child plans. - * - * We don't need to (and in fact mustn't) recurse into subqueries, so no - * need to examine initPlan list. - */ - adjust_plan_varnos(plan->lefttree, rtoffset); - adjust_plan_varnos(plan->righttree, rtoffset); -} - -/* - * adjust_expr_varnos - * Offset varnos of Vars in an expression by rtoffset. + * fix_scan_expr + * Do set_plan_references processing on a scan-level expression * - * This is different from the rewriter's OffsetVarNodes in that it has to - * work on an already-planned expression tree; in particular, we should not - * disturb INNER and OUTER references. On the other hand, we don't have to - * recurse into subqueries nor deal with outer-level Vars, so it's pretty - * simple. + * This consists of incrementing all Vars' varnos by rtoffset and + * looking up operator opcode info for OpExpr and related nodes. */ -static void -adjust_expr_varnos(Node *node, int rtoffset) +static Node * +fix_scan_expr(Node *node, int rtoffset) { - /* This tree walk requires no special setup, so away we go... */ - adjust_expr_varnos_walker(node, &rtoffset); + fix_scan_expr_context context; + + context.rtoffset = rtoffset; + return fix_scan_expr_mutator(node, &context); } -static bool -adjust_expr_varnos_walker(Node *node, int *context) +static Node * +fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context) { if (node == NULL) - return false; + return NULL; if (IsA(node, Var)) { - Var *var = (Var *) node; + Var *var = (Var *) copyObject(node); Assert(var->varlevelsup == 0); - if (var->varno > 0 && var->varno != INNER && var->varno != OUTER) - var->varno += *context; + /* + * We should not see any Vars marked INNER, but in a nestloop inner + * scan there could be OUTER Vars. Leave them alone. + */ + Assert(var->varno != INNER); + if (var->varno > 0 && var->varno != OUTER) + var->varno += context->rtoffset; if (var->varnoold > 0) - var->varnoold += *context; - return false; + var->varnoold += context->rtoffset; + return (Node *) var; } - return expression_tree_walker(node, adjust_expr_varnos_walker, - (void *) context); -} - -/* - * fix_expr_references - * Do final cleanup on expressions (targetlists or quals). - * - * This consists of looking up operator opcode info for OpExpr nodes - * and recursively performing set_plan_references on subplans. - * - * The Plan argument is currently unused, but might be needed again someday. - */ -static void -fix_expr_references(Plan *plan, Node *node) -{ - /* This tree walk requires no special setup, so away we go... */ - fix_expr_references_walker(node, NULL); -} - -static bool -fix_expr_references_walker(Node *node, void *context) -{ - if (node == NULL) - return false; + /* + * Since we update opcode info in-place, this part could possibly + * scribble on the planner's input data structures, but it's OK. + */ if (IsA(node, OpExpr)) set_opfuncid((OpExpr *) node); else if (IsA(node, DistinctExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, ScalarArrayOpExpr)) - set_sa_opfuncid((ScalarArrayOpExpr *) node); else if (IsA(node, NullIfExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, SubPlan)) - { - SubPlan *sp = (SubPlan *) node; - - sp->plan = set_plan_references(sp->plan, sp->rtable); - } - return expression_tree_walker(node, fix_expr_references_walker, context); + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); + return expression_tree_mutator(node, fix_scan_expr_mutator, + (void *) context); } /* * set_join_references - * Modifies the target list and quals of a join node to reference its + * Modify the target list and quals of a join node to reference its * subplans, by setting the varnos to OUTER or INNER and setting attno * values to the result domain number of either the corresponding outer - * or inner join tuple item. + * or inner join tuple item. Also perform opcode lookup for these + * expressions. * * In the case of a nestloop with inner indexscan, we will also need to * apply the same transformation to any outer vars appearing in the * quals of the child indexscan. set_inner_join_references does that. - * - * 'join' is a join plan node */ static void -set_join_references(Join *join) +set_join_references(Join *join, int rtoffset) { Plan *outer_plan = join->plan.lefttree; Plan *inner_plan = join->plan.righttree; @@ -728,43 +646,47 @@ set_join_references(Join *join) inner_itlist = build_tlist_index(inner_plan->targetlist); /* All join plans have tlist, qual, and joinqual */ - join->plan.targetlist = join_references(join->plan.targetlist, - outer_itlist, - inner_itlist, - (Index) 0); - join->plan.qual = join_references(join->plan.qual, - outer_itlist, - inner_itlist, - (Index) 0); - join->joinqual = join_references(join->joinqual, - outer_itlist, - inner_itlist, - (Index) 0); + join->plan.targetlist = fix_join_expr(join->plan.targetlist, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); + join->plan.qual = fix_join_expr(join->plan.qual, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); + join->joinqual = fix_join_expr(join->joinqual, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); /* Now do join-type-specific stuff */ if (IsA(join, NestLoop)) { /* This processing is split out to handle possible recursion */ - set_inner_join_references(inner_plan, - outer_itlist); + set_inner_join_references(inner_plan, outer_itlist); } else if (IsA(join, MergeJoin)) { MergeJoin *mj = (MergeJoin *) join; - mj->mergeclauses = join_references(mj->mergeclauses, - outer_itlist, - inner_itlist, - (Index) 0); + mj->mergeclauses = fix_join_expr(mj->mergeclauses, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); } else if (IsA(join, HashJoin)) { HashJoin *hj = (HashJoin *) join; - hj->hashclauses = join_references(hj->hashclauses, - outer_itlist, - inner_itlist, - (Index) 0); + hj->hashclauses = fix_join_expr(hj->hashclauses, + outer_itlist, + inner_itlist, + (Index) 0, + rtoffset); } pfree(outer_itlist); @@ -779,6 +701,10 @@ set_join_references(Join *join) * to the bottom BitmapIndexScan nodes; likewise, appendrel indexscans * require recursing through Append nodes. This is split out as a separate * function so that it can recurse. + * + * Note we do *not* apply any rtoffset for non-join Vars; this is because + * the quals will be processed again by fix_scan_expr when the set_plan_refs + * recursion reaches the inner indexscan, and so we'd have done it twice. */ static void set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) @@ -800,14 +726,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) Index innerrel = innerscan->scan.scanrelid; /* only refs to outer vars get changed in the inner qual */ - innerscan->indexqualorig = join_references(indexqualorig, - outer_itlist, - NULL, - innerrel); - innerscan->indexqual = join_references(innerscan->indexqual, - outer_itlist, - NULL, - innerrel); + innerscan->indexqualorig = fix_join_expr(indexqualorig, + outer_itlist, + NULL, + innerrel, + 0); + innerscan->indexqual = fix_join_expr(innerscan->indexqual, + outer_itlist, + NULL, + innerrel, + 0); /* * We must fix the inner qpqual too, if it has join clauses (this @@ -815,10 +743,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * may get rechecked as qpquals). */ if (NumRelids((Node *) inner_plan->qual) > 1) - inner_plan->qual = join_references(inner_plan->qual, - outer_itlist, - NULL, - innerrel); + inner_plan->qual = fix_join_expr(inner_plan->qual, + outer_itlist, + NULL, + innerrel, + 0); } } else if (IsA(inner_plan, BitmapIndexScan)) @@ -835,14 +764,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) Index innerrel = innerscan->scan.scanrelid; /* only refs to outer vars get changed in the inner qual */ - innerscan->indexqualorig = join_references(indexqualorig, - outer_itlist, - NULL, - innerrel); - innerscan->indexqual = join_references(innerscan->indexqual, - outer_itlist, - NULL, - innerrel); + innerscan->indexqualorig = fix_join_expr(indexqualorig, + outer_itlist, + NULL, + innerrel, + 0); + innerscan->indexqual = fix_join_expr(innerscan->indexqual, + outer_itlist, + NULL, + innerrel, + 0); /* no need to fix inner qpqual */ Assert(inner_plan->qual == NIL); } @@ -862,10 +793,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) /* only refs to outer vars get changed in the inner qual */ if (NumRelids((Node *) bitmapqualorig) > 1) - innerscan->bitmapqualorig = join_references(bitmapqualorig, - outer_itlist, - NULL, - innerrel); + innerscan->bitmapqualorig = fix_join_expr(bitmapqualorig, + outer_itlist, + NULL, + innerrel, + 0); /* * We must fix the inner qpqual too, if it has join clauses (this @@ -873,14 +805,14 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * get rechecked as qpquals). */ if (NumRelids((Node *) inner_plan->qual) > 1) - inner_plan->qual = join_references(inner_plan->qual, - outer_itlist, - NULL, - innerrel); + inner_plan->qual = fix_join_expr(inner_plan->qual, + outer_itlist, + NULL, + innerrel, + 0); /* Now recurse */ - set_inner_join_references(inner_plan->lefttree, - outer_itlist); + set_inner_join_references(inner_plan->lefttree, outer_itlist); } else if (IsA(inner_plan, BitmapAnd)) { @@ -890,8 +822,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, innerscan->bitmapplans) { - set_inner_join_references((Plan *) lfirst(l), - outer_itlist); + set_inner_join_references((Plan *) lfirst(l), outer_itlist); } } else if (IsA(inner_plan, BitmapOr)) @@ -902,10 +833,20 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, innerscan->bitmapplans) { - set_inner_join_references((Plan *) lfirst(l), - outer_itlist); + set_inner_join_references((Plan *) lfirst(l), outer_itlist); } } + else if (IsA(inner_plan, TidScan)) + { + TidScan *innerscan = (TidScan *) inner_plan; + Index innerrel = innerscan->scan.scanrelid; + + innerscan->tidquals = fix_join_expr(innerscan->tidquals, + outer_itlist, + NULL, + innerrel, + 0); + } else if (IsA(inner_plan, Append)) { /* @@ -917,8 +858,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, appendplan->appendplans) { - set_inner_join_references((Plan *) lfirst(l), - outer_itlist); + set_inner_join_references((Plan *) lfirst(l), outer_itlist); } } else if (IsA(inner_plan, Result)) @@ -929,22 +869,13 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) if (result->plan.lefttree) set_inner_join_references(result->plan.lefttree, outer_itlist); } - else if (IsA(inner_plan, TidScan)) - { - TidScan *innerscan = (TidScan *) inner_plan; - Index innerrel = innerscan->scan.scanrelid; - - innerscan->tidquals = join_references(innerscan->tidquals, - outer_itlist, - NULL, - innerrel); - } } /* - * set_uppernode_references + * set_upper_references * Update the targetlist and quals of an upper-level plan node * to refer to the tuples returned by its lefttree subplan. + * Also perform opcode lookup for these expressions. * * This is used for single-input plan types like Agg, Group, Result. * @@ -958,7 +889,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * the expression. */ static void -set_uppernode_references(Plan *plan, Index subvarno) +set_upper_references(Plan *plan, Index subvarno, int rtoffset) { Plan *subplan = plan->lefttree; indexed_tlist *subplan_itlist; @@ -976,9 +907,10 @@ set_uppernode_references(Plan *plan, Index subvarno) TargetEntry *tle = (TargetEntry *) lfirst(l); Node *newexpr; - newexpr = replace_vars_with_subplan_refs((Node *) tle->expr, - subplan_itlist, - subvarno); + newexpr = fix_upper_expr((Node *) tle->expr, + subplan_itlist, + subvarno, + rtoffset); tle = flatCopyTargetEntry(tle); tle->expr = (Expr *) newexpr; output_targetlist = lappend(output_targetlist, tle); @@ -986,9 +918,10 @@ set_uppernode_references(Plan *plan, Index subvarno) plan->targetlist = output_targetlist; plan->qual = (List *) - replace_vars_with_subplan_refs((Node *) plan->qual, - subplan_itlist, - subvarno); + fix_upper_expr((Node *) plan->qual, + subplan_itlist, + subvarno, + rtoffset); pfree(subplan_itlist); } @@ -1096,10 +1029,12 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel) * * If a match is found, return a copy of the given Var with suitably * modified varno/varattno (to wit, newvarno and the resno of the TLE entry). + * Also ensure that varnoold is incremented by rtoffset. * If no match, return NULL. */ static Var * -search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno) +search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, + Index newvarno, int rtoffset) { Index varno = var->varno; AttrNumber varattno = var->varattno; @@ -1117,6 +1052,8 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno) newvar->varno = newvarno; newvar->varattno = vinfo->resno; + if (newvar->varnoold > 0) + newvar->varnoold += rtoffset; return newvar; } vinfo++; @@ -1157,22 +1094,23 @@ search_indexed_tlist_for_non_var(Node *node, } /* - * join_references - * Creates a new set of targetlist entries or join qual clauses by + * fix_join_expr + * Create a new set of targetlist entries or join qual clauses by * changing the varno/varattno values of variables in the clauses * to reference target list values from the outer and inner join - * relation target lists. + * relation target lists. Also perform opcode lookup. * * This is used in two different scenarios: a normal join clause, where * all the Vars in the clause *must* be replaced by OUTER or INNER references; * and an indexscan being used on the inner side of a nestloop join. * In the latter case we want to replace the outer-relation Vars by OUTER - * references, but not touch the Vars of the inner relation. (We also - * implement RETURNING clause fixup using this second scenario.) + * references, while Vars of the inner relation should be adjusted by rtoffset. + * (We also implement RETURNING clause fixup using this second scenario.) * * For a normal join, acceptable_rel should be zero so that any failure to * match a Var will be reported as an error. For the indexscan case, - * pass inner_itlist = NULL and acceptable_rel = the ID of the inner relation. + * pass inner_itlist = NULL and acceptable_rel = the (not-offseted-yet) ID + * of the inner relation. * * 'clauses' is the targetlist or list of join clauses * 'outer_itlist' is the indexed target list of the outer join relation @@ -1180,27 +1118,29 @@ search_indexed_tlist_for_non_var(Node *node, * or NULL * 'acceptable_rel' is either zero or the rangetable index of a relation * whose Vars may appear in the clause without provoking an error. + * 'rtoffset' is what to add to varno for Vars of acceptable_rel. * * Returns the new expression tree. The original clause structure is * not modified. */ static List * -join_references(List *clauses, - indexed_tlist *outer_itlist, - indexed_tlist *inner_itlist, - Index acceptable_rel) +fix_join_expr(List *clauses, + indexed_tlist *outer_itlist, + indexed_tlist *inner_itlist, + Index acceptable_rel, + int rtoffset) { - join_references_context context; + fix_join_expr_context context; context.outer_itlist = outer_itlist; context.inner_itlist = inner_itlist; context.acceptable_rel = acceptable_rel; - return (List *) join_references_mutator((Node *) clauses, &context); + context.rtoffset = rtoffset; + return (List *) fix_join_expr_mutator((Node *) clauses, &context); } static Node * -join_references_mutator(Node *node, - join_references_context *context) +fix_join_expr_mutator(Node *node, fix_join_expr_context *context) { Var *newvar; @@ -1213,21 +1153,28 @@ join_references_mutator(Node *node, /* First look for the var in the input tlists */ newvar = search_indexed_tlist_for_var(var, context->outer_itlist, - OUTER); + OUTER, + context->rtoffset); if (newvar) return (Node *) newvar; if (context->inner_itlist) { newvar = search_indexed_tlist_for_var(var, context->inner_itlist, - INNER); + INNER, + context->rtoffset); if (newvar) return (Node *) newvar; } - /* Return the Var unmodified, if it's for acceptable_rel */ + /* If it's for acceptable_rel, adjust and return it */ if (var->varno == context->acceptable_rel) - return (Node *) copyObject(var); + { + var = (Var *) copyObject(var); + var->varno += context->rtoffset; + var->varnoold += context->rtoffset; + return (Node *) var; + } /* No referent found for Var */ elog(ERROR, "variable not found in subplan target lists"); @@ -1249,16 +1196,30 @@ join_references_mutator(Node *node, if (newvar) return (Node *) newvar; } + /* + * Since we update opcode info in-place, this part could possibly + * scribble on the planner's input data structures, but it's OK. + */ + if (IsA(node, OpExpr)) + set_opfuncid((OpExpr *) node); + else if (IsA(node, DistinctExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, NullIfExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); return expression_tree_mutator(node, - join_references_mutator, + fix_join_expr_mutator, (void *) context); } /* - * replace_vars_with_subplan_refs - * This routine modifies an expression tree so that all Var nodes - * reference target nodes of a subplan. It is used to fix up - * target and qual expressions of non-join upper-level plan nodes. + * fix_upper_expr + * Modifies an expression tree so that all Var nodes reference outputs + * of a subplan. Also performs opcode lookup. + * + * This is used to fix up target and qual expressions of non-join upper-level + * plan nodes. * * An error is raised if no matching var can be found in the subplan tlist * --- so this routine should only be applied to nodes whose subplans' @@ -1273,26 +1234,28 @@ join_references_mutator(Node *node, * 'node': the tree to be fixed (a target item or qual) * 'subplan_itlist': indexed target list for subplan * 'subvarno': varno to be assigned to all Vars + * 'rtoffset': how much to increment varnoold by * * The resulting tree is a copy of the original in which all Var nodes have * varno = subvarno, varattno = resno of corresponding subplan target. * The original tree is not modified. */ static Node * -replace_vars_with_subplan_refs(Node *node, - indexed_tlist *subplan_itlist, - Index subvarno) +fix_upper_expr(Node *node, + indexed_tlist *subplan_itlist, + Index subvarno, + int rtoffset) { - replace_vars_with_subplan_refs_context context; + fix_upper_expr_context context; context.subplan_itlist = subplan_itlist; context.subvarno = subvarno; - return replace_vars_with_subplan_refs_mutator(node, &context); + context.rtoffset = rtoffset; + return fix_upper_expr_mutator(node, &context); } static Node * -replace_vars_with_subplan_refs_mutator(Node *node, - replace_vars_with_subplan_refs_context *context) +fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) { Var *newvar; @@ -1304,7 +1267,8 @@ replace_vars_with_subplan_refs_mutator(Node *node, newvar = search_indexed_tlist_for_var(var, context->subplan_itlist, - context->subvarno); + context->subvarno, + context->rtoffset); if (!newvar) elog(ERROR, "variable not found in subplan target list"); return (Node *) newvar; @@ -1318,8 +1282,20 @@ replace_vars_with_subplan_refs_mutator(Node *node, if (newvar) return (Node *) newvar; } + /* + * Since we update opcode info in-place, this part could possibly + * scribble on the planner's input data structures, but it's OK. + */ + if (IsA(node, OpExpr)) + set_opfuncid((OpExpr *) node); + else if (IsA(node, DistinctExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, NullIfExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); return expression_tree_mutator(node, - replace_vars_with_subplan_refs_mutator, + fix_upper_expr_mutator, (void *) context); } @@ -1335,12 +1311,15 @@ replace_vars_with_subplan_refs_mutator(Node *node, * adjusted RETURNING list, result-table Vars will still have their * original varno, but Vars for other rels will have varno OUTER. * - * We also must apply fix_expr_references to the list. + * We also must perform opcode lookup. * * 'rlist': the RETURNING targetlist to be fixed * 'topplan': the top Plan node for the query (not yet passed through * set_plan_references) - * 'resultRelation': RT index of the query's result relation + * 'resultRelation': RT index of the associated result relation + * + * Note: we assume that result relations will have rtoffset zero, that is, + * they are not coming from a subplan. */ List * set_returning_clause_references(List *rlist, @@ -1350,20 +1329,19 @@ set_returning_clause_references(List *rlist, indexed_tlist *itlist; /* - * We can perform the desired Var fixup by abusing the join_references + * We can perform the desired Var fixup by abusing the fix_join_expr * machinery that normally handles inner indexscan fixup. We search the * top plan's targetlist for Vars of non-result relations, and use - * join_references to convert RETURNING Vars into references to those + * fix_join_expr to convert RETURNING Vars into references to those * tlist entries, while leaving result-rel Vars as-is. */ itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation); - rlist = join_references(rlist, - itlist, - NULL, - resultRelation); - - fix_expr_references(topplan, (Node *) rlist); + rlist = fix_join_expr(rlist, + itlist, + NULL, + resultRelation, + 0); pfree(itlist); @@ -1399,10 +1377,10 @@ fix_opfuncids_walker(Node *node, void *context) set_opfuncid((OpExpr *) node); else if (IsA(node, DistinctExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, ScalarArrayOpExpr)) - set_sa_opfuncid((ScalarArrayOpExpr *) node); else if (IsA(node, NullIfExpr)) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); return expression_tree_walker(node, fix_opfuncids_walker, context); } diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index b62aeb853d8..d19e9d298c0 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.120 2007/02/19 07:03:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.121 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -46,6 +46,7 @@ typedef struct process_sublinks_context typedef struct finalize_primnode_context { + PlannerInfo *root; Bitmapset *paramids; /* Set of PARAM_EXEC paramids found */ Bitmapset *outer_params; /* Set of accessible outer paramids */ } finalize_primnode_context; @@ -57,12 +58,13 @@ static Node *convert_testexpr(PlannerInfo *root, List **righthandIds); static Node *convert_testexpr_mutator(Node *node, convert_testexpr_context *context); -static bool subplan_is_hashable(SubLink *slink, SubPlan *node); +static bool subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan); static bool hash_ok_operator(OpExpr *expr); static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root); static Node *process_sublinks_mutator(Node *node, process_sublinks_context *context); -static Bitmapset *finalize_plan(Plan *plan, List *rtable, +static Bitmapset *finalize_plan(PlannerInfo *root, + Plan *plan, Bitmapset *outer_params, Bitmapset *valid_params); static bool finalize_primnode(Node *node, finalize_primnode_context *context); @@ -204,6 +206,24 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod) } /* + * Get the datatype of the first column of the plan's output. + * + * This is a hack to support exprType(), which doesn't have any way to get + * at the plan associated with a SubPlan node. We really only need the value + * for EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency we set + * it always. + */ +static Oid +get_first_col_type(Plan *plan) +{ + TargetEntry *tent = (TargetEntry *) linitial(plan->targetlist); + + Assert(IsA(tent, TargetEntry)); + Assert(!tent->resjunk); + return exprType((Node *) tent->expr); +} + +/* * Convert a SubLink (as created by the parser) into a SubPlan. * * We are given the original SubLink and the already-processed testexpr @@ -219,10 +239,11 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod) static Node * make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) { - SubPlan *node = makeNode(SubPlan); Query *subquery = (Query *) (slink->subselect); double tuple_fraction; + SubPlan *node; Plan *plan; + PlannerInfo *subroot; Bitmapset *tmpset; int paramid; Node *result; @@ -266,22 +287,19 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) /* * Generate the plan for the subquery. */ - node->plan = plan = subquery_planner(root->glob, subquery, - root->query_level + 1, - tuple_fraction, - NULL); - - /* Assign quasi-unique ID to this SubPlan */ - node->plan_id = root->glob->next_plan_id++; - - node->rtable = subquery->rtable; + plan = subquery_planner(root->glob, subquery, + root->query_level + 1, + tuple_fraction, + &subroot); /* - * Initialize other fields of the SubPlan node. + * Initialize the SubPlan node. Note plan_id isn't set yet. */ + node = makeNode(SubPlan); node->subLinkType = slink->subLinkType; node->testexpr = NULL; node->paramIds = NIL; + node->firstColType = get_first_col_type(plan); node->useHashTable = false; /* At top level of a qual, can treat UNKNOWN the same as FALSE */ node->unknownEqFalse = isTopQual; @@ -384,7 +402,7 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) * tuple. But if it's an IN (= ANY) test, we might be able to use a * hashtable to avoid comparing all the tuples. */ - if (subplan_is_hashable(slink, node)) + if (subplan_is_hashable(slink, node, plan)) node->useHashTable = true; /* @@ -411,7 +429,7 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) break; } if (use_material) - node->plan = plan = materialize_finished_plan(plan); + plan = materialize_finished_plan(plan); } /* @@ -435,6 +453,15 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual) result = (Node *) node; } + /* + * Add the subplan and its rtable to the global lists. + */ + root->glob->subplans = lappend(root->glob->subplans, + plan); + root->glob->subrtables = lappend(root->glob->subrtables, + subroot->parse->rtable); + node->plan_id = list_length(root->glob->subplans); + return result; } @@ -539,7 +566,7 @@ convert_testexpr_mutator(Node *node, * on its plan and parParam fields, however. */ static bool -subplan_is_hashable(SubLink *slink, SubPlan *node) +subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan) { double subquery_size; ListCell *l; @@ -574,8 +601,8 @@ subplan_is_hashable(SubLink *slink, SubPlan *node) * actually be stored as MinimalTuples; this provides some fudge factor * for hashtable overhead.) */ - subquery_size = node->plan->plan_rows * - (MAXALIGN(node->plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData))); + subquery_size = plan->plan_rows * + (MAXALIGN(plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData))); if (subquery_size > work_mem * 1024L) return false; @@ -964,7 +991,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan) /* * Now recurse through plan tree. */ - (void) finalize_plan(plan, root->parse->rtable, outer_params, valid_params); + (void) finalize_plan(root, plan, outer_params, valid_params); bms_free(outer_params); bms_free(valid_params); @@ -988,16 +1015,16 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan) initplan_cost = 0; foreach(l, plan->initPlan) { - SubPlan *initplan = (SubPlan *) lfirst(l); + SubPlan *initsubplan = (SubPlan *) lfirst(l); + Plan *initplan = planner_subplan_get_plan(root, initsubplan); ListCell *l2; - initExtParam = bms_add_members(initExtParam, - initplan->plan->extParam); - foreach(l2, initplan->setParam) + initExtParam = bms_add_members(initExtParam, initplan->extParam); + foreach(l2, initsubplan->setParam) { initSetParam = bms_add_member(initSetParam, lfirst_int(l2)); } - initplan_cost += initplan->plan->total_cost; + initplan_cost += initplan->total_cost; } /* allParam must include all these params */ plan->allParam = bms_add_members(plan->allParam, initExtParam); @@ -1019,7 +1046,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan) * This is just an internal notational convenience. */ static Bitmapset * -finalize_plan(Plan *plan, List *rtable, +finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *outer_params, Bitmapset *valid_params) { finalize_primnode_context context; @@ -1027,6 +1054,7 @@ finalize_plan(Plan *plan, List *rtable, if (plan == NULL) return NULL; + context.root = root; context.paramids = NULL; /* initialize set to empty */ context.outer_params = outer_params; @@ -1110,8 +1138,8 @@ finalize_plan(Plan *plan, List *rtable, { context.paramids = bms_add_members(context.paramids, - finalize_plan((Plan *) lfirst(l), - rtable, + finalize_plan(root, + (Plan *) lfirst(l), outer_params, valid_params)); } @@ -1126,8 +1154,8 @@ finalize_plan(Plan *plan, List *rtable, { context.paramids = bms_add_members(context.paramids, - finalize_plan((Plan *) lfirst(l), - rtable, + finalize_plan(root, + (Plan *) lfirst(l), outer_params, valid_params)); } @@ -1142,8 +1170,8 @@ finalize_plan(Plan *plan, List *rtable, { context.paramids = bms_add_members(context.paramids, - finalize_plan((Plan *) lfirst(l), - rtable, + finalize_plan(root, + (Plan *) lfirst(l), outer_params, valid_params)); } @@ -1193,14 +1221,14 @@ finalize_plan(Plan *plan, List *rtable, /* Process left and right child plans, if any */ context.paramids = bms_add_members(context.paramids, - finalize_plan(plan->lefttree, - rtable, + finalize_plan(root, + plan->lefttree, outer_params, valid_params)); context.paramids = bms_add_members(context.paramids, - finalize_plan(plan->righttree, - rtable, + finalize_plan(root, + plan->righttree, outer_params, valid_params)); @@ -1252,10 +1280,11 @@ finalize_primnode(Node *node, finalize_primnode_context *context) if (is_subplan(node)) { SubPlan *subplan = (SubPlan *) node; + Plan *plan = planner_subplan_get_plan(context->root, subplan); /* Add outer-level params needed by the subplan to paramids */ context->paramids = bms_join(context->paramids, - bms_intersect(subplan->plan->extParam, + bms_intersect(plan->extParam, context->outer_params)); /* fall through to recurse into subplan args */ } @@ -1300,15 +1329,20 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, root->init_plans = saved_init_plans; /* + * Add the subplan and its rtable to the global lists. + */ + root->glob->subplans = lappend(root->glob->subplans, + plan); + root->glob->subrtables = lappend(root->glob->subrtables, + root->parse->rtable); + + /* * Create a SubPlan node and add it to the outer list of InitPlans. */ node = makeNode(SubPlan); node->subLinkType = EXPR_SUBLINK; - node->plan = plan; - /* Assign quasi-unique ID to this SubPlan */ - node->plan_id = root->glob->next_plan_id++; - - node->rtable = root->parse->rtable; + node->firstColType = get_first_col_type(plan); + node->plan_id = list_length(root->glob->subplans); root->init_plans = lappend(root->init_plans, node); diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 2f06e1bf532..f09ddb1d23d 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.138 2007/02/19 07:03:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.139 2007/02/22 22:00:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -169,6 +169,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable); Query *subquery = rte->subquery; + PlannerInfo *subroot; Plan *subplan, *plan; @@ -180,7 +181,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, subplan = subquery_planner(root->glob, subquery, root->query_level + 1, tuple_fraction, - NULL); + &subroot); /* * Add a SubqueryScan with the caller-requested targetlist @@ -193,7 +194,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, refnames_tlist), NIL, rtr->rtindex, - subplan); + subplan, + subroot->parse->rtable); /* * We don't bother to determine the subquery's output ordering since @@ -223,7 +225,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, * output columns. * * XXX you don't really want to know about this: setrefs.c will apply - * replace_vars_with_subplan_refs() to the Result node's tlist. This + * fix_upper_expr() to the Result node's tlist. This * would fail if the Vars generated by generate_setop_tlist() were not * exactly equal() to the corresponding tlist entries of the subplan. * However, since the subplan was generated by generate_union_plan() @@ -235,7 +237,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, !tlist_same_datatypes(plan->targetlist, colTypes, junkOK)) { plan = (Plan *) - make_result(generate_setop_tlist(colTypes, flag, + make_result(root, + generate_setop_tlist(colTypes, flag, 0, false, plan->targetlist, @@ -1216,28 +1219,6 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context) Assert(!IsA(node, SubLink)); Assert(!IsA(node, Query)); - /* - * BUT: although we don't need to recurse into subplans, we do need to - * make sure that they are copied, not just referenced as - * expression_tree_mutator will do by default. Otherwise we'll have the - * same subplan node referenced from each arm of the finished APPEND plan, - * which will cause trouble in the executor. This is a kluge that should - * go away when we redesign querytrees. - */ - if (is_subplan(node)) - { - SubPlan *subplan; - - /* Copy the node and process subplan args */ - node = expression_tree_mutator(node, adjust_appendrel_attrs_mutator, - (void *) context); - /* Make sure we have separate copies of subplan and its rtable */ - subplan = (SubPlan *) node; - subplan->plan = copyObject(subplan->plan); - subplan->rtable = copyObject(subplan->rtable); - return node; - } - return expression_tree_mutator(node, adjust_appendrel_attrs_mutator, (void *) context); } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f90d5500197..852b0f533e7 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.235 2007/02/19 07:03:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.236 2007/02/22 22:00:24 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2989,7 +2989,7 @@ inline_function(Oid funcid, Oid result_type, List *args, */ if (contain_subplans(param)) goto fail; - cost_qual_eval(&eval_cost, list_make1(param)); + cost_qual_eval(&eval_cost, list_make1(param), NULL); if (eval_cost.startup + eval_cost.per_tuple > 10 * cpu_operator_cost) goto fail; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 8cc6c81746f..6dfd2f61149 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.85 2007/01/20 20:45:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.86 2007/02/22 22:00:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -82,6 +82,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) rel->pages = 0; rel->tuples = 0; rel->subplan = NULL; + rel->subrtable = NIL; rel->baserestrictinfo = NIL; rel->baserestrictcost.startup = 0; rel->baserestrictcost.per_tuple = 0; @@ -333,6 +334,7 @@ build_join_rel(PlannerInfo *root, joinrel->pages = 0; joinrel->tuples = 0; joinrel->subplan = NULL; + joinrel->subrtable = NIL; joinrel->baserestrictinfo = NIL; joinrel->baserestrictcost.startup = 0; joinrel->baserestrictcost.per_tuple = 0; |