From 46212f3111b0290ded005ab3c6e3bc5d914742c8 Mon Sep 17 00:00:00 2001 From: Tender Wang Date: Mon, 26 Oct 2026 10:59:30 +0800 Subject: [PATCH v2] Clarify row mark handling in self-join elimination When row marks are needed, preprocess_rowmarks() creates one for every non-target base relation. Since target relations are not candidates for self-join elimination, a pair of candidate relations must either both have row marks or both lack them. Therefore, the code that transfers the removed relation's row mark when the kept relation has none is unreachable. Remove that code, add assertions for the invariant, and expand the comments explaining why relations with different row mark types are not merged. --- src/backend/optimizer/plan/analyzejoins.c | 40 +++++++++-------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c index 7aad91fd34e..dcfe5097586 100644 --- a/src/backend/optimizer/plan/analyzejoins.c +++ b/src/backend/optimizer/plan/analyzejoins.c @@ -1287,28 +1287,13 @@ remove_self_join_rel(PlannerInfo *root, Assert(found_relid); /* - * If the removed relation has a row mark, transfer it to the remaining - * one. - * - * If both rels have row marks, just keep the one corresponding to the - * remaining relation because we verified earlier that they have the same - * strength. + * If the relations have row marks, discard the removed relation's mark. + * The caller has verified that both marks exist and have the same type. */ if (rmark) { - if (kmark) - { - Assert(kmark->markType == rmark->markType); - - root->rowMarks = list_delete_ptr(root->rowMarks, rmark); - } - else - { - /* Shouldn't have inheritance children yet. */ - Assert(rmark->rti == rmark->prti); - - rmark->rti = rmark->prti = toKeep->relid; - } + Assert(rmark->markType == kmark->markType); + root->rowMarks = list_delete_ptr(root->rowMarks, rmark); } } @@ -1827,10 +1812,16 @@ remove_self_joins_one_group(PlannerInfo *root, Relids relids) continue; /* - * Check Row Marks equivalence. We can't remove the join if the - * relations have row marks of different strength (e.g., one is - * locked FOR UPDATE, and another just has ROW_MARK_REFERENCE for - * EvalPlanQual rechecking). + * If row marks are needed, preprocess_rowmarks() creates one for + * every non-target base relation. Target relations aren't SJE + * candidates, so these relations must either both have row marks or + * both lack them. + * + * Don't merge relations with different row mark types. In + * particular, a locking row mark can fetch an updated version of a + * tuple, while a non-locking row mark preserves the version found by + * the original scan. We also conservatively reject different locking + * row mark types rather than try to combine their lock strengths. */ foreach(lc, root->rowMarks) { @@ -1850,7 +1841,8 @@ remove_self_joins_one_group(PlannerInfo *root, Relids relids) if (kmark && rmark) break; } - if (kmark && rmark && kmark->markType != rmark->markType) + Assert((kmark == NULL) == (rmark == NULL)); + if (kmark && kmark->markType != rmark->markType) continue; /* -- 2.43.0