From 05c96b293b7bf1563752e42a3b659b9d549a08c5 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Mon, 7 Jul 2025 16:05:13 +0900
Subject: [PATCH v2 04/13] 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 | 16 ++++++++--------
 3 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/src/include/access/toast_external.h b/src/include/access/toast_external.h
index 1e3ba8062286..1a7c61454f75 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.
@@ -35,7 +40,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;
@@ -74,6 +79,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 a851a8e4184b..d179f0143803 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,
 	},
 };
 
@@ -129,3 +133,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 5e6e19fbc363..66dfedefc579 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -252,14 +252,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;
@@ -297,7 +297,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
@@ -306,9 +306,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));
 		}
-- 
2.50.0

