From a6c4cfe8e46b9aa21895b06fb18c24798d661867 Mon Sep 17 00:00:00 2001
From: Antonin Houska <ah@cybertec.at>
Date: Mon, 20 Apr 2026 13:30:04 +0200
Subject: [PATCH] Enhance tests for TOAST processing by REPACK.

It seems appropriate to test more kinds of TOASTing than we used to.
---
 .../expected/repack_toast.out                 | 11 +--
 .../injection_points/specs/repack_toast.spec  | 71 +++++++++++++++----
 2 files changed, 63 insertions(+), 19 deletions(-)

diff --git a/src/test/modules/injection_points/expected/repack_toast.out b/src/test/modules/injection_points/expected/repack_toast.out
index b56dde134f8..063d730ba8f 100644
--- a/src/test/modules/injection_points/expected/repack_toast.out
+++ b/src/test/modules/injection_points/expected/repack_toast.out
@@ -10,10 +10,13 @@ step wait_before_lock:
 	REPACK (CONCURRENTLY) repack_test;
  <waiting ...>
 step change: 
-	UPDATE repack_test SET j=get_long_string() where i=2;
-	DELETE FROM repack_test WHERE i=3;
-	INSERT INTO repack_test(i, j) VALUES (4, get_long_string());
-	UPDATE repack_test SET i=3 where i=1;
+	DELETE FROM repack_test WHERE i=1;
+	INSERT INTO repack_test(i, j) VALUES (1, gen_external());
+	UPDATE repack_test SET i=10 where i=2;
+	UPDATE repack_test SET j=gen_external() where i=3;
+	UPDATE repack_test SET j=gen_compressible(1) where i=4;
+	UPDATE repack_test SET j=gen_compressible_external(2) where i=5;
+	UPDATE repack_test SET j=gen_inline() where i=6;
 
 step check2: 
 	INSERT INTO relfilenodes(node)
diff --git a/src/test/modules/injection_points/specs/repack_toast.spec b/src/test/modules/injection_points/specs/repack_toast.spec
index b878b198971..9a6dc2eb124 100644
--- a/src/test/modules/injection_points/specs/repack_toast.spec
+++ b/src/test/modules/injection_points/specs/repack_toast.spec
@@ -5,17 +5,43 @@ setup
 {
 	CREATE EXTENSION injection_points;
 
-	-- Return a string that needs to be TOASTed.
-	CREATE FUNCTION get_long_string()
+	-- Generate text consisting of repeated strings so that it can be
+	-- compressed easily.
+	CREATE FUNCTION gen_compressible(seed int)
+	RETURNS text
+	LANGUAGE sql IMMUTABLE as $$
+		SELECT repeat(md5((seed * 1000)::text), 5000);
+	$$;
+
+	-- Like above, but too big even after compression.
+	CREATE FUNCTION gen_compressible_external(seed int)
+	RETURNS text
+	LANGUAGE sql IMMUTABLE as $$
+		SELECT repeat(md5((seed * 1000)::text), 10000);
+	$$;
+
+	-- Generate a string of random characters that is not likely to be
+	-- compressed, but is big enough to be stored externally.
+	CREATE FUNCTION gen_external()
 	RETURNS text
 	LANGUAGE sql as $$
 		SELECT string_agg(chr(65 + trunc(25 * random())::int), '')
 		FROM generate_series(1, 2048) s(x);
 	$$;
 
+	-- Not compressible like above, but small enough to stay in-line.
+	CREATE FUNCTION gen_inline()
+	RETURNS text
+	LANGUAGE sql as $$
+		SELECT string_agg(chr(65 + trunc(25 * random())::int), '')
+		FROM generate_series(1, 1024) s(x);
+	$$;
+
 	CREATE TABLE repack_test(i int PRIMARY KEY, j text);
-	INSERT INTO repack_test(i, j) VALUES (1, get_long_string()),
-		(2, get_long_string()), (3, get_long_string());
+	INSERT INTO repack_test(i, j) VALUES
+		(1, gen_external()), (2, gen_external()),
+		(3, gen_external()), (4, gen_external()),
+		(5, gen_external()), (6, gen_external());
 
 	CREATE TABLE relfilenodes(node oid);
 
@@ -27,7 +53,10 @@ teardown
 {
 	DROP TABLE repack_test;
 	DROP EXTENSION injection_points;
-	DROP FUNCTION get_long_string();
+	DROP FUNCTION gen_compressible(int);
+	DROP FUNCTION gen_compressible_external(int);
+	DROP FUNCTION gen_external();
+	DROP FUNCTION gen_inline();
 
 	DROP TABLE relfilenodes;
 	DROP TABLE data_s1;
@@ -74,17 +103,29 @@ teardown
 
 session s2
 step change
-# Separately test UPDATE of both plain ("i") and TOASTed ("j") attribute. In
-# the first case, the new tuple we get from reorderbuffer.c contains "j" as a
-# TOAST pointer, which we need to update so it points to the new heap. In the
-# latter case, we receive "j" as "external indirect" value - here we test that
-# the decoding worker writes the tuple to a file correctly and that the
-# backend executing REPACK manages to restore it.
+# Separately test different kinds of UPDATE:
+#
+# 1. The new tuple we get from reorderbuffer.c contains "j" as a TOAST
+# pointer. However, as the TOASTed value stays unchanged, it points to the old
+# tuple, so we need to adjust it.
+#
+# 2. We receive "j" as "external indirect" value - here we test that the
+# decoding worker writes the tuple to a file correctly and that the backend
+# executing REPACK manages to restore it.
+#
+# 3. Value of "j" is compressed.
+#
+# 4. Value of "j" is compressed and externalized.
+#
+# 5. Value of "j" stays in-line.
 {
-	UPDATE repack_test SET j=get_long_string() where i=2;
-	DELETE FROM repack_test WHERE i=3;
-	INSERT INTO repack_test(i, j) VALUES (4, get_long_string());
-	UPDATE repack_test SET i=3 where i=1;
+	DELETE FROM repack_test WHERE i=1;
+	INSERT INTO repack_test(i, j) VALUES (1, gen_external());
+	UPDATE repack_test SET i=10 where i=2;
+	UPDATE repack_test SET j=gen_external() where i=3;
+	UPDATE repack_test SET j=gen_compressible(1) where i=4;
+	UPDATE repack_test SET j=gen_compressible_external(2) where i=5;
+	UPDATE repack_test SET j=gen_inline() where i=6;
 }
 # Check the table from the perspective of s2.
 step check2
-- 
2.47.3

