* join quals here, except for obtaining the scan selectivity estimate which
* is really essential (but fortunately, use of caching keeps the cost of
* getting that down to something reasonable).
- * We also assume that cost_sort is cheap enough to use here.
+ * We also assume that cost_sort/cost_incremental_sort is cheap enough to use
+ * here.
*
* 'workspace' is to be filled with startup_cost, total_cost, and perhaps
* other data to be used by final_cost_mergejoin
outerendsel,
innerstartsel,
innerendsel;
- Path sort_path; /* dummy for result of cost_sort */
+ Path sort_path; /* dummy for result of
+ * cost_sort/cost_incremental_sort */
/* Protect some assumptions below that rowcounts aren't zero */
if (outer_path_rows <= 0)
if (outersortkeys) /* do we need to sort outer? */
{
- cost_sort(&sort_path,
- root,
- outersortkeys,
- outer_path->disabled_nodes,
- outer_path->total_cost,
- outer_path_rows,
- outer_path->pathtarget->width,
- 0.0,
- work_mem,
- -1.0);
+ bool use_incremental_sort = false;
+ int presorted_keys;
+
+ /*
+ * We choose to use incremental sort if it is enabled and there are
+ * presorted keys; otherwise we use full sort.
+ */
+ if (enable_incremental_sort)
+ {
+ bool is_sorted PG_USED_FOR_ASSERTS_ONLY;
+
+ is_sorted = pathkeys_count_contained_in(outersortkeys,
+ outer_path->pathkeys,
+ &presorted_keys);
+ Assert(!is_sorted);
+
+ if (presorted_keys > 0)
+ use_incremental_sort = true;
+ }
+
+ if (!use_incremental_sort)
+ {
+ cost_sort(&sort_path,
+ root,
+ outersortkeys,
+ outer_path->disabled_nodes,
+ outer_path->total_cost,
+ outer_path_rows,
+ outer_path->pathtarget->width,
+ 0.0,
+ work_mem,
+ -1.0);
+ }
+ else
+ {
+ cost_incremental_sort(&sort_path,
+ root,
+ outersortkeys,
+ presorted_keys,
+ outer_path->disabled_nodes,
+ outer_path->startup_cost,
+ outer_path->total_cost,
+ outer_path_rows,
+ outer_path->pathtarget->width,
+ 0.0,
+ work_mem,
+ -1.0);
+ }
disabled_nodes += sort_path.disabled_nodes;
startup_cost += sort_path.startup_cost;
startup_cost += (sort_path.total_cost - sort_path.startup_cost)
if (innersortkeys) /* do we need to sort inner? */
{
+ /*
+ * We do not consider incremental sort for inner path, because
+ * incremental sort does not support mark/restore.
+ */
+
cost_sort(&sort_path,
root,
innersortkeys,
static void copy_plan_costsize(Plan *dest, Plan *src);
static void label_sort_with_costsize(PlannerInfo *root, Sort *plan,
double limit_tuples);
+static void label_incrementalsort_with_costsize(PlannerInfo *root, IncrementalSort *plan,
+ List *pathkeys, double limit_tuples);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid,
TableSampleClause *tsc);
if (best_path->outersortkeys)
{
Relids outer_relids = outer_path->parent->relids;
- Sort *sort = make_sort_from_pathkeys(outer_plan,
+ Plan *sort_plan;
+ bool use_incremental_sort = false;
+ int presorted_keys;
+
+ /*
+ * We choose to use incremental sort if it is enabled and there are
+ * presorted keys; otherwise we use full sort.
+ */
+ if (enable_incremental_sort)
+ {
+ bool is_sorted PG_USED_FOR_ASSERTS_ONLY;
+
+ is_sorted = pathkeys_count_contained_in(best_path->outersortkeys,
+ outer_path->pathkeys,
+ &presorted_keys);
+ Assert(!is_sorted);
+
+ if (presorted_keys > 0)
+ use_incremental_sort = true;
+ }
+
+ if (!use_incremental_sort)
+ {
+ sort_plan = (Plan *)
+ make_sort_from_pathkeys(outer_plan,
+ best_path->outersortkeys,
+ outer_relids);
+
+ label_sort_with_costsize(root, (Sort *) sort_plan, -1.0);
+ }
+ else
+ {
+ sort_plan = (Plan *)
+ make_incrementalsort_from_pathkeys(outer_plan,
best_path->outersortkeys,
- outer_relids);
+ outer_relids,
+ presorted_keys);
- label_sort_with_costsize(root, sort, -1.0);
- outer_plan = (Plan *) sort;
+ label_incrementalsort_with_costsize(root,
+ (IncrementalSort *) sort_plan,
+ best_path->outersortkeys,
+ -1.0);
+ }
+
+ outer_plan = sort_plan;
outerpathkeys = best_path->outersortkeys;
}
else
if (best_path->innersortkeys)
{
+ /*
+ * We do not consider incremental sort for inner path, because
+ * incremental sort does not support mark/restore.
+ */
+
Relids inner_relids = inner_path->parent->relids;
Sort *sort = make_sort_from_pathkeys(inner_plan,
best_path->innersortkeys,
Plan *lefttree = plan->plan.lefttree;
Path sort_path; /* dummy for result of cost_sort */
- /*
- * This function shouldn't have to deal with IncrementalSort plans because
- * they are only created from corresponding Path nodes.
- */
Assert(IsA(plan, Sort));
cost_sort(&sort_path, root, NIL,
plan->plan.parallel_safe = lefttree->parallel_safe;
}
+/*
+ * Same as label_sort_with_costsize, but labels the IncrementalSort node
+ * instead.
+ */
+static void
+label_incrementalsort_with_costsize(PlannerInfo *root, IncrementalSort *plan,
+ List *pathkeys, double limit_tuples)
+{
+ Plan *lefttree = plan->sort.plan.lefttree;
+ Path sort_path; /* dummy for result of cost_incremental_sort */
+
+ Assert(IsA(plan, IncrementalSort));
+
+ cost_incremental_sort(&sort_path, root, pathkeys,
+ plan->nPresortedCols,
+ plan->sort.plan.disabled_nodes,
+ lefttree->startup_cost,
+ lefttree->total_cost,
+ lefttree->plan_rows,
+ lefttree->plan_width,
+ 0.0,
+ work_mem,
+ limit_tuples);
+ plan->sort.plan.startup_cost = sort_path.startup_cost;
+ plan->sort.plan.total_cost = sort_path.total_cost;
+ plan->sort.plan.plan_rows = lefttree->plan_rows;
+ plan->sort.plan.plan_width = lefttree->plan_width;
+ plan->sort.plan.parallel_aware = false;
+ plan->sort.plan.parallel_safe = lefttree->parallel_safe;
+}
+
/*
* bitmap_subplan_mark_shared
* Set isshared flag in bitmap subplan so that it will be created in
-> Index Scan using btg_x_y_idx on btg
(6 rows)
--- Utilize the ordering of merge join to avoid a full Sort operation
+-- Utilize the ordering of merge join to avoid a Sort operation
SET enable_hashjoin = off;
SET enable_nestloop = off;
EXPLAIN (COSTS OFF)
SELECT count(*)
- FROM btg t1 JOIN btg t2 ON t1.z = t2.z AND t1.w = t2.w AND t1.x = t2.x
- GROUP BY t1.x, t1.y, t1.z, t1.w;
- QUERY PLAN
--------------------------------------------------------------------------------
+ FROM btg t1 JOIN btg t2 ON t1.w = t2.w AND t1.x = t2.x AND t1.z = t2.z
+ GROUP BY t1.w, t1.z, t1.x;
+ QUERY PLAN
+-------------------------------------------------------------------------
GroupAggregate
- Group Key: t1.z, t1.w, t1.x, t1.y
- -> Incremental Sort
- Sort Key: t1.z, t1.w, t1.x, t1.y
- Presorted Key: t1.z, t1.w, t1.x
- -> Merge Join
- Merge Cond: ((t1.z = t2.z) AND (t1.w = t2.w) AND (t1.x = t2.x))
- -> Sort
- Sort Key: t1.z, t1.w, t1.x
- -> Index Scan using btg_x_y_idx on btg t1
- -> Sort
- Sort Key: t2.z, t2.w, t2.x
- -> Index Scan using btg_x_y_idx on btg t2
-(13 rows)
+ Group Key: t1.x, t1.w, t1.z
+ -> Merge Join
+ Merge Cond: ((t1.x = t2.x) AND (t1.w = t2.w) AND (t1.z = t2.z))
+ -> Incremental Sort
+ Sort Key: t1.x, t1.w, t1.z
+ Presorted Key: t1.x
+ -> Index Scan using btg_x_y_idx on btg t1
+ -> Sort
+ Sort Key: t2.x, t2.w, t2.z
+ -> Index Scan using btg_x_y_idx on btg t2
+(11 rows)
RESET enable_nestloop;
RESET enable_hashjoin;
Order By: (a <-> '(5,5)'::point)
(6 rows)
+-- Ensure we get an incremental sort on the outer side of the mergejoin
+explain (costs off)
+select * from
+ (select * from tenk1 order by four) t1 join tenk1 t2 on t1.four = t2.four and t1.two = t2.two
+order by t1.four, t1.two limit 1;
+ QUERY PLAN
+-----------------------------------------------------------------------
+ Limit
+ -> Merge Join
+ Merge Cond: ((tenk1.four = t2.four) AND (tenk1.two = t2.two))
+ -> Incremental Sort
+ Sort Key: tenk1.four, tenk1.two
+ Presorted Key: tenk1.four
+ -> Sort
+ Sort Key: tenk1.four
+ -> Seq Scan on tenk1
+ -> Sort
+ Sort Key: t2.four, t2.two
+ -> Seq Scan on tenk1 t2
+(12 rows)
+
FROM (SELECT * FROM btg ORDER BY x, y, w, z) AS q1
GROUP BY w, x, z, y;
--- Utilize the ordering of merge join to avoid a full Sort operation
+-- Utilize the ordering of merge join to avoid a Sort operation
SET enable_hashjoin = off;
SET enable_nestloop = off;
EXPLAIN (COSTS OFF)
SELECT count(*)
- FROM btg t1 JOIN btg t2 ON t1.z = t2.z AND t1.w = t2.w AND t1.x = t2.x
- GROUP BY t1.x, t1.y, t1.z, t1.w;
+ FROM btg t1 JOIN btg t2 ON t1.w = t2.w AND t1.x = t2.x AND t1.z = t2.z
+ GROUP BY t1.w, t1.z, t1.x;
RESET enable_nestloop;
RESET enable_hashjoin;
-- Ensure we get an incremental sort plan for both of the following queries
explain (costs off) select a, b, a <-> point(5, 5) dist from point_table order by dist, b limit 1;
explain (costs off) select a, b, a <-> point(5, 5) dist from point_table order by dist, b desc limit 1;
+
+-- Ensure we get an incremental sort on the outer side of the mergejoin
+explain (costs off)
+select * from
+ (select * from tenk1 order by four) t1 join tenk1 t2 on t1.four = t2.four and t1.two = t2.two
+order by t1.four, t1.two limit 1;