diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-rename-unique-index.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-rename-unique-index.command.ts index 7cf7551d40..c0079447a0 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-rename-unique-index.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-rename-unique-index.command.ts @@ -1,17 +1,18 @@ import { Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; import { Command } from 'nest-commander'; import { isDefined } from 'twenty-shared/utils'; -import { Repository } from 'typeorm'; +import { DataSource, Repository } from 'typeorm'; import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; +import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; -import { generateDeterministicIndexNameV2 } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2'; -import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource'; +import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; @@ -27,10 +28,10 @@ export class RenameIndexNameCommand extends ActiveOrSuspendedWorkspacesMigration @InjectRepository(WorkspaceEntity) protected readonly workspaceRepository: Repository, protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, - @InjectRepository(IndexMetadataEntity) - private readonly indexMetadataRepository: Repository, - protected readonly dataSourceService: DataSourceService, protected readonly workspaceCacheService: WorkspaceCacheService, + protected readonly dataSourceService: DataSourceService, + @InjectDataSource() + private readonly coreDataSource: DataSource, ) { super(workspaceRepository, twentyORMGlobalManager, dataSourceService); } @@ -54,27 +55,30 @@ export class RenameIndexNameCommand extends ActiveOrSuspendedWorkspacesMigration this.logger.log('Dry run mode: No changes will be applied'); } - const indexes = await this.indexMetadataRepository.find({ - where: { - workspaceId, - }, - relations: ['objectMetadata', 'indexFieldMetadatas.fieldMetadata'], - }); + const { flatIndexMaps, flatObjectMetadataMaps, flatFieldMetadataMaps } = + await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatIndexMaps', + 'flatObjectMetadataMaps', + 'flatFieldMetadataMaps', + ]); let hasIndexNameChanges = false; - for (const index of indexes) { - const indexNameV2 = generateDeterministicIndexNameV2({ - flatObjectMetadata: { - nameSingular: index.objectMetadata.nameSingular, - isCustom: index.objectMetadata.isCustom, - }, - relatedFieldNames: index.indexFieldMetadatas.map( - (indexFieldMetadata) => ({ - name: indexFieldMetadata.fieldMetadata.name, - }), - ), - isUnique: index.isUnique, + for (const index of Object.values(flatIndexMaps.byId).filter(isDefined)) { + const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: index.objectMetadataId, + flatEntityMaps: flatObjectMetadataMaps, + }); + + const flatFieldMetadatas = findManyFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityIds: flatObjectMetadata.fieldMetadataIds, + flatEntityMaps: flatFieldMetadataMaps, + }); + + const { name: indexNameV2 } = generateFlatIndexMetadataWithNameOrThrow({ + flatObjectMetadata, + objectFlatFieldMetadatas: flatFieldMetadatas, + flatIndex: index, }); if (indexNameV2 === index.name) { @@ -84,16 +88,27 @@ export class RenameIndexNameCommand extends ActiveOrSuspendedWorkspacesMigration this.logger.log(`Renaming index ${index.name} to ${indexNameV2}`); hasIndexNameChanges = true; if (!isDryRun) { - await this.renameIndexOnDatabase( - dataSource, - schemaName, - index.name, - indexNameV2, - ); + const queryRunner = this.coreDataSource.createQueryRunner(); - await this.indexMetadataRepository.update(index.id, { - name: indexNameV2, - }); + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + await queryRunner.query( + `ALTER INDEX "${schemaName}"."${index.name}" RENAME TO "${indexNameV2}"`, + ); + + await queryRunner.manager.update(IndexMetadataEntity, index.id, { + name: indexNameV2, + }); + + await queryRunner.commitTransaction(); + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } } } } @@ -108,18 +123,4 @@ export class RenameIndexNameCommand extends ActiveOrSuspendedWorkspacesMigration } } } - - private async renameIndexOnDatabase( - dataSource: WorkspaceDataSource, - schemaName: string, - oldIndexName: string, - newIndexName: string, - ): Promise { - await dataSource.query( - `ALTER INDEX "${schemaName}"."${oldIndexName}" RENAME TO "${newIndexName}"`, - [], - undefined, - { shouldBypassPermissionChecks: true }, - ); - } } diff --git a/packages/twenty-server/src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2.ts b/packages/twenty-server/src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2.ts index 66f4fa689d..f31f27a4f5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2.ts +++ b/packages/twenty-server/src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2.ts @@ -1,16 +1,15 @@ import { createHash } from 'crypto'; -import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; import { computeTableName } from 'src/engine/utils/compute-table-name.util'; type GenerateDeterministicIndexNameArgs = { flatObjectMetadata: Pick; isUnique?: boolean; - relatedFieldNames: Pick[]; + orderedIndexColumnNames: string[]; }; export const generateDeterministicIndexNameV2 = ({ - relatedFieldNames, + orderedIndexColumnNames, flatObjectMetadata, isUnique = false, }: GenerateDeterministicIndexNameArgs): string => { @@ -21,11 +20,7 @@ export const generateDeterministicIndexNameV2 = ({ flatObjectMetadata.isCustom, ); - const columnsNames = relatedFieldNames.map( - (flatFieldMetadata) => flatFieldMetadata.name, - ); - - [tableName, ...columnsNames].forEach((column) => { + [tableName, ...orderedIndexColumnNames].forEach((column) => { hash.update(column); }); diff --git a/packages/twenty-server/src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util.ts b/packages/twenty-server/src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util.ts index a4804f16f9..c74a6d506a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util.ts @@ -47,12 +47,12 @@ export const generateFlatIndexMetadataWithNameOrThrow = ({ }); const isUnique = orderedFlatFields.some((flatField) => flatField.isUnique); - const orderedFlatFieldNames = orderedFlatFields.map((flatField) => ({ - name: flatField.name, - })); + const orderedIndexColumnNames = orderedFlatFields.map( + (flatField) => flatField.name, + ); const name = generateDeterministicIndexNameV2({ flatObjectMetadata, - relatedFieldNames: orderedFlatFieldNames, + orderedIndexColumnNames, isUnique, }); diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field-index.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field-index.decorator.ts index f656288fb0..0aa6a57281 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field-index.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field-index.decorator.ts @@ -1,4 +1,4 @@ -import { generateDeterministicIndexNameV2 } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2'; +import { generateDeterministicIndexName } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name'; import { type WorkspaceIndexOptions } from 'src/engine/twenty-orm/decorators/workspace-index.decorator'; import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; import { getColumnsForIndex } from 'src/engine/twenty-orm/utils/get-default-columns-for-index.util'; @@ -30,18 +30,10 @@ export function WorkspaceFieldIndex( ]; metadataArgsStorage.addIndexes({ - name: generateDeterministicIndexNameV2({ - flatObjectMetadata: { - nameSingular: convertClassNameToObjectMetadataName( - target.constructor.name, - ), - isCustom: false, - }, - relatedFieldNames: columns.map((column) => ({ - name: column, - })), - isUnique: options?.isUnique ?? false, - }), + name: `IDX_${generateDeterministicIndexName([ + convertClassNameToObjectMetadataName(target.constructor.name), + ...columns, + ])}`, columns, target: target.constructor, gate, diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-index.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-index.decorator.ts index 5a68af3177..47c2944bf2 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-index.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-index.decorator.ts @@ -1,5 +1,5 @@ import { type IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types'; -import { generateDeterministicIndexNameV2 } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2'; +import { generateDeterministicIndexName } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name'; import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util'; import { TypedReflect } from 'src/utils/typed-reflect'; @@ -26,18 +26,12 @@ export function WorkspaceIndex( ); metadataArgsStorage.addIndexes({ - name: generateDeterministicIndexNameV2({ - flatObjectMetadata: { - nameSingular: convertClassNameToObjectMetadataName( - target.constructor.name, - ), - isCustom: false, - }, - relatedFieldNames: columns.map((column) => ({ - name: column, - })), - isUnique: options?.isUnique ?? false, - }), + name: `IDX_${ + options?.isUnique ? 'UNIQUE_' : '' + }${generateDeterministicIndexName([ + convertClassNameToObjectMetadataName(target.name), + ...columns, + ])}`, columns, target: target, gate, diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-is-unique.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-is-unique.decorator.ts index 98b0d3186a..adaa1e87ba 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-is-unique.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-is-unique.decorator.ts @@ -1,4 +1,4 @@ -import { generateDeterministicIndexNameV2 } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2'; +import { generateDeterministicIndexName } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name'; import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util'; import { TypedReflect } from 'src/utils/typed-reflect'; @@ -19,18 +19,10 @@ export function WorkspaceIsUnique(): PropertyDecorator { const columns = [propertyKey.toString()]; metadataArgsStorage.addIndexes({ - name: generateDeterministicIndexNameV2({ - flatObjectMetadata: { - nameSingular: convertClassNameToObjectMetadataName( - target.constructor.name, - ), - isCustom: false, - }, - relatedFieldNames: columns.map((column) => ({ - name: column, - })), - isUnique: true, - }), + name: `IDX_${generateDeterministicIndexName([ + convertClassNameToObjectMetadataName(target.constructor.name), + ...columns, + ])}`, columns, target: target.constructor, gate, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-index.factory.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-index.factory.ts index 5d826af3cb..89018a67f4 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-index.factory.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/factories/standard-index.factory.ts @@ -5,11 +5,12 @@ import { type WorkspaceSyncContext } from 'src/engine/workspace-manager/workspac import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { IndexType } from 'src/engine/metadata-modules/index-metadata/types/indexType.types'; -import { generateDeterministicIndexNameV2 } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name-v2'; +import { generateDeterministicIndexName } from 'src/engine/metadata-modules/index-metadata/utils/generate-deterministic-index-name'; import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { type BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity'; import { CustomWorkspaceEntity } from 'src/engine/twenty-orm/custom.workspace-entity'; import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; import { isGatedAndNotEnabled } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-gate-and-not-enabled.util'; @Injectable() @@ -123,18 +124,7 @@ export class StandardIndexFactory { const indexMetadata: PartialIndexMetadata = { workspaceId: context.workspaceId, objectMetadataId: customObjectMetadata.id, - name: generateDeterministicIndexNameV2({ - flatObjectMetadata: { - nameSingular: customObjectName, - isCustom: true, - }, - relatedFieldNames: workspaceIndexMetadataArgs.columns.map( - (column) => ({ - name: column, - }), - ), - isUnique: workspaceIndexMetadataArgs.isUnique, - }), + name: `IDX_${generateDeterministicIndexName([computeTableName(customObjectName, true), ...workspaceIndexMetadataArgs.columns])}`, columns: workspaceIndexMetadataArgs.columns, isCustom: false, isUnique: workspaceIndexMetadataArgs.isUnique,