Skip to content

Pick the test/install load mode server-side, and turn on ON_ERROR_STOP - #63

Open
jnasbyupgrade wants to merge 11 commits into
Postgres-Extensions:masterfrom
jnasbyupgrade:load-mode-without-if
Open

Pick the test/install load mode server-side, and turn on ON_ERROR_STOP#63
jnasbyupgrade wants to merge 11 commits into
Postgres-Extensions:masterfrom
jnasbyupgrade:load-mode-without-if

Conversation

@jnasbyupgrade

@jnasbyupgrade jnasbyupgrade commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

test/install/load.sql's RAISE EXCEPTIONs were decorative. pg_regress resolves its expected and result paths to the same file, so the diff is self-identical no matter what, and a psql that reports an error, carries on and exits 0 leaves pg_regress nothing to notice. ON_ERROR_STOP is what makes the file fail the way its own header says it does.

That couldn't just be switched on, because \if is psql 10 and CI covers back to 9.4, where psql reports it as an invalid command and then, having skipped nothing, runs every branch. On those versions load.sql already behaved badly, silently: the existing-mode assertion fired and errored in every mode, and update mode installed 0.9.6, updated it, then re-entered create_test_schema.sql, whose stale-schema cleanup dropped that schema CASCADE — taking the just-updated extension with it — and installed current fresh instead. make test TEST_LOAD_SOURCE=update on 9.4/9.5/9.6 has therefore never once exercised the update path. Under ON_ERROR_STOP the same \if would have aborted those legs outright.

So the mode selection moves server-side, into pg_temp.count_nulls_load(), leaving load.sql with no branching of its own — matching what test/helpers/test_user.sql already does, for the same reason. The schema creation and install it shares with bin/test_existing's prepare-old moves to test/helpers/install_extension.sql, which only defines the function; test/helpers/create_test_schema.sql is now just the call, and exists as a file purely because prepare-old invokes it as a standalone psql -f.

Drops the client_min_messages suppression that used to wrap the ALTER EXTENSION ... UPDATE. Postgres already raises it to at least WARNING for the duration of an update script and restores the caller's setting afterwards, so it was redundant, and being unconditional it lowered the level for a caller who had set something stricter. Confirmed: update mode still emits no NOTICEs without it.

Stacks on #62, so the commit list here shows those commits until it merges.

count_nulls is nothing but SQL functions, so requiring a superuser to
CREATE EXTENSION it was never anything more than the control file's
default. Set superuser = false.

For that to stay true, the suite now runs as an ordinary role throughout -
both the install session and every test session. test/helpers/test_user.sql
creates "Test user for count_nulls" if it isn't there, grants it exactly two
privileges (CREATE on the database, and USAGE on schema tap, since pgTAP is
harness and pgxntool's setup.sql creates that schema as the connecting role,
which grants nobody else access), and raises if the role is a superuser or a
member of any managed-cloud equivalent - rds_superuser, cloudsqlsuperuser or
azure_pg_admin, none of which carry rolsuper. Reverting superuser = false
now fails the suite outright, with "permission denied to create extension".

Every decision is made server-side, by a pg_temp function taking the role
name and returning the role the session should run as, because psql can't
branch before 10: \if is psql 10 and CI covers back to 9.4, where psql
reports it as an invalid command and then runs the branch it should have
skipped. \gset feeds that returned name into a plain SET ROLE. The file
opens with RESET ROLE so a second \i in one session behaves like the first -
not hypothetical, since on psql older than 10 install/load.sql's mode
branches all run and this file gets reached twice.

One case deliberately doesn't switch: a count_nulls already installed and
owned by somebody else, which is what a real pg_upgrade leaves behind.
PostgreSQL has no ALTER EXTENSION ... OWNER TO, so pg_dump can't carry an
extension's ownership across - --binary-upgrade emits
binary_upgrade_create_empty_extension(), which takes no owner, and the
extension ends up belonging to whoever ran the restore. Member functions
keep their owner; only the extension object is out of reach, which is
exactly what test__shutdown__drop_all needs. Nothing is lost by staying put:
existing mode installs nothing, so it was never the leg proving the install
works unprivileged.

Also fixes a live bug in install/load.sql's existing-mode assertion, whose
nested $$-quoted RAISE message closed its own DO block's body, leaving the
rest to be parsed as bare SQL - the whole assertion was a no-op. Fixed with
a distinct tag. It stays silent for now because that file has no
ON_ERROR_STOP; turning that on needs its \if branching to go server-side
first, which is a separate change.
@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: f8f1fb3c-f102-4515-b95f-b979b66bd42b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

…rtcoming

The carve-off for an extension owned by somebody else reads like something
that ought to be fixable, or like something a newer PostgreSQL would make
unnecessary. It isn't either: extowner has had no matching ALTER EXTENSION
... OWNER TO since it was added in 2011, because what that should do to the
contained objects was never settled, and it's been reported (BUG #18625) and
acknowledged as a known shortcoming without being fixed.
@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

Claude encountered an error after 2s —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 4m 22s —— View job


Claude's review

Tasks

  • Gather context (PR body, diff, CLAUDE.md)
  • Read changed files in full
  • Review test/install/load.sql and install_extension.sql changes for correctness
  • Review create_test_schema.sql / test_user.sql changes
  • Check downstream consumers (bin/test_existing, bin/compare_fresh_vs_update, etc.) for consistency
  • Final summary

Summary

This commit (40ec4ea, the actual content of #63 — the other 4 changed files are from the stacked #62) does what it says: test/install/load.sql now has zero branching of its own, mode/deploy selection moved into pg_temp.count_nulls_load(), and ON_ERROR_STOP is finally on. I traced all three modes (fresh/update/existing) and both deploy values through the new function and didn't find a logic bug — the SET LOCAL client_min_messages inside the function is actually more correct than the old session-level SET/reset pair, since it can't leak past the enclosing implicit transaction. I also checked that count_nulls.test_existing_deploy is unconditionally exported via PGOPTIONS in the Makefile (default filesystem) regardless of TEST_LOAD_SOURCE, so the new code's eager current_setting('count_nulls.test_existing_deploy') — evaluated on every call now, not just in existing mode like before — won't blow up on fresh/update runs.

Finding: stale "see X" pointers left behind by the create_test_schema.sqlinstall_extension.sql split

The refactor moved the stale-schema-sweep and WITH SCHEMA/search_path reasoning out of test/helpers/create_test_schema.sql's header into the new test/helpers/install_extension.sql, and test/README.md was updated accordingly. But several other comments elsewhere in the tree still point at create_test_schema.sql for reasoning/logic that no longer lives there:

  • bin/test_existing:184-187 — "shared with test/install/load.sql's own fresh/update installs via test/helpers/create_test_schema.sql" is no longer true: load.sql now calls install_extension.sql's function directly and never touches create_test_schema.sql.
  • bin/compare_fresh_vs_update:93-94 — "see test/helpers/create_test_schema.sql's header comment for the fuller version of this reasoning" (the WITH SCHEMA vs. mutating search_path argument) — that reasoning now lives in install_extension.sql.
  • bin/compare_fresh_vs_update:145-146 — points at create_test_schema.sql's header for "the fuller explanation" of psql not interpolating variables inside dollar-quoted strings. That explanation was tied to the old :version → GUC bridging trick, which was deleted outright (the new function takes a plain parameter) — so this pointer now dangles at an explanation that doesn't exist anywhere anymore.
  • bin/compare_fresh_vs_update:207-208 — "a narrower case than test/helpers/create_test_schema.sql's own sweep" — the sweep is in install_extension.sql now.
  • test/helpers/find_test_schema.sql:4-5 — "See test/helpers/create_test_schema.sql for how/why the name is randomized" — same.
  • test/sql/extension_tests.sql:48-50 — "test/helpers/create_test_schema.sql already unconditionally drops any leftover ... schema" — this is the exact sentence test/README.md correctly updated to say install_extension.sql elsewhere in this same PR; this occurrence was missed.
  • bin/test_existing.sql/plant_guard.sql:11-12 — same pointer, lower priority since it's a more general "where does count_nulls live" reference rather than reasoning that moved.

None of these are functional bugs (none of the affected files' behavior changed), but this codebase leans heavily on precise cross-file "see X's header comment" pointers as its documentation style, and a couple of these now point future readers at an explanation that either moved or no longer exists. Worth a follow-up pass to repoint them at install_extension.sql.

Fix stale create_test_schema.sql references →

It's declared CONSTANT, so it takes the constant prefix, matching
c_managed_superuser_roles right above it. v_role stays as it is - that
one really is a loop variable.
@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 22m 11s —— View job


Claude's review

Tasks

Summary

Ran a code review of this PR (test/install/load.sql's mode selection moving server-side into pg_temp.count_nulls_load(), plus ON_ERROR_STOP). Two confirmed findings were posted as inline PR comments:

  1. test/helpers/create_test_schema.sql:17 — Adding \i test/helpers/test_user.sql before install makes bin/test_existing's prepare-old install count_nulls as the test-user role, silently defeating the ownership-mismatch scenario test_user.sql's "stay put" branch is meant to exercise for the pg-tle-test CI job. That job has no real pg_upgrade step to reset ownership, so by the time run-suite runs, c_extension_owner == p_test_user and the no-switch branch becomes unreachable — the job silently stops proving the suite behaves correctly when count_nulls is owned by someone other than the test user.

  2. test/install/load.sql:144count_nulls.test_existing_deploy is now read unconditionally via current_setting (no missing_ok) on every load mode, not just existing, whereas the old code only read it in the existing-mode branch. Driving this file directly without also setting that GUC now raises "unrecognized configuration parameter" even in fresh/update mode. This is currently masked because the Makefile unconditionally exports it via PGOPTIONS regardless of TEST_LOAD_SOURCE.

Both findings went through an independent verification pass before posting. Separately fact-checked that removing the client_min_messages suppression around ALTER EXTENSION UPDATE is safe — Postgres's execute_extension_script already forces it to at least WARNING and restores the caller's setting via GUC nesting — so that was not flagged. No other correctness, cleanup, or CLAUDE.md-violation issues survived scrutiny across multiple review passes.

test/README.md had grown into a paraphrase of the comments in the files it
describes, so it's now a layout list plus the two cross-cutting facts a
reader needs before touching anything - the non-superuser role and the
randomly named schema - each pointing at the file that explains itself.

test/deps.sql said it was intentionally empty, which stopped being true when
it took over switching to the test user, and spent a paragraph restating
pgxntool's own docs. It now points at them instead.

Records why the database grant can't move into the CREATE ROLE beside it:
the role is cluster-wide and outlives a run, but the grant lives in the
current database's ACL, so a run against a new database has to reissue it.

Corrects the MEMBER-vs-USAGE note, which claimed being able to SET ROLE to a
role is equivalent to holding its privileges. It isn't; the check is
deliberately over-strict, and stays that way because nothing needs looser.
Comment thread test/helpers/create_test_schema.sql Outdated
END LOOP;
END
$$;
\i test/helpers/test_user.sql

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness / test-fidelity regression: Adding \i test/helpers/test_user.sql here makes bin/test_existing's prepare-old install count_nulls as the Test user for count_nulls role instead of as the connecting (super)user.

For the pg-tle-test CI job's existing-mode leg — which, per bin/test_existing's own header comment, reuses prepare-old -> update -> run-suite with no real binary pg_upgrade in between — this means the extension's owner going into run-suite is now the test user itself. That silently makes test_user.sql's "extension already installed and owned by somebody else" branch (IF c_extension_owner IS NOT NULL AND c_extension_owner <> p_test_user THEN RETURN current_user) unreachable via this path: c_extension_owner at prepare-old time is NULL (nothing installed yet) so the switch-to-test-user proceeds and installs as that role; by run-suite time c_extension_owner already equals p_test_user, so the "stay put" branch that's supposed to model a real pg_upgrade's ownership loss never fires for the pg-tle-test job. No test fails — the existing-mode leg just silently stops exercising the ownership-mismatch scenario it's documented to model.

(For the real pg-upgrade-test job this is masked because a genuine binary pg_upgrade resets ownership to whoever performs the restore regardless of what prepare-old set it to — so the gap is specific to the pg_tle path.)

Verified: before this diff create_test_schema.sql did no role-switching at all (ran as the connecting role), and bin/test_existing's prepare-old/CI invocations pass no PGUSER/role override, so this is a genuine behavior change, not a false alarm.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here — this one's intended, not a regression, and applies to both PRs. prepare-old installing as the test user rather than superuser is the exact property this work exists to prove (count_nulls needs no superuser rights to install); the pg_upgrade leg still works because the owner short-circuit, now scoped to existing mode, leaves that later session running as the connecting role rather than switching it.

Comment thread test/install/load.sql
Comment on lines +142 to +146
SELECT pg_temp.count_nulls_load(
current_setting('count_nulls.test_load_mode')
, current_setting('count_nulls.test_existing_deploy')
) AS count_nulls_loaded
\gset

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor robustness regression: count_nulls.test_existing_deploy is now read unconditionally for every mode (as a plain argument to pg_temp.count_nulls_load), whereas before this refactor it was only read from inside the existing-mode branch (\if :count_nulls_existing_mode).

current_setting() is called here without missing_ok, so if this file is ever driven directly (e.g. psql -c "SET count_nulls.test_load_mode='fresh'" -f test/install/load.sql, bypassing this repo's Makefile) with only the load-mode GUC set, it now raises unrecognized configuration parameter "count_nulls.test_existing_deploy" even in fresh/update mode, where that GUC is semantically irrelevant and previously was never touched.

This is masked in the normal workflow because the Makefile unconditionally exports count_nulls.test_existing_deploy via PGOPTIONS (default 'filesystem') regardless of TEST_LOAD_SOURCE, so make test/make test-update never hits it — but it does broaden the "fail loudly on unpropagated GUC" behavior beyond what the surrounding comment describes, for any caller that invokes this file directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here — acknowledged, deferring; this one is #63-specific. #62 only reads count_nulls.test_existing_deploy inside its existing-mode branch, but #63's server-side mode dispatch (d1a820e) reads it unconditionally. The Makefile always exports it regardless of mode though, so every supported invocation still sets it - reading it unconditionally doesn't introduce a case where it's actually missing.

@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 4m 36s —— View job


Code review complete

  • Gather context (PR body, diff, prior review comments, CLAUDE.md)
  • Check whether prior findings are still present after the latest push
  • Run full code review pass on current diff
  • Post findings as inline PR comments
  • Final summary

Summary

I re-diffed origin/master...HEAD (still the same 9 files, +408/-293) and compared file-by-file against what the two earlier inline reviews (2026-08-30 21:59) already covered. The content of test/install/load.sql, test/helpers/create_test_schema.sql, test/helpers/install_extension.sql and test/helpers/test_user.sql is byte-identical to what those reviews already examined — HEAD (4acb9f3) carries the same content as the previously-reviewed 40ec4ea, just re-committed with a different SHA (same author date). No new inline comments needed; nothing changed for them to be stale against.

Both previously-flagged findings remain accurate, so I'm not duplicating them, but noting current status:

  1. test/install/load.sql:137 (current_setting('count_nulls.test_existing_deploy') read unconditionally, no missing_ok) — confirmed still present, and confirmed still masked by the Makefile's unconditional PGOPTIONS export (Makefile:77) in every real invocation. This one is part of this PR's own commit (4acb9f3). Minor robustness gap only for a caller who drives load.sql directly without going through make.

  2. test/helpers/create_test_schema.sql:17 (\i test_user.sql making the pg-tle-test job's ownership-mismatch branch unreachable) — I traced this back further than the original review did: the \i test/helpers/test_user.sql line was introduced by a730479 ("Install count_nulls without superuser..."), which is Install count_nulls without superuser, and prove it in the suite #62's commit, not this PR's (4acb9f3 only edits test_user.sql's header comment, not its \i site or logic). Since the PR body says this branch stacks on Install count_nulls without superuser, and prove it in the suite #62 and will show its commits until that merges, this finding belongs on Install count_nulls without superuser, and prove it in the suite #62, not Pick the test/install load mode server-side, and turn on ON_ERROR_STOP #63 — nothing in Pick the test/install load mode server-side, and turn on ON_ERROR_STOP #63's own diff regressed it further.

I also checked test/README.md's full rewrite for consistency with the new install_extension.sql/create_test_schema.sql split and didn't find any new dangling references introduced by this PR (the previously-noted stale "see create_test_schema.sql" pointers in bin/test_existing, bin/compare_fresh_vs_update, test/helpers/find_test_schema.sql, and test/sql/extension_tests.sql are all in files this PR doesn't touch, so they're an existing follow-up, not a new regression here).

No new correctness issues found in this PR's own diff. The ON_ERROR_STOP/server-side mode selection logic in pg_temp.count_nulls_load() checks out for all three modes (fresh/update/existing), and dropping the client_min_messages suppression around ALTER EXTENSION UPDATE is safe per the earlier fact-check (Postgres already raises it to WARNING for the script's duration and restores the caller's setting via GUC nesting).
(branch: load-mode-without-if)

test/helpers/test_user.sql becomes use_test_user.sql. The other helpers are
named for the action running them performs (create_test_schema,
find_test_schema); this one performed an action but read as a noun.

Drops the comment above the SET ROLE, which restated what the two statements
under it plainly do, and the one explaining why a nested RAISE message needs
its own dollar-quote tag - that's general plpgsql knowledge, not something
this site needs to teach.
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 10m 38s —— View job


Code review complete

  • Gather context (PR body, diff, prior reviews, CLAUDE.md)
  • Run /code-review:code-review on current diff
  • Post findings as inline PR comments
  • Final summary

Summary

Reviewed the current diff (origin/master...HEAD, 9 files, +392/-293). Note the branch has moved since the previous reviews on this PR: test/helpers/install_extension.sql / test/helpers/test_user.sql have been renamed to test/helpers/extension_installer.sql / test/helpers/use_test_user.sql, with further changes inside, so this was a fresh full pass rather than a diff-against-prior-review.

The refactor itself — moving test/install/load.sql's mode branching and the non-superuser test-role machinery entirely server-side (plpgsql), eliminating all \if usage (which silently misbehaved on pre-10 psql, per the PR body), and turning on ON_ERROR_STOP — checks out. The core logic (schema-ownership handling around what a real pg_upgrade does to extension ownership, WITH SCHEMA install to avoid search_path coupling, GUC propagation, mode selection for fresh/update/existing) is well-documented and internally consistent.

Three findings posted as inline comments, all in the new test/helpers/use_test_user.sql:

  1. Line 84 — The test-role bootstrap (CREATE ROLE + grants) is gated on current_setting('is_superuser') = 'on', which is never true for managed-cloud pseudo-superuser roles (rds_superuser, cloudsqlsuperuser, azure_pg_admin) that this same function explicitly names a few lines later — those roles are only ever treated as a restriction elsewhere, never as an alternate bootstrap-allowing condition here. On a first run against a fresh managed-cloud database connected as the platform admin role, the test role never gets created and the function raises "cannot be created" even though the connecting role has ample privilege to do so.

  2. Line 86 — The check-then-create of the shared, non-randomized test role name (IF NOT EXISTS ... THEN CREATE ROLE) isn't atomic across sessions. Two concurrent suite invocations against the same cluster (e.g. make test and make test-update run in parallel locally) can both see the role missing and both attempt CREATE ROLE; the loser hits an unhandled duplicate_object error, aborting that session under ON_ERROR_STOP.

  3. Line 108 — Minor efficiency note: the idempotent bootstrap block (existence check, GRANT CREATE ON DATABASE, GRANT USAGE ON tap) re-runs on every per-test-file session rather than once after install/load creates the role. Not a correctness bug (all statements are idempotent), just wasted catalog lookups/DDL per test file.
    (branch: load-mode-without-if)

Comment thread test/helpers/use_test_user.sql Outdated
RETURN current_user;
END IF;

IF current_setting('is_superuser') = 'on' THEN

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness: managed-cloud "superuser" roles can never bootstrap the test role.

The bootstrap block (create the role, GRANT CREATE ON DATABASE, GRANT USAGE ON SCHEMA tap) only runs IF current_setting('is_superuser') = 'on'. is_superuser reflects the actual rolsuper catalog bit of the current role — it is never on for AWS RDS/Aurora's rds_superuser, GCP Cloud SQL's cloudsqlsuperuser, or Azure Flexible Server's azure_pg_admin, none of which carry rolsuper (as this very function's own comment on c_managed_superuser_roles a few lines below explicitly notes).

Those roles are only ever consulted later, as a restriction (the test user must not be a member of one) — never as an alternate condition for allowing the bootstrap. So the very first run of this suite against a fresh managed-cloud database, connected as that platform's admin role, hits:

IF NOT EXISTS(...) THEN
  RAISE EXCEPTION 'role "%" does not exist, and this session is not a superuser so it cannot be created', p_test_user;

even though the connecting role has ample privilege (it can create roles and grant CREATE ON DATABASE in practice) to do exactly that. Given the pg_tle CI job this suite ships (pg-tle-test) exists specifically because pg_tle is commonly deployed on exactly these managed platforms, this looks like a real gap rather than a merely theoretical one — worth gating the bootstrap on current_setting('is_superuser') = 'on' OR (SELECT bool_or(pg_has_role(current_user, r, 'MEMBER')) FROM unnest(c_managed_superuser_roles) r) (or similar) rather than is_superuser alone.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here — already fixed, applies to both PRs. The bootstrap gate now treats the managed-cloud admin roles (rds_superuser, cloudsqlsuperuser, azure_pg_admin) as equivalent to superuser, so they can create the test role and grant it what it needs; that fix landed on #62 in 6529b72.


IF current_setting('is_superuser') = 'on' THEN
IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname = p_test_user) THEN
EXECUTE format('CREATE ROLE %I', p_test_user);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness: check-then-create race on the test role.

IF NOT EXISTS(SELECT 1 FROM pg_roles WHERE rolname = p_test_user) THEN
  EXECUTE format('CREATE ROLE %I', p_test_user);
END IF;

p_test_user is a fixed, non-randomized name ('Test user for count_nulls', unlike the randomized schema name), and this role is cluster-wide, not per-database. If two invocations of this suite run concurrently against the same Postgres cluster (different databases) — e.g. a developer running make test and make test-update locally at the same time, or any future CI layout that shares one instance across parallel jobs — both sessions can see "role does not exist" and both execute CREATE ROLE. The loser gets an unhandled duplicate_object error, and since this file runs under ON_ERROR_STOP, that whole install/load.sql (or deps.sql) session aborts.

Not exploitable in this repo's current CI (each job gets its own isolated pg-start cluster), but it's a real hazard for local/dev use and worth at least a comment, if not a CREATE ROLE IF NOT EXISTS / exception-swallowing guard.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here — acknowledged, not fixing now; applies to both PRs (shared code on #62). pgxntool's schedule-based install file completes before the parallel test/sql/ batch starts, so by the time any session could race on the check-then-create, the role already exists. Deferring rather than dismissing, and tracking it for follow-up since that ordering is an external invariant this file doesn't enforce itself.

IF EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'tap') THEN
EXECUTE format('GRANT USAGE ON SCHEMA tap TO %I', p_test_user);
END IF;
END IF;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Efficiency (minor): the whole bootstrap block (role-existence check, GRANT CREATE ON DATABASE, tap-schema GRANT USAGE) re-runs on every per-test-file session, not just once — test/deps.sql \i's this file for every file under test/sql/ (currently just one file, but the mechanism re-executes per session regardless). Each of the GRANTs and EXISTS checks is idempotent so this is harmless, but it's a few avoidable catalog lookups/statements per session that only ever need to happen once, right after install/load.sql creates the role. Not blocking, just noting it as wasted work if this suite ever grows more test files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here — acknowledged as real but non-blocking; applies to both PRs (shared code on #62). The bootstrap statements (role creation, grants) are idempotent, so re-running them every session is wasted work, not a correctness risk. Deferring that optimization and tracking it for follow-up.

The bootstrap that creates the test user and grants it what it needs was
gated on is_superuser alone. On RDS, Cloud SQL and Azure Flexible Server the
admin account never reports that, by design, so on those platforms the
bootstrap could never run even though the account has the rights - the
managed roles were only ever consulted afterwards, to disqualify the test
user. Both checks now consult the same list.

An admin that isn't a real superuser also can't SET ROLE to the role it just
created: from PG16 a CREATEROLE creator gets ADMIN on that role, which isn't
SET. It now grants the role to itself in that case, which a real superuser
doesn't need.

Stale-schema cleanup moves ahead of the role switch, so it runs as the
connecting role. A leftover schema can belong to anyone - one created by a
run predating this change belongs to a superuser - and only the connecting
role is sure to be able to drop it.
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 7m 2s —— View job


Code review complete

  • Gather context (PR body, diff, prior reviews, CLAUDE.md)
  • Run /code-review:code-review on current diff
  • Post findings as inline PR comments
  • Final summary

Summary

Reviewed the current diff (origin/master...HEAD, branch load-mode-without-if at 0b817de). Two findings were confirmed and posted as inline comments, both in the same area: this refactor's server-side mode selection reshuffled ordering/scoping that a sibling commit in this stack (6529b72) had deliberately fixed.

  1. test/install/load.sql:20\i test/helpers/use_test_user.sql now runs before test/helpers/extension_installer.sql is loaded, so the session switches to the non-superuser test user first, and count_nulls_load()'s internal stale-schema cleanup (pg_temp.count_nulls_cleanup_test_schemas()) then runs as the test user. If a prior crashed run left a count_nulls test schema * behind owned by a different role (e.g. a superuser — per 6529b72's own rationale for why ownership matters here), the test user can't DROP SCHEMA ... CASCADE it, and the run fails with a permission error. The sibling test/helpers/create_test_schema.sql still gets this right (cleanup while still the connecting role, then the switch) — load.sql regressed relative to it.

  2. test/helpers/use_test_user.sql:95 — The check that skips the role switch when count_nulls is already installed under a different owner isn't scoped to existing mode. Since load.sql now calls use_test_user.sql unconditionally before the requested mode is even known, a leftover extension owned by someone else (e.g. from an earlier interrupted/manual run) makes count_nulls_prepare_test_user() return current_user without switching — even in fresh/update mode — so the install then silently runs as the superuser, defeating the suite's whole "runs as a non-superuser" guarantee, with no error to flag it.

Both are genuine ordering/scoping regressions introduced by moving mode selection server-side, not issues with the ON_ERROR_STOP / pg_temp.count_nulls_load() logic itself, which otherwise checks out for all three modes.
(branch: load-mode-without-if)

Comment thread test/install/load.sql Outdated
Comment on lines +20 to +21
\i test/helpers/use_test_user.sql
\i test/helpers/extension_installer.sql

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness: stale-schema cleanup now runs after the switch to the test user, undoing the fix from 6529b72 ("Stale-schema cleanup moves ahead of the role switch, so it runs as the connecting role").

Here test/helpers/use_test_user.sql (the SET ROLE to the non-superuser test user) runs before test/helpers/extension_installer.sql is even loaded. count_nulls_load() — created and invoked later in this same file, i.e. after the switch — calls pg_temp.count_nulls_cleanup_test_schemas() internally (line 106), so cleanup executes as the test user, not the connecting role.

Compare with the sibling file test/helpers/create_test_schema.sql, which gets the order right: \i extension_installer.sqlSELECT pg_temp.count_nulls_cleanup_test_schemas() (still the connecting role) → \i use_test_user.sql (switch) → install.

extension_installer.sql's own header comment states the contract this file breaks: "every caller can run cleanup before switching to the test user … while installing after - a leftover schema can belong to any role, and only the connecting one is sure to be able to drop it."

Failure scenario: a prior fresh/update run crashes before its own teardown, leaving count_nulls test schema * behind owned by a superuser (or any role other than the test user) — exactly the scenario 6529b72 called out ("one created by a run predating this change belongs to a superuser"). The next run's cleanup now executes as the test user, which doesn't own that schema, so DROP SCHEMA %I CASCADE fails with a permission error instead of cleaning it up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here — already fixed in d1a820e. Cleanup now runs before the role switch, mode-gated, so in existing mode it doesn't drop the schema the pg_upgrade run is being verified to have preserved.

Comment thread test/helpers/use_test_user.sql Outdated
Comment on lines +95 to +96
IF c_extension_owner IS NOT NULL AND c_extension_owner <> p_test_user THEN
RETURN current_user;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness: this owner short-circuit isn't scoped to "existing" mode, so it can silently defeat the non-superuser guarantee in fresh/update mode too.

count_nulls_prepare_test_user() is called unconditionally at the top of test/install/load.sql, before the caller's count_nulls.test_load_mode is even read. If count_nulls already exists in the target database under some other owner (e.g. a leftover extension from an earlier interrupted/manual run, installed by a superuser), this check returns current_user without ever switching to the test user — regardless of whether the actual requested mode is fresh or update.

Failure scenario: a stale count_nulls extension (owned by, say, postgres) lingers in the test database when make test TEST_LOAD_SOURCE=fresh runs. count_nulls_prepare_test_user() sees c_extension_owner IS NOT NULL AND c_extension_owner <> p_test_user, returns current_user (the superuser), and the session never switches roles. count_nulls_load() then runs cleanup + fresh install entirely as the superuser, with no error — silently defeating the very property (superuser = false actually works) this test suite exists to prove.

@jnasbyupgrade jnasbyupgrade Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here — fixed: count_nulls_prepare_test_user() now takes the load mode as an explicit argument, and the owner short-circuit only returns current_user when that mode is existing; otherwise it raises, naming both the owner and the test user. This applies to both PRs (the function lives on #62); the fix lands there and reaches #63 by rebase.

Doubled single quotes ('' ... '') in RAISE EXCEPTION messages made two
call sites hard to read; switch both to distinct-tag dollar quoting so
the literal quotes read as themselves.

create_test_schema.sql's header comment had grown to explain nearly
everything the file does, most of it far from the code it was
describing. Cut it to a few lines on what the file is and why it
exists standalone (bin/test_existing's prepare-old runs it outside any
psql session that also runs load.sql), and move each remaining
rationale down next to the code it explains: the GUC-bridging and
'current' sentinel notes by the guard, the crash-recovery note by the
cleanup loop, the identifier-quoting note by the name generation, and
the WITH SCHEMA note by CREATE EXTENSION.
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 3m 57s —— View job


Code review complete

  • Gather context (PR body, diff, prior reviews, CLAUDE.md)
  • Run code review on current diff
  • Check whether prior findings are still present
  • Post findings as inline PR comments
  • Final summary

Summary

Branch has moved again since the last review (now d1a820e, two commits on top of #62's stack: 922c349 "Use dollar quoting for error messages, and trim the schema-setup header" and d1a820e "Pick the test/install load mode server-side, and turn on ON_ERROR_STOP" — the latter is the actual tip and this PR's namesake).

Both previously-confirmed findings are now fixed:

  1. Cleanup-order regression (flagged 2026-08-31 20:55) — test/install/load.sql now runs pg_temp.count_nulls_cleanup_test_schemas() before \i test/helpers/use_test_user.sql (lines 26–33), matching test/helpers/create_test_schema.sql's order. Cleanup runs as the connecting role again, so a leftover schema owned by a superuser (or any non-test-user role) can be dropped.
  2. Managed-cloud admin bootstrap gating (flagged 2026-08-31 20:31) — use_test_user.sql's bootstrap block now gates on c_admin (is_superuser = 'on' OR membership in rds_superuser/cloudsqlsuperuser/azure_pg_admin), not is_superuser alone, so a first run against a fresh managed-cloud database connected as the platform admin role can now create the test role.

Re-examined the one finding that looked like it might still be open — the "owner short-circuit isn't scoped to mode" point from the 20:55 review (use_test_user.sql's IF c_extension_owner IS NOT NULL AND c_extension_owner <> p_test_user THEN RETURN current_user firing regardless of fresh/update/existing). Traced this back through git show 6529b72:test/install/load.sql: pre-#63, fresh/update mode already unconditionally invoked this same check (via \i create_test_schema.sql, itself unconditional inside those \if branches) — the only actual change from #63 is that existing mode's install session also now reaches it, which is the mode this short-circuit is documented and intended for. So this isn't a regression introduced by #63's own two commits; it's inherited, pre-existing behavior from #62's a730479. Not re-flagging it here for the same reason the create_test_schema.sql ownership finding was reattributed to #62 in the previous round.

No new correctness issues found in 922c349/d1a820e's own diff. Spot-checked test/README.md's layout section and the renamed-file references (extension_installer.sql, use_test_user.sql) — consistent, and grepping the tree found no dangling references to the old filenames in files this PR touches (count_nulls.control's comment still says test/helpers/test_user.sql, but that line was written by a730479 and never touched by the rename commit 8b32f15 either — both #62 commits, not #63's).

No new inline comments posted; nothing new to flag.
(branch: load-mode-without-if)

count_nulls_prepare_test_user() now takes the load mode as an explicit
argument (the caller must supply it - the GUC it comes from isn't set for
every invocation, e.g. bin/test_existing's prepare-old) and only skips the
role switch for a foreign-owned extension when that mode is 'existing'. In
fresh/update, a foreign-owned count_nulls is a cleanup miss, not a
preserved pg_upgrade artifact, so it now raises instead of silently letting
the suite run as the connecting role.
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 2m 21s —— View job


Code review complete

  • Gather context (PR body, diff, prior reviews, CLAUDE.md)
  • Run code review on current diff
  • Check whether prior findings are still present
  • Post findings as inline PR comments
  • Final summary

Summary

Branch has moved again since the last review (now 3178cd9, two new commits on top: b5659f4 "Scope the extension-owner short-circuit to existing mode only" and 3178cd9 "Pass the real load mode into use_test_user.sql").

Both previously-open findings from the 20:55 review are now fixed:

  1. Cleanup-order regressiontest/install/load.sql runs pg_temp.count_nulls_cleanup_test_schemas() (lines 26–38) before \i test/helpers/use_test_user.sql (line 40), so cleanup still executes as the connecting role, matching test/helpers/create_test_schema.sql's order.
  2. Owner short-circuit not scoped to modecount_nulls_prepare_test_user() now takes an explicit p_load_mode parameter (threaded through from count_nulls.test_load_mode in every includer: load.sql, deps.sql, and a hardcoded 'fresh' literal in create_test_schema.sql). The owner-mismatch short-circuit only returns current_user without switching when p_load_mode = 'existing'; in fresh/update it now raises a clear exception (count_nulls is owned by "%", not test user "%", and load mode "%" expects no pre-existing installation) instead of silently running the rest of the install as the connecting (often superuser) role. This is a stronger fix than just "not a regression" — it actively closes the gap.

Traced both fixes through all three call sites (load.sql, deps.sql, create_test_schema.sql) and didn't find a case where count_nulls_load_mode is read before it's set.

Not re-flagging (already covered in earlier rounds on this PR, unchanged by these two commits, and either minor or out of this PR's own diff):

  • The check-then-create race on CREATE ROLE in use_test_user.sql (cluster-wide role name, no IF NOT EXISTS atomicity) — pre-existing, low severity, not exploitable in current CI.
  • test/install/load.sql's unconditional current_setting('count_nulls.test_existing_deploy') read — pre-existing, masked by the Makefile's PGOPTIONS export.
  • Stale test/helpers/test_user.sql reference in count_nulls.control's new comment, and the older stale create_test_schema.sql pointers in bin/test_existing, bin/compare_fresh_vs_update, test/helpers/find_test_schema.sql, test/sql/extension_tests.sql — none of these files are in this PR's diff (9 changed files: HISTORY.md, README.md, count_nulls.control, test/README.md, test/deps.sql, test/helpers/create_test_schema.sql, test/helpers/extension_installer.sql, test/helpers/use_test_user.sql, test/install/load.sql); they belong to Install count_nulls without superuser, and prove it in the suite #62's stack or are pre-existing follow-up work.

Spot-checked test/README.md's rewrite and HISTORY.md/README.md/count_nulls.control's new superuser = false documentation for consistency with the renamed files (extension_installer.sql, use_test_user.sql) — no new dangling references introduced by this round's two commits.

No new correctness issues found; no inline comments needed this round.
(branch: load-mode-without-if)

comment, and trim an unenforceable ownership claim

The load-mode literal comment asserted an unenforced fact about the
rest of the system instead of stating the intent of this code path;
restate it as intent, noting the loud-failure fallback if that intent
ever breaks. The WITH SCHEMA comment had ended up above an unrelated
CASE expression when the header was split up; move it back next to the
CREATE EXTENSION statement it documents. The extension-owner
short-circuit's comment assumed a foreign-owned extension always came
from a real pg_upgrade restore, which the code doesn't and can't know;
trim it to the two facts the code actually depends on plus the one
non-obvious reason (no ALTER EXTENSION ... OWNER TO) existing mode can
meet a foreign owner at all.
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 4m 8s —— View job


Code review complete

  • Gather context (PR body, diff, prior reviews, CLAUDE.md)
  • Run /code-review:code-review on current diff
  • Check whether prior findings are still present
  • Post findings as inline PR comments
  • Final summary

Summary

The branch has been rewritten again since the last review round (history restructured — commits reordered/rebased, current tip 3b63cfb, on top of 8c0dabb "Pick the test/install load mode server-side, and turn on ON_ERROR_STOP" and af48b23 "Address reviewer comments..."). Since the old reviewed SHAs (3178cd9/d1a820e) no longer exist locally after the rewrite, I did a full fresh read of every changed file's current content rather than a commit-by-commit diff, and traced the fresh/update/existing modes end-to-end through test/install/load.sql, test/helpers/extension_installer.sql, test/helpers/use_test_user.sql, test/helpers/create_test_schema.sql, and test/deps.sql.

All previously-confirmed findings on this PR remain fixed in the current state:

  1. Stale-schema cleanup runs before the switch to the test user in both load.sql (lines 26–38) and create_test_schema.sql (lines 13–19), so it still executes as the connecting role.
  2. The extension-owner short-circuit in use_test_user.sql (lines 91–100) is scoped to p_load_mode = 'existing'; fresh/update now raise a clear exception instead of silently continuing as the connecting role.
  3. The bootstrap gate (c_admin, lines 68–72) treats managed-cloud admin roles (rds_superuser/cloudsqlsuperuser/azure_pg_admin) as equivalent to superuser, so a first run against a fresh managed-cloud database can create the test role.
  4. The managed-cloud admin now GRANTs itself the test role (line 113) before SET ROLE, since PG16+ CREATEROLE-conferred ADMIN isn't the same as SET privilege.
  5. count_nulls_load_mode is explicitly \gset in every includer (load.sql:37, deps.sql:11, create_test_schema.sql:26) before \i test/helpers/use_test_user.sql, so the mode is never silently unset now that this file runs unconditionally rather than only in fresh/update.

I also re-checked test/README.md against the renamed files (extension_installer.sql, use_test_user.sql) — consistent, no dangling references introduced by this PR's own commits.

Not re-flagging (deferred/accepted in earlier rounds on this PR, unchanged by the latest commits):

  • The check-then-create race on CREATE ROLE in use_test_user.sql — acknowledged, not exploitable given pgxntool's schedule ordering.
  • test/install/load.sql's unconditional current_setting('count_nulls.test_existing_deploy') read — acknowledged, masked by the Makefile's PGOPTIONS export.
  • count_nulls.control's comment still naming test/helpers/test_user.sql (renamed to use_test_user.sql) — this line was written and left untouched by commits belonging to Install count_nulls without superuser, and prove it in the suite #62's stack, not this PR's own three commits (af48b23, 8c0dabb, 3b63cfb), consistent with how earlier rounds scoped findings on this stacked PR.

No new correctness issues found. No new inline comments needed this round.
(branch: load-mode-without-if)

…load mode

count_nulls.control referenced test/helpers/test_user.sql, which was renamed
to use_test_user.sql; the tree-wide grep for that rename had been scoped to
subdirectories and missed it.

create_test_schema.sql hardcoded \set count_nulls_load_mode 'fresh' right
before including use_test_user.sql, which both invented a mode the caller
never asked for and would silently override one a caller had legitimately
set. It's already wrong in practice: test/install/load.sql's update leg runs
through this file with the mode hardcoded to 'fresh' even though it's really
'update'. The value now comes from whichever caller genuinely knows it -
test/install/load.sql (derived from count_nulls.test_load_mode, the same way
test/deps.sql already does) or bin/test_existing's -v on the command line
(always a genuine fresh install) - and create_test_schema.sql only consumes
:count_nulls_load_mode, same as every other includer of use_test_user.sql.
test/install/load.sql's RAISE EXCEPTIONs were decorative. pg_regress
resolves its expected and result paths to the same file, so the diff is
self-identical no matter what, and a psql that reports an error, carries on
and exits 0 leaves pg_regress nothing to notice. ON_ERROR_STOP is what makes
the file fail the way its own header says it does.

That couldn't just be switched on, because \if is psql 10 and CI covers back
to 9.4, where psql reports it as an invalid command - and then, having
skipped nothing, runs every branch. On those versions load.sql already
behaved badly, silently: the existing-mode assertion fired and errored in
every mode, and update mode installed 0.9.6, updated it, then re-entered
create_test_schema.sql, whose stale-schema cleanup dropped that schema
CASCADE - taking the just-updated extension with it - and installed current
fresh instead. `make test TEST_LOAD_SOURCE=update` on 9.4/9.5/9.6 has
therefore never once exercised the update path. Under ON_ERROR_STOP the same
\if would have aborted those legs outright.

So the mode selection moves server-side, into pg_temp.count_nulls_load(),
leaving load.sql with no branching of its own - matching what
test/helpers/test_user.sql already does, for the same reason. The schema
creation and install it shares with bin/test_existing's prepare-old moves to
test/helpers/install_extension.sql, which only defines the function;
test/helpers/create_test_schema.sql is now just the call, and exists as a
file purely because prepare-old invokes it as a standalone psql -f.

Drops the client_min_messages suppression that used to wrap the ALTER
EXTENSION UPDATE. Postgres already raises it to at least WARNING for the
duration of an update script and restores the caller's setting afterwards,
so it was redundant, and being unconditional it lowered the level for a
caller who had set something stricter. Confirmed: update mode still emits no
NOTICEs without it.
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 3m 45s —— View job


Code review complete

  • Gather context (PR body, diff, prior reviews, CLAUDE.md)
  • Run code review on current diff
  • Post findings as inline PR comments
  • Final summary

Summary

Branch has moved since the last review round: two new commits, 06bce4e ("Fix stale control-file filename, and make the caller supply the test load mode") and c7179df ("Pick the test/install load mode server-side, and turn on ON_ERROR_STOP", rebased to a new SHA but same content as the previously-reviewed tip). I re-traced the full fresh/update/existing flow end-to-end through test/install/load.sql, test/helpers/extension_installer.sql, test/helpers/use_test_user.sql, test/helpers/create_test_schema.sql, test/deps.sql, and bin/test_existing.

06bce4e's own two fixes check out:

  1. count_nulls.control's comment now correctly says test/helpers/use_test_user.sql (was stale at test_user.sql).
  2. test/helpers/create_test_schema.sql no longer invents \set count_nulls_load_mode 'fresh' before including use_test_user.sql; the mode now comes from whichever caller actually knows it — test/install/load.sql (via count_nulls.test_load_mode) or bin/test_existing's -v count_nulls_load_mode=fresh on the command line. Traced this through all three call sites and the values line up correctly in every mode.

All previously-confirmed findings on this PR (cleanup-order-before-role-switch, the owner short-circuit scoped to existing mode, managed-cloud admin bootstrap gating, SET ROLE after GRANT ... TO) remain fixed and unaffected by these two commits.

One new finding, posted inline: bin/test_existing is, for the first time this round, actually part of this PR's own diff (06bce4e edited it), and the edit landed right next to a stale pointer it didn't fix. The comment above create_extension_in_schema() (lines 184–189) still says the schema-creation logic is "shared with test/install/load.sql's own fresh/update installs via test/helpers/create_test_schema.sql (see that file for the full rationale)" — but load.sql hasn't touched create_test_schema.sql since the extension_installer.sql split; it now \i's extension_installer.sql directly, which is also where the actual shared rationale lives. This sentence was already stale before 06bce4e touched this file, and earlier review rounds correctly deferred it as out-of-scope while bin/test_existing wasn't part of the diff — but now that this exact comment block is where this PR's own commit added text, it's in scope. Not a functional bug, just a doc pointer worth correcting alongside the rest of this stack's careful pointer-hygiene.

No other new correctness issues found. The ON_ERROR_STOP/server-side mode selection logic in pg_temp.count_nulls_load() and the cleanup-before-switch ordering both remain correct for all three modes.
(branch: load-mode-without-if)

Comment thread bin/test_existing
Comment on lines 184 to +189
# CREATE EXTENSION count_nulls at VERSION, into a freshly, randomly
# generated schema - shared with test/install/load.sql's own fresh/update
# installs via test/helpers/create_test_schema.sql (see that file for the
# full rationale).
# full rationale). This invocation sets no count_nulls.test_load_mode GUC,
# so count_nulls_load_mode is supplied here instead: it's a genuine fresh
# install (prepare-old), never 'existing'.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale pointer, in a comment this PR's own commit (06bce4e) just edited: "shared with test/install/load.sql's own fresh/update installs via test/helpers/create_test_schema.sql (see that file for the full rationale)" is no longer true. test/install/load.sql doesn't touch create_test_schema.sql at all anymore — it \i's test/helpers/extension_installer.sql directly (test/install/load.sql:26), and that's also where the shared cleanup/install logic and its rationale actually live now. create_test_schema.sql's own header already reflects this correctly: "the work itself lives in test/helpers/extension_installer.sql."

This sentence predates this PR (it was flagged as stale in an earlier review round, when this file wasn't part of the diff yet), but 06bce4e added two new sentences immediately after it in this same comment block without correcting the one that was already wrong — so it's now shipping inaccurate documentation as part of this PR's own change, not just inheriting it unmodified.

Fix stale create_test_schema.sql pointer in bin/test_existing →

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant