From 35ec26478880efbff8eeac230f0af3cb057d345c Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Thu, 19 Jun 2025 11:15:48 +0900
Subject: [PATCH v3 11/14] Add tests for TOAST relations with bigint as value
 type

This adds coverage for relations created with default_toast_type =
'int8', for external TOAST pointers both compressed and uncompressed.
---
 src/test/regress/expected/strings.out | 238 ++++++++++++++++++++++----
 src/test/regress/sql/strings.sql      | 142 +++++++++++----
 2 files changed, 305 insertions(+), 75 deletions(-)

diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 1bfd33de3f3c..9cf643c9031a 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -1945,21 +1945,40 @@ SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar";
 (1 row)
 
 --
--- test substr with toasted text values
+-- test substr with toasted text values, for all types of TOAST relations
+-- supported.
 --
-CREATE TABLE toasttest(f1 text);
-insert into toasttest values(repeat('1234567890',10000));
-insert into toasttest values(repeat('1234567890',10000));
+SET default_toast_type = 'oid';
+CREATE TABLE toasttest_oid(f1 text);
+SET default_toast_type = 'int8';
+CREATE TABLE toasttest_int8(f1 text);
+RESET default_toast_type;
+insert into toasttest_oid values(repeat('1234567890',10000));
+insert into toasttest_oid values(repeat('1234567890',10000));
+insert into toasttest_int8 values(repeat('1234567890',10000));
+insert into toasttest_int8 values(repeat('1234567890',10000));
 --
 -- Ensure that some values are uncompressed, to test the faster substring
 -- operation used in that case
 --
-alter table toasttest alter column f1 set storage external;
-insert into toasttest values(repeat('1234567890',10000));
-insert into toasttest values(repeat('1234567890',10000));
+alter table toasttest_oid alter column f1 set storage external;
+insert into toasttest_oid values(repeat('1234567890',10000));
+insert into toasttest_oid values(repeat('1234567890',10000));
+alter table toasttest_int8 alter column f1 set storage external;
+insert into toasttest_int8 values(repeat('1234567890',10000));
+insert into toasttest_int8 values(repeat('1234567890',10000));
 -- If the starting position is zero or less, then return from the start of the string
 -- adjusting the length to be consistent with the "negative start" per SQL.
-SELECT substr(f1, -1, 5) from toasttest;
+SELECT substr(f1, -1, 5) from toasttest_oid;
+ substr 
+--------
+ 123
+ 123
+ 123
+ 123
+(4 rows)
+
+SELECT substr(f1, -1, 5) from toasttest_int8;
  substr 
 --------
  123
@@ -1969,11 +1988,22 @@ SELECT substr(f1, -1, 5) from toasttest;
 (4 rows)
 
 -- If the length is less than zero, an ERROR is thrown.
-SELECT substr(f1, 5, -1) from toasttest;
+SELECT substr(f1, 5, -1) from toasttest_oid;
+ERROR:  negative substring length not allowed
+SELECT substr(f1, 5, -1) from toasttest_int8;
 ERROR:  negative substring length not allowed
 -- If no third argument (length) is provided, the length to the end of the
 -- string is assumed.
-SELECT substr(f1, 99995) from toasttest;
+SELECT substr(f1, 99995) from toasttest_oid;
+ substr 
+--------
+ 567890
+ 567890
+ 567890
+ 567890
+(4 rows)
+
+SELECT substr(f1, 99995) from toasttest_int8;
  substr 
 --------
  567890
@@ -1984,7 +2014,7 @@ SELECT substr(f1, 99995) from toasttest;
 
 -- If start plus length is > string length, the result is truncated to
 -- string length
-SELECT substr(f1, 99995, 10) from toasttest;
+SELECT substr(f1, 99995, 10) from toasttest_oid;
  substr 
 --------
  567890
@@ -1993,50 +2023,108 @@ SELECT substr(f1, 99995, 10) from toasttest;
  567890
 (4 rows)
 
-TRUNCATE TABLE toasttest;
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
+SELECT substr(f1, 99995, 10) from toasttest_int8;
+ substr 
+--------
+ 567890
+ 567890
+ 567890
+ 567890
+(4 rows)
+
+-- TRUNCATE cases for TOAST relations with OID values.
+TRUNCATE TABLE toasttest_oid;
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
 -- expect >0 blocks
 SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
-  FROM pg_class where relname = 'toasttest';
+  FROM pg_class where relname = 'toasttest_oid';
  is_empty 
 ----------
  f
 (1 row)
 
-TRUNCATE TABLE toasttest;
-ALTER TABLE toasttest set (toast_tuple_target = 4080);
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
+TRUNCATE TABLE toasttest_oid;
+ALTER TABLE toasttest_oid set (toast_tuple_target = 4080);
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
 -- expect 0 blocks
 SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
-  FROM pg_class where relname = 'toasttest';
+  FROM pg_class where relname = 'toasttest_oid';
  is_empty 
 ----------
  t
 (1 row)
 
-DROP TABLE toasttest;
+DROP TABLE toasttest_oid;
+-- TRUNCATE cases for TOAST relation with int8 values.
+TRUNCATE TABLE toasttest_int8;
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+-- expect >0 blocks
+SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
+  FROM pg_class where relname = 'toasttest_int8';
+ is_empty 
+----------
+ f
+(1 row)
+
+TRUNCATE TABLE toasttest_int8;
+ALTER TABLE toasttest_int8 set (toast_tuple_target = 4080);
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+-- expect 0 blocks
+SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
+  FROM pg_class where relname = 'toasttest_int8';
+ is_empty 
+----------
+ t
+(1 row)
+
+DROP TABLE toasttest_int8;
 --
--- test substr with toasted bytea values
+-- test substr with toasted bytea values, for all types of TOAST relations
+-- supported.
 --
-CREATE TABLE toasttest(f1 bytea);
-insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
-insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
+SET default_toast_type = 'oid';
+CREATE TABLE toasttest_oid(f1 bytea);
+SET default_toast_type = 'int8';
+CREATE TABLE toasttest_int8(f1 bytea);
+RESET default_toast_type;
+insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_int8 values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_int8 values(decode(repeat('1234567890',10000),'escape'));
 --
 -- Ensure that some values are uncompressed, to test the faster substring
 -- operation used in that case
 --
-alter table toasttest alter column f1 set storage external;
-insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
-insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
+alter table toasttest_oid alter column f1 set storage external;
+insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
+alter table toasttest_int8 alter column f1 set storage external;
+insert into toasttest_int8 values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_int8 values(decode(repeat('1234567890',10000),'escape'));
 -- If the starting position is zero or less, then return from the start of the string
 -- adjusting the length to be consistent with the "negative start" per SQL.
-SELECT substr(f1, -1, 5) from toasttest;
+SELECT substr(f1, -1, 5) from toasttest_oid;
+ substr 
+--------
+ 123
+ 123
+ 123
+ 123
+(4 rows)
+
+SELECT substr(f1, -1, 5) from toasttest_int8;
  substr 
 --------
  123
@@ -2046,11 +2134,22 @@ SELECT substr(f1, -1, 5) from toasttest;
 (4 rows)
 
 -- If the length is less than zero, an ERROR is thrown.
-SELECT substr(f1, 5, -1) from toasttest;
+SELECT substr(f1, 5, -1) from toasttest_oid;
+ERROR:  negative substring length not allowed
+SELECT substr(f1, 5, -1) from toasttest_int8;
 ERROR:  negative substring length not allowed
 -- If no third argument (length) is provided, the length to the end of the
 -- string is assumed.
-SELECT substr(f1, 99995) from toasttest;
+SELECT substr(f1, 99995) from toasttest_oid;
+ substr 
+--------
+ 567890
+ 567890
+ 567890
+ 567890
+(4 rows)
+
+SELECT substr(f1, 99995) from toasttest_int8;
  substr 
 --------
  567890
@@ -2061,7 +2160,7 @@ SELECT substr(f1, 99995) from toasttest;
 
 -- If start plus length is > string length, the result is truncated to
 -- string length
-SELECT substr(f1, 99995, 10) from toasttest;
+SELECT substr(f1, 99995, 10) from toasttest_oid;
  substr 
 --------
  567890
@@ -2070,7 +2169,72 @@ SELECT substr(f1, 99995, 10) from toasttest;
  567890
 (4 rows)
 
-DROP TABLE toasttest;
+SELECT substr(f1, 99995, 10) from toasttest_int8;
+ substr 
+--------
+ 567890
+ 567890
+ 567890
+ 567890
+(4 rows)
+
+-- A relation rewrite leaves the TOAST value attributes unchanged.
+VACUUM FULL toasttest_oid;
+VACUUM FULL toasttest_int8;
+SELECT c1.relname, a.atttypid::regtype
+  FROM pg_attribute AS a,
+       pg_class AS c1,
+       pg_class AS c2
+  WHERE
+       c1.relname IN ('toasttest_oid', 'toasttest_int8') AND
+       c1.reltoastrelid = c2.oid AND
+       a.attrelid = c2.oid AND
+       a.attname = 'chunk_id'
+  ORDER BY c1.relname COLLATE "C";
+    relname     | atttypid 
+----------------+----------
+ toasttest_int8 | bigint
+ toasttest_oid  | oid
+(2 rows)
+
+-- Check that data slices are still accessible.
+SELECT substr(f1, 99995) from toasttest_oid;
+ substr 
+--------
+ 567890
+ 567890
+ 567890
+ 567890
+(4 rows)
+
+SELECT substr(f1, 99995) from toasttest_int8;
+ substr 
+--------
+ 567890
+ 567890
+ 567890
+ 567890
+(4 rows)
+
+SELECT substr(f1, 99995, 10) from toasttest_oid;
+ substr 
+--------
+ 567890
+ 567890
+ 567890
+ 567890
+(4 rows)
+
+SELECT substr(f1, 99995, 10) from toasttest_int8;
+ substr 
+--------
+ 567890
+ 567890
+ 567890
+ 567890
+(4 rows)
+
+DROP TABLE toasttest_oid, toasttest_int8;
 -- test internally compressing datums
 -- this tests compressing a datum to a very small size which exercises a
 -- corner case in packed-varlena handling: even though small, the compressed
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index 92c445c24396..d4606bc04fe2 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -553,89 +553,155 @@ SELECT text 'text' || char(20) ' and characters' AS "Concat text to char";
 SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar";
 
 --
--- test substr with toasted text values
+-- test substr with toasted text values, for all types of TOAST relations
+-- supported.
 --
-CREATE TABLE toasttest(f1 text);
+SET default_toast_type = 'oid';
+CREATE TABLE toasttest_oid(f1 text);
+SET default_toast_type = 'int8';
+CREATE TABLE toasttest_int8(f1 text);
+RESET default_toast_type;
 
-insert into toasttest values(repeat('1234567890',10000));
-insert into toasttest values(repeat('1234567890',10000));
+insert into toasttest_oid values(repeat('1234567890',10000));
+insert into toasttest_oid values(repeat('1234567890',10000));
+insert into toasttest_int8 values(repeat('1234567890',10000));
+insert into toasttest_int8 values(repeat('1234567890',10000));
 
 --
 -- Ensure that some values are uncompressed, to test the faster substring
 -- operation used in that case
 --
-alter table toasttest alter column f1 set storage external;
-insert into toasttest values(repeat('1234567890',10000));
-insert into toasttest values(repeat('1234567890',10000));
+alter table toasttest_oid alter column f1 set storage external;
+insert into toasttest_oid values(repeat('1234567890',10000));
+insert into toasttest_oid values(repeat('1234567890',10000));
+alter table toasttest_int8 alter column f1 set storage external;
+insert into toasttest_int8 values(repeat('1234567890',10000));
+insert into toasttest_int8 values(repeat('1234567890',10000));
 
 -- If the starting position is zero or less, then return from the start of the string
 -- adjusting the length to be consistent with the "negative start" per SQL.
-SELECT substr(f1, -1, 5) from toasttest;
+SELECT substr(f1, -1, 5) from toasttest_oid;
+SELECT substr(f1, -1, 5) from toasttest_int8;
 
 -- If the length is less than zero, an ERROR is thrown.
-SELECT substr(f1, 5, -1) from toasttest;
+SELECT substr(f1, 5, -1) from toasttest_oid;
+SELECT substr(f1, 5, -1) from toasttest_int8;
 
 -- If no third argument (length) is provided, the length to the end of the
 -- string is assumed.
-SELECT substr(f1, 99995) from toasttest;
+SELECT substr(f1, 99995) from toasttest_oid;
+SELECT substr(f1, 99995) from toasttest_int8;
 
 -- If start plus length is > string length, the result is truncated to
 -- string length
-SELECT substr(f1, 99995, 10) from toasttest;
+SELECT substr(f1, 99995, 10) from toasttest_oid;
+SELECT substr(f1, 99995, 10) from toasttest_int8;
 
-TRUNCATE TABLE toasttest;
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
+-- TRUNCATE cases for TOAST relations with OID values.
+TRUNCATE TABLE toasttest_oid;
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
 -- expect >0 blocks
 SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
-  FROM pg_class where relname = 'toasttest';
-
-TRUNCATE TABLE toasttest;
-ALTER TABLE toasttest set (toast_tuple_target = 4080);
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
-INSERT INTO toasttest values (repeat('1234567890',300));
+  FROM pg_class where relname = 'toasttest_oid';
+TRUNCATE TABLE toasttest_oid;
+ALTER TABLE toasttest_oid set (toast_tuple_target = 4080);
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
+INSERT INTO toasttest_oid values (repeat('1234567890',300));
 -- expect 0 blocks
 SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
-  FROM pg_class where relname = 'toasttest';
+  FROM pg_class where relname = 'toasttest_oid';
+DROP TABLE toasttest_oid;
 
-DROP TABLE toasttest;
+-- TRUNCATE cases for TOAST relation with int8 values.
+TRUNCATE TABLE toasttest_int8;
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+-- expect >0 blocks
+SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
+  FROM pg_class where relname = 'toasttest_int8';
+TRUNCATE TABLE toasttest_int8;
+ALTER TABLE toasttest_int8 set (toast_tuple_target = 4080);
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+INSERT INTO toasttest_int8 values (repeat('1234567890',300));
+-- expect 0 blocks
+SELECT pg_relation_size(reltoastrelid) = 0 AS is_empty
+  FROM pg_class where relname = 'toasttest_int8';
+DROP TABLE toasttest_int8;
 
 --
--- test substr with toasted bytea values
+-- test substr with toasted bytea values, for all types of TOAST relations
+-- supported.
 --
-CREATE TABLE toasttest(f1 bytea);
+SET default_toast_type = 'oid';
+CREATE TABLE toasttest_oid(f1 bytea);
+SET default_toast_type = 'int8';
+CREATE TABLE toasttest_int8(f1 bytea);
+RESET default_toast_type;
 
-insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
-insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_int8 values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_int8 values(decode(repeat('1234567890',10000),'escape'));
 
 --
 -- Ensure that some values are uncompressed, to test the faster substring
 -- operation used in that case
 --
-alter table toasttest alter column f1 set storage external;
-insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
-insert into toasttest values(decode(repeat('1234567890',10000),'escape'));
+alter table toasttest_oid alter column f1 set storage external;
+insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_oid values(decode(repeat('1234567890',10000),'escape'));
+alter table toasttest_int8 alter column f1 set storage external;
+insert into toasttest_int8 values(decode(repeat('1234567890',10000),'escape'));
+insert into toasttest_int8 values(decode(repeat('1234567890',10000),'escape'));
 
 -- If the starting position is zero or less, then return from the start of the string
 -- adjusting the length to be consistent with the "negative start" per SQL.
-SELECT substr(f1, -1, 5) from toasttest;
+SELECT substr(f1, -1, 5) from toasttest_oid;
+SELECT substr(f1, -1, 5) from toasttest_int8;
 
 -- If the length is less than zero, an ERROR is thrown.
-SELECT substr(f1, 5, -1) from toasttest;
+SELECT substr(f1, 5, -1) from toasttest_oid;
+SELECT substr(f1, 5, -1) from toasttest_int8;
 
 -- If no third argument (length) is provided, the length to the end of the
 -- string is assumed.
-SELECT substr(f1, 99995) from toasttest;
+SELECT substr(f1, 99995) from toasttest_oid;
+SELECT substr(f1, 99995) from toasttest_int8;
 
 -- If start plus length is > string length, the result is truncated to
 -- string length
-SELECT substr(f1, 99995, 10) from toasttest;
+SELECT substr(f1, 99995, 10) from toasttest_oid;
+SELECT substr(f1, 99995, 10) from toasttest_int8;
 
-DROP TABLE toasttest;
+-- A relation rewrite leaves the TOAST value attributes unchanged.
+VACUUM FULL toasttest_oid;
+VACUUM FULL toasttest_int8;
+SELECT c1.relname, a.atttypid::regtype
+  FROM pg_attribute AS a,
+       pg_class AS c1,
+       pg_class AS c2
+  WHERE
+       c1.relname IN ('toasttest_oid', 'toasttest_int8') AND
+       c1.reltoastrelid = c2.oid AND
+       a.attrelid = c2.oid AND
+       a.attname = 'chunk_id'
+  ORDER BY c1.relname COLLATE "C";
+-- Check that data slices are still accessible.
+SELECT substr(f1, 99995) from toasttest_oid;
+SELECT substr(f1, 99995) from toasttest_int8;
+SELECT substr(f1, 99995, 10) from toasttest_oid;
+SELECT substr(f1, 99995, 10) from toasttest_int8;
+
+DROP TABLE toasttest_oid, toasttest_int8;
 
 -- test internally compressing datums
 
-- 
2.50.0

