From c6ee793bfe3718336c76b59e0f31c5f6b2a1a360 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 29 Sep 2026 16:18:23 -0500 Subject: [PATCH 1/3] Fire the event trigger for DROP SCHEMA and DROP OWNED `DROP SCHEMA ... CASCADE` and `DROP OWNED BY` can drop an extension, but the trigger only fired for `DROP EXTENSION`. The registered SQL never ran and the leftover row in `extension_drop__commands` made every later `DROP EXTENSION` raise `XD001`. The trigger's `WHEN tag` list now includes both tags. The update script recreates the trigger, since `ALTER EVENT TRIGGER` cannot change `WHEN`. Co-Authored-By: Claude Sonnet 5.5 --- HISTORY.asc | 8 +++ README.md | 3 +- sql/extension_drop--0.1.1--stable.sql | 8 +++ sql/extension_drop.sql | 3 +- test/expected/drop_owned.out | 16 ++++++ test/expected/drop_owned_1.out | 16 ++++++ test/expected/drop_schema_cascade.out | 16 ++++++ test/expected/drop_schema_cascade_1.out | 16 ++++++ test/sql/drop_owned.sql | 66 +++++++++++++++++++++++++ test/sql/drop_schema_cascade.sql | 64 ++++++++++++++++++++++++ 10 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 test/expected/drop_owned.out create mode 100644 test/expected/drop_owned_1.out create mode 100644 test/expected/drop_schema_cascade.out create mode 100644 test/expected/drop_schema_cascade_1.out create mode 100644 test/sql/drop_owned.sql create mode 100644 test/sql/drop_schema_cascade.sql diff --git a/HISTORY.asc b/HISTORY.asc index 7e6369e..bf26060 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -6,6 +6,14 @@ extension_drop depends on cat_tools, which already requires PostgreSQL too (a cat_tools function it calls needs PG11+ internally) -- this just makes the declared floor honest. +== Run drop commands when `DROP SCHEMA ... CASCADE` or `DROP OWNED BY` drops an extension +Previously only `DROP EXTENSION` fired the event trigger, so dropping an +extension either of the other two ways skipped its registered SQL and left a +stale registration that made every later `DROP EXTENSION` fail with `XD001`. +The event trigger now also fires for `DROP SCHEMA` and `DROP OWNED`. Any +stale registration already present must be removed with +`extension_drop__repair()`. + == Remove redundant client_min_messages handling from install script `CREATE EXTENSION` already raises `client_min_messages` to `WARNING` for the install script (only-raising, so a stricter caller is respected) and diff --git a/README.md b/README.md index c38c877..b550a53 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ arbitrary SQL to run later. Grant them to specific roles as needed. ## How it works An event trigger (`extension_drop`, on `sql_drop`) fires whenever an -extension is dropped, runs any SQL registered for it via +extension is dropped -- by `DROP EXTENSION`, `DROP SCHEMA ... CASCADE` or +`DROP OWNED BY` -- runs any SQL registered for it via `extension_drop__add`/`extension_drop__update`, and removes the registration afterward. diff --git a/sql/extension_drop--0.1.1--stable.sql b/sql/extension_drop--0.1.1--stable.sql index b011c62..a6e4d38 100644 --- a/sql/extension_drop--0.1.1--stable.sql +++ b/sql/extension_drop--0.1.1--stable.sql @@ -25,4 +25,12 @@ BEGIN END $body$; +-- ALTER EVENT TRIGGER can't change WHEN +DROP EVENT TRIGGER extension_drop; +CREATE EVENT TRIGGER extension_drop + ON sql_drop + WHEN tag IN( 'DROP EXTENSION', 'DROP SCHEMA', 'DROP OWNED' ) -- NOTE! This MUST be IN + EXECUTE PROCEDURE extension_drop__event_trigger() +; + -- vim: sw=2 ts=2 expandtab diff --git a/sql/extension_drop.sql b/sql/extension_drop.sql index 75319be..b3871aa 100644 --- a/sql/extension_drop.sql +++ b/sql/extension_drop.sql @@ -291,7 +291,8 @@ $body$ CREATE EVENT TRIGGER extension_drop ON sql_drop - WHEN tag IN( 'DROP EXTENSION' ) -- NOTE! This MUST be IN + -- DROP SCHEMA CASCADE and DROP OWNED BY also drop extensions. + WHEN tag IN( 'DROP EXTENSION', 'DROP SCHEMA', 'DROP OWNED' ) -- NOTE! This MUST be IN EXECUTE PROCEDURE extension_drop__event_trigger() ; diff --git a/test/expected/drop_owned.out b/test/expected/drop_owned.out new file mode 100644 index 0000000..36d377c --- /dev/null +++ b/test/expected/drop_owned.out @@ -0,0 +1,16 @@ +\set ECHO none +1..13 +ok 1 - Insert into extension_drop_test_table +ok 2 - Create role +ok 3 - Create test extension as role +ok 4 - Verify extension_drop__get() +ok 5 - Drop owned +ok 6 - Test extension does not exist +ok 7 - extension_drop_test_table is empty +ok 8 - No stale registration +ok 9 - Create test extension +ok 10 - Test extension exists +ok 11 - Drop test extension +ok 12 - Test extension does not exist +ok 13 - extension_drop's schema should not be reachable via search_path +# TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/expected/drop_owned_1.out b/test/expected/drop_owned_1.out new file mode 100644 index 0000000..3801831 --- /dev/null +++ b/test/expected/drop_owned_1.out @@ -0,0 +1,16 @@ +\set ECHO none +1..13 +ok 1 - Insert into extension_drop_test_table +ok 2 - Create role +ok 3 - Create test extension as role +ok 4 - Verify extension_drop__get() +ok 5 - Drop owned +ok 6 - Test extension does not exist +ok 7 - extension_drop_test_table is empty +ok 8 - No stale registration +ok 9 - Create test extension +ok 10 - Test extension exists +ok 11 - Drop test extension +ok 12 - Test extension does not exist +ok 13 # SKIP existing mode: extension_drop's placement is outside this suite's control +# TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/expected/drop_schema_cascade.out b/test/expected/drop_schema_cascade.out new file mode 100644 index 0000000..e280978 --- /dev/null +++ b/test/expected/drop_schema_cascade.out @@ -0,0 +1,16 @@ +\set ECHO none +1..13 +ok 1 - Insert into extension_drop_test_table +ok 2 - Create schema +ok 3 - Create test extension in schema +ok 4 - Verify extension_drop__get() +ok 5 - Drop schema cascade +ok 6 - Test extension does not exist +ok 7 - extension_drop_test_table is empty +ok 8 - No stale registration +ok 9 - Create test extension +ok 10 - Test extension exists +ok 11 - Drop test extension +ok 12 - Test extension does not exist +ok 13 - extension_drop's schema should not be reachable via search_path +# TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/expected/drop_schema_cascade_1.out b/test/expected/drop_schema_cascade_1.out new file mode 100644 index 0000000..a634d57 --- /dev/null +++ b/test/expected/drop_schema_cascade_1.out @@ -0,0 +1,16 @@ +\set ECHO none +1..13 +ok 1 - Insert into extension_drop_test_table +ok 2 - Create schema +ok 3 - Create test extension in schema +ok 4 - Verify extension_drop__get() +ok 5 - Drop schema cascade +ok 6 - Test extension does not exist +ok 7 - extension_drop_test_table is empty +ok 8 - No stale registration +ok 9 - Create test extension +ok 10 - Test extension exists +ok 11 - Drop test extension +ok 12 - Test extension does not exist +ok 13 # SKIP existing mode: extension_drop's placement is outside this suite's control +# TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/sql/drop_owned.sql b/test/sql/drop_owned.sql new file mode 100644 index 0000000..a79979d --- /dev/null +++ b/test/sql/drop_owned.sql @@ -0,0 +1,66 @@ +\set ECHO none +\i test/pgxntool/setup.sql + +/* + * DROP OWNED BY can drop an extension; its registered command must run and + * the registration must go away, or the stale row makes every later DROP + * EXTENSION fail. + */ +SELECT plan( + 0 + + + 1 -- Insert into test table + + + 1 -- Create role + + 1 -- Create test extension as role + + 1 -- Verify extension_drop__get() + + 1 -- Drop owned + + 1 -- Verify test extension is gone + + 1 -- Verify test table is empty + + 1 -- Verify no stale registration + + + 4 -- Create and drop test extension again (test/helpers/test_ext__create_drop.sql) + + + 1 -- extension_drop's schema should not be on search_path (test/finish.sql) +); + +\i test/helpers/test_ext__insert.sql + +SELECT lives_ok( + 'CREATE ROLE extension_drop_test_owner SUPERUSER' + , 'Create role' +); +-- ALTER EXTENSION can't change the owner, so create it as that role +SELECT lives_ok( + $q$DO $$ BEGIN + SET LOCAL ROLE extension_drop_test_owner; + CREATE EXTENSION extension_drop_test; + RESET ROLE; + END $$$q$ + , 'Create test extension as role' +); +SELECT bag_eq( + format($$SELECT * FROM %I.extension_drop__get('extension_drop_test')$$, :'extension_drop_schema') + , $$SELECT 'extension_drop_test'::name, 'DELETE FROM extension_drop_test_table'::text$$ + , 'Verify extension_drop__get()' +); + +SELECT lives_ok( + 'DROP OWNED BY extension_drop_test_owner' + , 'Drop owned' +); +SELECT hasnt_extension('extension_drop_test', 'Test extension does not exist'); +SELECT is_empty( + $$SELECT * FROM $$ || :'TT' + , :'TT' || ' is empty' +); +SELECT is_empty( + format($$SELECT unnest(%I.extension_drop__sanity_check())$$, :'extension_drop_schema') + , 'No stale registration' +); + +\i test/helpers/test_ext__create_drop.sql + +\i test/finish.sql + +-- vi: expandtab sw=2 ts=2 diff --git a/test/sql/drop_schema_cascade.sql b/test/sql/drop_schema_cascade.sql new file mode 100644 index 0000000..81acd22 --- /dev/null +++ b/test/sql/drop_schema_cascade.sql @@ -0,0 +1,64 @@ +\set ECHO none +\i test/pgxntool/setup.sql + +/* + * DROP SCHEMA ... CASCADE can drop an extension; its registered command must + * run and the registration must go away, or the stale row makes every later + * DROP EXTENSION fail. + */ +SELECT plan( + 0 + + + 1 -- Insert into test table + + + 1 -- Create schema + + 1 -- Create test extension in it + + 1 -- Verify extension_drop__get() + + 1 -- Drop schema cascade + + 1 -- Verify test extension is gone + + 1 -- Verify test table is empty + + 1 -- Verify no stale registration + + + 4 -- Create and drop test extension again (test/helpers/test_ext__create_drop.sql) + + + 1 -- extension_drop's schema should not be on search_path (test/finish.sql) +); + +-- Hide the version-specific "drop cascades to" NOTICE +SET client_min_messages = WARNING; + +\i test/helpers/test_ext__insert.sql + +SELECT lives_ok( + 'CREATE SCHEMA extension_drop_test_schema' + , 'Create schema' +); +SELECT lives_ok( + 'CREATE EXTENSION extension_drop_test SCHEMA extension_drop_test_schema' + , 'Create test extension in schema' +); +SELECT bag_eq( + format($$SELECT * FROM %I.extension_drop__get('extension_drop_test')$$, :'extension_drop_schema') + , $$SELECT 'extension_drop_test'::name, 'DELETE FROM extension_drop_test_table'::text$$ + , 'Verify extension_drop__get()' +); + +SELECT lives_ok( + 'DROP SCHEMA extension_drop_test_schema CASCADE' + , 'Drop schema cascade' +); +SELECT hasnt_extension('extension_drop_test', 'Test extension does not exist'); +SELECT is_empty( + $$SELECT * FROM $$ || :'TT' + , :'TT' || ' is empty' +); +SELECT is_empty( + format($$SELECT unnest(%I.extension_drop__sanity_check())$$, :'extension_drop_schema') + , 'No stale registration' +); + +\i test/helpers/test_ext__create_drop.sql + +\i test/finish.sql + +-- vi: expandtab sw=2 ts=2 From 49a778f6af28f2451f2210d3ee22f6f0832245de Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 29 Sep 2026 16:57:33 -0500 Subject: [PATCH 2/3] Return early from the event trigger unless an extension was dropped Now that the trigger fires for every `DROP SCHEMA` and `DROP OWNED`, it must not read `extension_drop__commands` or run the sanity assert for drops that involve no extension: a non-superuser's ordinary drops failed with a permission error, and a stale row broke unrelated drops. The row variable is a `record` so the function compiles without access to the extension's schema. Adds tests for non-superuser and stale-row drops, and factors the shared post-drop assertions into a helper. Co-Authored-By: Claude Sonnet 5.5 --- HISTORY.asc | 12 ++-- README.md | 3 +- sql/extension_drop--0.1.1--stable.sql | 10 +++- sql/extension_drop.sql | 10 +++- test/expected/unrelated_drops.out | 11 ++++ test/expected/unrelated_drops_1.out | 11 ++++ test/helpers/test_ext__verify_dropped.sql | 11 ++++ test/sql/drop_owned.sql | 14 +---- test/sql/drop_schema_cascade.sql | 14 +---- test/sql/unrelated_drops.sql | 73 +++++++++++++++++++++++ 10 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 test/expected/unrelated_drops.out create mode 100644 test/expected/unrelated_drops_1.out create mode 100644 test/helpers/test_ext__verify_dropped.sql create mode 100644 test/sql/unrelated_drops.sql diff --git a/HISTORY.asc b/HISTORY.asc index bf26060..54478b8 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -7,12 +7,12 @@ too (a cat_tools function it calls needs PG11+ internally) -- this just makes the declared floor honest. == Run drop commands when `DROP SCHEMA ... CASCADE` or `DROP OWNED BY` drops an extension -Previously only `DROP EXTENSION` fired the event trigger, so dropping an -extension either of the other two ways skipped its registered SQL and left a -stale registration that made every later `DROP EXTENSION` fail with `XD001`. -The event trigger now also fires for `DROP SCHEMA` and `DROP OWNED`. Any -stale registration already present must be removed with -`extension_drop__repair()`. +The event trigger now fires for `DROP SCHEMA` and `DROP OWNED` as well as +`DROP EXTENSION`, and returns immediately unless an extension is among the +dropped objects, so other drops are unaffected. Previously the other two +commands skipped the registered SQL and left a stale registration that made +every later `DROP EXTENSION` fail with `XD001`; remove any existing stale +registration with `extension_drop__repair()`. == Remove redundant client_min_messages handling from install script `CREATE EXTENSION` already raises `client_min_messages` to `WARNING` for diff --git a/README.md b/README.md index b550a53..08effaa 100644 --- a/README.md +++ b/README.md @@ -45,4 +45,5 @@ An event trigger (`extension_drop`, on `sql_drop`) fires whenever an extension is dropped -- by `DROP EXTENSION`, `DROP SCHEMA ... CASCADE` or `DROP OWNED BY` -- runs any SQL registered for it via `extension_drop__add`/`extension_drop__update`, and removes the registration -afterward. +afterward. Those commands fire the trigger for every drop, but it returns +immediately unless an extension is among the dropped objects. diff --git a/sql/extension_drop--0.1.1--stable.sql b/sql/extension_drop--0.1.1--stable.sql index a6e4d38..ffb8e6b 100644 --- a/sql/extension_drop--0.1.1--stable.sql +++ b/sql/extension_drop--0.1.1--stable.sql @@ -2,9 +2,17 @@ CREATE OR REPLACE FUNCTION extension_drop__event_trigger( ) RETURNS event_trigger LANGUAGE plpgsql SET search_path FROM CURRENT AS $body$ DECLARE - r extension_drop__commands; + r record; BEGIN RAISE DEBUG 'extension_drop event trigger entry: tg_event %, tg_tag %', tg_event, tg_tag; + + -- Most drops involve no extension; don't touch our table (or assert) for them. + IF NOT EXISTS( + SELECT 1 FROM pg_catalog.pg_event_trigger_dropped_objects() WHERE object_type = 'extension' + ) THEN + RETURN; + END IF; + FOR r IN SELECT c.* FROM extension_drop__commands c diff --git a/sql/extension_drop.sql b/sql/extension_drop.sql index b3871aa..ae9757d 100644 --- a/sql/extension_drop.sql +++ b/sql/extension_drop.sql @@ -264,9 +264,17 @@ SELECT __extension_drop.create_function( , 'event_trigger LANGUAGE plpgsql' , $body$ DECLARE - r extension_drop__commands; + r record; BEGIN RAISE DEBUG 'extension_drop event trigger entry: tg_event %, tg_tag %', tg_event, tg_tag; + + -- Most drops involve no extension; don't touch our table (or assert) for them. + IF NOT EXISTS( + SELECT 1 FROM pg_catalog.pg_event_trigger_dropped_objects() WHERE object_type = 'extension' + ) THEN + RETURN; + END IF; + FOR r IN SELECT c.* FROM extension_drop__commands c diff --git a/test/expected/unrelated_drops.out b/test/expected/unrelated_drops.out new file mode 100644 index 0000000..d1d9177 --- /dev/null +++ b/test/expected/unrelated_drops.out @@ -0,0 +1,11 @@ +\set ECHO none +1..8 +ok 1 - Create non-superuser role +ok 2 - Grant CREATE on database +ok 3 - Non-superuser DROP SCHEMA CASCADE +ok 4 - Non-superuser DROP OWNED BY +ok 5 - Insert stale registration +ok 6 - Stale registration is present +ok 7 - Superuser DROP SCHEMA with stale registration +ok 8 - extension_drop's schema should not be reachable via search_path +# TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/expected/unrelated_drops_1.out b/test/expected/unrelated_drops_1.out new file mode 100644 index 0000000..6860796 --- /dev/null +++ b/test/expected/unrelated_drops_1.out @@ -0,0 +1,11 @@ +\set ECHO none +1..8 +ok 1 - Create non-superuser role +ok 2 - Grant CREATE on database +ok 3 - Non-superuser DROP SCHEMA CASCADE +ok 4 - Non-superuser DROP OWNED BY +ok 5 - Insert stale registration +ok 6 - Stale registration is present +ok 7 - Superuser DROP SCHEMA with stale registration +ok 8 # SKIP existing mode: extension_drop's placement is outside this suite's control +# TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/helpers/test_ext__verify_dropped.sql b/test/helpers/test_ext__verify_dropped.sql new file mode 100644 index 0000000..a86f134 --- /dev/null +++ b/test/helpers/test_ext__verify_dropped.sql @@ -0,0 +1,11 @@ +SELECT hasnt_extension('extension_drop_test', 'Test extension does not exist'); +SELECT is_empty( + $$SELECT * FROM $$ || :'TT' + , :'TT' || ' is empty' +); +SELECT is_empty( + format($$SELECT unnest(%I.extension_drop__sanity_check())$$, :'extension_drop_schema') + , 'No stale registration' +); + +-- vi: expandtab sw=2 ts=2 diff --git a/test/sql/drop_owned.sql b/test/sql/drop_owned.sql index a79979d..dcc439c 100644 --- a/test/sql/drop_owned.sql +++ b/test/sql/drop_owned.sql @@ -15,9 +15,7 @@ SELECT plan( + 1 -- Create test extension as role + 1 -- Verify extension_drop__get() + 1 -- Drop owned - + 1 -- Verify test extension is gone - + 1 -- Verify test table is empty - + 1 -- Verify no stale registration + + 3 -- Verify extension is gone, table is empty, no stale registration (test/helpers/test_ext__verify_dropped.sql) + 4 -- Create and drop test extension again (test/helpers/test_ext__create_drop.sql) @@ -49,15 +47,7 @@ SELECT lives_ok( 'DROP OWNED BY extension_drop_test_owner' , 'Drop owned' ); -SELECT hasnt_extension('extension_drop_test', 'Test extension does not exist'); -SELECT is_empty( - $$SELECT * FROM $$ || :'TT' - , :'TT' || ' is empty' -); -SELECT is_empty( - format($$SELECT unnest(%I.extension_drop__sanity_check())$$, :'extension_drop_schema') - , 'No stale registration' -); +\i test/helpers/test_ext__verify_dropped.sql \i test/helpers/test_ext__create_drop.sql diff --git a/test/sql/drop_schema_cascade.sql b/test/sql/drop_schema_cascade.sql index 81acd22..a6b46c3 100644 --- a/test/sql/drop_schema_cascade.sql +++ b/test/sql/drop_schema_cascade.sql @@ -15,9 +15,7 @@ SELECT plan( + 1 -- Create test extension in it + 1 -- Verify extension_drop__get() + 1 -- Drop schema cascade - + 1 -- Verify test extension is gone - + 1 -- Verify test table is empty - + 1 -- Verify no stale registration + + 3 -- Verify extension is gone, table is empty, no stale registration (test/helpers/test_ext__verify_dropped.sql) + 4 -- Create and drop test extension again (test/helpers/test_ext__create_drop.sql) @@ -47,15 +45,7 @@ SELECT lives_ok( 'DROP SCHEMA extension_drop_test_schema CASCADE' , 'Drop schema cascade' ); -SELECT hasnt_extension('extension_drop_test', 'Test extension does not exist'); -SELECT is_empty( - $$SELECT * FROM $$ || :'TT' - , :'TT' || ' is empty' -); -SELECT is_empty( - format($$SELECT unnest(%I.extension_drop__sanity_check())$$, :'extension_drop_schema') - , 'No stale registration' -); +\i test/helpers/test_ext__verify_dropped.sql \i test/helpers/test_ext__create_drop.sql diff --git a/test/sql/unrelated_drops.sql b/test/sql/unrelated_drops.sql new file mode 100644 index 0000000..1bdc4a5 --- /dev/null +++ b/test/sql/unrelated_drops.sql @@ -0,0 +1,73 @@ +\set ECHO none +\i test/pgxntool/setup.sql + +/* + * DROP SCHEMA and DROP OWNED fire extension_drop's event trigger, so drops + * that involve no extension must not depend on the caller's privileges or on + * the state of extension_drop__commands. + */ +SELECT plan( + 0 + + + 1 -- Create non-superuser role + + 1 -- Grant it CREATE on the database + + 1 -- Non-superuser DROP SCHEMA CASCADE + + 1 -- Non-superuser DROP OWNED BY + + + 1 -- Insert stale registration + + 1 -- Stale registration is present + + 1 -- Superuser DROP SCHEMA with stale registration + + + 1 -- extension_drop's schema should not be on search_path (test/finish.sql) +); + +-- Hide version- and database-specific NOTICE/WARNING text from the drops +SET client_min_messages = ERROR; + +SELECT lives_ok( + 'CREATE ROLE extension_drop_test_plain' + , 'Create non-superuser role' +); +SELECT lives_ok( + format('GRANT CREATE ON DATABASE %I TO extension_drop_test_plain', current_database()) + , 'Grant CREATE on database' +); + +SELECT lives_ok( + $q$DO $$ BEGIN + SET LOCAL ROLE extension_drop_test_plain; + CREATE SCHEMA extension_drop_test_plain_schema; + CREATE TABLE extension_drop_test_plain_schema.t(i int); + DROP SCHEMA extension_drop_test_plain_schema CASCADE; + RESET ROLE; + END $$$q$ + , 'Non-superuser DROP SCHEMA CASCADE' +); +SELECT lives_ok( + $q$DO $$ BEGIN + SET LOCAL ROLE extension_drop_test_plain; + CREATE SCHEMA extension_drop_test_plain_schema; + CREATE TABLE extension_drop_test_plain_schema.t(i int); + DROP OWNED BY extension_drop_test_plain; + RESET ROLE; + END $$$q$ + , 'Non-superuser DROP OWNED BY' +); + +SELECT lives_ok( + format($$INSERT INTO %I.extension_drop__commands VALUES('extension_drop_test_stale', 'SELECT 1')$$, :'extension_drop_schema') + , 'Insert stale registration' +); +SELECT results_eq( + format($$SELECT %I.extension_drop__sanity_check()$$, :'extension_drop_schema') + , $$SELECT '{extension_drop_test_stale}'::name[]$$ + , 'Stale registration is present' +); +SELECT lives_ok( + $$CREATE SCHEMA extension_drop_test_su_schema; DROP SCHEMA extension_drop_test_su_schema$$ + , 'Superuser DROP SCHEMA with stale registration' +); + +\i test/finish.sql + +-- vi: expandtab sw=2 ts=2 From e26c90244d8f9396e85c92b1f74ad000205a74df Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 29 Sep 2026 17:06:51 -0500 Subject: [PATCH 3/3] Comment why the trigger's row variable is a record Co-Authored-By: Claude Sonnet 5.5 --- sql/extension_drop--0.1.1--stable.sql | 2 +- sql/extension_drop.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/extension_drop--0.1.1--stable.sql b/sql/extension_drop--0.1.1--stable.sql index ffb8e6b..940420e 100644 --- a/sql/extension_drop--0.1.1--stable.sql +++ b/sql/extension_drop--0.1.1--stable.sql @@ -2,7 +2,7 @@ CREATE OR REPLACE FUNCTION extension_drop__event_trigger( ) RETURNS event_trigger LANGUAGE plpgsql SET search_path FROM CURRENT AS $body$ DECLARE - r record; + r record; -- Not extension_drop__commands: the type must not resolve before the early return BEGIN RAISE DEBUG 'extension_drop event trigger entry: tg_event %, tg_tag %', tg_event, tg_tag; diff --git a/sql/extension_drop.sql b/sql/extension_drop.sql index ae9757d..2e66442 100644 --- a/sql/extension_drop.sql +++ b/sql/extension_drop.sql @@ -264,7 +264,7 @@ SELECT __extension_drop.create_function( , 'event_trigger LANGUAGE plpgsql' , $body$ DECLARE - r record; + r record; -- Not extension_drop__commands: the type must not resolve before the early return BEGIN RAISE DEBUG 'extension_drop event trigger entry: tg_event %, tg_tag %', tg_event, tg_tag;