diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index c208e9bfb0b..95ae99bcf30 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -122,7 +122,6 @@ static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths); static PathClauseUsage *classify_index_clause_usage(Path *path, List **clauselist); -static Relids get_bitmap_tree_required_outer(Path *bitmapqual); static void find_indexpath_quals(Path *bitmapqual, List **quals, List **preds); static int find_list_position(Node *node, List **nodelist); static bool check_index_only(RelOptInfo *rel, IndexOptInfo *index); @@ -357,23 +356,16 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel) */ if (bitjoinpaths != NIL) { - List *path_outer; List *all_path_outers; ListCell *lc; - /* - * path_outer holds the parameterization of each path in bitjoinpaths - * (to save recalculating that several times), while all_path_outers - * holds all distinct parameterization sets. - */ - path_outer = all_path_outers = NIL; + /* Identify each distinct parameterization seen in bitjoinpaths */ + all_path_outers = NIL; foreach(lc, bitjoinpaths) { Path *path = (Path *) lfirst(lc); - Relids required_outer; + Relids required_outer = PATH_REQ_OUTER(path); - required_outer = get_bitmap_tree_required_outer(path); - path_outer = lappend(path_outer, required_outer); if (!bms_equal_any(required_outer, all_path_outers)) all_path_outers = lappend(all_path_outers, required_outer); } @@ -388,16 +380,14 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel) double loop_count; BitmapHeapPath *bpath; ListCell *lcp; - ListCell *lco; /* Identify all the bitmap join paths needing no more than that */ this_path_set = NIL; - forboth(lcp, bitjoinpaths, lco, path_outer) + foreach(lcp, bitjoinpaths) { Path *path = (Path *) lfirst(lcp); - Relids p_outers = (Relids) lfirst(lco); - if (bms_is_subset(p_outers, max_outers)) + if (bms_is_subset(PATH_REQ_OUTER(path), max_outers)) this_path_set = lappend(this_path_set, path); } @@ -411,7 +401,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel) bitmapqual = choose_bitmap_and(root, rel, this_path_set); /* And push that path into the mix */ - required_outer = get_bitmap_tree_required_outer(bitmapqual); + required_outer = PATH_REQ_OUTER(bitmapqual); loop_count = get_loop_count(root, rel->relid, required_outer); bpath = create_bitmap_heap_path(root, rel, bitmapqual, required_outer, loop_count, 0); @@ -1609,25 +1599,19 @@ path_usage_comparator(const void *a, const void *b) /* * Estimate the cost of actually executing a bitmap scan with a single - * index path (no BitmapAnd, at least not at this level; but it could be - * a BitmapOr). + * index path (which could be a BitmapAnd or BitmapOr node). */ static Cost bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, Path *ipath) { BitmapHeapPath bpath; - Relids required_outer; - - /* Identify required outer rels, in case it's a parameterized scan */ - required_outer = get_bitmap_tree_required_outer(ipath); /* Set up a dummy BitmapHeapPath */ bpath.path.type = T_BitmapHeapPath; bpath.path.pathtype = T_BitmapHeapScan; bpath.path.parent = rel; bpath.path.pathtarget = rel->reltarget; - bpath.path.param_info = get_baserel_parampathinfo(root, rel, - required_outer); + bpath.path.param_info = ipath->param_info; bpath.path.pathkeys = NIL; bpath.bitmapqual = ipath; @@ -1636,10 +1620,13 @@ bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, Path *ipath) * Parallel bitmap heap path will be considered at later stage. */ bpath.path.parallel_workers = 0; + + /* Now we can do cost_bitmap_heap_scan */ cost_bitmap_heap_scan(&bpath.path, root, rel, bpath.path.param_info, ipath, - get_loop_count(root, rel->relid, required_outer)); + get_loop_count(root, rel->relid, + PATH_REQ_OUTER(ipath))); return bpath.path.total_cost; } @@ -1651,46 +1638,15 @@ bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, Path *ipath) static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths) { - BitmapAndPath apath; - BitmapHeapPath bpath; - Relids required_outer; - - /* Set up a dummy BitmapAndPath */ - apath.path.type = T_BitmapAndPath; - apath.path.pathtype = T_BitmapAnd; - apath.path.parent = rel; - apath.path.pathtarget = rel->reltarget; - apath.path.param_info = NULL; /* not used in bitmap trees */ - apath.path.pathkeys = NIL; - apath.bitmapquals = paths; - cost_bitmap_and_node(&apath, root); - - /* Identify required outer rels, in case it's a parameterized scan */ - required_outer = get_bitmap_tree_required_outer((Path *) &apath); - - /* Set up a dummy BitmapHeapPath */ - bpath.path.type = T_BitmapHeapPath; - bpath.path.pathtype = T_BitmapHeapScan; - bpath.path.parent = rel; - bpath.path.pathtarget = rel->reltarget; - bpath.path.param_info = get_baserel_parampathinfo(root, rel, - required_outer); - bpath.path.pathkeys = NIL; - bpath.bitmapqual = (Path *) &apath; + BitmapAndPath *apath; /* - * Check the cost of temporary path without considering parallelism. - * Parallel bitmap heap path will be considered at later stage. + * Might as well build a real BitmapAndPath here, as the work is slightly + * too complicated to be worth repeating just to save one palloc. */ - bpath.path.parallel_workers = 0; + apath = create_bitmap_and_path(root, rel, paths); - /* Now we can do cost_bitmap_heap_scan */ - cost_bitmap_heap_scan(&bpath.path, root, rel, - bpath.path.param_info, - (Path *) &apath, - get_loop_count(root, rel->relid, required_outer)); - - return bpath.path.total_cost; + return bitmap_scan_cost_est(root, rel, (Path *) apath); } @@ -1761,49 +1717,6 @@ classify_index_clause_usage(Path *path, List **clauselist) } -/* - * get_bitmap_tree_required_outer - * Find the required outer rels for a bitmap tree (index/and/or) - * - * We don't associate any particular parameterization with a BitmapAnd or - * BitmapOr node; however, the IndexPaths have parameterization info, in - * their capacity as standalone access paths. The parameterization required - * for the bitmap heap scan node is the union of rels referenced in the - * child IndexPaths. - */ -static Relids -get_bitmap_tree_required_outer(Path *bitmapqual) -{ - Relids result = NULL; - ListCell *lc; - - if (IsA(bitmapqual, IndexPath)) - { - return bms_copy(PATH_REQ_OUTER(bitmapqual)); - } - else if (IsA(bitmapqual, BitmapAndPath)) - { - foreach(lc, ((BitmapAndPath *) bitmapqual)->bitmapquals) - { - result = bms_join(result, - get_bitmap_tree_required_outer((Path *) lfirst(lc))); - } - } - else if (IsA(bitmapqual, BitmapOrPath)) - { - foreach(lc, ((BitmapOrPath *) bitmapqual)->bitmapquals) - { - result = bms_join(result, - get_bitmap_tree_required_outer((Path *) lfirst(lc))); - } - } - else - elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual)); - - return result; -} - - /* * find_indexpath_quals * diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 2d15a9b7b0e..056f7422c19 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1107,11 +1107,27 @@ create_bitmap_and_path(PlannerInfo *root, List *bitmapquals) { BitmapAndPath *pathnode = makeNode(BitmapAndPath); + Relids required_outer = NULL; + ListCell *lc; pathnode->path.pathtype = T_BitmapAnd; pathnode->path.parent = rel; pathnode->path.pathtarget = rel->reltarget; - pathnode->path.param_info = NULL; /* not used in bitmap trees */ + + /* + * Identify the required outer rels as the union of what the child paths + * depend on. (Alternatively, we could insist that the caller pass this + * in, but it's more convenient and reliable to compute it here.) + */ + foreach(lc, bitmapquals) + { + Path *bitmapqual = (Path *) lfirst(lc); + + required_outer = bms_add_members(required_outer, + PATH_REQ_OUTER(bitmapqual)); + } + pathnode->path.param_info = get_baserel_parampathinfo(root, rel, + required_outer); /* * Currently, a BitmapHeapPath, BitmapAndPath, or BitmapOrPath will be @@ -1143,11 +1159,27 @@ create_bitmap_or_path(PlannerInfo *root, List *bitmapquals) { BitmapOrPath *pathnode = makeNode(BitmapOrPath); + Relids required_outer = NULL; + ListCell *lc; pathnode->path.pathtype = T_BitmapOr; pathnode->path.parent = rel; pathnode->path.pathtarget = rel->reltarget; - pathnode->path.param_info = NULL; /* not used in bitmap trees */ + + /* + * Identify the required outer rels as the union of what the child paths + * depend on. (Alternatively, we could insist that the caller pass this + * in, but it's more convenient and reliable to compute it here.) + */ + foreach(lc, bitmapquals) + { + Path *bitmapqual = (Path *) lfirst(lc); + + required_outer = bms_add_members(required_outer, + PATH_REQ_OUTER(bitmapqual)); + } + pathnode->path.param_info = get_baserel_parampathinfo(root, rel, + required_outer); /* * Currently, a BitmapHeapPath, BitmapAndPath, or BitmapOrPath will be @@ -3851,7 +3883,18 @@ do { \ !bms_overlap(PATH_REQ_OUTER(path), child_rel->top_parent_relids)) return path; - /* Reparameterize a copy of given path. */ + /* + * If possible, reparameterize the given path, making a copy. + * + * This function is currently only applied to the inner side of a nestloop + * join that is being partitioned by the partitionwise-join code. Hence, + * we need only support path types that plausibly arise in that context. + * (In particular, supporting sorted path types would be a waste of code + * and cycles: even if we translated them here, they'd just lose in + * subsequent cost comparisons.) If we do see an unsupported path type, + * that just means we won't be able to generate a partitionwise-join plan + * using that path type. + */ switch (nodeTag(path)) { case T_Path: @@ -3898,16 +3941,6 @@ do { \ } break; - case T_TidPath: - { - TidPath *tpath; - - FLAT_COPY_PATH(tpath, path, TidPath); - ADJUST_CHILD_ATTRS(tpath->tidquals); - new_path = (Path *) tpath; - } - break; - case T_ForeignPath: { ForeignPath *fpath; @@ -3998,37 +4031,6 @@ do { \ } break; - case T_MergeAppendPath: - { - MergeAppendPath *mapath; - - FLAT_COPY_PATH(mapath, path, MergeAppendPath); - REPARAMETERIZE_CHILD_PATH_LIST(mapath->subpaths); - new_path = (Path *) mapath; - } - break; - - case T_MaterialPath: - { - MaterialPath *mpath; - - FLAT_COPY_PATH(mpath, path, MaterialPath); - REPARAMETERIZE_CHILD_PATH(mpath->subpath); - new_path = (Path *) mpath; - } - break; - - case T_UniquePath: - { - UniquePath *upath; - - FLAT_COPY_PATH(upath, path, UniquePath); - REPARAMETERIZE_CHILD_PATH(upath->subpath); - ADJUST_CHILD_ATTRS(upath->uniq_exprs); - new_path = (Path *) upath; - } - break; - case T_GatherPath: { GatherPath *gpath; @@ -4039,16 +4041,6 @@ do { \ } break; - case T_GatherMergePath: - { - GatherMergePath *gmpath; - - FLAT_COPY_PATH(gmpath, path, GatherMergePath); - REPARAMETERIZE_CHILD_PATH(gmpath->subpath); - new_path = (Path *) gmpath; - } - break; - default: /* We don't know how to reparameterize this path. */ diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index 78ec12e4951..fa063485f55 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -2082,3 +2082,107 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = Filter: (b = 0) (16 rows) +-- +-- Test some other plan types in a partitionwise join (unfortunately, +-- we need larger tables to get the planner to choose these plan types) +-- +create temp table prtx1 (a integer, b integer, c integer) + partition by range (a); +create temp table prtx1_1 partition of prtx1 for values from (1) to (11); +create temp table prtx1_2 partition of prtx1 for values from (11) to (21); +create temp table prtx1_3 partition of prtx1 for values from (21) to (31); +create temp table prtx2 (a integer, b integer, c integer) + partition by range (a); +create temp table prtx2_1 partition of prtx2 for values from (1) to (11); +create temp table prtx2_2 partition of prtx2 for values from (11) to (21); +create temp table prtx2_3 partition of prtx2 for values from (21) to (31); +insert into prtx1 select 1 + i%30, i, i + from generate_series(1,1000) i; +insert into prtx2 select 1 + i%30, i, i + from generate_series(1,500) i, generate_series(1,10) j; +create index on prtx2 (b); +create index on prtx2 (c); +analyze prtx1; +analyze prtx2; +explain (costs off) +select * from prtx1 +where not exists (select 1 from prtx2 + where prtx2.a=prtx1.a and prtx2.b=prtx1.b and prtx2.c=123) + and a<20 and c=120; + QUERY PLAN +------------------------------------------------------------- + Append + -> Nested Loop Anti Join + -> Seq Scan on prtx1_1 + Filter: ((a < 20) AND (c = 120)) + -> Bitmap Heap Scan on prtx2_1 + Recheck Cond: ((b = prtx1_1.b) AND (c = 123)) + Filter: (a = prtx1_1.a) + -> BitmapAnd + -> Bitmap Index Scan on prtx2_1_b_idx + Index Cond: (b = prtx1_1.b) + -> Bitmap Index Scan on prtx2_1_c_idx + Index Cond: (c = 123) + -> Nested Loop Anti Join + -> Seq Scan on prtx1_2 + Filter: ((a < 20) AND (c = 120)) + -> Bitmap Heap Scan on prtx2_2 + Recheck Cond: ((b = prtx1_2.b) AND (c = 123)) + Filter: (a = prtx1_2.a) + -> BitmapAnd + -> Bitmap Index Scan on prtx2_2_b_idx + Index Cond: (b = prtx1_2.b) + -> Bitmap Index Scan on prtx2_2_c_idx + Index Cond: (c = 123) +(23 rows) + +select * from prtx1 +where not exists (select 1 from prtx2 + where prtx2.a=prtx1.a and prtx2.b=prtx1.b and prtx2.c=123) + and a<20 and c=120; + a | b | c +---+-----+----- + 1 | 120 | 120 +(1 row) + +explain (costs off) +select * from prtx1 +where not exists (select 1 from prtx2 + where prtx2.a=prtx1.a and (prtx2.b=prtx1.b+1 or prtx2.c=99)) + and a<20 and c=91; + QUERY PLAN +----------------------------------------------------------------- + Append + -> Nested Loop Anti Join + -> Seq Scan on prtx1_1 + Filter: ((a < 20) AND (c = 91)) + -> Bitmap Heap Scan on prtx2_1 + Recheck Cond: ((b = (prtx1_1.b + 1)) OR (c = 99)) + Filter: (a = prtx1_1.a) + -> BitmapOr + -> Bitmap Index Scan on prtx2_1_b_idx + Index Cond: (b = (prtx1_1.b + 1)) + -> Bitmap Index Scan on prtx2_1_c_idx + Index Cond: (c = 99) + -> Nested Loop Anti Join + -> Seq Scan on prtx1_2 + Filter: ((a < 20) AND (c = 91)) + -> Bitmap Heap Scan on prtx2_2 + Recheck Cond: ((b = (prtx1_2.b + 1)) OR (c = 99)) + Filter: (a = prtx1_2.a) + -> BitmapOr + -> Bitmap Index Scan on prtx2_2_b_idx + Index Cond: (b = (prtx1_2.b + 1)) + -> Bitmap Index Scan on prtx2_2_c_idx + Index Cond: (c = 99) +(23 rows) + +select * from prtx1 +where not exists (select 1 from prtx2 + where prtx2.a=prtx1.a and (prtx2.b=prtx1.b+1 or prtx2.c=99)) + and a<20 and c=91; + a | b | c +---+----+---- + 2 | 91 | 91 +(1 row) + diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql index 575ba7b8d4f..ffbb87af8b2 100644 --- a/src/test/regress/sql/partition_join.sql +++ b/src/test/regress/sql/partition_join.sql @@ -453,3 +453,48 @@ ANALYZE prt2; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + +-- +-- Test some other plan types in a partitionwise join (unfortunately, +-- we need larger tables to get the planner to choose these plan types) +-- +create temp table prtx1 (a integer, b integer, c integer) + partition by range (a); +create temp table prtx1_1 partition of prtx1 for values from (1) to (11); +create temp table prtx1_2 partition of prtx1 for values from (11) to (21); +create temp table prtx1_3 partition of prtx1 for values from (21) to (31); +create temp table prtx2 (a integer, b integer, c integer) + partition by range (a); +create temp table prtx2_1 partition of prtx2 for values from (1) to (11); +create temp table prtx2_2 partition of prtx2 for values from (11) to (21); +create temp table prtx2_3 partition of prtx2 for values from (21) to (31); +insert into prtx1 select 1 + i%30, i, i + from generate_series(1,1000) i; +insert into prtx2 select 1 + i%30, i, i + from generate_series(1,500) i, generate_series(1,10) j; +create index on prtx2 (b); +create index on prtx2 (c); +analyze prtx1; +analyze prtx2; + +explain (costs off) +select * from prtx1 +where not exists (select 1 from prtx2 + where prtx2.a=prtx1.a and prtx2.b=prtx1.b and prtx2.c=123) + and a<20 and c=120; + +select * from prtx1 +where not exists (select 1 from prtx2 + where prtx2.a=prtx1.a and prtx2.b=prtx1.b and prtx2.c=123) + and a<20 and c=120; + +explain (costs off) +select * from prtx1 +where not exists (select 1 from prtx2 + where prtx2.a=prtx1.a and (prtx2.b=prtx1.b+1 or prtx2.c=99)) + and a<20 and c=91; + +select * from prtx1 +where not exists (select 1 from prtx2 + where prtx2.a=prtx1.a and (prtx2.b=prtx1.b+1 or prtx2.c=99)) + and a<20 and c=91;