diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index f3873872891..f087369f753 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -60,30 +60,29 @@ typedef struct AppendRelInfo **appinfos; } adjust_appendrel_attrs_context; -static Path *recurse_set_operations(Node *setOp, PlannerInfo *root, +static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root, List *colTypes, List *colCollations, bool junkOK, int flag, List *refnames_tlist, List **pTargetList, double *pNumGroups); -static Path *generate_recursion_path(SetOperationStmt *setOp, +static RelOptInfo *generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, List *refnames_tlist, List **pTargetList); -static Path *generate_union_path(SetOperationStmt *op, PlannerInfo *root, - List *refnames_tlist, - List **pTargetList, - double *pNumGroups); -static Path *generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root, - List *refnames_tlist, - List **pTargetList, - double *pNumGroups); +static RelOptInfo *generate_union_paths(SetOperationStmt *op, PlannerInfo *root, + List *refnames_tlist, + List **pTargetList); +static RelOptInfo *generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root, + List *refnames_tlist, + List **pTargetList); static List *plan_union_children(PlannerInfo *root, SetOperationStmt *top_union, List *refnames_tlist, List **tlist_list); static Path *make_union_unique(SetOperationStmt *op, Path *path, List *tlist, PlannerInfo *root); +static void postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel); static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses, Path *input_path, double dNumGroups, double dNumOutputRows, @@ -149,7 +148,6 @@ plan_set_operations(PlannerInfo *root) RangeTblEntry *leftmostRTE; Query *leftmostQuery; RelOptInfo *setop_rel; - Path *path; List *top_tlist; Assert(topop); @@ -181,57 +179,34 @@ plan_set_operations(PlannerInfo *root) leftmostQuery = leftmostRTE->subquery; Assert(leftmostQuery != NULL); - /* - * We return our results in the (SETOP, NULL) upperrel. For the moment, - * this is also the parent rel of all Paths in the setop tree; we may well - * change that in future. - */ - setop_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL); - - /* - * We don't currently worry about setting setop_rel's consider_parallel - * flag, nor about allowing FDWs to contribute paths to it. - */ - /* * If the topmost node is a recursive union, it needs special processing. */ if (root->hasRecursion) { - path = generate_recursion_path(topop, root, - leftmostQuery->targetList, - &top_tlist); + setop_rel = generate_recursion_path(topop, root, + leftmostQuery->targetList, + &top_tlist); } else { /* * Recurse on setOperations tree to generate paths for set ops. The - * final output path should have just the column types shown as the + * final output paths should have just the column types shown as the * output from the top-level node, plus possibly resjunk working * columns (we can rely on upper-level nodes to deal with that). */ - path = recurse_set_operations((Node *) topop, root, - topop->colTypes, topop->colCollations, - true, -1, - leftmostQuery->targetList, - &top_tlist, - NULL); + setop_rel = recurse_set_operations((Node *) topop, root, + topop->colTypes, topop->colCollations, + true, -1, + leftmostQuery->targetList, + &top_tlist, + NULL); } /* Must return the built tlist into root->processed_tlist. */ root->processed_tlist = top_tlist; - /* Add only the final path to the SETOP upperrel. */ - add_path(setop_rel, path); - - /* Let extensions possibly add some more paths */ - if (create_upper_paths_hook) - (*create_upper_paths_hook) (root, UPPERREL_SETOP, - NULL, setop_rel); - - /* Select cheapest path */ - set_cheapest(setop_rel); - return setop_rel; } @@ -245,21 +220,21 @@ plan_set_operations(PlannerInfo *root) * flag: if >= 0, add a resjunk output column indicating value of flag * refnames_tlist: targetlist to take column names from * - * Returns a path for the subtree, as well as these output parameters: + * Returns a RelOptInfo for the subtree, as well as these output parameters: * *pTargetList: receives the fully-fledged tlist for the subtree's top plan * *pNumGroups: if not NULL, we estimate the number of distinct groups * in the result, and store it there * * The pTargetList output parameter is mostly redundant with the pathtarget - * of the returned path, but for the moment we need it because much of the - * logic in this file depends on flag columns being marked resjunk. Pending - * a redesign of how that works, this is the easy way out. + * of the returned RelOptInfo, but for the moment we need it because much of + * the logic in this file depends on flag columns being marked resjunk. + * Pending a redesign of how that works, this is the easy way out. * * We don't have to care about typmods here: the only allowed difference * between set-op input and output typmods is input is a specific typmod * and output is -1, and that does not require a coercion. */ -static Path * +static RelOptInfo * recurse_set_operations(Node *setOp, PlannerInfo *root, List *colTypes, List *colCollations, bool junkOK, @@ -267,6 +242,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, List **pTargetList, double *pNumGroups) { + RelOptInfo *rel = NULL; /* keep compiler quiet */ + /* Guard against stack overflow due to overly complex setop nests */ check_stack_depth(); @@ -275,7 +252,6 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex]; Query *subquery = rte->subquery; - RelOptInfo *rel; PlannerInfo *subroot; RelOptInfo *final_rel; Path *subpath; @@ -284,11 +260,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, Assert(subquery != NULL); - /* - * We need to build a RelOptInfo for each leaf subquery. This isn't - * used for much here, but it carries the subroot data structures - * forward to setrefs.c processing. - */ + /* Build a RelOptInfo for this leaf subquery. */ rel = build_simple_rel(root, rtr->rtindex, NULL); /* plan_params should not be in use in current query level */ @@ -307,6 +279,18 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, if (root->plan_params) elog(ERROR, "unexpected outer reference in set operation subquery"); + /* Figure out the appropriate target list for this subquery. */ + tlist = generate_setop_tlist(colTypes, colCollations, + flag, + rtr->rtindex, + true, + subroot->processed_tlist, + refnames_tlist); + rel->reltarget = create_pathtarget(root, tlist); + + /* Return the fully-fledged tlist to caller, too */ + *pTargetList = tlist; + /* * Mark rel with estimated output rows, width, etc. Note that we have * to do this before generating outer-query paths, else @@ -334,22 +318,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, path = (Path *) create_subqueryscan_path(root, rel, subpath, NIL, NULL); - /* - * Figure out the appropriate target list, and update the - * SubqueryScanPath with the PathTarget form of that. - */ - tlist = generate_setop_tlist(colTypes, colCollations, - flag, - rtr->rtindex, - true, - subroot->processed_tlist, - refnames_tlist); - - path = apply_projection_to_path(root, rel, path, - create_pathtarget(root, tlist)); - - /* Return the fully-fledged tlist to caller, too */ - *pTargetList = tlist; + add_path(rel, path); /* * Estimate number of groups if caller wants it. If the subquery used @@ -378,25 +347,22 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, subpath->rows, NULL); } - - return (Path *) path; } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; - Path *path; /* UNIONs are much different from INTERSECT/EXCEPT */ if (op->op == SETOP_UNION) - path = generate_union_path(op, root, + rel = generate_union_paths(op, root, refnames_tlist, - pTargetList, - pNumGroups); + pTargetList); else - path = generate_nonunion_path(op, root, + rel = generate_nonunion_paths(op, root, refnames_tlist, - pTargetList, - pNumGroups); + pTargetList); + if (pNumGroups) + *pNumGroups = rel->rows; /* * If necessary, add a Result node to project the caller-requested @@ -415,39 +381,70 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, !tlist_same_datatypes(*pTargetList, colTypes, junkOK) || !tlist_same_collations(*pTargetList, colCollations, junkOK)) { + PathTarget *target; + ListCell *lc; + *pTargetList = generate_setop_tlist(colTypes, colCollations, flag, 0, false, *pTargetList, refnames_tlist); - path = apply_projection_to_path(root, - path->parent, - path, - create_pathtarget(root, - *pTargetList)); + target = create_pathtarget(root, *pTargetList); + + /* Apply projection to each path */ + foreach(lc, rel->pathlist) + { + Path *subpath = (Path *) lfirst(lc); + Path *path; + + Assert(subpath->param_info == NULL); + path = apply_projection_to_path(root, subpath->parent, + subpath, target); + /* If we had to add a Result, path is different from subpath */ + if (path != subpath) + lfirst(lc) = path; + } + + /* Apply projection to each partial path */ + foreach(lc, rel->partial_pathlist) + { + Path *subpath = (Path *) lfirst(lc); + Path *path; + + Assert(subpath->param_info == NULL); + + /* avoid apply_projection_to_path, in case of multiple refs */ + path = (Path *) create_projection_path(root, subpath->parent, + subpath, target); + lfirst(lc) = path; + } } - return path; } else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(setOp)); *pTargetList = NIL; - return NULL; /* keep compiler quiet */ } + + postprocess_setop_rel(root, rel); + + return rel; } /* - * Generate path for a recursive UNION node + * Generate paths for a recursive UNION node */ -static Path * +static RelOptInfo * generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, List *refnames_tlist, List **pTargetList) { - RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL); + RelOptInfo *result_rel; Path *path; + RelOptInfo *lrel, + *rrel; Path *lpath; Path *rpath; List *lpath_tlist; @@ -466,20 +463,22 @@ generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, * Unlike a regular UNION node, process the left and right inputs * separately without any intention of combining them into one Append. */ - lpath = recurse_set_operations(setOp->larg, root, - setOp->colTypes, setOp->colCollations, - false, -1, - refnames_tlist, - &lpath_tlist, - NULL); + lrel = recurse_set_operations(setOp->larg, root, + setOp->colTypes, setOp->colCollations, + false, -1, + refnames_tlist, + &lpath_tlist, + NULL); + lpath = lrel->cheapest_total_path; /* The right path will want to look at the left one ... */ root->non_recursive_path = lpath; - rpath = recurse_set_operations(setOp->rarg, root, - setOp->colTypes, setOp->colCollations, - false, -1, - refnames_tlist, - &rpath_tlist, - NULL); + rrel = recurse_set_operations(setOp->rarg, root, + setOp->colTypes, setOp->colCollations, + false, -1, + refnames_tlist, + &rpath_tlist, + NULL); + rpath = rrel->cheapest_total_path; root->non_recursive_path = NULL; /* @@ -491,6 +490,11 @@ generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, *pTargetList = tlist; + /* Build result relation. */ + result_rel = fetch_upper_rel(root, UPPERREL_SETOP, + bms_union(lrel->relids, rrel->relids)); + result_rel->reltarget = create_pathtarget(root, tlist); + /* * If UNION, identify the grouping operators */ @@ -525,26 +529,30 @@ generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root, result_rel, lpath, rpath, - create_pathtarget(root, tlist), + result_rel->reltarget, groupList, root->wt_param_id, dNumGroups); - return path; + add_path(result_rel, path); + postprocess_setop_rel(root, result_rel); + return result_rel; } /* - * Generate path for a UNION or UNION ALL node + * Generate paths for a UNION or UNION ALL node */ -static Path * -generate_union_path(SetOperationStmt *op, PlannerInfo *root, - List *refnames_tlist, - List **pTargetList, - double *pNumGroups) +static RelOptInfo * +generate_union_paths(SetOperationStmt *op, PlannerInfo *root, + List *refnames_tlist, + List **pTargetList) { - RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL); + Relids relids = NULL; + RelOptInfo *result_rel; double save_fraction = root->tuple_fraction; - List *pathlist; + ListCell *lc; + List *pathlist = NIL; + List *rellist; List *tlist_list; List *tlist; Path *path; @@ -569,7 +577,7 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root, * only one Append and unique-ification for the lot. Recurse to find such * nodes and compute their children's paths. */ - pathlist = plan_union_children(root, op, refnames_tlist, &tlist_list); + rellist = plan_union_children(root, op, refnames_tlist, &tlist_list); /* * Generate tlist for Append plan node. @@ -583,13 +591,24 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root, *pTargetList = tlist; + /* Build path list and relid set. */ + foreach(lc, rellist) + { + RelOptInfo *rel = lfirst(lc); + + pathlist = lappend(pathlist, rel->cheapest_total_path); + relids = bms_union(relids, rel->relids); + } + + /* Build result relation. */ + result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids); + result_rel->reltarget = create_pathtarget(root, tlist); + /* * Append the child results together. */ path = (Path *) create_append_path(result_rel, pathlist, NIL, NULL, 0, false, NIL, -1); - /* We have to manually jam the right tlist into the path; ick */ - path->pathtarget = create_pathtarget(root, tlist); /* * For UNION ALL, we just need the Append path. For UNION, need to add @@ -598,30 +617,32 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root, if (!op->all) path = make_union_unique(op, path, tlist, root); + add_path(result_rel, path); + /* - * Estimate number of groups if caller wants it. For now we just assume - * the output is unique --- this is certainly true for the UNION case, and - * we want worst-case estimates anyway. + * Estimate number of groups. For now we just assume the output is unique + * --- this is certainly true for the UNION case, and we want worst-case + * estimates anyway. */ - if (pNumGroups) - *pNumGroups = path->rows; + result_rel->rows = path->rows; /* Undo effects of possibly forcing tuple_fraction to 0 */ root->tuple_fraction = save_fraction; - return path; + return result_rel; } /* - * Generate path for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node + * Generate paths for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node */ -static Path * -generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root, - List *refnames_tlist, - List **pTargetList, - double *pNumGroups) +static RelOptInfo * +generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root, + List *refnames_tlist, + List **pTargetList) { - RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL); + RelOptInfo *result_rel; + RelOptInfo *lrel, + *rrel; double save_fraction = root->tuple_fraction; Path *lpath, *rpath, @@ -646,18 +667,20 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root, root->tuple_fraction = 0.0; /* Recurse on children, ensuring their outputs are marked */ - lpath = recurse_set_operations(op->larg, root, - op->colTypes, op->colCollations, - false, 0, - refnames_tlist, - &lpath_tlist, - &dLeftGroups); - rpath = recurse_set_operations(op->rarg, root, - op->colTypes, op->colCollations, - false, 1, - refnames_tlist, - &rpath_tlist, - &dRightGroups); + lrel = recurse_set_operations(op->larg, root, + op->colTypes, op->colCollations, + false, 0, + refnames_tlist, + &lpath_tlist, + &dLeftGroups); + lpath = lrel->cheapest_total_path; + rrel = recurse_set_operations(op->rarg, root, + op->colTypes, op->colCollations, + false, 1, + refnames_tlist, + &rpath_tlist, + &dRightGroups); + rpath = rrel->cheapest_total_path; /* Undo effects of forcing tuple_fraction to 0 */ root->tuple_fraction = save_fraction; @@ -695,15 +718,17 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root, *pTargetList = tlist; + /* Build result relation. */ + result_rel = fetch_upper_rel(root, UPPERREL_SETOP, + bms_union(lrel->relids, rrel->relids)); + result_rel->reltarget = create_pathtarget(root, tlist);; + /* * Append the child results together. */ path = (Path *) create_append_path(result_rel, pathlist, NIL, NULL, 0, false, NIL, -1); - /* We have to manually jam the right tlist into the path; ick */ - path->pathtarget = create_pathtarget(root, tlist); - /* Identify the grouping semantics */ groupList = generate_setop_grouplist(op, tlist); @@ -769,10 +794,9 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root, dNumGroups, dNumOutputRows); - if (pNumGroups) - *pNumGroups = dNumGroups; - - return path; + result_rel->rows = path->rows; + add_path(result_rel, path); + return result_rel; } /* @@ -786,7 +810,7 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root, * collations have the same notion of equality. It is valid from an * implementation standpoint because we don't care about the ordering of * a UNION child's result: UNION ALL results are always unordered, and - * generate_union_path will force a fresh sort if the top level is a UNION. + * generate_union_paths will force a fresh sort if the top level is a UNION. */ static List * plan_union_children(PlannerInfo *root, @@ -897,8 +921,6 @@ make_union_unique(SetOperationStmt *op, Path *path, List *tlist, groupList, tlist), -1.0); - /* We have to manually jam the right tlist into the path; ick */ - path->pathtarget = create_pathtarget(root, tlist); path = (Path *) create_upper_unique_path(root, result_rel, path, @@ -909,6 +931,24 @@ make_union_unique(SetOperationStmt *op, Path *path, List *tlist, return path; } +/* + * postprocess_setop_rel - perform steps required after adding paths + */ +static void +postprocess_setop_rel(PlannerInfo *root, RelOptInfo *rel) +{ + /* + * We don't currently worry about allowing FDWs to contribute paths to + * this relation, but give extensions a chance. + */ + if (create_upper_paths_hook) + (*create_upper_paths_hook) (root, UPPERREL_SETOP, + NULL, rel); + + /* Select cheapest path */ + set_cheapest(rel); +} + /* * choose_hashed_setop - should we use hashing for a set operation? */