From 043fcf768f069908bce9a26ac7b398edcf7eaa2a Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Tue, 8 Sep 2026 11:46:39 +0900 Subject: [PATCH v15 08/11] More test coverage for TOAST relations with oid8 as chunk_id This covers more area of the code in terms of tests, where oid8 vartags are added: - amcheck - test_decoding - reloptions, with various incorrect patterns for toast_value_type. --- src/test/regress/expected/reloptions.out | 35 +++++++++++++++++++++ src/test/regress/sql/reloptions.sql | 20 ++++++++++++ contrib/amcheck/expected/check_heap.out | 40 ++++++++++++++++++++++++ contrib/amcheck/sql/check_heap.sql | 27 ++++++++++++++++ contrib/test_decoding/expected/toast.out | 39 +++++++++++++++++++++++ contrib/test_decoding/sql/toast.sql | 20 ++++++++++++ 6 files changed, 181 insertions(+) diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out index e3a974f26112..6a50713fb58e 100644 --- a/src/test/regress/expected/reloptions.out +++ b/src/test/regress/expected/reloptions.out @@ -194,6 +194,41 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid; -- Fail on non-existent options in toast namespace CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42); ERROR: unrecognized parameter "not_existing_option" +-- Test toast_value_type. +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.toast_value_type = 'oid8'); +ERROR: unrecognized parameter "toast_value_type" +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast_value_type = 'int8'); +ERROR: invalid value for enum option "toast_value_type": int8 +DETAIL: Valid values are "oid" and "oid8". +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast_value_type = 'oid8'); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test2'::regclass; + reloptions +------------------------- + {toast_value_type=oid8} +(1 row) + +-- The option is only consulted when the TOAST relation is created, so +-- changing it afterwards leaves chunk_id alone. +ALTER TABLE reloptions_test2 SET (toast_value_type = 'oid'); +SELECT a.atttypid::regtype AS chunk_id_type + FROM pg_class AS c, pg_attribute AS a + WHERE c.oid = 'reloptions_test2'::regclass AND + a.attrelid = c.reltoastrelid AND a.attname = 'chunk_id'; + chunk_id_type +--------------- + oid8 +(1 row) + +ALTER TABLE reloptions_test2 RESET (toast_value_type); +DROP TABLE reloptions_test2; +-- Non-supported relkinds. +CREATE INDEX reloptions_test_idx0 ON reloptions_test (s) + WITH (toast_value_type = 'oid8'); +ERROR: unrecognized parameter "toast_value_type" +CREATE TABLE reloptions_test2 (i int) PARTITION BY RANGE (i) + WITH (toast_value_type = 'oid8'); +ERROR: cannot specify storage parameters for a partitioned table +HINT: Specify storage parameters for its leaf partitions instead. -- Mix TOAST & heap DROP TABLE reloptions_test; CREATE TABLE reloptions_test (s VARCHAR) WITH diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql index 680c8bf86148..53cad02f2a28 100644 --- a/src/test/regress/sql/reloptions.sql +++ b/src/test/regress/sql/reloptions.sql @@ -112,6 +112,26 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid; -- Fail on non-existent options in toast namespace CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42); +-- Test toast_value_type. +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.toast_value_type = 'oid8'); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast_value_type = 'int8'); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast_value_type = 'oid8'); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test2'::regclass; +-- The option is only consulted when the TOAST relation is created, so +-- changing it afterwards leaves chunk_id alone. +ALTER TABLE reloptions_test2 SET (toast_value_type = 'oid'); +SELECT a.atttypid::regtype AS chunk_id_type + FROM pg_class AS c, pg_attribute AS a + WHERE c.oid = 'reloptions_test2'::regclass AND + a.attrelid = c.reltoastrelid AND a.attname = 'chunk_id'; +ALTER TABLE reloptions_test2 RESET (toast_value_type); +DROP TABLE reloptions_test2; +-- Non-supported relkinds. +CREATE INDEX reloptions_test_idx0 ON reloptions_test (s) + WITH (toast_value_type = 'oid8'); +CREATE TABLE reloptions_test2 (i int) PARTITION BY RANGE (i) + WITH (toast_value_type = 'oid8'); + -- Mix TOAST & heap DROP TABLE reloptions_test; diff --git a/contrib/amcheck/expected/check_heap.out b/contrib/amcheck/expected/check_heap.out index 979e5e84e723..8686fba712d0 100644 --- a/contrib/amcheck/expected/check_heap.out +++ b/contrib/amcheck/expected/check_heap.out @@ -199,6 +199,44 @@ SELECT * FROM verify_heapam('test_partition', -------+--------+--------+----- (0 rows) +-- Check TOAST relations of both chunk_id: oid and oid8 +CREATE TABLE test_toast_oid (a int, b text) WITH (toast_value_type = 'oid'); +CREATE TABLE test_toast_oid8 (a int, b text) WITH (toast_value_type = 'oid8'); +-- Uncompressed out-of-line values +ALTER TABLE test_toast_oid ALTER COLUMN b SET STORAGE EXTERNAL; +ALTER TABLE test_toast_oid8 ALTER COLUMN b SET STORAGE EXTERNAL; +INSERT INTO test_toast_oid (a, b) + (SELECT gs, repeat('xyzzy', 20000) FROM generate_series(1,5) gs); +INSERT INTO test_toast_oid8 (a, b) + (SELECT gs, repeat('xyzzy', 20000) FROM generate_series(1,5) gs); +-- Compressed out-of-line values. +ALTER TABLE test_toast_oid ALTER COLUMN b SET STORAGE EXTENDED; +ALTER TABLE test_toast_oid8 ALTER COLUMN b SET STORAGE EXTENDED; +INSERT INTO test_toast_oid (a, b) + (SELECT gs, repeat('xyzzy', 20000) FROM generate_series(6,10) gs); +INSERT INTO test_toast_oid8 (a, b) + (SELECT gs, repeat('xyzzy', 20000) FROM generate_series(6,10) gs); +SELECT c.relname, a.atttypid::regtype AS chunk_id_type + FROM pg_class AS c, pg_attribute AS a + WHERE c.relname IN ('test_toast_oid', 'test_toast_oid8') AND + a.attrelid = c.reltoastrelid AND a.attname = 'chunk_id' + ORDER BY c.relname COLLATE "C"; + relname | chunk_id_type +-----------------+--------------- + test_toast_oid | oid + test_toast_oid8 | oid8 +(2 rows) + +SELECT * FROM verify_heapam('test_toast_oid', check_toast := true); + blkno | offnum | attnum | msg +-------+--------+--------+----- +(0 rows) + +SELECT * FROM verify_heapam('test_toast_oid8', check_toast := true); + blkno | offnum | attnum | msg +-------+--------+--------+----- +(0 rows) + -- Check that indexes are rejected CREATE INDEX test_index ON test_partition (a); SELECT * FROM verify_heapam('test_index', @@ -232,6 +270,8 @@ SELECT * FROM verify_heapam('test_foreign_table', ERROR: cannot check relation "test_foreign_table" DETAIL: This operation is not supported for foreign tables. -- cleanup +DROP TABLE test_toast_oid; +DROP TABLE test_toast_oid8; DROP TABLE heaptest; DROP TABLESPACE regress_test_stats_tblspc; DROP TABLE test_partition; diff --git a/contrib/amcheck/sql/check_heap.sql b/contrib/amcheck/sql/check_heap.sql index 1745bae634e5..eb46fe710351 100644 --- a/contrib/amcheck/sql/check_heap.sql +++ b/contrib/amcheck/sql/check_heap.sql @@ -112,6 +112,31 @@ SELECT * FROM verify_heapam('test_partition', startblock := NULL, endblock := NULL); +-- Check TOAST relations of both chunk_id: oid and oid8 +CREATE TABLE test_toast_oid (a int, b text) WITH (toast_value_type = 'oid'); +CREATE TABLE test_toast_oid8 (a int, b text) WITH (toast_value_type = 'oid8'); +-- Uncompressed out-of-line values +ALTER TABLE test_toast_oid ALTER COLUMN b SET STORAGE EXTERNAL; +ALTER TABLE test_toast_oid8 ALTER COLUMN b SET STORAGE EXTERNAL; +INSERT INTO test_toast_oid (a, b) + (SELECT gs, repeat('xyzzy', 20000) FROM generate_series(1,5) gs); +INSERT INTO test_toast_oid8 (a, b) + (SELECT gs, repeat('xyzzy', 20000) FROM generate_series(1,5) gs); +-- Compressed out-of-line values. +ALTER TABLE test_toast_oid ALTER COLUMN b SET STORAGE EXTENDED; +ALTER TABLE test_toast_oid8 ALTER COLUMN b SET STORAGE EXTENDED; +INSERT INTO test_toast_oid (a, b) + (SELECT gs, repeat('xyzzy', 20000) FROM generate_series(6,10) gs); +INSERT INTO test_toast_oid8 (a, b) + (SELECT gs, repeat('xyzzy', 20000) FROM generate_series(6,10) gs); +SELECT c.relname, a.atttypid::regtype AS chunk_id_type + FROM pg_class AS c, pg_attribute AS a + WHERE c.relname IN ('test_toast_oid', 'test_toast_oid8') AND + a.attrelid = c.reltoastrelid AND a.attname = 'chunk_id' + ORDER BY c.relname COLLATE "C"; +SELECT * FROM verify_heapam('test_toast_oid', check_toast := true); +SELECT * FROM verify_heapam('test_toast_oid8', check_toast := true); + -- Check that indexes are rejected CREATE INDEX test_index ON test_partition (a); SELECT * FROM verify_heapam('test_index', @@ -139,6 +164,8 @@ SELECT * FROM verify_heapam('test_foreign_table', endblock := NULL); -- cleanup +DROP TABLE test_toast_oid; +DROP TABLE test_toast_oid8; DROP TABLE heaptest; DROP TABLESPACE regress_test_stats_tblspc; DROP TABLE test_partition; diff --git a/contrib/test_decoding/expected/toast.out b/contrib/test_decoding/expected/toast.out index a757e7dc8d54..8bef6cc32af4 100644 --- a/contrib/test_decoding/expected/toast.out +++ b/contrib/test_decoding/expected/toast.out @@ -382,6 +382,45 @@ SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', COMMIT (4 rows) +-- Test decoding of TOAST values with oid8 +CREATE TABLE toasted_oid8 (id serial primary key, data text) + WITH (toast_value_type = 'oid8'); +-- uncompressed external toast data +ALTER TABLE toasted_oid8 ALTER COLUMN data SET STORAGE EXTERNAL; +INSERT INTO toasted_oid8(data) VALUES (repeat('1234567890', 20000)); +-- compressed external toast data +ALTER TABLE toasted_oid8 ALTER COLUMN data SET STORAGE EXTENDED; +INSERT INTO toasted_oid8(data) VALUES (repeat('1234567890', 20000)); +-- update without changing the toasted column, reported as unchanged +UPDATE toasted_oid8 SET id = id + 10 WHERE id = 1; +-- update changing the toasted column +UPDATE toasted_oid8 SET data = repeat('abcdefghij', 20000) WHERE id = 2; +DELETE FROM toasted_oid8; +-- Check that the values are reassembled in full. +SELECT regexp_replace(data, '^(.{60}).*(.{20})$', '\1..\2') AS shortened, + length(data) AS len + FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + shortened | len +------------------------------------------------------------------------------------+-------- + BEGIN | 5 + table public.toasted_oid8: INSERT: id[integer]:1 data[text]:..2345678901234567890' | 200062 + COMMIT | 6 + BEGIN | 5 + table public.toasted_oid8: INSERT: id[integer]:2 data[text]:..2345678901234567890' | 200062 + COMMIT | 6 + BEGIN | 5 + table public.toasted_oid8: UPDATE: old-key: id[integer]:1 ne..nchanged-toast-datum | 116 + COMMIT | 6 + BEGIN | 5 + table public.toasted_oid8: UPDATE: id[integer]:2 data[text]:..bcdefghijabcdefghij' | 200062 + COMMIT | 6 + BEGIN | 5 + table public.toasted_oid8: DELETE: id[integer]:11 | 49 + table public.toasted_oid8: DELETE: id[integer]:2 | 48 + COMMIT | 6 +(16 rows) + +DROP TABLE toasted_oid8; SELECT pg_drop_replication_slot('regression_slot'); pg_drop_replication_slot -------------------------- diff --git a/contrib/test_decoding/sql/toast.sql b/contrib/test_decoding/sql/toast.sql index d1c560a174d6..8a2c49a5c9a7 100644 --- a/contrib/test_decoding/sql/toast.sql +++ b/contrib/test_decoding/sql/toast.sql @@ -324,4 +324,24 @@ INSERT INTO tbl2 VALUES(1); commit; SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); +-- Test decoding of TOAST values with oid8 +CREATE TABLE toasted_oid8 (id serial primary key, data text) + WITH (toast_value_type = 'oid8'); +-- uncompressed external toast data +ALTER TABLE toasted_oid8 ALTER COLUMN data SET STORAGE EXTERNAL; +INSERT INTO toasted_oid8(data) VALUES (repeat('1234567890', 20000)); +-- compressed external toast data +ALTER TABLE toasted_oid8 ALTER COLUMN data SET STORAGE EXTENDED; +INSERT INTO toasted_oid8(data) VALUES (repeat('1234567890', 20000)); +-- update without changing the toasted column, reported as unchanged +UPDATE toasted_oid8 SET id = id + 10 WHERE id = 1; +-- update changing the toasted column +UPDATE toasted_oid8 SET data = repeat('abcdefghij', 20000) WHERE id = 2; +DELETE FROM toasted_oid8; +-- Check that the values are reassembled in full. +SELECT regexp_replace(data, '^(.{60}).*(.{20})$', '\1..\2') AS shortened, + length(data) AS len + FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); +DROP TABLE toasted_oid8; + SELECT pg_drop_replication_slot('regression_slot'); -- 2.55.0