On Mon, Sep 21, 2026 at 12:18 PM Melanie Plageman <[email protected]> wrote: > > I've tightened up the commit messages in the latest version and > changed 0002 as you suggested. I'll commit 0001-0003 after beta4 is > tagged tomorrow. 0004 I'm going to think about just a bit longer (and > would be master only).
I've committed all but v2-0004. That turned out to have a bug where I updated the prune_xid to newest_live_xid even when there were dead items (because that code runs before we set all-visible to false due to dead items. Attached v3 fixes that and has a patch to replace foreach with foreach_int and foreach_node which David suggested a long time ago and I never got around to. 0001 targets master only, 0002 targets 19 and master. I'll commit 0002 very shortly since it's so trivial. - Melanie
From 41b2088e4e781cb0f403d6429a05559543831b7a Mon Sep 17 00:00:00 2001 From: Melanie Plageman <[email protected]> Date: Fri, 18 Sep 2026 15:45:03 -0400 Subject: [PATCH v3 1/2] Retain newest live xid as prune hint after visibility horizon rejection When live tuples are too young to mark a page all-visible, pruning can clear pd_prune_xid and prevent on-access retries after the horizon advances. Instead, retain the newest live xmin as a retry hint, unless LP_DEAD items remain. Remaining LP_DEAD items prevent setting the VM until VACUUM removes them. VACUUM also records this hint. Setting a previously invalid pd_prune_xid can dirty an otherwise unchanged page and emit a heap FPI when hint logging is required; retaining an existing hint can avoid dirtying the page just to clear it. Reviewed-by: Andrey Borodin <[email protected]> Discussion: https://postgr.es/m/CAAKRu_amj7qLF4c=9ijd=708Fu2G8gg-2EqwBu=acdahu2s...@mail.gmail.com --- src/backend/access/heap/pruneheap.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 98fba4bb7c1..b541c6d709b 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -1209,8 +1209,20 @@ heap_page_prune_and_freeze(PruneFreezeParams *params, GlobalVisTestXidConsideredRunning(prstate.vistest, prstate.newest_live_xid, true)) + { prstate.set_all_visible = prstate.set_all_frozen = false; + /* + * Preserve an opportunity to set the VM on-access once the newest + * live xmin is visible to everyone, unless LP_DEAD items remain. + */ + if (prstate.lpdead_items == 0) + { + Assert(!TransactionIdIsValid(prstate.new_prune_xid)); + prstate.new_prune_xid = prstate.newest_live_xid; + } + } + /* * If checksums are enabled, calling heap_prune_satisfies_vacuum() while * checking tuple visibility information in prune_freeze_plan() may have -- 2.43.0
From 96f690abe32900bfeeb50bfe52eda63e52baab98 Mon Sep 17 00:00:00 2001 From: Melanie Plageman <[email protected]> Date: Wed, 23 Sep 2026 17:15:51 -0400 Subject: [PATCH v3 2/2] Use typed foreach macros for planner relation lists Use foreach_int() and foreach_node() when building the result-relation and row-mark bitmapsets in standard_planner(). This avoids manual ListCell extraction and removes an unnecessary iterator declaration. Suggested-by: David Rowley <[email protected]> Discussion: https://postgr.es/m/CAApHDvq_R-gNXu%2B06GQW6w_HaEMh1pezsyiCh7GNhgh%2Bh0UqMw%40mail.gmail.com --- src/backend/optimizer/plan/planner.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 55a35aa3397..3580bbc21b4 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -364,8 +364,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, Path *best_path; Plan *top_plan; ListCell *lp, - *lr, - *lc; + *lr; /* * Set up global state for this planner invocation. This data is needed @@ -689,12 +688,12 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, * Compute resultRelationRelids and rowMarkRelids from resultRelations and * rowMarks. These can be used for cheap membership checks. */ - foreach(lc, glob->resultRelations) + foreach_int(rti, glob->resultRelations) result->resultRelationRelids = bms_add_member(result->resultRelationRelids, - lfirst_int(lc)); - foreach(lc, glob->finalrowmarks) + rti); + foreach_node(PlanRowMark, rowmark, glob->finalrowmarks) result->rowMarkRelids = bms_add_member(result->rowMarkRelids, - ((PlanRowMark *) lfirst(lc))->rti); + rowmark->rti); result->relationOids = glob->relationOids; result->invalItems = glob->invalItems; -- 2.43.0
