diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 86dc9cf51a9..69a22559661 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -1681,6 +1681,7 @@ check_tuple_attribute(HeapCheckContext *ctx)
 	uint32		va_extinfo;
 	CompactAttribute *thisatt;
 	vartag_external va_tag_value;
+	toast_external_data toast_ext_data;
 
 	infomask = ctx->tuphdr->t_infomask;
 	thisatt = TupleDescCompactAttr(RelationGetDescr(ctx->rel), ctx->attnum);
@@ -1783,31 +1784,12 @@ check_tuple_attribute(HeapCheckContext *ctx)
 
 	/* It is external, and we're looking at a page on disk */
 
-	/*
-	 * Must copy attr into toast_pointer for alignment considerations.
-	 * Branch on the tag to determine which pointer type to extract.
-	 */
-	va_tag_value = VARTAG_EXTERNAL(attr);
-	if (va_tag_value == VARTAG_ONDISK_OID8)
-	{
-		varatt_external_oid8 toast_pointer8;
-
-		/* Must copy to access aligned fields */
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer8, attr);
-		toast_pointer_valueid = VARATT_EXTERNAL_OID8_GET_VALUEID(toast_pointer8);
-		va_rawsize = toast_pointer8.va_rawsize;
-		va_extinfo = toast_pointer8.va_extinfo;
-	}
-	else
-	{
-		varatt_external_oid toast_pointer;
-
-		/* Must copy to access aligned fields */
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-		toast_pointer_valueid = toast_pointer.va_valueid;
-		va_rawsize = toast_pointer.va_rawsize;
-		va_extinfo = toast_pointer.va_extinfo;
-	}
+	/* Must copy attr into a decoded pointer for alignment considerations */
+	toast_external_info_get(attr, &toast_ext_data);
+	va_tag_value = toast_ext_data.tag;
+	toast_pointer_valueid = toast_ext_data.valueid;
+	va_rawsize = toast_ext_data.rawsize;
+	va_extinfo = toast_ext_data.extinfo;
 
 	/* Toasted attributes too large to be untoasted should never be stored */
 	if (va_rawsize > VARLENA_SIZE_LIMIT)
@@ -1817,13 +1799,13 @@ check_tuple_attribute(HeapCheckContext *ctx)
 								   va_rawsize,
 								   VARLENA_SIZE_LIMIT));
 
-	if ((va_extinfo & VARLENA_EXTSIZE_MASK) < (Size) (va_rawsize - VARHDRSZ))
+	if (VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize))
 	{
 		ToastCompressionId cmid;
 		bool		valid = false;
 
 		/* Compressed attributes should have a valid compression method */
-		cmid = va_extinfo >> VARLENA_EXTSIZE_BITS;
+		cmid = VARATT_EXTINFO_GET_COMPRESS_METHOD(toast_ext_data.extinfo);
 		switch (cmid)
 		{
 				/* List of all valid compression method IDs */
@@ -1908,31 +1890,16 @@ check_toasted_attribute(HeapCheckContext *ctx, ToastedAttribute *ta)
 	int32		last_chunk_seq;
 	int32		max_chunk_size;
 	Oid8		toast_valueid;
+	Oid			toast_typid;
 
 	toast_valueid = ta->va_valueid;
-	extsize = ta->va_extinfo & VARLENA_EXTSIZE_MASK;
+	extsize = VARATT_EXTINFO_GET_EXTSIZE(ta->va_extinfo);
+	toast_typid = (ta->tag == VARTAG_ONDISK_OID8) ? OID8OID : OIDOID;
 
-	/*
-	 * Setup a scan key to find chunks in toast table with matching va_valueid
-	 */
-	if (ta->tag == VARTAG_ONDISK_OID8)
-	{
-		max_chunk_size = TOAST_OID8_MAX_CHUNK_SIZE;
-
-		ScanKeyInit(&toastkey,
-					(AttrNumber) 1,
-					BTEqualStrategyNumber, F_OID8EQ,
-					ObjectId8GetDatum(toast_valueid));
-	}
-	else
-	{
-		max_chunk_size = TOAST_OID_MAX_CHUNK_SIZE;
-
-		ScanKeyInit(&toastkey,
-					(AttrNumber) 1,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum((Oid) toast_valueid));
-	}
+	/* Set up a scan key to find chunks in the toast table by value ID */
+	max_chunk_size = TOAST_MAX_CHUNK_SIZE(toast_typid);
+	toast_valueid_scankey_init(&toastkey, (AttrNumber) 1, toast_typid,
+							   toast_valueid);
 
 	last_chunk_seq = (extsize - 1) / max_chunk_size;
 
diff --git a/src/backend/access/common/detoast.c b/src/backend/access/common/detoast.c
index 393135f1b9f..7e5863262b3 100644
--- a/src/backend/access/common/detoast.c
+++ b/src/backend/access/common/detoast.c
@@ -225,29 +225,15 @@ detoast_attr_slice(varlena *attr,
 
 	if (VARATT_IS_EXTERNAL_ONDISK(attr))
 	{
+		toast_external_data toast_ext_data;
 		int32		extsize;
 		uint32		compress_method;
 		bool		is_compressed;
 
-		/* Extract extsize and compression info from the appropriate pointer */
-		if (VARTAG_EXTERNAL(attr) == VARTAG_ONDISK_OID8)
-		{
-			varatt_external_oid8 toast_pointer;
-
-			VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-			extsize = VARATT_EXTERNAL_OID8_GET_EXTSIZE(toast_pointer);
-			compress_method = VARATT_EXTERNAL_OID8_GET_COMPRESS_METHOD(toast_pointer);
-			is_compressed = VARATT_EXTERNAL_OID8_IS_COMPRESSED(toast_pointer);
-		}
-		else
-		{
-			varatt_external_oid toast_pointer;
-
-			VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-			extsize = VARATT_EXTERNAL_OID_GET_EXTSIZE(toast_pointer);
-			compress_method = VARATT_EXTERNAL_OID_GET_COMPRESS_METHOD(toast_pointer);
-			is_compressed = VARATT_EXTERNAL_OID_IS_COMPRESSED(toast_pointer);
-		}
+		toast_external_info_get(attr, &toast_ext_data);
+		extsize = VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo);
+		compress_method = VARATT_EXTINFO_GET_COMPRESS_METHOD(toast_ext_data.extinfo);
+		is_compressed = VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize);
 
 		/* fast path for non-compressed external datums */
 		if (!is_compressed)
@@ -359,6 +345,7 @@ toast_fetch_datum(varlena *attr)
 {
 	Relation	toastrel;
 	varlena    *result;
+	toast_external_data toast_ext_data;
 	int32		attrsize;
 	Oid			toastrelid;
 	Oid8		valueid;
@@ -366,40 +353,17 @@ toast_fetch_datum(varlena *attr)
 	if (!VARATT_IS_EXTERNAL_ONDISK(attr))
 		elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums");
 
-	if (VARTAG_EXTERNAL(attr) == VARTAG_ONDISK_OID8)
-	{
-		varatt_external_oid8 toast_pointer;
-
-		/* Must copy to access aligned fields */
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-		attrsize = VARATT_EXTERNAL_OID8_GET_EXTSIZE(toast_pointer);
-		toastrelid = toast_pointer.va_toastrelid;
-		valueid = VARATT_EXTERNAL_OID8_GET_VALUEID(toast_pointer);
+	toast_external_info_get(attr, &toast_ext_data);
+	attrsize = VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo);
+	toastrelid = toast_ext_data.toastrelid;
+	valueid = toast_ext_data.valueid;
 
-		result = (varlena *) palloc(attrsize + VARHDRSZ);
+	result = (varlena *) palloc(attrsize + VARHDRSZ);
 
-		if (VARATT_EXTERNAL_OID8_IS_COMPRESSED(toast_pointer))
-			SET_VARSIZE_COMPRESSED(result, attrsize + VARHDRSZ);
-		else
-			SET_VARSIZE(result, attrsize + VARHDRSZ);
-	}
+	if (VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize))
+		SET_VARSIZE_COMPRESSED(result, attrsize + VARHDRSZ);
 	else
-	{
-		varatt_external_oid toast_pointer;
-
-		/* Must copy to access aligned fields */
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-		attrsize = VARATT_EXTERNAL_OID_GET_EXTSIZE(toast_pointer);
-		toastrelid = toast_pointer.va_toastrelid;
-		valueid = toast_pointer.va_valueid;
-
-		result = (varlena *) palloc(attrsize + VARHDRSZ);
-
-		if (VARATT_EXTERNAL_OID_IS_COMPRESSED(toast_pointer))
-			SET_VARSIZE_COMPRESSED(result, attrsize + VARHDRSZ);
-		else
-			SET_VARSIZE(result, attrsize + VARHDRSZ);
-	}
+		SET_VARSIZE(result, attrsize + VARHDRSZ);
 
 	if (attrsize == 0)
 		return result;			/* Probably shouldn't happen, but just in
@@ -437,6 +401,7 @@ toast_fetch_datum_slice(varlena *attr, int32 sliceoffset,
 {
 	Relation	toastrel;
 	varlena    *result;
+	toast_external_data toast_ext_data;
 	int32		attrsize;
 	Oid			toastrelid;
 	Oid8		valueid;
@@ -445,28 +410,11 @@ toast_fetch_datum_slice(varlena *attr, int32 sliceoffset,
 	if (!VARATT_IS_EXTERNAL_ONDISK(attr))
 		elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums");
 
-	if (VARTAG_EXTERNAL(attr) == VARTAG_ONDISK_OID8)
-	{
-		varatt_external_oid8 toast_pointer;
-
-		/* Must copy to access aligned fields */
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-		attrsize = VARATT_EXTERNAL_OID8_GET_EXTSIZE(toast_pointer);
-		toastrelid = toast_pointer.va_toastrelid;
-		valueid = VARATT_EXTERNAL_OID8_GET_VALUEID(toast_pointer);
-		is_compressed = VARATT_EXTERNAL_OID8_IS_COMPRESSED(toast_pointer);
-	}
-	else
-	{
-		varatt_external_oid toast_pointer;
-
-		/* Must copy to access aligned fields */
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-		attrsize = VARATT_EXTERNAL_OID_GET_EXTSIZE(toast_pointer);
-		toastrelid = toast_pointer.va_toastrelid;
-		valueid = toast_pointer.va_valueid;
-		is_compressed = VARATT_EXTERNAL_OID_IS_COMPRESSED(toast_pointer);
-	}
+	toast_external_info_get(attr, &toast_ext_data);
+	attrsize = VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo);
+	toastrelid = toast_ext_data.toastrelid;
+	valueid = toast_ext_data.valueid;
+	is_compressed = VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize);
 
 	/*
 	 * It's nonsense to fetch slices of a compressed datum unless when it's a
@@ -609,20 +557,10 @@ toast_raw_datum_size(Datum value)
 	if (VARATT_IS_EXTERNAL_ONDISK(attr))
 	{
 		/* va_rawsize is the size of the original datum -- including header */
-		if (VARTAG_EXTERNAL(attr) == VARTAG_ONDISK_OID8)
-		{
-			varatt_external_oid8 toast_pointer;
-
-			VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-			result = toast_pointer.va_rawsize;
-		}
-		else
-		{
-			varatt_external_oid toast_pointer;
+		toast_external_data toast_ext_data;
 
-			VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-			result = toast_pointer.va_rawsize;
-		}
+		toast_external_info_get(attr, &toast_ext_data);
+		result = toast_ext_data.rawsize;
 	}
 	else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
 	{
@@ -679,20 +617,10 @@ toast_datum_size(Datum value)
 		 * compressed or not.  We do not count the size of the toast pointer
 		 * ... should we?
 		 */
-		if (VARTAG_EXTERNAL(attr) == VARTAG_ONDISK_OID8)
-		{
-			varatt_external_oid8 toast_pointer;
+		toast_external_data toast_ext_data;
 
-			VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-			result = VARATT_EXTERNAL_OID8_GET_EXTSIZE(toast_pointer);
-		}
-		else
-		{
-			varatt_external_oid toast_pointer;
-
-			VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-			result = VARATT_EXTERNAL_OID_GET_EXTSIZE(toast_pointer);
-		}
+		toast_external_info_get(attr, &toast_ext_data);
+		result = VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo);
 	}
 	else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
 	{
diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c
index 200cee6be70..09cdf880e4f 100644
--- a/src/backend/access/common/toast_compression.c
+++ b/src/backend/access/common/toast_compression.c
@@ -260,23 +260,14 @@ toast_get_compression_id(varlena *attr)
 	 * the external toast pointer.  If compressed inline, fetch it from the
 	 * toast compression header.
 	 */
-	if (VARATT_IS_EXTERNAL_ONDISK_OID8(attr))
+	if (VARATT_IS_EXTERNAL_ONDISK(attr))
 	{
-		varatt_external_oid8 toast_pointer;
+		toast_external_data toast_ext_data;
 
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
+		toast_external_info_get(attr, &toast_ext_data);
 
-		if (VARATT_EXTERNAL_OID8_IS_COMPRESSED(toast_pointer))
-			cmid = VARATT_EXTERNAL_OID8_GET_COMPRESS_METHOD(toast_pointer);
-	}
-	else if (VARATT_IS_EXTERNAL_ONDISK_OID(attr))
-	{
-		varatt_external_oid toast_pointer;
-
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-
-		if (VARATT_EXTERNAL_OID_IS_COMPRESSED(toast_pointer))
-			cmid = VARATT_EXTERNAL_OID_GET_COMPRESS_METHOD(toast_pointer);
+		if (VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize))
+			cmid = VARATT_EXTINFO_GET_COMPRESS_METHOD(toast_ext_data.extinfo);
 	}
 	else if (VARATT_IS_COMPRESSED(attr))
 		cmid = VARDATA_COMPRESSED_GET_COMPRESS_METHOD(attr);
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index 84c4361af23..38a50883935 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -104,6 +104,55 @@ toast_compress_datum(Datum value, char cmethod)
 	}
 }
 
+/* ----------
+ * toast_preserve_valueid -
+ *
+ *	During a table rewrite that preserves rd_toastoid, we want to preserve
+ *	toast value IDs too. If the datum previously had an external value from
+ *	that same toast table, return its value ID so the caller can re-use it;
+ *	otherwise return InvalidOid8. This works for both Oid and Oid8 value IDs,
+ *	as the value ID is decoded independently of the pointer's vartag.
+ *
+ *	There is a corner case here: the table rewrite might have to copy both
+ *	live and recently-dead versions of a row, and those versions could easily
+ *	reference the same toast value. When we copy the second or later version
+ *	of such a row, preserving the value ID means we select one that's already
+ *	in the new toast table. We detect that and set *data_todo to 0 so the
+ *	caller falls through without writing the data again.
+ *
+ *	While annoying and ugly-looking, this is a good thing because it ensures
+ *	that we wind up with only one copy of the toast value when there is only
+ *	one copy in the old toast table. Before we detected this case, we'd have
+ *	made multiple copies, wasting space; and what's worse, the copies
+ *	belonging to already-deleted heap tuples would not be reclaimed by VACUUM.
+ * ----------
+ */
+static Oid8
+toast_preserve_valueid(Relation toastrel, Oid toastoid,
+					   varlena *oldexternal, int32 *data_todo)
+{
+	toast_external_data old;
+
+	if (!OidIsValid(toastoid) || oldexternal == NULL)
+		return InvalidOid8;
+
+	Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal));
+	toast_external_info_get(oldexternal, &old);
+
+	/*
+	 * Only re-use the value ID if the old pointer came from this same toast
+	 * table. Since that is the very same relation, its chunk_id type
+	 * necessarily matches, so no separate vartag check is needed.
+	 */
+	if (old.toastrelid != toastoid)
+		return InvalidOid8;
+
+	if (toastrel_valueid_exists(toastrel, old.valueid))
+		*data_todo = 0;
+
+	return old.valueid;
+}
+
 /* ----------
  * toast_save_datum -
  *
@@ -190,7 +239,7 @@ toast_save_datum(Relation rel, Datum value,
 		va_extinfo = data_todo | (cmid << VARLENA_EXTSIZE_BITS);
 
 		/* Assert that the numbers look like it's compressed */
-		Assert((va_extinfo & VARLENA_EXTSIZE_MASK) < (Size) (va_rawsize - VARHDRSZ));
+		Assert(VARATT_EXTINFO_IS_COMPRESSED(va_extinfo, va_rawsize));
 	}
 	else
 	{
@@ -214,162 +263,45 @@ toast_save_datum(Relation rel, Datum value,
 		va_toastrelid = RelationGetRelid(toastrel);
 
 	/*
-	 * Choose a new value to use as the value ID for this toast value, and
-	 * determine the maximum chunk size based on the TOAST table's chunk_id
-	 * type.
-	 *
-	 * Normally we just choose an unused value within the toast table.  But
-	 * during table-rewriting operations where we are preserving an existing
-	 * toast table OID, we want to preserve toast value IDs too.  So, if
-	 * rd_toastoid is set and we had a prior external value from that same
-	 * toast table, re-use its value ID.  If we didn't have a prior external
-	 * value (which is a corner case, but possible if the table's attstorage
-	 * options have been changed), we have to pick a value ID that doesn't
-	 * conflict with either new or existing toast value IDs.  For Oid8 tables,
-	 * value conflicts are not a concern.
+	 * Choose the value ID for this toast value. During a table rewrite that
+	 * preserves rd_toastoid we re-use the prior value ID if we can (see
+	 * toast_preserve_valueid); otherwise we pick a fresh one. For Oid value
+	 * IDs the fresh one must not conflict with old or new toast values; for
+	 * Oid8 the ID space is large enough that conflicts are not a concern.
 	 */
-	if (toast_typid == OID8OID)
+	if (toast_typid == OIDOID)
 	{
-		if (!OidIsValid(rel->rd_toastoid))
-		{
-			/* normal case: just choose a new Oid8 */
-			va_valueid = GetNewObjectId8();
-		}
-		else
+		va_valueid = toast_preserve_valueid(toastrel, rel->rd_toastoid,
+											oldexternal, &data_todo);
+		if (va_valueid == InvalidOid)
 		{
-			/* rewrite case: check to see if value was in old toast table */
-			va_valueid = InvalidOid8;
-			if (oldexternal != NULL)
-			{
-				Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal));
-
-				/*
-				 * Only consider the old pointer if its vartag matches the
-				 * kind of pointer we are about to build.
-				 */
-				if (VARATT_IS_EXTERNAL_ONDISK_OID8(oldexternal))
-				{
-					varatt_external_oid8 old_toast_pointer;
-
-					/* Must copy to access aligned fields */
-					VARATT_EXTERNAL_GET_POINTER(old_toast_pointer, oldexternal);
-					if (old_toast_pointer.va_toastrelid == rel->rd_toastoid)
-					{
-						/*
-						 * This value came from the old toast table; reuse its
-						 * ID.
-						 */
-						va_valueid = VARATT_EXTERNAL_OID8_GET_VALUEID(old_toast_pointer);
-
-						/*
-						 * There is a corner case here: the table rewrite
-						 * might have to copy both live and recently-dead
-						 * versions of a row, and those versions could easily
-						 * reference the same toast value.  When we copy the
-						 * second or later version of such a row, reusing the
-						 * OID will mean we select an OID that's already in the
-						 * new toast table.  Check for that, and if so, just
-						 * fall through without writing the data again.
-						 *
-						 * While annoying and ugly-looking, this is a good
-						 * thing because it ensures that we wind up with only
-						 * one copy of the toast value when there is only one
-						 * copy in the old toast table.  Before we detected
-						 * this case, we'd have made multiple copies, wasting
-						 * space; and what's worse, the copies belonging to
-						 * already-deleted heap tuples would not be reclaimed
-						 * by VACUUM.
-						 */
-						if (toastrel_valueid_exists(toastrel, va_valueid))
-						{
-							/*
-							 * Match, so short-circuit the data storage loop
-							 * below.
-							 */
-							data_todo = 0;
-						}
-					}
-				}
-			}
-			if (va_valueid == InvalidOid8)
-			{
-				/*
-				 * New value.  For Oid8 we just pick a new ID without worrying
-				 * about conflicts.
-				 */
-				va_valueid = GetNewObjectId8();
-			}
+			/*
+			 * Choose an unused OID. During a rewrite we must also avoid IDs
+			 * that are still present in the old toast table.
+			 */
+			do
+				va_valueid =
+					GetNewOidWithIndex(toastrel,
+									   RelationGetRelid(toastidxs[validIndex]),
+									   (AttrNumber) 1);
+			while (OidIsValid(rel->rd_toastoid) &&
+				   toastid_valueid_exists(rel->rd_toastoid, va_valueid));
 		}
-
-		max_chunk_size = TOAST_OID8_MAX_CHUNK_SIZE;
 	}
-	else
+	else if (toast_typid == OID8OID)
 	{
-		if (!OidIsValid(rel->rd_toastoid))
-		{
-			/* normal case: just choose an unused OID */
-			va_valueid =
-				GetNewOidWithIndex(toastrel,
-								   RelationGetRelid(toastidxs[validIndex]),
-								   (AttrNumber) 1);
-		}
-		else
+		va_valueid = toast_preserve_valueid(toastrel, rel->rd_toastoid,
+											oldexternal, &data_todo);
+		if (va_valueid == InvalidOid8)
 		{
-			/* rewrite case: check to see if value was in old toast table */
-			va_valueid = InvalidOid;
-			if (oldexternal != NULL)
-			{
-				Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal));
-
-				/* Only reuse a pointer of the matching type; same as above */
-				if (VARATT_IS_EXTERNAL_ONDISK_OID(oldexternal))
-				{
-					varatt_external_oid old_toast_pointer;
-
-					/* Must copy to access aligned fields */
-					VARATT_EXTERNAL_GET_POINTER(old_toast_pointer, oldexternal);
-					if (old_toast_pointer.va_toastrelid == rel->rd_toastoid)
-					{
-						/*
-						 * This value came from the old toast table; reuse its
-						 * OID.
-						 */
-						va_valueid = old_toast_pointer.va_valueid;
-
-						/*
-						 * Corner case during table rewrite with multiple
-						 * versions of the same row.  See above for details.
-						 */
-						if (toastrel_valueid_exists(toastrel, va_valueid))
-						{
-							/*
-							 * Match, so short-circuit the data storage loop
-							 * below.
-							 */
-							data_todo = 0;
-						}
-					}
-				}
-			}
-			if (va_valueid == InvalidOid)
-			{
-				/*
-				 * new value; must choose a value that doesn't conflict in
-				 * either old or new toast table.
-				 */
-				do
-				{
-					va_valueid =
-						GetNewOidWithIndex(toastrel,
-										   RelationGetRelid(toastidxs[validIndex]),
-										   (AttrNumber) 1);
-				} while (toastid_valueid_exists(rel->rd_toastoid,
-												va_valueid));
-			}
+			/* The Oid8 space is large enough that we need not check conflicts */
+			va_valueid = GetNewObjectId8();
 		}
-
-		max_chunk_size = TOAST_OID_MAX_CHUNK_SIZE;
 	}
+	else
+		elog(ERROR, "unexpected TOAST value ID type %u", toast_typid);
+
+	max_chunk_size = TOAST_MAX_CHUNK_SIZE(toast_typid);
 
 	/*
 	 * Split up the item into chunks
@@ -486,6 +418,29 @@ toast_save_datum(Relation rel, Datum value,
 	return PointerGetDatum(result);
 }
 
+/* ----------
+ * toast_valueid_scankey_init -
+ *
+ *	Initialize a scan key that matches the value ID column of a TOAST table.
+ *	The column type (OIDOID or OID8OID) selects the comparison operator, so
+ *	callers need only supply the value ID widened to Oid8.
+ * ----------
+ */
+void
+toast_valueid_scankey_init(ScanKey entry, AttrNumber attno,
+						   Oid toast_typid, Oid8 valueid)
+{
+	Assert(toast_typid == OIDOID || toast_typid == OID8OID);
+	if (toast_typid == OID8OID)
+		ScanKeyInit(entry, attno,
+					BTEqualStrategyNumber, F_OID8EQ,
+					ObjectId8GetDatum(valueid));
+	else
+		ScanKeyInit(entry, attno,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum((Oid) valueid));
+}
+
 /* ----------
  * toast_delete_datum -
  *
@@ -496,6 +451,7 @@ void
 toast_delete_datum(Relation rel, Datum value, bool is_speculative)
 {
 	varlena    *attr = (varlena *) DatumGetPointer(value);
+	toast_external_data toast_ext_data;
 	Relation	toastrel;
 	Relation   *toastidxs;
 	ScanKeyData toastkey;
@@ -503,75 +459,30 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative)
 	HeapTuple	toasttup;
 	int			num_indexes;
 	int			validIndex;
-	Oid			toastrelid;
-	vartag_external tag;
 
 	if (!VARATT_IS_EXTERNAL_ONDISK(attr))
 		return;
 
 	/*
-	 * Determine the pointer type from the datum's vartag and extract the
-	 * toast relation OID and value ID accordingly.  The vartag tells us
-	 * everything we need - no TOAST table schema lookup required.
+	 * Decode the pointer to get the toast relation OID and value ID. The
+	 * vartag tells us everything we need - no TOAST table schema lookup
+	 * required.
 	 */
-	tag = VARTAG_EXTERNAL(attr);
-
-	if (tag == VARTAG_ONDISK_OID8)
-	{
-		varatt_external_oid8 toast_pointer8;
-
-		/* Must copy to access aligned fields */
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer8, attr);
-		toastrelid = toast_pointer8.va_toastrelid;
-
-		/*
-		 * Open the toast relation and its indexes
-		 */
-		toastrel = table_open(toastrelid, RowExclusiveLock);
-
-		/* Fetch valid relation used for process */
-		validIndex = toast_open_indexes(toastrel,
-										RowExclusiveLock,
-										&toastidxs,
-										&num_indexes);
+	toast_external_info_get(attr, &toast_ext_data);
 
-		/*
-		 * Setup a scan key to find chunks with matching va_valueid
-		 */
-		ScanKeyInit(&toastkey,
-					(AttrNumber) 1,
-					BTEqualStrategyNumber, F_OID8EQ,
-					ObjectId8GetDatum(VARATT_EXTERNAL_OID8_GET_VALUEID(toast_pointer8)));
-	}
-	else
-	{
-		varatt_external_oid toast_pointer;
-
-		Assert(tag == VARTAG_ONDISK_OID);
-
-		/* Must copy to access aligned fields */
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-		toastrelid = toast_pointer.va_toastrelid;
-
-		/*
-		 * Open the toast relation and its indexes
-		 */
-		toastrel = table_open(toastrelid, RowExclusiveLock);
+	/*
+	 * Open the toast relation and its indexes
+	 */
+	toastrel = table_open(toast_ext_data.toastrelid, RowExclusiveLock);
+	validIndex = toast_open_indexes(toastrel,
+									RowExclusiveLock,
+									&toastidxs,
+									&num_indexes);
 
-		/* Fetch valid relation used for process */
-		validIndex = toast_open_indexes(toastrel,
-										RowExclusiveLock,
-										&toastidxs,
-										&num_indexes);
-
-		/*
-		 * Setup a scan key to find chunks with matching va_valueid
-		 */
-		ScanKeyInit(&toastkey,
-					(AttrNumber) 1,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum(toast_pointer.va_valueid));
-	}
+	/* Set up a scan key to find chunks with matching value ID */
+	toast_valueid_scankey_init(&toastkey, (AttrNumber) 1,
+							   TupleDescAttr(toastrel->rd_att, 0)->atttypid,
+							   toast_ext_data.valueid);
 
 	/*
 	 * Find all the chunks.  (We don't actually care whether we see them in
@@ -627,21 +538,9 @@ toastrel_valueid_exists(Relation toastrel, Oid8 valueid)
 									&num_indexes);
 
 	toast_typid = TupleDescAttr(toastrel->rd_att, 0)->atttypid;
-	Assert(toast_typid == OIDOID || toast_typid == OID8OID);
 
-	/*
-	 * Setup a scan key to find chunks with matching va_valueid
-	 */
-	if (toast_typid == OID8OID)
-		ScanKeyInit(&toastkey,
-					(AttrNumber) 1,
-					BTEqualStrategyNumber, F_OID8EQ,
-					ObjectId8GetDatum(valueid));
-	else
-		ScanKeyInit(&toastkey,
-					(AttrNumber) 1,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum((Oid) valueid));
+	/* Set up a scan key to find chunks with matching value ID */
+	toast_valueid_scankey_init(&toastkey, (AttrNumber) 1, toast_typid, valueid);
 
 	/*
 	 * Is there any such chunk?
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 31e6115de20..419adc1f3f7 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -650,10 +650,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid8 valueid, int32 attrsize,
 
 	toast_typid = TupleDescAttr(toastrel->rd_att, 0)->atttypid;
 	Assert(toast_typid == OIDOID || toast_typid == OID8OID);
-	if (toast_typid == OID8OID)
-		max_chunk_size = TOAST_OID8_MAX_CHUNK_SIZE;
-	else
-		max_chunk_size = TOAST_OID_MAX_CHUNK_SIZE;
+	max_chunk_size = TOAST_MAX_CHUNK_SIZE(toast_typid);
 
 	totalchunks = ((attrsize - 1) / max_chunk_size) + 1;
 	startchunk = sliceoffset / max_chunk_size;
@@ -661,16 +658,7 @@ heap_fetch_toast_slice(Relation toastrel, Oid8 valueid, int32 attrsize,
 	Assert(endchunk <= totalchunks);
 
 	/* Set up a scan key to fetch from the index. */
-	if (toast_typid == OID8OID)
-		ScanKeyInit(&toastkey[0],
-					(AttrNumber) 1,
-					BTEqualStrategyNumber, F_OID8EQ,
-					ObjectId8GetDatum(valueid));
-	else
-		ScanKeyInit(&toastkey[0],
-					(AttrNumber) 1,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum((Oid) valueid));
+	toast_valueid_scankey_init(&toastkey[0], (AttrNumber) 1, toast_typid, valueid);
 
 	/*
 	 * No additional condition if fetching all chunks. Otherwise, use an
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 086ef31e4f2..69a479cd31d 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -5175,15 +5175,13 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
 		varlena    *varlena_pointer;
 
 		/* va_rawsize is the size of the original datum -- including header */
-		varatt_external_oid toast_pointer;
-		varatt_external_oid8 toast_pointer8;
+		toast_external_data toast_ext_data;
 		varatt_indirect redirect_pointer;
 		varlena    *new_datum = NULL;
 		varlena    *reconstructed;
 		dlist_iter	it;
 		Size		data_done = 0;
 		Oid8		toast_valueid;
-		int32		rawsize;
 
 		if (attr->attisdropped)
 			continue;
@@ -5203,18 +5201,8 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
 		if (!VARATT_IS_EXTERNAL_ONDISK(varlena_pointer))
 			continue;
 
-		if (VARATT_IS_EXTERNAL_ONDISK_OID8(varlena_pointer))
-		{
-			VARATT_EXTERNAL_GET_POINTER(toast_pointer8, varlena_pointer);
-			toast_valueid = VARATT_EXTERNAL_OID8_GET_VALUEID(toast_pointer8);
-			rawsize = toast_pointer8.va_rawsize;
-		}
-		else
-		{
-			VARATT_EXTERNAL_GET_POINTER(toast_pointer, varlena_pointer);
-			toast_valueid = toast_pointer.va_valueid;
-			rawsize = toast_pointer.va_rawsize;
-		}
+		toast_external_info_get(varlena_pointer, &toast_ext_data);
+		toast_valueid = toast_ext_data.valueid;
 
 		/*
 		 * Check whether the toast tuple changed, replace if so.
@@ -5232,7 +5220,7 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
 
 		free[natt] = true;
 
-		reconstructed = palloc0(rawsize);
+		reconstructed = palloc0(toast_ext_data.rawsize);
 
 		ent->reconstructed = reconstructed;
 
@@ -5258,27 +5246,13 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn,
 			data_done += VARSIZE(chunk) - VARHDRSZ;
 		}
 
-		/* Verify size and set compression status based on pointer type */
-		if (VARATT_IS_EXTERNAL_ONDISK_OID8(varlena_pointer))
-		{
-			Assert(data_done == VARATT_EXTERNAL_OID8_GET_EXTSIZE(toast_pointer8));
+		Assert(data_done == VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo));
 
-			/* make sure its marked as compressed or not */
-			if (VARATT_EXTERNAL_OID8_IS_COMPRESSED(toast_pointer8))
-				SET_VARSIZE_COMPRESSED(reconstructed, data_done + VARHDRSZ);
-			else
-				SET_VARSIZE(reconstructed, data_done + VARHDRSZ);
-		}
+		/* make sure its marked as compressed or not */
+		if (VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize))
+			SET_VARSIZE_COMPRESSED(reconstructed, data_done + VARHDRSZ);
 		else
-		{
-			Assert(data_done == VARATT_EXTERNAL_OID_GET_EXTSIZE(toast_pointer));
-
-			/* make sure its marked as compressed or not */
-			if (VARATT_EXTERNAL_OID_IS_COMPRESSED(toast_pointer))
-				SET_VARSIZE_COMPRESSED(reconstructed, data_done + VARHDRSZ);
-			else
-				SET_VARSIZE(reconstructed, data_done + VARHDRSZ);
-		}
+			SET_VARSIZE(reconstructed, data_done + VARHDRSZ);
 
 		memset(&redirect_pointer, 0, sizeof(redirect_pointer));
 		redirect_pointer.pointer = reconstructed;
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 0616db77555..f8c16bd4c87 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4261,6 +4261,7 @@ pg_column_toast_chunk_id(PG_FUNCTION_ARGS)
 {
 	int			typlen;
 	varlena    *attr;
+	toast_external_data toast_ext_data;
 	Oid8		result;
 
 	/* On first call, get the input type's typlen, and save at *fn_extra */
@@ -4288,20 +4289,8 @@ pg_column_toast_chunk_id(PG_FUNCTION_ARGS)
 	if (!VARATT_IS_EXTERNAL_ONDISK(attr))
 		PG_RETURN_NULL();
 
-	if (VARATT_IS_EXTERNAL_ONDISK_OID8(attr))
-	{
-		varatt_external_oid8 toast_pointer;
-
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-		result = VARATT_EXTERNAL_OID8_GET_VALUEID(toast_pointer);
-	}
-	else
-	{
-		varatt_external_oid toast_pointer;
-
-		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
-		result = toast_pointer.va_valueid;
-	}
+	toast_external_info_get(attr, &toast_ext_data);
+	result = toast_ext_data.valueid;
 
 	PG_RETURN_OID8(result);
 }
diff --git a/src/include/access/detoast.h b/src/include/access/detoast.h
index f065ea034e9..88301308544 100644
--- a/src/include/access/detoast.h
+++ b/src/include/access/detoast.h
@@ -12,6 +12,8 @@
 #ifndef DETOAST_H
 #define DETOAST_H
 
+#include "varatt.h"
+
 /*
  * Macro to fetch the possibly-unaligned contents of an EXTERNAL datum
  * into a local "varatt_external_oid" toast pointer.  This should be
@@ -36,6 +38,52 @@ do { \
 /* Size of an EXTERNAL datum that contains an indirection pointer */
 #define INDIRECT_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_indirect))
 
+/*
+ * Decoded contents of an on-disk TOAST pointer, independent of whether the
+ * value ID is an Oid or an Oid8. This lets the many callers that only need
+ * the pointer's fields avoid open-coding the vartag branch. The value ID is
+ * always widened to Oid8; for a VARTAG_ONDISK_OID pointer it holds the 4-byte
+ * ID losslessly, and callers that need the narrow form cast back to Oid.
+ */
+typedef struct toast_external_data
+{
+	vartag_external tag;		/* VARTAG_ONDISK_OID or VARTAG_ONDISK_OID8 */
+	int32		rawsize;		/* original data size (includes header) */
+	uint32		extinfo;		/* saved size + compression method */
+	Oid8		valueid;		/* value ID (widened from Oid if needed) */
+	Oid			toastrelid;		/* OID of the TOAST table containing it */
+} toast_external_data;
+
+/*
+ * Decode an on-disk TOAST pointer into a toast_external_data. The datum must
+ * be VARATT_IS_EXTERNAL_ONDISK.
+ */
+static inline void
+toast_external_info_get(const struct varlena *attr, toast_external_data *toast_ext_data)
+{
+	toast_ext_data->tag = VARTAG_EXTERNAL(attr);
+	if (toast_ext_data->tag == VARTAG_ONDISK_OID8)
+	{
+		varatt_external_oid8 toast_pointer;
+
+		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
+		toast_ext_data->rawsize = toast_pointer.va_rawsize;
+		toast_ext_data->extinfo = toast_pointer.va_extinfo;
+		toast_ext_data->valueid = VARATT_EXTERNAL_OID8_GET_VALUEID(toast_pointer);
+		toast_ext_data->toastrelid = toast_pointer.va_toastrelid;
+	}
+	else
+	{
+		varatt_external_oid toast_pointer;
+
+		VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
+		toast_ext_data->rawsize = toast_pointer.va_rawsize;
+		toast_ext_data->extinfo = toast_pointer.va_extinfo;
+		toast_ext_data->valueid = toast_pointer.va_valueid;
+		toast_ext_data->toastrelid = toast_pointer.va_toastrelid;
+	}
+}
+
 /* ----------
  * detoast_external_attr() -
  *
diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h
index b7ef69a8f56..c58d0fa36a4 100644
--- a/src/include/access/heaptoast.h
+++ b/src/include/access/heaptoast.h
@@ -96,6 +96,13 @@
 	 sizeof(int32) -									\
 	 VARHDRSZ)
 
+/*
+ * Select the chunk size for a TOAST table from its value ID column type
+ * (OIDOID or OID8OID).
+ */
+#define TOAST_MAX_CHUNK_SIZE(toast_typid) \
+	((toast_typid) == OID8OID ? TOAST_OID8_MAX_CHUNK_SIZE : TOAST_OID_MAX_CHUNK_SIZE)
+
 /* ----------
  * heap_toast_insert_or_update -
  *
diff --git a/src/include/access/toast_internals.h b/src/include/access/toast_internals.h
index bf45889a642..c0015f9cc6c 100644
--- a/src/include/access/toast_internals.h
+++ b/src/include/access/toast_internals.h
@@ -12,6 +12,7 @@
 #ifndef TOAST_INTERNALS_H
 #define TOAST_INTERNALS_H
 
+#include "access/skey.h"
 #include "access/toast_compression.h"
 #include "storage/lockdefs.h"
 #include "utils/relcache.h"
@@ -52,6 +53,9 @@ extern void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
 extern Datum toast_save_datum(Relation rel, Datum value,
 							  varlena *oldexternal, uint32 options);
 
+extern void toast_valueid_scankey_init(ScanKey entry, AttrNumber attno,
+									   Oid toast_typid, Oid8 valueid);
+
 extern int	toast_open_indexes(Relation toastrel,
 							   LOCKMODE lock,
 							   Relation **toastidxs,
diff --git a/src/include/varatt.h b/src/include/varatt.h
index 1349a54371d..c8895df2651 100644
--- a/src/include/varatt.h
+++ b/src/include/varatt.h
@@ -564,33 +564,58 @@ VARDATA_COMPRESSED_GET_COMPRESS_METHOD(const void *PTR)
 }
 
 /*
- * Same for external Datums; but note argument is a struct
- * varatt_external_oid.
+ * The size and compression method of an externally-stored value are packed
+ * into a single 32-bit va_extinfo word: the low VARLENA_EXTSIZE_BITS hold the
+ * saved size, the top two bits the compression method. This encoding does not
+ * depend on the value ID width, so these helpers operate on the raw word and
+ * are shared by every on-disk TOAST pointer type.
  */
 static inline Size
-VARATT_EXTERNAL_OID_GET_EXTSIZE(varatt_external_oid toast_pointer)
+VARATT_EXTINFO_GET_EXTSIZE(uint32 extinfo)
 {
-	return toast_pointer.va_extinfo & VARLENA_EXTSIZE_MASK;
+	return extinfo & VARLENA_EXTSIZE_MASK;
 }
 
 static inline uint32
-VARATT_EXTERNAL_OID_GET_COMPRESS_METHOD(varatt_external_oid toast_pointer)
+VARATT_EXTINFO_GET_COMPRESS_METHOD(uint32 extinfo)
 {
-	return toast_pointer.va_extinfo >> VARLENA_EXTSIZE_BITS;
+	return extinfo >> VARLENA_EXTSIZE_BITS;
 }
 
 /*
- * Testing whether an externally-stored value is compressed now requires
- * comparing size stored in va_extinfo (the actual length of the external data)
+ * Testing whether an externally-stored value is compressed requires comparing
+ * the saved size stored in va_extinfo (the actual length of the external data)
  * to rawsize (the original uncompressed datum's size).  The latter includes
  * VARHDRSZ overhead, the former doesn't.  We never use compression unless it
  * actually saves space, so we expect either equality or less-than.
  */
+static inline bool
+VARATT_EXTINFO_IS_COMPRESSED(uint32 extinfo, int32 rawsize)
+{
+	return VARATT_EXTINFO_GET_EXTSIZE(extinfo) < (Size) (rawsize - VARHDRSZ);
+}
+
+/*
+ * Same for external Datums; but note argument is a struct
+ * varatt_external_oid.
+ */
+static inline Size
+VARATT_EXTERNAL_OID_GET_EXTSIZE(varatt_external_oid toast_pointer)
+{
+	return VARATT_EXTINFO_GET_EXTSIZE(toast_pointer.va_extinfo);
+}
+
+static inline uint32
+VARATT_EXTERNAL_OID_GET_COMPRESS_METHOD(varatt_external_oid toast_pointer)
+{
+	return VARATT_EXTINFO_GET_COMPRESS_METHOD(toast_pointer.va_extinfo);
+}
+
 static inline bool
 VARATT_EXTERNAL_OID_IS_COMPRESSED(varatt_external_oid toast_pointer)
 {
-	return VARATT_EXTERNAL_OID_GET_EXTSIZE(toast_pointer) <
-		(Size) (toast_pointer.va_rawsize - VARHDRSZ);
+	return VARATT_EXTINFO_IS_COMPRESSED(toast_pointer.va_extinfo,
+										toast_pointer.va_rawsize);
 }
 
 /*
@@ -600,20 +625,20 @@ VARATT_EXTERNAL_OID_IS_COMPRESSED(varatt_external_oid toast_pointer)
 static inline Size
 VARATT_EXTERNAL_OID8_GET_EXTSIZE(varatt_external_oid8 toast_pointer)
 {
-	return toast_pointer.va_extinfo & VARLENA_EXTSIZE_MASK;
+	return VARATT_EXTINFO_GET_EXTSIZE(toast_pointer.va_extinfo);
 }
 
 static inline uint32
 VARATT_EXTERNAL_OID8_GET_COMPRESS_METHOD(varatt_external_oid8 toast_pointer)
 {
-	return toast_pointer.va_extinfo >> VARLENA_EXTSIZE_BITS;
+	return VARATT_EXTINFO_GET_COMPRESS_METHOD(toast_pointer.va_extinfo);
 }
 
 static inline bool
 VARATT_EXTERNAL_OID8_IS_COMPRESSED(varatt_external_oid8 toast_pointer)
 {
-	return VARATT_EXTERNAL_OID8_GET_EXTSIZE(toast_pointer) <
-		(Size) (toast_pointer.va_rawsize - VARHDRSZ);
+	return VARATT_EXTINFO_IS_COMPRESSED(toast_pointer.va_extinfo,
+										toast_pointer.va_rawsize);
 }
 
 #endif
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 7aedaafab90..dd5394ab3e0 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -4357,6 +4357,7 @@ timeout_params
 timerCA
 tlist_vinfo
 toast_compress_header
+toast_external_data
 tokenize_error_callback_arg
 transferMode
 transfer_thread_arg
@@ -4413,6 +4414,7 @@ vacuumingOptions
 validate_string_relopt
 varatt_expanded
 varatt_external_oid
+varatt_external_oid8
 varatt_indirect
 varattrib_1b
 varattrib_1b_e
