diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f29aa0b..4a37fd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,12 +113,22 @@ jobs: run: pg-start ${{ matrix.pg }} - name: Check out the repo uses: actions/checkout@v4 + # pgxntool's test-build (test/build/build.sql) syncs test/build/*.sql via + # rsync (pgxntool/run-test-build.sh); the pgxn/pgxn-tools image lacks it. + - name: Install rsync + run: apt-get install -y rsync - name: Test on PostgreSQL ${{ matrix.pg }} # verify-results runs the same TEST_DEPS chain as `make test`, then # inspects the pgTAP results (PGXNTOOL_ENABLE_VERIFY_RESULTS defaults # to yes, PGXNTOOL_VERIFY_RESULTS_MODE to pgtap) and exits non-zero on # failure. run: make verify-results + - name: Test the update path on PostgreSQL ${{ matrix.pg }} + # Update-path suite (test/install/load.sql installs TEST_UPDATE_FROM, + # then runs ALTER EXTENSION UPDATE) plus the fresh-vs-update comparison. + # Plain `test` suffices: the fresh verify-results step above already + # checks the same expected output. + run: make test-update # A single stable check name for use as a required status check in branch # protection rules. Matrix jobs produce check names like "🐘 PostgreSQL 14" diff --git a/Makefile b/Makefile index 7af5536..104bac7 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,7 @@ +# Explicit, not auto-detected: an empty test/install/ gives one clear make-time +# error instead of a pile of test failures. +PGXNTOOL_ENABLE_TEST_INSTALL = yes + include pgxntool/base.mk # @@ -6,3 +10,30 @@ include pgxntool/base.mk test_core_files = $(wildcard $(TESTDIR)/core/*.sql) testdeps: $(test_core_files) + +# TEST_LOAD_SOURCE selects how test/install/load.sql installs trunklet, once and +# committed, for the whole test run: +# - fresh (default): CREATE EXTENSION at the current version. +# - update: CREATE EXTENSION at TEST_UPDATE_FROM, store a test language and +# templates, then ALTER EXTENSION UPDATE to the current version and check +# them (test/helpers/update_with_data.sql). The suite and its expected +# output are the same, so a pass means the updated install behaves like a +# fresh one. +# The settings reach load.sql as placeholder GUCs via PGOPTIONS, which the psql +# processes pg_regress spawns inherit. Both are always exported so load.sql can +# fail if they didn't propagate; test_update_from is only read in update mode. +# There is no `existing` mode: trunklet has no pg_upgrade job. +TEST_LOAD_SOURCE ?= fresh +ifeq ($(filter $(TEST_LOAD_SOURCE),fresh update),) +$(error TEST_LOAD_SOURCE must be 'fresh' or 'update', got '$(TEST_LOAD_SOURCE)') +endif +TEST_UPDATE_FROM ?= 0.2.0 +export PGOPTIONS := $(PGOPTIONS) -c trunklet.test_load_mode=$(TEST_LOAD_SOURCE) -c trunklet.test_update_from=$(TEST_UPDATE_FROM) + +# Runs the suite against an updated install, then diffs it against a fresh one +# (bin/compare_fresh_vs_update). Recurses so the PGOPTIONS export above is +# re-evaluated with update set. +.PHONY: test-update +test-update: + $(MAKE) test TEST_LOAD_SOURCE=update + bin/compare_fresh_vs_update $(TEST_UPDATE_FROM) diff --git a/bin/compare_fresh_vs_update b/bin/compare_fresh_vs_update new file mode 100755 index 0000000..309adff --- /dev/null +++ b/bin/compare_fresh_vs_update @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# +# USAGE: bin/compare_fresh_vs_update FROM_VERSION +# +# Fails, printing a diff, if any trunklet extension member differs between a +# fresh install and one created at FROM_VERSION then ALTER EXTENSION UPDATEd. +# The test suite only proves the behavior it asserts; this catches an update +# script that leaves a member defined differently from a fresh install. +# +# Members come from pg_depend, so new objects are covered without editing this. +# Each one is rendered as its identity, its definition (functions, views, and +# table columns and constraints), its comments and its object-level ACL. +# Not compared: sequence settings (identity, comments and ACL only; the query +# must run on every supported version, where sequence metadata lives in +# different places) and column-level ACLs (pg_attribute.attacl; trunklet grants +# none). Both installs run in scratch databases on the server the usual PG* +# environment points at, and are dropped on exit. +set -euo pipefail + +from_version=${1:?usage: $0 FROM_VERSION} + +suffix="$$_$RANDOM" +fresh_db="trunklet_compare_fresh_$suffix" +update_db="trunklet_compare_update_$suffix" +tmp=$(mktemp -d) + +cleanup() { + dropdb --if-exists "$fresh_db" + dropdb --if-exists "$update_db" + rm -rf "$tmp" +} +trap cleanup EXIT + +run_psql() { + local db=$1 + shift + psql -X -q -v ON_ERROR_STOP=1 -d "$db" "$@" +} + +snapshot() { + run_psql "$1" -tA <<'SQL' +SELECT 'extension trunklet version ' || extversion FROM pg_extension WHERE extname = 'trunklet'; + +WITH member AS ( + SELECT d.classid, d.objid, pg_describe_object(d.classid, d.objid, 0) AS identity + FROM pg_depend d + WHERE d.refclassid = 'pg_extension'::regclass + AND d.refobjid = (SELECT oid FROM pg_extension WHERE extname = 'trunklet') + AND d.deptype = 'e' +) +SELECT concat_ws(E'\n' + , '== ' || m.identity + , CASE WHEN m.classid = 'pg_proc'::regclass THEN pg_get_functiondef(m.objid) END + , (SELECT pg_get_viewdef(c.oid) FROM pg_class c WHERE m.classid = 'pg_class'::regclass AND c.oid = m.objid AND c.relkind = 'v') + , (SELECT string_agg( + format('column %I %s%s%s' + , a.attname + , format_type(a.atttypid, a.atttypmod) + , CASE WHEN a.attnotnull THEN ' NOT NULL' END + , ' DEFAULT ' || pg_get_expr(ad.adbin, ad.adrelid) + ) + , E'\n' ORDER BY a.attnum + ) + FROM pg_class c + JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum > 0 AND NOT a.attisdropped + LEFT JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum + WHERE m.classid = 'pg_class'::regclass AND c.oid = m.objid AND c.relkind = 'r' + ) + , (SELECT string_agg(format('constraint %I %s', conname, pg_get_constraintdef(oid)), E'\n' ORDER BY conname) + FROM pg_constraint + WHERE m.classid = 'pg_class'::regclass AND conrelid = m.objid + ) + , (SELECT string_agg( + format('comment on %s: %s', pg_describe_object(classoid, objoid, objsubid), description) + , E'\n' ORDER BY objsubid + ) + FROM pg_description + WHERE classoid = m.classid AND objoid = m.objid + ) + , 'acl: ' || coalesce( + CASE + WHEN m.classid = 'pg_proc'::regclass THEN (SELECT proacl FROM pg_proc WHERE oid = m.objid) + WHEN m.classid = 'pg_class'::regclass THEN (SELECT relacl FROM pg_class WHERE oid = m.objid) + WHEN m.classid = 'pg_namespace'::regclass THEN (SELECT nspacl FROM pg_namespace WHERE oid = m.objid) + WHEN m.classid = 'pg_type'::regclass THEN (SELECT typacl FROM pg_type WHERE oid = m.objid) + END::text + , '(default)' + ) + ) + FROM member m + ORDER BY m.identity +; +SQL +} + +createdb "$fresh_db" +run_psql "$fresh_db" <<'SQL' +SET client_min_messages = warning; +CREATE EXTENSION trunklet; +SQL + +createdb "$update_db" +run_psql "$update_db" -v from_version="$from_version" <<'SQL' +SET client_min_messages = warning; +CREATE EXTENSION trunklet VERSION :'from_version'; +ALTER EXTENSION trunklet UPDATE; +SQL + +snapshot "$fresh_db" > "$tmp/fresh" +snapshot "$update_db" > "$tmp/update" + +if diff -u --label "fresh install" --label "updated from $from_version" "$tmp/fresh" "$tmp/update"; then + echo "OK: an install updated from $from_version matches a fresh install" +else + echo "FAIL: an install updated from $from_version differs from a fresh install (diff above)" >&2 + exit 1 +fi diff --git a/test/sql/build.sql b/test/build/build.sql similarity index 100% rename from test/sql/build.sql rename to test/build/build.sql diff --git a/test/expected/build.out b/test/build/expected/build.out similarity index 100% rename from test/expected/build.out rename to test/build/expected/build.out diff --git a/test/core/functions.sql b/test/core/functions.sql index 0f05330..76bb94f 100644 --- a/test/core/functions.sql +++ b/test/core/functions.sql @@ -151,107 +151,8 @@ BEGIN END $body$; -/* - * LANGUAGE FACTORY - */ -CREATE FUNCTION bogus_language_name( -) RETURNS text LANGUAGE sql AS $$SELECT 'bogus template language that does not exist'::text$$; -CREATE FUNCTION get_test_language_name( - langtype text DEFAULT 'text' -) RETURNS text LANGUAGE sql AS $$SELECT format('Our internal %s test language', langtype)$$; -CREATE FUNCTION get_test_language_id( -) RETURNS int LANGUAGE plpgsql AS $body$ -BEGIN - BEGIN - --SET LOCAL client_min_messages = debug; - PERFORM trunklet.template_language__add( - get_test_language_name() - , parameter_type := 'text[]' - , template_type := 'text' - , process_function_options := 'LANGUAGE plpgsql' - , process_function_body := -$process$ -DECLARE - -- Convert parameters into a string of ', , , ...' - v_args CONSTANT text := array_to_string( - array( - SELECT ', ' || quote_nullable(a) - FROM unnest(parameters) a(a) - ) - , '' - ); - sql CONSTANT text := format( 'SELECT format( %L%s )', template, v_args ); - v_return text; -BEGIN - RAISE DEBUG 'EXECUTE INTO using sql %', sql; - EXECUTE sql INTO v_return; - RETURN v_return; -END -$process$ - , extract_parameters_options := 'LANGUAGE sql' - , extract_parameters_body := -$extract$ -SELECT array( - SELECT parameters[i] - FROM generate_subscripts( parameters, 1 ) i - WHERE i = ANY( extract_list::int[] ) - ) -$extract$ - ); - EXCEPTION - -- TODO: incorrect return value - WHEN no_data_found OR unique_violation THEN - NULL; - END; - RETURN _trunklet.language__get_id( get_test_language_name() ); -END -$body$; - -CREATE FUNCTION get_json_language_id( -) RETURNS int LANGUAGE plpgsql AS $body$ -BEGIN - BEGIN - --SET LOCAL client_min_messages = debug; - PERFORM trunklet.template_language__add( - get_test_language_name('json') - , parameter_type := 'jsonb' - , template_type := 'json' - , process_function_options := 'LANGUAGE plpgsql' - , process_function_body := $process$ -DECLARE - k text; - v text; - - v_replace text; - v_return text := template::text; -BEGIN - FOR k,v IN - SELECT key, parameters->>key FROM jsonb_object_keys(parameters) key - LOOP - v_replace := format('!%s!', k); - RAISE DEBUG 'replacing all occurences of % with %', v_replace, v; - v_return := replace( v_return, v_replace, v ); - END LOOP; - RETURN v_return::json; -END -$process$ - , extract_parameters_options := 'LANGUAGE sql' - , extract_parameters_body := $extract$ -SELECT jsonb_object(keys, values) FROM ( - SELECT array_agg(k) AS keys, array_agg(parameters->>k) AS values - FROM jsonb_object_keys(parameters) k - WHERE k = ANY( extract_list ) - ) a -$extract$ - ); - EXCEPTION - -- TODO: incorrect return value - WHEN no_data_found OR unique_violation THEN - NULL; - END; - RETURN _trunklet.language__get_id( get_test_language_name() ); -END -$body$; +-- LANGUAGE FACTORY +\i test/helpers/language_factory.sql /* * FUNCTION _trunklet.language__get_id @@ -627,30 +528,6 @@ CREATE FUNCTION any_to_trunklet_template( CREATE FUNCTION any_to_trunklet_parameter( anyelement ) RETURNS text LANGUAGE plpgsql VOLATILE AS $$BEGIN RETURN quote_literal($1::text) || '::' || pg_typeof($1); END$$; -CREATE FUNCTION get_test_templates( -) RETURNS int[] LANGUAGE plpgsql AS $body$ -DECLARE - ids int[]; -BEGIN - -- Do this even if the test_templates table already exists - PERFORM get_test_language_id(); - - BEGIN - SELECT ids INTO ids FROM test_templates; - EXCEPTION - WHEN undefined_table THEN - ids[1] := trunklet.template__add( get_test_language_name(), 'test template', --any_to_trunklet_template - ('test 1'::text) ); - ids[2] := trunklet.template__add( get_test_language_name(), 'test template', 2, --any_to_trunklet_template - ('test 2'::text) ); - - -- See also test_template__remove - CREATE TEMP TABLE test_templates AS VALUES(ids); - END; - - RETURN ids; -END -$body$; CREATE FUNCTION test_template__add () RETURNS SETOF text LANGUAGE plpgsql AS $body$ diff --git a/test/helpers/language_factory.sql b/test/helpers/language_factory.sql new file mode 100644 index 0000000..f3e02e9 --- /dev/null +++ b/test/helpers/language_factory.sql @@ -0,0 +1,133 @@ +/* + * LANGUAGE FACTORY: defines, in the first schema on search_path, functions that + * create the test languages and templates on demand. + * + * test/helpers/update_with_data.sql also runs this under TEST_UPDATE_FROM, so + * stick to the API of the oldest install script in sql/ (currently 0.2.0), and + * don't use pgTAP. + */ +CREATE FUNCTION bogus_language_name( +) RETURNS text LANGUAGE sql AS $$SELECT 'bogus template language that does not exist'::text$$; +CREATE FUNCTION get_test_language_name( + langtype text DEFAULT 'text' +) RETURNS text LANGUAGE sql AS $$SELECT format('Our internal %s test language', langtype)$$; +CREATE FUNCTION get_test_language_id( +) RETURNS int LANGUAGE plpgsql AS $body$ +BEGIN + BEGIN + --SET LOCAL client_min_messages = debug; + PERFORM trunklet.template_language__add( + get_test_language_name() + , parameter_type := 'text[]' + , template_type := 'text' + , process_function_options := 'LANGUAGE plpgsql' + , process_function_body := +$process$ +DECLARE + -- Convert parameters into a string of ', , , ...' + v_args CONSTANT text := array_to_string( + array( + SELECT ', ' || quote_nullable(a) + FROM unnest(parameters) a(a) + ) + , '' + ); + sql CONSTANT text := format( 'SELECT format( %L%s )', template, v_args ); + v_return text; +BEGIN + RAISE DEBUG 'EXECUTE INTO using sql %', sql; + EXECUTE sql INTO v_return; + RETURN v_return; +END +$process$ + , extract_parameters_options := 'LANGUAGE sql' + , extract_parameters_body := +$extract$ +SELECT array( + SELECT parameters[i] + FROM generate_subscripts( parameters, 1 ) i + WHERE i = ANY( extract_list::int[] ) + ) +$extract$ + ); + EXCEPTION + -- TODO: incorrect return value + WHEN no_data_found OR unique_violation THEN + NULL; + END; + RETURN _trunklet.language__get_id( get_test_language_name() ); +END +$body$; + +CREATE FUNCTION get_json_language_id( +) RETURNS int LANGUAGE plpgsql AS $body$ +BEGIN + BEGIN + --SET LOCAL client_min_messages = debug; + PERFORM trunklet.template_language__add( + get_test_language_name('json') + , parameter_type := 'jsonb' + , template_type := 'json' + , process_function_options := 'LANGUAGE plpgsql' + , process_function_body := $process$ +DECLARE + k text; + v text; + + v_replace text; + v_return text := template::text; +BEGIN + FOR k,v IN + SELECT key, parameters->>key FROM jsonb_object_keys(parameters) key + LOOP + v_replace := format('!%s!', k); + RAISE DEBUG 'replacing all occurences of % with %', v_replace, v; + v_return := replace( v_return, v_replace, v ); + END LOOP; + RETURN v_return::json; +END +$process$ + , extract_parameters_options := 'LANGUAGE sql' + , extract_parameters_body := $extract$ +SELECT jsonb_object(keys, values) FROM ( + SELECT array_agg(k) AS keys, array_agg(parameters->>k) AS values + FROM jsonb_object_keys(parameters) k + WHERE k = ANY( extract_list ) + ) a +$extract$ + ); + EXCEPTION + -- TODO: incorrect return value + WHEN no_data_found OR unique_violation THEN + NULL; + END; + RETURN _trunklet.language__get_id( get_test_language_name() ); +END +$body$; + +CREATE FUNCTION get_test_templates( +) RETURNS int[] LANGUAGE plpgsql AS $body$ +DECLARE + ids int[]; +BEGIN + -- Do this even if the test_templates table already exists + PERFORM get_test_language_id(); + + BEGIN + SELECT ids INTO ids FROM test_templates; + EXCEPTION + WHEN undefined_table THEN + ids[1] := trunklet.template__add( get_test_language_name(), 'test template', --any_to_trunklet_template + ('test 1'::text) ); + ids[2] := trunklet.template__add( get_test_language_name(), 'test template', 2, --any_to_trunklet_template + ('test 2'::text) ); + + -- See also test_template__remove + CREATE TEMP TABLE test_templates AS VALUES(ids); + END; + + RETURN ids; +END +$body$; + +-- vi: expandtab ts=2 sw=2 diff --git a/test/helpers/setup.sql b/test/helpers/setup.sql index 764b6ee..fded349 100644 --- a/test/helpers/setup.sql +++ b/test/helpers/setup.sql @@ -2,13 +2,6 @@ BEGIN; \i test/helpers/tap_setup.sql --CREATE EXTENSION IF NOT EXISTS variant; --- No IF NOT EXISTS because we'll be confused if we're not loading the new stuff ---\i sql/trunklet.sql -CREATE EXTENSION trunklet; - -/* -CREATE EXTENSION trunklet VERSION '0.2.1'; -ALTER EXTENSION trunklet UPDATE; - */ +-- trunklet itself is installed by test/install/load.sql \i test/core/functions.sql diff --git a/test/helpers/update_with_data.sql b/test/helpers/update_with_data.sql new file mode 100644 index 0000000..6bca2fd --- /dev/null +++ b/test/helpers/update_with_data.sql @@ -0,0 +1,106 @@ +/* + * 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. + * + * test/install/load.sql runs it in update mode. + */ +CREATE SCHEMA _trunklet_test; +SET search_path = _trunklet_test; +\i test/helpers/language_factory.sql + +-- Committed before the update, as stored data would be in production +CREATE TABLE seeded_template_ids AS SELECT get_test_templates() AS ids; + +ALTER EXTENSION trunklet UPDATE; + +DO $body$ +DECLARE + v_installed text; + v_default text; +BEGIN + SELECT extversion INTO v_installed FROM pg_extension WHERE extname = 'trunklet'; + SELECT default_version INTO v_default FROM pg_available_extensions WHERE name = 'trunklet'; + IF v_installed IS DISTINCT FROM v_default THEN + RAISE EXCEPTION 'trunklet is at version % after ALTER EXTENSION UPDATE, not default_version %' + , v_installed, v_default + ; + END IF; +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 _trunklet_test.seeded_template_ids); + c_parameters CONSTANT text[] := '{}'; + r record; +BEGIN + -- get_test_templates() stores 'test ' as versions 1 and 2 of 'test template' + FOR r IN + SELECT format('process(%L, %s, ...)', 'test template', template_version) AS call + , trunklet.process('test template'::text, template_version, c_parameters) AS actual + , 'test ' || template_version AS expected + FROM generate_series(1, 2) template_version + UNION ALL + SELECT format('process(template_id %s, ...)', c_ids[template_version]) + , trunklet.process(c_ids[template_version], c_parameters) + , 'test ' || template_version + FROM generate_series(1, 2) template_version + LOOP + IF r.actual IS DISTINCT FROM r.expected THEN + RAISE EXCEPTION 'trunklet.% returned %, expected %' + , r.call, quote_nullable(r.actual), quote_literal(r.expected) + ; + END IF; + END LOOP; +END +$body$; + +DO $body$ +BEGIN + -- Keeps the emptiness check below from passing vacuously; the comment text is the same in every version + IF (SELECT count(DISTINCT split_part(obj_description(oid, 'pg_proc'), ' ', 1)) + FROM pg_proc + WHERE pronamespace = '_trunklet_functions'::regnamespace + AND obj_description(oid, 'pg_proc') LIKE '% function for trunklet language %' + AND split_part(obj_description(oid, 'pg_proc'), ' ', 1) IN ('process', 'extract_parameters') + ) <> 2 + THEN + RAISE EXCEPTION 'test language lacks its process or extract_parameters function in _trunklet_functions before template_language__remove()'; + END IF; +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); + +DO $body$ +DECLARE + c_functions CONSTANT text := ( + SELECT string_agg(oid::regprocedure::text, ', ') + FROM pg_proc + WHERE pronamespace = '_trunklet_functions'::regnamespace + ); +BEGIN + IF c_functions IS NOT NULL THEN + RAISE EXCEPTION 'template_language__remove() left functions in schema _trunklet_functions' + USING DETAIL = c_functions + ; + END IF; + IF EXISTS(SELECT 1 FROM _trunklet.language) OR EXISTS(SELECT 1 FROM _trunklet.template) THEN + RAISE EXCEPTION 'template_language__remove() left rows in _trunklet.language or _trunklet.template'; + END IF; +END +$body$; + +-- Back to the never-used state of a fresh install +SELECT setval(pg_get_serial_sequence('_trunklet.language', 'language_id'), 1, false) + , setval(pg_get_serial_sequence('_trunklet.template', 'template_id'), 1, false) +; +DROP SCHEMA _trunklet_test CASCADE; +RESET search_path; + +-- vi: expandtab ts=2 sw=2 diff --git a/test/install/load.sql b/test/install/load.sql new file mode 100644 index 0000000..6e50390 --- /dev/null +++ b/test/install/load.sql @@ -0,0 +1,41 @@ +/* + * Installs trunklet once, committed, before the test/sql/ suite runs, so every + * test file finds it already present (see pgxntool/README.asc's "test/install" + * 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. + */ +\set ON_ERROR_STOP on + +-- Branches server-side because \if needs psql 10+ +DO $body$ +DECLARE + -- No missing_ok: a setting that didn't propagate must fail here + c_mode CONSTANT text := current_setting('trunklet.test_load_mode'); +BEGIN + IF c_mode = 'fresh' THEN + CREATE EXTENSION trunklet; + ELSIF c_mode = 'update' THEN + EXECUTE format( + 'CREATE EXTENSION trunklet VERSION %L' + , current_setting('trunklet.test_update_from') + ); + ELSE + RAISE EXCEPTION 'unknown trunklet.test_load_mode "%"', c_mode; + END IF; +END +$body$; + +-- Update mode only: update_with_data.sql does the ALTER EXTENSION UPDATE. Branches +-- server-side for the same reason as above. +SELECT CASE current_setting('trunklet.test_load_mode') + WHEN 'update' THEN 'test/helpers/update_with_data.sql' + ELSE '/dev/null' + END AS update_script +\gset +\i :update_script + +-- vi: expandtab ts=2 sw=2 diff --git a/test/sql/search_path.sql b/test/sql/search_path.sql index 4240a29..83d56cb 100644 --- a/test/sql/search_path.sql +++ b/test/sql/search_path.sql @@ -7,6 +7,9 @@ SELECT plan( +1 -- verify search_path ); +-- Drop test/install/load.sql's committed install; this test's rollback restores it +DROP EXTENSION trunklet; + CREATE TEMP TABLE before AS SELECT current_setting('search_path') ;