From d9abd9e1050db4c2fcdce6fcd8dc0ede6769db16 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 26 Jan 2026 16:32:33 +0900 Subject: [PATCH] Add test for MAINTAIN permission with pg_restore_extended_stats() Like its cousin functions for the restore of relation and attribute stats, pg_restore_extended_stats() needs to be run by a user that is the database owner or has MAINTAIN privileges on the table whose stats are restored. This commit adds a regression test ensuring that MAINTAIN is required when calling the function. This test also checks that a ShareUpdateExclusive lock is taken on the table whose stats are restored. This has been split from the commit that has introduced pg_restore_extended_stats(), for clarity. Author: Corey Huinker Reviewed-by: Chao Li Reviewed-by: Michael Paquier Discussion: https://postgr.es/m/CADkLM=dpz3KFnqP-dgJ-zvRvtjsa8UZv8wDAQdqho=qN3kX0Zg@mail.gmail.com --- src/test/regress/expected/stats_import.out | 42 ++++++++++++++++++++++ src/test/regress/sql/stats_import.sql | 32 +++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out index acab1367855..9c2362e70c6 100644 --- a/src/test/regress/expected/stats_import.out +++ b/src/test/regress/expected/stats_import.out @@ -1685,6 +1685,48 @@ WARNING: could not restore extended statistics object "stats_import"."test_stat f (1 row) +-- Check that MAINTAIN is required when restoring statistics. +CREATE ROLE regress_test_extstat_restore; +GRANT ALL ON SCHEMA stats_import TO regress_test_extstat_restore; +SET ROLE regress_test_extstat_restore; +-- No data to restore; this fails on a permission failure. +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +ERROR: permission denied for table test_clone +RESET ROLE; +GRANT MAINTAIN ON stats_import.test_clone TO regress_test_extstat_restore; +SET ROLE regress_test_extstat_restore; +-- This works, check the lock on the relation while on it. +BEGIN; +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false, + 'n_distinct', '[{"attributes" : [2,3], "ndistinct" : 4}]'::pg_ndistinct); + pg_restore_extended_stats +--------------------------- + t +(1 row) + +SELECT mode FROM pg_locks WHERE locktype = 'relation' AND + relation = 'stats_import.test_clone'::regclass AND + pid = pg_backend_pid(); + mode +-------------------------- + ShareUpdateExclusiveLock +(1 row) + +COMMIT; +RESET ROLE; +REVOKE MAINTAIN ON stats_import.test_clone FROM regress_test_extstat_restore; +REVOKE ALL ON SCHEMA stats_import FROM regress_test_extstat_restore; +DROP ROLE regress_test_extstat_restore; -- ndistinct value doesn't match object definition SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import', diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql index 5d35de1bc88..c9b1d9da2f7 100644 --- a/src/test/regress/sql/stats_import.sql +++ b/src/test/regress/sql/stats_import.sql @@ -1210,6 +1210,38 @@ SELECT pg_catalog.pg_restore_extended_stats( 'statistics_name', 'test_stat_clone', 'inherited', false); +-- Check that MAINTAIN is required when restoring statistics. +CREATE ROLE regress_test_extstat_restore; +GRANT ALL ON SCHEMA stats_import TO regress_test_extstat_restore; +SET ROLE regress_test_extstat_restore; +-- No data to restore; this fails on a permission failure. +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +RESET ROLE; +GRANT MAINTAIN ON stats_import.test_clone TO regress_test_extstat_restore; +SET ROLE regress_test_extstat_restore; +-- This works, check the lock on the relation while on it. +BEGIN; +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false, + 'n_distinct', '[{"attributes" : [2,3], "ndistinct" : 4}]'::pg_ndistinct); +SELECT mode FROM pg_locks WHERE locktype = 'relation' AND + relation = 'stats_import.test_clone'::regclass AND + pid = pg_backend_pid(); +COMMIT; +RESET ROLE; +REVOKE MAINTAIN ON stats_import.test_clone FROM regress_test_extstat_restore; +REVOKE ALL ON SCHEMA stats_import FROM regress_test_extstat_restore; +DROP ROLE regress_test_extstat_restore; + -- ndistinct value doesn't match object definition SELECT pg_catalog.pg_restore_extended_stats( 'schemaname', 'stats_import',