From bad1f20012769f015ce4fcd286daf89bea93c080 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Mon, 18 May 2026 23:04:15 +0200 Subject: [PATCH] fix(server): handle legacy PK name in 2.6 rename-permission-flag upgrade (#20697) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary The 2.6 `RenamePermissionFlagToRolePermissionFlag` upgrade command failed on staging and dev with: ``` [QueryFailedError] constraint "PK_a02789db60620a1e9f90147b50f" for table "rolePermissionFlag" does not exist in RenamePermissionFlagToRolePermissionFlag1778235340020 (2.6.0) (instance fast) ``` ### Root cause TypeORM names PKs as `PK_`. So: - `permissionFlag_id` → `PK_a02789db60620a1e9f90147b50f` - `settingPermission_id` → `PK_8c144a021030d7e3326835a04c8` - `rolePermissionFlag_id` → `PK_76591adc8035c2e7b0cd6115136` On databases initially migrated before the v1.5.5 migration squash (#15183), the table was renamed `settingPermission` → `permissionFlag` via the pre-squash migration `1753149175945-renameSettingPermissionToPermissionFlag.ts`. That migration renamed the table, the column, the unique index, and the role FK, but **never renamed the PK constraint** — and Postgres does not auto-rename constraints on `ALTER TABLE ... RENAME TO`. Those instances therefore still carry the legacy PK name `PK_8c144a021030d7e3326835a04c8`. Fresh installs (squashed `setupMetadataTables` migration) instead have the expected `PK_a02789db60620a1e9f90147b50f`. The 2.6 upgrade only handled the fresh-install name, so it broke for any DB that went through the historical rename chain. ### Fix Replace the brittle `RENAME CONSTRAINT` with `DROP CONSTRAINT IF EXISTS` for both historical PK names, followed by `ADD CONSTRAINT ... PRIMARY KEY ("id")` with the canonical new name. The migration now converges to the same PK name regardless of the DB's history. The same pattern is applied symmetrically in `down()`. ### Why this is safe - The whole instance command runs in a transaction (`InstanceCommandRunnerService.runFastInstanceCommand`). - The first statement (`ALTER TABLE ... RENAME TO`) takes `ACCESS EXCLUSIVE` on the table, so the drop/add window for the PK is invisible to any concurrent writer — they queue on the lock until commit. - No FK references `rolePermissionFlag.id` at this point in the sequence (migration 22 introduces an FK pointing at the new `permissionFlag` catalog created in migration 21, not at the renamed grant table), so dropping the PK does not cascade or block. - `NOT NULL` and the `uuid_generate_v4()` default on `id` are column-level and remain in place when the PK is dropped. ## Test plan - [ ] Run 2.6 upgrade against a fresh-install database (PK = `PK_a02789db60620a1e9f90147b50f`) — should succeed. - [ ] Run 2.6 upgrade against a pre-squash database (PK = `PK_8c144a021030d7e3326835a04c8`, reproducible on current staging/dev) — should now succeed. - [ ] Verify post-migration: `rolePermissionFlag` exists, PK is named `PK_76591adc8035c2e7b0cd6115136`, all FKs and indexes named as expected. - [ ] Run `down()` and verify table returns to `permissionFlag` with PK `PK_a02789db60620a1e9f90147b50f`. - [ ] Subsequent migrations (`1778235340021` permission-flag catalog, `1778235340022` link, `1778235340023` backfill) still apply cleanly. --- ...-permission-flag-to-role-permission-flag.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1778235340020-rename-permission-flag-to-role-permission-flag.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1778235340020-rename-permission-flag-to-role-permission-flag.ts index 132a07f910..9886bbd6b2 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1778235340020-rename-permission-flag-to-role-permission-flag.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1778235340020-rename-permission-flag-to-role-permission-flag.ts @@ -8,14 +8,18 @@ export class RenamePermissionFlagToRolePermissionFlagFastInstanceCommand implements FastInstanceCommand { public async up(queryRunner: QueryRunner): Promise { - // The legacy permissionFlag table stores per-role grants (`roleId` + `flag`). - // Rename it to preserve those rows before creating the new permissionFlag catalog table. await queryRunner.query( `ALTER TABLE "core"."permissionFlag" RENAME TO "rolePermissionFlag"`, ); await queryRunner.query( - `ALTER TABLE "core"."rolePermissionFlag" RENAME CONSTRAINT "PK_a02789db60620a1e9f90147b50f" TO "PK_76591adc8035c2e7b0cd6115136"`, + `ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT IF EXISTS "PK_a02789db60620a1e9f90147b50f"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT IF EXISTS "PK_8c144a021030d7e3326835a04c8"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" ADD CONSTRAINT "PK_76591adc8035c2e7b0cd6115136" PRIMARY KEY ("id")`, ); await queryRunner.query( @@ -26,9 +30,6 @@ export class RenamePermissionFlagToRolePermissionFlagFastInstanceCommand `ALTER INDEX "core"."IDX_PERMISSION_FLAG_ROLE_ID" RENAME TO "IDX_ROLE_PERMISSION_FLAG_ROLE_ID"`, ); - // Re-hash inherited constraints/indexes so TypeORM's schema diff matches - // the renamed table. Original names were derived from "permissionFlag" - // and stay free for the new catalog table created by the next migration. await queryRunner.query( `ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT "FK_13f8ca9c517976733a1ce4c10eb"`, ); @@ -92,7 +93,10 @@ export class RenamePermissionFlagToRolePermissionFlagFastInstanceCommand ); await queryRunner.query( - `ALTER TABLE "core"."rolePermissionFlag" RENAME CONSTRAINT "PK_76591adc8035c2e7b0cd6115136" TO "PK_a02789db60620a1e9f90147b50f"`, + `ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT IF EXISTS "PK_76591adc8035c2e7b0cd6115136"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" ADD CONSTRAINT "PK_a02789db60620a1e9f90147b50f" PRIMARY KEY ("id")`, ); await queryRunner.query(