v36-0011-Track-which-relations-are-modified-by-a-query.patch
text/x-patch
Filename: v36-0011-Track-which-relations-are-modified-by-a-query.patch
Type: text/x-patch
Part: 10
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v36-0011
Subject: Track which relations are modified by a query
| File | + | − |
|---|---|---|
| src/backend/executor/execMain.c | 18 | 0 |
| src/backend/executor/execUtils.c | 31 | 0 |
| src/include/executor/executor.h | 3 | 0 |
| src/include/nodes/execnodes.h | 6 | 0 |
From 9f7098111d8520a955b0b4d6d4d62c4a79a5497c Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Wed, 3 Dec 2025 15:07:24 -0500
Subject: [PATCH v36 11/16] Track which relations are modified by a query
Save the relids in a bitmap in the estate. A later commit will pass this
information down to scan nodes to control whether or not the scan allows
setting the visibility map while on-access pruning. We don't want to set
the visibility map if the query is just going to modify the page
immediately after.
Author: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/F5CDD1B5-628C-44A1-9F85-3958C626F6A9%40gmail.com
---
src/backend/executor/execMain.c | 18 ++++++++++++++++++
src/backend/executor/execUtils.c | 31 +++++++++++++++++++++++++++++++
src/include/executor/executor.h | 3 +++
src/include/nodes/execnodes.h | 6 ++++++
4 files changed, 58 insertions(+)
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index bfd3ebc601e..57dcdeda056 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -920,6 +920,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
break;
}
+ /* If it has a rowmark, the relation may be modified */
+ estate->es_modified_relids = bms_add_member(estate->es_modified_relids,
+ rc->rti);
+
/* Check that relation is a legal target for marking */
if (relation)
CheckValidRowMarkRel(relation, rc->markType);
@@ -990,6 +994,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
*/
planstate = ExecInitNode(plan, estate, eflags);
+#ifdef USE_ASSERT_CHECKING
+ CrossCheckModifiedRelids(estate);
+#endif
+
/*
* Get the tuple descriptor describing the type of tuples to return.
*/
@@ -3033,6 +3041,12 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree)
rcestate->es_output_cid = parentestate->es_output_cid;
rcestate->es_queryEnv = parentestate->es_queryEnv;
+ /*
+ * Use a deep copy to avoid stale pointers since bms_add_member() may
+ * reallocate the bitmap.
+ */
+ rcestate->es_modified_relids = bms_copy(parentestate->es_modified_relids);
+
/*
* ResultRelInfos needed by subplans are initialized from scratch when the
* subplans themselves are initialized.
@@ -3165,6 +3179,10 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree)
*/
epqstate->recheckplanstate = ExecInitNode(planTree, rcestate, 0);
+#ifdef USE_ASSERT_CHECKING
+ CrossCheckModifiedRelids(rcestate);
+#endif
+
MemoryContextSwitchTo(oldcontext);
}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index cd4d5452cfb..0f8364b8720 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -123,6 +123,8 @@ CreateExecutorState(void)
estate->es_part_prune_results = NIL;
estate->es_unpruned_relids = NULL;
+ estate->es_modified_relids = NULL;
+
estate->es_junkFilter = NULL;
estate->es_output_cid = (CommandId) 0;
@@ -871,6 +873,33 @@ ExecGetRangeTableRelation(EState *estate, Index rti, bool isResultRel)
return rel;
}
+#ifdef USE_ASSERT_CHECKING
+/*
+ * Assert that es_modified_relids includes all potentially modified RT
+ * indexes.
+ */
+void
+CrossCheckModifiedRelids(EState *estate)
+{
+ Bitmapset *expected = NULL;
+ ListCell *lc;
+
+ foreach(lc, estate->es_opened_result_relations)
+ {
+ ResultRelInfo *rri = lfirst_node(ResultRelInfo, lc);
+
+ expected = bms_add_member(expected, rri->ri_RangeTableIndex);
+ }
+ if (estate->es_rowmarks)
+ {
+ for (Index rti = 1; rti <= estate->es_range_table_size; rti++)
+ if (estate->es_rowmarks[rti - 1] != NULL)
+ expected = bms_add_member(expected, rti);
+ }
+ Assert(bms_is_subset(expected, estate->es_modified_relids));
+}
+#endif
+
/*
* ExecInitResultRelation
* Open relation given by the passed-in RT index and fill its
@@ -896,6 +925,8 @@ ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo,
estate->es_result_relations = (ResultRelInfo **)
palloc0(estate->es_range_table_size * sizeof(ResultRelInfo *));
estate->es_result_relations[rti - 1] = resultRelInfo;
+ estate->es_modified_relids = bms_add_member(estate->es_modified_relids,
+ rti);
/*
* Saving in the list allows to avoid needlessly traversing the whole
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index d46ba59895d..05f032baeaa 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -703,6 +703,9 @@ extern Relation ExecGetRangeTableRelation(EState *estate, Index rti,
bool isResultRel);
extern void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo,
Index rti);
+#ifdef USE_ASSERT_CHECKING
+extern void CrossCheckModifiedRelids(EState *estate);
+#endif
extern int executor_errposition(EState *estate, int location);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 63c067d5aae..610385df12b 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -679,6 +679,12 @@ typedef struct EState
* ExecDoInitialPruning() */
const char *es_sourceText; /* Source text from QueryDesc */
+ /*
+ * RT indexes of relations modified by the query through a
+ * UPDATE/DELETE/INSERT/MERGE or targeted by a SELECT FOR UPDATE.
+ */
+ Bitmapset *es_modified_relids;
+
JunkFilter *es_junkFilter; /* top-level junk filter, if any */
/* If query can insert/delete tuples, the command ID to mark them with */
--
2.43.0