From 7bb6aeadd318ede7f78b98908d58a432bee3309f Mon Sep 17 00:00:00 2001
From: Alexander Pyhalov <a.pyhalov@postgrespro.ru>
Date: Tue, 16 Dec 2025 18:46:27 +0300
Subject: [PATCH 1/2] Limit batch_size for foreign insert with work_mem

Option batch_size can be set on foreign server level. It can be very optimistic
even for one table with different tuple lengths. To prevent large memory usage
limit effective batch size with work_mem.
---
 src/backend/executor/nodeModifyTable.c | 38 ++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 874b71e6608..19cedde0faa 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -176,6 +176,8 @@ static TupleTableSlot *ExecMergeNotMatched(ModifyTableContext *context,
 										   ResultRelInfo *resultRelInfo,
 										   bool canSetTag);
 
+static Size estimate_batch_length(ResultRelInfo *resultRelInfo);
+
 
 /*
  * Verify that the tuples to be produced by INSERT match the
@@ -828,6 +830,33 @@ ExecGetUpdateNewTuple(ResultRelInfo *relinfo,
 	return ExecProject(newProj);
 }
 
+/*
+ * estimate_batch_length
+ * 		When batch foreign insert is enabled, estimate size of
+ * 		currently gathered batch.
+ */
+static Size
+estimate_batch_length(ResultRelInfo *resultRelInfo)
+{
+	Size		batch_len = 0;
+	int			i = 0;
+
+	for (i = 0; i < resultRelInfo->ri_NumSlots; i++)
+	{
+		TupleTableSlot *slot;
+		Size		slot_tuple_len;
+
+		slot = resultRelInfo->ri_Slots[i];
+
+		slot_getallattrs(slot);
+		slot_tuple_len = heap_compute_data_size(slot->tts_tupleDescriptor, slot->tts_values, slot->tts_isnull);
+		batch_len += slot_tuple_len;
+	}
+
+	return batch_len;
+}
+
+
 /* ----------------------------------------------------------------
  *		ExecInsert
  *
@@ -943,12 +972,15 @@ ExecInsert(ModifyTableContext *context,
 		if (resultRelInfo->ri_BatchSize > 1)
 		{
 			bool		flushed = false;
+			Size		batch_len;
+
+			batch_len = estimate_batch_length(resultRelInfo);
 
 			/*
-			 * When we've reached the desired batch size, perform the
-			 * insertion.
+			 * When we've reached the desired batch size or exceeded work_mem,
+			 * perform the insertion.
 			 */
-			if (resultRelInfo->ri_NumSlots == resultRelInfo->ri_BatchSize)
+			if (resultRelInfo->ri_NumSlots == resultRelInfo->ri_BatchSize || batch_len > work_mem)
 			{
 				ExecBatchInsert(mtstate, resultRelInfo,
 								resultRelInfo->ri_Slots,
-- 
2.43.0

