From 7eb968d4cd94082616c6e7b9bf6c2c74bff86b41 Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Tue, 28 May 2024 13:49:07 +1200
Subject: [PATCH v1 2/4] Move TupleDesc.attrs out of line

Change TupleDesc attrs field so that it's no longer memory allocated at
the end of the TupleDesc struct.  Here we change this so that 'attrs' is
a pointer that always points to memory beyond the end of the struct.

This is just refactoring work to make way for a follow-on patch which
adds another variable length array to TupleDesc.
---
 src/backend/access/common/indextuple.c |  2 +-
 src/backend/access/common/tupdesc.c    | 11 +++++++++--
 src/backend/access/spgist/spgutils.c   |  3 +++
 src/backend/utils/cache/typcache.c     | 15 ++++++++++++++-
 src/include/access/tupdesc.h           | 12 ++++++++----
 5 files changed, 35 insertions(+), 8 deletions(-)

diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index bb2c6a2bcc..79ae29989d 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -588,7 +588,7 @@ index_truncate_tuple(TupleDesc sourceDescriptor, IndexTuple source,
 		return CopyIndexTuple(source);
 
 	/* Create temporary descriptor to scribble on */
-	truncdesc = palloc(TupleDescSize(sourceDescriptor));
+	truncdesc = CreateTemplateTupleDesc(sourceDescriptor->natts);
 	TupleDescCopy(truncdesc, sourceDescriptor);
 	truncdesc->natts = leavenatts;
 
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 47379fef22..fba0026520 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -85,7 +85,7 @@ CreateTemplateTupleDesc(int natts)
 	 * could be less due to trailing padding, although with the current
 	 * definition of pg_attribute there probably isn't any padding.
 	 */
-	desc = (TupleDesc) palloc(offsetof(struct TupleDescData, attrs) +
+	desc = (TupleDesc) palloc(MAXALIGN(sizeof(TupleDescData)) +
 							  natts * sizeof(FormData_pg_attribute));
 
 	/*
@@ -96,6 +96,7 @@ CreateTemplateTupleDesc(int natts)
 	desc->tdtypeid = RECORDOID;
 	desc->tdtypmod = -1;
 	desc->tdrefcount = -1;		/* assume not reference-counted */
+	desc->attrs = TupleDescAttrAddress(desc);
 
 	return desc;
 }
@@ -252,9 +253,15 @@ TupleDescCopy(TupleDesc dst, TupleDesc src)
 {
 	int			i;
 
-	/* Flat-copy the header and attribute array */
+	/* Flat-copy the header */
 	memcpy(dst, src, TupleDescSize(src));
 
+	/* restore original attribute array pointer and replace contents from src */
+	dst->attrs = TupleDescAttrAddress(dst);
+	memcpy(TupleDescAttr(dst, 0),
+		   TupleDescAttr(src, 0),
+		   sizeof(FormData_pg_attribute) * dst->natts);
+
 	/*
 	 * Since we're not copying constraints and defaults, clear fields
 	 * associated with them.
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 76b80146ff..94d49b7571 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -331,6 +331,9 @@ getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType)
 		/* In case we changed typlen, we'd better reset following offsets */
 		for (int i = spgFirstIncludeColumn; i < outTupDesc->natts; i++)
 			TupleDescAttr(outTupDesc, i)->attcacheoff = -1;
+
+		populate_TupleDescAttr(TupleDescDeformAttr(outTupDesc, spgKeyColumn),
+							   att);
 	}
 	return outTupDesc;
 }
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index aa4720cb59..426eabd9d9 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -222,12 +222,18 @@ shared_record_table_compare(const void *a, const void *b, size_t size,
 	TupleDesc	t2;
 
 	if (k1->shared)
+	{
 		t1 = (TupleDesc) dsa_get_address(area, k1->u.shared_tupdesc);
+		t1->attrs = TupleDescAttrAddress(t1);
+	}
 	else
 		t1 = k1->u.local_tupdesc;
 
 	if (k2->shared)
+	{
 		t2 = (TupleDesc) dsa_get_address(area, k2->u.shared_tupdesc);
+		t2->attrs = TupleDescAttrAddress(t2);
+	}
 	else
 		t2 = k2->u.local_tupdesc;
 
@@ -245,7 +251,10 @@ shared_record_table_hash(const void *a, size_t size, void *arg)
 	TupleDesc	t;
 
 	if (k->shared)
+	{
 		t = (TupleDesc) dsa_get_address(area, k->u.shared_tupdesc);
+		t->attrs = TupleDescAttrAddress(t);
+	}
 	else
 		t = k->u.local_tupdesc;
 
@@ -1780,6 +1789,7 @@ lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
 					tupdesc = (TupleDesc)
 						dsa_get_address(CurrentSession->area,
 										entry->shared_tupdesc);
+					tupdesc->attrs = TupleDescAttrAddress(tupdesc);
 					Assert(typmod == tupdesc->tdtypmod);
 
 					/* We may need to extend the local RecordCacheArray. */
@@ -2737,7 +2747,7 @@ share_tupledesc(dsa_area *area, TupleDesc tupdesc, uint32 typmod)
 	dsa_pointer shared_dp;
 	TupleDesc	shared;
 
-	shared_dp = dsa_allocate(area, TupleDescSize(tupdesc));
+	shared_dp = dsa_allocate(area, TupleDescFullSize(tupdesc));
 	shared = (TupleDesc) dsa_get_address(area, shared_dp);
 	TupleDescCopy(shared, tupdesc);
 	shared->tdtypmod = typmod;
@@ -2780,6 +2790,7 @@ find_or_make_matching_shared_tupledesc(TupleDesc tupdesc)
 		result = (TupleDesc)
 			dsa_get_address(CurrentSession->area,
 							record_table_entry->key.u.shared_tupdesc);
+		result->attrs = TupleDescAttrAddress(result);
 		Assert(result->tdrefcount == -1);
 
 		return result;
@@ -2843,6 +2854,7 @@ find_or_make_matching_shared_tupledesc(TupleDesc tupdesc)
 		result = (TupleDesc)
 			dsa_get_address(CurrentSession->area,
 							record_table_entry->key.u.shared_tupdesc);
+		result->attrs = TupleDescAttrAddress(result);
 		Assert(result->tdrefcount == -1);
 
 		return result;
@@ -2855,6 +2867,7 @@ find_or_make_matching_shared_tupledesc(TupleDesc tupdesc)
 						record_table_entry);
 	result = (TupleDesc)
 		dsa_get_address(CurrentSession->area, shared_dp);
+	result->attrs = TupleDescAttrAddress(result);
 	Assert(result->tdrefcount == -1);
 
 	return result;
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 8930a28d66..2c435cdcb2 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -84,7 +84,7 @@ typedef struct TupleDescData
 	int			tdrefcount;		/* reference count, or -1 if not counting */
 	TupleConstr *constr;		/* constraints, or NULL if none */
 	/* attrs[N] is the description of Attribute Number N+1 */
-	FormData_pg_attribute attrs[FLEXIBLE_ARRAY_MEMBER];
+	FormData_pg_attribute *attrs;
 }			TupleDescData;
 typedef struct TupleDescData *TupleDesc;
 
@@ -99,9 +99,13 @@ extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
 
 extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
 
-#define TupleDescSize(src) \
-	(offsetof(struct TupleDescData, attrs) + \
-	 (src)->natts * sizeof(FormData_pg_attribute))
+#define TupleDescSize(src) MAXALIGN(sizeof(TupleDescData))
+
+#define TupleDescFullSize(src) \
+	(MAXALIGN(sizeof(TupleDescData)) + sizeof(FormData_pg_attribute) * (src)->natts)
+
+#define TupleDescAttrAddress(desc) \
+	(Form_pg_attribute) ((char *) (desc) + MAXALIGN(sizeof(TupleDescData)))
 
 extern void TupleDescCopy(TupleDesc dst, TupleDesc src);
 
-- 
2.34.1

