Subject: Re: Convert macros to static inline functions

From: Peter Eisentraut <[email-masked]>
Date: Fri, 27 Dec 2024 11:16:46 +0100

On 16.05.22 10:27, Peter Eisentraut wrote:
> Inspired by [0], I looked to convert more macros to inline functions. 

This is an older thread where I left something unfinished:

> Note 2: Many macros in htup_details.h operate both on HeapTupleHeader 
> and on MinimalTuple, so converting them to a function doesn't work in a 
> straightforward way.  I have some in-progress work in that area, but I 
> have not included any of that here.

Here is the patch set for this.

There are actually only two macros that operate on both HeapTupleHeader 
and MinimalTuple, so it wasn't as much as I had written above.  I just 
left those as macros.  I converted the rest to inline functions in a 
straightforward way as before.  A small amount of reordering was necessary.

But just for language-nerd fun, I'm including here an additional patch 
showing how the remaining ones could be done with C11 generic selection. 
  I'm not planning to commit that one at this time.
From 422181163bfe1a292ee51941a007d54bc127ccec Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email-masked]>
Date: Fri, 27 Dec 2024 11:07:35 +0100
Subject: [PATCH 1/3] Add some const decorations (htup.h)

---
 src/backend/access/heap/heapam.c  | 6 +++---
 src/backend/utils/time/combocid.c | 6 +++---
 src/include/access/htup.h         | 8 ++++----
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 329e727f80d..75171e8f2c0 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -7419,10 +7419,10 @@ MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask)
  * checking the hint bits.
  */
 TransactionId
-HeapTupleGetUpdateXid(HeapTupleHeader tuple)
+HeapTupleGetUpdateXid(const HeapTupleHeaderData *tup)
 {
-	return MultiXactIdGetUpdateXid(HeapTupleHeaderGetRawXmax(tuple),
-								   tuple->t_infomask);
+	return MultiXactIdGetUpdateXid(HeapTupleHeaderGetRawXmax(tup),
+								   tup->t_infomask);
 }
 
 /*
diff --git a/src/backend/utils/time/combocid.c b/src/backend/utils/time/combocid.c
index f85510b74ff..1ecdc93b7e2 100644
--- a/src/backend/utils/time/combocid.c
+++ b/src/backend/utils/time/combocid.c
@@ -101,7 +101,7 @@ static CommandId GetRealCmax(CommandId combocid);
  */
 
 CommandId
-HeapTupleHeaderGetCmin(HeapTupleHeader tup)
+HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
 {
 	CommandId	cid = HeapTupleHeaderGetRawCommandId(tup);
 
@@ -115,7 +115,7 @@ HeapTupleHeaderGetCmin(HeapTupleHeader tup)
 }
 
 CommandId
-HeapTupleHeaderGetCmax(HeapTupleHeader tup)
+HeapTupleHeaderGetCmax(const HeapTupleHeaderData *tup)
 {
 	CommandId	cid = HeapTupleHeaderGetRawCommandId(tup);
 
@@ -150,7 +150,7 @@ HeapTupleHeaderGetCmax(HeapTupleHeader tup)
  * changes the tuple in shared buffers.
  */
 void
-HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
+HeapTupleHeaderAdjustCmax(const HeapTupleHeaderData *tup,
 						  CommandId *cmax,
 						  bool *iscombo)
 {
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 116cb1bb273..a5f1ef2441d 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -78,12 +78,12 @@ typedef HeapTupleData *HeapTuple;
 #define HeapTupleIsValid(tuple) PointerIsValid(tuple)
 
 /* HeapTupleHeader functions implemented in utils/time/combocid.c */
-extern CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup);
-extern CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup);
-extern void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
+extern CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup);
+extern CommandId HeapTupleHeaderGetCmax(const HeapTupleHeaderData *tup);
+extern void HeapTupleHeaderAdjustCmax(const HeapTupleHeaderData *tup,
 									  CommandId *cmax, bool *iscombo);
 
 /* Prototype for HeapTupleHeader accessors in heapam.c */
-extern TransactionId HeapTupleGetUpdateXid(HeapTupleHeader tuple);
+extern TransactionId HeapTupleGetUpdateXid(const HeapTupleHeaderData *tup);
 
 #endif							/* HTUP_H */

base-commit: d85ce012f99f63249bb45a78fcecb7a2383920b1
-- 
2.47.1

From 89308e0446259a1ae619e39603324d2c2b8ed66c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email-masked]>
Date: Fri, 27 Dec 2024 11:07:35 +0100
Subject: [PATCH 2/3] Convert macros to static inline functions
 (htup_details.h, itup.h)

---
 contrib/pageinspect/heapfuncs.c   |  15 +-
 src/include/access/htup_details.h | 543 ++++++++++++++++++------------
 src/include/access/itup.h         |  20 +-
 3 files changed, 352 insertions(+), 226 deletions(-)

diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 41ff597199c..4961a3c866c 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -42,13 +42,14 @@
  * was used to upgrade from an older version, tuples might still have an
  * oid. Seems worthwhile to display that.
  */
-#define HeapTupleHeaderGetOidOld(tup) \
-( \
-	((tup)->t_infomask & HEAP_HASOID_OLD) ? \
-	   *((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) \
-	: \
-		InvalidOid \
-)
+static inline Oid
+HeapTupleHeaderGetOidOld(const HeapTupleHeaderData *tup)
+{
+	if (tup->t_infomask & HEAP_HASOID_OLD)
+		return *((Oid *) ((char *) (tup) + (tup)->t_hoff - sizeof(Oid)));
+	else
+		return InvalidOid;
+}

 /*
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 0d1adff540f..bab480fe2f0 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -225,12 +225,13 @@ struct HeapTupleHeaderData
  *
  * See also HeapTupleHeaderIsOnlyLocked, which also checks for a possible
  * aborted updater transaction.
- *
- * Beware of multiple evaluations of the argument.
  */
-#define HEAP_XMAX_IS_LOCKED_ONLY(infomask) \
-	(((infomask) & HEAP_XMAX_LOCK_ONLY) || \
-	 (((infomask) & (HEAP_XMAX_IS_MULTI | HEAP_LOCK_MASK)) == HEAP_XMAX_EXCL_LOCK))
+static inline bool
+HEAP_XMAX_IS_LOCKED_ONLY(uint16 infomask)
+{
+	return (infomask & HEAP_XMAX_LOCK_ONLY) ||
+		(infomask & (HEAP_XMAX_IS_MULTI | HEAP_LOCK_MASK)) == HEAP_XMAX_EXCL_LOCK;
+}
 
 /*
  * A tuple that has HEAP_XMAX_IS_MULTI and HEAP_XMAX_LOCK_ONLY but neither of
@@ -250,22 +251,35 @@ struct HeapTupleHeaderData
  * bogus, regardless of where they stand with respect to the current valid
  * multixact range.
  */
-#define HEAP_LOCKED_UPGRADED(infomask) \
-( \
-	 ((infomask) & HEAP_XMAX_IS_MULTI) != 0 && \
-	 ((infomask) & HEAP_XMAX_LOCK_ONLY) != 0 && \
-	 (((infomask) & (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)) == 0) \
-)
+static inline bool
+HEAP_LOCKED_UPGRADED(uint16 infomask)
+{
+	return
+		(infomask & HEAP_XMAX_IS_MULTI) != 0 &&
+		(infomask & HEAP_XMAX_LOCK_ONLY) != 0 &&
+		(infomask & (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)) == 0;
+}
 
 /*
  * Use these to test whether a particular lock is applied to a tuple
  */
-#define HEAP_XMAX_IS_SHR_LOCKED(infomask) \
-	(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_SHR_LOCK)
-#define HEAP_XMAX_IS_EXCL_LOCKED(infomask) \
-	(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_EXCL_LOCK)
-#define HEAP_XMAX_IS_KEYSHR_LOCKED(infomask) \
-	(((infomask) & HEAP_LOCK_MASK) == HEAP_XMAX_KEYSHR_LOCK)
+static inline bool
+HEAP_XMAX_IS_SHR_LOCKED(int16 infomask)
+{
+	return (infomask & HEAP_LOCK_MASK) == HEAP_XMAX_SHR_LOCK;
+}
+
+static inline bool
+HEAP_XMAX_IS_EXCL_LOCKED(int16 infomask)
+{
+	return (infomask & HEAP_LOCK_MASK) == HEAP_XMAX_EXCL_LOCK;
+}
+
+static inline bool
+HEAP_XMAX_IS_KEYSHR_LOCKED(int16 infomask)
+{
+	return (infomask & HEAP_LOCK_MASK) == HEAP_XMAX_KEYSHR_LOCK;
+}
 
 /* turn these all off when Xmax is to change */
 #define HEAP_XMAX_BITS (HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | \
@@ -292,12 +306,11 @@ struct HeapTupleHeaderData
 #define HEAP_TUPLE_HAS_MATCH	HEAP_ONLY_TUPLE /* tuple has a join match */
 
 /*
- * HeapTupleHeader accessor macros
- *
- * Note: beware of multiple evaluations of "tup" argument.  But the Set
- * macros evaluate their other argument only once.
+ * HeapTupleHeader accessor functions
  */
 
+static bool HeapTupleHeaderXminFrozen(const HeapTupleHeaderData *tup);
+
 /*
  * HeapTupleHeaderGetRawXmin returns the "raw" xmin field, which is the xid
  * originally used to insert the tuple.  However, the tuple might actually
@@ -306,56 +319,78 @@ struct HeapTupleHeaderData
  * the xmin to FrozenTransactionId, and that value may still be encountered
  * on disk.
  */
-#define HeapTupleHeaderGetRawXmin(tup) \
-( \
-	(tup)->t_choice.t_heap.t_xmin \
-)
+static inline TransactionId
+HeapTupleHeaderGetRawXmin(const HeapTupleHeaderData *tup)
+{
+	return tup->t_choice.t_heap.t_xmin;
+}
 
-#define HeapTupleHeaderGetXmin(tup) \
-( \
-	HeapTupleHeaderXminFrozen(tup) ? \
-		FrozenTransactionId : HeapTupleHeaderGetRawXmin(tup) \
-)
+static inline TransactionId
+HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
+{
+	return HeapTupleHeaderXminFrozen(tup) ?
+		FrozenTransactionId : HeapTupleHeaderGetRawXmin(tup);
+}
 
-#define HeapTupleHeaderSetXmin(tup, xid) \
-( \
-	(tup)->t_choice.t_heap.t_xmin = (xid) \
-)
+static inline void
+HeapTupleHeaderSetXmin(HeapTupleHeaderData *tup, TransactionId xid)
+{
+	tup->t_choice.t_heap.t_xmin = xid;
+}
 
-#define HeapTupleHeaderXminCommitted(tup) \
-( \
-	((tup)->t_infomask & HEAP_XMIN_COMMITTED) != 0 \
-)
+static inline bool
+HeapTupleHeaderXminCommitted(const HeapTupleHeaderData *tup)
+{
+	return (tup->t_infomask & HEAP_XMIN_COMMITTED) != 0;
+}
 
-#define HeapTupleHeaderXminInvalid(tup) \
-( \
-	((tup)->t_infomask & (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)) == \
-		HEAP_XMIN_INVALID \
-)
+static inline bool
+HeapTupleHeaderXminInvalid(const HeapTupleHeaderData *tup) \
+{
+	return (tup->t_infomask & (HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID)) ==
+		HEAP_XMIN_INVALID;
+}
 
-#define HeapTupleHeaderXminFrozen(tup) \
-( \
-	((tup)->t_infomask & (HEAP_XMIN_FROZEN)) == HEAP_XMIN_FROZEN \
-)
+static inline bool
+HeapTupleHeaderXminFrozen(const HeapTupleHeaderData *tup)
+{
+	return (tup->t_infomask & HEAP_XMIN_FROZEN) == HEAP_XMIN_FROZEN;
+}
 
-#define HeapTupleHeaderSetXminCommitted(tup) \
-( \
-	AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
-	((tup)->t_infomask |= HEAP_XMIN_COMMITTED) \
-)
+static inline void
+HeapTupleHeaderSetXminCommitted(HeapTupleHeaderData *tup)
+{
+	Assert(!HeapTupleHeaderXminInvalid(tup));
+	tup->t_infomask |= HEAP_XMIN_COMMITTED;
+}
 
-#define HeapTupleHeaderSetXminInvalid(tup) \
-( \
-	AssertMacro(!HeapTupleHeaderXminCommitted(tup)), \
-	((tup)->t_infomask |= HEAP_XMIN_INVALID) \
-)
+static inline void
+HeapTupleHeaderSetXminInvalid(HeapTupleHeaderData *tup)
+{
+	Assert(!HeapTupleHeaderXminCommitted(tup));
+	tup->t_infomask |= HEAP_XMIN_INVALID;
+}
 
-#define HeapTupleHeaderSetXminFrozen(tup) \
-( \
-	AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
-	((tup)->t_infomask |= HEAP_XMIN_FROZEN) \
-)
+static inline void
+HeapTupleHeaderSetXminFrozen(HeapTupleHeaderData *tup)
+{
+	Assert(!HeapTupleHeaderXminInvalid(tup));
+	tup->t_infomask |= HEAP_XMIN_FROZEN;
+}
 
+static inline TransactionId
+HeapTupleHeaderGetRawXmax(const HeapTupleHeaderData *tup)
+{
+	return tup->t_choice.t_heap.t_xmax;
+}
+
+static inline void
+HeapTupleHeaderSetXmax(HeapTupleHeaderData *tup, TransactionId xid)
+{
+	tup->t_choice.t_heap.t_xmax = xid;
+}
+
+#ifndef FRONTEND
 /*
  * HeapTupleHeaderGetRawXmax gets you the raw Xmax field.  To find out the Xid
  * that updated a tuple, you might need to resolve the MultiXactId if certain
@@ -363,25 +398,17 @@ struct HeapTupleHeaderData
  * to resolve the MultiXactId if necessary.  This might involve multixact I/O,
  * so it should only be used if absolutely necessary.
  */
-#define HeapTupleHeaderGetUpdateXid(tup) \
-( \
-	(!((tup)->t_infomask & HEAP_XMAX_INVALID) && \
-	 ((tup)->t_infomask & HEAP_XMAX_IS_MULTI) && \
-	 !((tup)->t_infomask & HEAP_XMAX_LOCK_ONLY)) ? \
-		HeapTupleGetUpdateXid(tup) \
-	: \
-		HeapTupleHeaderGetRawXmax(tup) \
-)
-
-#define HeapTupleHeaderGetRawXmax(tup) \
-( \
-	(tup)->t_choice.t_heap.t_xmax \
-)
-
-#define HeapTupleHeaderSetXmax(tup, xid) \
-( \
-	(tup)->t_choice.t_heap.t_xmax = (xid) \
-)
+static inline TransactionId
+HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
+{
+	if (!((tup)->t_infomask & HEAP_XMAX_INVALID) &&
+		((tup)->t_infomask & HEAP_XMAX_IS_MULTI) &&
+		!((tup)->t_infomask & HEAP_XMAX_LOCK_ONLY))
+		return HeapTupleGetUpdateXid(tup);
+	else
+		return HeapTupleHeaderGetRawXmax(tup);
+}
+#endif							/* FRONTEND */
 
 /*
  * HeapTupleHeaderGetRawCommandId will give you what's in the header whether
@@ -389,147 +416,168 @@ struct HeapTupleHeaderData
  * HeapTupleHeaderGetCmax instead, but note that those Assert that you can
  * get a legitimate result, ie you are in the originating transaction!
  */
-#define HeapTupleHeaderGetRawCommandId(tup) \
-( \
-	(tup)->t_choice.t_heap.t_field3.t_cid \
-)
+static inline CommandId
+HeapTupleHeaderGetRawCommandId(const HeapTupleHeaderData *tup)
+{
+	return tup->t_choice.t_heap.t_field3.t_cid;
+}
 
 /* SetCmin is reasonably simple since we never need a combo CID */
-#define HeapTupleHeaderSetCmin(tup, cid) \
-do { \
-	Assert(!((tup)->t_infomask & HEAP_MOVED)); \
-	(tup)->t_choice.t_heap.t_field3.t_cid = (cid); \
-	(tup)->t_infomask &= ~HEAP_COMBOCID; \
-} while (0)
+static inline void
+HeapTupleHeaderSetCmin(HeapTupleHeaderData *tup, CommandId cid)
+{
+	Assert(!(tup->t_infomask & HEAP_MOVED));
+	tup->t_choice.t_heap.t_field3.t_cid = cid;
+	tup->t_infomask &= ~HEAP_COMBOCID;
+}
 
 /* SetCmax must be used after HeapTupleHeaderAdjustCmax; see combocid.c */
-#define HeapTupleHeaderSetCmax(tup, cid, iscombo) \
-do { \
-	Assert(!((tup)->t_infomask & HEAP_MOVED)); \
-	(tup)->t_choice.t_heap.t_field3.t_cid = (cid); \
-	if (iscombo) \
-		(tup)->t_infomask |= HEAP_COMBOCID; \
-	else \
-		(tup)->t_infomask &= ~HEAP_COMBOCID; \
-} while (0)
-
-#define HeapTupleHeaderGetXvac(tup) \
-( \
-	((tup)->t_infomask & HEAP_MOVED) ? \
-		(tup)->t_choice.t_heap.t_field3.t_xvac \
-	: \
-		InvalidTransactionId \
-)
+static inline void
+HeapTupleHeaderSetCmax(HeapTupleHeaderData *tup, CommandId cid, bool iscombo)
+{
+	Assert(!((tup)->t_infomask & HEAP_MOVED));
+	tup->t_choice.t_heap.t_field3.t_cid = cid;
+	if (iscombo)
+		tup->t_infomask |= HEAP_COMBOCID;
+	else
+		tup->t_infomask &= ~HEAP_COMBOCID;
+}
+
+static inline TransactionId
+HeapTupleHeaderGetXvac(const HeapTupleHeaderData *tup)
+{
+	if (tup->t_infomask & HEAP_MOVED)
+		return tup->t_choice.t_heap.t_field3.t_xvac;
+	else
+		return InvalidTransactionId;
+}
 
-#define HeapTupleHeaderSetXvac(tup, xid) \
-do { \
-	Assert((tup)->t_infomask & HEAP_MOVED); \
-	(tup)->t_choice.t_heap.t_field3.t_xvac = (xid); \
-} while (0)
+static inline void
+HeapTupleHeaderSetXvac(HeapTupleHeaderData *tup, TransactionId xid)
+{
+	Assert(tup->t_infomask & HEAP_MOVED);
+	tup->t_choice.t_heap.t_field3.t_xvac = xid;
+}
 
 StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
 				 "invalid speculative token constant");
 
-#define HeapTupleHeaderIsSpeculative(tup) \
-( \
-	(ItemPointerGetOffsetNumberNoCheck(&(tup)->t_ctid) == SpecTokenOffsetNumber) \
-)
+static inline bool
+HeapTupleHeaderIsSpeculative(const HeapTupleHeaderData *tup)
+{
+	return ItemPointerGetOffsetNumberNoCheck(&tup->t_ctid) == SpecTokenOffsetNumber;
+}
 
-#define HeapTupleHeaderGetSpeculativeToken(tup) \
-( \
-	AssertMacro(HeapTupleHeaderIsSpeculative(tup)), \
-	ItemPointerGetBlockNumber(&(tup)->t_ctid) \
-)
+static inline BlockNumber
+HeapTupleHeaderGetSpeculativeToken(const HeapTupleHeaderData *tup)
+{
+	Assert(HeapTupleHeaderIsSpeculative(tup));
+	return ItemPointerGetBlockNumber(&tup->t_ctid);
+}
 
-#define HeapTupleHeaderSetSpeculativeToken(tup, token)	\
-( \
-	ItemPointerSet(&(tup)->t_ctid, token, SpecTokenOffsetNumber) \
-)
+static inline void
+HeapTupleHeaderSetSpeculativeToken(HeapTupleHeaderData *tup, BlockNumber token)
+{
+	ItemPointerSet(&tup->t_ctid, token, SpecTokenOffsetNumber);
+}
 
-#define HeapTupleHeaderIndicatesMovedPartitions(tup) \
-	ItemPointerIndicatesMovedPartitions(&(tup)->t_ctid)
+static inline bool
+HeapTupleHeaderIndicatesMovedPartitions(const HeapTupleHeaderData *tup)
+{
+	return ItemPointerIndicatesMovedPartitions(&tup->t_ctid);
+}
 
-#define HeapTupleHeaderSetMovedPartitions(tup) \
-	ItemPointerSetMovedPartitions(&(tup)->t_ctid)
+static inline void
+HeapTupleHeaderSetMovedPartitions(HeapTupleHeaderData *tup)
+{
+	ItemPointerSetMovedPartitions(&tup->t_ctid);
+}
 
-#define HeapTupleHeaderGetDatumLength(tup) \
-	VARSIZE(tup)
+static inline uint32
+HeapTupleHeaderGetDatumLength(const HeapTupleHeaderData *tup)
+{
+	return VARSIZE(tup);
+}
 
-#define HeapTupleHeaderSetDatumLength(tup, len) \
-	SET_VARSIZE(tup, len)
+static inline void
+HeapTupleHeaderSetDatumLength(HeapTupleHeaderData *tup, uint32 len)
+{
+	SET_VARSIZE(tup, len);
+}
 
-#define HeapTupleHeaderGetTypeId(tup) \
-( \
-	(tup)->t_choice.t_datum.datum_typeid \
-)
+static inline Oid
+HeapTupleHeaderGetTypeId(const HeapTupleHeaderData *tup)
+{
+	return tup->t_choice.t_datum.datum_typeid;
+}
 
-#define HeapTupleHeaderSetTypeId(tup, typeid) \
-( \
-	(tup)->t_choice.t_datum.datum_typeid = (typeid) \
-)
+static inline void
+HeapTupleHeaderSetTypeId(HeapTupleHeaderData *tup, Oid typeid)
+{
+	tup->t_choice.t_datum.datum_typeid = typeid;
+}
 
-#define HeapTupleHeaderGetTypMod(tup) \
-( \
-	(tup)->t_choice.t_datum.datum_typmod \
-)
+static inline int32
+HeapTupleHeaderGetTypMod(const HeapTupleHeaderData *tup)
+{
+	return tup->t_choice.t_datum.datum_typmod;
+}
 
-#define HeapTupleHeaderSetTypMod(tup, typmod) \
-( \
-	(tup)->t_choice.t_datum.datum_typmod = (typmod) \
-)
+static inline void
+HeapTupleHeaderSetTypMod(HeapTupleHeaderData *tup, int32 typmod)
+{
+	tup->t_choice.t_datum.datum_typmod = typmod;
+}
 
 /*
  * Note that we stop considering a tuple HOT-updated as soon as it is known
  * aborted or the would-be updating transaction is known aborted.  For best
- * efficiency, check tuple visibility before using this macro, so that the
+ * efficiency, check tuple visibility before using this function, so that the
  * INVALID bits will be as up to date as possible.
  */
-#define HeapTupleHeaderIsHotUpdated(tup) \
-( \
-	((tup)->t_infomask2 & HEAP_HOT_UPDATED) != 0 && \
-	((tup)->t_infomask & HEAP_XMAX_INVALID) == 0 && \
-	!HeapTupleHeaderXminInvalid(tup) \
-)
-
-#define HeapTupleHeaderSetHotUpdated(tup) \
-( \
-	(tup)->t_infomask2 |= HEAP_HOT_UPDATED \
-)
-
-#define HeapTupleHeaderClearHotUpdated(tup) \
-( \
-	(tup)->t_infomask2 &= ~HEAP_HOT_UPDATED \
-)
+static inline bool
+HeapTupleHeaderIsHotUpdated(const HeapTupleHeaderData *tup)
+{
+	return
+		(tup->t_infomask2 & HEAP_HOT_UPDATED) != 0 &&
+		(tup->t_infomask & HEAP_XMAX_INVALID) == 0 &&
+		!HeapTupleHeaderXminInvalid(tup);
+}
 
-#define HeapTupleHeaderIsHeapOnly(tup) \
-( \
-  ((tup)->t_infomask2 & HEAP_ONLY_TUPLE) != 0 \
-)
+static inline void
+HeapTupleHeaderSetHotUpdated(HeapTupleHeaderData *tup)
+{
+	tup->t_infomask2 |= HEAP_HOT_UPDATED;
+}
 
-#define HeapTupleHeaderSetHeapOnly(tup) \
-( \
-  (tup)->t_infomask2 |= HEAP_ONLY_TUPLE \
-)
+static inline void
+HeapTupleHeaderClearHotUpdated(HeapTupleHeaderData *tup)
+{
+	tup->t_infomask2 &= ~HEAP_HOT_UPDATED;
+}
 
-#define HeapTupleHeaderClearHeapOnly(tup) \
-( \
-  (tup)->t_infomask2 &= ~HEAP_ONLY_TUPLE \
-)
+static inline bool
+HeapTupleHeaderIsHeapOnly(const HeapTupleHeaderData *tup) \
+{
+	return (tup->t_infomask2 & HEAP_ONLY_TUPLE) != 0;
+}
 
-#define HeapTupleHeaderHasMatch(tup) \
-( \
-  ((tup)->t_infomask2 & HEAP_TUPLE_HAS_MATCH) != 0 \
-)
+static inline void
+HeapTupleHeaderSetHeapOnly(HeapTupleHeaderData *tup)
+{
+	tup->t_infomask2 |= HEAP_ONLY_TUPLE;
+}
 
-#define HeapTupleHeaderSetMatch(tup) \
-( \
-  (tup)->t_infomask2 |= HEAP_TUPLE_HAS_MATCH \
-)
+static inline void
+HeapTupleHeaderClearHeapOnly(HeapTupleHeaderData *tup)
+{
+	tup->t_infomask2 &= ~HEAP_ONLY_TUPLE;
+}
 
-#define HeapTupleHeaderClearMatch(tup) \
-( \
-  (tup)->t_infomask2 &= ~HEAP_TUPLE_HAS_MATCH \
-)
+/*
+ * These are used with both HeapTuple and MinimalTuple, so they must be
+ * macros.
+ */
 
 #define HeapTupleHeaderGetNatts(tup) \
 	((tup)->t_infomask2 & HEAP_NATTS_MASK)
@@ -547,7 +595,11 @@ StaticAssertDecl(MaxOffsetNumber < SpecTokenOffsetNumber,
  * BITMAPLEN(NATTS) -
  *		Computes size of null bitmap given number of data columns.
  */
-#define BITMAPLEN(NATTS)	(((int)(NATTS) + 7) / 8)
+static inline int
+BITMAPLEN(int NATTS)
+{
+	return (NATTS + 7) / 8;
+}
 
 /*
  * MaxHeapTupleSize is the maximum allowed size of a heap tuple, including
@@ -651,48 +703,107 @@ struct MinimalTupleData
 
 #define SizeofMinimalTupleHeader offsetof(MinimalTupleData, t_bits)
 
+/*
+ * MinimalTuple accessor functions
+ */
+
+static inline bool
+HeapTupleHeaderHasMatch(const MinimalTupleData *tup)
+{
+	return (tup->t_infomask2 & HEAP_TUPLE_HAS_MATCH) != 0;
+}
+
+static inline void
+HeapTupleHeaderSetMatch(MinimalTupleData *tup)
+{
+	tup->t_infomask2 |= HEAP_TUPLE_HAS_MATCH;
+}
+
+static inline void
+HeapTupleHeaderClearMatch(MinimalTupleData *tup)
+{
+	tup->t_infomask2 &= ~HEAP_TUPLE_HAS_MATCH;
+}
+
 
 /*
  * GETSTRUCT - given a HeapTuple pointer, return address of the user data
  */
-#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
+static inline void *
+GETSTRUCT(const HeapTupleData *tuple)
+{
+	return ((char *) (tuple->t_data) + tuple->t_data->t_hoff);
+}
 
 /*
- * Accessor macros to be used with HeapTuple pointers.
+ * Accessor functions to be used with HeapTuple pointers.
  */
 
-#define HeapTupleHasNulls(tuple) \
-		(((tuple)->t_data->t_infomask & HEAP_HASNULL) != 0)
+static inline bool
+HeapTupleHasNulls(const HeapTupleData *tuple)
+{
+	return (tuple->t_data->t_infomask & HEAP_HASNULL) != 0;
+}
 
-#define HeapTupleNoNulls(tuple) \
-		(!((tuple)->t_data->t_infomask & HEAP_HASNULL))
+static inline bool
+HeapTupleNoNulls(const HeapTupleData *tuple)
+{
+	return !HeapTupleHasNulls(tuple);
+}
 
-#define HeapTupleHasVarWidth(tuple) \
-		(((tuple)->t_data->t_infomask & HEAP_HASVARWIDTH) != 0)
+static inline bool
+HeapTupleHasVarWidth(const HeapTupleData *tuple)
+{
+	return (tuple->t_data->t_infomask & HEAP_HASVARWIDTH) != 0;
+}
 
-#define HeapTupleAllFixed(tuple) \
-		(!((tuple)->t_data->t_infomask & HEAP_HASVARWIDTH))
+static inline bool
+HeapTupleAllFixed(const HeapTupleData *tuple)
+{
+	return !HeapTupleHasVarWidth(tuple);
+}
 
-#define HeapTupleHasExternal(tuple) \
-		(((tuple)->t_data->t_infomask & HEAP_HASEXTERNAL) != 0)
+static inline bool
+HeapTupleHasExternal(const HeapTupleData *tuple)
+{
+	return (tuple->t_data->t_infomask & HEAP_HASEXTERNAL) != 0;
+}
 
-#define HeapTupleIsHotUpdated(tuple) \
-		HeapTupleHeaderIsHotUpdated((tuple)->t_data)
+static inline bool
+HeapTupleIsHotUpdated(const HeapTupleData *tuple)
+{
+	return HeapTupleHeaderIsHotUpdated(tuple->t_data);
+}
 
-#define HeapTupleSetHotUpdated(tuple) \
-		HeapTupleHeaderSetHotUpdated((tuple)->t_data)
+static inline void
+HeapTupleSetHotUpdated(const HeapTupleData *tuple)
+{
+	HeapTupleHeaderSetHotUpdated(tuple->t_data);
+}
 
-#define HeapTupleClearHotUpdated(tuple) \
-		HeapTupleHeaderClearHotUpdated((tuple)->t_data)
+static inline void
+HeapTupleClearHotUpdated(const HeapTupleData *tuple)
+{
+	HeapTupleHeaderClearHotUpdated(tuple->t_data);
+}
 
-#define HeapTupleIsHeapOnly(tuple) \
-		HeapTupleHeaderIsHeapOnly((tuple)->t_data)
+static inline bool
+HeapTupleIsHeapOnly(const HeapTupleData *tuple)
+{
+	return HeapTupleHeaderIsHeapOnly(tuple->t_data);
+}
 
-#define HeapTupleSetHeapOnly(tuple) \
-		HeapTupleHeaderSetHeapOnly((tuple)->t_data)
+static inline void
+HeapTupleSetHeapOnly(const HeapTupleData *tuple)
+{
+	HeapTupleHeaderSetHeapOnly(tuple->t_data);
+}
 
-#define HeapTupleClearHeapOnly(tuple) \
-		HeapTupleHeaderClearHeapOnly((tuple)->t_data)
+static inline void
+HeapTupleClearHeapOnly(const HeapTupleData *tuple)
+{
+	HeapTupleHeaderClearHeapOnly(tuple->t_data);
+}
 
 /* prototypes for functions in common/heaptuple.c */
 extern Size heap_compute_data_size(TupleDesc tupleDesc,
diff --git a/src/include/access/itup.h b/src/include/access/itup.h
index 4393b19a7fd..bce0e8aa8be 100644
--- a/src/include/access/itup.h
+++ b/src/include/access/itup.h
@@ -68,9 +68,23 @@ typedef IndexAttributeBitMapData * IndexAttributeBitMap;
 #define INDEX_VAR_MASK	0x4000
 #define INDEX_NULL_MASK 0x8000
 
-#define IndexTupleSize(itup)		((Size) ((itup)->t_info & INDEX_SIZE_MASK))
-#define IndexTupleHasNulls(itup)	((((IndexTuple) (itup))->t_info & INDEX_NULL_MASK))
-#define IndexTupleHasVarwidths(itup) ((((IndexTuple) (itup))->t_info & INDEX_VAR_MASK))
+static inline Size
+IndexTupleSize(const IndexTupleData *itup)
+{
+	return (itup->t_info & INDEX_SIZE_MASK);
+}
+
+static inline bool
+IndexTupleHasNulls(const IndexTupleData *itup)
+{
+	return itup->t_info & INDEX_NULL_MASK;
+}
+
+static inline bool
+IndexTupleHasVarwidths(const IndexTupleData *itup)
+{
+	return itup->t_info & INDEX_VAR_MASK;
+}

 /* routines in indextuple.c */
-- 
2.47.1

From b8f2c2f144c1763029ba8b84093a1885f57627a1 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email-masked]>
Date: Fri, 27 Dec 2024 11:07:35 +0100
Subject: [PATCH 3/3] WIP: Use some generic selection

Use generic selection (C11 _Generic) to allow a few functions in
htup_details.h to work with both HeapTuple and MinimalTuple.
---
 src/include/access/htup_details.h | 50 +++++++++++++++++++++++--------
 1 file changed, 37 insertions(+), 13 deletions(-)

diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index bab480fe2f0..cb862ff089d 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -574,21 +574,23 @@ HeapTupleHeaderClearHeapOnly(HeapTupleHeaderData *tup)
 	tup->t_infomask2 &= ~HEAP_ONLY_TUPLE;
 }
 
-/*
- * These are used with both HeapTuple and MinimalTuple, so they must be
- * macros.
- */
-
-#define HeapTupleHeaderGetNatts(tup) \
-	((tup)->t_infomask2 & HEAP_NATTS_MASK)
+static inline int
+HeapTupleHeaderGetNatts(const HeapTupleHeaderData *tup)
+{
+	return tup->t_infomask2 & HEAP_NATTS_MASK;
+}
 
-#define HeapTupleHeaderSetNatts(tup, natts) \
-( \
-	(tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \
-)
+static inline void
+HeapTupleHeaderSetNatts(HeapTupleHeaderData *tup, int natts)
+{
+	tup->t_infomask2 = (tup->t_infomask2 & ~HEAP_NATTS_MASK) | natts;
+}
 
-#define HeapTupleHeaderHasExternal(tup) \
-		(((tup)->t_infomask & HEAP_HASEXTERNAL) != 0)
+static inline bool
+HeapTupleHeaderHasExternal(HeapTupleHeaderData *tup)
+{
+	return (tup->t_infomask & HEAP_HASEXTERNAL) != 0;
+}

 /*
@@ -725,6 +727,28 @@ HeapTupleHeaderClearMatch(MinimalTupleData *tup)
 	tup->t_infomask2 &= ~HEAP_TUPLE_HAS_MATCH;
 }
 
+/*
+ * Accessor functions for both HeapTupleHeader and MinimalTuple
+ */
+
+static inline bool
+MinimalTupleHasExternal(MinimalTupleData *tup)
+{
+	return (tup->t_infomask & HEAP_HASEXTERNAL) != 0;
+}
+
+#define HeapTupleHeaderHasExternal(tup) \
+	_Generic((tup), HeapTupleHeaderData*: HeapTupleHeaderHasExternal, MinimalTupleData*: MinimalTupleHasExternal)(tup)
+
+static inline void
+MinimalTupleSetNatts(MinimalTupleData *tup, int natts)
+{
+	tup->t_infomask2 = (tup->t_infomask2 & ~HEAP_NATTS_MASK) | natts;
+}
+
+#define HeapTupleHeaderSetNatts(tup, natts) \
+	_Generic((tup), HeapTupleHeaderData*: HeapTupleHeaderSetNatts, MinimalTupleData*: MinimalTupleSetNatts)(tup, natts)
+
 
 /*
  * GETSTRUCT - given a HeapTuple pointer, return address of the user data
-- 
2.47.1