From 773db22269d474fab46d25e9e15b1e55252cf92c Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Thu, 8 May 2025 18:20:18 +0900 Subject: Suppress unnecessary explicit sorting for EPQ mergejoin path When building a ForeignPath for a joinrel, if there's a possibility that EvalPlanQual will be executed, we must identify a suitable path for EPQ checks. If the outer or inner path of the chosen path is a ForeignPath representing a pushed-down join, we replace it with its fdw_outerpath to ensure that the EPQ check path consists entirely of local joins. If the chosen path is a MergePath, and its outer or inner path is a ForeignPath that is not already well enough ordered, the MergePath will have non-NIL outersortkeys or innersortkeys indicating the desired ordering to be created by an explicit Sort node. If we then replace the outer or inner path with its corresponding fdw_outerpath, and that path is already sufficiently ordered, we end up in an inconsistent state: the MergePath has non-NIL outersortkeys or innersortkeys, and its input path is already properly ordered. This inconsistency can result in an Assert failure or the addition of a redundant Sort node. To fix, check if the new outer or inner path of a MergePath is already properly sorted, and set its outersortkeys or innersortkeys to NIL if so. Bug: #18902 Reported-by: Nikita Kalinin Author: Richard Guo Reviewed-by: Tender Wang Discussion: https://postgr.es/m/18902-71c1bed2b9f7c46f@postgresql.org --- src/backend/foreign/foreign.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'src') diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index f0835fc3070..9c57f7a259c 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -22,6 +22,7 @@ #include "foreign/foreign.h" #include "funcapi.h" #include "miscadmin.h" +#include "optimizer/paths.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/memutils.h" @@ -808,7 +809,23 @@ GetExistingLocalJoinPath(RelOptInfo *joinrel) foreign_path = (ForeignPath *) joinpath->outerjoinpath; if (IS_JOIN_REL(foreign_path->path.parent)) + { joinpath->outerjoinpath = foreign_path->fdw_outerpath; + + if (joinpath->path.pathtype == T_MergeJoin) + { + MergePath *merge_path = (MergePath *) joinpath; + + /* + * If the new outer path is already well enough ordered + * for the mergejoin, we can skip doing an explicit sort. + */ + if (merge_path->outersortkeys && + pathkeys_contained_in(merge_path->outersortkeys, + joinpath->outerjoinpath->pathkeys)) + merge_path->outersortkeys = NIL; + } + } } if (IsA(joinpath->innerjoinpath, ForeignPath)) @@ -817,7 +834,23 @@ GetExistingLocalJoinPath(RelOptInfo *joinrel) foreign_path = (ForeignPath *) joinpath->innerjoinpath; if (IS_JOIN_REL(foreign_path->path.parent)) + { joinpath->innerjoinpath = foreign_path->fdw_outerpath; + + if (joinpath->path.pathtype == T_MergeJoin) + { + MergePath *merge_path = (MergePath *) joinpath; + + /* + * If the new inner path is already well enough ordered + * for the mergejoin, we can skip doing an explicit sort. + */ + if (merge_path->innersortkeys && + pathkeys_contained_in(merge_path->innersortkeys, + joinpath->innerjoinpath->pathkeys)) + merge_path->innersortkeys = NIL; + } + } } return (Path *) joinpath; -- cgit v1.2.3