fix(BA-6897): make pre-constraint RBAC backfill upgrades skip existing rows#12883
Draft
jopemachine wants to merge 2 commits into
Draft
Conversation
…g rows Six RBAC backfill migrations insert with a bare ON CONFLICT DO NOTHING. At their point in the chain the permissions table carries only a primary key on id, and id defaults to uuid_generate_v4(), so the only conflict target is a freshly generated UUID and the clause never fires. The same holds for permission_groups and object_permissions in the vfolder migration. Re-running any of these upgrades duplicated every row. This was masked while the downgrades deleted the rows first. With the downgrades now forward-only, a downgrade/upgrade cycle would accumulate duplicates until the chain reached the dedupe migrations that run before the unique constraints are created. Skip existing rows explicitly instead, and let DISTINCT collapse repeats within a batch. association_scopes_entities is left alone: it already carries uq_scope_id_entity_id at these revisions, so its bare ON CONFLICT works. The nine migrations whose constraints already exist are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📚 Stacked PRs
BA-6896fix: RBAC backfill downgrades no longer delete live permissionsBA-6897fix: pre-constraint RBAC backfill upgrades skip existing rows ← you are hereMerge in order, bottom-up. Each PR is based on the one above it, so its diff shows only its own layer.
Problem
Six RBAC backfill migrations insert with a bare
ON CONFLICT DO NOTHING, but the clause can never fire.At their point in the chain,
permissions(created in9adcd6f48ba1) carries only:iddefaults touuid_generate_v4(), so the sole conflict target is a freshly generated UUID. There is no unique constraint on(permission_group_id, entity_type, operation)—ix_id_permission_group_idisunique=False. The same holds forpermission_groupsandobject_permissionsin the vfolder migration.3f5c20f7bb07only adds thepermissionsunique constraint much later in the chain, and it deletes duplicates before creating it — evidence that duplicates were reachable all along.This was masked while the downgrades deleted the rows first. With #12881 making them forward-only, a downgrade/upgrade cycle accumulates duplicates until the chain reaches
3f5c20f7bb07/7369d1eb7d4a, which dedupe before creating the constraints.Verified by running the real migration code
These six target the pre-redesign schema (
permission_group_id), which no longer exists at head, so they cannot be driven through alembic on a head DB. Instead the tables were recreated exactly as defined at that revision and each migration module's own_migrate_new_entity_type()was invoked three times against a real Postgres connection — the real code path, not a transcription of it.Row counts after each run:
mainrun 1 → 2 → 3Scope
permission_groups+object_permissions).ON CONFLICTgenuinely fires (a bareON CONFLICT DO NOTHINGcatches any unique violation).association_scopes_entitiesleft alone: it carriesuq_scope_id_entity_idat these revisions (82f4d3dea750recreates it after430b1631804ddrops it), so its bareON CONFLICTworks.DISTINCTcollapses repeats within a single batch. Editing releasedupgrade()bodies is safe here: a DB already past these revisions never re-runs them.🤖 Generated with Claude Code