From 27e835b677b979a6e054bd86848ecc1827ce11d6 Mon Sep 17 00:00:00 2001 From: Rui Zhao Date: Mon, 27 Jul 2026 00:51:02 +0800 Subject: [PATCH] pg_dump: make the dangling-initprivs test exercise the fix The test shipped with the dangling pg_init_privs fix passes with the fix reverted: every object it creates keeps its default ACL, so pg_dump emits no GRANT/REVOKE for those objects at all, and the unlike() assertions hold vacuously. The test also does not run at all on a cluster whose bootstrap superuser name needs quoting, because the aclitem literals concatenate current_user unquoted and aclitemin stops parsing the name at the dot. Quote current_user with quote_ident(), give each object a non-default ACL so that pg_dump actually emits privilege statements for it, widen the catch-all assertion to cover REVOKE ... FROM "" as well as GRANT ... TO "", and add coverage for the PUBLIC (grantee = 0) branch of the filter. --- .../t/008_pg_dump_dangling_initprivs.pl | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/bin/pg_dump/t/008_pg_dump_dangling_initprivs.pl b/src/bin/pg_dump/t/008_pg_dump_dangling_initprivs.pl index 1580a25638..e87f1a13e3 100644 --- a/src/bin/pg_dump/t/008_pg_dump_dangling_initprivs.pl +++ b/src/bin/pg_dump/t/008_pg_dump_dangling_initprivs.pl @@ -29,36 +29,40 @@ SET allow_system_table_mods = true; CREATE ROLE ghost_grantee; CREATE ROLE ghost_grantor; CREATE ROLE "007"; +CREATE ROLE regress_col_grantee; -- Case 1: dangling grantee (function) CREATE FUNCTION public.test_func_grantee() RETURNS int LANGUAGE sql AS 'SELECT 1'; +REVOKE ALL ON FUNCTION public.test_func_grantee() FROM PUBLIC; INSERT INTO pg_init_privs (objoid, classoid, objsubid, privtype, initprivs) SELECT p.oid, (SELECT oid FROM pg_class WHERE relname = 'pg_proc'), 0, 'e', - ARRAY[('ghost_grantee=X/' || current_user)::aclitem] + ARRAY[('ghost_grantee=X/' || quote_ident(current_user))::aclitem] FROM pg_proc p WHERE p.proname = 'test_func_grantee' AND p.pronamespace = 'public'::regnamespace; -- Case 2: dangling grantor (function) CREATE FUNCTION public.test_func_grantor() RETURNS int LANGUAGE sql AS 'SELECT 2'; +REVOKE ALL ON FUNCTION public.test_func_grantor() FROM PUBLIC; INSERT INTO pg_init_privs (objoid, classoid, objsubid, privtype, initprivs) SELECT p.oid, (SELECT oid FROM pg_class WHERE relname = 'pg_proc'), 0, 'e', - ARRAY[(current_user || '=X/ghost_grantor')::aclitem] + ARRAY[(quote_ident(current_user) || '=X/ghost_grantor')::aclitem] FROM pg_proc p WHERE p.proname = 'test_func_grantor' AND p.pronamespace = 'public'::regnamespace; -- Case 3: dangling column-level grantee (table) CREATE TABLE public.test_tbl (id int, secret text); +GRANT SELECT (secret) ON public.test_tbl TO regress_col_grantee; INSERT INTO pg_init_privs (objoid, classoid, objsubid, privtype, initprivs) SELECT c.oid, (SELECT oid FROM pg_class WHERE relname = 'pg_class'), 2, 'e', - ARRAY[('ghost_grantee=r/' || current_user)::aclitem] + ARRAY[('ghost_grantee=r/' || quote_ident(current_user))::aclitem] FROM pg_class c WHERE c.relname = 'test_tbl' AND c.relnamespace = 'public'::regnamespace; @@ -70,7 +74,7 @@ INSERT INTO pg_init_privs (objoid, classoid, objsubid, privtype, initprivs) SELECT p.oid, (SELECT oid FROM pg_class WHERE relname = 'pg_proc'), 0, 'e', - ARRAY[('ghost_grantee=X/' || current_user)::aclitem] + ARRAY[('ghost_grantee=X/' || quote_ident(current_user))::aclitem] FROM pg_proc p WHERE p.proname = 'test_func_revoke' AND p.pronamespace = 'public'::regnamespace; @@ -79,6 +83,30 @@ WHERE p.proname = 'test_func_revoke' CREATE FUNCTION public.test_func_007() RETURNS int LANGUAGE sql AS 'SELECT 7'; GRANT EXECUTE ON FUNCTION public.test_func_007() TO "007"; +-- Case 6: PUBLIC grant whose grantor is dangling (must be filtered) +CREATE FUNCTION public.test_func_public_ghost() RETURNS int LANGUAGE sql AS 'SELECT 6'; +REVOKE ALL ON FUNCTION public.test_func_public_ghost() FROM PUBLIC; +INSERT INTO pg_init_privs (objoid, classoid, objsubid, privtype, initprivs) +SELECT p.oid, + (SELECT oid FROM pg_class WHERE relname = 'pg_proc'), + 0, 'e', + ARRAY['=X/ghost_grantor'::aclitem] +FROM pg_proc p +WHERE p.proname = 'test_func_public_ghost' + AND p.pronamespace = 'public'::regnamespace; + +-- Case 7: PUBLIC grant with a valid grantor (must NOT be filtered) +CREATE FUNCTION public.test_func_public_ok() RETURNS int LANGUAGE sql AS 'SELECT 7'; +REVOKE ALL ON FUNCTION public.test_func_public_ok() FROM PUBLIC; +INSERT INTO pg_init_privs (objoid, classoid, objsubid, privtype, initprivs) +SELECT p.oid, + (SELECT oid FROM pg_class WHERE relname = 'pg_proc'), + 0, 'e', + ARRAY[('=X/' || quote_ident(current_user))::aclitem] +FROM pg_proc p +WHERE p.proname = 'test_func_public_ok' + AND p.pronamespace = 'public'::regnamespace; + -- Now delete the ghost roles to create dangling OIDs DELETE FROM pg_authid WHERE rolname = 'ghost_grantee'; DELETE FROM pg_authid WHERE rolname = 'ghost_grantor'; @@ -117,8 +145,8 @@ unlike($dump, qr/GRANT\b.*\btest_func_grantor/, # --- Case 3: column-level dangling --- like($dump, qr/CREATE TABLE public\.test_tbl/, 'case 3: table is present in dump'); -unlike($dump, qr/GRANT\b.*\btest_tbl\b.*secret/i, - 'case 3: no column GRANT for dangling column-level entry'); +unlike($dump, qr/REVOKE\b.*\btest_tbl\b/, + 'case 3: no column-level REVOKE naming a dangling OID'); # --- Case 4: spurious REVOKE --- like($dump, qr/CREATE FUNCTION public\.test_func_revoke/, @@ -133,8 +161,16 @@ like($dump, qr/GRANT\b.*\btest_func_007\b.*TO\s+"007"/, 'case 5: GRANT to valid all-digit role "007" is preserved'); # --- General: no numeric OID as role name (other than the valid "007") --- -# Match any GRANT ... TO "digits" where the digits are NOT "007". -unlike($dump, qr/GRANT\b.*\bTO\s+"(?!007")[0-9]+"/, - 'no GRANT with bare numeric OID as role name (other than valid "007")'); +# Match any GRANT/REVOKE naming a role as "digits", except the valid "007". +unlike($dump, qr/^(?:GRANT|REVOKE)\b.*\b(?:TO|FROM)\s+"(?!007")[0-9]+"/m, + 'no GRANT/REVOKE with bare numeric OID as role name (other than valid "007")'); + +# --- Case 6: PUBLIC entry with dangling grantor is filtered --- +unlike($dump, qr/GRANT\b.*\btest_func_public_ghost/, + 'case 6: no GRANT for PUBLIC entry with dangling grantor'); + +# --- Case 7: PUBLIC entry with valid grantor survives the filter --- +like($dump, qr/GRANT\b.*\btest_func_public_ok/, + 'case 7: PUBLIC entry with valid grantor is preserved'); done_testing(); -- 2.43.7