Hi, Yet another thing I noticed while working on [1] is this in grouping_planner:
/* * If the input rel is marked consider_parallel and there's nothing that's * not parallel-safe in the LIMIT clause, then the final_rel can be marked * consider_parallel as well. Note that if the query has rowMarks or is * not a SELECT, consider_parallel will be false for every relation in the * query. */ if (current_rel->consider_parallel && is_parallel_safe(root, parse->limitOffset) && is_parallel_safe(root, parse->limitCount)) final_rel->consider_parallel = true; If there is a need to add a LIMIT node, we don't consider generating partial paths for the final relation below (see commit 0927d2f46ddd4cf7d6bf2cc84b3be923e0aedc52), so it seems unnecessary anymore to assess the parallel-safety of the LIMIT and OFFSET clauses. To save cycles, why not remove those tests from that function like the attached? Best regards, Etsuro Fujita [1] https://commitfest.postgresql.org/22/1950/
*** a/src/backend/optimizer/plan/planner.c --- b/src/backend/optimizer/plan/planner.c *************** *** 2141,2155 **** grouping_planner(PlannerInfo *root, bool inheritance_update, final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL); /* ! * If the input rel is marked consider_parallel and there's nothing that's ! * not parallel-safe in the LIMIT clause, then the final_rel can be marked ! * consider_parallel as well. Note that if the query has rowMarks or is ! * not a SELECT, consider_parallel will be false for every relation in the ! * query. */ ! if (current_rel->consider_parallel && ! is_parallel_safe(root, parse->limitOffset) && ! is_parallel_safe(root, parse->limitCount)) final_rel->consider_parallel = true; /* --- 2141,2152 ---- final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL); /* ! * If the input rel is marked consider_parallel and there's no need to add ! * a LIMIT node, then the final_rel can be marked consider_parallel as ! * well. Note that if the query has rowMarks or is not a SELECT, ! * consider_parallel will be false for every relation in the query. */ ! if (current_rel->consider_parallel && !limit_needed(parse)) final_rel->consider_parallel = true; /* *************** *** 2263,2272 **** grouping_planner(PlannerInfo *root, bool inheritance_update, * Generate partial paths for final_rel, too, if outer query levels might * be able to make use of them. */ ! if (final_rel->consider_parallel && root->query_level > 1 && ! !limit_needed(parse)) { ! Assert(!parse->rowMarks && parse->commandType == CMD_SELECT); foreach(lc, current_rel->partial_pathlist) { Path *partial_path = (Path *) lfirst(lc); --- 2260,2269 ---- * Generate partial paths for final_rel, too, if outer query levels might * be able to make use of them. */ ! if (final_rel->consider_parallel && root->query_level > 1) { ! Assert(!parse->rowMarks && !limit_needed(parse) && ! parse->commandType == CMD_SELECT); foreach(lc, current_rel->partial_pathlist) { Path *partial_path = (Path *) lfirst(lc);