Skip to content

chore: add export-db-dump.sh, cover extension_version_change in the dump - #2258

Merged
netomi merged 8 commits into
mainfrom
chore/db-dump-export-and-version-change
Sep 28, 2026
Merged

netomi merged 8 commits into
mainfrom
chore/db-dump-export-and-version-change

Conversation

@netomi

@netomi netomi commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

scrub-db-dump.sh and import-db-dump.sh already agree on a per-table Postgres COPY-TEXT dump format, but nothing in the repo actually produces that dump from a live source - someone has to hand-roll the \copy ... to commands themselves. Ran into this while testing #2246 against a production DB copy.

  • scripts/export-db-dump.sh (new): exports the 9 tables import-db-dump.sh expects from a given Postgres connection string, all in one transaction/snapshot (REPEATABLE READ, READ ONLY) so concurrent writes on the source can't leave the tables inconsistent with each other. Stages into a temp directory and promotes each table's file individually once psql has committed - deliberately per-file rather than a directory-level swap, since scrub-db-dump.sh leaves *.csv.bak originals alongside the redacted files and a whole-directory swap would silently delete those (and anything else already there) on a routine re-export. This isn't atomic as a set: a failure partway through the promotion loop can leave a mixed generation, accepted as the better tradeoff over destroying files the export doesn't own (a stale CSV is fixed by re-running the export). Also checks for psql up front, restricts permissions on the raw (pre-redaction) dump, resolves symlinked output directories to their real path, and runs psql -X so a ~/.psqlrc can't override its error handling.
  • scripts/import-db-dump.sh: adds extension_version_change (introduced by V1_71) to the table list. It predates that table, so a DB copy built via this pipeline had an always-empty extension_version_change regardless of production reality - nothing that reads the registry changes feed (/api/-/version-changes, and anything fix: report a namespace rename on the registry changes feed #2246 touches) could be exercised against such a copy. Its sequence reset also falls back to the <table>_seq naming convention when pg_get_serial_sequence can't resolve a sequence (true for extension_version_change on any schema before fix: report a namespace rename on the registry changes feed #2246's ownership fix, including a fresh target pinned to an older dump version).

Depends on #2246 for full correctness: extension_version_change_seq (V1_71) was created without OWNED BY, unlike every other entity's sequence, which means pg_get_serial_sequence can't find it on an unpatched schema. #2246's V1_77 now fixes that (folded in there instead of shipping a second, colliding V1_77 here). The naming-convention fallback above means this still works correctly regardless of merge order, but the underlying schema gap is real either way.

Test plan

  • bash -n on both scripts.
  • Per-file publish verified directly against a real filesystem with a psql stub: a pre-existing .csv.bak and an unrelated file both survive an export run untouched, all 9 real tables refresh.
  • Symlinked output directory and apostrophe-in-path handling verified directly (reproduced each bug first, then confirmed the fix).
  • detect_actual_cols/version-detection empty-table handling verified against a real empty CSV and a set -e array-assignment repro.
  • Not run end-to-end here (no Docker available in this environment, which import-db-dump.sh needs for its scratch Postgres/Flyway containers) - worth a real run through export-db-dump.sh → scrub-db-dump.sh → import-db-dump.sh before merging.

🤖 Generated with Claude Code

scrub-db-dump.sh and import-db-dump.sh already had a well-defined,
matched per-table dump format, but nothing in the repo produced that
dump from a live source in the first place - export-db-dump.sh fills
that gap, taking every table in one transaction/snapshot so concurrent
writes on the source can't leave the tables inconsistent with each
other.

extension_version_change (added by V1_71) was missing from both the
export and the existing import-db-dump.sh entirely, since it predates
that table. Without it, a DB copy built via this pipeline can't
exercise anything version-changes-feed related - the table is simply
empty regardless of what's actually in the app. Added it to both
scripts' table lists; import-db-dump.sh's delete/import ordering,
column adaptation and id-sequence fixup are all already driven
generically off that list, so no other logic changes were needed
there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@utafrali utafrali left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The import-db-dump.sh update is a clean, correct one-liner addition with accurate doc updates throughout. The new export-db-dump.sh is well-structured and the REPEATABLE READ + READ ONLY transaction approach correctly pins all \copy calls to a single consistent snapshot. The only gap is a missing psql availability check, which is inconsistent with the other scripts in the same directory.

Comment thread scripts/export-db-dump.sh Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Raw dumps need secure permissions, failure-safe publishing, and handling for empty-table imports.

Review effort: Balanced
Findings: 1 High severity · 2 Medium severity

Open (3)
What changed in this PR

Adds a consistent PostgreSQL export path and includes registry version-change history in database dumps.

Changes:

  • Adds transactional export of nine database tables.
  • Adds extension_version_change to imports.
  • Updates import documentation and diagnostics.
File Description
scripts/​export-db-dump.sh Exports database tables from one snapshot.
scripts/​import-db-dump.sh Imports version-change records and updates table counts.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/export-db-dump.sh Resolved
Comment thread scripts/export-db-dump.sh Resolved
Comment thread scripts/import-db-dump.sh Resolved
- export-db-dump.sh: check for psql on PATH up front, matching
  scrub-db-dump.sh/import-db-dump.sh, instead of failing on the last
  line with an empty output directory left behind.
- export-db-dump.sh: set a restrictive umask before creating anything.
  The raw dump holds personal_access_token.value,
  signature_key_pair.private_key and user_data.eclipse_token until
  scrub-db-dump.sh redacts them, so it shouldn't inherit whatever
  umask the caller happens to have.
- export-db-dump.sh: stage \copy output next to the target directory
  and only move files into place once psql has committed. \copy
  writes its file directly, independent of the transaction, so a
  failure partway through previously left the target with a mix of
  freshly-overwritten and stale files that still passed
  import-db-dump.sh's existence checks.
- import-db-dump.sh: handle a table with zero dump rows during Flyway
  version auto-detection instead of crashing. Verified the crash first
  (an empty CSV makes counts.most_common(1)[0] raise IndexError, which
  set -e propagates through the array assignment and kills the
  script), then confirmed the fix handles it: an empty dump file is
  now treated as carrying no signal for that table rather than
  vetoing every candidate version, and detection is refused with a
  clear message only if every table is empty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Default-path inconsistency and empty-table version detection can break the advertised export-to-import pipeline.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (3)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Resolve default dump path relative to the project root

scripts/​export-db-dump.sh:12

The default output path is resolved from the caller's working directory, while both scrub-db-dump.sh and import-db-dump.sh resolve their default db/dump under the repository root. Invoking this script by absolute path from another directory therefore writes a dump that the next pipeline step cannot find with its defaults. Anchor only the default path to the script's project root.

Comment thread scripts/import-db-dump.sh Resolved
…path

import-db-dump.sh: an empty dump file for a table was skipped from
version matching entirely, not just its exact column count - so a
version from before that table existed could still "match" on the
strength of every other table alone (concretely: an empty
extension_version_change.csv could select a pre-V1_71 schema). The
file existing at all means the table existed at export time, so a
candidate version where it has zero columns (doesn't exist yet) is
now rejected outright; only the exact-count comparison is skipped
when there's no row signal for it. Verified both branches directly
against a table_column_count stub.

export-db-dump.sh: the default output directory was relative to the
caller's cwd, unlike scrub-db-dump.sh/import-db-dump.sh's own db/dump
default, which both resolve against the repo root. Running this
script by path from elsewhere silently wrote the dump somewhere
neither of those two scripts would look for it. Anchored the default
to the script's own project root the same way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Sequence reset and atomic publication issues can leave imported databases or dump generations inconsistent.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Multi-file publication is not atomic

scripts/​export-db-dump.sh:58

Staging protects against a failure during psql, but promoting nine files in a loop is still not atomic as a set. If the process is interrupted or a later mv fails, earlier tables are new while later tables remain old, recreating the mixed dump that staging is intended to prevent; the importer only checks that each file exists and will accept it. Publish a generation atomically (for example via a versioned directory/pointer) or add a manifest that import/scrub validate before consuming the files.

Medium severity Unowned sequence is not reset after import

scripts/​import-db-dump.sh:549

extension_version_change_seq is created as the column default but is never OWNED BY extension_version_change.id (V1_71__ExtensionVersion_Changes_Feed.sql:11-15). Consequently pg_get_serial_sequence returns NULL for this newly included table, so the imported IDs do not advance its sequence and the next feed insert can fail with a duplicate primary key. Explicitly map this unowned sequence (or add ownership in a migration) so it is reset with the others.

… ownership

export-db-dump.sh: publish the staged dump as two directory renames
(old -> .previous, staged -> live) instead of moving each table's
file into place individually. A crash or a failed move partway
through the old per-file loop could still leave the target with some
tables on the new generation and some on the old one - exactly the
mixed state staging was introduced to prevent in the first place.
Verified both the success and failure paths directly: on failure the
target is left completely untouched, on success no staging/backup
directories are left behind.

V1_77__ExtensionVersionChange_Sequence_Ownership.sql: fixes a real,
independently-verified server-side gap this PR's own testing exposed.
extension_version_change_seq (V1_71) was created without OWNED BY,
unlike every other entity's sequence - confirmed by checking
V1_40__Entity_Sequencies.sql, which explicitly added OWNED BY for
every one of import-db-dump.sh's original 8 tables. Without it,
pg_get_serial_sequence('extension_version_change', 'id') returns
NULL, so import-db-dump.sh's existing generic per-table sequence
reset silently skips this table - reproducing the exact duplicate-key
crash hit earlier when this table's sequence wasn't reset by hand,
except now happening through the "fixed" script itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Historical-schema imports can retain a stale sequence, and dump publication and migration numbering have unresolved operational risks.

Review effort: Balanced
Findings: 2 High severity · 1 Medium severity

Open (3)

Comment thread scripts/export-db-dump.sh Resolved
netomi added a commit that referenced this pull request Sep 28, 2026
Folded in from #2258 rather than shipping as its own V1_77, which
would collide with this PR's own V1_77 the moment both land - same
table, same migration file is also just a better fit: this PR is the
one already touching extension_version_change's schema semantics.

extension_version_change_seq was created (V1_71) without OWNED BY,
unlike every other entity's sequence (V1_40 added this same link for
the sequences that predated it). Without it, pg_get_serial_sequence
can't find the sequence, which silently defeats
scripts/import-db-dump.sh's generic per-table sequence reset for this
one table - discovered while testing #2258's db-dump tooling changes
against a production copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Moved the ALTER SEQUENCE fix into #2246's own V1_77 (same table, same
migration file is the better fit anyway) rather than shipping a
second, colliding V1_77 here - Flyway can't apply two migrations at
the same version once both PRs land.

import-db-dump.sh: pg_get_serial_sequence also returns NULL for
extension_version_change on any target schema before that ownership
fix has actually run - notably a fresh target, which this script
deliberately migrates only to the dump's own Flyway version, not the
latest one. Added a fallback to the table's own <table>_seq naming
convention (every table here follows it once it has a sequence) so
the reset works regardless of merge order or which schema version the
target ends up on.

export-db-dump.sh: the two-rename publish wasn't atomic as a pair -
if the second mv failed after the first succeeded, the exit trap
deleted the old generation it had just moved aside instead of
restoring it, leaving no usable output directory at all. Track
whether publication completed and have cleanup restore the previous
directory when it didn't. Verified all three paths (failure before
any rename, failure between the two renames, success) directly:
failure now always leaves a usable directory with the right contents,
success leaves no leftovers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The exporter can mishandle symlinked paths, load unsafe psql startup configuration, and delete unrelated output-directory contents.

Review effort: Balanced
Findings: None

Resolved since last review (3)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Canonicalize symlinked output directories before staging

scripts/​export-db-dump.sh:28

pwd preserves a logical symlink path, so when output-dir is a symlink this later renames the symlink itself and replaces it with a new directory; the real target directory receives no dump. Canonicalize the physical directory before creating the sibling stage so export behaves like scrub/import on symlinked dump locations.

This issue also appears on line 75 of the same file.

Medium severity Run psql with -X to prevent unsafe user configuration

scripts/​export-db-dump.sh:67

Run psql with -X. Otherwise the caller's ~/.psqlrc is loaded after the command-line variable assignment and can change ON_ERROR_STOP or transaction behavior; a COPY error can then leave psql exiting successfully and cause an incomplete stage to be published. The direct-client path already enforces this in scripts/import-db-dump.sh:156-160.

pwd (without -P) preserves a logical symlink path, so when the output
directory is a symlink the rename-based publish renamed the symlink
itself rather than the real directory it points at - the actual
target directory was silently left on the old generation forever,
and a plain directory took the symlink's place. Reproduced directly
(a symlinked "dump-link" ended up replaced by a real directory, with
the real target directory it used to point at orphaned and
untouched), then confirmed pwd -P fixes it: the symlink stays a
symlink and correctly resolves to the new generation.

Also run psql with -X, matching import-db-dump.sh's own target_psql -
without it, a ~/.psqlrc loads after -v ON_ERROR_STOP=1 and could
override it, letting a broken export get published as if it were
complete.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@netomi

netomi commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the two "previously missed" findings from the latest review (no standalone threads to reply on):

  • Symlinked output directories: `pwd` (without `-P`) preserves a logical symlink path, so when the output directory is a symlink, the rename-based publish renamed the symlink itself rather than the real directory it points at - the actual target directory was silently orphaned on the old generation, replaced by a plain directory at the symlink's old path. Reproduced this directly (a symlinked `dump-link` ended up replaced by a real directory; the real target it used to point at survived but became unreachable through it), then confirmed `pwd -P` fixes it - the symlink stays a symlink and correctly resolves to the new generation.
  • `psql -X`: added, matching `import-db-dump.sh`'s own `target_psql` - without it, a `~/.psqlrc` loads after `-v ON_ERROR_STOP=1` and could override it, letting a broken export get published as if it were complete.

Fixed in ad82d62.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Export publication can delete ancillary files, and valid output paths containing apostrophes fail.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Escape staging paths embedded in generated psql commands

scripts/​export-db-dump.sh:73

The generated \copy command embeds the staging path inside a single-quoted psql argument without escaping it. A valid output path (including the default when the checkout path contains an apostrophe) then fails to parse. Run psql from inside the staging directory and use fixed relative filenames so arbitrary filesystem paths never enter the command text.

Comment thread scripts/export-db-dump.sh Resolved
Publishing by renaming the whole output directory moved everything in
it - not just the 9 generated CSVs - into the backup path the exit
trap then deleted. scrub-db-dump.sh deliberately leaves *.csv.bak
originals alongside the redacted files until they've been reviewed;
a whole-directory swap silently destroyed those (and anything else
already in the directory) the moment this script is re-run to refresh
a dump. Reverted to promoting each of the 9 known files individually,
which can only ever touch filenames this script itself owns. Verified
directly with a psql stub: a pre-existing .csv.bak and an unrelated
file both survive an export run untouched, while all 9 real tables
refresh.

Also stopped embedding STAGE_DIR's full path inside the generated
\copy command's single-quoted argument - that path comes from OUT_DIR
(derived from the repo checkout path or a caller-supplied directory),
and psql has no way to escape a literal "'" in it, so a path
containing one broke the quoting outright. Reproduced this directly
(a literal apostrophe in the path corrupts the generated SQL), then
fixed it by cd-ing into the staging directory and \copy-ing to bare
relative filenames instead, which can never contain a quote since
they're fixed table names under this script's own control. Verified
the generated SQL never contains the path at all, and a full export
run into an apostrophe-containing directory succeeds end to end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@netomi

netomi commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the "previously missed" quoting finding too (no standalone thread): the generated `\copy ... to '${STAGE_DIR}/${t}.csv' ...` embedded STAGE_DIR's full path inside a single-quoted psql argument, with no way to escape a literal `'` in it - a path containing one (plausible: a username, a parent directory name) broke the SQL outright. Reproduced this directly, then fixed it by `cd`-ing into the staging directory and `\copy`-ing to bare relative filenames instead, so no filesystem path ever enters the generated SQL text at all. Verified a full export into an apostrophe-containing directory now succeeds end to end.

Fixed in 036f315 (same commit as the file-preservation fix above).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The PR description claims atomic directory publication, but the implementation promotes files individually and can leave mixed generations.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Resolved since last review (1)

Comment thread scripts/export-db-dump.sh Resolved
The comment explained why per-file promotion was chosen over a
directory swap, but not what it gives up: a failure partway through
can leave a mixed generation. Spelled that out next to the loop
itself, not just in the PR description.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@netomi
netomi merged commit c86d7d7 into main Sep 28, 2026
5 checks passed
@netomi
netomi deleted the chore/db-dump-export-and-version-change branch September 28, 2026 18:51
netomi added a commit that referenced this pull request Sep 28, 2026
Folded in from #2258 rather than shipping as its own V1_77, which
would collide with this PR's own V1_77 the moment both land - same
table, same migration file is also just a better fit: this PR is the
one already touching extension_version_change's schema semantics.

extension_version_change_seq was created (V1_71) without OWNED BY,
unlike every other entity's sequence (V1_40 added this same link for
the sequences that predated it). Without it, pg_get_serial_sequence
can't find the sequence, which silently defeats
scripts/import-db-dump.sh's generic per-table sequence reset for this
one table - discovered while testing #2258's db-dump tooling changes
against a production copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
netomi added a commit that referenced this pull request Sep 28, 2026
* fix: report a namespace rename on the registry changes feed

Renaming a namespace moved every extension's versions to the new
namespace row in place, without appending anything to
extension_version_change. The feed kept reporting the old
(namespace, extension, version) tuple as ACTIVE forever, and the new
tuple never appeared until an unrelated transition happened to touch
it (#2244).

ChangeNamespaceService now records a REMOVED entry for the old tuple
of every version the feed already knew about, and an ACTIVE entry for
the new tuple of every version still active, both at the same instant
the rename happens. wasReportedAsAvailable moves from ExtensionService
to RepositoryService so both call sites share it.

A new migration backfills the same two entries for versions that were
already renamed before this fix, stamping changed_at with the
migration's own run time rather than a reconstructed rename time,
since none is known and a backdated entry would sort into a part of
the feed a caught-up consumer has already read past.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: withdraw every abandoned namespace tuple, not just the latest

A version could pick up a further, correctly-recorded transition
under its new namespace after a silent rename (e.g. an admin
deactivation, which always reads the version's current namespace),
without that ever withdrawing the stale tuple the rename abandoned.
Collapsing to one row per version, as before, hid that stale tuple:
its single latest entry already matched the current namespace, so the
old (still not-REMOVED) namespace was never even considered.

Track history per (version, namespace) for the departure side instead,
so an abandoned namespace's own latest state is checked independently
of what happened later under a different one. The arrival side keeps
using the version's single overall-latest entry, which is still the
right question for "has the feed caught up to where this version
lives now".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: scope wasReportedAsAvailable to the version's current namespace

main added V1_76__Unique_Active_Review.sql after this branch was cut,
colliding with this PR's migration version - renamed the migration to
V1_77.

More importantly: once a version's changes-feed history can span more
than one namespace (a rename backfill, or two renames over its life),
the single most recent entry for a version is no longer necessarily
the one that reflects its current namespace - an entry appended later
for an abandoned namespace (e.g. this PR's own backfill, timestamped
with the migration's run time) can outrank an earlier, correctly
recorded entry under the namespace the version actually lives in now.
wasReportedAsAvailable, read that way, would then answer for the wrong
tuple and skip or duplicate a withdrawal on a later delete or rename.

Added ExtensionVersionChangeRepository#findFirstByExtensionVersionAndNamespaceOrderByChangedAtDescIdDesc
and scoped wasReportedAsAvailable to it, resolving the latest state
for the version's current namespace specifically. Covered by a new
ExtensionVersionChangeRepositoryTest exercising the exact scenario.

Documented two remaining, narrower gaps as known limitations rather
than folding them into this fix: a concurrent writer (scan completion,
admin action) racing a rename isn't locked against today, and neither
is any other transition writer - pre-existing, not introduced here,
and fixing it means auditing every writer, not just this one. A
version renamed back to a namespace it occupied before is only
repaired correctly if that namespace's own last entry already agrees
with the version's current state - not a concern for any of the four
renames #2244 was filed about.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: set target_platform in ExtensionVersionChangeRepositoryTest

extension_version.target_platform has been NOT NULL since V1_22, so
persisting an ExtensionVersion without it failed CI's real-Postgres
run even though it compiled fine and the mocked unit tests never
touch the column.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: lock the affected tables before the migration's snapshots

A rolling deployment can have an old-version instance still serving
traffic while this migration runs on a new one. Without a lock, a
concurrent purge landing between the departures snapshot and its
insert would delete the extension_version row that snapshot already
captured the id of, failing the insert on the foreign key; a
concurrent rename landing between the departures and arrivals
snapshots would let one see the version under its old namespace and
the other under a namespace newer still, repairing neither correctly.

LOCK TABLE ... IN SHARE MODE on extension, extension_version and
extension_version_change before the first snapshot, same pattern
V1_76__Unique_Active_Review.sql already uses for the same class of
rolling-deployment race.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: verify namespaces at call time in ChangeNamespaceServiceTest

recordExtensionVersionChange takes no namespace argument - the real
implementation reads extVersion.getExtension().getNamespace() at call
time, from the same mutable Extension object changeExtensionNamespace
later mutates in place. The order-only assertion couldn't tell that
apart from a namespace already changed before either call: it would
still see REMOVED-then-ACTIVE, both against the new namespace.

Capture the namespace via doAnswer at each invocation instead, and
assert it was "old" for the departure and "new" for the arrival.
Verified this catches the regression by temporarily swapping
changeExtensionNamespace before recordNamespaceDeparture locally: the
new assertion fails while the order-only one still passed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* style: compact comments added across this fix

Kept the why, cut the restating and repetition - same content in
roughly half the lines across the service, repository, migration and
tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: also fix extension_version_change_seq's missing OWNED BY here

Folded in from #2258 rather than shipping as its own V1_77, which
would collide with this PR's own V1_77 the moment both land - same
table, same migration file is also just a better fit: this PR is the
one already touching extension_version_change's schema semantics.

extension_version_change_seq was created (V1_71) without OWNED BY,
unlike every other entity's sequence (V1_40 added this same link for
the sequences that predated it). Without it, pg_get_serial_sequence
can't find the sequence, which silently defeats
scripts/import-db-dump.sh's generic per-table sequence reset for this
one table - discovered while testing #2258's db-dump tooling changes
against a production copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* style: compact the migration's comments further

Kept the why, cut the restating - same content in fewer lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.

3 participants