Convert varatt.h macros to static inline functions

Started by Peter Eisentraut6 months ago16 messages
#1Peter Eisentraut
peter@eisentraut.org
2 attachment(s)

I had this lying around as a draft patch, as part of my ongoing campaign
to convert many complicated macros to static inline functions. Since
the topic was mentioned in another thread [0]/messages/by-id/1749799.1752797397@sss.pgh.pa.us, I cleaned up the patch so
that we can all look at it.

The titular change is to convert the macros in varatt.h to inline
functions, so they are easier to read and use. I only touched the ones
for external use, not the internal ones, mainly because I don't have a
way to test a big-endian build.

Part of the change is also figuring out exactly what the argument and
return types should be. In many cases, there were some inconsistencies,
because the macros would just cast anything you give them into the shape
they want, no matter whether it makes sense. In particular, the callers
were inconsistent about whether macros like VARDATA() and VARSIZE()
should take struct varlena or Datum, or I guess both. I cleaned this up
by adding the required DatumGetPointer() calls in the first patch. The
thread [0]/messages/by-id/1749799.1752797397@sss.pgh.pa.us is now discussing some other ideas, but this is what I had,
and this way it's at least consistent with other existing code.

[0]: /messages/by-id/1749799.1752797397@sss.pgh.pa.us
/messages/by-id/1749799.1752797397@sss.pgh.pa.us

Attachments:

v1-0001-Fix-varatt-versus-Datum-type-confusions.patchtext/plain; charset=UTF-8; name=v1-0001-Fix-varatt-versus-Datum-type-confusions.patchDownload
From fb4036e3ea593fe3d6f0791793cc9e282226783d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 31 Jul 2025 14:54:16 +0200
Subject: [PATCH v1 1/2] Fix varatt versus Datum type confusions

Macros like VARDATA() and VARSIZE() should be thought of as taking
values of type pointer to struct varlena or some other related struct.
The way they are implemented, you can pass anything to it and it will
cast it right.  But this is in principle incorrect.  To fix, add the
required DatumGetPointer() calls.  Or in a couple of cases, remove
superfluous PointerGetDatum() calls.
---
 contrib/hstore/hstore_gin.c                 |  2 +-
 contrib/hstore/hstore_gist.c                |  4 ++--
 contrib/hstore/hstore_io.c                  | 24 ++++++++++-----------
 contrib/hstore/hstore_op.c                  |  4 ++--
 contrib/test_decoding/test_decoding.c       |  2 +-
 src/backend/access/brin/brin_minmax_multi.c |  2 +-
 src/backend/access/common/heaptuple.c       |  8 +++----
 src/backend/access/common/reloptions.c      |  8 +++----
 src/backend/access/common/toast_internals.c |  2 +-
 src/backend/access/gin/gininsert.c          |  2 +-
 src/backend/access/spgist/spgutils.c        |  4 ++--
 src/backend/access/table/toast_helper.c     |  2 +-
 src/backend/replication/logical/proto.c     |  2 +-
 src/backend/replication/pgoutput/pgoutput.c |  4 ++--
 src/backend/statistics/mcv.c                |  2 +-
 src/backend/tsearch/ts_selfuncs.c           |  2 +-
 src/backend/utils/adt/jsonb_gin.c           |  4 ++--
 src/backend/utils/adt/jsonb_op.c            |  8 +++----
 src/backend/utils/adt/jsonfuncs.c           |  4 ++--
 src/backend/utils/adt/jsonpath_exec.c       |  4 ++--
 src/backend/utils/adt/multirangetypes.c     |  7 +++---
 src/backend/utils/adt/rangetypes.c          |  6 ++----
 src/backend/utils/adt/tsvector_op.c         | 24 ++++++++++-----------
 23 files changed, 65 insertions(+), 66 deletions(-)

diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c
index 766c00bb6a7..2e5fa115924 100644
--- a/contrib/hstore/hstore_gin.c
+++ b/contrib/hstore/hstore_gin.c
@@ -127,7 +127,7 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
 			/* Nulls in the array are ignored, cf hstoreArrayToPairs */
 			if (key_nulls[i])
 				continue;
-			item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
+			item = makeitem(VARDATA(DatumGetPointer(key_datums[i])), VARSIZE(DatumGetPointer(key_datums[i])) - VARHDRSZ, KEYFLAG);
 			entries[j++] = PointerGetDatum(item);
 		}
 
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index a3b08af3850..69515dc3d3f 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -576,7 +576,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 			if (key_nulls[i])
 				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
+			crc = crc32_sz(VARDATA(DatumGetPointer(key_datums[i])), VARSIZE(DatumGetPointer(key_datums[i])) - VARHDRSZ);
 			if (!(GETBIT(sign, HASHVAL(crc, siglen))))
 				res = false;
 		}
@@ -599,7 +599,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 			if (key_nulls[i])
 				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
+			crc = crc32_sz(VARDATA(DatumGetPointer(key_datums[i])), VARSIZE(DatumGetPointer(key_datums[i])) - VARHDRSZ);
 			if (GETBIT(sign, HASHVAL(crc, siglen)))
 				res = true;
 		}
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 4f867e4bd1f..9c53877c4a5 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -684,22 +684,22 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 
 		if (!value_nulls || value_nulls[i])
 		{
-			pairs[i].key = VARDATA(key_datums[i]);
+			pairs[i].key = VARDATA(DatumGetPointer(key_datums[i]));
 			pairs[i].val = NULL;
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE(DatumGetPointer(key_datums[i])) - VARHDRSZ);
 			pairs[i].vallen = 4;
 			pairs[i].isnull = true;
 			pairs[i].needfree = false;
 		}
 		else
 		{
-			pairs[i].key = VARDATA(key_datums[i]);
-			pairs[i].val = VARDATA(value_datums[i]);
+			pairs[i].key = VARDATA(DatumGetPointer(key_datums[i]));
+			pairs[i].val = VARDATA(DatumGetPointer(value_datums[i]));
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE(DatumGetPointer(key_datums[i])) - VARHDRSZ);
 			pairs[i].vallen =
-				hstoreCheckValLen(VARSIZE(value_datums[i]) - VARHDRSZ);
+				hstoreCheckValLen(VARSIZE(DatumGetPointer(value_datums[i])) - VARHDRSZ);
 			pairs[i].isnull = false;
 			pairs[i].needfree = false;
 		}
@@ -778,22 +778,22 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
 		if (in_nulls[i * 2 + 1])
 		{
-			pairs[i].key = VARDATA(in_datums[i * 2]);
+			pairs[i].key = VARDATA(DatumGetPointer(in_datums[i * 2]));
 			pairs[i].val = NULL;
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE(DatumGetPointer(in_datums[i * 2])) - VARHDRSZ);
 			pairs[i].vallen = 4;
 			pairs[i].isnull = true;
 			pairs[i].needfree = false;
 		}
 		else
 		{
-			pairs[i].key = VARDATA(in_datums[i * 2]);
-			pairs[i].val = VARDATA(in_datums[i * 2 + 1]);
+			pairs[i].key = VARDATA(DatumGetPointer(in_datums[i * 2]));
+			pairs[i].val = VARDATA(DatumGetPointer(in_datums[i * 2 + 1]));
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE(DatumGetPointer(in_datums[i * 2])) - VARHDRSZ);
 			pairs[i].vallen =
-				hstoreCheckValLen(VARSIZE(in_datums[i * 2 + 1]) - VARHDRSZ);
+				hstoreCheckValLen(VARSIZE(DatumGetPointer(in_datums[i * 2 + 1])) - VARHDRSZ);
 			pairs[i].isnull = false;
 			pairs[i].needfree = false;
 		}
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 5e57eceffc8..bcba75f9258 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -107,8 +107,8 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
 	{
 		if (!key_nulls[i])
 		{
-			key_pairs[j].key = VARDATA(key_datums[i]);
-			key_pairs[j].keylen = VARSIZE(key_datums[i]) - VARHDRSZ;
+			key_pairs[j].key = VARDATA(DatumGetPointer(key_datums[i]));
+			key_pairs[j].keylen = VARSIZE(DatumGetPointer(key_datums[i])) - VARHDRSZ;
 			key_pairs[j].val = NULL;
 			key_pairs[j].vallen = 0;
 			key_pairs[j].needfree = 0;
diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c
index bb495563200..f671a7d4b31 100644
--- a/contrib/test_decoding/test_decoding.c
+++ b/contrib/test_decoding/test_decoding.c
@@ -581,7 +581,7 @@ tuple_to_stringinfo(StringInfo s, TupleDesc tupdesc, HeapTuple tuple, bool skip_
 		/* print data */
 		if (isnull)
 			appendStringInfoString(s, "null");
-		else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(origval))
+		else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(DatumGetPointer(origval)))
 			appendStringInfoString(s, "unchanged-toast-datum");
 		else if (!typisvarlena)
 			print_literal(s, typid,
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 0d1507a2a36..b85a70a0db2 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -624,7 +624,7 @@ brin_range_serialize(Ranges *range)
 
 		for (i = 0; i < nvalues; i++)
 		{
-			len += VARSIZE_ANY(range->values[i]);
+			len += VARSIZE_ANY(DatumGetPointer(range->values[i]));
 		}
 	}
 	else if (typlen == -2)		/* cstring */
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 969d1028cae..a410b5eb99b 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -189,7 +189,7 @@ getmissingattr(TupleDesc tupleDesc,
 			if (att->attlen > 0)
 				key.len = att->attlen;
 			else
-				key.len = VARSIZE_ANY(attrmiss->am_value);
+				key.len = VARSIZE_ANY(DatumGetPointer(attrmiss->am_value));
 			key.value = attrmiss->am_value;
 
 			entry = hash_search(missing_cache, &key, HASH_ENTER, &found);
@@ -901,9 +901,9 @@ expand_tuple(HeapTuple *targetHeapTuple,
 												  att->attlen,
 												  attrmiss[attnum].am_value);
 
-				targetDataLen = att_addlength_pointer(targetDataLen,
-													  att->attlen,
-													  attrmiss[attnum].am_value);
+				targetDataLen = att_addlength_datum(targetDataLen,
+													att->attlen,
+													attrmiss[attnum].am_value);
 			}
 			else
 			{
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 50747c16396..594a657ea1a 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1190,8 +1190,8 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
 
 		for (i = 0; i < noldoptions; i++)
 		{
-			char	   *text_str = VARDATA(oldoptions[i]);
-			int			text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
+			char	   *text_str = VARDATA(DatumGetPointer(oldoptions[i]));
+			int			text_len = VARSIZE(DatumGetPointer(oldoptions[i])) - VARHDRSZ;
 
 			/* Search for a match in defList */
 			foreach(cell, defList)
@@ -1456,8 +1456,8 @@ parseRelOptionsInternal(Datum options, bool validate,
 
 	for (i = 0; i < noptions; i++)
 	{
-		char	   *text_str = VARDATA(optiondatums[i]);
-		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		char	   *text_str = VARDATA(DatumGetPointer(optiondatums[i]));
+		int			text_len = VARSIZE(DatumGetPointer(optiondatums[i])) - VARHDRSZ;
 		int			j;
 
 		/* Search for a match in reloptions */
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 7d8be8346ce..196e06115e9 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -144,7 +144,7 @@ toast_save_datum(Relation rel, Datum value,
 	int			num_indexes;
 	int			validIndex;
 
-	Assert(!VARATT_IS_EXTERNAL(value));
+	Assert(!VARATT_IS_EXTERNAL(dval));
 
 	/*
 	 * Open the toast relation and its indexes.  We can use the index to check
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index a65acd89104..47b1898a064 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -2233,7 +2233,7 @@ _gin_build_tuple(OffsetNumber attrnum, unsigned char category,
 	else if (typlen > 0)
 		keylen = typlen;
 	else if (typlen == -1)
-		keylen = VARSIZE_ANY(key);
+		keylen = VARSIZE_ANY(DatumGetPointer(key));
 	else if (typlen == -2)
 		keylen = strlen(DatumGetPointer(key)) + 1;
 	else
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 95fea74e296..9b86c016acb 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -785,7 +785,7 @@ SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum)
 	else if (att->attlen > 0)
 		size = att->attlen;
 	else
-		size = VARSIZE_ANY(datum);
+		size = VARSIZE_ANY(DatumGetPointer(datum));
 
 	return MAXALIGN(size);
 }
@@ -804,7 +804,7 @@ memcpyInnerDatum(void *target, SpGistTypeDesc *att, Datum datum)
 	}
 	else
 	{
-		size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
+		size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(DatumGetPointer(datum));
 		memcpy(target, DatumGetPointer(datum), size);
 	}
 }
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index b60fab0a4d2..11f97d65367 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -330,7 +330,7 @@ toast_delete_external(Relation rel, const Datum *values, const bool *isnull,
 
 			if (isnull[i])
 				continue;
-			else if (VARATT_IS_EXTERNAL_ONDISK(value))
+			else if (VARATT_IS_EXTERNAL_ONDISK(DatumGetPointer(value)))
 				toast_delete_datum(rel, value, is_speculative);
 		}
 	}
diff --git a/src/backend/replication/logical/proto.c b/src/backend/replication/logical/proto.c
index 1a352b542dc..1b3d9eb49dd 100644
--- a/src/backend/replication/logical/proto.c
+++ b/src/backend/replication/logical/proto.c
@@ -809,7 +809,7 @@ logicalrep_write_tuple(StringInfo out, Relation rel, TupleTableSlot *slot,
 			continue;
 		}
 
-		if (att->attlen == -1 && VARATT_IS_EXTERNAL_ONDISK(values[i]))
+		if (att->attlen == -1 && VARATT_IS_EXTERNAL_ONDISK(DatumGetPointer(values[i])))
 		{
 			/*
 			 * Unchanged toasted datum.  (Note that we don't promise to detect
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index f4c977262c5..80540c017bd 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -1374,8 +1374,8 @@ pgoutput_row_filter(Relation relation, TupleTableSlot *old_slot,
 		 * VARTAG_INDIRECT. See ReorderBufferToastReplace.
 		 */
 		if (att->attlen == -1 &&
-			VARATT_IS_EXTERNAL_ONDISK(new_slot->tts_values[i]) &&
-			!VARATT_IS_EXTERNAL_ONDISK(old_slot->tts_values[i]))
+			VARATT_IS_EXTERNAL_ONDISK(DatumGetPointer(new_slot->tts_values[i])) &&
+			!VARATT_IS_EXTERNAL_ONDISK(DatumGetPointer(old_slot->tts_values[i])))
 		{
 			if (!tmp_new_slot)
 			{
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index d98cda698d9..f59fb821543 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -767,7 +767,7 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats)
 				values[dim][i] = PointerGetDatum(PG_DETOAST_DATUM(values[dim][i]));
 
 				/* serialized length (uint32 length + data) */
-				len = VARSIZE_ANY_EXHDR(values[dim][i]);
+				len = VARSIZE_ANY_EXHDR(DatumGetPointer(values[dim][i]));
 				info[dim].nbytes += sizeof(uint32); /* length */
 				info[dim].nbytes += len;	/* value (no header) */
 
diff --git a/src/backend/tsearch/ts_selfuncs.c b/src/backend/tsearch/ts_selfuncs.c
index 0c1d2bc1109..453a5e5c2ea 100644
--- a/src/backend/tsearch/ts_selfuncs.c
+++ b/src/backend/tsearch/ts_selfuncs.c
@@ -233,7 +233,7 @@ mcelem_tsquery_selec(TSQuery query, Datum *mcelem, int nmcelem,
 		 * The text Datums came from an array, so it cannot be compressed or
 		 * stored out-of-line -- it's safe to use VARSIZE_ANY*.
 		 */
-		Assert(!VARATT_IS_COMPRESSED(mcelem[i]) && !VARATT_IS_EXTERNAL(mcelem[i]));
+		Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(mcelem[i])) && !VARATT_IS_EXTERNAL(DatumGetPointer(mcelem[i])));
 		lookup[i].element = (text *) DatumGetPointer(mcelem[i]);
 		lookup[i].frequency = numbers[i];
 	}
diff --git a/src/backend/utils/adt/jsonb_gin.c b/src/backend/utils/adt/jsonb_gin.c
index c1950792b5a..9b56248cf0b 100644
--- a/src/backend/utils/adt/jsonb_gin.c
+++ b/src/backend/utils/adt/jsonb_gin.c
@@ -896,8 +896,8 @@ gin_extract_jsonb_query(PG_FUNCTION_ARGS)
 				continue;
 			/* We rely on the array elements not being toasted */
 			entries[j++] = make_text_key(JGINFLAG_KEY,
-										 VARDATA_ANY(key_datums[i]),
-										 VARSIZE_ANY_EXHDR(key_datums[i]));
+										 VARDATA_ANY(DatumGetPointer(key_datums[i])),
+										 VARSIZE_ANY_EXHDR(DatumGetPointer(key_datums[i])));
 		}
 
 		*nentries = j;
diff --git a/src/backend/utils/adt/jsonb_op.c b/src/backend/utils/adt/jsonb_op.c
index fa5603f26e1..51d38e321fb 100644
--- a/src/backend/utils/adt/jsonb_op.c
+++ b/src/backend/utils/adt/jsonb_op.c
@@ -63,8 +63,8 @@ jsonb_exists_any(PG_FUNCTION_ARGS)
 
 		strVal.type = jbvString;
 		/* We rely on the array elements not being toasted */
-		strVal.val.string.val = VARDATA_ANY(key_datums[i]);
-		strVal.val.string.len = VARSIZE_ANY_EXHDR(key_datums[i]);
+		strVal.val.string.val = VARDATA_ANY(DatumGetPointer(key_datums[i]));
+		strVal.val.string.len = VARSIZE_ANY_EXHDR(DatumGetPointer(key_datums[i]));
 
 		if (findJsonbValueFromContainer(&jb->root,
 										JB_FOBJECT | JB_FARRAY,
@@ -96,8 +96,8 @@ jsonb_exists_all(PG_FUNCTION_ARGS)
 
 		strVal.type = jbvString;
 		/* We rely on the array elements not being toasted */
-		strVal.val.string.val = VARDATA_ANY(key_datums[i]);
-		strVal.val.string.len = VARSIZE_ANY_EXHDR(key_datums[i]);
+		strVal.val.string.val = VARDATA_ANY(DatumGetPointer(key_datums[i]));
+		strVal.val.string.len = VARSIZE_ANY_EXHDR(DatumGetPointer(key_datums[i]));
 
 		if (findJsonbValueFromContainer(&jb->root,
 										JB_FOBJECT | JB_FARRAY,
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index bcb1720b6cd..370456408bf 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -4766,8 +4766,8 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
 					continue;
 
 				/* We rely on the array elements not being toasted */
-				keyptr = VARDATA_ANY(keys_elems[i]);
-				keylen = VARSIZE_ANY_EXHDR(keys_elems[i]);
+				keyptr = VARDATA_ANY(DatumGetPointer(keys_elems[i]));
+				keylen = VARSIZE_ANY_EXHDR(DatumGetPointer(keys_elems[i]));
 				if (keylen == v.val.string.len &&
 					memcmp(keyptr, v.val.string.val, keylen) == 0)
 				{
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index dbab24737ef..407041b14a1 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -3074,8 +3074,8 @@ JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
 		case TEXTOID:
 		case VARCHAROID:
 			res->type = jbvString;
-			res->val.string.val = VARDATA_ANY(val);
-			res->val.string.len = VARSIZE_ANY_EXHDR(val);
+			res->val.string.val = VARDATA_ANY(DatumGetPointer(val));
+			res->val.string.len = VARSIZE_ANY_EXHDR(DatumGetPointer(val));
 			break;
 		case DATEOID:
 		case TIMEOID:
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index cd84ced5b48..bb1c7011e38 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -394,12 +394,13 @@ multirange_send(PG_FUNCTION_ARGS)
 	for (int i = 0; i < range_count; i++)
 	{
 		Datum		range;
+		bytea	   *outputbytes;
 
 		range = RangeTypePGetDatum(ranges[i]);
-		range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+		outputbytes = SendFunctionCall(&cache->typioproc, range);
 
-		pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
-		pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+		pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
+		pq_sendbytes(buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ);
 	}
 
 	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 66cc0acf4a7..802646df1bb 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -285,8 +285,7 @@ range_send(PG_FUNCTION_ARGS)
 
 	if (RANGE_HAS_LBOUND(flags))
 	{
-		Datum		bound = PointerGetDatum(SendFunctionCall(&cache->typioproc,
-															 lower.val));
+		bytea	   *bound = SendFunctionCall(&cache->typioproc, lower.val);
 		uint32		bound_len = VARSIZE(bound) - VARHDRSZ;
 		char	   *bound_data = VARDATA(bound);
 
@@ -296,8 +295,7 @@ range_send(PG_FUNCTION_ARGS)
 
 	if (RANGE_HAS_UBOUND(flags))
 	{
-		Datum		bound = PointerGetDatum(SendFunctionCall(&cache->typioproc,
-															 upper.val));
+		bytea	   *bound = SendFunctionCall(&cache->typioproc, upper.val);
 		uint32		bound_len = VARSIZE(bound) - VARHDRSZ;
 		char	   *bound_data = VARDATA(bound);
 
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 1fa1275ca63..0625da9532f 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -329,8 +329,8 @@ tsvector_setweight_by_filter(PG_FUNCTION_ARGS)
 		if (nulls[i])
 			continue;
 
-		lex = VARDATA(dlexemes[i]);
-		lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		lex = VARDATA(DatumGetPointer(dlexemes[i]));
+		lex_len = VARSIZE(DatumGetPointer(dlexemes[i])) - VARHDRSZ;
 		lex_pos = tsvector_bsearch(tsout, lex, lex_len);
 
 		if (lex_pos >= 0 && (j = POSDATALEN(tsout, entry + lex_pos)) != 0)
@@ -443,10 +443,10 @@ compare_text_lexemes(const void *va, const void *vb)
 {
 	Datum		a = *((const Datum *) va);
 	Datum		b = *((const Datum *) vb);
-	char	   *alex = VARDATA_ANY(a);
-	int			alex_len = VARSIZE_ANY_EXHDR(a);
-	char	   *blex = VARDATA_ANY(b);
-	int			blex_len = VARSIZE_ANY_EXHDR(b);
+	char	   *alex = VARDATA_ANY(DatumGetPointer(a));
+	int			alex_len = VARSIZE_ANY_EXHDR(DatumGetPointer(a));
+	char	   *blex = VARDATA_ANY(DatumGetPointer(b));
+	int			blex_len = VARSIZE_ANY_EXHDR(DatumGetPointer(b));
 
 	return tsCompareString(alex, alex_len, blex, blex_len, false);
 }
@@ -605,8 +605,8 @@ tsvector_delete_arr(PG_FUNCTION_ARGS)
 		if (nulls[i])
 			continue;
 
-		lex = VARDATA(dlexemes[i]);
-		lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		lex = VARDATA(DatumGetPointer(dlexemes[i]));
+		lex_len = VARSIZE(DatumGetPointer(dlexemes[i])) - VARHDRSZ;
 		lex_pos = tsvector_bsearch(tsin, lex, lex_len);
 
 		if (lex_pos >= 0)
@@ -770,7 +770,7 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 					 errmsg("lexeme array may not contain nulls")));
 
-		if (VARSIZE(dlexemes[i]) - VARHDRSZ == 0)
+		if (VARSIZE(DatumGetPointer(dlexemes[i])) - VARHDRSZ == 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING),
 					 errmsg("lexeme array may not contain empty strings")));
@@ -786,7 +786,7 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 
 	/* Calculate space needed for surviving lexemes. */
 	for (i = 0; i < nitems; i++)
-		datalen += VARSIZE(dlexemes[i]) - VARHDRSZ;
+		datalen += VARSIZE(DatumGetPointer(dlexemes[i])) - VARHDRSZ;
 	tslen = CALCDATASIZE(nitems, datalen);
 
 	/* Allocate and fill tsvector. */
@@ -798,8 +798,8 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 	cur = STRPTR(tsout);
 	for (i = 0; i < nitems; i++)
 	{
-		char	   *lex = VARDATA(dlexemes[i]);
-		int			lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		char	   *lex = VARDATA(DatumGetPointer(dlexemes[i]));
+		int			lex_len = VARSIZE(DatumGetPointer(dlexemes[i])) - VARHDRSZ;
 
 		memcpy(cur, lex, lex_len);
 		arrout[i].haspos = 0;

base-commit: 3357471cf9f5e470dfed0c7919bcf31c7efaf2b9
-- 
2.50.1

v1-0002-Convert-varatt.h-macros-to-static-inline-function.patchtext/plain; charset=UTF-8; name=v1-0002-Convert-varatt.h-macros-to-static-inline-function.patchDownload
From e1ef8d2e1233e6cfb9dfdd1514fc9a311b9bee2c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 31 Jul 2025 14:54:16 +0200
Subject: [PATCH v1 2/2] Convert varatt.h macros to static inline functions

Only the external interfaces, not the endian-dependent internal
macros (mostly because I don't have access to test big-endian
architectures).  (The VARTAG_1B_E() changes are required for C++
compatibility.)
---
 doc/src/sgml/xfunc.sgml |   2 +-
 src/include/varatt.h    | 270 ++++++++++++++++++++++++++++++----------
 2 files changed, 205 insertions(+), 67 deletions(-)

diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 2d81afce8cb..30219f432d9 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2165,7 +2165,7 @@ <title>Base Types in C-Language Functions</title>
      it's considered good style to use the macro <literal>VARHDRSZ</literal>
      to refer to the size of the overhead for a variable-length type.
      Also, the length field <emphasis>must</emphasis> be set using the
-     <literal>SET_VARSIZE</literal> macro, not by simple assignment.
+     <literal>SET_VARSIZE</literal> function, not by simple assignment.
     </para>
 
     <para>
diff --git a/src/include/varatt.h b/src/include/varatt.h
index 2e8564d4998..4061ec1a82a 100644
--- a/src/include/varatt.h
+++ b/src/include/varatt.h
@@ -90,14 +90,27 @@ typedef enum vartag_external
 } vartag_external;
 
 /* this test relies on the specific tag values above */
-#define VARTAG_IS_EXPANDED(tag) \
-	(((tag) & ~1) == VARTAG_EXPANDED_RO)
+static inline bool
+VARTAG_IS_EXPANDED(vartag_external tag)
+{
+	return ((tag & ~1) == VARTAG_EXPANDED_RO);
+}
 
-#define VARTAG_SIZE(tag) \
-	((tag) == VARTAG_INDIRECT ? sizeof(varatt_indirect) : \
-	 VARTAG_IS_EXPANDED(tag) ? sizeof(varatt_expanded) : \
-	 (tag) == VARTAG_ONDISK ? sizeof(varatt_external) : \
-	 (AssertMacro(false), 0))
+static inline size_t
+VARTAG_SIZE(vartag_external tag)
+{
+	if (tag == VARTAG_INDIRECT)
+		return sizeof(varatt_indirect);
+	else if (VARTAG_IS_EXPANDED(tag))
+		return sizeof(varatt_expanded);
+	else if (tag == VARTAG_ONDISK)
+		return sizeof(varatt_external);
+	else
+	{
+		Assert(false);
+		return 0;
+	}
+}
 
 /*
  * These structs describe the header of a varlena object that may have been
@@ -194,7 +207,7 @@ typedef struct
 #define VARSIZE_1B(PTR) \
 	(((varattrib_1b *) (PTR))->va_header & 0x7F)
 #define VARTAG_1B_E(PTR) \
-	(((varattrib_1b_e *) (PTR))->va_tag)
+	((vartag_external) ((varattrib_1b_e *) (PTR))->va_tag)
 
 #define SET_VARSIZE_4B(PTR,len) \
 	(((varattrib_4b *) (PTR))->va_4byte.va_header = (len) & 0x3FFFFFFF)
@@ -227,7 +240,7 @@ typedef struct
 #define VARSIZE_1B(PTR) \
 	((((varattrib_1b *) (PTR))->va_header >> 1) & 0x7F)
 #define VARTAG_1B_E(PTR) \
-	(((varattrib_1b_e *) (PTR))->va_tag)
+	((vartag_external) ((varattrib_1b_e *) (PTR))->va_tag)
 
 #define SET_VARSIZE_4B(PTR,len) \
 	(((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2))
@@ -254,13 +267,6 @@ typedef struct
 #define VARHDRSZ_COMPRESSED		offsetof(varattrib_4b, va_compressed.va_data)
 #define VARHDRSZ_SHORT			offsetof(varattrib_1b, va_data)
 
-#define VARATT_SHORT_MAX		0x7F
-#define VARATT_CAN_MAKE_SHORT(PTR) \
-	(VARATT_IS_4B_U(PTR) && \
-	 (VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT) <= VARATT_SHORT_MAX)
-#define VARATT_CONVERTED_SHORT_SIZE(PTR) \
-	(VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT)
-
 /*
  * In consumers oblivious to data alignment, call PG_DETOAST_DATUM_PACKED(),
  * VARDATA_ANY(), VARSIZE_ANY() and VARSIZE_ANY_EXHDR().  Elsewhere, call
@@ -272,63 +278,195 @@ typedef struct
  * Code assembling a new datum should call VARDATA() and SET_VARSIZE().
  * (Datums begin life untoasted.)
  *
- * Other macros here should usually be used only by tuple assembly/disassembly
+ * Other functions here should usually be used only by tuple assembly/disassembly
  * code and code that specifically wants to work with still-toasted Datums.
  */
-#define VARDATA(PTR)						VARDATA_4B(PTR)
-#define VARSIZE(PTR)						VARSIZE_4B(PTR)
-
-#define VARSIZE_SHORT(PTR)					VARSIZE_1B(PTR)
-#define VARDATA_SHORT(PTR)					VARDATA_1B(PTR)
-
-#define VARTAG_EXTERNAL(PTR)				VARTAG_1B_E(PTR)
-#define VARSIZE_EXTERNAL(PTR)				(VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR)))
-#define VARDATA_EXTERNAL(PTR)				VARDATA_1B_E(PTR)
-
-#define VARATT_IS_COMPRESSED(PTR)			VARATT_IS_4B_C(PTR)
-#define VARATT_IS_EXTERNAL(PTR)				VARATT_IS_1B_E(PTR)
-#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
-#define VARATT_IS_EXTERNAL_INDIRECT(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_INDIRECT)
-#define VARATT_IS_EXTERNAL_EXPANDED_RO(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RO)
-#define VARATT_IS_EXTERNAL_EXPANDED_RW(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RW)
-#define VARATT_IS_EXTERNAL_EXPANDED(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)))
-#define VARATT_IS_EXTERNAL_NON_EXPANDED(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)))
-#define VARATT_IS_SHORT(PTR)				VARATT_IS_1B(PTR)
-#define VARATT_IS_EXTENDED(PTR)				(!VARATT_IS_4B_U(PTR))
-
-#define SET_VARSIZE(PTR, len)				SET_VARSIZE_4B(PTR, len)
-#define SET_VARSIZE_SHORT(PTR, len)			SET_VARSIZE_1B(PTR, len)
-#define SET_VARSIZE_COMPRESSED(PTR, len)	SET_VARSIZE_4B_C(PTR, len)
-
-#define SET_VARTAG_EXTERNAL(PTR, tag)		SET_VARTAG_1B_E(PTR, tag)
-
-#define VARSIZE_ANY(PTR) \
-	(VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR) : \
-	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR) : \
-	  VARSIZE_4B(PTR)))
+
+static inline char *
+VARDATA(const void *PTR)
+{
+	return VARDATA_4B(PTR);
+}
+
+static inline size_t
+VARSIZE(const void *PTR)
+{
+	return VARSIZE_4B(PTR);
+}
+
+static inline size_t
+VARSIZE_SHORT(const void *PTR)
+{
+	return VARSIZE_1B(PTR);
+}
+
+static inline char *
+VARDATA_SHORT(const void *PTR)
+{
+	return VARDATA_1B(PTR);
+}
+
+static inline vartag_external
+VARTAG_EXTERNAL(const void *PTR)
+{
+	return VARTAG_1B_E(PTR);
+}
+
+static inline size_t
+VARSIZE_EXTERNAL(const void *PTR)
+{
+	return VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR));
+}
+
+static inline char *
+VARDATA_EXTERNAL(const void *PTR)
+{
+	return VARDATA_1B_E(PTR);
+}
+
+static inline bool
+VARATT_IS_COMPRESSED(const void *PTR)
+{
+	return VARATT_IS_4B_C(PTR);
+}
+
+static inline bool
+VARATT_IS_EXTERNAL(const void *PTR)
+{
+	return VARATT_IS_1B_E(PTR);
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_ONDISK(const void *PTR)
+{
+	return (VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK);
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_INDIRECT(const void *PTR)
+{
+	return (VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_INDIRECT);
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED_RO(const void *PTR)
+{
+	return (VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RO);
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED_RW(const void *PTR)
+{
+	return (VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RW);
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED(const void *PTR)
+{
+	return (VARATT_IS_EXTERNAL(PTR) && VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)));
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_NON_EXPANDED(const void *PTR)
+{
+	return (VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)));
+}
+
+static inline bool
+VARATT_IS_SHORT(const void *PTR)
+{
+	return VARATT_IS_1B(PTR);
+}
+
+static inline bool
+VARATT_IS_EXTENDED(const void *PTR)
+{
+	return !VARATT_IS_4B_U(PTR);
+}
+
+#define VARATT_SHORT_MAX		0x7F
+
+static inline bool
+VARATT_CAN_MAKE_SHORT(const void *PTR)
+{
+	return (VARATT_IS_4B_U(PTR) &&
+			(VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT) <= VARATT_SHORT_MAX);
+}
+
+static inline size_t
+VARATT_CONVERTED_SHORT_SIZE(const void *PTR)
+{
+	return VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT;
+}
+
+static inline void
+SET_VARSIZE(void *PTR, size_t len)
+{
+	SET_VARSIZE_4B(PTR, len);
+}
+
+static inline void
+SET_VARSIZE_SHORT(void *PTR, size_t len)
+{
+	SET_VARSIZE_1B(PTR, len);
+}
+
+static inline void
+SET_VARSIZE_COMPRESSED(void *PTR, size_t len)
+{
+	SET_VARSIZE_4B_C(PTR, len);
+}
+
+static inline void
+SET_VARTAG_EXTERNAL(void *PTR, vartag_external tag)
+{
+	SET_VARTAG_1B_E(PTR, tag);
+}
+
+static inline size_t
+VARSIZE_ANY(const void *PTR)
+{
+	if (VARATT_IS_1B_E(PTR))
+		return VARSIZE_EXTERNAL(PTR);
+	else if (VARATT_IS_1B(PTR))
+		return VARSIZE_1B(PTR);
+	else
+		return VARSIZE_4B(PTR);
+}
 
 /* Size of a varlena data, excluding header */
-#define VARSIZE_ANY_EXHDR(PTR) \
-	(VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR)-VARHDRSZ_EXTERNAL : \
-	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR)-VARHDRSZ_SHORT : \
-	  VARSIZE_4B(PTR)-VARHDRSZ))
+static inline size_t
+VARSIZE_ANY_EXHDR(const void *PTR)
+{
+	if (VARATT_IS_1B_E(PTR))
+		return VARSIZE_EXTERNAL(PTR) - VARHDRSZ_EXTERNAL;
+	else if (VARATT_IS_1B(PTR))
+		return VARSIZE_1B(PTR) - VARHDRSZ_SHORT;
+	else
+		return VARSIZE_4B(PTR) - VARHDRSZ;
+}
 
 /* caution: this will not work on an external or compressed-in-line Datum */
 /* caution: this will return a possibly unaligned pointer */
-#define VARDATA_ANY(PTR) \
-	 (VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR))
-
-/* Decompressed size and compression method of a compressed-in-line Datum */
-#define VARDATA_COMPRESSED_GET_EXTSIZE(PTR) \
-	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK)
-#define VARDATA_COMPRESSED_GET_COMPRESS_METHOD(PTR) \
-	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS)
+static inline char *
+VARDATA_ANY(const void *PTR)
+{
+	return (VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR));
+}
+
+/* Decompressed size of a compressed-in-line Datum */
+static inline size_t
+VARDATA_COMPRESSED_GET_EXTSIZE(const void *PTR)
+{
+	return (((const varattrib_4b *) PTR)->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK);
+}
+
+/* Compression method of a compressed-in-line Datum */
+static inline unsigned int
+VARDATA_COMPRESSED_GET_COMPRESS_METHOD(const void *PTR)
+{
+	return (((const varattrib_4b *) PTR)->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS);
+}
 
 /* Same for external Datums; but note argument is a struct varatt_external */
 #define VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) \
-- 
2.50.1

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#1)
1 attachment(s)
Re: Convert varatt.h macros to static inline functions

Peter Eisentraut <peter@eisentraut.org> writes:

I had this lying around as a draft patch, as part of my ongoing campaign
to convert many complicated macros to static inline functions. Since
the topic was mentioned in another thread [0], I cleaned up the patch so
that we can all look at it.

I had just finished doing exactly that, per my idea in the other
thread of providing both pointer and Datum variants where needed.
I'll look more closely at yours in a bit, but here's mine.

regards, tom lane

Attachments:

wip-make-varatt-macros-inline-functions.patchtext/x-diff; charset=us-ascii; name=wip-make-varatt-macros-inline-functions.patchDownload
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index 0949c88983a..9b671a3ef6a 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -2893,15 +2893,15 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup)
 		 * index without further processing, so an external varlena header
 		 * should never be encountered here
 		 */
-		if (VARATT_IS_EXTERNAL(DatumGetPointer(normalized[i])))
+		if (VARATT_IS_EXTERNAL_D(normalized[i]))
 			ereport(ERROR,
 					(errcode(ERRCODE_INDEX_CORRUPTED),
 					 errmsg("external varlena datum in tuple that references heap row (%u,%u) in index \"%s\"",
 							ItemPointerGetBlockNumber(&(itup->t_tid)),
 							ItemPointerGetOffsetNumber(&(itup->t_tid)),
 							RelationGetRelationName(state->rel))));
-		else if (!VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])) &&
-				 VARSIZE(DatumGetPointer(normalized[i])) > TOAST_INDEX_TARGET &&
+		else if (!VARATT_IS_COMPRESSED_D(normalized[i]) &&
+				 VARSIZE_D(normalized[i]) > TOAST_INDEX_TARGET &&
 				 (att->attstorage == TYPSTORAGE_EXTENDED ||
 				  att->attstorage == TYPSTORAGE_MAIN))
 		{
@@ -2912,7 +2912,7 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup)
 			 */
 			formnewtup = true;
 		}
-		else if (VARATT_IS_COMPRESSED(DatumGetPointer(normalized[i])))
+		else if (VARATT_IS_COMPRESSED_D(normalized[i]))
 		{
 			formnewtup = true;
 			normalized[i] = PointerGetDatum(PG_DETOAST_DATUM(normalized[i]));
@@ -2930,7 +2930,7 @@ bt_normalize_tuple(BtreeCheckState *state, IndexTuple itup)
 			char	   *data = palloc(len);
 
 			SET_VARSIZE_SHORT(data, len);
-			memcpy(data + 1, VARDATA(DatumGetPointer(normalized[i])), len - 1);
+			memcpy(data + 1, VARDATA_D(normalized[i]), len - 1);
 
 			formnewtup = true;
 			normalized[i] = PointerGetDatum(data);
diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c
index 766c00bb6a7..0ead2aaa272 100644
--- a/contrib/hstore/hstore_gin.c
+++ b/contrib/hstore/hstore_gin.c
@@ -127,7 +127,9 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
 			/* Nulls in the array are ignored, cf hstoreArrayToPairs */
 			if (key_nulls[i])
 				continue;
-			item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
+			item = makeitem(VARDATA_D(key_datums[i]),
+							VARSIZE_D(key_datums[i]) - VARHDRSZ,
+							KEYFLAG);
 			entries[j++] = PointerGetDatum(item);
 		}
 
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index a3b08af3850..8d1b94e5238 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -576,7 +576,8 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 			if (key_nulls[i])
 				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
+			crc = crc32_sz(VARDATA_D(key_datums[i]),
+						   VARSIZE_D(key_datums[i]) - VARHDRSZ);
 			if (!(GETBIT(sign, HASHVAL(crc, siglen))))
 				res = false;
 		}
@@ -599,7 +600,8 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 			if (key_nulls[i])
 				continue;
-			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
+			crc = crc32_sz(VARDATA_D(key_datums[i]),
+						   VARSIZE_D(key_datums[i]) - VARHDRSZ);
 			if (GETBIT(sign, HASHVAL(crc, siglen)))
 				res = true;
 		}
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 4f867e4bd1f..ad384ccbe1f 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -684,22 +684,22 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 
 		if (!value_nulls || value_nulls[i])
 		{
-			pairs[i].key = VARDATA(key_datums[i]);
+			pairs[i].key = VARDATA_D(key_datums[i]);
 			pairs[i].val = NULL;
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE_D(key_datums[i]) - VARHDRSZ);
 			pairs[i].vallen = 4;
 			pairs[i].isnull = true;
 			pairs[i].needfree = false;
 		}
 		else
 		{
-			pairs[i].key = VARDATA(key_datums[i]);
-			pairs[i].val = VARDATA(value_datums[i]);
+			pairs[i].key = VARDATA_D(key_datums[i]);
+			pairs[i].val = VARDATA_D(value_datums[i]);
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE_D(key_datums[i]) - VARHDRSZ);
 			pairs[i].vallen =
-				hstoreCheckValLen(VARSIZE(value_datums[i]) - VARHDRSZ);
+				hstoreCheckValLen(VARSIZE_D(value_datums[i]) - VARHDRSZ);
 			pairs[i].isnull = false;
 			pairs[i].needfree = false;
 		}
@@ -778,22 +778,22 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
 		if (in_nulls[i * 2 + 1])
 		{
-			pairs[i].key = VARDATA(in_datums[i * 2]);
+			pairs[i].key = VARDATA_D(in_datums[i * 2]);
 			pairs[i].val = NULL;
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE_D(in_datums[i * 2]) - VARHDRSZ);
 			pairs[i].vallen = 4;
 			pairs[i].isnull = true;
 			pairs[i].needfree = false;
 		}
 		else
 		{
-			pairs[i].key = VARDATA(in_datums[i * 2]);
-			pairs[i].val = VARDATA(in_datums[i * 2 + 1]);
+			pairs[i].key = VARDATA_D(in_datums[i * 2]);
+			pairs[i].val = VARDATA_D(in_datums[i * 2 + 1]);
 			pairs[i].keylen =
-				hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ);
+				hstoreCheckKeyLen(VARSIZE_D(in_datums[i * 2]) - VARHDRSZ);
 			pairs[i].vallen =
-				hstoreCheckValLen(VARSIZE(in_datums[i * 2 + 1]) - VARHDRSZ);
+				hstoreCheckValLen(VARSIZE_D(in_datums[i * 2 + 1]) - VARHDRSZ);
 			pairs[i].isnull = false;
 			pairs[i].needfree = false;
 		}
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 5e57eceffc8..90f85653fe5 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -107,8 +107,8 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
 	{
 		if (!key_nulls[i])
 		{
-			key_pairs[j].key = VARDATA(key_datums[i]);
-			key_pairs[j].keylen = VARSIZE(key_datums[i]) - VARHDRSZ;
+			key_pairs[j].key = VARDATA_D(key_datums[i]);
+			key_pairs[j].keylen = VARSIZE_D(key_datums[i]) - VARHDRSZ;
 			key_pairs[j].val = NULL;
 			key_pairs[j].vallen = 0;
 			key_pairs[j].needfree = 0;
diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c
index bb495563200..135cf351d95 100644
--- a/contrib/test_decoding/test_decoding.c
+++ b/contrib/test_decoding/test_decoding.c
@@ -581,7 +581,7 @@ tuple_to_stringinfo(StringInfo s, TupleDesc tupdesc, HeapTuple tuple, bool skip_
 		/* print data */
 		if (isnull)
 			appendStringInfoString(s, "null");
-		else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK(origval))
+		else if (typisvarlena && VARATT_IS_EXTERNAL_ONDISK_D(origval))
 			appendStringInfoString(s, "unchanged-toast-datum");
 		else if (!typisvarlena)
 			print_literal(s, typid,
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 0d1507a2a36..d92c1f92569 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -624,7 +624,7 @@ brin_range_serialize(Ranges *range)
 
 		for (i = 0; i < nvalues; i++)
 		{
-			len += VARSIZE_ANY(range->values[i]);
+			len += VARSIZE_ANY_D(range->values[i]);
 		}
 	}
 	else if (typlen == -2)		/* cstring */
@@ -687,7 +687,7 @@ brin_range_serialize(Ranges *range)
 		}
 		else if (typlen == -1)	/* varlena */
 		{
-			int			tmp = VARSIZE_ANY(DatumGetPointer(range->values[i]));
+			int			tmp = VARSIZE_ANY_D(range->values[i]);
 
 			memcpy(ptr, DatumGetPointer(range->values[i]), tmp);
 			ptr += tmp;
diff --git a/src/backend/access/brin/brin_tuple.c b/src/backend/access/brin/brin_tuple.c
index 861f397e6db..5509354be48 100644
--- a/src/backend/access/brin/brin_tuple.c
+++ b/src/backend/access/brin/brin_tuple.c
@@ -205,7 +205,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 			 * even for range with non-NULL data? E.g. degenerate bloom filter
 			 * may be thrown away, etc.
 			 */
-			if (VARATT_IS_EXTERNAL(DatumGetPointer(value)))
+			if (VARATT_IS_EXTERNAL_D(value))
 			{
 				value = PointerGetDatum(detoast_external_attr((struct varlena *)
 															  DatumGetPointer(value)));
@@ -217,7 +217,7 @@ brin_form_tuple(BrinDesc *brdesc, BlockNumber blkno, BrinMemTuple *tuple,
 			 * datatype, try to compress it in-line.
 			 */
 			if (!VARATT_IS_EXTENDED(DatumGetPointer(value)) &&
-				VARSIZE(DatumGetPointer(value)) > TOAST_INDEX_TARGET &&
+				VARSIZE_D(value) > TOAST_INDEX_TARGET &&
 				(atttype->typstorage == TYPSTORAGE_EXTENDED ||
 				 atttype->typstorage == TYPSTORAGE_MAIN))
 			{
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 969d1028cae..ffb7b4d0a80 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -189,7 +189,7 @@ getmissingattr(TupleDesc tupleDesc,
 			if (att->attlen > 0)
 				key.len = att->attlen;
 			else
-				key.len = VARSIZE_ANY(attrmiss->am_value);
+				key.len = VARSIZE_ANY_D(attrmiss->am_value);
 			key.value = attrmiss->am_value;
 
 			entry = hash_search(missing_cache, &key, HASH_ENTER, &found);
@@ -245,7 +245,7 @@ heap_compute_data_size(TupleDesc tupleDesc,
 			data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
 		}
 		else if (atti->attlen == -1 &&
-				 VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
+				 VARATT_IS_EXTERNAL_EXPANDED_D(val))
 		{
 			/*
 			 * we want to flatten the expanded value so that the constructed
@@ -903,7 +903,7 @@ expand_tuple(HeapTuple *targetHeapTuple,
 
 				targetDataLen = att_addlength_pointer(targetDataLen,
 													  att->attlen,
-													  attrmiss[attnum].am_value);
+													  DatumGetPointer(attrmiss[attnum].am_value));
 			}
 			else
 			{
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index 1986b943a28..79cba541a58 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -105,7 +105,7 @@ index_form_tuple_context(TupleDesc tupleDescriptor,
 		 * If value is stored EXTERNAL, must fetch it so we are not depending
 		 * on outside storage.  This should be improved someday.
 		 */
-		if (VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (VARATT_IS_EXTERNAL_D(values[i]))
 		{
 			untoasted_values[i] =
 				PointerGetDatum(detoast_external_attr((struct varlena *)
@@ -118,7 +118,7 @@ index_form_tuple_context(TupleDesc tupleDescriptor,
 		 * try to compress it in-line.
 		 */
 		if (!VARATT_IS_EXTENDED(DatumGetPointer(untoasted_values[i])) &&
-			VARSIZE(DatumGetPointer(untoasted_values[i])) > TOAST_INDEX_TARGET &&
+			VARSIZE_D(untoasted_values[i]) > TOAST_INDEX_TARGET &&
 			(att->attstorage == TYPSTORAGE_EXTENDED ||
 			 att->attstorage == TYPSTORAGE_MAIN))
 		{
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 50747c16396..3582ccfcdc6 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1190,8 +1190,8 @@ transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
 
 		for (i = 0; i < noldoptions; i++)
 		{
-			char	   *text_str = VARDATA(oldoptions[i]);
-			int			text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
+			char	   *text_str = VARDATA_D(oldoptions[i]);
+			int			text_len = VARSIZE_D(oldoptions[i]) - VARHDRSZ;
 
 			/* Search for a match in defList */
 			foreach(cell, defList)
@@ -1456,8 +1456,8 @@ parseRelOptionsInternal(Datum options, bool validate,
 
 	for (i = 0; i < noptions; i++)
 	{
-		char	   *text_str = VARDATA(optiondatums[i]);
-		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		char	   *text_str = VARDATA_D(optiondatums[i]);
+		int			text_len = VARSIZE_D(optiondatums[i]) - VARHDRSZ;
 		int			j;
 
 		/* Search for a match in reloptions */
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 7d8be8346ce..10f1cedcd6f 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -49,10 +49,10 @@ toast_compress_datum(Datum value, char cmethod)
 	int32		valsize;
 	ToastCompressionId cmid = TOAST_INVALID_COMPRESSION_ID;
 
-	Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
-	Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
+	Assert(!VARATT_IS_EXTERNAL_D(value));
+	Assert(!VARATT_IS_COMPRESSED_D(value));
 
-	valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+	valsize = VARSIZE_ANY_EXHDR_D(value);
 
 	/* If the compression method is not valid, use the current default */
 	if (!CompressionMethodIsValid(cmethod))
@@ -144,7 +144,7 @@ toast_save_datum(Relation rel, Datum value,
 	int			num_indexes;
 	int			validIndex;
 
-	Assert(!VARATT_IS_EXTERNAL(value));
+	Assert(!VARATT_IS_EXTERNAL(dval));
 
 	/*
 	 * Open the toast relation and its indexes.  We can use the index to check
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index a65acd89104..374afe31211 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -2233,7 +2233,7 @@ _gin_build_tuple(OffsetNumber attrnum, unsigned char category,
 	else if (typlen > 0)
 		keylen = typlen;
 	else if (typlen == -1)
-		keylen = VARSIZE_ANY(key);
+		keylen = VARSIZE_ANY_D(key);
 	else if (typlen == -2)
 		keylen = strlen(DatumGetPointer(key)) + 1;
 	else
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 95fea74e296..de91a81f15b 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -785,7 +785,7 @@ SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum)
 	else if (att->attlen > 0)
 		size = att->attlen;
 	else
-		size = VARSIZE_ANY(datum);
+		size = VARSIZE_ANY_D(datum);
 
 	return MAXALIGN(size);
 }
@@ -804,7 +804,7 @@ memcpyInnerDatum(void *target, SpGistTypeDesc *att, Datum datum)
 	}
 	else
 	{
-		size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
+		size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY_D(datum);
 		memcpy(target, DatumGetPointer(datum), size);
 	}
 }
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index b60fab0a4d2..7aae799cbe5 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -197,10 +197,10 @@ toast_tuple_find_biggest_attribute(ToastTupleContext *ttc,
 
 		if ((ttc->ttc_attr[i].tai_colflags & skip_colflags) != 0)
 			continue;
-		if (VARATT_IS_EXTERNAL(DatumGetPointer(ttc->ttc_values[i])))
+		if (VARATT_IS_EXTERNAL_D(ttc->ttc_values[i]))
 			continue;			/* can't happen, toast_action would be PLAIN */
 		if (for_compression &&
-			VARATT_IS_COMPRESSED(DatumGetPointer(ttc->ttc_values[i])))
+			VARATT_IS_COMPRESSED_D(ttc->ttc_values[i]))
 			continue;
 		if (check_main && att->attstorage != TYPSTORAGE_MAIN)
 			continue;
@@ -239,7 +239,7 @@ toast_tuple_try_compression(ToastTupleContext *ttc, int attribute)
 			pfree(DatumGetPointer(*value));
 		*value = new_value;
 		attr->tai_colflags |= TOASTCOL_NEEDS_FREE;
-		attr->tai_size = VARSIZE(DatumGetPointer(*value));
+		attr->tai_size = VARSIZE_D(*value);
 		ttc->ttc_flags |= (TOAST_NEEDS_CHANGE | TOAST_NEEDS_FREE);
 	}
 	else
@@ -330,7 +330,7 @@ toast_delete_external(Relation rel, const Datum *values, const bool *isnull,
 
 			if (isnull[i])
 				continue;
-			else if (VARATT_IS_EXTERNAL_ONDISK(value))
+			else if (VARATT_IS_EXTERNAL_ONDISK_D(value))
 				toast_delete_datum(rel, value, is_speculative);
 		}
 	}
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 7111d5d5334..db622c62947 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -2005,7 +2005,7 @@ compute_trivial_stats(VacAttrStatsP stats,
 		 */
 		if (is_varlena)
 		{
-			total_width += VARSIZE_ANY(DatumGetPointer(value));
+			total_width += VARSIZE_ANY_D(value);
 		}
 		else if (is_varwidth)
 		{
@@ -2121,7 +2121,7 @@ compute_distinct_stats(VacAttrStatsP stats,
 		 */
 		if (is_varlena)
 		{
-			total_width += VARSIZE_ANY(DatumGetPointer(value));
+			total_width += VARSIZE_ANY_D(value);
 
 			/*
 			 * If the value is toasted, we want to detoast it just once to
@@ -2468,7 +2468,7 @@ compute_scalar_stats(VacAttrStatsP stats,
 		 */
 		if (is_varlena)
 		{
-			total_width += VARSIZE_ANY(DatumGetPointer(value));
+			total_width += VARSIZE_ANY_D(value);
 
 			/*
 			 * If the value is toasted, we want to detoast it just once to
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1a37737d4a2..b919d95cf82 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -3744,7 +3744,7 @@ ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 	tupDatum = *op->resvalue;
 
 	/* We can special-case expanded records for speed */
-	if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(tupDatum)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_D(tupDatum))
 	{
 		ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(tupDatum);
 
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 8e02d68824f..f55c1e183e2 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -196,7 +196,7 @@ tts_virtual_materialize(TupleTableSlot *slot)
 		val = slot->tts_values[natt];
 
 		if (att->attlen == -1 &&
-			VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
+			VARATT_IS_EXTERNAL_EXPANDED_D(val))
 		{
 			/*
 			 * We want to flatten the expanded value so that the materialized
@@ -232,7 +232,7 @@ tts_virtual_materialize(TupleTableSlot *slot)
 		val = slot->tts_values[natt];
 
 		if (att->attlen == -1 &&
-			VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(val)))
+			VARATT_IS_EXTERNAL_EXPANDED_D(val))
 		{
 			Size		data_length;
 
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index 562de676457..5acc8a157ac 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -158,7 +158,7 @@ tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
 
 		if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
 		{
-			if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
+			if (VARATT_IS_EXTERNAL_D(val))
 			{
 				val = PointerGetDatum(detoast_external_attr((struct varlena *)
 															DatumGetPointer(val)));
diff --git a/src/backend/replication/logical/proto.c b/src/backend/replication/logical/proto.c
index 1a352b542dc..fdfe1f143a5 100644
--- a/src/backend/replication/logical/proto.c
+++ b/src/backend/replication/logical/proto.c
@@ -809,7 +809,7 @@ logicalrep_write_tuple(StringInfo out, Relation rel, TupleTableSlot *slot,
 			continue;
 		}
 
-		if (att->attlen == -1 && VARATT_IS_EXTERNAL_ONDISK(values[i]))
+		if (att->attlen == -1 && VARATT_IS_EXTERNAL_ONDISK_D(values[i]))
 		{
 			/*
 			 * Unchanged toasted datum.  (Note that we don't promise to detect
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index f4c977262c5..7ede271c03e 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -1374,8 +1374,8 @@ pgoutput_row_filter(Relation relation, TupleTableSlot *old_slot,
 		 * VARTAG_INDIRECT. See ReorderBufferToastReplace.
 		 */
 		if (att->attlen == -1 &&
-			VARATT_IS_EXTERNAL_ONDISK(new_slot->tts_values[i]) &&
-			!VARATT_IS_EXTERNAL_ONDISK(old_slot->tts_values[i]))
+			VARATT_IS_EXTERNAL_ONDISK_D(new_slot->tts_values[i]) &&
+			!VARATT_IS_EXTERNAL_ONDISK_D(old_slot->tts_values[i]))
 		{
 			if (!tmp_new_slot)
 			{
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index d98cda698d9..283e25d77ab 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -767,7 +767,7 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats)
 				values[dim][i] = PointerGetDatum(PG_DETOAST_DATUM(values[dim][i]));
 
 				/* serialized length (uint32 length + data) */
-				len = VARSIZE_ANY_EXHDR(values[dim][i]);
+				len = VARSIZE_ANY_EXHDR_D(values[dim][i]);
 				info[dim].nbytes += sizeof(uint32); /* length */
 				info[dim].nbytes += len;	/* value (no header) */
 
@@ -895,14 +895,14 @@ statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats)
 			}
 			else if (info[dim].typlen == -1)	/* varlena */
 			{
-				uint32		len = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+				uint32		len = VARSIZE_ANY_EXHDR_D(value);
 
 				/* copy the length */
 				memcpy(ptr, &len, sizeof(uint32));
 				ptr += sizeof(uint32);
 
 				/* data from the varlena value (without the header) */
-				memcpy(ptr, VARDATA_ANY(DatumGetPointer(value)), len);
+				memcpy(ptr, VARDATA_ANY_D(value), len);
 				ptr += len;
 			}
 			else if (info[dim].typlen == -2)	/* cstring */
diff --git a/src/backend/tsearch/ts_selfuncs.c b/src/backend/tsearch/ts_selfuncs.c
index 0c1d2bc1109..57534d41ab5 100644
--- a/src/backend/tsearch/ts_selfuncs.c
+++ b/src/backend/tsearch/ts_selfuncs.c
@@ -233,7 +233,8 @@ mcelem_tsquery_selec(TSQuery query, Datum *mcelem, int nmcelem,
 		 * The text Datums came from an array, so it cannot be compressed or
 		 * stored out-of-line -- it's safe to use VARSIZE_ANY*.
 		 */
-		Assert(!VARATT_IS_COMPRESSED(mcelem[i]) && !VARATT_IS_EXTERNAL(mcelem[i]));
+		Assert(!VARATT_IS_COMPRESSED_D(mcelem[i]) &&
+			   !VARATT_IS_EXTERNAL_D(mcelem[i]));
 		lookup[i].element = (text *) DatumGetPointer(mcelem[i]);
 		lookup[i].frequency = numbers[i];
 	}
diff --git a/src/backend/tsearch/ts_typanalyze.c b/src/backend/tsearch/ts_typanalyze.c
index c5a71331ce8..9346d0a2d69 100644
--- a/src/backend/tsearch/ts_typanalyze.c
+++ b/src/backend/tsearch/ts_typanalyze.c
@@ -223,7 +223,7 @@ compute_tsvector_stats(VacAttrStats *stats,
 		 * compute_minimal_stats function, we use the toasted width for this
 		 * calculation.
 		 */
-		total_width += VARSIZE_ANY(DatumGetPointer(value));
+		total_width += VARSIZE_ANY_D(value);
 
 		/*
 		 * Now detoast the tsvector if needed.
diff --git a/src/backend/utils/adt/array_expanded.c b/src/backend/utils/adt/array_expanded.c
index fc036d1eb30..11ab7da3da1 100644
--- a/src/backend/utils/adt/array_expanded.c
+++ b/src/backend/utils/adt/array_expanded.c
@@ -73,7 +73,7 @@ expand_array(Datum arraydatum, MemoryContext parentcontext,
 	eah->ea_magic = EA_MAGIC;
 
 	/* If the source is an expanded array, we may be able to optimize */
-	if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_D(arraydatum))
 	{
 		ExpandedArrayHeader *oldeah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
 
@@ -352,7 +352,7 @@ ExpandedArrayHeader *
 DatumGetExpandedArray(Datum d)
 {
 	/* If it's a writable expanded array already, just return it */
-	if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_RW_D(d))
 	{
 		ExpandedArrayHeader *eah = (ExpandedArrayHeader *) DatumGetEOHP(d);
 
@@ -372,7 +372,7 @@ ExpandedArrayHeader *
 DatumGetExpandedArrayX(Datum d, ArrayMetaState *metacache)
 {
 	/* If it's a writable expanded array already, just return it */
-	if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_RW_D(d))
 	{
 		ExpandedArrayHeader *eah = (ExpandedArrayHeader *) DatumGetEOHP(d);
 
@@ -405,7 +405,7 @@ DatumGetAnyArrayP(Datum d)
 	/*
 	 * If it's an expanded array (RW or RO), return the header pointer.
 	 */
-	if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(d)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_D(d))
 	{
 		eah = (ExpandedArrayHeader *) DatumGetEOHP(d);
 		Assert(eah->ea_magic == EA_MAGIC);
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index c8f53c6fbe7..8f2aa856fd9 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -1850,7 +1850,7 @@ array_get_element(Datum arraydatum,
 		arraydataptr = (char *) DatumGetPointer(arraydatum);
 		arraynullsptr = NULL;
 	}
-	else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
+	else if (VARATT_IS_EXTERNAL_EXPANDED_D(arraydatum))
 	{
 		/* expanded array: let's do this in a separate function */
 		return array_get_element_expanded(arraydatum,
@@ -2270,7 +2270,7 @@ array_set_element(Datum arraydatum,
 	if (elmlen == -1 && !isNull)
 		dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
 
-	if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_D(arraydatum))
 	{
 		/* expanded array: let's do this in a separate function */
 		return array_set_element_expanded(arraydatum,
diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c
index fcd5b1653dd..268a99e5a06 100644
--- a/src/backend/utils/adt/datum.c
+++ b/src/backend/utils/adt/datum.c
@@ -194,7 +194,7 @@ Datum
 datumTransfer(Datum value, bool typByVal, int typLen)
 {
 	if (!typByVal && typLen == -1 &&
-		VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(value)))
+		VARATT_IS_EXTERNAL_EXPANDED_RW_D(value))
 		value = TransferExpandedObject(value, CurrentMemoryContext);
 	else
 		value = datumCopy(value, typByVal, typLen);
@@ -419,7 +419,7 @@ datumEstimateSpace(Datum value, bool isnull, bool typByVal, int typLen)
 		if (typByVal)
 			sz += sizeof(Datum);
 		else if (typLen == -1 &&
-				 VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
+				 VARATT_IS_EXTERNAL_EXPANDED_D(value))
 		{
 			/* Expanded objects need to be flattened, see comment below */
 			sz += EOH_get_flat_size(DatumGetEOHP(value));
@@ -468,7 +468,7 @@ datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
 	else if (typByVal)
 		header = -1;
 	else if (typLen == -1 &&
-			 VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
+			 VARATT_IS_EXTERNAL_EXPANDED_D(value))
 	{
 		eoh = DatumGetEOHP(value);
 		header = EOH_get_flat_size(eoh);
diff --git a/src/backend/utils/adt/expandeddatum.c b/src/backend/utils/adt/expandeddatum.c
index 6b4b8eaf005..882101caf5e 100644
--- a/src/backend/utils/adt/expandeddatum.c
+++ b/src/backend/utils/adt/expandeddatum.c
@@ -97,7 +97,7 @@ MakeExpandedObjectReadOnlyInternal(Datum d)
 	ExpandedObjectHeader *eohptr;
 
 	/* Nothing to do if not a read-write expanded-object pointer */
-	if (!VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
+	if (!VARATT_IS_EXTERNAL_EXPANDED_RW_D(d))
 		return d;
 
 	/* Now safe to extract the object pointer */
@@ -120,7 +120,7 @@ TransferExpandedObject(Datum d, MemoryContext new_parent)
 	ExpandedObjectHeader *eohptr = DatumGetEOHP(d);
 
 	/* Assert caller gave a R/W pointer */
-	Assert(VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)));
+	Assert(VARATT_IS_EXTERNAL_EXPANDED_RW_D(d));
 
 	/* Transfer ownership */
 	MemoryContextSetParent(eohptr->eoh_context, new_parent);
@@ -138,7 +138,7 @@ DeleteExpandedObject(Datum d)
 	ExpandedObjectHeader *eohptr = DatumGetEOHP(d);
 
 	/* Assert caller gave a R/W pointer */
-	Assert(VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)));
+	Assert(VARATT_IS_EXTERNAL_EXPANDED_RW_D(d));
 
 	/* Kill it */
 	MemoryContextDelete(eohptr->eoh_context);
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index 13752db44e8..a7a883c7d23 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -703,7 +703,7 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 
 			if (!erh->dnulls[i] &&
 				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
+				VARATT_IS_EXTERNAL_D(erh->dvalues[i]))
 			{
 				/*
 				 * expanded_record_set_field_internal can do the actual work
@@ -927,7 +927,7 @@ ExpandedRecordHeader *
 DatumGetExpandedRecord(Datum d)
 {
 	/* If it's a writable expanded record already, just return it */
-	if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_RW_D(d))
 	{
 		ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(d);
 
@@ -1155,7 +1155,7 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				VARATT_IS_EXTERNAL_D(newValue))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
@@ -1185,7 +1185,7 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		 * by itself have made the value non-external.)
 		 */
 		if (attr->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+			VARATT_IS_EXTERNAL_D(newValue))
 			erh->flags |= ER_FLAG_HAVE_EXTERNAL;
 	}
 
@@ -1300,7 +1300,7 @@ expanded_record_set_fields(ExpandedRecordHeader *erh,
 			{
 				/* Is it an external toasted value? */
 				if (attr->attlen == -1 &&
-					VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+					VARATT_IS_EXTERNAL_D(newValue))
 				{
 					if (expand_external)
 					{
@@ -1312,7 +1312,7 @@ expanded_record_set_fields(ExpandedRecordHeader *erh,
 						/* Just copy the value */
 						newValue = datumCopy(newValue, false, -1);
 						/* If it's still external, remember that */
-						if (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+						if (VARATT_IS_EXTERNAL_D(newValue))
 							erh->flags |= ER_FLAG_HAVE_EXTERNAL;
 					}
 				}
@@ -1544,7 +1544,7 @@ check_domain_for_new_field(ExpandedRecordHeader *erh, int fnumber,
 		CompactAttribute *attr = TupleDescCompactAttr(erh->er_tupdesc, fnumber - 1);
 
 		if (!attr->attbyval && attr->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+			VARATT_IS_EXTERNAL_D(newValue))
 			dummy_erh->flags |= ER_FLAG_HAVE_EXTERNAL;
 	}
 
diff --git a/src/backend/utils/adt/jsonb_gin.c b/src/backend/utils/adt/jsonb_gin.c
index c1950792b5a..08888f8d919 100644
--- a/src/backend/utils/adt/jsonb_gin.c
+++ b/src/backend/utils/adt/jsonb_gin.c
@@ -896,8 +896,8 @@ gin_extract_jsonb_query(PG_FUNCTION_ARGS)
 				continue;
 			/* We rely on the array elements not being toasted */
 			entries[j++] = make_text_key(JGINFLAG_KEY,
-										 VARDATA_ANY(key_datums[i]),
-										 VARSIZE_ANY_EXHDR(key_datums[i]));
+										 VARDATA_ANY_D(key_datums[i]),
+										 VARSIZE_ANY_EXHDR_D(key_datums[i]));
 		}
 
 		*nentries = j;
diff --git a/src/backend/utils/adt/jsonb_op.c b/src/backend/utils/adt/jsonb_op.c
index fa5603f26e1..6a50c76bd0e 100644
--- a/src/backend/utils/adt/jsonb_op.c
+++ b/src/backend/utils/adt/jsonb_op.c
@@ -63,8 +63,8 @@ jsonb_exists_any(PG_FUNCTION_ARGS)
 
 		strVal.type = jbvString;
 		/* We rely on the array elements not being toasted */
-		strVal.val.string.val = VARDATA_ANY(key_datums[i]);
-		strVal.val.string.len = VARSIZE_ANY_EXHDR(key_datums[i]);
+		strVal.val.string.val = VARDATA_ANY_D(key_datums[i]);
+		strVal.val.string.len = VARSIZE_ANY_EXHDR_D(key_datums[i]);
 
 		if (findJsonbValueFromContainer(&jb->root,
 										JB_FOBJECT | JB_FARRAY,
@@ -96,8 +96,8 @@ jsonb_exists_all(PG_FUNCTION_ARGS)
 
 		strVal.type = jbvString;
 		/* We rely on the array elements not being toasted */
-		strVal.val.string.val = VARDATA_ANY(key_datums[i]);
-		strVal.val.string.len = VARSIZE_ANY_EXHDR(key_datums[i]);
+		strVal.val.string.val = VARDATA_ANY_D(key_datums[i]);
+		strVal.val.string.len = VARSIZE_ANY_EXHDR_D(key_datums[i]);
 
 		if (findJsonbValueFromContainer(&jb->root,
 										JB_FOBJECT | JB_FARRAY,
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index bcb1720b6cd..2de62891139 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -4766,8 +4766,8 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
 					continue;
 
 				/* We rely on the array elements not being toasted */
-				keyptr = VARDATA_ANY(keys_elems[i]);
-				keylen = VARSIZE_ANY_EXHDR(keys_elems[i]);
+				keyptr = VARDATA_ANY_D(keys_elems[i]);
+				keylen = VARSIZE_ANY_EXHDR_D(keys_elems[i]);
 				if (keylen == v.val.string.len &&
 					memcmp(keyptr, v.val.string.val, keylen) == 0)
 				{
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index dbab24737ef..bcc2fbf6f01 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -3074,8 +3074,8 @@ JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
 		case TEXTOID:
 		case VARCHAROID:
 			res->type = jbvString;
-			res->val.string.val = VARDATA_ANY(val);
-			res->val.string.len = VARSIZE_ANY_EXHDR(val);
+			res->val.string.val = VARDATA_ANY_D(val);
+			res->val.string.len = VARSIZE_ANY_EXHDR_D(val);
 			break;
 		case DATEOID:
 		case TIMEOID:
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index cd84ced5b48..a92aa77e43b 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -398,8 +398,8 @@ multirange_send(PG_FUNCTION_ARGS)
 		range = RangeTypePGetDatum(ranges[i]);
 		range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
 
-		pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
-		pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+		pq_sendint32(buf, VARSIZE_D(range) - VARHDRSZ);
+		pq_sendbytes(buf, VARDATA_D(range), VARSIZE_D(range) - VARHDRSZ);
 	}
 
 	PG_RETURN_BYTEA_P(pq_endtypsend(buf));
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 66cc0acf4a7..d1d22807644 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -287,8 +287,8 @@ range_send(PG_FUNCTION_ARGS)
 	{
 		Datum		bound = PointerGetDatum(SendFunctionCall(&cache->typioproc,
 															 lower.val));
-		uint32		bound_len = VARSIZE(bound) - VARHDRSZ;
-		char	   *bound_data = VARDATA(bound);
+		uint32		bound_len = VARSIZE_D(bound) - VARHDRSZ;
+		char	   *bound_data = VARDATA_D(bound);
 
 		pq_sendint32(buf, bound_len);
 		pq_sendbytes(buf, bound_data, bound_len);
@@ -298,8 +298,8 @@ range_send(PG_FUNCTION_ARGS)
 	{
 		Datum		bound = PointerGetDatum(SendFunctionCall(&cache->typioproc,
 															 upper.val));
-		uint32		bound_len = VARSIZE(bound) - VARHDRSZ;
-		char	   *bound_data = VARDATA(bound);
+		uint32		bound_len = VARSIZE_D(bound) - VARHDRSZ;
+		char	   *bound_data = VARDATA_D(bound);
 
 		pq_sendint32(buf, bound_len);
 		pq_sendbytes(buf, bound_data, bound_len);
diff --git a/src/backend/utils/adt/rangetypes_typanalyze.c b/src/backend/utils/adt/rangetypes_typanalyze.c
index a18196d8a34..266ab9d7a29 100644
--- a/src/backend/utils/adt/rangetypes_typanalyze.c
+++ b/src/backend/utils/adt/rangetypes_typanalyze.c
@@ -181,7 +181,7 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
 		 * XXX: should we ignore wide values, like std_typanalyze does, to
 		 * avoid bloating the statistics table?
 		 */
-		total_width += VARSIZE_ANY(DatumGetPointer(value));
+		total_width += VARSIZE_ANY_D(value);
 
 		/* Get range and deserialize it for further analysis. */
 		if (mltrng_typcache != NULL)
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 1fa1275ca63..5b1f08c349d 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -329,8 +329,8 @@ tsvector_setweight_by_filter(PG_FUNCTION_ARGS)
 		if (nulls[i])
 			continue;
 
-		lex = VARDATA(dlexemes[i]);
-		lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		lex = VARDATA_D(dlexemes[i]);
+		lex_len = VARSIZE_D(dlexemes[i]) - VARHDRSZ;
 		lex_pos = tsvector_bsearch(tsout, lex, lex_len);
 
 		if (lex_pos >= 0 && (j = POSDATALEN(tsout, entry + lex_pos)) != 0)
@@ -443,10 +443,10 @@ compare_text_lexemes(const void *va, const void *vb)
 {
 	Datum		a = *((const Datum *) va);
 	Datum		b = *((const Datum *) vb);
-	char	   *alex = VARDATA_ANY(a);
-	int			alex_len = VARSIZE_ANY_EXHDR(a);
-	char	   *blex = VARDATA_ANY(b);
-	int			blex_len = VARSIZE_ANY_EXHDR(b);
+	char	   *alex = VARDATA_ANY_D(a);
+	int			alex_len = VARSIZE_ANY_EXHDR_D(a);
+	char	   *blex = VARDATA_ANY_D(b);
+	int			blex_len = VARSIZE_ANY_EXHDR_D(b);
 
 	return tsCompareString(alex, alex_len, blex, blex_len, false);
 }
@@ -605,8 +605,8 @@ tsvector_delete_arr(PG_FUNCTION_ARGS)
 		if (nulls[i])
 			continue;
 
-		lex = VARDATA(dlexemes[i]);
-		lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		lex = VARDATA_D(dlexemes[i]);
+		lex_len = VARSIZE_D(dlexemes[i]) - VARHDRSZ;
 		lex_pos = tsvector_bsearch(tsin, lex, lex_len);
 
 		if (lex_pos >= 0)
@@ -770,7 +770,7 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 					 errmsg("lexeme array may not contain nulls")));
 
-		if (VARSIZE(dlexemes[i]) - VARHDRSZ == 0)
+		if (VARSIZE_D(dlexemes[i]) - VARHDRSZ == 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_ZERO_LENGTH_CHARACTER_STRING),
 					 errmsg("lexeme array may not contain empty strings")));
@@ -786,7 +786,7 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 
 	/* Calculate space needed for surviving lexemes. */
 	for (i = 0; i < nitems; i++)
-		datalen += VARSIZE(dlexemes[i]) - VARHDRSZ;
+		datalen += VARSIZE_D(dlexemes[i]) - VARHDRSZ;
 	tslen = CALCDATASIZE(nitems, datalen);
 
 	/* Allocate and fill tsvector. */
@@ -798,8 +798,8 @@ array_to_tsvector(PG_FUNCTION_ARGS)
 	cur = STRPTR(tsout);
 	for (i = 0; i < nitems; i++)
 	{
-		char	   *lex = VARDATA(dlexemes[i]);
-		int			lex_len = VARSIZE(dlexemes[i]) - VARHDRSZ;
+		char	   *lex = VARDATA_D(dlexemes[i]);
+		int			lex_len = VARSIZE_D(dlexemes[i]) - VARHDRSZ;
 
 		memcpy(cur, lex, lex_len);
 		arrout[i].haspos = 0;
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index ffae8c23abf..b30a30a3900 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -707,8 +707,8 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
 		 * If we're working with an untoasted source, no need to do an extra
 		 * copying step.
 		 */
-		if (VARATT_IS_COMPRESSED(DatumGetPointer(str)) ||
-			VARATT_IS_EXTERNAL(DatumGetPointer(str)))
+		if (VARATT_IS_COMPRESSED_D(str) ||
+			VARATT_IS_EXTERNAL_D(str))
 			slice = DatumGetTextPSlice(str, slice_start, slice_size);
 		else
 			slice = (text *) DatumGetPointer(str);
diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h
index cde9a0c073b..62ea104d30e 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -150,7 +150,7 @@ EOHPGetRODatum(const struct ExpandedObjectHeader *eohptr)
 /* Does the Datum represent a writable expanded object? */
 #define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
 	(((isnull) || (typlen) != -1) ? false : \
-	 VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
+	 VARATT_IS_EXTERNAL_EXPANDED_RW_D(d))
 
 #define MakeExpandedObjectReadOnly(d, isnull, typlen) \
 	(((isnull) || (typlen) != -1) ? (d) : \
diff --git a/src/include/varatt.h b/src/include/varatt.h
index 2e8564d4998..b37cc04e58e 100644
--- a/src/include/varatt.h
+++ b/src/include/varatt.h
@@ -166,7 +166,9 @@ typedef struct
 
 /*
  * Endian-dependent macros.  These are considered internal --- use the
- * external macros below instead of using these directly.
+ * external functions below instead of using these directly.  All of these
+ * expect an argument that is a pointer, not a Datum.  Some of them have
+ * multiple-evaluation hazards, too.
  *
  * Note: IS_1B is true for external toast records but VARSIZE_1B will return 0
  * for such records. Hence you should usually check for IS_EXTERNAL before
@@ -247,19 +249,24 @@ typedef struct
 #define VARDATA_1B_E(PTR)	(((varattrib_1b_e *) (PTR))->va_data)
 
 /*
- * Externally visible TOAST macros begin here.
+ * Externally visible TOAST functions and macros begin here.  All of these
+ * were originally macros, accounting for the upper-case naming.
+ *
+ * The basic functions such as VARDATA() accept a pointer to a value of a
+ * toastable data type; the caller's variable might be declared "text *"
+ * or the like, so we use "void *" here.
+ *
+ * There are also functions named like VARDATA_D() that accept a Datum.
+ * It used to be okay to call the basic functions directly on a Datum,
+ * but no longer.  In the interests of compilation speed, there are _D()
+ * functions only for the commonly-used cases.  Apply DatumGetPointer()
+ * yourself if you need to use one of the other basic functions on a Datum.
  */
 
 #define VARHDRSZ_EXTERNAL		offsetof(varattrib_1b_e, va_data)
 #define VARHDRSZ_COMPRESSED		offsetof(varattrib_4b, va_compressed.va_data)
 #define VARHDRSZ_SHORT			offsetof(varattrib_1b, va_data)
-
 #define VARATT_SHORT_MAX		0x7F
-#define VARATT_CAN_MAKE_SHORT(PTR) \
-	(VARATT_IS_4B_U(PTR) && \
-	 (VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT) <= VARATT_SHORT_MAX)
-#define VARATT_CONVERTED_SHORT_SIZE(PTR) \
-	(VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT)
 
 /*
  * In consumers oblivious to data alignment, call PG_DETOAST_DATUM_PACKED(),
@@ -275,67 +282,285 @@ typedef struct
  * Other macros here should usually be used only by tuple assembly/disassembly
  * code and code that specifically wants to work with still-toasted Datums.
  */
-#define VARDATA(PTR)						VARDATA_4B(PTR)
-#define VARSIZE(PTR)						VARSIZE_4B(PTR)
-
-#define VARSIZE_SHORT(PTR)					VARSIZE_1B(PTR)
-#define VARDATA_SHORT(PTR)					VARDATA_1B(PTR)
-
-#define VARTAG_EXTERNAL(PTR)				VARTAG_1B_E(PTR)
-#define VARSIZE_EXTERNAL(PTR)				(VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR)))
-#define VARDATA_EXTERNAL(PTR)				VARDATA_1B_E(PTR)
-
-#define VARATT_IS_COMPRESSED(PTR)			VARATT_IS_4B_C(PTR)
-#define VARATT_IS_EXTERNAL(PTR)				VARATT_IS_1B_E(PTR)
-#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
-#define VARATT_IS_EXTERNAL_INDIRECT(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_INDIRECT)
-#define VARATT_IS_EXTERNAL_EXPANDED_RO(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RO)
-#define VARATT_IS_EXTERNAL_EXPANDED_RW(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RW)
-#define VARATT_IS_EXTERNAL_EXPANDED(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)))
-#define VARATT_IS_EXTERNAL_NON_EXPANDED(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)))
-#define VARATT_IS_SHORT(PTR)				VARATT_IS_1B(PTR)
-#define VARATT_IS_EXTENDED(PTR)				(!VARATT_IS_4B_U(PTR))
-
-#define SET_VARSIZE(PTR, len)				SET_VARSIZE_4B(PTR, len)
-#define SET_VARSIZE_SHORT(PTR, len)			SET_VARSIZE_1B(PTR, len)
-#define SET_VARSIZE_COMPRESSED(PTR, len)	SET_VARSIZE_4B_C(PTR, len)
-
-#define SET_VARTAG_EXTERNAL(PTR, tag)		SET_VARTAG_1B_E(PTR, tag)
-
-#define VARSIZE_ANY(PTR) \
-	(VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR) : \
-	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR) : \
-	  VARSIZE_4B(PTR)))
-
-/* Size of a varlena data, excluding header */
-#define VARSIZE_ANY_EXHDR(PTR) \
-	(VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR)-VARHDRSZ_EXTERNAL : \
-	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR)-VARHDRSZ_SHORT : \
-	  VARSIZE_4B(PTR)-VARHDRSZ))
 
+/* Size of a known-not-toasted varlena datum, including header */
+static inline Size
+VARSIZE(const void *PTR)
+{
+	return VARSIZE_4B(PTR);
+}
+
+static inline Size
+VARSIZE_D(Datum DAT)
+{
+	return VARSIZE(DatumGetPointer(DAT));
+}
+
+/* Start of data area of a known-not-toasted varlena datum */
+static inline char *
+VARDATA(const void *PTR)
+{
+	return VARDATA_4B(PTR);
+}
+
+static inline char *
+VARDATA_D(Datum DAT)
+{
+	return VARDATA(DatumGetPointer(DAT));
+}
+
+/* Size of a known-short-header varlena datum, including header */
+static inline Size
+VARSIZE_SHORT(const void *PTR)
+{
+	return VARSIZE_1B(PTR);
+}
+
+/* Start of data area of a known-short-header varlena datum */
+static inline char *
+VARDATA_SHORT(const void *PTR)
+{
+	return VARDATA_1B(PTR);
+}
+
+/* Type tag of a "TOAST pointer" datum */
+static inline uint8
+VARTAG_EXTERNAL(const void *PTR)
+{
+	return VARTAG_1B_E(PTR);
+}
+
+/* Size of a "TOAST pointer" datum, including header */
+static inline Size
+VARSIZE_EXTERNAL(const void *PTR)
+{
+	return VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR));
+}
+
+/* Start of data area of a "TOAST pointer" datum */
+static inline char *
+VARDATA_EXTERNAL(const void *PTR)
+{
+	return VARDATA_1B_E(PTR);
+}
+
+/* Is varlena datum in inline-compressed format? */
+static inline bool
+VARATT_IS_COMPRESSED(const void *PTR)
+{
+	return VARATT_IS_4B_C(PTR);
+}
+
+static inline bool
+VARATT_IS_COMPRESSED_D(Datum DAT)
+{
+	return VARATT_IS_COMPRESSED(DatumGetPointer(DAT));
+}
+
+/* Is varlena datum a "TOAST pointer" datum? */
+static inline bool
+VARATT_IS_EXTERNAL(const void *PTR)
+{
+	return VARATT_IS_1B_E(PTR);
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_D(Datum DAT)
+{
+	return VARATT_IS_EXTERNAL(DatumGetPointer(DAT));
+}
+
+/* Is varlena datum a pointer to on-disk toasted data? */
+static inline bool
+VARATT_IS_EXTERNAL_ONDISK(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK;
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_ONDISK_D(Datum DAT)
+{
+	return VARATT_IS_EXTERNAL_ONDISK(DatumGetPointer(DAT));
+}
+
+/* Is varlena datum an indirect pointer? */
+static inline bool
+VARATT_IS_EXTERNAL_INDIRECT(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_INDIRECT;
+}
+
+/* Is varlena datum a read-only pointer to an expanded object? */
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED_RO(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RO;
+}
+
+/* Is varlena datum a read-write pointer to an expanded object? */
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED_RW(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RW;
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED_RW_D(Datum DAT)
+{
+	return VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(DAT));
+}
+
+/* Is varlena datum either type of pointer to an expanded object? */
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR));
+}
+
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED_D(Datum DAT)
+{
+	return VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(DAT));
+}
+
+/* Is varlena datum a "TOAST pointer", but not for an expanded object? */
+static inline bool
+VARATT_IS_EXTERNAL_NON_EXPANDED(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR));
+}
+
+/* Is varlena datum a short-header datum? */
+static inline bool
+VARATT_IS_SHORT(const void *PTR)
+{
+	return VARATT_IS_1B(PTR);
+}
+
+/* Is varlena datum not in traditional (4-byte-header, uncompressed) format? */
+static inline bool
+VARATT_IS_EXTENDED(const void *PTR)
+{
+	return !VARATT_IS_4B_U(PTR);
+}
+
+/* Is varlena datum short enough to convert to short-header format? */
+static inline bool
+VARATT_CAN_MAKE_SHORT(const void *PTR)
+{
+	return VARATT_IS_4B_U(PTR) &&
+		(VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT) <= VARATT_SHORT_MAX;
+}
+
+/* Size that datum will have in short-header format, including header */
+static inline Size
+VARATT_CONVERTED_SHORT_SIZE(const void *PTR)
+{
+	return VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT;
+}
+
+/* Set the size (including header) of a 4-byte-header varlena datum */
+static inline void
+SET_VARSIZE(void *PTR, Size len)
+{
+	SET_VARSIZE_4B(PTR, len);
+}
+
+/* Set the size (including header) of a short-header varlena datum */
+static inline void
+SET_VARSIZE_SHORT(void *PTR, Size len)
+{
+	SET_VARSIZE_1B(PTR, len);
+}
+
+/* Set the size (including header) of an inline-compressed varlena datum */
+static inline void
+SET_VARSIZE_COMPRESSED(void *PTR, Size len)
+{
+	SET_VARSIZE_4B_C(PTR, len);
+}
+
+/* Set the type tag of a "TOAST pointer" datum */
+static inline void
+SET_VARTAG_EXTERNAL(void *PTR, uint8 tag)
+{
+	SET_VARTAG_1B_E(PTR, tag);
+}
+
+/* Size of a varlena datum of any format, including header */
+static inline Size
+VARSIZE_ANY(const void *PTR)
+{
+	return VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR) :
+		(VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR) :
+		 VARSIZE_4B(PTR));
+}
+
+static inline Size
+VARSIZE_ANY_D(Datum DAT)
+{
+	return VARSIZE_ANY(DatumGetPointer(DAT));
+}
+
+/* Size of a varlena datum of any format, excluding header */
+static inline Size
+VARSIZE_ANY_EXHDR(const void *PTR)
+{
+	return VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR) - VARHDRSZ_EXTERNAL :
+		(VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR) - VARHDRSZ_SHORT :
+		 VARSIZE_4B(PTR) - VARHDRSZ);
+}
+
+static inline Size
+VARSIZE_ANY_EXHDR_D(Datum DAT)
+{
+	return VARSIZE_ANY_EXHDR(DatumGetPointer(DAT));
+}
+
+/* Start of data area of a plain or short-header varlena datum */
 /* caution: this will not work on an external or compressed-in-line Datum */
 /* caution: this will return a possibly unaligned pointer */
-#define VARDATA_ANY(PTR) \
-	 (VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR))
+static inline char *
+VARDATA_ANY(const void *PTR)
+{
+	return VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR);
+}
+
+static inline char *
+VARDATA_ANY_D(Datum DAT)
+{
+	return VARDATA_ANY(DatumGetPointer(DAT));
+}
+
+/* Decompressed size of a compressed-in-line varlena datum */
+static inline Size
+VARDATA_COMPRESSED_GET_EXTSIZE(const void *PTR)
+{
+	return ((varattrib_4b *) PTR)->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK;
+}
 
-/* Decompressed size and compression method of a compressed-in-line Datum */
-#define VARDATA_COMPRESSED_GET_EXTSIZE(PTR) \
-	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK)
-#define VARDATA_COMPRESSED_GET_COMPRESS_METHOD(PTR) \
-	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS)
+/* Compression method of a compressed-in-line varlena datum */
+static inline uint32
+VARDATA_COMPRESSED_GET_COMPRESS_METHOD(const void *PTR)
+{
+	return ((varattrib_4b *) PTR)->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS;
+}
 
 /* Same for external Datums; but note argument is a struct varatt_external */
-#define VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) \
-	((toast_pointer).va_extinfo & VARLENA_EXTSIZE_MASK)
-#define VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer) \
-	((toast_pointer).va_extinfo >> VARLENA_EXTSIZE_BITS)
+static inline Size
+VARATT_EXTERNAL_GET_EXTSIZE(struct varatt_external toast_pointer)
+{
+	return toast_pointer.va_extinfo & VARLENA_EXTSIZE_MASK;
+}
 
+static inline uint32
+VARATT_EXTERNAL_GET_COMPRESS_METHOD(struct varatt_external toast_pointer)
+{
+	return toast_pointer.va_extinfo >> VARLENA_EXTSIZE_BITS;
+}
+
+/* Set size and compress method of an externally-stored varlena datum */
+/* This has to remain a macro; beware multiple evaluations! */
 #define VARATT_EXTERNAL_SET_SIZE_AND_COMPRESS_METHOD(toast_pointer, len, cm) \
 	do { \
 		Assert((cm) == TOAST_PGLZ_COMPRESSION_ID || \
@@ -351,8 +576,11 @@ typedef struct
  * VARHDRSZ overhead, the former doesn't.  We never use compression unless it
  * actually saves space, so we expect either equality or less-than.
  */
-#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) \
-	(VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) < \
-	 (toast_pointer).va_rawsize - VARHDRSZ)
+static inline bool
+VARATT_EXTERNAL_IS_COMPRESSED(struct varatt_external toast_pointer)
+{
+	return VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) <
+		toast_pointer.va_rawsize - VARHDRSZ;
+}
 
 #endif
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index d19425b7a71..487bb73408b 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -560,7 +560,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
 					 */
 					if (!var->isnull && var->datatype->typlen == -1)
 					{
-						if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(var->value)))
+						if (VARATT_IS_EXTERNAL_EXPANDED_RW_D(var->value))
 						{
 							/* take ownership of R/W object */
 							assign_simple_var(&estate, var,
@@ -831,7 +831,7 @@ coerce_function_result_tuple(PLpgSQL_execstate *estate, TupleDesc tupdesc)
 	Assert(type_is_rowtype(estate->rettype));
 
 	/* We can special-case expanded records for speed */
-	if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(estate->retval)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_D(estate->retval))
 	{
 		ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(estate->retval);
 
@@ -1087,7 +1087,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
 		Assert(type_is_rowtype(estate.rettype));
 
 		/* We can special-case expanded records for speed */
-		if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(estate.retval)))
+		if (VARATT_IS_EXTERNAL_EXPANDED_D(estate.retval))
 		{
 			ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(estate.retval);
 
@@ -5120,7 +5120,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
 				if (!var->datatype->typbyval && !isNull)
 				{
 					if (var->datatype->typisarray &&
-						!VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(newvalue)))
+						!VARATT_IS_EXTERNAL_EXPANDED_RW_D(newvalue))
 					{
 						/* array and not already R/W, so apply expand_array */
 						newvalue = expand_array(newvalue,
@@ -6552,7 +6552,7 @@ plpgsql_param_eval_var_check(ExprState *state, ExprEvalStep *op,
 	 * decide whether/how to optimize the assignment.
 	 */
 	if (!var->isnull &&
-		VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(var->value)))
+		VARATT_IS_EXTERNAL_EXPANDED_RW_D(var->value))
 	{
 		PLpgSQL_expr *expr = (PLpgSQL_expr *) op->d.cparam.paramarg;
 		Param	   *param = (Param *) op->d.cparam.paramarg2;
@@ -6652,7 +6652,7 @@ plpgsql_param_eval_var_transfer(ExprState *state, ExprEvalStep *op,
 	 * from the expression.
 	 */
 	if (!var->isnull &&
-		VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(var->value)))
+		VARATT_IS_EXTERNAL_EXPANDED_RW_D(var->value))
 	{
 		*op->resvalue = TransferExpandedObject(var->value,
 											   get_eval_mcontext(estate));
@@ -7593,7 +7593,7 @@ exec_move_row_from_datum(PLpgSQL_execstate *estate,
 						 Datum value)
 {
 	/* Check to see if source is an expanded record */
-	if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
+	if (VARATT_IS_EXTERNAL_EXPANDED_D(value))
 	{
 		ExpandedRecordHeader *erh = (ExpandedRecordHeader *) DatumGetEOHP(value);
 		ExpandedRecordHeader *newerh = NULL;
@@ -7628,7 +7628,7 @@ exec_move_row_from_datum(PLpgSQL_execstate *estate,
 			 * value.  We'll treat it as the base composite type instead;
 			 * compare logic in make_expanded_record_for_rec.)
 			 */
-			if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(value)) &&
+			if (VARATT_IS_EXTERNAL_EXPANDED_RW_D(value) &&
 				(rec->rectypeid == erh->er_decltypeid ||
 				 (rec->rectypeid == RECORDOID &&
 				  !ExpandedRecordIsDomain(erh))))
#3Greg Burd
greg@burd.me
In reply to: Tom Lane (#2)
Re: Convert varatt.h macros to static inline functions

On Jul 31, 2025, at 10:06 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Peter Eisentraut <peter@eisentraut.org> writes:

I had this lying around as a draft patch, as part of my ongoing campaign 
to convert many complicated macros to static inline functions.  Since 
the topic was mentioned in another thread [0], I cleaned up the patch so 
that we can all look at it.

I had just finished doing exactly that, per my idea in the other
thread of providing both pointer and Datum variants where needed.
I'll look more closely at yours in a bit, but here's mine.

regards, tom lane

Thank you both for diving into this, I really appreciate the clarity
this brings to the code.  I've applied both patches and compared the two
approaches.  I build and tested Peter's patches on macOS (15.5, M3 Pro)
using meson and found no issues.  It's clear you both had the same idea
in mind, but with slight differences in application.

I find VARDATA(DatumGetPointer(key_datums[I])) to be more clear
than VARDATA_D(key_datums[I]).  Yes, the line length suffers a bit but I
find it much more readable.

I'm +1 for changes like this, and this one in particular.

Just my $0.02, best.

-greg

#4Michael Paquier
michael@paquier.xyz
In reply to: Tom Lane (#2)
Re: Convert varatt.h macros to static inline functions

On Thu, Jul 31, 2025 at 10:06:02AM -0400, Tom Lane wrote:

Peter Eisentraut <peter@eisentraut.org> writes:

I had this lying around as a draft patch, as part of my ongoing campaign
to convert many complicated macros to static inline functions. Since
the topic was mentioned in another thread [0], I cleaned up the patch so
that we can all look at it.

I had just finished doing exactly that, per my idea in the other
thread of providing both pointer and Datum variants where needed.
I'll look more closely at yours in a bit, but here's mine.

I'm finding that your wip version is bit more complete as you have
introduced inline functions for these ones:
- VARATT_EXTERNAL_GET_EXTSIZE()
- VARATT_EXTERNAL_GET_COMPRESS_METHOD().

Another comment that can apply to all the patches presented on this
thread. Could it be worth splitting these inline functions into a
separate header that declares varatt.h, meaning that we'd need to
think a bit more about the structures themselves and all the
sub-macros like SET_VARSIZE_1B() & friends? There is a bit of chaos
that has accumulated in varatt.h over the ages, still there is a
hierarchy with the most "internal" structures and what gets used by
the code C code. So we could use this occasion to split things if
that brings some clarity..
--
Michael

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Michael Paquier (#4)
Re: Convert varatt.h macros to static inline functions

Michael Paquier <michael@paquier.xyz> writes:

Another comment that can apply to all the patches presented on this
thread. Could it be worth splitting these inline functions into a
separate header that declares varatt.h, meaning that we'd need to
think a bit more about the structures themselves and all the
sub-macros like SET_VARSIZE_1B() & friends?

IIRC we already moved these macros out of postgres.h, some years ago.

I don't really see how we could hide the lower-level macros from
callers' eyes while still having them available to inline functions.
The point of this exercise IMO is not information hiding: it is to
be clearer about the input and output datatypes of the functions,
and to eliminate multiple-evaluation hazards.

regards, tom lane

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#5)
1 attachment(s)
Re: Convert varatt.h macros to static inline functions

It looks like the majority vote is still in favor of writing out
DatumGetPointer instead of using "_D()" functions, so let's roll
with that approach.

I looked through our two versions of the varatt.h changes and
merged them. The attached is only cosmetically different from
yours, I think --- mostly, I kept the comments I'd written.

I've tested this atop 0001-0005 from [1]/messages/by-id/8246d7ff-f4b7-4363-913e-827dadfeb145@eisentraut.org, and it all seems good.
I'd like to move along with getting these changes committed, and
then I'll take another look at the 8-byte-datums-everywhere proposal.

regards, tom lane

[1]: /messages/by-id/8246d7ff-f4b7-4363-913e-827dadfeb145@eisentraut.org

Attachments:

v2-0001-Convert-varatt.h-access-macros-to-static-inline-f.patchtext/x-diff; charset=us-ascii; name*0=v2-0001-Convert-varatt.h-access-macros-to-static-inline-f.p; name*1=atchDownload
From 15e51f71635f98cf5703eedac1e95369bdcd3fab Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 3 Aug 2025 14:52:55 -0400
Subject: [PATCH v2] Convert varatt.h access macros to static inline functions.

We've only bothered converting the external interfaces, not the
endian-dependent internal macros (which should not be used by any
callers other than the interface functions in this header, anyway).

The VARTAG_1B_E() changes are required for C++ compatibility.

Author: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/928ea48f-77c6-417b-897c-621ef16685a6@eisentraut.org
---
 doc/src/sgml/xfunc.sgml |   2 +-
 src/include/varatt.h    | 336 +++++++++++++++++++++++++++++++---------
 2 files changed, 261 insertions(+), 77 deletions(-)

diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 2d81afce8cb..30219f432d9 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2165,7 +2165,7 @@ memcpy(destination->data, buffer, 40);
      it's considered good style to use the macro <literal>VARHDRSZ</literal>
      to refer to the size of the overhead for a variable-length type.
      Also, the length field <emphasis>must</emphasis> be set using the
-     <literal>SET_VARSIZE</literal> macro, not by simple assignment.
+     <literal>SET_VARSIZE</literal> function, not by simple assignment.
     </para>
 
     <para>
diff --git a/src/include/varatt.h b/src/include/varatt.h
index 2e8564d4998..aeeabf9145b 100644
--- a/src/include/varatt.h
+++ b/src/include/varatt.h
@@ -89,20 +89,35 @@ typedef enum vartag_external
 	VARTAG_ONDISK = 18
 } vartag_external;
 
+/* Is a TOAST pointer either type of expanded-object pointer? */
 /* this test relies on the specific tag values above */
-#define VARTAG_IS_EXPANDED(tag) \
-	(((tag) & ~1) == VARTAG_EXPANDED_RO)
+static inline bool
+VARTAG_IS_EXPANDED(vartag_external tag)
+{
+	return ((tag & ~1) == VARTAG_EXPANDED_RO);
+}
 
-#define VARTAG_SIZE(tag) \
-	((tag) == VARTAG_INDIRECT ? sizeof(varatt_indirect) : \
-	 VARTAG_IS_EXPANDED(tag) ? sizeof(varatt_expanded) : \
-	 (tag) == VARTAG_ONDISK ? sizeof(varatt_external) : \
-	 (AssertMacro(false), 0))
+/* Size of the data part of a "TOAST pointer" datum */
+static inline Size
+VARTAG_SIZE(vartag_external tag)
+{
+	if (tag == VARTAG_INDIRECT)
+		return sizeof(varatt_indirect);
+	else if (VARTAG_IS_EXPANDED(tag))
+		return sizeof(varatt_expanded);
+	else if (tag == VARTAG_ONDISK)
+		return sizeof(varatt_external);
+	else
+	{
+		Assert(false);
+		return 0;
+	}
+}
 
 /*
  * These structs describe the header of a varlena object that may have been
  * TOASTed.  Generally, don't reference these structs directly, but use the
- * macros below.
+ * functions and macros below.
  *
  * We use separate structs for the aligned and unaligned cases because the
  * compiler might otherwise think it could generate code that assumes
@@ -166,7 +181,9 @@ typedef struct
 
 /*
  * Endian-dependent macros.  These are considered internal --- use the
- * external macros below instead of using these directly.
+ * external functions below instead of using these directly.  All of these
+ * expect an argument that is a pointer, not a Datum.  Some of them have
+ * multiple-evaluation hazards, too.
  *
  * Note: IS_1B is true for external toast records but VARSIZE_1B will return 0
  * for such records. Hence you should usually check for IS_EXTERNAL before
@@ -194,7 +211,7 @@ typedef struct
 #define VARSIZE_1B(PTR) \
 	(((varattrib_1b *) (PTR))->va_header & 0x7F)
 #define VARTAG_1B_E(PTR) \
-	(((varattrib_1b_e *) (PTR))->va_tag)
+	((vartag_external) ((varattrib_1b_e *) (PTR))->va_tag)
 
 #define SET_VARSIZE_4B(PTR,len) \
 	(((varattrib_4b *) (PTR))->va_4byte.va_header = (len) & 0x3FFFFFFF)
@@ -227,7 +244,7 @@ typedef struct
 #define VARSIZE_1B(PTR) \
 	((((varattrib_1b *) (PTR))->va_header >> 1) & 0x7F)
 #define VARTAG_1B_E(PTR) \
-	(((varattrib_1b_e *) (PTR))->va_tag)
+	((vartag_external) ((varattrib_1b_e *) (PTR))->va_tag)
 
 #define SET_VARSIZE_4B(PTR,len) \
 	(((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2))
@@ -247,19 +264,19 @@ typedef struct
 #define VARDATA_1B_E(PTR)	(((varattrib_1b_e *) (PTR))->va_data)
 
 /*
- * Externally visible TOAST macros begin here.
+ * Externally visible TOAST functions and macros begin here.  All of these
+ * were originally macros, accounting for the upper-case naming.
+ *
+ * Most of these functions accept a pointer to a value of a toastable data
+ * type.  The caller's variable might be declared "text *" or the like,
+ * so we use "void *" here.  Callers that are working with a Datum variable
+ * must apply DatumGetPointer before calling these functions.
  */
 
 #define VARHDRSZ_EXTERNAL		offsetof(varattrib_1b_e, va_data)
 #define VARHDRSZ_COMPRESSED		offsetof(varattrib_4b, va_compressed.va_data)
 #define VARHDRSZ_SHORT			offsetof(varattrib_1b, va_data)
-
 #define VARATT_SHORT_MAX		0x7F
-#define VARATT_CAN_MAKE_SHORT(PTR) \
-	(VARATT_IS_4B_U(PTR) && \
-	 (VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT) <= VARATT_SHORT_MAX)
-#define VARATT_CONVERTED_SHORT_SIZE(PTR) \
-	(VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT)
 
 /*
  * In consumers oblivious to data alignment, call PG_DETOAST_DATUM_PACKED(),
@@ -272,70 +289,234 @@ typedef struct
  * Code assembling a new datum should call VARDATA() and SET_VARSIZE().
  * (Datums begin life untoasted.)
  *
- * Other macros here should usually be used only by tuple assembly/disassembly
+ * Other functions here should usually be used only by tuple assembly/disassembly
  * code and code that specifically wants to work with still-toasted Datums.
  */
-#define VARDATA(PTR)						VARDATA_4B(PTR)
-#define VARSIZE(PTR)						VARSIZE_4B(PTR)
-
-#define VARSIZE_SHORT(PTR)					VARSIZE_1B(PTR)
-#define VARDATA_SHORT(PTR)					VARDATA_1B(PTR)
-
-#define VARTAG_EXTERNAL(PTR)				VARTAG_1B_E(PTR)
-#define VARSIZE_EXTERNAL(PTR)				(VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR)))
-#define VARDATA_EXTERNAL(PTR)				VARDATA_1B_E(PTR)
-
-#define VARATT_IS_COMPRESSED(PTR)			VARATT_IS_4B_C(PTR)
-#define VARATT_IS_EXTERNAL(PTR)				VARATT_IS_1B_E(PTR)
-#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
-#define VARATT_IS_EXTERNAL_INDIRECT(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_INDIRECT)
-#define VARATT_IS_EXTERNAL_EXPANDED_RO(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RO)
-#define VARATT_IS_EXTERNAL_EXPANDED_RW(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RW)
-#define VARATT_IS_EXTERNAL_EXPANDED(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)))
-#define VARATT_IS_EXTERNAL_NON_EXPANDED(PTR) \
-	(VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)))
-#define VARATT_IS_SHORT(PTR)				VARATT_IS_1B(PTR)
-#define VARATT_IS_EXTENDED(PTR)				(!VARATT_IS_4B_U(PTR))
-
-#define SET_VARSIZE(PTR, len)				SET_VARSIZE_4B(PTR, len)
-#define SET_VARSIZE_SHORT(PTR, len)			SET_VARSIZE_1B(PTR, len)
-#define SET_VARSIZE_COMPRESSED(PTR, len)	SET_VARSIZE_4B_C(PTR, len)
-
-#define SET_VARTAG_EXTERNAL(PTR, tag)		SET_VARTAG_1B_E(PTR, tag)
-
-#define VARSIZE_ANY(PTR) \
-	(VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR) : \
-	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR) : \
-	  VARSIZE_4B(PTR)))
-
-/* Size of a varlena data, excluding header */
-#define VARSIZE_ANY_EXHDR(PTR) \
-	(VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR)-VARHDRSZ_EXTERNAL : \
-	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR)-VARHDRSZ_SHORT : \
-	  VARSIZE_4B(PTR)-VARHDRSZ))
 
+/* Size of a known-not-toasted varlena datum, including header */
+static inline Size
+VARSIZE(const void *PTR)
+{
+	return VARSIZE_4B(PTR);
+}
+
+/* Start of data area of a known-not-toasted varlena datum */
+static inline char *
+VARDATA(const void *PTR)
+{
+	return VARDATA_4B(PTR);
+}
+
+/* Size of a known-short-header varlena datum, including header */
+static inline Size
+VARSIZE_SHORT(const void *PTR)
+{
+	return VARSIZE_1B(PTR);
+}
+
+/* Start of data area of a known-short-header varlena datum */
+static inline char *
+VARDATA_SHORT(const void *PTR)
+{
+	return VARDATA_1B(PTR);
+}
+
+/* Type tag of a "TOAST pointer" datum */
+static inline vartag_external
+VARTAG_EXTERNAL(const void *PTR)
+{
+	return VARTAG_1B_E(PTR);
+}
+
+/* Size of a "TOAST pointer" datum, including header */
+static inline Size
+VARSIZE_EXTERNAL(const void *PTR)
+{
+	return VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR));
+}
+
+/* Start of data area of a "TOAST pointer" datum */
+static inline char *
+VARDATA_EXTERNAL(const void *PTR)
+{
+	return VARDATA_1B_E(PTR);
+}
+
+/* Is varlena datum in inline-compressed format? */
+static inline bool
+VARATT_IS_COMPRESSED(const void *PTR)
+{
+	return VARATT_IS_4B_C(PTR);
+}
+
+/* Is varlena datum a "TOAST pointer" datum? */
+static inline bool
+VARATT_IS_EXTERNAL(const void *PTR)
+{
+	return VARATT_IS_1B_E(PTR);
+}
+
+/* Is varlena datum a pointer to on-disk toasted data? */
+static inline bool
+VARATT_IS_EXTERNAL_ONDISK(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK;
+}
+
+/* Is varlena datum an indirect pointer? */
+static inline bool
+VARATT_IS_EXTERNAL_INDIRECT(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_INDIRECT;
+}
+
+/* Is varlena datum a read-only pointer to an expanded object? */
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED_RO(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RO;
+}
+
+/* Is varlena datum a read-write pointer to an expanded object? */
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED_RW(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RW;
+}
+
+/* Is varlena datum either type of pointer to an expanded object? */
+static inline bool
+VARATT_IS_EXTERNAL_EXPANDED(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR));
+}
+
+/* Is varlena datum a "TOAST pointer", but not for an expanded object? */
+static inline bool
+VARATT_IS_EXTERNAL_NON_EXPANDED(const void *PTR)
+{
+	return VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR));
+}
+
+/* Is varlena datum a short-header datum? */
+static inline bool
+VARATT_IS_SHORT(const void *PTR)
+{
+	return VARATT_IS_1B(PTR);
+}
+
+/* Is varlena datum not in traditional (4-byte-header, uncompressed) format? */
+static inline bool
+VARATT_IS_EXTENDED(const void *PTR)
+{
+	return !VARATT_IS_4B_U(PTR);
+}
+
+/* Is varlena datum short enough to convert to short-header format? */
+static inline bool
+VARATT_CAN_MAKE_SHORT(const void *PTR)
+{
+	return VARATT_IS_4B_U(PTR) &&
+		(VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT) <= VARATT_SHORT_MAX;
+}
+
+/* Size that datum will have in short-header format, including header */
+static inline Size
+VARATT_CONVERTED_SHORT_SIZE(const void *PTR)
+{
+	return VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT;
+}
+
+/* Set the size (including header) of a 4-byte-header varlena datum */
+static inline void
+SET_VARSIZE(void *PTR, Size len)
+{
+	SET_VARSIZE_4B(PTR, len);
+}
+
+/* Set the size (including header) of a short-header varlena datum */
+static inline void
+SET_VARSIZE_SHORT(void *PTR, Size len)
+{
+	SET_VARSIZE_1B(PTR, len);
+}
+
+/* Set the size (including header) of an inline-compressed varlena datum */
+static inline void
+SET_VARSIZE_COMPRESSED(void *PTR, Size len)
+{
+	SET_VARSIZE_4B_C(PTR, len);
+}
+
+/* Set the type tag of a "TOAST pointer" datum */
+static inline void
+SET_VARTAG_EXTERNAL(void *PTR, vartag_external tag)
+{
+	SET_VARTAG_1B_E(PTR, tag);
+}
+
+/* Size of a varlena datum of any format, including header */
+static inline Size
+VARSIZE_ANY(const void *PTR)
+{
+	if (VARATT_IS_1B_E(PTR))
+		return VARSIZE_EXTERNAL(PTR);
+	else if (VARATT_IS_1B(PTR))
+		return VARSIZE_1B(PTR);
+	else
+		return VARSIZE_4B(PTR);
+}
+
+/* Size of a varlena datum of any format, excluding header */
+static inline Size
+VARSIZE_ANY_EXHDR(const void *PTR)
+{
+	if (VARATT_IS_1B_E(PTR))
+		return VARSIZE_EXTERNAL(PTR) - VARHDRSZ_EXTERNAL;
+	else if (VARATT_IS_1B(PTR))
+		return VARSIZE_1B(PTR) - VARHDRSZ_SHORT;
+	else
+		return VARSIZE_4B(PTR) - VARHDRSZ;
+}
+
+/* Start of data area of a plain or short-header varlena datum */
 /* caution: this will not work on an external or compressed-in-line Datum */
 /* caution: this will return a possibly unaligned pointer */
-#define VARDATA_ANY(PTR) \
-	 (VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR))
+static inline char *
+VARDATA_ANY(const void *PTR)
+{
+	return VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR);
+}
 
-/* Decompressed size and compression method of a compressed-in-line Datum */
-#define VARDATA_COMPRESSED_GET_EXTSIZE(PTR) \
-	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK)
-#define VARDATA_COMPRESSED_GET_COMPRESS_METHOD(PTR) \
-	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS)
+/* Decompressed size of a compressed-in-line varlena datum */
+static inline Size
+VARDATA_COMPRESSED_GET_EXTSIZE(const void *PTR)
+{
+	return ((varattrib_4b *) PTR)->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK;
+}
+
+/* Compression method of a compressed-in-line varlena datum */
+static inline uint32
+VARDATA_COMPRESSED_GET_COMPRESS_METHOD(const void *PTR)
+{
+	return ((varattrib_4b *) PTR)->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS;
+}
 
 /* Same for external Datums; but note argument is a struct varatt_external */
-#define VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) \
-	((toast_pointer).va_extinfo & VARLENA_EXTSIZE_MASK)
-#define VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer) \
-	((toast_pointer).va_extinfo >> VARLENA_EXTSIZE_BITS)
+static inline Size
+VARATT_EXTERNAL_GET_EXTSIZE(struct varatt_external toast_pointer)
+{
+	return toast_pointer.va_extinfo & VARLENA_EXTSIZE_MASK;
+}
 
+static inline uint32
+VARATT_EXTERNAL_GET_COMPRESS_METHOD(struct varatt_external toast_pointer)
+{
+	return toast_pointer.va_extinfo >> VARLENA_EXTSIZE_BITS;
+}
+
+/* Set size and compress method of an externally-stored varlena datum */
+/* This has to remain a macro; beware multiple evaluations! */
 #define VARATT_EXTERNAL_SET_SIZE_AND_COMPRESS_METHOD(toast_pointer, len, cm) \
 	do { \
 		Assert((cm) == TOAST_PGLZ_COMPRESSION_ID || \
@@ -351,8 +532,11 @@ typedef struct
  * VARHDRSZ overhead, the former doesn't.  We never use compression unless it
  * actually saves space, so we expect either equality or less-than.
  */
-#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) \
-	(VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) < \
-	 (toast_pointer).va_rawsize - VARHDRSZ)
+static inline bool
+VARATT_EXTERNAL_IS_COMPRESSED(struct varatt_external toast_pointer)
+{
+	return VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) <
+		(Size) (toast_pointer.va_rawsize - VARHDRSZ);
+}
 
 #endif
-- 
2.43.7

#7Peter Eisentraut
peter@eisentraut.org
In reply to: Tom Lane (#6)
Re: Convert varatt.h macros to static inline functions

On 03.08.25 22:20, Tom Lane wrote:

It looks like the majority vote is still in favor of writing out
DatumGetPointer instead of using "_D()" functions, so let's roll
with that approach.

I looked through our two versions of the varatt.h changes and
merged them. The attached is only cosmetically different from
yours, I think --- mostly, I kept the comments I'd written.

I've tested this atop 0001-0005 from [1], and it all seems good.
I'd like to move along with getting these changes committed, and
then I'll take another look at the 8-byte-datums-everywhere proposal.

I committed this with the required prerequisite patches. That concludes
this thread, I think. I'll follow up on the remaining work in the
"Datum as struct" thread, and the work in the "8 byte Datums" thread can
also continue.

#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#7)
Re: Convert varatt.h macros to static inline functions

Peter Eisentraut <peter@eisentraut.org> writes:

I committed this with the required prerequisite patches. That concludes
this thread, I think. I'll follow up on the remaining work in the
"Datum as struct" thread, and the work in the "8 byte Datums" thread can
also continue.

Thanks! The "8 byte Datums" work is still blocked on the remaining
two preliminary patches from "Datum as struct", though.

regards, tom lane

#9Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Peter Eisentraut (#7)
1 attachment(s)
Re: Convert varatt.h macros to static inline functions

Hi,

On Tue, Aug 5, 2025 at 9:42 AM Peter Eisentraut <peter@eisentraut.org> wrote:

On 03.08.25 22:20, Tom Lane wrote:

It looks like the majority vote is still in favor of writing out
DatumGetPointer instead of using "_D()" functions, so let's roll
with that approach.

I looked through our two versions of the varatt.h changes and
merged them. The attached is only cosmetically different from
yours, I think --- mostly, I kept the comments I'd written.

I've tested this atop 0001-0005 from [1], and it all seems good.
I'd like to move along with getting these changes committed, and
then I'll take another look at the 8-byte-datums-everywhere proposal.

I committed this with the required prerequisite patches. That concludes
this thread, I think. I'll follow up on the remaining work in the
"Datum as struct" thread, and the work in the "8 byte Datums" thread can
also continue.

I got the following compiler warning:

% make -C src/backend/storage/large_object
inv_api.c: In function ‘inv_write’:
inv_api.c:565:29: warning: ‘workbuf’ may be used uninitialized
[-Wmaybe-uninitialized]
565 | char *workb = VARDATA(&workbuf.hdr);
| ^~~~~~~~~~~~~~~~~~~~~
In file included from ../../../../src/include/access/htup_details.h:22,
from ../../../../src/include/nodes/tidbitmap.h:25,
from ../../../../src/include/access/genam.h:20,
from inv_api.c:36:
../../../../src/include/varatt.h:305:1: note: by argument 1 of type
‘const void *’ to ‘VARDATA’ declared here
305 | VARDATA(const void *PTR)
| ^~~~~~~
inv_api.c:564:33: note: ‘workbuf’ declared here
564 | } workbuf;
| ^~~~~~~
inv_api.c: In function ‘inv_truncate’:
inv_api.c:756:29: warning: ‘workbuf’ may be used uninitialized
[-Wmaybe-uninitialized]
756 | char *workb = VARDATA(&workbuf.hdr);
| ^~~~~~~~~~~~~~~~~~~~~
../../../../src/include/varatt.h:305:1: note: by argument 1 of type
‘const void *’ to ‘VARDATA’ declared here
305 | VARDATA(const void *PTR)
| ^~~~~~~
inv_api.c:755:33: note: ‘workbuf’ declared here
755 | } workbuf;
| ^~~~~~~

I've not fully investigated the root cause but commit e035863c9a0
presumably is the culprit. FYI I'm using gcc 14.2.1.

The attached patch fixes the warning.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

Attachments:

fix_compiler_warning.patchapplication/x-patch; name=fix_compiler_warning.patchDownload
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 68b76f2cc18..a874000c8ca 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -561,7 +561,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 		char		data[LOBLKSIZE + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
-	}			workbuf;
+	}			workbuf = {0};
 	char	   *workb = VARDATA(&workbuf.hdr);
 	HeapTuple	newtup;
 	Datum		values[Natts_pg_largeobject];
@@ -752,7 +752,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 		char		data[LOBLKSIZE + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
-	}			workbuf;
+	}			workbuf = {0};
 	char	   *workb = VARDATA(&workbuf.hdr);
 	HeapTuple	newtup;
 	Datum		values[Natts_pg_largeobject];
#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Masahiko Sawada (#9)
Re: Convert varatt.h macros to static inline functions

Masahiko Sawada <sawada.mshk@gmail.com> writes:

I got the following compiler warning:

% make -C src/backend/storage/large_object
inv_api.c: In function ‘inv_write’:
inv_api.c:565:29: warning: ‘workbuf’ may be used uninitialized
[-Wmaybe-uninitialized]
565 | char *workb = VARDATA(&workbuf.hdr);
| ^~~~~~~~~~~~~~~~~~~~~

I've not fully investigated the root cause but commit e035863c9a0
presumably is the culprit. FYI I'm using gcc 14.2.1.

Interesting. I did not see such warnings with gcc 14.3.1, 15.1.1,
nor older gcc versions. Must be something peculiar to 14.2.

The attached patch fixes the warning.

Theoretically this shouldn't be necessary, since VARDATA()
does not touch the contents of the pointed-to object.
Still, no objection.

regards, tom lane

#11Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Tom Lane (#10)
Re: Convert varatt.h macros to static inline functions

On Tue, Aug 5, 2025 at 11:34 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Masahiko Sawada <sawada.mshk@gmail.com> writes:

I got the following compiler warning:

% make -C src/backend/storage/large_object
inv_api.c: In function ‘inv_write’:
inv_api.c:565:29: warning: ‘workbuf’ may be used uninitialized
[-Wmaybe-uninitialized]
565 | char *workb = VARDATA(&workbuf.hdr);
| ^~~~~~~~~~~~~~~~~~~~~

I've not fully investigated the root cause but commit e035863c9a0
presumably is the culprit. FYI I'm using gcc 14.2.1.

Interesting. I did not see such warnings with gcc 14.3.1, 15.1.1,
nor older gcc versions. Must be something peculiar to 14.2.

Hmm, I got the same warning with 14.3.1 (exact version shown below) so
probably something is strange on my end:

% gcc --version
gcc (GCC) 14.3.1 20250805

Theoretically this shouldn't be necessary, since VARDATA()
does not touch the contents of the pointed-to object.

Right. I'll do more research.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Masahiko Sawada (#11)
Re: Convert varatt.h macros to static inline functions

Masahiko Sawada <sawada.mshk@gmail.com> writes:

On Tue, Aug 5, 2025 at 11:34 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Interesting. I did not see such warnings with gcc 14.3.1, 15.1.1,
nor older gcc versions. Must be something peculiar to 14.2.

Hmm, I got the same warning with 14.3.1 (exact version shown below) so
probably something is strange on my end:

% gcc --version
gcc (GCC) 14.3.1 20250805

That's even more interesting. The specific late-model gcc versions
I checked were from Fedora 41:

$ gcc --version
gcc (GCC) 14.3.1 20250523 (Red Hat 14.3.1-1)

and Fedora 42:

gcc (GCC) 15.1.1 20250521 (Red Hat 15.1.1-2)

Maybe there's some strange cross-distro difference here, but
what I'm wondering is if there's a difference in CFLAGS.
My build used

CFLAGS = -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wshadow=compatible-local -Wformat-security -Wmissing-variable-declarations -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -O2

regards, tom lane

#13Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Tom Lane (#12)
Re: Convert varatt.h macros to static inline functions

On Tue, Aug 5, 2025 at 1:59 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Masahiko Sawada <sawada.mshk@gmail.com> writes:

On Tue, Aug 5, 2025 at 11:34 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Interesting. I did not see such warnings with gcc 14.3.1, 15.1.1,
nor older gcc versions. Must be something peculiar to 14.2.

Hmm, I got the same warning with 14.3.1 (exact version shown below) so
probably something is strange on my end:

% gcc --version
gcc (GCC) 14.3.1 20250805

That's even more interesting. The specific late-model gcc versions
I checked were from Fedora 41:

$ gcc --version
gcc (GCC) 14.3.1 20250523 (Red Hat 14.3.1-1)

and Fedora 42:

gcc (GCC) 15.1.1 20250521 (Red Hat 15.1.1-2)

Maybe there's some strange cross-distro difference here, but
what I'm wondering is if there's a difference in CFLAGS.
My build used

CFLAGS = -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wshadow=compatible-local -Wformat-security -Wmissing-variable-declarations -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -O2

Yeah, interestingly I didn't see the warning with CFLAGS your build
used but got it if I use -O0 instead of -O2.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

#14Tom Lane
tgl@sss.pgh.pa.us
In reply to: Masahiko Sawada (#13)
Re: Convert varatt.h macros to static inline functions

Masahiko Sawada <sawada.mshk@gmail.com> writes:

On Tue, Aug 5, 2025 at 1:59 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Maybe there's some strange cross-distro difference here, but
what I'm wondering is if there's a difference in CFLAGS.
My build used

CFLAGS = -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wshadow=compatible-local -Wformat-security -Wmissing-variable-declarations -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -O2

Yeah, interestingly I didn't see the warning with CFLAGS your build
used but got it if I use -O0 instead of -O2.

I checked the buildfarm, and (so far) adder and flaviventris have
shown this warning, but nothing else has. adder is using gcc 14.2.0
with -O0, while flaviventris is using gcc 16.0.0 with -O0. Also
I tried -O0 with gcc 15.1.1 on my Fedora 42 box, and now it shows the
warning. So maybe the difference is just -O0? But I think there are
other buildfarm animals using that, so I'm not certain we've explained
the difference fully.

Anyway, based on that I think there's enough reason to go ahead
with your patch.

regards, tom lane

#15Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Tom Lane (#14)
1 attachment(s)
Re: Convert varatt.h macros to static inline functions

On Tue, Aug 5, 2025 at 2:39 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Masahiko Sawada <sawada.mshk@gmail.com> writes:

On Tue, Aug 5, 2025 at 1:59 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Maybe there's some strange cross-distro difference here, but
what I'm wondering is if there's a difference in CFLAGS.
My build used

CFLAGS = -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wshadow=compatible-local -Wformat-security -Wmissing-variable-declarations -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -O2

Yeah, interestingly I didn't see the warning with CFLAGS your build
used but got it if I use -O0 instead of -O2.

I checked the buildfarm, and (so far) adder and flaviventris have
shown this warning, but nothing else has. adder is using gcc 14.2.0
with -O0, while flaviventris is using gcc 16.0.0 with -O0.

Indeed. Thank you for checking.

Also
I tried -O0 with gcc 15.1.1 on my Fedora 42 box, and now it shows the
warning. So maybe the difference is just -O0? But I think there are
other buildfarm animals using that, so I'm not certain we've explained
the difference fully.

Anyway, based on that I think there's enough reason to go ahead
with your patch.

Agreed. I've attached the patch. I'll push it, barring comments.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com

Attachments:

0001-Suppress-maybe-uninitialized-warning.patchapplication/octet-stream; name=0001-Suppress-maybe-uninitialized-warning.patchDownload
From db59b5709dee5103c4b8d1d411b05216f74fce12 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Tue, 5 Aug 2025 14:54:31 -0700
Subject: [PATCH] Suppress maybe-uninitialized warning.

Following commit e035863c9a0, building with -O0 began triggering
warnings about potentially uninitialized 'workbuf' usage. While
theoretically the initialization isn't necessary since VARDATA()
doesn't access the contents of the pointed-to object, this commit
explicitly initializes the workbuf variable to suppress the warning.

Buildfarm members adder and flaviventris have shown the warning.

Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAD21AoCOZxfqnNgfM5yVKJZYnOq5m2Q96fBGy1fovEqQ9V4OZA@mail.gmail.com
---
 src/backend/storage/large_object/inv_api.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 68b76f2cc18..a874000c8ca 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -561,7 +561,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 		char		data[LOBLKSIZE + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
-	}			workbuf;
+	}			workbuf = {0};
 	char	   *workb = VARDATA(&workbuf.hdr);
 	HeapTuple	newtup;
 	Datum		values[Natts_pg_largeobject];
@@ -752,7 +752,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int64 len)
 		char		data[LOBLKSIZE + VARHDRSZ];
 		/* ensure union is aligned well enough: */
 		int32		align_it;
-	}			workbuf;
+	}			workbuf = {0};
 	char	   *workb = VARDATA(&workbuf.hdr);
 	HeapTuple	newtup;
 	Datum		values[Natts_pg_largeobject];
-- 
2.47.3

#16Masahiko Sawada
sawada.mshk@gmail.com
In reply to: Masahiko Sawada (#15)
Re: Convert varatt.h macros to static inline functions

On Tue, Aug 5, 2025 at 3:07 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:

On Tue, Aug 5, 2025 at 2:39 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Masahiko Sawada <sawada.mshk@gmail.com> writes:

On Tue, Aug 5, 2025 at 1:59 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Maybe there's some strange cross-distro difference here, but
what I'm wondering is if there's a difference in CFLAGS.
My build used

CFLAGS = -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wshadow=compatible-local -Wformat-security -Wmissing-variable-declarations -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -g -O2

Yeah, interestingly I didn't see the warning with CFLAGS your build
used but got it if I use -O0 instead of -O2.

I checked the buildfarm, and (so far) adder and flaviventris have
shown this warning, but nothing else has. adder is using gcc 14.2.0
with -O0, while flaviventris is using gcc 16.0.0 with -O0.

Indeed. Thank you for checking.

Also
I tried -O0 with gcc 15.1.1 on my Fedora 42 box, and now it shows the
warning. So maybe the difference is just -O0? But I think there are
other buildfarm animals using that, so I'm not certain we've explained
the difference fully.

Anyway, based on that I think there's enough reason to go ahead
with your patch.

Agreed. I've attached the patch. I'll push it, barring comments.

Pushed.

Regards,

--
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com