From 5af3bac8abdbf38e6c7e584e95fb3038d0759d96 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 1 Aug 2025 16:29:33 +0900
Subject: [PATCH v3 05/14] Introduce new callback to get fresh TOAST values

This callback is called by toast_save_datum() to retrieve a new value
from a source related to the vartag_external we are dealing with.  As of
now, it is simply a wrapper around GetNewOidWithIndex() for the "OID"
on-disk TOAST external pointer.

This will be used later on by more external pointer types, like the int8
one.

InvalidToastId is introduced to track the concept of an "invalid" TOAST
value, required for toast_save_datum().
---
 src/include/access/toast_external.h         | 19 +++++++++++++-
 src/backend/access/common/toast_external.c  | 11 ++++++++
 src/backend/access/common/toast_internals.c | 28 ++++++++++++++-------
 src/backend/access/heap/heaptoast.c         |  2 +-
 4 files changed, 49 insertions(+), 11 deletions(-)

diff --git a/src/include/access/toast_external.h b/src/include/access/toast_external.h
index 849cb1779b67..1f580acd02db 100644
--- a/src/include/access/toast_external.h
+++ b/src/include/access/toast_external.h
@@ -15,9 +15,14 @@
 #ifndef TOAST_EXTERNAL_H
 #define TOAST_EXTERNAL_H
 
+#include "access/attnum.h"
 #include "access/toast_compression.h"
+#include "utils/relcache.h"
 #include "varatt.h"
 
+/* Invalid TOAST value ID */
+#define InvalidToastId 0
+
 /*
  * Intermediate in-memory structure used when creating on-disk
  * varatt_external_* or when deserializing varlena contents.
@@ -39,7 +44,7 @@ typedef struct toast_external_data
 	/*
 	 * Unique ID of value within TOAST table.  This could be an OID or an
 	 * int8 value.  This field is large enough to be able to store any of
-	 * them.
+	 * them.  InvalidToastId if invalid.
 	 */
 	uint64		value;
 } toast_external_data;
@@ -78,6 +83,18 @@ typedef struct toast_external_info
 	 */
 	struct varlena  *(*create_external_data) (toast_external_data data);
 
+	/*
+	 * Retrieve a new value, to be assigned for a TOAST entry that will
+	 * be saved.  "toastrel" is the relation where the entry is added.
+	 * "indexid" and "attnum" can be used to check if a value is already
+	 * in use in the TOAST relation where the new entry is inserted.
+	 *
+	 * When "check" is set to true, the value generated should be rechecked
+	 * with the existing TOAST index.
+	 */
+	uint64		(*get_new_value) (Relation toastrel, Oid indexid,
+								  AttrNumber attnum);
+
 } toast_external_info;
 
 /* Retrieve a toast_external_info from a vartag */
diff --git a/src/backend/access/common/toast_external.c b/src/backend/access/common/toast_external.c
index 5bfb6a6d434f..a17de2fa8b3d 100644
--- a/src/backend/access/common/toast_external.c
+++ b/src/backend/access/common/toast_external.c
@@ -16,11 +16,14 @@
 #include "access/detoast.h"
 #include "access/heaptoast.h"
 #include "access/toast_external.h"
+#include "catalog/catalog.h"
 
 /* Callbacks for VARTAG_ONDISK_OID */
 static void ondisk_oid_to_external_data(struct varlena *attr,
 										toast_external_data *data);
 static struct varlena *ondisk_oid_create_external_data(toast_external_data data);
+static uint64 ondisk_oid_get_new_value(Relation toastrel, Oid indexid,
+									   AttrNumber attnum);
 
 
 /*
@@ -48,6 +51,7 @@ static const toast_external_info toast_external_infos[TOAST_EXTERNAL_INFO_SIZE]
 		.maximum_chunk_size = TOAST_MAX_CHUNK_SIZE_OID,
 		.to_external_data = ondisk_oid_to_external_data,
 		.create_external_data = ondisk_oid_create_external_data,
+		.get_new_value = ondisk_oid_get_new_value,
 	},
 };
 
@@ -154,3 +158,10 @@ ondisk_oid_create_external_data(toast_external_data data)
 
 	return result;
 }
+
+static uint64
+ondisk_oid_get_new_value(Relation toastrel, Oid indexid,
+						 AttrNumber attnum)
+{
+	return GetNewOidWithIndex(toastrel, indexid, attnum);
+}
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 80bda9034cdf..31cb02a48e15 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -227,6 +227,16 @@ toast_save_datum(Relation rel, Datum value,
 	else
 		toast_pointer.toastrelid = RelationGetRelid(toastrel);
 
+	/*
+	 * Retrieve the external TOAST information, with the value still
+	 * unknown.  We need to do this again once we know the actual value
+	 * assigned, to define the correct vartag_external for the new TOAST
+	 * tuple.
+	 */
+	tag = toast_external_assign_vartag(toast_pointer.toastrelid,
+									   InvalidToastId);
+	info = toast_external_get_info(tag);
+
 	/*
 	 * Choose an OID to use as the value ID for this toast value.
 	 *
@@ -243,14 +253,14 @@ toast_save_datum(Relation rel, Datum value,
 	{
 		/* normal case: just choose an unused OID */
 		toast_pointer.value =
-			GetNewOidWithIndex(toastrel,
-							   RelationGetRelid(toastidxs[validIndex]),
-							   (AttrNumber) 1);
+			info->get_new_value(toastrel,
+								RelationGetRelid(toastidxs[validIndex]),
+								(AttrNumber) 1);
 	}
 	else
 	{
 		/* rewrite case: check to see if value was in old toast table */
-		toast_pointer.value = InvalidOid;
+		toast_pointer.value = InvalidToastId;
 		if (oldexternal != NULL)
 		{
 			struct toast_external_data old_toast_pointer;
@@ -288,7 +298,7 @@ toast_save_datum(Relation rel, Datum value,
 				}
 			}
 		}
-		if (toast_pointer.value == InvalidOid)
+		if (toast_pointer.value == InvalidToastId)
 		{
 			/*
 			 * new value; must choose an OID that doesn't conflict in either
@@ -297,9 +307,9 @@ toast_save_datum(Relation rel, Datum value,
 			do
 			{
 				toast_pointer.value =
-					GetNewOidWithIndex(toastrel,
-									   RelationGetRelid(toastidxs[validIndex]),
-									   (AttrNumber) 1);
+					info->get_new_value(toastrel,
+										RelationGetRelid(toastidxs[validIndex]),
+										(AttrNumber) 1);
 			} while (toastid_valueid_exists(rel->rd_toastoid,
 											toast_pointer.value));
 		}
@@ -316,7 +326,7 @@ toast_save_datum(Relation rel, Datum value,
 
 	/*
 	 * Retrieve the vartag that can be assigned for the new TOAST tuple.
-	 * This depends on the type of TOAST table and its assigned value.
+	 * This depends on the type of TOAST table and its now-assigned value.
 	 */
 	tag = toast_external_assign_vartag(toast_pointer.toastrelid,
 									   toast_pointer.value);
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 47cc49be2aba..0c716be3860f 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -149,7 +149,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 	 */
 
 	/* The default value is invalid, to work as a default. */
-	tag = toast_external_assign_vartag(rel->rd_rel->reltoastrelid, InvalidOid);
+	tag = toast_external_assign_vartag(rel->rd_rel->reltoastrelid, InvalidToastId);
 	ttc.ttc_toast_pointer_size = toast_external_info_get_pointer_size(tag);
 
 	ttc.ttc_rel = rel;
-- 
2.50.0

