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,
RangeTblEntry *leftmostRTE;
Query *leftmostQuery;
RelOptInfo *setop_rel;
- Path *path;
List *top_tlist;
Assert(topop);
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;
}
* 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,
List **pTargetList,
double *pNumGroups)
{
+ RelOptInfo *rel = NULL; /* keep compiler quiet */
+
/* Guard against stack overflow due to overly complex setop nests */
check_stack_depth();
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;
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 */
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
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
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
!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;
* 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;
/*
*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
*/
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;
* 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.
*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
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,
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;
*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);
dNumGroups,
dNumOutputRows);
- if (pNumGroups)
- *pNumGroups = dNumGroups;
-
- return path;
+ result_rel->rows = path->rows;
+ add_path(result_rel, path);
+ return result_rel;
}
/*
* 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,
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,
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?
*/