From 1b8fe1c7438966b639630eeabe08f1e48d711cd6 Mon Sep 17 00:00:00 2001
From: Alexander Pyhalov <a.pyhalov@postgrespro.ru>
Date: Thu, 29 Jan 2026 16:18:40 +0300
Subject: [PATCH 1/3] Introduce function to estimate tuple size

Unlike heap_compute_data_size() it assumes that all toasts will be
detoasted.
---
 src/backend/access/common/heaptuple.c | 54 +++++++++++++++++++++++++++
 src/include/access/htup_details.h     |  3 ++
 2 files changed, 57 insertions(+)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 11bec20e82e..7308d2ff4f0 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -57,6 +57,7 @@
 
 #include "postgres.h"
 
+#include "access/detoast.h"
 #include "access/heaptoast.h"
 #include "access/sysattr.h"
 #include "access/tupdesc_details.h"
@@ -266,6 +267,59 @@ heap_compute_data_size(TupleDesc tupleDesc,
 	return data_length;
 }
 
+/*
+ * Estimate tuple size when it's transferred to the wire. Unlike
+ * heap_compute_data_size() it assumes that all toasts will be
+ * detoasted (but doesn't make assumptions about further
+ * processing).
+ */
+Size
+estimate_tuple_size(TupleDesc tupleDesc,
+					   const Datum *values,
+					   const bool *isnull)
+{
+	Size		data_length = 0;
+	int			i;
+	int			numberOfAttributes = tupleDesc->natts;
+
+	for (i = 0; i < numberOfAttributes; i++)
+	{
+		Datum		val;
+		CompactAttribute *atti;
+		bool		is_varlena;
+		bool		is_varwidth;
+
+		if (isnull[i])
+			continue;
+
+		val = values[i];
+		atti = TupleDescCompactAttr(tupleDesc, i);
+
+		is_varlena = (!atti->attbyval && atti->attlen == -1);
+		is_varwidth = (!atti->attbyval && atti->attlen == -2);
+
+		if (is_varlena)
+		{
+			data_length = att_nominal_alignby(data_length, atti->attalignby);
+			data_length += toast_raw_datum_size(val);
+		}
+		else if (is_varwidth)
+		{
+			data_length = att_nominal_alignby(data_length, atti->attalignby);
+			data_length += strlen(DatumGetCString(val)) + 1;
+		}
+		else
+		{
+			data_length = att_datum_alignby(data_length, atti->attalignby,
+											atti->attlen, val);
+			data_length = att_addlength_datum(data_length, atti->attlen,
+											  val);
+		}
+	}
+
+	return data_length;
+}
+
 /*
  * Per-attribute helper for heap_fill_tuple and other routines building tuples.
  *
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index d406825ff22..cb24e159509 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -808,6 +808,9 @@ HeapTupleClearHeapOnly(const HeapTupleData *tuple)
 /* prototypes for functions in common/heaptuple.c */
 extern Size heap_compute_data_size(TupleDesc tupleDesc,
 								   const Datum *values, const bool *isnull);
+extern Size estimate_tuple_size(TupleDesc tupleDesc,
+					   const Datum *values, const bool *isnull);
+
 extern void heap_fill_tuple(TupleDesc tupleDesc,
 							const Datum *values, const bool *isnull,
 							char *data, Size data_size,
-- 
2.43.0

