From feaba42e4783c2a6c16b75c06b55e0a9eb5b7928 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 14 Jan 2026 09:20:11 +0000 Subject: [PATCH] Fix rowid datum memory leak Add cleanup for rowid datum allocated on each insert. The datum was never freed, causing a significant memory leak. During bulk inserts of large row counts this led to excessive RAM usage and could trigger the OOM killer. --- src/backend/executor/execIndexing.c | 28 +++++++++++++++++++++----- src/backend/executor/nodeModifyTable.c | 12 ++++++++++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index f93bfe71d20..f51c7f2069f 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -314,12 +314,13 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; ItemPointer tupleid; - + Datum rowidDatum = (Datum) 0; if (table_get_row_ref_type(resultRelInfo->ri_RelationDesc) == ROW_REF_ROWID) { bool isnull; - tupleid = DatumGetItemPointer(slot_getsysattr(slot, RowIdAttributeNumber, &isnull)); + rowidDatum = slot_getsysattr(slot, RowIdAttributeNumber, &isnull); + tupleid = DatumGetItemPointer(rowidDatum); Assert(!isnull); } else @@ -514,6 +515,9 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, } } + if (DatumGetPointer(rowidDatum) != NULL) + pfree(DatumGetPointer(rowidDatum)); + return result; } @@ -537,11 +541,13 @@ ExecUpdateIndexTuples(ResultRelInfo *resultRelInfo, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; ItemPointer tupleid; + Datum rowidDatum = (Datum) 0; if (table_get_row_ref_type(resultRelInfo->ri_RelationDesc) == ROW_REF_ROWID) { bool isnull; - tupleid = DatumGetItemPointer(slot_getsysattr(slot, RowIdAttributeNumber, &isnull)); + rowidDatum = slot_getsysattr(slot, RowIdAttributeNumber, &isnull); + tupleid = DatumGetItemPointer(rowidDatum); Assert(!isnull); } else @@ -666,12 +672,14 @@ ExecUpdateIndexTuples(ResultRelInfo *resultRelInfo, Datum valuesOld[INDEX_MAX_KEYS]; bool isnullOld[INDEX_MAX_KEYS]; Datum oldTupleid; + Datum oldRowIdDatum = (Datum) 0; bool old_valid = true; if (table_get_row_ref_type(resultRelInfo->ri_RelationDesc) == ROW_REF_ROWID) { bool isnull; - oldTupleid = slot_getsysattr(oldSlot, RowIdAttributeNumber, &isnull); + oldRowIdDatum = slot_getsysattr(oldSlot, RowIdAttributeNumber, &isnull); + oldTupleid = oldRowIdDatum; Assert(!isnull); } else @@ -724,6 +732,8 @@ ExecUpdateIndexTuples(ResultRelInfo *resultRelInfo, checkUnique, /* type of uniqueness check to do */ indexInfo); /* index AM may need this */ + if (DatumGetPointer(oldRowIdDatum) != NULL) + pfree(DatumGetPointer(oldRowIdDatum)); } else { @@ -808,6 +818,9 @@ ExecUpdateIndexTuples(ResultRelInfo *resultRelInfo, } } + if (DatumGetPointer(rowidDatum) != NULL) + pfree(DatumGetPointer(rowidDatum)); + return result; } @@ -824,11 +837,13 @@ ExecDeleteIndexTuples(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; Datum tupleid; + Datum rowidDatum = (Datum) 0; if (table_get_row_ref_type(resultRelInfo->ri_RelationDesc) == ROW_REF_ROWID) { bool isnull; - tupleid = slot_getsysattr(slot, RowIdAttributeNumber, &isnull); + rowidDatum = slot_getsysattr(slot, RowIdAttributeNumber, &isnull); + tupleid = rowidDatum; Assert(!isnull); } else @@ -915,6 +930,9 @@ ExecDeleteIndexTuples(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, heapRelation, /* heap relation */ indexInfo); /* index AM may need this */ } + + if (DatumGetPointer(rowidDatum) != NULL) + pfree(DatumGetPointer(rowidDatum)); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 83dc55e7b87..53b5a6b1a31 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2403,6 +2403,7 @@ ExecOnConflictUpdate(ModifyTableContext *context, ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause; TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing; Datum tupleid; + Datum rowidDatum = (Datum) 0; /* * Parse analysis should have blocked ON CONFLICT for all system @@ -2415,7 +2416,8 @@ ExecOnConflictUpdate(ModifyTableContext *context, if (table_get_row_ref_type(resultRelInfo->ri_RelationDesc) == ROW_REF_ROWID) { bool isnull; - tupleid = slot_getsysattr(existing, RowIdAttributeNumber, &isnull); + rowidDatum = slot_getsysattr(existing, RowIdAttributeNumber, &isnull); + tupleid = rowidDatum; Assert(!isnull); } else @@ -2438,6 +2440,10 @@ ExecOnConflictUpdate(ModifyTableContext *context, { ExecClearTuple(existing); /* see return below */ InstrCountFiltered1(&mtstate->ps, 1); + + if (DatumGetPointer(rowidDatum) != NULL) + pfree(DatumGetPointer(rowidDatum)); + return true; /* done with the tuple */ } @@ -2488,6 +2494,10 @@ ExecOnConflictUpdate(ModifyTableContext *context, * query. */ ExecClearTuple(existing); + + if (DatumGetPointer(rowidDatum) != NULL) + pfree(DatumGetPointer(rowidDatum)); + return true; }