From e43e97f354e5749bfe61b3425d410f3b834ee00d Mon Sep 17 00:00:00 2001 From: Andy Fan Date: Wed, 6 Sep 2023 19:48:34 +0800 Subject: [PATCH v1] make add_paths_to_append_rel aware of startup cost. --- src/backend/optimizer/geqo/geqo_eval.c | 2 +- src/backend/optimizer/path/allpaths.c | 82 +++++++++++++++---- src/backend/optimizer/plan/planner.c | 50 +++++++---- src/backend/optimizer/plan/subselect.c | 2 +- src/backend/optimizer/prep/prepunion.c | 3 +- src/backend/optimizer/util/pathnode.c | 18 ++++ src/include/optimizer/pathnode.h | 1 + src/include/optimizer/paths.h | 6 +- src/include/optimizer/planner.h | 6 +- .../regress/expected/partition_aggregate.out | 22 +++++ src/test/regress/expected/partition_join.out | 27 ++++++ src/test/regress/expected/partition_prune.out | 52 ++++++++++++ src/test/regress/expected/union.out | 13 +++ src/test/regress/sql/partition_aggregate.sql | 3 + src/test/regress/sql/partition_join.sql | 3 + src/test/regress/sql/partition_prune.sql | 2 + src/test/regress/sql/union.sql | 6 ++ 17 files changed, 256 insertions(+), 42 deletions(-) diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c index a694ac4a130..bd6449af69d 100644 --- a/src/backend/optimizer/geqo/geqo_eval.c +++ b/src/backend/optimizer/geqo/geqo_eval.c @@ -265,7 +265,7 @@ merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, int num_gene, if (joinrel) { /* Create paths for partitionwise joins. */ - generate_partitionwise_join_paths(root, joinrel); + generate_partitionwise_join_paths(root, joinrel, joinrel->relids); /* * Except for the topmost scan/join rel, consider gathering diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 9bdc70c702e..4037f02c6f2 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -97,7 +97,7 @@ static void set_base_rel_pathlists(PlannerInfo *root); static void set_rel_size(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte); + Index rti, RangeTblEntry *rte, Relids top_relids); static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); static void create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel); @@ -116,7 +116,7 @@ static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, static void set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte); + Index rti, RangeTblEntry *rte, Relids top_relids); static void generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, List *live_childrels, List *all_child_pathkeys); @@ -351,7 +351,7 @@ set_base_rel_pathlists(PlannerInfo *root) if (rel->reloptkind != RELOPT_BASEREL) continue; - set_rel_pathlist(root, rel, rti, root->simple_rte_array[rti]); + set_rel_pathlist(root, rel, rti, root->simple_rte_array[rti], rel->relids); } } @@ -470,7 +470,7 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel, */ static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte) + Index rti, RangeTblEntry *rte, Relids top_relids) { if (IS_DUMMY_REL(rel)) { @@ -479,7 +479,7 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, else if (rte->inh) { /* It's an "append relation", process accordingly */ - set_append_rel_pathlist(root, rel, rti, rte); + set_append_rel_pathlist(root, rel, rti, rte, top_relids); } else { @@ -1233,7 +1233,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, */ static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte) + Index rti, RangeTblEntry *rte, Relids top_relids) { int parentRTindex = rti; List *live_childrels = NIL; @@ -1271,7 +1271,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, /* * Compute the child's access paths. */ - set_rel_pathlist(root, childrel, childRTindex, childRTE); + set_rel_pathlist(root, childrel, childRTindex, childRTE, top_relids); /* * If child is dummy, ignore it. @@ -1286,7 +1286,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, } /* Add paths to the append relation. */ - add_paths_to_append_rel(root, rel, live_childrels); + add_paths_to_append_rel(root, rel, live_childrels, top_relids); } @@ -1303,7 +1303,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, */ void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, - List *live_childrels) + List *live_childrels, Relids top_relids) { List *subpaths = NIL; bool subpaths_valid = true; @@ -1316,6 +1316,9 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, List *all_child_outers = NIL; ListCell *l; double partial_rows = -1; + bool consider_startup = rel->consider_startup && bms_equal(top_relids, root->all_baserels) && root->tuple_fraction > 0; + + consider_startup = consider_startup && enable_geqo; /* just for debug. */ /* If appropriate, consider parallel append */ pa_subpaths_valid = enable_parallel_append && rel->consider_parallel; @@ -1329,7 +1332,9 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, { RelOptInfo *childrel = lfirst(l); ListCell *lcp; + Path *cheapest_startup_path = NULL; Path *cheapest_partial_path = NULL; + Cost cheapest_partial_cost = 0; /* * If child has an unparameterized cheapest-total path, add that to @@ -1339,19 +1344,36 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, * With partitionwise aggregates, the child rel's pathlist may be * empty, so don't assume that a path exists here. */ - if (childrel->pathlist != NIL && + if (consider_startup && + (cheapest_startup_path = get_cheapest_fractional_path(childrel, + root->tuple_fraction, + false, false, false)) != NULL) + accumulate_append_subpath(cheapest_startup_path, + &subpaths, NULL); + else if (childrel->pathlist != NIL && childrel->cheapest_total_path->param_info == NULL) accumulate_append_subpath(childrel->cheapest_total_path, &subpaths, NULL); else subpaths_valid = false; + if (consider_startup && + (cheapest_partial_path = get_cheapest_fractional_path(childrel, + root->tuple_fraction, + false, true, false)) !=NULL) + { + accumulate_append_subpath(cheapest_partial_path, + &partial_subpaths, NULL); + cheapest_partial_cost = get_fractional_path_cost(cheapest_partial_path, root->tuple_fraction); + } + /* Same idea, but for a partial plan. */ - if (childrel->partial_pathlist != NIL) + else if (childrel->partial_pathlist != NIL) { cheapest_partial_path = linitial(childrel->partial_pathlist); accumulate_append_subpath(cheapest_partial_path, &partial_subpaths, NULL); + cheapest_partial_cost = cheapest_partial_path->total_cost; } else partial_subpaths_valid = false; @@ -1363,9 +1385,33 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, if (pa_subpaths_valid) { Path *nppath = NULL; + Cost nppath_cost = 0; + + /* + * Check if the cheapest non-partial and parallel safe path (nppath) + * is cheaper than cheapest_partial_path, if so use the nppath instead of + * cheapest_partial_path. Note the cheapest_partial_path has been startup + * aware already. + */ + if (consider_startup && cheapest_startup_path) + { + if (cheapest_startup_path->parallel_safe) + nppath = cheapest_startup_path; + else + nppath = get_cheapest_fractional_path(childrel, root->tuple_fraction, + false, false, true); - nppath = - get_cheapest_parallel_safe_total_inner(childrel->pathlist); + if (nppath) + nppath_cost = get_fractional_path_cost(nppath, + root->tuple_fraction); + } + else + { + nppath = + get_cheapest_parallel_safe_total_inner(childrel->pathlist); + if (nppath) + nppath_cost = nppath->total_cost; + } if (cheapest_partial_path == NULL && nppath == NULL) { @@ -1374,7 +1420,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, } else if (nppath == NULL || (cheapest_partial_path != NULL && - cheapest_partial_path->total_cost < nppath->total_cost)) + cheapest_partial_cost < nppath_cost)) { /* Partial path is cheaper or the only option. */ Assert(cheapest_partial_path != NULL); @@ -3468,7 +3514,7 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels) rel = (RelOptInfo *) lfirst(lc); /* Create paths for partitionwise joins. */ - generate_partitionwise_join_paths(root, rel); + generate_partitionwise_join_paths(root, rel, rel->relids); /* * Except for the topmost scan/join rel, consider gathering @@ -4295,7 +4341,7 @@ compute_parallel_worker(RelOptInfo *rel, double heap_pages, double index_pages, * generated here has a reference. */ void -generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel) +generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel, Relids top_relids) { List *live_children = NIL; int cnt_parts; @@ -4329,7 +4375,7 @@ generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel) continue; /* Make partitionwise join paths for this partitioned child-join. */ - generate_partitionwise_join_paths(root, child_rel); + generate_partitionwise_join_paths(root, child_rel, top_relids); /* If we failed to make any path for this child, we must give up. */ if (child_rel->pathlist == NIL) @@ -4364,7 +4410,7 @@ generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel) } /* Build additional paths for this rel from child-join paths. */ - add_paths_to_append_rel(root, rel, live_children); + add_paths_to_append_rel(root, rel, live_children, top_relids); list_free(live_children); } diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 44efb1f4ebc..f616b7f2d85 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -241,7 +241,8 @@ static void apply_scanjoin_target_to_paths(PlannerInfo *root, List *scanjoin_targets, List *scanjoin_targets_contain_srfs, bool scanjoin_target_parallel_safe, - bool tlist_same_exprs); + bool tlist_same_exprs, + Relids top_relids); static void create_partitionwise_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, @@ -415,7 +416,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* Select best Path and turn it into a Plan */ final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL); - best_path = get_cheapest_fractional_path(final_rel, tuple_fraction); + best_path = get_cheapest_fractional_path(final_rel, tuple_fraction, true, false, false); top_plan = create_plan(root, best_path); @@ -1633,7 +1634,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets, scanjoin_targets_contain_srfs, scanjoin_target_parallel_safe, - scanjoin_target_same_exprs); + scanjoin_target_same_exprs, + current_rel->relids); /* * Save the various upper-rel PathTargets we just computed into @@ -6326,28 +6328,41 @@ make_sort_input_target(PlannerInfo *root, * We assume set_cheapest() has been run on the given rel. */ Path * -get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction) +get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction, + bool allow_parameterized, bool look_partial, + bool must_parallel_safe) +/* + * NB: (just for review purpose) + * allow_parameterized = true and look_partial = false and must_parallel_safe=ture + * is the same behavior as before. + */ { - Path *best_path = rel->cheapest_total_path; + List *pathlist = look_partial ? rel->partial_pathlist : rel->pathlist; + Path *best_path = allow_parameterized ? linitial(pathlist) : NULL; ListCell *l; + double total_rows = ((Path *)linitial(rel->pathlist))->rows; /* If all tuples will be retrieved, just return the cheapest-total path */ if (tuple_fraction <= 0.0) return best_path; /* Convert absolute # of tuples to a fraction; no need to clamp to 0..1 */ - if (tuple_fraction >= 1.0 && best_path->rows > 0) - tuple_fraction /= best_path->rows; + if (tuple_fraction >= 1.0 && total_rows > 0) + tuple_fraction /= total_rows; - foreach(l, rel->pathlist) + foreach(l, pathlist) { Path *path = (Path *) lfirst(l); - if (path == rel->cheapest_total_path || - compare_fractional_path_costs(best_path, path, tuple_fraction) <= 0) + if (!allow_parameterized && !bms_is_empty(PATH_REQ_OUTER(path))) + continue; + + if (must_parallel_safe && !path->parallel_safe) continue; - best_path = path; + if (best_path == NULL || + compare_fractional_path_costs(best_path, path, tuple_fraction) > 0) + best_path = path; } return best_path; @@ -7540,7 +7555,8 @@ apply_scanjoin_target_to_paths(PlannerInfo *root, List *scanjoin_targets, List *scanjoin_targets_contain_srfs, bool scanjoin_target_parallel_safe, - bool tlist_same_exprs) + bool tlist_same_exprs, + Relids top_relids) { bool rel_is_partitioned = IS_PARTITIONED_REL(rel); PathTarget *scanjoin_target; @@ -7723,7 +7739,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root, child_scanjoin_targets, scanjoin_targets_contain_srfs, scanjoin_target_parallel_safe, - tlist_same_exprs); + tlist_same_exprs, top_relids); /* Save non-dummy children for Append paths. */ if (!IS_DUMMY_REL(child_rel)) @@ -7731,7 +7747,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root, } /* Build new paths for this relation by appending child paths. */ - add_paths_to_append_rel(root, rel, live_children); + add_paths_to_append_rel(root, rel, live_children, top_relids); } /* @@ -7888,7 +7904,8 @@ create_partitionwise_grouping_paths(PlannerInfo *root, Assert(partially_grouped_live_children != NIL); add_paths_to_append_rel(root, partially_grouped_rel, - partially_grouped_live_children); + partially_grouped_live_children, + input_rel->relids); /* * We need call set_cheapest, since the finalization step will use the @@ -7903,7 +7920,8 @@ create_partitionwise_grouping_paths(PlannerInfo *root, { Assert(grouped_live_children != NIL); - add_paths_to_append_rel(root, grouped_rel, grouped_live_children); + /* group_rel->relids is not set. */ + add_paths_to_append_rel(root, grouped_rel, grouped_live_children, input_rel->relids); } } diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 7a9fe88fec3..c704719a07d 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -231,7 +231,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, * seems no reason to postpone doing that. */ final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL); - best_path = get_cheapest_fractional_path(final_rel, tuple_fraction); + best_path = get_cheapest_fractional_path(final_rel, tuple_fraction, true, false, false); plan = create_plan(subroot, best_path); diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 0c68ec011be..f435d4b4181 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -283,7 +283,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, * set_subquery_pathlist). */ subpath = get_cheapest_fractional_path(final_rel, - root->tuple_fraction); + root->tuple_fraction, + true, false, false); /* * Stick a SubqueryScanPath atop that. diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 211ba65389d..506c0c98583 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -120,6 +120,9 @@ compare_fractional_path_costs(Path *path1, Path *path2, Cost cost1, cost2; + if (path1 == path2) + return 0; + if (fraction <= 0.0 || fraction >= 1.0) return compare_path_costs(path1, path2, TOTAL_COST); cost1 = path1->startup_cost + @@ -133,6 +136,21 @@ compare_fractional_path_costs(Path *path1, Path *path2, return 0; } +Cost +get_fractional_path_cost(Path *path1, double fraction) +{ + double total_rows = path1->rows; + + if (total_rows <= 0) + return path1->total_cost; + + if (fraction >= 1.0) + fraction = fraction / path1->rows; + + return path1->startup_cost + fraction * (path1->total_cost - path1->startup_cost); +} + + /* * compare_path_costs_fuzzily * Compare the costs of two paths to see if either can be said to diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 6e557bebc44..b1544a13cff 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -25,6 +25,7 @@ extern int compare_path_costs(Path *path1, Path *path2, CostSelector criterion); extern int compare_fractional_path_costs(Path *path1, Path *path2, double fraction); +extern Cost get_fractional_path_cost(Path *path1, double fraction); extern void set_cheapest(RelOptInfo *parent_rel); extern void add_path(RelOptInfo *parent_rel, Path *new_path); extern bool add_path_precheck(RelOptInfo *parent_rel, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 50bc3b503a6..15416a650c4 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -61,8 +61,8 @@ extern int compute_parallel_worker(RelOptInfo *rel, double heap_pages, extern void create_partial_bitmap_paths(PlannerInfo *root, RelOptInfo *rel, Path *bitmapqual); extern void generate_partitionwise_join_paths(PlannerInfo *root, - RelOptInfo *rel); - + RelOptInfo *rel, + Relids top_relids); #ifdef OPTIMIZER_DEBUG extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel); #endif @@ -261,6 +261,6 @@ extern PathKey *make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, int strategy, bool nulls_first); extern void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, - List *live_childrels); + List *live_childrels, Relids top_relids); #endif /* PATHS_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index fc2e15496dd..b539fa585f4 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -54,8 +54,10 @@ extern bool limit_needed(Query *parse); extern void mark_partial_aggref(Aggref *agg, AggSplit aggsplit); extern Path *get_cheapest_fractional_path(RelOptInfo *rel, - double tuple_fraction); - + double tuple_fraction, + bool allow_parameterized, + bool look_partial, + bool must_parallel_safe); extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); #endif /* PLANNER_H */ diff --git a/src/test/regress/expected/partition_aggregate.out b/src/test/regress/expected/partition_aggregate.out index 1b900fddf8e..ccd5108a439 100644 --- a/src/test/regress/expected/partition_aggregate.out +++ b/src/test/regress/expected/partition_aggregate.out @@ -55,6 +55,28 @@ SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVI 0008 | 2000 | 14.0000000000000000 | 250 | 0 | 26 (6 rows) +EXPLAIN (COSTS OFF) +SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3 LIMIT 1; + QUERY PLAN +-------------------------------------------------------------------- + Limit + -> Sort + Sort Key: pagg_tab.c, (sum(pagg_tab.a)), (avg(pagg_tab.b)) + -> Append + -> HashAggregate + Group Key: pagg_tab.c + Filter: (avg(pagg_tab.d) < '15'::numeric) + -> Seq Scan on pagg_tab_p1 pagg_tab + -> HashAggregate + Group Key: pagg_tab_1.c + Filter: (avg(pagg_tab_1.d) < '15'::numeric) + -> Seq Scan on pagg_tab_p2 pagg_tab_1 + -> HashAggregate + Group Key: pagg_tab_2.c + Filter: (avg(pagg_tab_2.d) < '15'::numeric) + -> Seq Scan on pagg_tab_p3 pagg_tab_2 +(16 rows) + -- When GROUP BY clause does not match; partial aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3; diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index 6560fe2416f..c41c26dc482 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -62,6 +62,33 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 450 | 0450 | 450 | 0450 (4 rows) +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 LIMIT 1; + QUERY PLAN +--------------------------------------------------------------- + Limit + -> Merge Append + Sort Key: t1.a + -> Nested Loop + Join Filter: (t1_1.a = t2_1.b) + -> Index Scan using iprt2_p1_b on prt2_p1 t2_1 + -> Materialize + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Nested Loop + Join Filter: (t1_2.a = t2_2.b) + -> Index Scan using iprt2_p2_b on prt2_p2 t2_2 + -> Materialize + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Nested Loop + Join Filter: (t1_3.a = t2_3.b) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + -> Materialize + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) +(21 rows) + -- left outer join, 3-way EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM prt1 t1 diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 1eb347503aa..231384d6ed1 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -143,6 +143,58 @@ explain (costs off) select * from lp where a not in ('a', 'd'); Filter: (a <> ALL ('{a,d}'::bpchar[])) (9 rows) +explain (costs off) select * from lp limit 3; + QUERY PLAN +----------------------------------------- + Limit + -> Append + -> Seq Scan on lp_ad lp_1 + -> Seq Scan on lp_bc lp_2 + -> Seq Scan on lp_ef lp_3 + -> Seq Scan on lp_g lp_4 + -> Seq Scan on lp_null lp_5 + -> Seq Scan on lp_default lp_6 +(8 rows) + +explain (costs off) select * from lp where exists (select 1 from lp where random() > 0.1) limit 3; + QUERY PLAN +-------------------------------------------------------------- + Limit + InitPlan 1 (returns $0) + -> Append + -> Seq Scan on lp_ad lp_8 + Filter: (random() > '0.1'::double precision) + -> Seq Scan on lp_bc lp_9 + Filter: (random() > '0.1'::double precision) + -> Seq Scan on lp_ef lp_10 + Filter: (random() > '0.1'::double precision) + -> Seq Scan on lp_g lp_11 + Filter: (random() > '0.1'::double precision) + -> Seq Scan on lp_null lp_12 + Filter: (random() > '0.1'::double precision) + -> Seq Scan on lp_default lp_13 + Filter: (random() > '0.1'::double precision) + -> Append + -> Result + One-Time Filter: $0 + -> Seq Scan on lp_ad lp_1 + -> Result + One-Time Filter: $0 + -> Seq Scan on lp_bc lp_2 + -> Result + One-Time Filter: $0 + -> Seq Scan on lp_ef lp_3 + -> Result + One-Time Filter: $0 + -> Seq Scan on lp_g lp_4 + -> Result + One-Time Filter: $0 + -> Seq Scan on lp_null lp_5 + -> Result + One-Time Filter: $0 + -> Seq Scan on lp_default lp_6 +(34 rows) + -- collation matches the partitioning collation, pruning works create table coll_pruning (a text collate "C") partition by list (a); create table coll_pruning_a partition of coll_pruning for values in ('a'); diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out index e2613d6777e..6c52da4bf35 100644 --- a/src/test/regress/expected/union.out +++ b/src/test/regress/expected/union.out @@ -1432,3 +1432,16 @@ where (x = 0) or (q1 >= q2 and q1 <= q2); 4567890123456789 | 4567890123456789 | 1 (6 rows) +explain (costs off) +(select * from tenk1 order by hundred) +UNION ALL +(select * from tenk1 order by hundred) +limit 3; + QUERY PLAN +------------------------------------------------------------- + Limit + -> Append + -> Index Scan using tenk1_hundred on tenk1 + -> Index Scan using tenk1_hundred on tenk1 tenk1_1 +(4 rows) + diff --git a/src/test/regress/sql/partition_aggregate.sql b/src/test/regress/sql/partition_aggregate.sql index ab070fee244..a2943a62961 100644 --- a/src/test/regress/sql/partition_aggregate.sql +++ b/src/test/regress/sql/partition_aggregate.sql @@ -30,6 +30,9 @@ EXPLAIN (COSTS OFF) SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3; SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3; +EXPLAIN (COSTS OFF) +SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3 LIMIT 1; + -- When GROUP BY clause does not match; partial aggregation is performed for each partition. EXPLAIN (COSTS OFF) SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3; diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql index 48daf3aee39..88b6e5168ae 100644 --- a/src/test/regress/sql/partition_join.sql +++ b/src/test/regress/sql/partition_join.sql @@ -34,6 +34,9 @@ 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; 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; +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 LIMIT 1; + -- left outer join, 3-way EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM prt1 t1 diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index d1c60b8fe9d..d68b7c999ba 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -24,6 +24,8 @@ explain (costs off) select * from lp where a is not null and (a = 'a' or a = 'c' explain (costs off) select * from lp where a <> 'g'; explain (costs off) select * from lp where a <> 'a' and a <> 'd'; explain (costs off) select * from lp where a not in ('a', 'd'); +explain (costs off) select * from lp limit 3; +explain (costs off) select * from lp where exists (select 1 from lp where random() > 0.1) limit 3; -- collation matches the partitioning collation, pruning works create table coll_pruning (a text collate "C") partition by list (a); diff --git a/src/test/regress/sql/union.sql b/src/test/regress/sql/union.sql index ca8c9b4d128..654667d1f59 100644 --- a/src/test/regress/sql/union.sql +++ b/src/test/regress/sql/union.sql @@ -540,3 +540,9 @@ select * from union all select *, 1 as x from int8_tbl b) ss where (x = 0) or (q1 >= q2 and q1 <= q2); + +explain (costs off) +(select * from tenk1 order by hundred) +UNION ALL +(select * from tenk1 order by hundred) +limit 3; -- 2.21.0