From 099ea3c46847196eba132344ce861f9d74b01be0 Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Mon, 2 Mar 2026 16:31:33 -0500 Subject: [PATCH v36 13/16] Pass down information on table modification to scan node Pass down information to sequential scan, index [only] scan, and bitmap table scan nodes on whether or not the query modifies the relation being scanned. A later commit will use this information to update the VM during on-access pruning only if the relation is not modified by the query. Author: Melanie Plageman Reviewed-by: Andres Freund Reviewed-by: Andrey Borodin Reviewed-by: Chao Li Discussion: https://postgr.es/m/4379FDA3-9446-4E2C-9C15-32EFE8D4F31B%40yandex-team.ru --- src/backend/access/heap/heapam_handler.c | 1 + src/backend/executor/nodeBitmapHeapscan.c | 9 +++++++- src/backend/executor/nodeIndexonlyscan.c | 9 +++++++- src/backend/executor/nodeIndexscan.c | 18 ++++++++++++++-- src/backend/executor/nodeSeqscan.c | 26 ++++++++++++++++++++--- src/include/access/heapam.h | 6 ++++++ src/include/access/tableam.h | 3 +++ 7 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index ebe2e87a28b..3a8eb9d8b61 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -86,6 +86,7 @@ heapam_index_fetch_begin(Relation rel, uint32 flags) hscan->xs_base.rel = rel; hscan->xs_cbuf = InvalidBuffer; hscan->xs_vmbuffer = InvalidBuffer; + hscan->modifies_base_rel = !(flags & SO_HINT_REL_READ_ONLY); return &hscan->xs_base; } diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index b3b6da3d7e4..9bcf9a68183 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -104,11 +104,18 @@ BitmapTableScanSetup(BitmapHeapScanState *node) */ if (!node->ss.ss_currentScanDesc) { + uint32 flags = 0; + + if (!bms_is_member(((Scan *) node->ss.ps.plan)->scanrelid, + node->ss.ps.state->es_modified_relids)) + flags = SO_HINT_REL_READ_ONLY; + node->ss.ss_currentScanDesc = table_beginscan_bm(node->ss.ss_currentRelation, node->ss.ps.state->es_snapshot, 0, - NULL, 0); + NULL, + flags); } node->ss.ss_currentScanDesc->st.rs_tbmiterator = tbmiterator; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index cf4d9a4f832..2fe724a323f 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -84,6 +84,12 @@ IndexOnlyNext(IndexOnlyScanState *node) if (scandesc == NULL) { + uint32 flags = 0; + + if (!bms_is_member(((Scan *) node->ss.ps.plan)->scanrelid, + estate->es_modified_relids)) + flags = SO_HINT_REL_READ_ONLY; + /* * We reach here if the index only scan is not parallel, or if we're * serially executing an index only scan that was planned to be @@ -94,7 +100,8 @@ IndexOnlyNext(IndexOnlyScanState *node) estate->es_snapshot, &node->ioss_Instrument, node->ioss_NumScanKeys, - node->ioss_NumOrderByKeys, 0); + node->ioss_NumOrderByKeys, + flags); node->ioss_ScanDesc = scandesc; diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index a7af2f6628a..8730dab7469 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -102,6 +102,12 @@ IndexNext(IndexScanState *node) if (scandesc == NULL) { + uint32 flags = 0; + + if (!bms_is_member(((Scan *) node->ss.ps.plan)->scanrelid, + estate->es_modified_relids)) + flags = SO_HINT_REL_READ_ONLY; + /* * We reach here if the index scan is not parallel, or if we're * serially executing an index scan that was planned to be parallel. @@ -111,7 +117,8 @@ IndexNext(IndexScanState *node) estate->es_snapshot, &node->iss_Instrument, node->iss_NumScanKeys, - node->iss_NumOrderByKeys, 0); + node->iss_NumOrderByKeys, + flags); node->iss_ScanDesc = scandesc; @@ -198,6 +205,12 @@ IndexNextWithReorder(IndexScanState *node) if (scandesc == NULL) { + uint32 flags = 0; + + if (!bms_is_member(((Scan *) node->ss.ps.plan)->scanrelid, + estate->es_modified_relids)) + flags = SO_HINT_REL_READ_ONLY; + /* * We reach here if the index scan is not parallel, or if we're * serially executing an index scan that was planned to be parallel. @@ -207,7 +220,8 @@ IndexNextWithReorder(IndexScanState *node) estate->es_snapshot, &node->iss_Instrument, node->iss_NumScanKeys, - node->iss_NumOrderByKeys, 0); + node->iss_NumOrderByKeys, + flags); node->iss_ScanDesc = scandesc; diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index d9d7ec0516a..336354922a2 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -65,13 +65,20 @@ SeqNext(SeqScanState *node) if (scandesc == NULL) { + uint32 flags = 0; + + if (!bms_is_member(((Scan *) node->ss.ps.plan)->scanrelid, + estate->es_modified_relids)) + flags = SO_HINT_REL_READ_ONLY; + /* * We reach here if the scan is not parallel, or if we're serially * executing a scan that was planned to be parallel. */ scandesc = table_beginscan(node->ss.ss_currentRelation, estate->es_snapshot, - 0, NULL, 0); + 0, NULL, flags); + node->ss.ss_currentScanDesc = scandesc; } @@ -367,14 +374,20 @@ ExecSeqScanInitializeDSM(SeqScanState *node, { EState *estate = node->ss.ps.state; ParallelTableScanDesc pscan; + uint32 flags = 0; pscan = shm_toc_allocate(pcxt->toc, node->pscan_len); table_parallelscan_initialize(node->ss.ss_currentRelation, pscan, estate->es_snapshot); shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan); + if (!bms_is_member(((Scan *) node->ss.ps.plan)->scanrelid, + estate->es_modified_relids)) + flags = SO_HINT_REL_READ_ONLY; + node->ss.ss_currentScanDesc = - table_beginscan_parallel(node->ss.ss_currentRelation, pscan, 0); + table_beginscan_parallel(node->ss.ss_currentRelation, pscan, + flags); } /* ---------------------------------------------------------------- @@ -404,8 +417,15 @@ ExecSeqScanInitializeWorker(SeqScanState *node, ParallelWorkerContext *pwcxt) { ParallelTableScanDesc pscan; + uint32 flags = 0; + + if (!bms_is_member(((Scan *) node->ss.ps.plan)->scanrelid, + node->ss.ps.state->es_modified_relids)) + flags = SO_HINT_REL_READ_ONLY; pscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false); node->ss.ss_currentScanDesc = - table_beginscan_parallel(node->ss.ss_currentRelation, pscan, 0); + table_beginscan_parallel(node->ss.ss_currentRelation, + pscan, + flags); } diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 7ef4cbbfb1e..c20218f8190 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -130,6 +130,12 @@ typedef struct IndexFetchHeapData /* Current heap block's corresponding page in the visibility map */ Buffer xs_vmbuffer; + + /* + * Some optimizations can only be performed if the query does not modify + * the underlying relation. Track that here. + */ + bool modifies_base_rel; } IndexFetchHeapData; /* Result codes for HeapTupleSatisfiesVacuum */ diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index e881e4f82a0..51dfd122307 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -63,6 +63,9 @@ typedef enum ScanOptions /* unregister snapshot at scan end? */ SO_TEMP_SNAPSHOT = 1 << 9, + + /* set if the query doesn't modify the relation */ + SO_HINT_REL_READ_ONLY = 1 << 10, } ScanOptions; /* -- 2.43.0