fix(server): normalize legacy index names (command) (#22053)

## TL;DR

Adds a workspace upgrade command that normalizes index names to the
current v2 deterministic naming convention (IDX_ prefix). This will
close https://github.com/twentyhq/twenty/issues/21383 : uniqueness
constraint cannot be disabled (for users who set it before v2
determinist naming is enforced).

### Background

The deterministic index name embeds the table name, columns, uniqueness
and where clause. The naming convention changed on **2025-09-23**
(#14567 - added the `IDX_`/`IDX_UNIQUE_` prefix and folded the table
name into the hash). Index rows created before that kept their old name
in `core."indexMetadata"`, and nothing rewrites it (only targeted
phone/relation rebuilds got new names).

Code paths that locate an index by recomputing its expected name then
miss these legacy-named rows. The most visible symptom is #21383:
toggling a field's `isUnique` from `true` → `false` recomputes the
expected unique-index name, fails to find the legacy-named index, and
silently no-ops — so uniqueness can't be disabled.

### What the command does

Per workspace, for each index:
- recomputes the expected name with the same generator the app uses
(`generateFlatIndexMetadataWithNameOrThrow`);
- if the stored name differs → **rename** it (metadata `UPDATE` + a new
metadata-only `ALTER INDEX … RENAME`, which preserves the unique
constraint with no rebuild/lock);
- if a correctly-named twin already exists (the legacy +
freshly-generated duplicate case) → **drop the redundant** one (physical
+ metadata, field rows cascade) and keep the canonical;
- invalidates the metadata cache so the running app + `isUnique`
derivation reflect the new names.

Honors `--dry-run`, wraps writes in a transaction (rolls back on error),
and skips (with a warning) any single index whose name can't be
recomputed so it can't abort the whole workspace.

### Notable changes
- New `renameIndex` on `WorkspaceSchemaIndexManagerService` (`ALTER
INDEX IF EXISTS … RENAME`).
- Planning logic extracted into a pure, unit-tested util
(`planIndexNameNormalization`).
This commit is contained in:
Marie
2026-06-29 16:04:40 +02:00
committed by GitHub
parent 6fa803edc2
commit 4eb28b73c7
7 changed files with 773 additions and 0 deletions
@@ -90,4 +90,20 @@ export class WorkspaceSchemaIndexManagerService {
await queryRunner.query(sql);
}
async renameIndexWithoutRebuild({
queryRunner,
schemaName,
fromIndexName,
toIndexName,
}: {
queryRunner: QueryRunner;
schemaName: string;
fromIndexName: string;
toIndexName: string;
}): Promise<void> {
const sql = `ALTER INDEX ${escapeIdentifier(schemaName)}.${escapeIdentifier(fromIndexName)} RENAME TO ${escapeIdentifier(toIndexName)}`;
await queryRunner.query(sql);
}
}