From ee86b55a13d719bca50c69f684425a77fc97dc4a Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Fri, 19 Feb 2021 17:57:05 +0530
Subject: [PATCH v27 1/3] Disallow compressed data inside container types

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the comprassed field but while contructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we onlle decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.
---
 contrib/dblink/dblink.c                         |  2 +-
 contrib/hstore/hstore_io.c                      |  4 +-
 contrib/hstore/hstore_op.c                      |  2 +-
 contrib/old_snapshot/time_mapping.c             |  2 +-
 contrib/pageinspect/brinfuncs.c                 |  2 +-
 contrib/pageinspect/btreefuncs.c                |  6 +-
 contrib/pageinspect/ginfuncs.c                  |  6 +-
 contrib/pageinspect/gistfuncs.c                 |  2 +-
 contrib/pageinspect/hashfuncs.c                 |  8 +--
 contrib/pageinspect/heapfuncs.c                 |  6 +-
 contrib/pageinspect/rawpage.c                   |  2 +-
 contrib/pg_buffercache/pg_buffercache_pages.c   |  2 +-
 contrib/pg_stat_statements/pg_stat_statements.c |  2 +-
 contrib/pg_visibility/pg_visibility.c           | 10 ++--
 contrib/pgcrypto/pgp-pgsql.c                    |  2 +-
 contrib/pgstattuple/pgstatapprox.c              |  2 +-
 contrib/pgstattuple/pgstatindex.c               |  6 +-
 contrib/pgstattuple/pgstattuple.c               |  2 +-
 contrib/sslinfo/sslinfo.c                       |  2 +-
 src/backend/access/heap/heaptoast.c             |  4 +-
 src/backend/access/transam/commit_ts.c          |  4 +-
 src/backend/access/transam/multixact.c          |  2 +-
 src/backend/access/transam/twophase.c           |  2 +-
 src/backend/access/transam/xlogfuncs.c          |  2 +-
 src/backend/catalog/objectaddress.c             |  6 +-
 src/backend/commands/sequence.c                 |  2 +-
 src/backend/executor/execExprInterp.c           | 28 ++++++++-
 src/backend/executor/execTuples.c               |  4 --
 src/backend/replication/slotfuncs.c             |  8 +--
 src/backend/replication/walreceiver.c           |  2 +-
 src/backend/statistics/mcv.c                    |  2 +-
 src/backend/tsearch/wparser.c                   |  4 +-
 src/backend/utils/adt/acl.c                     |  2 +-
 src/backend/utils/adt/datetime.c                |  2 +-
 src/backend/utils/adt/expandedrecord.c          | 76 +++++++++----------------
 src/backend/utils/adt/genfile.c                 |  2 +-
 src/backend/utils/adt/jsonfuncs.c               |  7 ++-
 src/backend/utils/adt/lockfuncs.c               |  4 +-
 src/backend/utils/adt/misc.c                    |  4 +-
 src/backend/utils/adt/partitionfuncs.c          |  2 +-
 src/backend/utils/adt/pgstatfuncs.c             |  4 +-
 src/backend/utils/adt/tsvector_op.c             |  4 +-
 src/backend/utils/misc/guc.c                    |  2 +-
 src/backend/utils/misc/pg_controldata.c         |  8 +--
 src/include/fmgr.h                              |  2 +-
 src/include/funcapi.h                           |  4 ++
 src/pl/plperl/plperl.c                          |  4 +-
 src/pl/plpgsql/src/pl_exec.c                    |  4 +-
 src/pl/tcl/pltcl.c                              |  4 +-
 src/test/modules/test_predtest/test_predtest.c  |  2 +-
 50 files changed, 141 insertions(+), 136 deletions(-)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 3a0beaa..5a41116 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1630,7 +1630,7 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
 		tuple = BuildTupleFromCStrings(attinmeta, values);
 
 		/* make the tuple into a datum */
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, result);
 	}
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index b3304ff..6c3b571 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -1137,14 +1137,14 @@ hstore_populate_record(PG_FUNCTION_ARGS)
 	 * check domain constraints before deciding we're done.
 	 */
 	if (argtype != tupdesc->tdtypeid)
-		domain_check(HeapTupleGetDatum(rettuple), false,
+		domain_check(HeapTupleGetRawDatum(rettuple), false,
 					 argtype,
 					 &my_extra->domain_info,
 					 fcinfo->flinfo->fn_mcxt);
 
 	ReleaseTupleDesc(tupdesc);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(rettuple));
 }
 
 
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index dd79d01..d466f6c 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -1067,7 +1067,7 @@ hstore_each(PG_FUNCTION_ARGS)
 		}
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
-		res = HeapTupleGetDatum(tuple);
+		res = HeapTupleGetRawDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
 	}
diff --git a/contrib/old_snapshot/time_mapping.c b/contrib/old_snapshot/time_mapping.c
index 3df0717..5d517c8 100644
--- a/contrib/old_snapshot/time_mapping.c
+++ b/contrib/old_snapshot/time_mapping.c
@@ -72,7 +72,7 @@ pg_old_snapshot_time_mapping(PG_FUNCTION_ARGS)
 
 		tuple = MakeOldSnapshotTimeMappingTuple(funcctx->tuple_desc, mapping);
 		++mapping->current_index;
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+		SRF_RETURN_NEXT(funcctx, HeapTupleGetRawDatum(tuple));
 	}
 
 	SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index 0e3c2de..b3147ba 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -366,7 +366,7 @@ brin_metapage_info(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 /*
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 8bb180b..f7b220d 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -243,7 +243,7 @@ bt_page_stats_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 	tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
 								   values);
 
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	PG_RETURN_DATUM(result);
 }
@@ -416,7 +416,7 @@ bt_page_print_tuples(struct user_args *uargs)
 	/* Build and return the result tuple */
 	tuple = heap_form_tuple(uargs->tupd, values, nulls);
 
-	return HeapTupleGetDatum(tuple);
+	return HeapTupleGetRawDatum(tuple);
 }
 
 /*-------------------------------------------------------
@@ -737,7 +737,7 @@ bt_metap(PG_FUNCTION_ARGS)
 	tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
 								   values);
 
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	UnlockReleaseBuffer(buffer);
 	relation_close(rel, AccessShareLock);
diff --git a/contrib/pageinspect/ginfuncs.c b/contrib/pageinspect/ginfuncs.c
index e425cbc..77dd559 100644
--- a/contrib/pageinspect/ginfuncs.c
+++ b/contrib/pageinspect/ginfuncs.c
@@ -82,7 +82,7 @@ gin_metapage_info(PG_FUNCTION_ARGS)
 	/* Build and return the result tuple. */
 	resultTuple = heap_form_tuple(tupdesc, values, nulls);
 
-	return HeapTupleGetDatum(resultTuple);
+	return HeapTupleGetRawDatum(resultTuple);
 }
 
 
@@ -150,7 +150,7 @@ gin_page_opaque_info(PG_FUNCTION_ARGS)
 	/* Build and return the result tuple. */
 	resultTuple = heap_form_tuple(tupdesc, values, nulls);
 
-	return HeapTupleGetDatum(resultTuple);
+	return HeapTupleGetRawDatum(resultTuple);
 }
 
 typedef struct gin_leafpage_items_state
@@ -254,7 +254,7 @@ gin_leafpage_items(PG_FUNCTION_ARGS)
 
 		/* Build and return the result tuple. */
 		resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
-		result = HeapTupleGetDatum(resultTuple);
+		result = HeapTupleGetRawDatum(resultTuple);
 
 		inter_call_data->seg = GinNextPostingListSegment(cur);
 
diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c
index eb9f630..dbf34f8 100644
--- a/contrib/pageinspect/gistfuncs.c
+++ b/contrib/pageinspect/gistfuncs.c
@@ -89,7 +89,7 @@ gist_page_opaque_info(PG_FUNCTION_ARGS)
 	/* Build and return the result tuple. */
 	resultTuple = heap_form_tuple(tupdesc, values, nulls);
 
-	return HeapTupleGetDatum(resultTuple);
+	return HeapTupleGetRawDatum(resultTuple);
 }
 
 Datum
diff --git a/contrib/pageinspect/hashfuncs.c b/contrib/pageinspect/hashfuncs.c
index ff01119..4039c9d 100644
--- a/contrib/pageinspect/hashfuncs.c
+++ b/contrib/pageinspect/hashfuncs.c
@@ -273,7 +273,7 @@ hash_page_stats(PG_FUNCTION_ARGS)
 
 	tuple = heap_form_tuple(tupleDesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(tuple));
 }
 
 /*
@@ -368,7 +368,7 @@ hash_page_items(PG_FUNCTION_ARGS)
 		values[j] = Int64GetDatum((int64) hashkey);
 
 		tuple = heap_form_tuple(fctx->attinmeta->tupdesc, values, nulls);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		uargs->offset = uargs->offset + 1;
 
@@ -499,7 +499,7 @@ hash_bitmap_info(PG_FUNCTION_ARGS)
 
 	tuple = heap_form_tuple(tupleDesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(tuple));
 }
 
 /* ------------------------------------------------
@@ -577,5 +577,5 @@ hash_metapage_info(PG_FUNCTION_ARGS)
 
 	tuple = heap_form_tuple(tupleDesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(tuple));
 }
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 9abcee3..893a15b 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -282,7 +282,7 @@ heap_page_items(PG_FUNCTION_ARGS)
 
 		/* Build and return the result tuple. */
 		resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
-		result = HeapTupleGetDatum(resultTuple);
+		result = HeapTupleGetRawDatum(resultTuple);
 
 		inter_call_data->offset++;
 
@@ -540,7 +540,7 @@ heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
 		values[0] = PointerGetDatum(construct_empty_array(TEXTOID));
 		values[1] = PointerGetDatum(construct_empty_array(TEXTOID));
 		tuple = heap_form_tuple(tupdesc, values, nulls);
-		PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+		PG_RETURN_DATUM(HeapTupleGetRawDatum(tuple));
 	}
 
 	/* build set of raw flags */
@@ -618,5 +618,5 @@ heap_tuple_infomask_flags(PG_FUNCTION_ARGS)
 
 	/* Returns the record as Datum */
 	tuple = heap_form_tuple(tupdesc, values, nulls);
-	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(tuple));
 }
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index 9e9ee8a..da72901 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -329,7 +329,7 @@ page_header(PG_FUNCTION_ARGS)
 	memset(nulls, 0, sizeof(nulls));
 
 	tuple = heap_form_tuple(tupdesc, values, nulls);
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	PG_RETURN_DATUM(result);
 }
diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c
index 1bd579f..1fd405e 100644
--- a/contrib/pg_buffercache/pg_buffercache_pages.c
+++ b/contrib/pg_buffercache/pg_buffercache_pages.c
@@ -230,7 +230,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
 
 		/* Build and return the tuple. */
 		tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, result);
 	}
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 62cccbf..da390a0 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -1922,7 +1922,7 @@ pg_stat_statements_info(PG_FUNCTION_ARGS)
 	values[0] = Int64GetDatum(stats.dealloc);
 	values[1] = TimestampTzGetDatum(stats.stats_reset);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
 
 /*
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index dd0c124..4819625 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -97,7 +97,7 @@ pg_visibility_map(PG_FUNCTION_ARGS)
 
 	relation_close(rel, AccessShareLock);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
 
 /*
@@ -156,7 +156,7 @@ pg_visibility(PG_FUNCTION_ARGS)
 
 	relation_close(rel, AccessShareLock);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
 
 /*
@@ -197,7 +197,7 @@ pg_visibility_map_rel(PG_FUNCTION_ARGS)
 		info->next++;
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+		SRF_RETURN_NEXT(funcctx, HeapTupleGetRawDatum(tuple));
 	}
 
 	SRF_RETURN_DONE(funcctx);
@@ -243,7 +243,7 @@ pg_visibility_rel(PG_FUNCTION_ARGS)
 		info->next++;
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+		SRF_RETURN_NEXT(funcctx, HeapTupleGetRawDatum(tuple));
 	}
 
 	SRF_RETURN_DONE(funcctx);
@@ -303,7 +303,7 @@ pg_visibility_map_summary(PG_FUNCTION_ARGS)
 	values[0] = Int64GetDatum(all_visible);
 	values[1] = Int64GetDatum(all_frozen);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
 
 /*
diff --git a/contrib/pgcrypto/pgp-pgsql.c b/contrib/pgcrypto/pgp-pgsql.c
index 0536bfb..333f7fd 100644
--- a/contrib/pgcrypto/pgp-pgsql.c
+++ b/contrib/pgcrypto/pgp-pgsql.c
@@ -973,7 +973,7 @@ pgp_armor_headers(PG_FUNCTION_ARGS)
 
 		/* build a tuple */
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+		SRF_RETURN_NEXT(funcctx, HeapTupleGetRawDatum(tuple));
 	}
 }
 
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 1fe193b..147c8d2 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -314,5 +314,5 @@ pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo)
 	values[i++] = Float8GetDatum(stat.free_percent);
 
 	ret = heap_form_tuple(tupdesc, values, nulls);
-	return HeapTupleGetDatum(ret);
+	return HeapTupleGetRawDatum(ret);
 }
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index b1ce0d7..c2ad847 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -358,7 +358,7 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
 		tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(tupleDesc),
 									   values);
 
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 	}
 
 	return result;
@@ -567,7 +567,7 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
 	 * Build and return the tuple
 	 */
 	tuple = heap_form_tuple(tupleDesc, values, nulls);
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	return result;
 }
@@ -723,7 +723,7 @@ pgstathashindex(PG_FUNCTION_ARGS)
 	values[7] = Float8GetDatum(free_percent);
 	tuple = heap_form_tuple(tupleDesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(tuple));
 }
 
 /* -------------------------------------------------
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 21fdeff..70b8bf0 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -147,7 +147,7 @@ build_pgstattuple_type(pgstattuple_type *stat, FunctionCallInfo fcinfo)
 	tuple = BuildTupleFromCStrings(attinmeta, values);
 
 	/* make the tuple into a datum */
-	return HeapTupleGetDatum(tuple);
+	return HeapTupleGetRawDatum(tuple);
 }
 
 /* ----------
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 30cae0b..53801e9 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -460,7 +460,7 @@ ssl_extension_info(PG_FUNCTION_ARGS)
 
 		/* Build tuple */
 		tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		if (BIO_free(membuf) != 1)
 			elog(ERROR, "could not free OpenSSL BIO structure");
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d..b094623 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index 48e8d66..54d835d 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -469,7 +469,7 @@ pg_last_committed_xact(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 /*
@@ -518,7 +518,7 @@ pg_xact_commit_timestamp_origin(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 /*
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 1f9f1a1..b44066a 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -3399,7 +3399,7 @@ pg_get_multixact_members(PG_FUNCTION_ARGS)
 
 		multi->iter++;
 		pfree(values[0]);
-		SRF_RETURN_NEXT(funccxt, HeapTupleGetDatum(tuple));
+		SRF_RETURN_NEXT(funccxt, HeapTupleGetRawDatum(tuple));
 	}
 
 	SRF_RETURN_DONE(funccxt);
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 70d2257..f7afc24 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -786,7 +786,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
 		values[4] = ObjectIdGetDatum(proc->databaseId);
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 		SRF_RETURN_NEXT(funcctx, result);
 	}
 
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index d8c5bf6..53f84de 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -487,7 +487,7 @@ pg_walfile_name_offset(PG_FUNCTION_ARGS)
 	 */
 	resultHeapTuple = heap_form_tuple(resultTupleDesc, values, isnull);
 
-	result = HeapTupleGetDatum(resultHeapTuple);
+	result = HeapTupleGetRawDatum(resultHeapTuple);
 
 	PG_RETURN_DATUM(result);
 }
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 6d88b69..982130a 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2355,7 +2355,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 /*
@@ -4233,7 +4233,7 @@ pg_identify_object(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 /*
@@ -4304,7 +4304,7 @@ pg_identify_object_as_address(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 /*
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 0415df9..f566e1e 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1834,7 +1834,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
 
 	ReleaseSysCache(pgstuple);
 
-	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+	return HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, isnull));
 }
 
 /*
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 6308286..32782c7 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2840,12 +2840,23 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 {
 	HeapTuple	tuple;
 
+	/* Retrieve externally stored values and decompress. */
+	for (int i = 0; i < op->d.row.tupdesc->natts; i++)
+	{
+		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
+
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
+			continue;
+		op->d.row.elemvalues[i] =
+			PointerGetDatum(PG_DETOAST_DATUM_PACKED(op->d.row.elemvalues[i]));
+	}
+
 	/* build tuple from evaluated field values */
 	tuple = heap_form_tuple(op->d.row.tupdesc,
 							op->d.row.elemvalues,
 							op->d.row.elemnulls);
 
-	*op->resvalue = HeapTupleGetDatum(tuple);
+	*op->resvalue = HeapTupleGetRawDatum(tuple);
 	*op->resnull = false;
 }
 
@@ -3085,12 +3096,23 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 {
 	HeapTuple	tuple;
 
+	/* Retrieve externally stored values and decompress. */
+	for (int i = 0; i < (*op->d.fieldstore.argdesc)->natts; i++)
+	{
+		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
+
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
+			continue;
+		op->d.fieldstore.values[i] = PointerGetDatum(
+						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
+	}
+
 	/* argdesc should already be valid from the DeForm step */
 	tuple = heap_form_tuple(*op->d.fieldstore.argdesc,
 							op->d.fieldstore.values,
 							op->d.fieldstore.nulls);
 
-	*op->resvalue = HeapTupleGetDatum(tuple);
+	*op->resvalue = HeapTupleGetRawDatum(tuple);
 	*op->resnull = false;
 }
 
@@ -3170,7 +3192,7 @@ ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 		/* Full conversion with attribute rearrangement needed */
 		result = execute_attr_map_tuple(&tmptup, op->d.convert_rowtype.map);
 		/* Result already has appropriate composite-datum header fields */
-		*op->resvalue = HeapTupleGetDatum(result);
+		*op->resvalue = HeapTupleGetRawDatum(result);
 	}
 	else
 	{
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df..f115464 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index 057f410..bc05981 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -106,7 +106,7 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS)
 		nulls[1] = true;
 
 	tuple = heap_form_tuple(tupdesc, values, nulls);
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	ReplicationSlotRelease();
 
@@ -202,7 +202,7 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS)
 	memset(nulls, 0, sizeof(nulls));
 
 	tuple = heap_form_tuple(tupdesc, values, nulls);
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	/* ok, slot is now fully created, mark it as persistent if needed */
 	if (!temporary)
@@ -685,7 +685,7 @@ pg_replication_slot_advance(PG_FUNCTION_ARGS)
 	nulls[1] = false;
 
 	tuple = heap_form_tuple(tupdesc, values, nulls);
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	PG_RETURN_DATUM(result);
 }
@@ -906,7 +906,7 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot)
 		nulls[1] = true;
 
 	tuple = heap_form_tuple(tupdesc, values, nulls);
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	ReplicationSlotRelease();
 
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 9ec7123..52946bc 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -1424,5 +1424,5 @@ pg_stat_get_wal_receiver(PG_FUNCTION_ARGS)
 	}
 
 	/* Returns the record as Datum */
-	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index abbc1f1..115131c 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -1450,7 +1450,7 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
 		tuple = heap_form_tuple(funcctx->attinmeta->tupdesc, values, nulls);
 
 		/* make the tuple into a datum */
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, result);
 	}
diff --git a/src/backend/tsearch/wparser.c b/src/backend/tsearch/wparser.c
index 71882dc..633c90d 100644
--- a/src/backend/tsearch/wparser.c
+++ b/src/backend/tsearch/wparser.c
@@ -97,7 +97,7 @@ tt_process_call(FuncCallContext *funcctx)
 		values[2] = st->list[st->cur].descr;
 
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		pfree(values[1]);
 		pfree(values[2]);
@@ -236,7 +236,7 @@ prs_process_call(FuncCallContext *funcctx)
 		sprintf(tid, "%d", st->list[st->cur].type);
 		values[1] = st->list[st->cur].lexeme;
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		pfree(values[1]);
 		st->cur++;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index c7f029e..5871c0b 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -1804,7 +1804,7 @@ aclexplode(PG_FUNCTION_ARGS)
 			MemSet(nulls, 0, sizeof(nulls));
 
 			tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-			result = HeapTupleGetDatum(tuple);
+			result = HeapTupleGetRawDatum(tuple);
 
 			SRF_RETURN_NEXT(funcctx, result);
 		}
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 350b0c5..ce11554 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4776,7 +4776,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS)
 	(*pindex)++;
 
 	tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-	result = HeapTupleGetDatum(tuple);
+	result = HeapTupleGetRawDatum(tuple);
 
 	SRF_RETURN_NEXT(funcctx, result);
 }
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491e..66031a5 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+				* expanded_record_set_field_internal can do the actual work
+				* of detoasting.  It needn't recheck domain constraints.
+				*/
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 169ddf8..40d248e 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -454,7 +454,7 @@ pg_stat_file(PG_FUNCTION_ARGS)
 
 	pfree(filename);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(tuple));
 }
 
 /*
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index f194ff9..ad07fd4 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -2979,7 +2979,7 @@ populate_composite(CompositeIOData *io,
 		/* populate resulting record tuple */
 		tuple = populate_record(io->tupdesc, &io->record_io,
 								defaultval, mcxt, &jso);
-		result = HeapTupleHeaderGetDatum(tuple);
+		result = PointerGetDatum(tuple);
 
 		JsObjectFree(&jso);
 	}
@@ -3398,6 +3398,9 @@ populate_record(TupleDesc tupdesc,
 										  nulls[i] ? (Datum) 0 : values[i],
 										  &field,
 										  &nulls[i]);
+
+		if (!nulls[i] && att->attlen == -1)
+			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
 	res = heap_form_tuple(tupdesc, values, nulls);
@@ -3776,7 +3779,7 @@ populate_recordset_record(PopulateRecordsetState *state, JsObject *obj)
 
 	/* if it's domain over composite, check domain constraints */
 	if (cache->c.typcat == TYPECAT_COMPOSITE_DOMAIN)
-		domain_check(HeapTupleHeaderGetDatum(tuphead), false,
+		domain_check(PointerGetDatum(tuphead), false,
 					 cache->argtype,
 					 &cache->c.io.composite.domain_info,
 					 cache->fn_mcxt);
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 97f0265..0818449 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -344,7 +344,7 @@ pg_lock_status(PG_FUNCTION_ARGS)
 			nulls[15] = true;
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 		SRF_RETURN_NEXT(funcctx, result);
 	}
 
@@ -415,7 +415,7 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		nulls[15] = true;
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 		SRF_RETURN_NEXT(funcctx, result);
 	}
 
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 634f574..57e0ea0 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -484,7 +484,7 @@ pg_get_keywords(PG_FUNCTION_ARGS)
 
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
 
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+		SRF_RETURN_NEXT(funcctx, HeapTupleGetRawDatum(tuple));
 	}
 
 	SRF_RETURN_DONE(funcctx);
@@ -562,7 +562,7 @@ pg_get_catalog_foreign_keys(PG_FUNCTION_ARGS)
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
 
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+		SRF_RETURN_NEXT(funcctx, HeapTupleGetRawDatum(tuple));
 	}
 
 	SRF_RETURN_DONE(funcctx);
diff --git a/src/backend/utils/adt/partitionfuncs.c b/src/backend/utils/adt/partitionfuncs.c
index 03660d5..6915939 100644
--- a/src/backend/utils/adt/partitionfuncs.c
+++ b/src/backend/utils/adt/partitionfuncs.c
@@ -159,7 +159,7 @@ pg_partition_tree(PG_FUNCTION_ARGS)
 		values[3] = Int32GetDatum(level);
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 		SRF_RETURN_NEXT(funcctx, result);
 	}
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 62bff52..8437de0 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1843,7 +1843,7 @@ pg_stat_get_wal(PG_FUNCTION_ARGS)
 	values[4] = TimestampTzGetDatum(wal_stats->stat_reset_timestamp);
 
 	/* Returns the record as Datum */
-	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
 
 /*
@@ -2259,7 +2259,7 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
 		values[6] = TimestampTzGetDatum(archiver_stats->stat_reset_timestamp);
 
 	/* Returns the record as Datum */
-	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
 
 /* Get the statistics for the replication slots */
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 9236ebc..6679493 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -707,7 +707,7 @@ tsvector_unnest(PG_FUNCTION_ARGS)
 		}
 
 		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-		SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+		SRF_RETURN_NEXT(funcctx, HeapTupleGetRawDatum(tuple));
 	}
 	else
 	{
@@ -2385,7 +2385,7 @@ ts_process_call(FuncCallContext *funcctx)
 		values[2] = nentry;
 
 		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		pfree(values[0]);
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 00018ab..7a770fb 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -9797,7 +9797,7 @@ show_all_settings(PG_FUNCTION_ARGS)
 		tuple = BuildTupleFromCStrings(attinmeta, values);
 
 		/* make the tuple into a datum */
-		result = HeapTupleGetDatum(tuple);
+		result = HeapTupleGetRawDatum(tuple);
 
 		SRF_RETURN_NEXT(funcctx, result);
 	}
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index 209a20a..318dee2 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -73,7 +73,7 @@ pg_control_system(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 Datum
@@ -204,7 +204,7 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 Datum
@@ -257,7 +257,7 @@ pg_control_recovery(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
 
 Datum
@@ -340,5 +340,5 @@ pg_control_init(PG_FUNCTION_ARGS)
 
 	htup = heap_form_tuple(tupdesc, values, nulls);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(htup));
 }
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index ab7b85c..47f66e7 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -372,7 +372,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 #define PG_RETURN_TEXT_P(x)    PG_RETURN_POINTER(x)
 #define PG_RETURN_BPCHAR_P(x)  PG_RETURN_POINTER(x)
 #define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x)
-#define PG_RETURN_HEAPTUPLEHEADER(x)  return HeapTupleHeaderGetDatum(x)
+#define PG_RETURN_HEAPTUPLEHEADER(x)  return PointerGetDatum(x)
 
 
 /*-------------------------------------------------------------------------
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 83e6bc2..b5a825a 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -207,6 +207,8 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  *
  * Macro declarations:
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
+ * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
+ * 		but the input tuple should not contain compressed/external varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
@@ -219,8 +221,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  */
 
 #define HeapTupleGetDatum(tuple)		HeapTupleHeaderGetDatum((tuple)->t_data)
+#define HeapTupleGetRawDatum(tuple)		PointerGetDatum((tuple)->t_data)
 /* obsolete version of above */
 #define TupleGetDatum(_slot, _tuple)	HeapTupleGetDatum(_tuple)
+#define HeapTupleGetDatum(tuple)		PointerGetDatum((tuple)->t_data)
 
 extern TupleDesc RelationNameGetTupleDesc(const char *relname);
 extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 6299adf..cdf79f7 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1127,7 +1127,7 @@ plperl_hash_to_datum(SV *src, TupleDesc td)
 {
 	HeapTuple	tup = plperl_build_tuple_result((HV *) SvRV(src), td);
 
-	return HeapTupleGetDatum(tup);
+	return HeapTupleGetRawDatum(tup);
 }
 
 /*
@@ -3334,7 +3334,7 @@ plperl_return_next_internal(SV *sv)
 										  current_call_data->ret_tdesc);
 
 		if (OidIsValid(current_call_data->cdomain_oid))
-			domain_check(HeapTupleGetDatum(tuple), false,
+			domain_check(HeapTupleGetRawDatum(tuple), false,
 						 current_call_data->cdomain_oid,
 						 &current_call_data->cdomain_info,
 						 rsi->econtext->ecxt_per_query_memory);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index b4c70aa..0519253 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -5326,7 +5326,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
 					elog(ERROR, "row not compatible with its own tupdesc");
 				*typeid = row->rowtupdesc->tdtypeid;
 				*typetypmod = row->rowtupdesc->tdtypmod;
-				*value = HeapTupleGetDatum(tup);
+				*value = HeapTupleGetRawDatum(tup);
 				*isnull = false;
 				MemoryContextSwitchTo(oldcontext);
 				break;
@@ -7300,6 +7300,8 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
+			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
 
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index e118375..b4e710a 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1024,7 +1024,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 
 		tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc,
 									   call_state);
-		retval = HeapTupleGetDatum(tup);
+		retval = HeapTupleGetRawDatum(tup);
 	}
 	else
 		retval = InputFunctionCall(&prodesc->result_in_func,
@@ -3235,7 +3235,7 @@ pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
 
 	/* if result type is domain-over-composite, check domain constraints */
 	if (call_state->prodesc->fn_retisdomain)
-		domain_check(HeapTupleGetDatum(tuple), false,
+		domain_check(HeapTupleGetRawDatum(tuple), false,
 					 call_state->prodesc->result_typid,
 					 &call_state->prodesc->domain_info,
 					 call_state->prodesc->fn_cxt);
diff --git a/src/test/modules/test_predtest/test_predtest.c b/src/test/modules/test_predtest/test_predtest.c
index 9c0aadd..51a023c 100644
--- a/src/test/modules/test_predtest/test_predtest.c
+++ b/src/test/modules/test_predtest/test_predtest.c
@@ -214,5 +214,5 @@ test_predtest(PG_FUNCTION_ARGS)
 	values[6] = BoolGetDatum(s_r_holds);
 	values[7] = BoolGetDatum(w_r_holds);
 
-	PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+	PG_RETURN_DATUM(HeapTupleGetRawDatum(heap_form_tuple(tupdesc, values, nulls)));
 }
-- 
1.8.3.1

