From d6dd65374e99357d9965622d1c4074c3aad45d4b Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 27 Oct 2026 15:47:34 -0500 Subject: [PATCH 01/14] Stop install and update scripts changing caller settings CREATE EXTENSION and ALTER EXTENSION UPDATE left the caller's client_min_messages at warning, and updating from 0.2.0 or 0.2.1 also changed search_path. Remove the settings save/restore and the SETs from sql/trunklet.sql and the 0.2.0--0.2.1, 0.2.1--0.3.0 and 0.3.2--0.3.3 update scripts. The suite now asserts both settings are unchanged after a fresh install and after an update, and the update helper no longer needs to schema-qualify names. test/sql/build.sql loads the raw script with \i, so it sets client_min_messages itself. --- HISTORY.asc | 4 ++++ sql/trunklet--0.2.0--0.2.1.sql | 31 ------------------------------- sql/trunklet--0.2.1--0.3.0.sql | 16 ---------------- sql/trunklet--0.3.2--0.3.3.sql | 28 ---------------------------- sql/trunklet.sql | 28 ---------------------------- test/expected/build.out | 5 ----- test/helpers/update_with_data.sql | 25 ++++++++++++++++++++++--- test/install/load.sql | 21 +++++++++++++++++++++ test/sql/build.sql | 2 ++ 9 files changed, 49 insertions(+), 111 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index 28d9851..3926492 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -6,6 +6,10 @@ STABLE `ALTER EXTENSION trunklet UPDATE` now works from 0.2.0. Previously there was no update path from 0.2.0. Updated installs now also match a fresh install: the `language__get()` debug message that a fresh 0.3.3 install has is added by `0.3.3--stable` and was retroactively added to the already-released `0.3.2--0.3.3` update script. +=== Stop install and update scripts from changing the caller's settings +`CREATE EXTENSION` and `ALTER EXTENSION trunklet UPDATE` no longer change the caller's `search_path` or `client_min_messages`. Updating from 0.2.0 or 0.2.1 used to leave `search_path` altered (to `pg_catalog`, or to a nonexistent schema after `0.2.0--0.2.1`), and the install and update scripts left `client_min_messages` at `warning`. +The released `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited retroactively, as was the unreleased `0.2.0--0.2.1`. The versioned install scripts for 0.2.0, 0.2.1 and 0.3.3 still change them, so `CREATE EXTENSION ... VERSION` at one of those versions does too. + 0.3.3 ----- === Add `ignore_missing_functions` agument to `template_language__remove()` diff --git a/sql/trunklet--0.2.0--0.2.1.sql b/sql/trunklet--0.2.0--0.2.1.sql index 9de514e..37b50ef 100644 --- a/sql/trunklet--0.2.0--0.2.1.sql +++ b/sql/trunklet--0.2.0--0.2.1.sql @@ -1,17 +1,3 @@ -/* - * Using temp objects will result in the extension being dropped after session - * end. Create a real schema and then explicitly drop it instead. - */ -CREATE SCHEMA __trunklet; - -CREATE TABLE __trunklet.old_settings AS - SELECT name, setting - FROM pg_catalog.pg_settings - WHERE name IN ('client_min_messages', 'search_path') -; -SET client_min_messages = warning; -SET search_path = pg_catalog; - CREATE OR REPLACE FUNCTION _trunklet.template__get( template_id _trunklet.template.template_id%TYPE , loose boolean DEFAULT false @@ -258,21 +244,4 @@ CREATE OR REPLACE FUNCTION trunklet.execute_text( ) RETURNS anyelement LANGUAGE SQL AS $body$ SELECT trunklet.execute_text( template_name, 1, parameters ) $body$; - - - - - -SELECT _trunklet.exec( - format( - 'SET %I = %L' - , name - , setting - ) - ) - FROM __trunklet.old_settings -; -DROP TABLE __trunklet.old_settings; -DROP SCHEMA __trunklet; - -- vi: expandtab sw=2 ts=2 diff --git a/sql/trunklet--0.2.1--0.3.0.sql b/sql/trunklet--0.2.1--0.3.0.sql index 90628a5..7a9965b 100644 --- a/sql/trunklet--0.2.1--0.3.0.sql +++ b/sql/trunklet--0.2.1--0.3.0.sql @@ -1,17 +1,3 @@ -/* - * Using temp objects will result in the extension being dropped after session - * end. Create a real schema and then explicitly drop it instead. - */ -CREATE SCHEMA __trunklet; - -CREATE TABLE __trunklet.old_settings AS - SELECT name, setting - FROM pg_catalog.pg_settings - WHERE name IN ('client_min_messages', 'search_path') -; -SET client_min_messages = warning; -SET search_path = pg_catalog; - CREATE OR REPLACE FUNCTION _trunklet.language__get_loose( language_id _trunklet.language.language_id%TYPE ) RETURNS _trunklet.language STABLE LANGUAGE sql AS $body$ @@ -329,7 +315,5 @@ REVOKE ALL ON FUNCTION trunklet.template_language__remove( ) FROM public; -DROP TABLE __trunklet.old_settings; -DROP SCHEMA __trunklet; -- vi: expandtab sw=2 ts=2 diff --git a/sql/trunklet--0.3.2--0.3.3.sql b/sql/trunklet--0.3.2--0.3.3.sql index c4843f4..f8dff62 100644 --- a/sql/trunklet--0.3.2--0.3.3.sql +++ b/sql/trunklet--0.3.2--0.3.3.sql @@ -1,16 +1,3 @@ -/* - * Using temp objects will result in the extension being dropped after session - * end. Create a real schema and then explicitly drop it instead. - */ -CREATE SCHEMA __trunklet; - -CREATE TABLE __trunklet.old_settings AS - SELECT name, setting - FROM pg_catalog.pg_settings - WHERE name IN ('client_min_messages')-- This doesn't actually work as expected :( , 'search_path') -; -SET client_min_messages = warning; - CREATE OR REPLACE FUNCTION _trunklet.language__get( language_name _trunklet.language.language_name%TYPE ) RETURNS _trunklet.language STABLE LANGUAGE plpgsql AS $body$ @@ -180,19 +167,4 @@ REVOKE ALL ON FUNCTION trunklet.template_language__remove( , cascade boolean , boolean ) FROM public; - - - -SELECT _trunklet.exec( - format( - 'SET %I = %L' - , name - , setting - ) - ) - FROM __trunklet.old_settings -; -DROP TABLE __trunklet.old_settings; -DROP SCHEMA __trunklet; - -- vi: expandtab sw=2 ts=2 diff --git a/sql/trunklet.sql b/sql/trunklet.sql index 0342576..68a3121 100644 --- a/sql/trunklet.sql +++ b/sql/trunklet.sql @@ -4,20 +4,6 @@ * */ -/* - * Using temp objects will result in the extension being dropped after session - * end. Create a real schema and then explicitly drop it instead. - */ -CREATE SCHEMA __trunklet; - -CREATE TABLE __trunklet.old_settings AS - SELECT name, setting - FROM pg_catalog.pg_settings - WHERE name IN ('client_min_messages')-- This doesn't actually work as expected :( , 'search_path') -; -SET client_min_messages = warning; --- PG apparently truncates the path to a single element, so our reset doesn't work :( SET LOCAL search_path = pg_catalog; - DO $do$ BEGIN CREATE ROLE trunklet__dependency; @@ -956,18 +942,4 @@ BEGIN END $body$; */ - - -SELECT _trunklet.exec( - format( - 'SET %I = %L' - , name - , setting - ) - ) - FROM __trunklet.old_settings -; -DROP TABLE __trunklet.old_settings; -DROP SCHEMA __trunklet; - -- vi: expandtab sw=2 ts=2 diff --git a/test/expected/build.out b/test/expected/build.out index 2ae39bb..36fb97d 100644 --- a/test/expected/build.out +++ b/test/expected/build.out @@ -1,9 +1,4 @@ \set ECHO none - exec ------- - -(1 row) - 1..4 ok 1 - CREATE EXTENSION trunklet; ok 2 - Verify search_path is back to something sane after extension creation. diff --git a/test/helpers/update_with_data.sql b/test/helpers/update_with_data.sql index bb350fc..686db8e 100644 --- a/test/helpers/update_with_data.sql +++ b/test/helpers/update_with_data.sql @@ -11,13 +11,32 @@ SET search_path = _trunklet_test; -- Committed before the update, as stored data would be in production CREATE TABLE seeded_template_ids AS SELECT get_test_templates() AS ids; +-- An update script must leave the caller's settings as it found them. Reset +-- client_min_messages first: TEST_UPDATE_FROM's own install script may have left +-- it raised, which would hide an update script doing the same. +SET client_min_messages = notice; +SELECT set_config('trunklet.test_before_search_path', current_setting('search_path'), false) + , set_config('trunklet.test_before_client_min_messages', current_setting('client_min_messages'), false) +; + ALTER EXTENSION trunklet UPDATE; --- Updating from before 0.3.0 changes search_path, so qualify from here on +DO $body$ +BEGIN + IF current_setting('search_path') IS DISTINCT FROM current_setting('trunklet.test_before_search_path') THEN + RAISE EXCEPTION 'ALTER EXTENSION UPDATE changed search_path from % to %' + , current_setting('trunklet.test_before_search_path'), current_setting('search_path'); + END IF; + IF current_setting('client_min_messages') IS DISTINCT FROM current_setting('trunklet.test_before_client_min_messages') THEN + RAISE EXCEPTION 'ALTER EXTENSION UPDATE changed client_min_messages from % to %' + , current_setting('trunklet.test_before_client_min_messages'), current_setting('client_min_messages'); + END IF; +END +$body$; DO $body$ DECLARE - c_ids CONSTANT int[] := (SELECT ids FROM _trunklet_test.seeded_template_ids); + c_ids CONSTANT int[] := (SELECT ids FROM seeded_template_ids); c_parameters CONSTANT text[] := '{}'; r record; BEGIN @@ -43,7 +62,7 @@ END $body$; -- The language's functions were created by TEST_UPDATE_FROM's code -SELECT trunklet.template_language__remove(_trunklet_test.get_test_language_name(), cascade => true); +SELECT trunklet.template_language__remove(get_test_language_name(), cascade => true); DO $body$ DECLARE diff --git a/test/install/load.sql b/test/install/load.sql index c8f8ad7..945b493 100644 --- a/test/install/load.sql +++ b/test/install/load.sql @@ -9,6 +9,11 @@ */ \set ON_ERROR_STOP on +-- Saved to assert that installing leaves the caller's settings as it found them +SELECT set_config('trunklet.test_before_search_path', current_setting('search_path'), false) + , set_config('trunklet.test_before_client_min_messages', current_setting('client_min_messages'), false) +; + -- Branches server-side rather than with psql's \if, which needs psql 10+ DO $body$ DECLARE @@ -39,6 +44,22 @@ SELECT CASE current_setting('trunklet.test_load_mode') \gset \i :update_script +DO $body$ +BEGIN + -- Update mode asserts this around ALTER EXTENSION UPDATE in its helper, after changing search_path + IF current_setting('trunklet.test_load_mode') = 'fresh' THEN + IF current_setting('search_path') IS DISTINCT FROM current_setting('trunklet.test_before_search_path') THEN + RAISE EXCEPTION 'CREATE EXTENSION changed search_path from % to %' + , current_setting('trunklet.test_before_search_path'), current_setting('search_path'); + END IF; + IF current_setting('client_min_messages') IS DISTINCT FROM current_setting('trunklet.test_before_client_min_messages') THEN + RAISE EXCEPTION 'CREATE EXTENSION changed client_min_messages from % to %' + , current_setting('trunklet.test_before_client_min_messages'), current_setting('client_min_messages'); + END IF; + END IF; +END +$body$; + DO $body$ DECLARE v_installed text; diff --git a/test/sql/build.sql b/test/sql/build.sql index 581cc4d..b1302d8 100644 --- a/test/sql/build.sql +++ b/test/sql/build.sql @@ -6,6 +6,8 @@ -- Both blocks drop test/install/load.sql's committed install first; ROLLBACK restores it BEGIN; DROP EXTENSION trunklet; +-- \i skips CREATE EXTENSION's script-scoped suppression of the script's NOTICEs +SET LOCAL client_min_messages = warning; \i sql/trunklet.sql ROLLBACK; From da7c42e5f4bd62b3b29a50cf58be1094d531a043 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 27 Oct 2026 16:15:15 -0500 Subject: [PATCH 02/14] Test client_min_messages across CREATE EXTENSION in search_path.sql Check client_min_messages around CREATE EXTENSION in search_path.sql rather than load.sql, and reword the HISTORY entry to match the observed settings after each update and install script. --- HISTORY.asc | 4 ++-- test/expected/search_path.out | 3 ++- test/sql/search_path.sql | 20 ++++++++++++++++---- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index 3926492..da67ac5 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -7,8 +7,8 @@ STABLE Updated installs now also match a fresh install: the `language__get()` debug message that a fresh 0.3.3 install has is added by `0.3.3--stable` and was retroactively added to the already-released `0.3.2--0.3.3` update script. === Stop install and update scripts from changing the caller's settings -`CREATE EXTENSION` and `ALTER EXTENSION trunklet UPDATE` no longer change the caller's `search_path` or `client_min_messages`. Updating from 0.2.0 or 0.2.1 used to leave `search_path` altered (to `pg_catalog`, or to a nonexistent schema after `0.2.0--0.2.1`), and the install and update scripts left `client_min_messages` at `warning`. -The released `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited retroactively, as was the unreleased `0.2.0--0.2.1`. The versioned install scripts for 0.2.0, 0.2.1 and 0.3.3 still change them, so `CREATE EXTENSION ... VERSION` at one of those versions does too. +`CREATE EXTENSION` and `ALTER EXTENSION trunklet UPDATE` no longer change the caller's `search_path` or `client_min_messages`. Updating from 0.2.0 or 0.2.1 used to leave `search_path` at `pg_catalog`, and the install and update scripts left `client_min_messages` at `warning`. +The released `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited retroactively, and so was the unreleased `0.2.0--0.2.1`. The versioned install scripts for 0.2.0 and 0.2.1 still change both settings, and the 0.3.3 install script still changes `client_min_messages` only, so `CREATE EXTENSION ... VERSION` at one of those versions does too. 0.3.3 ----- diff --git a/test/expected/search_path.out b/test/expected/search_path.out index 52f2a59..0bcaed1 100644 --- a/test/expected/search_path.out +++ b/test/expected/search_path.out @@ -1,5 +1,6 @@ \set ECHO none -1..1 +1..2 tap, public ok 1 - verify search_path has not changed +ok 2 - verify client_min_messages has not changed # TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/sql/search_path.sql b/test/sql/search_path.sql index 83d56cb..57024bc 100644 --- a/test/sql/search_path.sql +++ b/test/sql/search_path.sql @@ -5,13 +5,18 @@ SELECT plan( 0 +1 -- verify search_path + +1 -- verify client_min_messages ); -- Drop test/install/load.sql's committed install; this test's rollback restores it DROP EXTENSION trunklet; +-- Start from a value the extension script could visibly change +SET client_min_messages = notice; + CREATE TEMP TABLE before AS - SELECT current_setting('search_path') + SELECT current_setting('search_path') AS search_path + , current_setting('client_min_messages') AS client_min_messages ; SHOW search_path; @@ -19,18 +24,25 @@ SHOW search_path; CREATE EXTENSION trunklet;-- CASCADE; CREATE TEMP TABLE after AS - SELECT current_setting('search_path') + SELECT current_setting('search_path') AS search_path + , current_setting('client_min_messages') AS client_min_messages ; -- Forcibly reset it so we know tap will work SET search_path=public,tap; SELECT is( - (SELECT current_setting FROM after) - , (SELECT current_setting FROM before) + (SELECT search_path FROM after) + , (SELECT search_path FROM before) , 'verify search_path has not changed' ); +SELECT is( + (SELECT client_min_messages FROM after) + , (SELECT client_min_messages FROM before) + , 'verify client_min_messages has not changed' +); + \i test/pgxntool/finish.sql -- vi: expandtab ts=2 sw=2 From 2414f094b93767f9d7637c22d3d5533632286b78 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 27 Oct 2026 16:22:44 -0500 Subject: [PATCH 03/14] Dedupe settings assertions; fix HISTORY wording --- HISTORY.asc | 4 ++-- test/helpers/update_with_data.sql | 28 +++++++++++++++++----------- test/sql/search_path.sql | 22 ++++++++-------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index da67ac5..21380ae 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -7,8 +7,8 @@ STABLE Updated installs now also match a fresh install: the `language__get()` debug message that a fresh 0.3.3 install has is added by `0.3.3--stable` and was retroactively added to the already-released `0.3.2--0.3.3` update script. === Stop install and update scripts from changing the caller's settings -`CREATE EXTENSION` and `ALTER EXTENSION trunklet UPDATE` no longer change the caller's `search_path` or `client_min_messages`. Updating from 0.2.0 or 0.2.1 used to leave `search_path` at `pg_catalog`, and the install and update scripts left `client_min_messages` at `warning`. -The released `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited retroactively, and so was the unreleased `0.2.0--0.2.1`. The versioned install scripts for 0.2.0 and 0.2.1 still change both settings, and the 0.3.3 install script still changes `client_min_messages` only, so `CREATE EXTENSION ... VERSION` at one of those versions does too. +`CREATE EXTENSION` and `ALTER EXTENSION trunklet UPDATE` no longer change the caller's `search_path` or `client_min_messages`. Updating from 0.2.1 used to leave `search_path` at `pg_catalog`, and the install and update scripts left `client_min_messages` at `warning`. +The released `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited retroactively; `0.2.0--0.2.1` was never released. The versioned install scripts for 0.2.0 and 0.2.1 still change both settings, and the 0.3.3 install script still changes `client_min_messages` only, so `CREATE EXTENSION ... VERSION` at one of those versions does too. 0.3.3 ----- diff --git a/test/helpers/update_with_data.sql b/test/helpers/update_with_data.sql index 69a8a42..c34cbc2 100644 --- a/test/helpers/update_with_data.sql +++ b/test/helpers/update_with_data.sql @@ -19,22 +19,28 @@ CREATE TABLE seeded_template_ids AS SELECT get_test_templates() AS ids; * it raised, which would hide an update script doing the same. */ SET client_min_messages = notice; -SELECT set_config('trunklet.test_before_search_path', current_setting('search_path'), false) - , set_config('trunklet.test_before_client_min_messages', current_setting('client_min_messages'), false) -; +DO $body$ +DECLARE + s text; +BEGIN + FOREACH s IN ARRAY ARRAY['search_path', 'client_min_messages'] LOOP + PERFORM set_config('trunklet.test_before_' || s, current_setting(s), false); + END LOOP; +END +$body$; ALTER EXTENSION trunklet UPDATE; DO $body$ +DECLARE + s text; BEGIN - IF current_setting('search_path') IS DISTINCT FROM current_setting('trunklet.test_before_search_path') THEN - RAISE EXCEPTION 'ALTER EXTENSION UPDATE changed search_path from % to %' - , current_setting('trunklet.test_before_search_path'), current_setting('search_path'); - END IF; - IF current_setting('client_min_messages') IS DISTINCT FROM current_setting('trunklet.test_before_client_min_messages') THEN - RAISE EXCEPTION 'ALTER EXTENSION UPDATE changed client_min_messages from % to %' - , current_setting('trunklet.test_before_client_min_messages'), current_setting('client_min_messages'); - END IF; + FOREACH s IN ARRAY ARRAY['search_path', 'client_min_messages'] LOOP + IF current_setting(s) IS DISTINCT FROM current_setting('trunklet.test_before_' || s) THEN + RAISE EXCEPTION 'ALTER EXTENSION UPDATE changed % from % to %' + , s, current_setting('trunklet.test_before_' || s), current_setting(s); + END IF; + END LOOP; END $body$; diff --git a/test/sql/search_path.sql b/test/sql/search_path.sql index 57024bc..6607414 100644 --- a/test/sql/search_path.sql +++ b/test/sql/search_path.sql @@ -15,8 +15,7 @@ DROP EXTENSION trunklet; SET client_min_messages = notice; CREATE TEMP TABLE before AS - SELECT current_setting('search_path') AS search_path - , current_setting('client_min_messages') AS client_min_messages + SELECT name, setting FROM pg_settings WHERE name IN ('search_path', 'client_min_messages') ; SHOW search_path; @@ -24,24 +23,19 @@ SHOW search_path; CREATE EXTENSION trunklet;-- CASCADE; CREATE TEMP TABLE after AS - SELECT current_setting('search_path') AS search_path - , current_setting('client_min_messages') AS client_min_messages + SELECT name, setting FROM pg_settings WHERE name IN ('search_path', 'client_min_messages') ; -- Forcibly reset it so we know tap will work SET search_path=public,tap; SELECT is( - (SELECT search_path FROM after) - , (SELECT search_path FROM before) - , 'verify search_path has not changed' -); - -SELECT is( - (SELECT client_min_messages FROM after) - , (SELECT client_min_messages FROM before) - , 'verify client_min_messages has not changed' -); + (SELECT setting FROM after WHERE name = n) + , (SELECT setting FROM before WHERE name = n) + , 'verify ' || n || ' has not changed' +) + FROM unnest(ARRAY['search_path', 'client_min_messages']) AS n +; \i test/pgxntool/finish.sql From 0eb911fcbcd6258ccec1b00a3b75daae1d339e22 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 27 Oct 2026 17:59:46 -0500 Subject: [PATCH 04/14] Make update scripts' settings changes SET LOCAL Restores the released update scripts' settings save/restore, with each SET and the set-back made SET LOCAL so the caller's search_path and client_min_messages survive the script. --- HISTORY.asc | 6 +++--- sql/trunklet--0.2.0--0.2.1.sql | 31 +++++++++++++++++++++++++++++++ sql/trunklet--0.2.1--0.3.0.sql | 16 ++++++++++++++++ sql/trunklet--0.3.2--0.3.3.sql | 28 ++++++++++++++++++++++++++++ sql/trunklet.sql | 28 ++++++++++++++++++++++++++++ test/build/build.sql | 2 -- test/build/expected/build.out | 5 +++++ 7 files changed, 111 insertions(+), 5 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index 21380ae..61c2cb3 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -6,9 +6,9 @@ STABLE `ALTER EXTENSION trunklet UPDATE` now works from 0.2.0. Previously there was no update path from 0.2.0. Updated installs now also match a fresh install: the `language__get()` debug message that a fresh 0.3.3 install has is added by `0.3.3--stable` and was retroactively added to the already-released `0.3.2--0.3.3` update script. -=== Stop install and update scripts from changing the caller's settings -`CREATE EXTENSION` and `ALTER EXTENSION trunklet UPDATE` no longer change the caller's `search_path` or `client_min_messages`. Updating from 0.2.1 used to leave `search_path` at `pg_catalog`, and the install and update scripts left `client_min_messages` at `warning`. -The released `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited retroactively; `0.2.0--0.2.1` was never released. The versioned install scripts for 0.2.0 and 0.2.1 still change both settings, and the 0.3.3 install script still changes `client_min_messages` only, so `CREATE EXTENSION ... VERSION` at one of those versions does too. +=== Stop update scripts from leaking `search_path` and `client_min_messages` +`ALTER EXTENSION trunklet UPDATE` no longer leaves the caller's `search_path` or `client_min_messages` changed. +The released `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to make their `SET` statements and the set-back `SET LOCAL`. 0.3.3 ----- diff --git a/sql/trunklet--0.2.0--0.2.1.sql b/sql/trunklet--0.2.0--0.2.1.sql index 37b50ef..f702cb7 100644 --- a/sql/trunklet--0.2.0--0.2.1.sql +++ b/sql/trunklet--0.2.0--0.2.1.sql @@ -1,3 +1,17 @@ +/* + * Using temp objects will result in the extension being dropped after session + * end. Create a real schema and then explicitly drop it instead. + */ +CREATE SCHEMA __trunklet; + +CREATE TABLE __trunklet.old_settings AS + SELECT name, setting + FROM pg_catalog.pg_settings + WHERE name IN ('client_min_messages', 'search_path') +; +SET LOCAL client_min_messages = warning; +SET LOCAL search_path = pg_catalog; + CREATE OR REPLACE FUNCTION _trunklet.template__get( template_id _trunklet.template.template_id%TYPE , loose boolean DEFAULT false @@ -244,4 +258,21 @@ CREATE OR REPLACE FUNCTION trunklet.execute_text( ) RETURNS anyelement LANGUAGE SQL AS $body$ SELECT trunklet.execute_text( template_name, 1, parameters ) $body$; + + + + + +SELECT _trunklet.exec( + format( + 'SET LOCAL %I = %L' + , name + , setting + ) + ) + FROM __trunklet.old_settings +; +DROP TABLE __trunklet.old_settings; +DROP SCHEMA __trunklet; + -- vi: expandtab sw=2 ts=2 diff --git a/sql/trunklet--0.2.1--0.3.0.sql b/sql/trunklet--0.2.1--0.3.0.sql index 7a9965b..5ff06f7 100644 --- a/sql/trunklet--0.2.1--0.3.0.sql +++ b/sql/trunklet--0.2.1--0.3.0.sql @@ -1,3 +1,17 @@ +/* + * Using temp objects will result in the extension being dropped after session + * end. Create a real schema and then explicitly drop it instead. + */ +CREATE SCHEMA __trunklet; + +CREATE TABLE __trunklet.old_settings AS + SELECT name, setting + FROM pg_catalog.pg_settings + WHERE name IN ('client_min_messages', 'search_path') +; +SET LOCAL client_min_messages = warning; +SET LOCAL search_path = pg_catalog; + CREATE OR REPLACE FUNCTION _trunklet.language__get_loose( language_id _trunklet.language.language_id%TYPE ) RETURNS _trunklet.language STABLE LANGUAGE sql AS $body$ @@ -315,5 +329,7 @@ REVOKE ALL ON FUNCTION trunklet.template_language__remove( ) FROM public; +DROP TABLE __trunklet.old_settings; +DROP SCHEMA __trunklet; -- vi: expandtab sw=2 ts=2 diff --git a/sql/trunklet--0.3.2--0.3.3.sql b/sql/trunklet--0.3.2--0.3.3.sql index f8dff62..cd513b3 100644 --- a/sql/trunklet--0.3.2--0.3.3.sql +++ b/sql/trunklet--0.3.2--0.3.3.sql @@ -1,3 +1,16 @@ +/* + * Using temp objects will result in the extension being dropped after session + * end. Create a real schema and then explicitly drop it instead. + */ +CREATE SCHEMA __trunklet; + +CREATE TABLE __trunklet.old_settings AS + SELECT name, setting + FROM pg_catalog.pg_settings + WHERE name IN ('client_min_messages')-- This doesn't actually work as expected :( , 'search_path') +; +SET LOCAL client_min_messages = warning; + CREATE OR REPLACE FUNCTION _trunklet.language__get( language_name _trunklet.language.language_name%TYPE ) RETURNS _trunklet.language STABLE LANGUAGE plpgsql AS $body$ @@ -167,4 +180,19 @@ REVOKE ALL ON FUNCTION trunklet.template_language__remove( , cascade boolean , boolean ) FROM public; + + + +SELECT _trunklet.exec( + format( + 'SET LOCAL %I = %L' + , name + , setting + ) + ) + FROM __trunklet.old_settings +; +DROP TABLE __trunklet.old_settings; +DROP SCHEMA __trunklet; + -- vi: expandtab sw=2 ts=2 diff --git a/sql/trunklet.sql b/sql/trunklet.sql index 68a3121..faf65e3 100644 --- a/sql/trunklet.sql +++ b/sql/trunklet.sql @@ -4,6 +4,20 @@ * */ +/* + * Using temp objects will result in the extension being dropped after session + * end. Create a real schema and then explicitly drop it instead. + */ +CREATE SCHEMA __trunklet; + +CREATE TABLE __trunklet.old_settings AS + SELECT name, setting + FROM pg_catalog.pg_settings + WHERE name IN ('client_min_messages')-- This doesn't actually work as expected :( , 'search_path') +; +SET LOCAL client_min_messages = warning; +-- PG apparently truncates the path to a single element, so our reset doesn't work :( SET LOCAL search_path = pg_catalog; + DO $do$ BEGIN CREATE ROLE trunklet__dependency; @@ -942,4 +956,18 @@ BEGIN END $body$; */ + + +SELECT _trunklet.exec( + format( + 'SET LOCAL %I = %L' + , name + , setting + ) + ) + FROM __trunklet.old_settings +; +DROP TABLE __trunklet.old_settings; +DROP SCHEMA __trunklet; + -- vi: expandtab sw=2 ts=2 diff --git a/test/build/build.sql b/test/build/build.sql index 5a67e8f..5b592e3 100644 --- a/test/build/build.sql +++ b/test/build/build.sql @@ -4,8 +4,6 @@ \i test/helpers/psql.sql BEGIN; --- CREATE EXTENSION raises client_min_messages to warning during the script; \i doesn't, so set it here. -SET LOCAL client_min_messages = warning; \i sql/trunklet.sql ROLLBACK; diff --git a/test/build/expected/build.out b/test/build/expected/build.out index 36fb97d..2ae39bb 100644 --- a/test/build/expected/build.out +++ b/test/build/expected/build.out @@ -1,4 +1,9 @@ \set ECHO none + exec +------ + +(1 row) + 1..4 ok 1 - CREATE EXTENSION trunklet; ok 2 - Verify search_path is back to something sane after extension creation. From 4126ec2bacedbbccce3dcf338b34b4d3afc8f9e8 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Wed, 28 Oct 2026 13:21:20 -0500 Subject: [PATCH 05/14] Clarify HISTORY entry for the SET LOCAL change --- HISTORY.asc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index 61c2cb3..8f0de8f 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -7,8 +7,8 @@ STABLE Updated installs now also match a fresh install: the `language__get()` debug message that a fresh 0.3.3 install has is added by `0.3.3--stable` and was retroactively added to the already-released `0.3.2--0.3.3` update script. === Stop update scripts from leaking `search_path` and `client_min_messages` -`ALTER EXTENSION trunklet UPDATE` no longer leaves the caller's `search_path` or `client_min_messages` changed. -The released `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to make their `SET` statements and the set-back `SET LOCAL`. +`ALTER EXTENSION trunklet UPDATE` and a fresh `CREATE EXTENSION` at `stable` no longer leave the caller's `search_path` or `client_min_messages` changed. Installing 0.3.3 or earlier by version still changes them. +The `0.2.0--0.2.1`, `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to change `SET` to `SET LOCAL`, including in the settings set-back. 0.3.3 ----- From e1a34af43de1b22a97c858d1977cbdb445b92251 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Wed, 28 Oct 2026 15:01:48 -0500 Subject: [PATCH 06/14] Make HISTORY entry precise about which settings each install path changes --- HISTORY.asc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.asc b/HISTORY.asc index 8f0de8f..8480a9a 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -7,7 +7,7 @@ STABLE Updated installs now also match a fresh install: the `language__get()` debug message that a fresh 0.3.3 install has is added by `0.3.3--stable` and was retroactively added to the already-released `0.3.2--0.3.3` update script. === Stop update scripts from leaking `search_path` and `client_min_messages` -`ALTER EXTENSION trunklet UPDATE` and a fresh `CREATE EXTENSION` at `stable` no longer leave the caller's `search_path` or `client_min_messages` changed. Installing 0.3.3 or earlier by version still changes them. +`ALTER EXTENSION trunklet UPDATE` no longer leaves the caller's `search_path` or `client_min_messages` changed, and a fresh `CREATE EXTENSION` at `stable` no longer leaves `client_min_messages` changed. Installing 0.3.3 by version still changes `client_min_messages`, and installing 0.2.0 or 0.2.1 by version still changes both. The `0.2.0--0.2.1`, `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to change `SET` to `SET LOCAL`, including in the settings set-back. 0.3.3 From 54c25d1c70fc7a183be2f3d2b0372a40210e6a59 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Wed, 28 Oct 2026 16:47:38 -0500 Subject: [PATCH 07/14] Remove stale search_path comment from update test helper --- test/helpers/update_with_data.sql | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/helpers/update_with_data.sql b/test/helpers/update_with_data.sql index c34cbc2..e431737 100644 --- a/test/helpers/update_with_data.sql +++ b/test/helpers/update_with_data.sql @@ -59,8 +59,6 @@ BEGIN END $body$; --- Updating from before 0.3.0 changes search_path, so qualify from here on - DO $body$ DECLARE c_ids CONSTANT int[] := (SELECT ids FROM seeded_template_ids); From baccd69e28a952f384c6908902cdd9cc4a19c066 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Wed, 28 Oct 2026 16:51:15 -0500 Subject: [PATCH 08/14] Check every user-settable setting in the settings-unchanged tests test/helpers/settings_snapshot.sql snapshots every pg_settings row with context user or superuser and exposes a view of the ones that changed. search_path.sql and update_with_data.sql both use it, replacing their separate search_path/client_min_messages checks. --- test/expected/search_path.out | 5 ++--- test/helpers/settings_snapshot.sql | 19 +++++++++++++++++++ test/helpers/update_with_data.sql | 28 +++++++++++----------------- test/sql/search_path.sql | 22 +++++++--------------- 4 files changed, 39 insertions(+), 35 deletions(-) create mode 100644 test/helpers/settings_snapshot.sql diff --git a/test/expected/search_path.out b/test/expected/search_path.out index 0bcaed1..285fcf9 100644 --- a/test/expected/search_path.out +++ b/test/expected/search_path.out @@ -1,6 +1,5 @@ \set ECHO none -1..2 +1..1 tap, public -ok 1 - verify search_path has not changed -ok 2 - verify client_min_messages has not changed +ok 1 - CREATE EXTENSION did not change any user-settable setting # TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/helpers/settings_snapshot.sql b/test/helpers/settings_snapshot.sql new file mode 100644 index 0000000..8190e72 --- /dev/null +++ b/test/helpers/settings_snapshot.sql @@ -0,0 +1,19 @@ +/* + * Snapshots every user-settable setting (pg_settings context 'user' or + * 'superuser', the only ones SET can change in-session) into + * pg_temp.settings_before. pg_temp.settings_changed lists each snapshotted + * setting whose current value differs, or that no longer exists; settings that + * first appear later are ignored, since loading a library can define new ones. + */ +CREATE TEMP TABLE settings_before AS + SELECT name, setting FROM pg_settings WHERE context IN ('user', 'superuser') +; + +CREATE TEMP VIEW settings_changed AS + SELECT b.name, b.setting AS before, s.setting AS after + FROM pg_temp.settings_before b + LEFT JOIN pg_settings s USING (name) + WHERE s.setting IS DISTINCT FROM b.setting +; + +-- vi: expandtab ts=2 sw=2 diff --git a/test/helpers/update_with_data.sql b/test/helpers/update_with_data.sql index e431737..d87eb6f 100644 --- a/test/helpers/update_with_data.sql +++ b/test/helpers/update_with_data.sql @@ -14,35 +14,29 @@ SET search_path = _trunklet_test; CREATE TABLE seeded_template_ids AS SELECT get_test_templates() AS ids; /* - * An update script must leave the caller's settings as it found them. Reset + * An update script must leave every user-settable setting as it found it. Reset * client_min_messages first: TEST_UPDATE_FROM's own install script may have left * it raised, which would hide an update script doing the same. */ SET client_min_messages = notice; -DO $body$ -DECLARE - s text; -BEGIN - FOREACH s IN ARRAY ARRAY['search_path', 'client_min_messages'] LOOP - PERFORM set_config('trunklet.test_before_' || s, current_setting(s), false); - END LOOP; -END -$body$; +\i test/helpers/settings_snapshot.sql ALTER EXTENSION trunklet UPDATE; DO $body$ DECLARE - s text; + c_changed CONSTANT text := ( + SELECT string_agg(format('%s: %L -> %L', name, before, after), ', ' ORDER BY name) + FROM pg_temp.settings_changed + ); BEGIN - FOREACH s IN ARRAY ARRAY['search_path', 'client_min_messages'] LOOP - IF current_setting(s) IS DISTINCT FROM current_setting('trunklet.test_before_' || s) THEN - RAISE EXCEPTION 'ALTER EXTENSION UPDATE changed % from % to %' - , s, current_setting('trunklet.test_before_' || s), current_setting(s); - END IF; - END LOOP; + IF c_changed IS NOT NULL THEN + RAISE EXCEPTION 'ALTER EXTENSION UPDATE changed settings: %', c_changed; + END IF; END $body$; +DROP VIEW pg_temp.settings_changed; +DROP TABLE pg_temp.settings_before; DO $body$ DECLARE diff --git a/test/sql/search_path.sql b/test/sql/search_path.sql index 6607414..06ab978 100644 --- a/test/sql/search_path.sql +++ b/test/sql/search_path.sql @@ -4,8 +4,7 @@ SELECT plan( 0 - +1 -- verify search_path - +1 -- verify client_min_messages + +1 -- no setting changed ); -- Drop test/install/load.sql's committed install; this test's rollback restores it @@ -14,28 +13,21 @@ DROP EXTENSION trunklet; -- Start from a value the extension script could visibly change SET client_min_messages = notice; -CREATE TEMP TABLE before AS - SELECT name, setting FROM pg_settings WHERE name IN ('search_path', 'client_min_messages') -; +\i test/helpers/settings_snapshot.sql SHOW search_path; CREATE EXTENSION trunklet;-- CASCADE; -CREATE TEMP TABLE after AS - SELECT name, setting FROM pg_settings WHERE name IN ('search_path', 'client_min_messages') -; +CREATE TEMP TABLE changed AS SELECT * FROM pg_temp.settings_changed; -- Forcibly reset it so we know tap will work SET search_path=public,tap; -SELECT is( - (SELECT setting FROM after WHERE name = n) - , (SELECT setting FROM before WHERE name = n) - , 'verify ' || n || ' has not changed' -) - FROM unnest(ARRAY['search_path', 'client_min_messages']) AS n -; +SELECT is_empty( + 'SELECT * FROM pg_temp.changed' + , 'CREATE EXTENSION did not change any user-settable setting' +); \i test/pgxntool/finish.sql From f5624769312b14519a800855a0c25ba0a574fb7f Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 29 Oct 2026 15:43:33 -0500 Subject: [PATCH 09/14] Check settings in load.sql; make released install scripts SET LOCAL `test/install/load.sql` snapshots every user-settable setting and checks it after `CREATE EXTENSION`, and `test/helpers/update_with_data.sql` uses the same `pg_temp.settings_snapshot()`/`pg_temp.settings_check()` pair around `ALTER EXTENSION UPDATE`. `test/sql/search_path.sql` is removed. An error in `load.sql` fails the run only by breaking the downstream tests, so the `CREATE` and its check run in one transaction: a leak rolls back the install and the suite fails. The released 0.2.0, 0.2.1 and 0.3.3 install scripts now use `SET LOCAL`, including in the settings set-back, so installing them by version no longer leaks `search_path` or `client_min_messages`. Each released script changed from plain `SET` has a comment saying so. --- HISTORY.asc | 6 ++--- RELEASE.md | 2 +- sql/trunklet--0.2.0.sql | 6 +++-- sql/trunklet--0.2.1--0.3.0.sql | 1 + sql/trunklet--0.2.1.sql | 8 +++--- sql/trunklet--0.3.2--0.3.3.sql | 2 ++ sql/trunklet--0.3.3.sql | 6 +++-- test/expected/search_path.out | 5 ---- test/helpers/settings_snapshot.sql | 42 +++++++++++++++++++++--------- test/helpers/update_with_data.sql | 34 +++++------------------- test/install/load.sql | 17 +++++++++--- test/sql/search_path.sql | 34 ------------------------ 12 files changed, 70 insertions(+), 93 deletions(-) delete mode 100644 test/expected/search_path.out delete mode 100644 test/sql/search_path.sql diff --git a/HISTORY.asc b/HISTORY.asc index 8480a9a..5eb1292 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -6,9 +6,9 @@ STABLE `ALTER EXTENSION trunklet UPDATE` now works from 0.2.0. Previously there was no update path from 0.2.0. Updated installs now also match a fresh install: the `language__get()` debug message that a fresh 0.3.3 install has is added by `0.3.3--stable` and was retroactively added to the already-released `0.3.2--0.3.3` update script. -=== Stop update scripts from leaking `search_path` and `client_min_messages` -`ALTER EXTENSION trunklet UPDATE` no longer leaves the caller's `search_path` or `client_min_messages` changed, and a fresh `CREATE EXTENSION` at `stable` no longer leaves `client_min_messages` changed. Installing 0.3.3 by version still changes `client_min_messages`, and installing 0.2.0 or 0.2.1 by version still changes both. -The `0.2.0--0.2.1`, `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to change `SET` to `SET LOCAL`, including in the settings set-back. +=== Stop install and update scripts from leaking `search_path` and `client_min_messages` +`CREATE EXTENSION trunklet`, at any version, and `ALTER EXTENSION trunklet UPDATE` no longer leave the caller's `search_path` or `client_min_messages` changed. +The `0.2.0`, `0.2.1` and `0.3.3` install scripts and the `0.2.0--0.2.1`, `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to change `SET` to `SET LOCAL`, including in the settings set-back where a script has one. 0.3.3 ----- diff --git a/RELEASE.md b/RELEASE.md index 7e0a937..7bdf9dd 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -6,4 +6,4 @@ Follow [`../ai/RELEASE.md`](https://github.com/Postgres-Extensions/ai/blob/main/ Each of those releases is a branch on `origin` named exactly the version; the repo has no tags before the first `make tag` release. Where step 1 says to compare against the tag, compare against that version's branch tip instead. -`sql/trunklet--0.2.0.sql`, `sql/trunklet--0.2.1.sql` and `sql/trunklet--0.3.3.sql` were committed after those versions shipped, so step 1 will flag their late last-touch dates. That is the backfill exception it describes, not drift. Each matches `sql/trunklet.sql` on its version branch, except that the 0.3.3 copy adds pgxntool's `/* DO NOT EDIT - AUTO-GENERATED FILE */` header line. +`sql/trunklet--0.2.0.sql`, `sql/trunklet--0.2.1.sql` and `sql/trunklet--0.3.3.sql` were committed after those versions shipped, so step 1 will flag their late last-touch dates. That is the backfill exception it describes, not drift. Each matches `sql/trunklet.sql` on its version branch except that its script-level `SET` commands, including any settings set-back, are now `SET LOCAL` and commented as released with plain `SET`, and the 0.3.3 copy also adds pgxntool's `/* DO NOT EDIT - AUTO-GENERATED FILE */` header line. diff --git a/sql/trunklet--0.2.0.sql b/sql/trunklet--0.2.0.sql index 3f23ef4..602d45e 100644 --- a/sql/trunklet--0.2.0.sql +++ b/sql/trunklet--0.2.0.sql @@ -4,10 +4,12 @@ * */ -SET client_min_messages = warning; +-- Released as plain SET, which leaks the setting to the caller +SET LOCAL client_min_messages = warning; -- Set a safe search_path -SET search_path = pg_catalog; +-- Released as plain SET, which leaks the setting to the caller +SET LOCAL search_path = pg_catalog; DO $do$ BEGIN diff --git a/sql/trunklet--0.2.1--0.3.0.sql b/sql/trunklet--0.2.1--0.3.0.sql index 5ff06f7..9d83db6 100644 --- a/sql/trunklet--0.2.1--0.3.0.sql +++ b/sql/trunklet--0.2.1--0.3.0.sql @@ -9,6 +9,7 @@ CREATE TABLE __trunklet.old_settings AS FROM pg_catalog.pg_settings WHERE name IN ('client_min_messages', 'search_path') ; +-- Released as plain SET, which leaks the setting to the caller SET LOCAL client_min_messages = warning; SET LOCAL search_path = pg_catalog; diff --git a/sql/trunklet--0.2.1.sql b/sql/trunklet--0.2.1.sql index 6a32c74..f159233 100644 --- a/sql/trunklet--0.2.1.sql +++ b/sql/trunklet--0.2.1.sql @@ -15,8 +15,9 @@ CREATE TABLE __trunklet.old_settings AS FROM pg_catalog.pg_settings WHERE name IN ('client_min_messages', 'search_path') ; -SET client_min_messages = warning; -SET search_path = pg_catalog; +-- Released as plain SET, which leaks the setting to the caller +SET LOCAL client_min_messages = warning; +SET LOCAL search_path = pg_catalog; DO $do$ BEGIN @@ -807,7 +808,8 @@ $body$; SELECT _trunklet.exec( format( - 'SET %I = %L' + -- Released as plain SET, which leaks the setting to the caller + 'SET LOCAL %I = %L' , name , setting ) diff --git a/sql/trunklet--0.3.2--0.3.3.sql b/sql/trunklet--0.3.2--0.3.3.sql index cd513b3..293955b 100644 --- a/sql/trunklet--0.3.2--0.3.3.sql +++ b/sql/trunklet--0.3.2--0.3.3.sql @@ -9,6 +9,7 @@ CREATE TABLE __trunklet.old_settings AS FROM pg_catalog.pg_settings WHERE name IN ('client_min_messages')-- This doesn't actually work as expected :( , 'search_path') ; +-- Released as plain SET, which leaks the setting to the caller SET LOCAL client_min_messages = warning; CREATE OR REPLACE FUNCTION _trunklet.language__get( @@ -185,6 +186,7 @@ REVOKE ALL ON FUNCTION trunklet.template_language__remove( SELECT _trunklet.exec( format( + -- Released as plain SET, which leaks the setting to the caller 'SET LOCAL %I = %L' , name , setting diff --git a/sql/trunklet--0.3.3.sql b/sql/trunklet--0.3.3.sql index 85fc93b..c13e507 100644 --- a/sql/trunklet--0.3.3.sql +++ b/sql/trunklet--0.3.3.sql @@ -16,7 +16,8 @@ CREATE TABLE __trunklet.old_settings AS FROM pg_catalog.pg_settings WHERE name IN ('client_min_messages')-- This doesn't actually work as expected :( , 'search_path') ; -SET client_min_messages = warning; +-- Released as plain SET, which leaks the setting to the caller +SET LOCAL client_min_messages = warning; -- PG apparently truncates the path to a single element, so our reset doesn't work :( SET LOCAL search_path = pg_catalog; DO $do$ @@ -961,7 +962,8 @@ $body$; SELECT _trunklet.exec( format( - 'SET %I = %L' + -- Released as plain SET, which leaks the setting to the caller + 'SET LOCAL %I = %L' , name , setting ) diff --git a/test/expected/search_path.out b/test/expected/search_path.out deleted file mode 100644 index 285fcf9..0000000 --- a/test/expected/search_path.out +++ /dev/null @@ -1,5 +0,0 @@ -\set ECHO none -1..1 -tap, public -ok 1 - CREATE EXTENSION did not change any user-settable setting -# TRANSACTION INTENTIONALLY LEFT OPEN! diff --git a/test/helpers/settings_snapshot.sql b/test/helpers/settings_snapshot.sql index 8190e72..c2de743 100644 --- a/test/helpers/settings_snapshot.sql +++ b/test/helpers/settings_snapshot.sql @@ -1,19 +1,35 @@ /* - * Snapshots every user-settable setting (pg_settings context 'user' or - * 'superuser', the only ones SET can change in-session) into - * pg_temp.settings_before. pg_temp.settings_changed lists each snapshotted - * setting whose current value differs, or that no longer exists; settings that - * first appear later are ignored, since loading a library can define new ones. + * pg_temp.settings_snapshot() records every user-settable setting (pg_settings + * context 'user' or 'superuser', the only ones SET can change in-session), + * replacing any earlier snapshot. pg_temp.settings_check(action) raises an + * error naming action if any snapshotted setting has since changed or no longer + * exists. Settings that first appear later are ignored, since loading a library + * can define new ones. */ -CREATE TEMP TABLE settings_before AS - SELECT name, setting FROM pg_settings WHERE context IN ('user', 'superuser') -; +CREATE TEMP TABLE settings_before(name text PRIMARY KEY, setting text); -CREATE TEMP VIEW settings_changed AS - SELECT b.name, b.setting AS before, s.setting AS after - FROM pg_temp.settings_before b - LEFT JOIN pg_settings s USING (name) - WHERE s.setting IS DISTINCT FROM b.setting +CREATE FUNCTION pg_temp.settings_snapshot() RETURNS void LANGUAGE sql AS $body$ +DELETE FROM pg_temp.settings_before; +INSERT INTO pg_temp.settings_before + SELECT name, setting FROM pg_catalog.pg_settings WHERE context IN ('user', 'superuser') ; +$body$; + +CREATE FUNCTION pg_temp.settings_check(action text) RETURNS void LANGUAGE plpgsql AS $body$ +DECLARE + c_changed CONSTANT text := ( + SELECT string_agg(format('%s: %L -> %L', b.name, b.setting, s.setting), ', ' ORDER BY b.name) + FROM pg_temp.settings_before b + LEFT JOIN pg_catalog.pg_settings s USING (name) + WHERE s.setting IS DISTINCT FROM b.setting + ); +BEGIN + IF c_changed IS NOT NULL THEN + RAISE EXCEPTION '% changed settings', action + USING DETAIL = c_changed + ; + END IF; +END +$body$; -- vi: expandtab ts=2 sw=2 diff --git a/test/helpers/update_with_data.sql b/test/helpers/update_with_data.sql index d87eb6f..a747063 100644 --- a/test/helpers/update_with_data.sql +++ b/test/helpers/update_with_data.sql @@ -1,10 +1,11 @@ /* * Seeds a test language and a test template under TEST_UPDATE_FROM, runs ALTER - * EXTENSION UPDATE, and checks the version and that the template still - * processes. It then removes the language and checks that nothing is left - * behind, so the suite's expected output matches fresh mode. + * EXTENSION UPDATE, and checks that it changed no setting, the version and that + * the template still processes. It then removes the language and checks that + * nothing is left behind, so the suite's expected output matches fresh mode. * - * test/install/load.sql runs it in update mode. + * test/install/load.sql runs it in update mode, after loading + * test/helpers/settings_snapshot.sql. */ CREATE SCHEMA _trunklet_test; SET search_path = _trunklet_test; @@ -13,30 +14,9 @@ SET search_path = _trunklet_test; -- Committed before the update, as stored data would be in production CREATE TABLE seeded_template_ids AS SELECT get_test_templates() AS ids; -/* - * An update script must leave every user-settable setting as it found it. Reset - * client_min_messages first: TEST_UPDATE_FROM's own install script may have left - * it raised, which would hide an update script doing the same. - */ -SET client_min_messages = notice; -\i test/helpers/settings_snapshot.sql - +SELECT pg_temp.settings_snapshot(); ALTER EXTENSION trunklet UPDATE; - -DO $body$ -DECLARE - c_changed CONSTANT text := ( - SELECT string_agg(format('%s: %L -> %L', name, before, after), ', ' ORDER BY name) - FROM pg_temp.settings_changed - ); -BEGIN - IF c_changed IS NOT NULL THEN - RAISE EXCEPTION 'ALTER EXTENSION UPDATE changed settings: %', c_changed; - END IF; -END -$body$; -DROP VIEW pg_temp.settings_changed; -DROP TABLE pg_temp.settings_before; +SELECT pg_temp.settings_check('ALTER EXTENSION UPDATE'); DO $body$ DECLARE diff --git a/test/install/load.sql b/test/install/load.sql index 6e50390..8f39288 100644 --- a/test/install/load.sql +++ b/test/install/load.sql @@ -4,12 +4,21 @@ * and "Update & Upgrade (U&U) Testing" sections). The mode comes from the * Makefile's TEST_LOAD_SOURCE / TEST_UPDATE_FROM via PGOPTIONS. * - * This file's output is never compared against anything. Its \set ON_ERROR_STOP - * makes psql exit non-zero on an error, which fails the run only on PG16+. On - * PG 9.5-15 a failure surfaces only through the downstream tests. + * This file's output is never compared against anything, and an error here + * fails the run only by breaking the downstream tests. \set ON_ERROR_STOP makes + * psql stop at the first error. */ \set ON_ERROR_STOP on +\i test/helpers/settings_snapshot.sql + +-- A value an install script would visibly change +SET client_min_messages = notice; +SELECT pg_temp.settings_snapshot(); + +-- One transaction, so a failed settings check rolls back the install and the +-- downstream tests fail +BEGIN; -- Branches server-side because \if needs psql 10+ DO $body$ DECLARE @@ -28,6 +37,8 @@ BEGIN END IF; END $body$; +SELECT pg_temp.settings_check('CREATE EXTENSION'); +COMMIT; -- Update mode only: update_with_data.sql does the ALTER EXTENSION UPDATE. Branches -- server-side for the same reason as above. diff --git a/test/sql/search_path.sql b/test/sql/search_path.sql deleted file mode 100644 index 06ab978..0000000 --- a/test/sql/search_path.sql +++ /dev/null @@ -1,34 +0,0 @@ -\set ECHO none -\i test/pgxntool/setup.sql - -SELECT plan( - 0 - - +1 -- no setting changed -); - --- Drop test/install/load.sql's committed install; this test's rollback restores it -DROP EXTENSION trunklet; - --- Start from a value the extension script could visibly change -SET client_min_messages = notice; - -\i test/helpers/settings_snapshot.sql - -SHOW search_path; - -CREATE EXTENSION trunklet;-- CASCADE; - -CREATE TEMP TABLE changed AS SELECT * FROM pg_temp.settings_changed; - --- Forcibly reset it so we know tap will work -SET search_path=public,tap; - -SELECT is_empty( - 'SELECT * FROM pg_temp.changed' - , 'CREATE EXTENSION did not change any user-settable setting' -); - -\i test/pgxntool/finish.sql - --- vi: expandtab ts=2 sw=2 From ad3efa607f5c7d3395370adaa11ca13e2261939d Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 29 Oct 2026 15:56:00 -0500 Subject: [PATCH 10/14] Narrow the settings-leak claims to what the scripts guarantee The 0.2.0 install and the 0.2.1--0.3.0 update have no set-back, so a caller at error sees client_min_messages at warning until the transaction ends. State that in HISTORY.asc and the PR description, move the set-back "released as plain SET" comments above their statements, note the two shipped update scripts in RELEASE.md, and clarify two test comments. --- HISTORY.asc | 4 ++-- RELEASE.md | 2 +- sql/trunklet--0.2.1.sql | 2 +- sql/trunklet--0.3.2--0.3.3.sql | 2 +- sql/trunklet--0.3.3.sql | 2 +- test/helpers/update_with_data.sql | 7 ++++--- test/install/load.sql | 2 +- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index 5eb1292..4fba5ba 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -7,8 +7,8 @@ STABLE Updated installs now also match a fresh install: the `language__get()` debug message that a fresh 0.3.3 install has is added by `0.3.3--stable` and was retroactively added to the already-released `0.3.2--0.3.3` update script. === Stop install and update scripts from leaking `search_path` and `client_min_messages` -`CREATE EXTENSION trunklet`, at any version, and `ALTER EXTENSION trunklet UPDATE` no longer leave the caller's `search_path` or `client_min_messages` changed. -The `0.2.0`, `0.2.1` and `0.3.3` install scripts and the `0.2.0--0.2.1`, `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to change `SET` to `SET LOCAL`, including in the settings set-back where a script has one. +After the transaction running `CREATE EXTENSION trunklet` or `ALTER EXTENSION trunklet UPDATE` ends, the caller's `search_path` and `client_min_messages` are unchanged. Within it, `search_path` is always unchanged; `client_min_messages` is too, except that a caller at `error` sees `warning` until the transaction ends when installing `0.2.0` or when updating from `0.2.0` or `0.2.1` (the `0.2.1--0.3.0` step). +The `0.2.0`, `0.2.1` and `0.3.3` install scripts and the `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to change `SET` to `SET LOCAL`, including in the settings set-back where a script has one, and to add a comment that they were released with plain `SET`. 0.3.3 ----- diff --git a/RELEASE.md b/RELEASE.md index 7bdf9dd..1710c47 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -6,4 +6,4 @@ Follow [`../ai/RELEASE.md`](https://github.com/Postgres-Extensions/ai/blob/main/ Each of those releases is a branch on `origin` named exactly the version; the repo has no tags before the first `make tag` release. Where step 1 says to compare against the tag, compare against that version's branch tip instead. -`sql/trunklet--0.2.0.sql`, `sql/trunklet--0.2.1.sql` and `sql/trunklet--0.3.3.sql` were committed after those versions shipped, so step 1 will flag their late last-touch dates. That is the backfill exception it describes, not drift. Each matches `sql/trunklet.sql` on its version branch except that its script-level `SET` commands, including any settings set-back, are now `SET LOCAL` and commented as released with plain `SET`, and the 0.3.3 copy also adds pgxntool's `/* DO NOT EDIT - AUTO-GENERATED FILE */` header line. +`sql/trunklet--0.2.0.sql`, `sql/trunklet--0.2.1.sql` and `sql/trunklet--0.3.3.sql` were committed after those versions shipped, so step 1 will flag their late last-touch dates. That is the backfill exception it describes, not drift. Each matches `sql/trunklet.sql` on its version branch except that its script-level `SET` commands, including any settings set-back, are now `SET LOCAL` and commented as released with plain `SET`, and the 0.3.3 copy also adds pgxntool's `/* DO NOT EDIT - AUTO-GENERATED FILE */` header line. `sql/trunklet--0.2.1--0.3.0.sql` and `sql/trunklet--0.3.2--0.3.3.sql` were also edited after release, so step 1 will flag them too, and their content intentionally differs from what shipped: both have their `SET` commands (and `0.3.2--0.3.3`'s set-back) changed to `SET LOCAL` with a comment, and `0.3.2--0.3.3` additionally gains the `language__get()` debug-message function that its release branch lacks. diff --git a/sql/trunklet--0.2.1.sql b/sql/trunklet--0.2.1.sql index f159233..1d03b6f 100644 --- a/sql/trunklet--0.2.1.sql +++ b/sql/trunklet--0.2.1.sql @@ -806,9 +806,9 @@ $body$; +-- Released as plain SET, which leaks the setting to the caller SELECT _trunklet.exec( format( - -- Released as plain SET, which leaks the setting to the caller 'SET LOCAL %I = %L' , name , setting diff --git a/sql/trunklet--0.3.2--0.3.3.sql b/sql/trunklet--0.3.2--0.3.3.sql index 293955b..bc66832 100644 --- a/sql/trunklet--0.3.2--0.3.3.sql +++ b/sql/trunklet--0.3.2--0.3.3.sql @@ -184,9 +184,9 @@ REVOKE ALL ON FUNCTION trunklet.template_language__remove( +-- Released as plain SET, which leaks the setting to the caller SELECT _trunklet.exec( format( - -- Released as plain SET, which leaks the setting to the caller 'SET LOCAL %I = %L' , name , setting diff --git a/sql/trunklet--0.3.3.sql b/sql/trunklet--0.3.3.sql index c13e507..529e987 100644 --- a/sql/trunklet--0.3.3.sql +++ b/sql/trunklet--0.3.3.sql @@ -960,9 +960,9 @@ $body$; */ +-- Released as plain SET, which leaks the setting to the caller SELECT _trunklet.exec( format( - -- Released as plain SET, which leaks the setting to the caller 'SET LOCAL %I = %L' , name , setting diff --git a/test/helpers/update_with_data.sql b/test/helpers/update_with_data.sql index a747063..5adf164 100644 --- a/test/helpers/update_with_data.sql +++ b/test/helpers/update_with_data.sql @@ -1,8 +1,9 @@ /* * Seeds a test language and a test template under TEST_UPDATE_FROM, runs ALTER - * EXTENSION UPDATE, and checks that it changed no setting, the version and that - * the template still processes. It then removes the language and checks that - * nothing is left behind, so the suite's expected output matches fresh mode. + * EXTENSION UPDATE, and checks that the update changed no setting, that the + * extension is at the expected version, and that the template still processes. + * It then removes the language and checks that nothing is left behind, so the + * suite's expected output matches fresh mode. * * test/install/load.sql runs it in update mode, after loading * test/helpers/settings_snapshot.sql. diff --git a/test/install/load.sql b/test/install/load.sql index 8f39288..f7dda9e 100644 --- a/test/install/load.sql +++ b/test/install/load.sql @@ -12,7 +12,7 @@ \i test/helpers/settings_snapshot.sql --- A value an install script would visibly change +-- Below warning, so the server saves it and a plain SET in a script would show (error or higher is not exercised) SET client_min_messages = notice; SELECT pg_temp.settings_snapshot(); From 91bb4f52a3d6aaf28ab1e303db55b4cb2e2570f1 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 29 Oct 2026 16:02:17 -0500 Subject: [PATCH 11/14] Check settings in the build test with the shared helper under pgTap test/build/build.sql now snapshots settings before CREATE EXTENSION and asserts via lives_ok that none changed, replacing the weaker search_path check, so the repo has one settings check. --- test/build/build.sql | 9 +++++---- test/build/expected/build.out | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/test/build/build.sql b/test/build/build.sql index 5b592e3..e3a82e8 100644 --- a/test/build/build.sql +++ b/test/build/build.sql @@ -9,6 +9,8 @@ ROLLBACK; BEGIN; \i test/helpers/tap_setup.sql +\i test/helpers/settings_snapshot.sql +SELECT pg_temp.settings_snapshot() AS snapshot \gset SELECT plan(4); @@ -17,10 +19,9 @@ SELECT lives_ok( , 'CREATE EXTENSION trunklet;' ); -SELECT isnt( - current_setting('search_path') - , 'pg_catalog' - , 'Verify search_path is back to something sane after extension creation.' +SELECT lives_ok( + $$SELECT pg_temp.settings_check('CREATE EXTENSION')$$ + , 'CREATE EXTENSION changed no setting' ); SELECT hasnt_schema('__trunklet'); diff --git a/test/build/expected/build.out b/test/build/expected/build.out index 2ae39bb..dd511c1 100644 --- a/test/build/expected/build.out +++ b/test/build/expected/build.out @@ -6,6 +6,6 @@ 1..4 ok 1 - CREATE EXTENSION trunklet; -ok 2 - Verify search_path is back to something sane after extension creation. +ok 2 - CREATE EXTENSION changed no setting ok 3 - Schema __trunklet should not exist ok 4 - DROP EXTENSION trunklet; From 16ba7e20067f44c2f1718c1fe611afd2336c0b6a Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 29 Oct 2026 16:10:15 -0500 Subject: [PATCH 12/14] Make unreleased install scripts follow the extension-script settings standard trunklet.sql and trunklet--0.2.0--0.2.1.sql no longer set client_min_messages. trunklet.sql now runs with search_path = pg_catalog, set back with SET LOCAL. The build test sets client_min_messages itself before \i-ing trunklet.sql. --- HISTORY.asc | 1 + sql/trunklet--0.2.0--0.2.1.sql | 3 +-- sql/trunklet.sql | 5 ++--- test/build/build.sql | 1 + 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index 4fba5ba..d393fe8 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -9,6 +9,7 @@ Updated installs now also match a fresh install: the `language__get()` debug mes === Stop install and update scripts from leaking `search_path` and `client_min_messages` After the transaction running `CREATE EXTENSION trunklet` or `ALTER EXTENSION trunklet UPDATE` ends, the caller's `search_path` and `client_min_messages` are unchanged. Within it, `search_path` is always unchanged; `client_min_messages` is too, except that a caller at `error` sees `warning` until the transaction ends when installing `0.2.0` or when updating from `0.2.0` or `0.2.1` (the `0.2.1--0.3.0` step). The `0.2.0`, `0.2.1` and `0.3.3` install scripts and the `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to change `SET` to `SET LOCAL`, including in the settings set-back where a script has one, and to add a comment that they were released with plain `SET`. +The current version's install script (`trunklet.sql`) no longer sets `client_min_messages`, and now runs with `search_path = pg_catalog`, set back with `SET LOCAL`. `0.2.0--0.2.1` likewise no longer sets `client_min_messages`. 0.3.3 ----- diff --git a/sql/trunklet--0.2.0--0.2.1.sql b/sql/trunklet--0.2.0--0.2.1.sql index f702cb7..d8c395f 100644 --- a/sql/trunklet--0.2.0--0.2.1.sql +++ b/sql/trunklet--0.2.0--0.2.1.sql @@ -7,9 +7,8 @@ CREATE SCHEMA __trunklet; CREATE TABLE __trunklet.old_settings AS SELECT name, setting FROM pg_catalog.pg_settings - WHERE name IN ('client_min_messages', 'search_path') + WHERE name IN ('search_path') ; -SET LOCAL client_min_messages = warning; SET LOCAL search_path = pg_catalog; CREATE OR REPLACE FUNCTION _trunklet.template__get( diff --git a/sql/trunklet.sql b/sql/trunklet.sql index faf65e3..3e84058 100644 --- a/sql/trunklet.sql +++ b/sql/trunklet.sql @@ -13,10 +13,9 @@ CREATE SCHEMA __trunklet; CREATE TABLE __trunklet.old_settings AS SELECT name, setting FROM pg_catalog.pg_settings - WHERE name IN ('client_min_messages')-- This doesn't actually work as expected :( , 'search_path') + WHERE name IN ('search_path') ; -SET LOCAL client_min_messages = warning; --- PG apparently truncates the path to a single element, so our reset doesn't work :( SET LOCAL search_path = pg_catalog; +SET LOCAL search_path = pg_catalog; DO $do$ BEGIN diff --git a/test/build/build.sql b/test/build/build.sql index e3a82e8..1c2800b 100644 --- a/test/build/build.sql +++ b/test/build/build.sql @@ -4,6 +4,7 @@ \i test/helpers/psql.sql BEGIN; +SET LOCAL client_min_messages = warning; \i sql/trunklet.sql ROLLBACK; From 1be1228e9c5cb0c1529b62749025e1ad188d32ff Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 30 Oct 2026 02:04:53 -0500 Subject: [PATCH 13/14] Set back list settings with set_config, search_path with pg_temp; test and comment fixes - trunklet.sql and 0.2.0--0.2.1: restore the saved settings with set_config(..., true) so a list setting such as "public, pg_temp" is restored as a list, and run with search_path = pg_catalog, pg_temp. - build test: do the setup under pgTap (lives_ok) and discard the set-back's result row. - update_with_data.sql: run ALTER EXTENSION UPDATE in a transaction so SET LOCAL leaks from update scripts are caught. - 0.2.0 script: one comment above both SET LOCALs; load.sql comment format; HISTORY.asc drops release-process detail. --- HISTORY.asc | 3 +-- sql/trunklet--0.2.0--0.2.1.sql | 11 +++-------- sql/trunklet--0.2.0.sql | 3 +-- sql/trunklet.sql | 11 +++-------- test/build/build.sql | 10 ++++++++-- test/build/expected/build.out | 16 ++++++---------- test/helpers/update_with_data.sql | 3 +++ test/install/load.sql | 8 +++++--- 8 files changed, 30 insertions(+), 35 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index d393fe8..cdb4f3d 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -8,8 +8,7 @@ Updated installs now also match a fresh install: the `language__get()` debug mes === Stop install and update scripts from leaking `search_path` and `client_min_messages` After the transaction running `CREATE EXTENSION trunklet` or `ALTER EXTENSION trunklet UPDATE` ends, the caller's `search_path` and `client_min_messages` are unchanged. Within it, `search_path` is always unchanged; `client_min_messages` is too, except that a caller at `error` sees `warning` until the transaction ends when installing `0.2.0` or when updating from `0.2.0` or `0.2.1` (the `0.2.1--0.3.0` step). -The `0.2.0`, `0.2.1` and `0.3.3` install scripts and the `0.2.1--0.3.0` and `0.3.2--0.3.3` update scripts were edited only to change `SET` to `SET LOCAL`, including in the settings set-back where a script has one, and to add a comment that they were released with plain `SET`. -The current version's install script (`trunklet.sql`) no longer sets `client_min_messages`, and now runs with `search_path = pg_catalog`, set back with `SET LOCAL`. `0.2.0--0.2.1` likewise no longer sets `client_min_messages`. +`trunklet.sql` and `0.2.0--0.2.1` no longer set `client_min_messages`, and run with `search_path = pg_catalog, pg_temp` set with `SET LOCAL`. 0.3.3 ----- diff --git a/sql/trunklet--0.2.0--0.2.1.sql b/sql/trunklet--0.2.0--0.2.1.sql index d8c395f..b00c87e 100644 --- a/sql/trunklet--0.2.0--0.2.1.sql +++ b/sql/trunklet--0.2.0--0.2.1.sql @@ -9,7 +9,7 @@ CREATE TABLE __trunklet.old_settings AS FROM pg_catalog.pg_settings WHERE name IN ('search_path') ; -SET LOCAL search_path = pg_catalog; +SET LOCAL search_path = pg_catalog, pg_temp; CREATE OR REPLACE FUNCTION _trunklet.template__get( template_id _trunklet.template.template_id%TYPE @@ -262,13 +262,8 @@ $body$; -SELECT _trunklet.exec( - format( - 'SET LOCAL %I = %L' - , name - , setting - ) - ) +-- set_config() parses a list setting; SET LOCAL %I = %L would turn it into one quoted name +SELECT pg_catalog.set_config(name, setting, true) FROM __trunklet.old_settings ; DROP TABLE __trunklet.old_settings; diff --git a/sql/trunklet--0.2.0.sql b/sql/trunklet--0.2.0.sql index 602d45e..3da29c2 100644 --- a/sql/trunklet--0.2.0.sql +++ b/sql/trunklet--0.2.0.sql @@ -4,11 +4,10 @@ * */ --- Released as plain SET, which leaks the setting to the caller +-- Both released as plain SET, which leaks the setting to the caller SET LOCAL client_min_messages = warning; -- Set a safe search_path --- Released as plain SET, which leaks the setting to the caller SET LOCAL search_path = pg_catalog; DO $do$ diff --git a/sql/trunklet.sql b/sql/trunklet.sql index 3e84058..5266d9f 100644 --- a/sql/trunklet.sql +++ b/sql/trunklet.sql @@ -15,7 +15,7 @@ CREATE TABLE __trunklet.old_settings AS FROM pg_catalog.pg_settings WHERE name IN ('search_path') ; -SET LOCAL search_path = pg_catalog; +SET LOCAL search_path = pg_catalog, pg_temp; DO $do$ BEGIN @@ -957,13 +957,8 @@ $body$; */ -SELECT _trunklet.exec( - format( - 'SET LOCAL %I = %L' - , name - , setting - ) - ) +-- set_config() parses a list setting; SET LOCAL %I = %L would turn it into one quoted name +SELECT pg_catalog.set_config(name, setting, true) FROM __trunklet.old_settings ; DROP TABLE __trunklet.old_settings; diff --git a/test/build/build.sql b/test/build/build.sql index 1c2800b..a54e7ee 100644 --- a/test/build/build.sql +++ b/test/build/build.sql @@ -5,15 +5,21 @@ BEGIN; SET LOCAL client_min_messages = warning; +-- Discards the set-back's set_config() result row, which depends on the server's default search_path +\o /dev/null \i sql/trunklet.sql +\o ROLLBACK; BEGIN; \i test/helpers/tap_setup.sql \i test/helpers/settings_snapshot.sql -SELECT pg_temp.settings_snapshot() AS snapshot \gset +SELECT plan(5); -SELECT plan(4); +SELECT lives_ok( + $$SELECT pg_temp.settings_snapshot()$$ + , 'settings_snapshot() saved the caller''s settings' +); SELECT lives_ok( 'CREATE EXTENSION trunklet;' diff --git a/test/build/expected/build.out b/test/build/expected/build.out index dd511c1..12f421d 100644 --- a/test/build/expected/build.out +++ b/test/build/expected/build.out @@ -1,11 +1,7 @@ \set ECHO none - exec ------- - -(1 row) - -1..4 -ok 1 - CREATE EXTENSION trunklet; -ok 2 - CREATE EXTENSION changed no setting -ok 3 - Schema __trunklet should not exist -ok 4 - DROP EXTENSION trunklet; +1..5 +ok 1 - settings_snapshot() saved the caller's settings +ok 2 - CREATE EXTENSION trunklet; +ok 3 - CREATE EXTENSION changed no setting +ok 4 - Schema __trunklet should not exist +ok 5 - DROP EXTENSION trunklet; diff --git a/test/helpers/update_with_data.sql b/test/helpers/update_with_data.sql index 5adf164..f8b5ef1 100644 --- a/test/helpers/update_with_data.sql +++ b/test/helpers/update_with_data.sql @@ -16,8 +16,11 @@ SET search_path = _trunklet_test; CREATE TABLE seeded_template_ids AS SELECT get_test_templates() AS ids; SELECT pg_temp.settings_snapshot(); +-- One transaction, so SET LOCAL leaks from the script are caught (as in load.sql's CREATE) +BEGIN; ALTER EXTENSION trunklet UPDATE; SELECT pg_temp.settings_check('ALTER EXTENSION UPDATE'); +COMMIT; DO $body$ DECLARE diff --git a/test/install/load.sql b/test/install/load.sql index f7dda9e..d7dfe75 100644 --- a/test/install/load.sql +++ b/test/install/load.sql @@ -12,12 +12,14 @@ \i test/helpers/settings_snapshot.sql --- Below warning, so the server saves it and a plain SET in a script would show (error or higher is not exercised) +-- Below warning, so a plain SET in a script would show (error or higher is not exercised) SET client_min_messages = notice; SELECT pg_temp.settings_snapshot(); --- One transaction, so a failed settings check rolls back the install and the --- downstream tests fail +/* + * One transaction, so a failed settings check rolls back the install and the + * downstream tests fail + */ BEGIN; -- Branches server-side because \if needs psql 10+ DO $body$ From 8d403654f7a060e618ab1419873ce16423c59c45 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Fri, 30 Oct 2026 02:29:42 -0500 Subject: [PATCH 14/14] Check the set-back in the build test; reword comments and HISTORY A bare \i of sql/trunklet.sql now has its restored search_path checked, so the old SET LOCAL %I = %L set-back fails the suite. --- HISTORY.asc | 2 +- sql/trunklet--0.2.0--0.2.1.sql | 2 +- sql/trunklet--0.2.1--0.3.0.sql | 2 +- sql/trunklet.sql | 2 +- test/build/build.sql | 5 +++++ test/build/expected/build.out | 2 ++ 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/HISTORY.asc b/HISTORY.asc index cdb4f3d..23a761b 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -8,7 +8,7 @@ Updated installs now also match a fresh install: the `language__get()` debug mes === Stop install and update scripts from leaking `search_path` and `client_min_messages` After the transaction running `CREATE EXTENSION trunklet` or `ALTER EXTENSION trunklet UPDATE` ends, the caller's `search_path` and `client_min_messages` are unchanged. Within it, `search_path` is always unchanged; `client_min_messages` is too, except that a caller at `error` sees `warning` until the transaction ends when installing `0.2.0` or when updating from `0.2.0` or `0.2.1` (the `0.2.1--0.3.0` step). -`trunklet.sql` and `0.2.0--0.2.1` no longer set `client_min_messages`, and run with `search_path = pg_catalog, pg_temp` set with `SET LOCAL`. +`trunklet.sql` and `0.2.0--0.2.1` do not set `client_min_messages`, and run with `search_path = pg_catalog, pg_temp` set with `SET LOCAL`. 0.3.3 ----- diff --git a/sql/trunklet--0.2.0--0.2.1.sql b/sql/trunklet--0.2.0--0.2.1.sql index b00c87e..04d0cf8 100644 --- a/sql/trunklet--0.2.0--0.2.1.sql +++ b/sql/trunklet--0.2.0--0.2.1.sql @@ -262,7 +262,7 @@ $body$; --- set_config() parses a list setting; SET LOCAL %I = %L would turn it into one quoted name +-- Use set_config(), not SET LOCAL %I = %L: the latter turns a list setting into one quoted name SELECT pg_catalog.set_config(name, setting, true) FROM __trunklet.old_settings ; diff --git a/sql/trunklet--0.2.1--0.3.0.sql b/sql/trunklet--0.2.1--0.3.0.sql index 9d83db6..8123085 100644 --- a/sql/trunklet--0.2.1--0.3.0.sql +++ b/sql/trunklet--0.2.1--0.3.0.sql @@ -9,7 +9,7 @@ CREATE TABLE __trunklet.old_settings AS FROM pg_catalog.pg_settings WHERE name IN ('client_min_messages', 'search_path') ; --- Released as plain SET, which leaks the setting to the caller +-- Released as plain SET, which leaks both settings to the caller SET LOCAL client_min_messages = warning; SET LOCAL search_path = pg_catalog; diff --git a/sql/trunklet.sql b/sql/trunklet.sql index 5266d9f..a56988a 100644 --- a/sql/trunklet.sql +++ b/sql/trunklet.sql @@ -957,7 +957,7 @@ $body$; */ --- set_config() parses a list setting; SET LOCAL %I = %L would turn it into one quoted name +-- Use set_config(), not SET LOCAL %I = %L: the latter turns a list setting into one quoted name SELECT pg_catalog.set_config(name, setting, true) FROM __trunklet.old_settings ; diff --git a/test/build/build.sql b/test/build/build.sql index a54e7ee..2a4041d 100644 --- a/test/build/build.sql +++ b/test/build/build.sql @@ -5,10 +5,15 @@ BEGIN; SET LOCAL client_min_messages = warning; +SET LOCAL search_path = public, pg_temp; -- Discards the set-back's set_config() result row, which depends on the server's default search_path \o /dev/null \i sql/trunklet.sql \o +-- A bare \i is the only place the set-back is visible; it must restore the list, not one quoted name +\t on +SELECT current_setting('search_path'); +\t off ROLLBACK; BEGIN; diff --git a/test/build/expected/build.out b/test/build/expected/build.out index 12f421d..0541b68 100644 --- a/test/build/expected/build.out +++ b/test/build/expected/build.out @@ -1,4 +1,6 @@ \set ECHO none + public, pg_temp + 1..5 ok 1 - settings_snapshot() saved the caller's settings ok 2 - CREATE EXTENSION trunklet;