From 41a8548ad41854d88578aebcfdf54bbdca42d49e Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Mon, 14 Sep 2026 10:42:55 -0400 Subject: [PATCH] Avoid setting pd_prune_xid when inserting frozen tuples 378a216187a set pd_prune_xid on inserts so that on-access pruning could later set the pages all-visible. heap_multi_insert() skipped this only when setting the page all-frozen, but it can insert frozen tuples even when not setting the page all-frozen. Inserting frozen tuples should not set pd_prune_xid as they introduce no work for on-access pruning. Fix by only setting pd_prune_xid when inserting non-frozen tuples. Reported-by: Melanie Plageman Reviewed-by: Chao Li Discussion: https://postgr.es/m/CAAKRu_a02pu7U2S0qPZ5L%2BoWb2m2Zp965sOKCCLnKUAKNo-hfg%40mail.gmail.com Backpatch-through: 19 --- src/backend/access/heap/heapam.c | 6 +++--- src/backend/access/heap/heapam_xlog.c | 21 ++++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index abba5dfd0e7..e57b5bb3315 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -2493,10 +2493,10 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, /* * Set pd_prune_xid. See heap_insert() for more on why we do this when - * inserting tuples. This only makes sense if we aren't already - * setting the page frozen in the VM and we're not in bootstrap mode. + * inserting tuples. This only makes sense if the tuples aren't frozen + * and we're not in bootstrap mode. */ - if (!all_frozen_set && TransactionIdIsNormal(xid)) + if (TransactionIdIsNormal(xid) && !(options & HEAP_INSERT_FROZEN)) PageSetPrunable(page, xid); MarkBufferDirty(buffer); diff --git a/src/backend/access/heap/heapam_xlog.c b/src/backend/access/heap/heapam_xlog.c index fbda931522c..b29717d52b9 100644 --- a/src/backend/access/heap/heapam_xlog.c +++ b/src/backend/access/heap/heapam_xlog.c @@ -586,6 +586,7 @@ heap_xlog_multi_insert(XLogReaderState *record) char *tupdata; char *endptr; Size len; + bool inserted_tuples_frozen = false; /* Tuples are stored as block data */ tupdata = XLogRecGetBlockData(record, HEAP_MULTI_INSERT_BLKREF_HEAP, @@ -633,6 +634,10 @@ heap_xlog_multi_insert(XLogReaderState *record) ItemPointerSetBlockNumber(&htup->t_ctid, blkno); ItemPointerSetOffsetNumber(&htup->t_ctid, offnum); + /* If one inserted tuple was frozen, they all were */ + if (i == 0) + inserted_tuples_frozen = HeapTupleHeaderXminFrozen(htup); + offnum = PageAddItem(page, htup, newlen, offnum, true, true); if (offnum == InvalidOffsetNumber) elog(PANIC, "failed to add tuple"); @@ -648,17 +653,23 @@ heap_xlog_multi_insert(XLogReaderState *record) PageClearAllVisible(page); /* - * XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible. If - * we are not setting the page frozen, then set the page's prunable - * hint so that we trigger on-access pruning later which may set the - * page all-visible in the VM. + * XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible, so + * set PD_ALL_VISIBLE and clear pd_prune_xid. + * + * If the page isn't being set all-frozen and we aren't inserting + * frozen tuples, set pd_prune_xid so that the page gets on-access + * pruned. + * + * Frozen tuples may be added to an already all-frozen page or to a + * page containing non-frozen tuples, but they introduce nothing new + * for on-access pruning, so preserve the existing hint. */ if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET) { PageSetAllVisible(page); PageClearPrunable(page); } - else + else if (!inserted_tuples_frozen) PageSetPrunable(page, XLogRecGetXid(record)); MarkBufferDirty(buffer);