diff options
Diffstat (limited to 'src/backend/optimizer/util/pathnode.c')
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 487 |
1 files changed, 265 insertions, 222 deletions
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index a2fc75a659e..ef1ce2a0b49 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -22,6 +22,7 @@ #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" @@ -213,7 +214,7 @@ set_cheapest(RelOptInfo *parent_rel) int cmp; /* We only consider unparameterized paths in this step */ - if (path->required_outer) + if (path->param_info) { have_parameterized_paths = true; continue; @@ -263,7 +264,7 @@ set_cheapest(RelOptInfo *parent_rel) { Path *path = (Path *) lfirst(p); - if (path->required_outer) + if (path->param_info) add_parameterized_path(parent_rel, path); } } @@ -273,13 +274,20 @@ set_cheapest(RelOptInfo *parent_rel) * add_path * Consider a potential implementation path for the specified parent rel, * and add it to the rel's pathlist if it is worthy of consideration. - * A path is worthy if it has either a better sort order (better pathkeys) - * or cheaper cost (on either dimension) than any of the existing old paths - * that have the same or superset required_outer rels. + * A path is worthy if it has a better sort order (better pathkeys) or + * cheaper cost (on either dimension), or generates fewer rows, than any + * existing path that has the same or superset parameterization rels. * * We also remove from the rel's pathlist any old paths that are dominated * by new_path --- that is, new_path is cheaper, at least as well ordered, - * and requires no outer rels not required by old path. + * generates no more rows, and requires no outer rels not required by the + * old path. + * + * In most cases, a path with a superset parameterization will generate + * fewer rows (since it has more join clauses to apply), so that those two + * figures of merit move in opposite directions; this means that a path of + * one parameterization can seldom dominate a path of another. But such + * cases do arise, so we make the full set of checks anyway. * * There is one policy decision embedded in this function, along with its * sibling add_path_precheck: we treat all parameterized paths as having @@ -325,7 +333,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path) CHECK_FOR_INTERRUPTS(); /* Pretend parameterized paths have no pathkeys, per comment above */ - new_path_pathkeys = new_path->required_outer ? NIL : new_path->pathkeys; + new_path_pathkeys = new_path->param_info ? NIL : new_path->pathkeys; /* * Loop to check proposed new path against old paths. Note it is possible @@ -352,16 +360,19 @@ add_path(RelOptInfo *parent_rel, Path *new_path) * If the two paths compare differently for startup and total cost, * then we want to keep both, and we can skip comparing pathkeys and * required_outer rels. If they compare the same, proceed with the - * other comparisons. (We make the tests in this order because the - * cost comparison is most likely to turn out "different", and the - * pathkeys comparison next most likely.) + * other comparisons. Row count is checked last. (We make the tests + * in this order because the cost comparison is most likely to turn + * out "different", and the pathkeys comparison next most likely. As + * explained above, row count very seldom makes a difference, so even + * though it's cheap to compare there's not much point in checking it + * earlier.) */ if (costcmp != COSTS_DIFFERENT) { /* Similarly check to see if either dominates on pathkeys */ List *old_path_pathkeys; - old_path_pathkeys = old_path->required_outer ? NIL : old_path->pathkeys; + old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys; keyscmp = compare_pathkeys(new_path_pathkeys, old_path_pathkeys); if (keyscmp != PATHKEYS_DIFFERENT) @@ -369,18 +380,20 @@ add_path(RelOptInfo *parent_rel, Path *new_path) switch (costcmp) { case COSTS_EQUAL: - outercmp = bms_subset_compare(new_path->required_outer, - old_path->required_outer); + outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path), + PATH_REQ_OUTER(old_path)); if (keyscmp == PATHKEYS_BETTER1) { - if (outercmp == BMS_EQUAL || - outercmp == BMS_SUBSET1) + if ((outercmp == BMS_EQUAL || + outercmp == BMS_SUBSET1) && + new_path->rows <= old_path->rows) remove_old = true; /* new dominates old */ } else if (keyscmp == PATHKEYS_BETTER2) { - if (outercmp == BMS_EQUAL || - outercmp == BMS_SUBSET2) + if ((outercmp == BMS_EQUAL || + outercmp == BMS_SUBSET2) && + new_path->rows >= old_path->rows) accept_new = false; /* old dominates new */ } else /* keyscmp == PATHKEYS_EQUAL */ @@ -389,19 +402,25 @@ add_path(RelOptInfo *parent_rel, Path *new_path) { /* * Same pathkeys and outer rels, and fuzzily - * the same cost, so keep just one --- but - * we'll do an exact cost comparison to decide - * which. + * the same cost, so keep just one; to decide + * which, first check rows and then do an + * exact cost comparison. */ - if (compare_path_costs(new_path, old_path, + if (new_path->rows < old_path->rows) + remove_old = true; /* new dominates old */ + else if (new_path->rows > old_path->rows) + accept_new = false; /* old dominates new */ + else if (compare_path_costs(new_path, old_path, TOTAL_COST) < 0) remove_old = true; /* new dominates old */ else accept_new = false; /* old equals or dominates new */ } - else if (outercmp == BMS_SUBSET1) + else if (outercmp == BMS_SUBSET1 && + new_path->rows <= old_path->rows) remove_old = true; /* new dominates old */ - else if (outercmp == BMS_SUBSET2) + else if (outercmp == BMS_SUBSET2 && + new_path->rows >= old_path->rows) accept_new = false; /* old dominates new */ /* else different parameterizations, keep both */ } @@ -409,20 +428,22 @@ add_path(RelOptInfo *parent_rel, Path *new_path) case COSTS_BETTER1: if (keyscmp != PATHKEYS_BETTER2) { - outercmp = bms_subset_compare(new_path->required_outer, - old_path->required_outer); - if (outercmp == BMS_EQUAL || - outercmp == BMS_SUBSET1) + outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path), + PATH_REQ_OUTER(old_path)); + if ((outercmp == BMS_EQUAL || + outercmp == BMS_SUBSET1) && + new_path->rows <= old_path->rows) remove_old = true; /* new dominates old */ } break; case COSTS_BETTER2: if (keyscmp != PATHKEYS_BETTER1) { - outercmp = bms_subset_compare(new_path->required_outer, - old_path->required_outer); - if (outercmp == BMS_EQUAL || - outercmp == BMS_SUBSET2) + outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path), + PATH_REQ_OUTER(old_path)); + if ((outercmp == BMS_EQUAL || + outercmp == BMS_SUBSET2) && + new_path->rows >= old_path->rows) accept_new = false; /* old dominates new */ } break; @@ -491,6 +512,14 @@ add_path(RelOptInfo *parent_rel, Path *new_path) * We assume we know the path's pathkeys and parameterization accurately, * and have lower bounds for its costs. * + * Note that we do not know the path's rowcount, since getting an estimate for + * that is too expensive to do before prechecking. We assume here that paths + * of a superset parameterization will generate fewer rows; if that holds, + * then paths with different parameterizations cannot dominate each other + * and so we can simply ignore existing paths of another parameterization. + * (In the infrequent cases where that rule of thumb fails, add_path will + * get rid of the inferior path.) + * * At the time this is called, we haven't actually built a Path structure, * so the required information has to be passed piecemeal. */ @@ -502,18 +531,19 @@ add_path_precheck(RelOptInfo *parent_rel, List *new_path_pathkeys; ListCell *p1; - /* Pretend parameterized paths have no pathkeys, per comment above */ + /* Pretend parameterized paths have no pathkeys, per add_path comment */ new_path_pathkeys = required_outer ? NIL : pathkeys; foreach(p1, parent_rel->pathlist) { Path *old_path = (Path *) lfirst(p1); PathKeysComparison keyscmp; - BMS_Comparison outercmp; /* - * We are looking for an old_path that dominates the new path across - * all four metrics. If we find one, we can reject the new path. + * We are looking for an old_path with the same parameterization (and + * by assumption the same rowcount) that dominates the new path on + * pathkeys as well as both cost metrics. If we find one, we can + * reject the new path. * * For speed, we make exact rather than fuzzy cost comparisons. * If an old path dominates the new path exactly on both costs, it @@ -525,17 +555,17 @@ add_path_precheck(RelOptInfo *parent_rel, { List *old_path_pathkeys; - old_path_pathkeys = old_path->required_outer ? NIL : old_path->pathkeys; + old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys; keyscmp = compare_pathkeys(new_path_pathkeys, old_path_pathkeys); if (keyscmp == PATHKEYS_EQUAL || keyscmp == PATHKEYS_BETTER2) { - outercmp = bms_subset_compare(required_outer, - old_path->required_outer); - if (outercmp == BMS_EQUAL || - outercmp == BMS_SUBSET2) + if (bms_equal(required_outer, PATH_REQ_OUTER(old_path))) + { + /* Found an old path that dominates the new one */ return false; + } } } } @@ -559,10 +589,10 @@ add_path_precheck(RelOptInfo *parent_rel, * and add it to the rel's cheapest_parameterized_paths list if it * belongs there, removing any old entries that it dominates. * - * This is essentially a cut-down form of add_path(): we do not care about - * startup cost or sort ordering, only total cost and parameterization. - * Also, we should not recycle rejected paths, since they will still be - * present in the rel's pathlist. + * This is essentially a cut-down form of add_path(): we do not care + * about startup cost or sort ordering, only total cost, rowcount, and + * parameterization. Also, we must not recycle rejected paths, since + * they will still be present in the rel's pathlist. * * 'parent_rel' is the relation entry to which the path corresponds. * 'new_path' is a parameterized path for parent_rel. @@ -598,27 +628,33 @@ add_parameterized_path(RelOptInfo *parent_rel, Path *new_path) p1_next = lnext(p1); costcmp = compare_path_costs(new_path, old_path, TOTAL_COST); - outercmp = bms_subset_compare(new_path->required_outer, - old_path->required_outer); + outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path), + PATH_REQ_OUTER(old_path)); if (outercmp != BMS_DIFFERENT) { if (costcmp < 0) { - if (outercmp != BMS_SUBSET2) + if (outercmp != BMS_SUBSET2 && + new_path->rows <= old_path->rows) remove_old = true; /* new dominates old */ } else if (costcmp > 0) { - if (outercmp != BMS_SUBSET1) + if (outercmp != BMS_SUBSET1 && + new_path->rows >= old_path->rows) accept_new = false; /* old dominates new */ } - else if (outercmp == BMS_SUBSET1) + else if (outercmp == BMS_SUBSET1 && + new_path->rows <= old_path->rows) remove_old = true; /* new dominates old */ - else if (outercmp == BMS_SUBSET2) + else if (outercmp == BMS_SUBSET2 && + new_path->rows >= old_path->rows) accept_new = false; /* old dominates new */ + else if (new_path->rows < old_path->rows) + remove_old = true; /* new dominates old */ else { - /* Same cost and outer rels, arbitrarily keep the old */ + /* Same cost, rows, and param rels; arbitrarily keep old */ accept_new = false; /* old equals or dominates new */ } } @@ -675,17 +711,17 @@ add_parameterized_path(RelOptInfo *parent_rel, Path *new_path) * pathnode. */ Path * -create_seqscan_path(PlannerInfo *root, RelOptInfo *rel) +create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer) { Path *pathnode = makeNode(Path); pathnode->pathtype = T_SeqScan; pathnode->parent = rel; + pathnode->param_info = get_baserel_parampathinfo(root, rel, + required_outer); pathnode->pathkeys = NIL; /* seqscan has unordered result */ - pathnode->required_outer = NULL; - pathnode->param_clauses = NIL; - cost_seqscan(pathnode, root, rel); + cost_seqscan(pathnode, root, rel, pathnode->param_info); return pathnode; } @@ -708,7 +744,7 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel) * for an ordered index, or NoMovementScanDirection for * an unordered index. * 'indexonly' is true if an index-only scan is wanted. - * 'required_outer' is the set of outer relids referenced in indexclauses. + * 'required_outer' is the set of outer relids for a parameterized path. * 'loop_count' is the number of repetitions of the indexscan to factor into * estimates of caching behavior. * @@ -734,25 +770,9 @@ create_index_path(PlannerInfo *root, pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan; pathnode->path.parent = rel; + pathnode->path.param_info = get_baserel_parampathinfo(root, rel, + required_outer); pathnode->path.pathkeys = pathkeys; - pathnode->path.required_outer = required_outer; - if (required_outer) - { - /* Identify index clauses that are join clauses */ - List *jclauses = NIL; - ListCell *lc; - - foreach(lc, indexclauses) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - - if (!bms_is_subset(rinfo->clause_relids, rel->relids)) - jclauses = lappend(jclauses, rinfo); - } - pathnode->path.param_clauses = jclauses; - } - else - pathnode->path.param_clauses = NIL; /* Convert clauses to indexquals the executor can handle */ expand_indexqual_conditions(index, indexclauses, indexclausecols, @@ -777,6 +797,7 @@ create_index_path(PlannerInfo *root, * Creates a path node for a bitmap scan. * * 'bitmapqual' is a tree of IndexPath, BitmapAndPath, and BitmapOrPath nodes. + * 'required_outer' is the set of outer relids for a parameterized path. * 'loop_count' is the number of repetitions of the indexscan to factor into * estimates of caching behavior. * @@ -787,19 +808,22 @@ BitmapHeapPath * create_bitmap_heap_path(PlannerInfo *root, RelOptInfo *rel, Path *bitmapqual, + Relids required_outer, double loop_count) { BitmapHeapPath *pathnode = makeNode(BitmapHeapPath); pathnode->path.pathtype = T_BitmapHeapScan; pathnode->path.parent = rel; + pathnode->path.param_info = get_baserel_parampathinfo(root, rel, + required_outer); pathnode->path.pathkeys = NIL; /* always unordered */ - pathnode->path.required_outer = bitmapqual->required_outer; - pathnode->path.param_clauses = bitmapqual->param_clauses; pathnode->bitmapqual = bitmapqual; - cost_bitmap_heap_scan(&pathnode->path, root, rel, bitmapqual, loop_count); + cost_bitmap_heap_scan(&pathnode->path, root, rel, + pathnode->path.param_info, + bitmapqual, loop_count); return pathnode; } @@ -814,29 +838,14 @@ create_bitmap_and_path(PlannerInfo *root, List *bitmapquals) { BitmapAndPath *pathnode = makeNode(BitmapAndPath); - ListCell *lc; pathnode->path.pathtype = T_BitmapAnd; pathnode->path.parent = rel; + pathnode->path.param_info = NULL; /* not used in bitmap trees */ pathnode->path.pathkeys = NIL; /* always unordered */ - pathnode->path.required_outer = NULL; - pathnode->path.param_clauses = NIL; pathnode->bitmapquals = bitmapquals; - /* required_outer and param_clauses are the union of the inputs' values */ - foreach(lc, bitmapquals) - { - Path *bpath = (Path *) lfirst(lc); - - pathnode->path.required_outer = - bms_add_members(pathnode->path.required_outer, - bpath->required_outer); - pathnode->path.param_clauses = - list_concat(pathnode->path.param_clauses, - list_copy(bpath->param_clauses)); - } - /* this sets bitmapselectivity as well as the regular cost fields: */ cost_bitmap_and_node(pathnode, root); @@ -853,29 +862,14 @@ create_bitmap_or_path(PlannerInfo *root, List *bitmapquals) { BitmapOrPath *pathnode = makeNode(BitmapOrPath); - ListCell *lc; pathnode->path.pathtype = T_BitmapOr; pathnode->path.parent = rel; + pathnode->path.param_info = NULL; /* not used in bitmap trees */ pathnode->path.pathkeys = NIL; /* always unordered */ - pathnode->path.required_outer = NULL; - pathnode->path.param_clauses = NIL; pathnode->bitmapquals = bitmapquals; - /* required_outer and param_clauses are the union of the inputs' values */ - foreach(lc, bitmapquals) - { - Path *bpath = (Path *) lfirst(lc); - - pathnode->path.required_outer = - bms_add_members(pathnode->path.required_outer, - bpath->required_outer); - pathnode->path.param_clauses = - list_concat(pathnode->path.param_clauses, - list_copy(bpath->param_clauses)); - } - /* this sets bitmapselectivity as well as the regular cost fields: */ cost_bitmap_or_node(pathnode, root); @@ -893,9 +887,8 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals) pathnode->path.pathtype = T_TidScan; pathnode->path.parent = rel; - pathnode->path.pathkeys = NIL; - pathnode->path.required_outer = NULL; - pathnode->path.param_clauses = NIL; + pathnode->path.param_info = NULL; /* never parameterized at present */ + pathnode->path.pathkeys = NIL; /* always unordered */ pathnode->tidquals = tidquals; @@ -912,17 +905,17 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals) * Note that we must handle subpaths = NIL, representing a dummy access path. */ AppendPath * -create_append_path(RelOptInfo *rel, List *subpaths) +create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer) { AppendPath *pathnode = makeNode(AppendPath); ListCell *l; pathnode->path.pathtype = T_Append; pathnode->path.parent = rel; + pathnode->path.param_info = get_appendrel_parampathinfo(rel, + required_outer); pathnode->path.pathkeys = NIL; /* result is always considered * unsorted */ - pathnode->path.required_outer = NULL; /* updated below */ - pathnode->path.param_clauses = NIL; /* XXX see below */ pathnode->subpaths = subpaths; /* @@ -932,18 +925,6 @@ create_append_path(RelOptInfo *rel, List *subpaths) * nothing extra for the Append itself, which perhaps is too optimistic, * but since it doesn't do any selection or projection, it is a pretty * cheap node. If you change this, see also make_append(). - * - * We also compute the correct required_outer set, namely the union of - * the input paths' requirements. - * - * XXX We should also compute a proper param_clauses list, but that - * will require identifying which joinclauses are enforced by all the - * subplans, as well as locating the original parent RestrictInfo from - * which they were generated. For the moment we punt and leave the list - * as NIL. This will result in uselessly rechecking such joinclauses - * at the parameter-supplying nestloop join, which is slightly annoying, - * as well as overestimating the sizes of any intermediate joins, which - * is significantly more annoying. */ pathnode->path.rows = 0; pathnode->path.startup_cost = 0; @@ -958,9 +939,8 @@ create_append_path(RelOptInfo *rel, List *subpaths) pathnode->path.startup_cost = subpath->startup_cost; pathnode->path.total_cost += subpath->total_cost; - pathnode->path.required_outer = - bms_add_members(pathnode->path.required_outer, - subpath->required_outer); + /* All child paths must have same parameterization */ + Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer)); } return pathnode; @@ -975,7 +955,8 @@ MergeAppendPath * create_merge_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, - List *pathkeys) + List *pathkeys, + Relids required_outer) { MergeAppendPath *pathnode = makeNode(MergeAppendPath); Cost input_startup_cost; @@ -984,46 +965,22 @@ create_merge_append_path(PlannerInfo *root, pathnode->path.pathtype = T_MergeAppend; pathnode->path.parent = rel; + pathnode->path.param_info = get_appendrel_parampathinfo(rel, + required_outer); pathnode->path.pathkeys = pathkeys; - pathnode->path.required_outer = NULL; /* updated below */ - pathnode->path.param_clauses = NIL; /* XXX see below */ pathnode->subpaths = subpaths; /* * Apply query-wide LIMIT if known and path is for sole base relation. - * Finding out the latter at this low level is a bit klugy. + * (Handling this at this low level is a bit klugy.) */ - pathnode->limit_tuples = root->limit_tuples; - if (pathnode->limit_tuples >= 0) - { - Index rti; - - for (rti = 1; rti < root->simple_rel_array_size; rti++) - { - RelOptInfo *brel = root->simple_rel_array[rti]; - - if (brel == NULL) - continue; - - /* ignore RTEs that are "other rels" */ - if (brel->reloptkind != RELOPT_BASEREL) - continue; - - if (brel != rel) - { - /* Oops, it's a join query */ - pathnode->limit_tuples = -1.0; - break; - } - } - } + if (bms_equal(rel->relids, root->all_baserels)) + pathnode->limit_tuples = root->limit_tuples; + else + pathnode->limit_tuples = -1.0; /* - * Add up the sizes and costs of the input paths, and also compute the - * real required_outer value. - * - * XXX as in create_append_path(), we should compute param_clauses but - * it will require more work. + * Add up the sizes and costs of the input paths. */ pathnode->path.rows = 0; input_startup_cost = 0; @@ -1058,9 +1015,8 @@ create_merge_append_path(PlannerInfo *root, input_total_cost += sort_path.total_cost; } - pathnode->path.required_outer = - bms_add_members(pathnode->path.required_outer, - subpath->required_outer); + /* All child paths must have same parameterization */ + Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer)); } /* Now we can compute total costs of the MergeAppend */ @@ -1084,9 +1040,8 @@ create_result_path(List *quals) pathnode->path.pathtype = T_Result; pathnode->path.parent = NULL; + pathnode->path.param_info = NULL; pathnode->path.pathkeys = NIL; - pathnode->path.required_outer = NULL; - pathnode->path.param_clauses = NIL; pathnode->quals = quals; /* Hardly worth defining a cost_result() function ... just do it */ @@ -1114,11 +1069,12 @@ create_material_path(RelOptInfo *rel, Path *subpath) { MaterialPath *pathnode = makeNode(MaterialPath); + Assert(subpath->parent == rel); + pathnode->path.pathtype = T_Material; pathnode->path.parent = rel; + pathnode->path.param_info = subpath->param_info; pathnode->path.pathkeys = subpath->pathkeys; - pathnode->path.required_outer = subpath->required_outer; - pathnode->path.param_clauses = subpath->param_clauses; pathnode->subpath = subpath; @@ -1159,6 +1115,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, /* Caller made a mistake if subpath isn't cheapest_total ... */ Assert(subpath == rel->cheapest_total_path); + Assert(subpath->parent == rel); /* ... or if SpecialJoinInfo is the wrong one */ Assert(sjinfo->jointype == JOIN_SEMI); Assert(bms_equal(rel->relids, sjinfo->syn_righthand)); @@ -1325,14 +1282,13 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, pathnode->path.pathtype = T_Unique; pathnode->path.parent = rel; + pathnode->path.param_info = subpath->param_info; /* * Assume the output is unsorted, since we don't necessarily have pathkeys * to represent it. (This might get overridden below.) */ pathnode->path.pathkeys = NIL; - pathnode->path.required_outer = subpath->required_outer; - pathnode->path.param_clauses = subpath->param_clauses; pathnode->subpath = subpath; pathnode->in_operators = in_operators; @@ -1661,17 +1617,18 @@ distinct_col_search(int colno, List *colnos, List *opids) * returning the pathnode. */ Path * -create_subqueryscan_path(RelOptInfo *rel, List *pathkeys) +create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, + List *pathkeys, Relids required_outer) { Path *pathnode = makeNode(Path); pathnode->pathtype = T_SubqueryScan; pathnode->parent = rel; + pathnode->param_info = get_baserel_parampathinfo(root, rel, + required_outer); pathnode->pathkeys = pathkeys; - pathnode->required_outer = NULL; - pathnode->param_clauses = NIL; - cost_subqueryscan(pathnode, rel); + cost_subqueryscan(pathnode, root, rel, pathnode->param_info); return pathnode; } @@ -1688,9 +1645,8 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel) pathnode->pathtype = T_FunctionScan; pathnode->parent = rel; + pathnode->param_info = NULL; /* never parameterized at present */ pathnode->pathkeys = NIL; /* for now, assume unordered result */ - pathnode->required_outer = NULL; - pathnode->param_clauses = NIL; cost_functionscan(pathnode, root, rel); @@ -1709,9 +1665,8 @@ create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel) pathnode->pathtype = T_ValuesScan; pathnode->parent = rel; + pathnode->param_info = NULL; /* never parameterized at present */ pathnode->pathkeys = NIL; /* result is always unordered */ - pathnode->required_outer = NULL; - pathnode->param_clauses = NIL; cost_valuesscan(pathnode, root, rel); @@ -1730,9 +1685,8 @@ create_ctescan_path(PlannerInfo *root, RelOptInfo *rel) pathnode->pathtype = T_CteScan; pathnode->parent = rel; + pathnode->param_info = NULL; /* never parameterized at present */ pathnode->pathkeys = NIL; /* XXX for now, result is always unordered */ - pathnode->required_outer = NULL; - pathnode->param_clauses = NIL; cost_ctescan(pathnode, root, rel); @@ -1751,9 +1705,8 @@ create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel) pathnode->pathtype = T_WorkTableScan; pathnode->parent = rel; + pathnode->param_info = NULL; /* never parameterized at present */ pathnode->pathkeys = NIL; /* result is always unordered */ - pathnode->required_outer = NULL; - pathnode->param_clauses = NIL; /* Cost is the same as for a regular CTE scan */ cost_ctescan(pathnode, root, rel); @@ -1775,19 +1728,19 @@ ForeignPath * create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, - Relids required_outer, List *param_clauses, + Relids required_outer, List *fdw_private) { ForeignPath *pathnode = makeNode(ForeignPath); pathnode->path.pathtype = T_ForeignScan; pathnode->path.parent = rel; + pathnode->path.param_info = get_baserel_parampathinfo(root, rel, + required_outer); pathnode->path.rows = rows; pathnode->path.startup_cost = startup_cost; pathnode->path.total_cost = total_cost; pathnode->path.pathkeys = pathkeys; - pathnode->path.required_outer = required_outer; - pathnode->path.param_clauses = param_clauses; pathnode->fdw_private = fdw_private; @@ -1803,17 +1756,17 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, Relids calc_nestloop_required_outer(Path *outer_path, Path *inner_path) { + Relids outer_paramrels = PATH_REQ_OUTER(outer_path); + Relids inner_paramrels = PATH_REQ_OUTER(inner_path); Relids required_outer; /* inner_path can require rels from outer path, but not vice versa */ - Assert(!bms_overlap(outer_path->required_outer, - inner_path->parent->relids)); + Assert(!bms_overlap(outer_paramrels, inner_path->parent->relids)); /* easy case if inner path is not parameterized */ - if (!inner_path->required_outer) - return bms_copy(outer_path->required_outer); + if (!inner_paramrels) + return bms_copy(outer_paramrels); /* else, form the union ... */ - required_outer = bms_union(outer_path->required_outer, - inner_path->required_outer); + required_outer = bms_union(outer_paramrels, inner_paramrels); /* ... and remove any mention of now-satisfied outer rels */ required_outer = bms_del_members(required_outer, outer_path->parent->relids); @@ -1835,16 +1788,15 @@ calc_nestloop_required_outer(Path *outer_path, Path *inner_path) Relids calc_non_nestloop_required_outer(Path *outer_path, Path *inner_path) { + Relids outer_paramrels = PATH_REQ_OUTER(outer_path); + Relids inner_paramrels = PATH_REQ_OUTER(inner_path); Relids required_outer; /* neither path can require rels from the other */ - Assert(!bms_overlap(outer_path->required_outer, - inner_path->parent->relids)); - Assert(!bms_overlap(inner_path->required_outer, - outer_path->parent->relids)); + Assert(!bms_overlap(outer_paramrels, inner_path->parent->relids)); + Assert(!bms_overlap(inner_paramrels, outer_path->parent->relids)); /* form the union ... */ - required_outer = bms_union(outer_path->required_outer, - inner_path->required_outer); + required_outer = bms_union(outer_paramrels, inner_paramrels); /* we do not need an explicit test for empty; bms_union gets it right */ return required_outer; } @@ -1881,30 +1833,45 @@ create_nestloop_path(PlannerInfo *root, Relids required_outer) { NestPath *pathnode = makeNode(NestPath); + Relids inner_req_outer = PATH_REQ_OUTER(inner_path); - pathnode->path.pathtype = T_NestLoop; - pathnode->path.parent = joinrel; - pathnode->path.pathkeys = pathkeys; - pathnode->path.required_outer = required_outer; - if (pathnode->path.required_outer) + /* + * If the inner path is parameterized by the outer, we must drop any + * restrict_clauses that are due to be moved into the inner path. We + * have to do this now, rather than postpone the work till createplan + * time, because the restrict_clauses list can affect the size and cost + * estimates for this path. + */ + if (bms_overlap(inner_req_outer, outer_path->parent->relids)) { - /* Identify parameter clauses not yet applied here */ - List *jclauses; + Relids inner_and_outer = bms_union(inner_path->parent->relids, + inner_req_outer); + List *jclauses = NIL; ListCell *lc; - /* LHS clauses could not be satisfied here */ - jclauses = list_copy(outer_path->param_clauses); - foreach(lc, inner_path->param_clauses) + foreach(lc, restrict_clauses) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - if (!bms_is_subset(rinfo->clause_relids, joinrel->relids)) + if (!join_clause_is_movable_into(rinfo, + inner_path->parent->relids, + inner_and_outer)) jclauses = lappend(jclauses, rinfo); } - pathnode->path.param_clauses = jclauses; + restrict_clauses = jclauses; } - else - pathnode->path.param_clauses = NIL; + + pathnode->path.pathtype = T_NestLoop; + pathnode->path.parent = joinrel; + pathnode->path.param_info = + get_joinrel_parampathinfo(root, + joinrel, + outer_path, + inner_path, + sjinfo, + required_outer, + &restrict_clauses); + pathnode->path.pathkeys = pathkeys; pathnode->jointype = jointype; pathnode->outerjoinpath = outer_path; pathnode->innerjoinpath = inner_path; @@ -1953,11 +1920,15 @@ create_mergejoin_path(PlannerInfo *root, pathnode->jpath.path.pathtype = T_MergeJoin; pathnode->jpath.path.parent = joinrel; + pathnode->jpath.path.param_info = + get_joinrel_parampathinfo(root, + joinrel, + outer_path, + inner_path, + sjinfo, + required_outer, + &restrict_clauses); pathnode->jpath.path.pathkeys = pathkeys; - pathnode->jpath.path.required_outer = required_outer; - pathnode->jpath.path.param_clauses = - list_concat(list_copy(outer_path->param_clauses), - inner_path->param_clauses); pathnode->jpath.jointype = jointype; pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; @@ -2005,6 +1976,14 @@ create_hashjoin_path(PlannerInfo *root, pathnode->jpath.path.pathtype = T_HashJoin; pathnode->jpath.path.parent = joinrel; + pathnode->jpath.path.param_info = + get_joinrel_parampathinfo(root, + joinrel, + outer_path, + inner_path, + sjinfo, + required_outer, + &restrict_clauses); /* * A hashjoin never has pathkeys, since its output ordering is @@ -2018,10 +1997,6 @@ create_hashjoin_path(PlannerInfo *root, * outer rel than it does now.) */ pathnode->jpath.path.pathkeys = NIL; - pathnode->jpath.path.required_outer = required_outer; - pathnode->jpath.path.param_clauses = - list_concat(list_copy(outer_path->param_clauses), - inner_path->param_clauses); pathnode->jpath.jointype = jointype; pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; @@ -2033,3 +2008,71 @@ create_hashjoin_path(PlannerInfo *root, return pathnode; } + +/* + * reparameterize_path + * Attempt to modify a Path to have greater parameterization + * + * We use this to attempt to bring all child paths of an appendrel to the + * same parameterization level, ensuring that they all enforce the same set + * of join quals (and thus that that parameterization can be attributed to + * an append path built from such paths). Currently, only a few path types + * are supported here, though more could be added at need. We return NULL + * if we can't reparameterize the given path. + * + * Note: we intentionally do not pass created paths to add_path(); it would + * possibly try to delete them on the grounds of being cost-inferior to the + * paths they were made from, and we don't want that. Paths made here are + * not necessarily of general-purpose usefulness, but they can be useful + * as members of an append path. + */ +Path * +reparameterize_path(PlannerInfo *root, Path *path, + Relids required_outer, + double loop_count) +{ + RelOptInfo *rel = path->parent; + + /* Can only increase, not decrease, path's parameterization */ + if (!bms_is_subset(PATH_REQ_OUTER(path), required_outer)) + return NULL; + switch (path->pathtype) + { + case T_SeqScan: + return create_seqscan_path(root, rel, required_outer); + case T_IndexScan: + case T_IndexOnlyScan: + { + IndexPath *ipath = (IndexPath *) path; + IndexPath *newpath = makeNode(IndexPath); + + /* + * We can't use create_index_path directly, and would not want to + * because it would re-compute the indexqual conditions which is + * wasted effort. Instead we hack things a bit: flat-copy the + * path node, revise its param_info, and redo the cost estimate. + */ + memcpy(newpath, ipath, sizeof(IndexPath)); + newpath->path.param_info = + get_baserel_parampathinfo(root, rel, required_outer); + cost_index(newpath, root, loop_count); + return (Path *) newpath; + } + case T_BitmapHeapScan: + { + BitmapHeapPath *bpath = (BitmapHeapPath *) path; + + return (Path *) create_bitmap_heap_path(root, + rel, + bpath->bitmapqual, + required_outer, + loop_count); + } + case T_SubqueryScan: + return create_subqueryscan_path(root, rel, path->pathkeys, + required_outer); + default: + break; + } + return NULL; +} |