From b98458770e75dd413d1a720bbd8ebf7ca38378d7 Mon Sep 17 00:00:00 2001
From: Nikhil Kumar Veldanda <nikhilkv@amazon.com>
Date: Mon, 14 Apr 2025 21:53:39 +0000
Subject: [PATCH v11 7/7] Some tests related to zstd dictionary based
 compression and decompression

---
 .../zstd-dictionary-build-cleanup.out         | 661 ++++++++++++++++
 .../zstd-dictionary-build-cleanup_2.out       | 709 ++++++++++++++++++
 .../zstd-dictionary-tblcpy-cleanup.out        | 199 +++++
 .../zstd-dictionary-tblcpy-cleanup_2.out      | 161 ++++
 src/test/isolation/isolation_schedule         |   2 +
 .../specs/zstd-dictionary-build-cleanup.spec  |  40 +
 .../specs/zstd-dictionary-tblcpy-cleanup.spec |  46 ++
 .../regress/expected/compression_zstd.out     | 158 ++++
 .../regress/expected/compression_zstd_1.out   | 251 +++++++
 src/test/regress/parallel_schedule            |   2 +-
 src/test/regress/sql/compression_zstd.sql     | 106 +++
 11 files changed, 2334 insertions(+), 1 deletion(-)
 create mode 100644 src/test/isolation/expected/zstd-dictionary-build-cleanup.out
 create mode 100644 src/test/isolation/expected/zstd-dictionary-build-cleanup_2.out
 create mode 100644 src/test/isolation/expected/zstd-dictionary-tblcpy-cleanup.out
 create mode 100644 src/test/isolation/expected/zstd-dictionary-tblcpy-cleanup_2.out
 create mode 100644 src/test/isolation/specs/zstd-dictionary-build-cleanup.spec
 create mode 100644 src/test/isolation/specs/zstd-dictionary-tblcpy-cleanup.spec
 create mode 100644 src/test/regress/expected/compression_zstd.out
 create mode 100644 src/test/regress/expected/compression_zstd_1.out
 create mode 100644 src/test/regress/sql/compression_zstd.sql

diff --git a/src/test/isolation/expected/zstd-dictionary-build-cleanup.out b/src/test/isolation/expected/zstd-dictionary-build-cleanup.out
new file mode 100644
index 00000000000..f68c27e3713
--- /dev/null
+++ b/src/test/isolation/expected/zstd-dictionary-build-cleanup.out
@@ -0,0 +1,661 @@
+Parsed test spec with 4 sessions
+
+starting permutation: insert_more build run_cleanup run_droptbl
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+
+starting permutation: insert_more build run_droptbl run_cleanup
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_more run_cleanup build run_droptbl
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+
+starting permutation: insert_more run_cleanup run_droptbl build
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_more run_droptbl build run_cleanup
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_more run_droptbl run_cleanup build
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build insert_more run_cleanup run_droptbl
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+
+starting permutation: build insert_more run_droptbl run_cleanup
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build run_cleanup insert_more run_droptbl
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+
+starting permutation: build run_cleanup run_droptbl insert_more
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+
+starting permutation: build run_droptbl insert_more run_cleanup
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build run_droptbl run_cleanup insert_more
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup insert_more build run_droptbl
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+
+starting permutation: run_cleanup insert_more run_droptbl build
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup build insert_more run_droptbl
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+
+starting permutation: run_cleanup build run_droptbl insert_more
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               1
+(1 row)
+
+
+starting permutation: run_cleanup run_droptbl insert_more build
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup run_droptbl build insert_more
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl insert_more build run_cleanup
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl insert_more run_cleanup build
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl build insert_more run_cleanup
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl build run_cleanup insert_more
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl run_cleanup insert_more build
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl run_cleanup build insert_more
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+ERROR:  relation "messages" does not exist
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
diff --git a/src/test/isolation/expected/zstd-dictionary-build-cleanup_2.out b/src/test/isolation/expected/zstd-dictionary-build-cleanup_2.out
new file mode 100644
index 00000000000..b4d5705b969
--- /dev/null
+++ b/src/test/isolation/expected/zstd-dictionary-build-cleanup_2.out
@@ -0,0 +1,709 @@
+Parsed test spec with 4 sessions
+
+starting permutation: insert_more build run_cleanup run_droptbl
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_more build run_droptbl run_cleanup
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_more run_cleanup build run_droptbl
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_more run_cleanup run_droptbl build
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_more run_droptbl build run_cleanup
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_more run_droptbl run_cleanup build
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build insert_more run_cleanup run_droptbl
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build insert_more run_droptbl run_cleanup
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build run_cleanup insert_more run_droptbl
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build run_cleanup run_droptbl insert_more
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build run_droptbl insert_more run_cleanup
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: build run_droptbl run_cleanup insert_more
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup insert_more build run_droptbl
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup insert_more run_droptbl build
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup build insert_more run_droptbl
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup build run_droptbl insert_more
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup run_droptbl insert_more build
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_cleanup run_droptbl build insert_more
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl insert_more build run_cleanup
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl insert_more run_cleanup build
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl build insert_more run_cleanup
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl build run_cleanup insert_more
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl run_cleanup insert_more build
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: run_droptbl run_cleanup build insert_more
+step run_droptbl: 
+  DROP TABLE IF EXISTS messages;
+
+step run_cleanup: 
+  SELECT cleanup_unused_zstd_dictionaries();
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+step build: 
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+step insert_more: 
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+
+ERROR:  relation "messages" does not exist
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
diff --git a/src/test/isolation/expected/zstd-dictionary-tblcpy-cleanup.out b/src/test/isolation/expected/zstd-dictionary-tblcpy-cleanup.out
new file mode 100644
index 00000000000..b0cef50c280
--- /dev/null
+++ b/src/test/isolation/expected/zstd-dictionary-tblcpy-cleanup.out
@@ -0,0 +1,199 @@
+Parsed test spec with 2 sessions
+
+starting permutation: build_dict_and_insert insert_into_othertbl build_dict_and_insert insert_into_othertbl
+step build_dict_and_insert: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+objid        |refobjid
+-------------+--------
+messages_temp|       1
+(1 row)
+
+step insert_into_othertbl: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid        |refobjid
+-------------+--------
+messages_temp|       1
+(1 row)
+
+datum_leak: NOTICE:  table "messages_temp_ctas" does not exist, skipping
+datum_leak: NOTICE:  table "messages_temp_ctas1" does not exist, skipping
+datum_leak: NOTICE:  table "messages_temp_ctas2" does not exist, skipping
+objid              |refobjid
+-------------------+--------
+messages_temp      |       1
+messages_temp_cpy  |       1
+messages_temp_ctas |       1
+messages_temp_ctas1|       1
+messages_temp_ctas2|       1
+(5 rows)
+
+step build_dict_and_insert: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid              |refobjid
+-------------------+--------
+messages_temp      |       1
+messages_temp_cpy  |       1
+messages_temp_ctas |       1
+messages_temp_ctas1|       1
+messages_temp_ctas2|       1
+(5 rows)
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+objid              |refobjid
+-------------------+--------
+messages_temp      |       1
+messages_temp      |       2
+messages_temp_cpy  |       1
+messages_temp_ctas |       1
+messages_temp_ctas1|       1
+messages_temp_ctas2|       1
+(6 rows)
+
+step insert_into_othertbl: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid              |refobjid
+-------------------+--------
+messages_temp      |       1
+messages_temp      |       2
+messages_temp_cpy  |       1
+messages_temp_ctas |       1
+messages_temp_ctas1|       1
+messages_temp_ctas2|       1
+(6 rows)
+
+objid              |refobjid
+-------------------+--------
+messages_temp      |       1
+messages_temp      |       2
+messages_temp_cpy  |       1
+messages_temp_cpy  |       2
+messages_temp_ctas |       1
+messages_temp_ctas |       2
+messages_temp_ctas1|       1
+messages_temp_ctas1|       2
+messages_temp_ctas2|       1
+messages_temp_ctas2|       2
+(10 rows)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               2
+(1 row)
+
+
+starting permutation: insert_into_othertbl insert_into_othertbl build_dict_and_insert build_dict_and_insert
+step insert_into_othertbl: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+datum_leak: NOTICE:  table "messages_temp_ctas" does not exist, skipping
+datum_leak: NOTICE:  table "messages_temp_ctas1" does not exist, skipping
+datum_leak: NOTICE:  table "messages_temp_ctas2" does not exist, skipping
+objid|refobjid
+-----+--------
+(0 rows)
+
+step insert_into_othertbl: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+step build_dict_and_insert: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+objid        |refobjid
+-------------+--------
+messages_temp|       1
+(1 row)
+
+step build_dict_and_insert: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid        |refobjid
+-------------+--------
+messages_temp|       1
+(1 row)
+
+build_zstd_dict_for_attribute
+-----------------------------
+t                            
+(1 row)
+
+objid        |refobjid
+-------------+--------
+messages_temp|       1
+messages_temp|       2
+(2 rows)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               2
+(1 row)
+
diff --git a/src/test/isolation/expected/zstd-dictionary-tblcpy-cleanup_2.out b/src/test/isolation/expected/zstd-dictionary-tblcpy-cleanup_2.out
new file mode 100644
index 00000000000..8229150eb64
--- /dev/null
+++ b/src/test/isolation/expected/zstd-dictionary-tblcpy-cleanup_2.out
@@ -0,0 +1,161 @@
+Parsed test spec with 2 sessions
+
+starting permutation: build_dict_and_insert insert_into_othertbl build_dict_and_insert insert_into_othertbl
+step build_dict_and_insert: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+step insert_into_othertbl: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+datum_leak: NOTICE:  table "messages_temp_ctas" does not exist, skipping
+datum_leak: NOTICE:  table "messages_temp_ctas1" does not exist, skipping
+datum_leak: NOTICE:  table "messages_temp_ctas2" does not exist, skipping
+objid|refobjid
+-----+--------
+(0 rows)
+
+step build_dict_and_insert: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+step insert_into_othertbl: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
+
+starting permutation: insert_into_othertbl insert_into_othertbl build_dict_and_insert build_dict_and_insert
+step insert_into_othertbl: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+datum_leak: NOTICE:  table "messages_temp_ctas" does not exist, skipping
+datum_leak: NOTICE:  table "messages_temp_ctas1" does not exist, skipping
+datum_leak: NOTICE:  table "messages_temp_ctas2" does not exist, skipping
+objid|refobjid
+-----+--------
+(0 rows)
+
+step insert_into_othertbl: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+step build_dict_and_insert: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+step build_dict_and_insert: 
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+build_zstd_dict_for_attribute
+-----------------------------
+f                            
+(1 row)
+
+objid|refobjid
+-----+--------
+(0 rows)
+
+cleanup_unused_zstd_dictionaries
+--------------------------------
+                               0
+(1 row)
+
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index e3c669a29c7..fd446e8f357 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -116,3 +116,5 @@ test: serializable-parallel-2
 test: serializable-parallel-3
 test: matview-write-skew
 test: lock-nowait
+test: zstd-dictionary-build-cleanup
+test: zstd-dictionary-tblcpy-cleanup
\ No newline at end of file
diff --git a/src/test/isolation/specs/zstd-dictionary-build-cleanup.spec b/src/test/isolation/specs/zstd-dictionary-build-cleanup.spec
new file mode 100644
index 00000000000..f526ba79d72
--- /dev/null
+++ b/src/test/isolation/specs/zstd-dictionary-build-cleanup.spec
@@ -0,0 +1,40 @@
+# Zstd compression race test for dictionary build and cleanup methods
+
+setup
+{
+  CREATE TABLE messages (content text);
+  DO $$
+    BEGIN
+      ALTER TABLE messages ALTER COLUMN content SET COMPRESSION ZSTD;
+    EXCEPTION WHEN others THEN
+      RAISE WARNING 'Ignoring zstd compression: this build does not support it.';
+    END
+	$$;
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 10);
+}
+
+teardown
+{
+  DROP TABLE IF EXISTS messages;
+  SELECT cleanup_unused_zstd_dictionaries();
+}
+
+session "insert"
+step "insert_more" {
+  INSERT INTO messages SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+}
+
+session "build_dict"
+step "build" {
+  SELECT build_zstd_dict_for_attribute('messages', 1);
+}
+
+session "cleanup"
+step "run_cleanup" {
+  SELECT cleanup_unused_zstd_dictionaries();
+}
+
+session "droptbls"
+step "run_droptbl" {
+  DROP TABLE IF EXISTS messages;
+}
diff --git a/src/test/isolation/specs/zstd-dictionary-tblcpy-cleanup.spec b/src/test/isolation/specs/zstd-dictionary-tblcpy-cleanup.spec
new file mode 100644
index 00000000000..57aabdf1cf7
--- /dev/null
+++ b/src/test/isolation/specs/zstd-dictionary-tblcpy-cleanup.spec
@@ -0,0 +1,46 @@
+# Zstd compression test for dictionary build, zstd table copy and cleanup method
+
+setup
+{
+  CREATE TABLE messages_temp (content text);
+  DO $$
+    BEGIN
+      ALTER TABLE messages_temp ALTER COLUMN content SET COMPRESSION ZSTD;
+    EXCEPTION WHEN others THEN
+      RAISE WARNING 'Ignoring zstd compression: this build does not support it.';
+    END
+	$$;
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 10);
+  CREATE TABLE messages_temp_cpy (like messages_temp INCLUDING ALL);
+}
+
+teardown
+{
+  DROP TABLE IF EXISTS messages_temp;
+  DROP TABLE IF EXISTS messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas1;
+  DROP TABLE IF EXISTS messages_temp_ctas2;
+  SELECT cleanup_unused_zstd_dictionaries();
+}
+
+session "build_dict_and_insert"
+step "build_dict_and_insert" {
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  SELECT build_zstd_dict_for_attribute('messages_temp', 1);
+  INSERT INTO messages_temp SELECT repeat('Random_', 500) FROM generate_series(1, 5);
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+}
+
+session "datum_leak"
+step "insert_into_othertbl" {
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+  INSERT INTO messages_temp_cpy SELECT * from messages_temp;
+  DROP TABLE IF EXISTS messages_temp_ctas; create table messages_temp_ctas as SELECT * from messages_temp_cpy;
+  DROP TABLE IF EXISTS messages_temp_ctas1; create table messages_temp_ctas1 as SELECT * from messages_temp_ctas;
+  DROP TABLE IF EXISTS messages_temp_ctas2; create table messages_temp_ctas2 as SELECT * from messages_temp_ctas1;
+  SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946 order by objid::regclass, refobjid;
+}
+
+permutation build_dict_and_insert insert_into_othertbl build_dict_and_insert insert_into_othertbl
+permutation insert_into_othertbl insert_into_othertbl build_dict_and_insert build_dict_and_insert
diff --git a/src/test/regress/expected/compression_zstd.out b/src/test/regress/expected/compression_zstd.out
new file mode 100644
index 00000000000..8fee9961abe
--- /dev/null
+++ b/src/test/regress/expected/compression_zstd.out
@@ -0,0 +1,158 @@
+\set HIDE_TOAST_COMPRESSION false
+-- Ensure stable results regardless of the installation's default.
+SET default_toast_compression = 'pglz';
+----------------------------------------------------------------
+-- 1. Create Test Table with Zstd Compression
+----------------------------------------------------------------
+DROP TABLE IF EXISTS cmdata_zstd CASCADE;
+NOTICE:  table "cmdata_zstd" does not exist, skipping
+CREATE TABLE cmdata_zstd (
+    f1 TEXT COMPRESSION zstd
+);
+ERROR:  compression method zstd not supported
+DETAIL:  This functionality requires the server to be built with zstd support.
+----------------------------------------------------------------
+-- 2. Insert Data Rows
+----------------------------------------------------------------
+DO $$
+BEGIN
+  FOR i IN 1..15 LOOP
+    INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 1004));
+  END LOOP;
+END $$;
+ERROR:  relation "cmdata_zstd" does not exist
+LINE 1: INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 10...
+                    ^
+QUERY:  INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 1004))
+CONTEXT:  PL/pgSQL function inline_code_block line 4 at SQL statement
+-- Train dictionary for f1 column and insert more.
+SELECT build_zstd_dict_for_attribute('cmdata_zstd', 1);
+ build_zstd_dict_for_attribute 
+-------------------------------
+ f
+(1 row)
+
+DO $$
+BEGIN
+  FOR i IN 1..15 LOOP
+    INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 1004));
+  END LOOP;
+END $$;
+ERROR:  relation "cmdata_zstd" does not exist
+LINE 1: INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 10...
+                    ^
+QUERY:  INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 1004))
+CONTEXT:  PL/pgSQL function inline_code_block line 4 at SQL statement
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+ objid | refobjid 
+-------+----------
+(0 rows)
+
+select dictid from pg_zstd_dictionaries;
+ dictid 
+--------
+(0 rows)
+
+----------------------------------------------------------------
+-- 3. Verify Table Structure and Compression Settings
+----------------------------------------------------------------
+-- Table Structure for cmdata_zstd
+\d+ cmdata_zstd;
+-- Compression Settings for f1 Column
+SELECT pg_column_compression(f1) AS compression_method,
+       count(*) AS row_count
+FROM cmdata_zstd
+GROUP BY pg_column_compression(f1);
+ERROR:  relation "cmdata_zstd" does not exist
+LINE 3: FROM cmdata_zstd
+             ^
+----------------------------------------------------------------
+-- 4. Decompression Tests
+----------------------------------------------------------------
+--  Decompression Slice Test (Extracting Substrings)
+SELECT SUBSTR(f1, 200, 50) AS data_slice
+FROM cmdata_zstd;
+ERROR:  relation "cmdata_zstd" does not exist
+LINE 2: FROM cmdata_zstd;
+             ^
+----------------------------------------------------------------
+-- 5. Test Table Creation with LIKE INCLUDING COMPRESSION
+----------------------------------------------------------------
+DROP TABLE IF EXISTS cmdata_zstd_2;
+NOTICE:  table "cmdata_zstd_2" does not exist, skipping
+CREATE TABLE cmdata_zstd_2 (LIKE cmdata_zstd INCLUDING COMPRESSION);
+ERROR:  relation "cmdata_zstd" does not exist
+LINE 1: CREATE TABLE cmdata_zstd_2 (LIKE cmdata_zstd INCLUDING COMPR...
+                                         ^
+--  Table Structure for cmdata_zstd_2
+\d+ cmdata_zstd_2;
+DROP TABLE cmdata_zstd_2;
+ERROR:  table "cmdata_zstd_2" does not exist
+----------------------------------------------------------------
+-- 6. Materialized View Compression Test
+----------------------------------------------------------------
+DROP MATERIALIZED VIEW IF EXISTS compressmv_zstd;
+NOTICE:  materialized view "compressmv_zstd" does not exist, skipping
+CREATE MATERIALIZED VIEW compressmv_zstd AS
+  SELECT f1 FROM cmdata_zstd;
+ERROR:  relation "cmdata_zstd" does not exist
+LINE 2:   SELECT f1 FROM cmdata_zstd;
+                         ^
+--  Materialized View Structure for compressmv_zstd
+\d+ compressmv_zstd;
+--  Materialized View Compression Check
+SELECT pg_column_compression(f1) AS mv_compression
+FROM compressmv_zstd;
+ERROR:  relation "compressmv_zstd" does not exist
+LINE 2: FROM compressmv_zstd;
+             ^
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+ objid | refobjid 
+-------+----------
+(0 rows)
+
+select dictid from pg_zstd_dictionaries;
+ dictid 
+--------
+(0 rows)
+
+----------------------------------------------------------------
+-- 7. Additional Updates and Round-Trip Tests
+----------------------------------------------------------------
+-- Update some rows to check if the dictionary remains effective after modifications.
+UPDATE cmdata_zstd
+SET f1 = f1 || ' UPDATED';
+ERROR:  relation "cmdata_zstd" does not exist
+LINE 1: UPDATE cmdata_zstd
+               ^
+--  Verification of Updated Rows
+SELECT SUBSTR(f1, LENGTH(f1) - 7 + 1, 7) AS preview
+FROM cmdata_zstd;
+ERROR:  relation "cmdata_zstd" does not exist
+LINE 2: FROM cmdata_zstd;
+             ^
+----------------------------------------------------------------
+-- 8. Clean Up
+----------------------------------------------------------------
+DROP MATERIALIZED VIEW compressmv_zstd;
+ERROR:  materialized view "compressmv_zstd" does not exist
+DROP TABLE cmdata_zstd;
+ERROR:  table "cmdata_zstd" does not exist
+--cleanup dictionary
+select cleanup_unused_zstd_dictionaries();
+ cleanup_unused_zstd_dictionaries 
+----------------------------------
+                                0
+(1 row)
+
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+ objid | refobjid 
+-------+----------
+(0 rows)
+
+select dictid from pg_zstd_dictionaries;
+ dictid 
+--------
+(0 rows)
+
+\set HIDE_TOAST_COMPRESSION true
diff --git a/src/test/regress/expected/compression_zstd_1.out b/src/test/regress/expected/compression_zstd_1.out
new file mode 100644
index 00000000000..c1a7936e574
--- /dev/null
+++ b/src/test/regress/expected/compression_zstd_1.out
@@ -0,0 +1,251 @@
+\set HIDE_TOAST_COMPRESSION false
+-- Ensure stable results regardless of the installation's default.
+SET default_toast_compression = 'pglz';
+----------------------------------------------------------------
+-- 1. Create Test Table with Zstd Compression
+----------------------------------------------------------------
+DROP TABLE IF EXISTS cmdata_zstd CASCADE;
+NOTICE:  table "cmdata_zstd" does not exist, skipping
+CREATE TABLE cmdata_zstd (
+    f1 TEXT COMPRESSION zstd
+);
+----------------------------------------------------------------
+-- 2. Insert Data Rows
+----------------------------------------------------------------
+DO $$
+BEGIN
+  FOR i IN 1..15 LOOP
+    INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 1004));
+  END LOOP;
+END $$;
+-- Train dictionary for f1 column and insert more.
+SELECT build_zstd_dict_for_attribute('cmdata_zstd', 1);
+ build_zstd_dict_for_attribute 
+-------------------------------
+ t
+(1 row)
+
+DO $$
+BEGIN
+  FOR i IN 1..15 LOOP
+    INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 1004));
+  END LOOP;
+END $$;
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+    objid    | refobjid 
+-------------+----------
+ cmdata_zstd |        1
+(1 row)
+
+select dictid from pg_zstd_dictionaries;
+ dictid 
+--------
+      1
+(1 row)
+
+----------------------------------------------------------------
+-- 3. Verify Table Structure and Compression Settings
+----------------------------------------------------------------
+-- Table Structure for cmdata_zstd
+\d+ cmdata_zstd;
+                                      Table "public.cmdata_zstd"
+ Column | Type | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1     | text |           |          |         | extended | zstd        |              | 
+
+-- Compression Settings for f1 Column
+SELECT pg_column_compression(f1) AS compression_method,
+       count(*) AS row_count
+FROM cmdata_zstd
+GROUP BY pg_column_compression(f1);
+ compression_method | row_count 
+--------------------+-----------
+ zstd               |        30
+(1 row)
+
+----------------------------------------------------------------
+-- 4. Decompression Tests
+----------------------------------------------------------------
+--  Decompression Slice Test (Extracting Substrings)
+SELECT SUBSTR(f1, 200, 50) AS data_slice
+FROM cmdata_zstd;
+                     data_slice                     
+----------------------------------------------------
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+ 01234567890123456789012345678901234567890123456789
+(30 rows)
+
+----------------------------------------------------------------
+-- 5. Test Table Creation with LIKE INCLUDING COMPRESSION
+----------------------------------------------------------------
+DROP TABLE IF EXISTS cmdata_zstd_2;
+NOTICE:  table "cmdata_zstd_2" does not exist, skipping
+CREATE TABLE cmdata_zstd_2 (LIKE cmdata_zstd INCLUDING COMPRESSION);
+--  Table Structure for cmdata_zstd_2
+\d+ cmdata_zstd_2;
+                                     Table "public.cmdata_zstd_2"
+ Column | Type | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1     | text |           |          |         | extended | zstd        |              | 
+
+DROP TABLE cmdata_zstd_2;
+----------------------------------------------------------------
+-- 6. Materialized View Compression Test
+----------------------------------------------------------------
+DROP MATERIALIZED VIEW IF EXISTS compressmv_zstd;
+NOTICE:  materialized view "compressmv_zstd" does not exist, skipping
+CREATE MATERIALIZED VIEW compressmv_zstd AS
+  SELECT f1 FROM cmdata_zstd;
+--  Materialized View Structure for compressmv_zstd
+\d+ compressmv_zstd;
+                              Materialized view "public.compressmv_zstd"
+ Column | Type | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
+--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
+ f1     | text |           |          |         | extended |             |              | 
+View definition:
+ SELECT f1
+   FROM cmdata_zstd;
+
+--  Materialized View Compression Check
+SELECT pg_column_compression(f1) AS mv_compression
+FROM compressmv_zstd;
+ mv_compression 
+----------------
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+ zstd
+(30 rows)
+
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+    objid    | refobjid 
+-------------+----------
+ cmdata_zstd |        1
+(1 row)
+
+select dictid from pg_zstd_dictionaries;
+ dictid 
+--------
+      1
+(1 row)
+
+----------------------------------------------------------------
+-- 7. Additional Updates and Round-Trip Tests
+----------------------------------------------------------------
+-- Update some rows to check if the dictionary remains effective after modifications.
+UPDATE cmdata_zstd
+SET f1 = f1 || ' UPDATED';
+--  Verification of Updated Rows
+SELECT SUBSTR(f1, LENGTH(f1) - 7 + 1, 7) AS preview
+FROM cmdata_zstd;
+ preview 
+---------
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+ UPDATED
+(30 rows)
+
+----------------------------------------------------------------
+-- 8. Clean Up
+----------------------------------------------------------------
+DROP MATERIALIZED VIEW compressmv_zstd;
+DROP TABLE cmdata_zstd;
+--cleanup dictionary
+select cleanup_unused_zstd_dictionaries();
+ cleanup_unused_zstd_dictionaries 
+----------------------------------
+                                1
+(1 row)
+
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+ objid | refobjid 
+-------+----------
+(0 rows)
+
+select dictid from pg_zstd_dictionaries;
+ dictid 
+--------
+(0 rows)
+
+\set HIDE_TOAST_COMPRESSION true
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 0f38caa0d24..4df32357d01 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -119,7 +119,7 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare conversion tr
 # The stats test resets stats, so nothing else needing stats access can be in
 # this group.
 # ----------
-test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain compression memoize stats predicate numa
+test: partition_join partition_prune reloptions hash_part indexing partition_aggregate partition_info tuplesort explain compression compression_zstd memoize stats predicate numa
 
 # event_trigger depends on create_am and cannot run concurrently with
 # any test that runs DDL
diff --git a/src/test/regress/sql/compression_zstd.sql b/src/test/regress/sql/compression_zstd.sql
new file mode 100644
index 00000000000..894e72da0cb
--- /dev/null
+++ b/src/test/regress/sql/compression_zstd.sql
@@ -0,0 +1,106 @@
+\set HIDE_TOAST_COMPRESSION false
+
+-- Ensure stable results regardless of the installation's default.
+SET default_toast_compression = 'pglz';
+
+----------------------------------------------------------------
+-- 1. Create Test Table with Zstd Compression
+----------------------------------------------------------------
+DROP TABLE IF EXISTS cmdata_zstd CASCADE;
+CREATE TABLE cmdata_zstd (
+    f1 TEXT COMPRESSION zstd
+);
+
+----------------------------------------------------------------
+-- 2. Insert Data Rows
+----------------------------------------------------------------
+DO $$
+BEGIN
+  FOR i IN 1..15 LOOP
+    INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 1004));
+  END LOOP;
+END $$;
+
+-- Train dictionary for f1 column and insert more.
+SELECT build_zstd_dict_for_attribute('cmdata_zstd', 1);
+
+DO $$
+BEGIN
+  FOR i IN 1..15 LOOP
+    INSERT INTO cmdata_zstd (f1) VALUES (repeat('1234567890', 1004));
+  END LOOP;
+END $$;
+
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+
+select dictid from pg_zstd_dictionaries;
+
+----------------------------------------------------------------
+-- 3. Verify Table Structure and Compression Settings
+----------------------------------------------------------------
+-- Table Structure for cmdata_zstd
+\d+ cmdata_zstd;
+
+-- Compression Settings for f1 Column
+SELECT pg_column_compression(f1) AS compression_method,
+       count(*) AS row_count
+FROM cmdata_zstd
+GROUP BY pg_column_compression(f1);
+
+----------------------------------------------------------------
+-- 4. Decompression Tests
+----------------------------------------------------------------
+--  Decompression Slice Test (Extracting Substrings)
+SELECT SUBSTR(f1, 200, 50) AS data_slice
+FROM cmdata_zstd;
+
+----------------------------------------------------------------
+-- 5. Test Table Creation with LIKE INCLUDING COMPRESSION
+----------------------------------------------------------------
+DROP TABLE IF EXISTS cmdata_zstd_2;
+CREATE TABLE cmdata_zstd_2 (LIKE cmdata_zstd INCLUDING COMPRESSION);
+--  Table Structure for cmdata_zstd_2
+\d+ cmdata_zstd_2;
+DROP TABLE cmdata_zstd_2;
+
+----------------------------------------------------------------
+-- 6. Materialized View Compression Test
+----------------------------------------------------------------
+DROP MATERIALIZED VIEW IF EXISTS compressmv_zstd;
+CREATE MATERIALIZED VIEW compressmv_zstd AS
+  SELECT f1 FROM cmdata_zstd;
+--  Materialized View Structure for compressmv_zstd
+\d+ compressmv_zstd;
+--  Materialized View Compression Check
+SELECT pg_column_compression(f1) AS mv_compression
+FROM compressmv_zstd;
+
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+
+select dictid from pg_zstd_dictionaries;
+
+----------------------------------------------------------------
+-- 7. Additional Updates and Round-Trip Tests
+----------------------------------------------------------------
+-- Update some rows to check if the dictionary remains effective after modifications.
+UPDATE cmdata_zstd
+SET f1 = f1 || ' UPDATED';
+
+--  Verification of Updated Rows
+SELECT SUBSTR(f1, LENGTH(f1) - 7 + 1, 7) AS preview
+FROM cmdata_zstd;
+
+----------------------------------------------------------------
+-- 8. Clean Up
+----------------------------------------------------------------
+DROP MATERIALIZED VIEW compressmv_zstd;
+DROP TABLE cmdata_zstd;
+
+--cleanup dictionary
+select cleanup_unused_zstd_dictionaries();
+
+SELECT objid::regclass, refobjid from pg_depend where refclassid = 9946;
+
+select dictid from pg_zstd_dictionaries;
+
+\set HIDE_TOAST_COMPRESSION true
-- 
2.47.1

