diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 11de286e60..cf08fcac51 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -1924,11 +1924,13 @@ find_mergeclauses_for_outer_pathkeys(PlannerInfo *root, * Since we assume here that a sort is required, there is no particular use * in matching any available ordering of the outerrel. (joinpath.c has an * entirely separate code path for considering sort-free mergejoins.) Rather, - * it's interesting to try to match the requested query_pathkeys so that a - * second output sort may be avoided; and failing that, we try to list "more - * popular" keys (those with the most unmatched EquivalenceClass peers) - * earlier, in hopes of making the resulting ordering useful for as many - * higher-level mergejoins as possible. + * it's interesting to try to match, or match a prefix of the requested + * query_pathkeys so that a second output sort may be avoided. We can get + * away with just a prefix of the query_pathkeys when that prefix covers the + * entire join condition. Failing that, we try to list "more popular" keys + * (those with the most unmatched EquivalenceClass peers) earlier, in hopes + * of making the resulting ordering useful for as many higher-level mergejoins + * as possible. */ List * select_outer_pathkeys_for_merge(PlannerInfo *root, @@ -1998,11 +2000,16 @@ select_outer_pathkeys_for_merge(PlannerInfo *root, /* * Find out if we have all the ECs mentioned in query_pathkeys; if so we - * can generate a sort order that's also useful for final output. There is - * no percentage in a partial match, though, so we have to have 'em all. + * can generate a sort order that's also useful for final output. If we + * only have a prefix of the query_pathkeys, and that prefix is the entire + * join condition, then it's useful to use the prefix as the pathkeys as + * this increases the chances that an incremental sort will be able to be + * used by the upper planner. */ if (root->query_pathkeys) { + int matches = 0; + foreach(lc, root->query_pathkeys) { PathKey *query_pathkey = (PathKey *) lfirst(lc); @@ -2015,6 +2022,8 @@ select_outer_pathkeys_for_merge(PlannerInfo *root, } if (j >= necs) break; /* didn't find match */ + + matches++; } /* if we got to the end of the list, we have them all */ if (lc == NULL) @@ -2037,6 +2046,22 @@ select_outer_pathkeys_for_merge(PlannerInfo *root, } } } + + /* + * If we didn't find a full match, but matched all of the join clauses + * then we'll make use of these as presorted input is better than + * nothing for the upper planner. + */ + else if (matches == nClauses) + { + pathkeys = list_copy_head(root->query_pathkeys, matches); + + /* we have all of the join pathkeys, so nothing more to do */ + pfree(ecs); + pfree(scores); + + return pathkeys; + } } /* diff --git a/src/test/regress/expected/tuplesort.out b/src/test/regress/expected/tuplesort.out index ef79574ecf..a2efa179fc 100644 --- a/src/test/regress/expected/tuplesort.out +++ b/src/test/regress/expected/tuplesort.out @@ -622,17 +622,18 @@ EXPLAIN (COSTS OFF) :qry; -> GroupAggregate Group Key: a.col12 Filter: (count(*) > 1) - -> Sort + -> Incremental Sort Sort Key: a.col12 DESC, a.col1 + Presorted Key: a.col12 -> Merge Join Merge Cond: (a.col12 = b.col12) -> Sort - Sort Key: a.col12 + Sort Key: a.col12 DESC -> Seq Scan on test_mark_restore a -> Sort - Sort Key: b.col12 + Sort Key: b.col12 DESC -> Seq Scan on test_mark_restore b -(16 rows) +(17 rows) :qry; col12 | count | count | count | count | count @@ -660,17 +661,18 @@ EXPLAIN (COSTS OFF) :qry; -> GroupAggregate Group Key: a.col12 Filter: (count(*) > 1) - -> Sort + -> Incremental Sort Sort Key: a.col12 DESC, a.col1 + Presorted Key: a.col12 -> Merge Join Merge Cond: (a.col12 = b.col12) -> Sort - Sort Key: a.col12 + Sort Key: a.col12 DESC -> Seq Scan on test_mark_restore a -> Sort - Sort Key: b.col12 + Sort Key: b.col12 DESC -> Seq Scan on test_mark_restore b -(16 rows) +(17 rows) :qry; col12 | count | count | count | count | count