diff --git a/src/driver/sqlserver/SqlServerDriver.ts b/src/driver/sqlserver/SqlServerDriver.ts index 768a2c6b3a..1a6fcd2a54 100644 --- a/src/driver/sqlserver/SqlServerDriver.ts +++ b/src/driver/sqlserver/SqlServerDriver.ts @@ -795,7 +795,13 @@ export class SqlServerDriver implements Driver { tableColumn.isNullable !== columnMetadata.isNullable || tableColumn.asExpression !== columnMetadata.asExpression || tableColumn.generatedType !== columnMetadata.generatedType || - tableColumn.isUnique !== this.normalizeIsUnique(columnMetadata) + tableColumn.isUnique !== this.normalizeIsUnique(columnMetadata) || + (tableColumn.enum && + columnMetadata.enum && + !OrmUtils.isArraysEqual( + tableColumn.enum, + columnMetadata.enum.map((val) => val + ""), + )) // DEBUG SECTION // if (isColumnChanged) { diff --git a/src/driver/sqlserver/SqlServerQueryRunner.ts b/src/driver/sqlserver/SqlServerQueryRunner.ts index 2779a1dd17..2482a08e80 100644 --- a/src/driver/sqlserver/SqlServerQueryRunner.ts +++ b/src/driver/sqlserver/SqlServerQueryRunner.ts @@ -1566,7 +1566,7 @@ export class SqlServerQueryRunner oldColumn.name = newColumn.name } - if (this.isColumnChanged(oldColumn, newColumn, false)) { + if (this.isColumnChanged(oldColumn, newColumn, false, false, false)) { upQueries.push( new Query( `ALTER TABLE ${this.escapePath( @@ -1576,6 +1576,7 @@ export class SqlServerQueryRunner newColumn, true, false, + true )}`, ), ) @@ -1588,11 +1589,35 @@ export class SqlServerQueryRunner oldColumn, true, false, + true )}`, ), ) } + if (this.isEnumChanged(oldColumn, newColumn)) { + upQueries.push( + new Query( + `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${oldColumn.enumName}"`, + ), + ); + upQueries.push( + new Query( + `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${oldColumn.enumName}" CHECK ( ${this.getEnumExpression(newColumn)} )`, + ), + ) + downQueries.push( + new Query( + `ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${oldColumn.enumName}"`, + ), + ); + downQueries.push( + new Query( + `ALTER TABLE ${this.escapePath(table)} ADD CONSTRAINT "${oldColumn.enumName}" CHECK ( ${this.getEnumExpression(oldColumn)} )`, + ), + ) + } + if (newColumn.isPrimary !== oldColumn.isPrimary) { const primaryColumns = clonedTable.primaryColumns @@ -3166,6 +3191,7 @@ export class SqlServerQueryRunner result[1], ) } + tableColumn.enumName=checkConstraint["CONSTRAINT_NAME"] // Skip other column constraints break } @@ -3904,17 +3930,15 @@ export class SqlServerQueryRunner column: TableColumn, skipIdentity: boolean, createDefault: boolean, + skipEnum?: boolean ) { let c = `"${column.name}" ${this.connection.driver.createFullType( column, )}` - if (column.enum) { + if (!skipEnum && column.enum) { const expression = - column.name + - " IN (" + - column.enum.map((val) => "'" + val + "'").join(",") + - ")" + this.getEnumExpression(column) const checkName = this.connection.namingStrategy.checkConstraintName( table, @@ -3976,6 +4000,16 @@ export class SqlServerQueryRunner return c } + private getEnumExpression(column: TableColumn) { + if (!column?.enum) { + throw new Error('Enum not defined!'); + } + return column.name + + " IN (" + + column.enum.map((val) => "'" + val + "'").join(",") + + ")"; + } + protected isEnumCheckConstraint(name: string): boolean { return name.indexOf("CHK_") !== -1 && name.indexOf("_ENUM") !== -1 } diff --git a/src/query-runner/BaseQueryRunner.ts b/src/query-runner/BaseQueryRunner.ts index 740ac8374f..342f080f7a 100644 --- a/src/query-runner/BaseQueryRunner.ts +++ b/src/query-runner/BaseQueryRunner.ts @@ -472,6 +472,7 @@ export abstract class BaseQueryRunner { newColumn: TableColumn, checkDefault?: boolean, checkComment?: boolean, + checkEnum =true ): boolean { // this logs need to debug issues in column change detection. Do not delete it! @@ -513,10 +514,14 @@ export abstract class BaseQueryRunner { oldColumn.onUpdate !== newColumn.onUpdate || // MySQL only oldColumn.isNullable !== newColumn.isNullable || (checkComment && oldColumn.comment !== newColumn.comment) || - !OrmUtils.isArraysEqual(oldColumn.enum || [], newColumn.enum || []) + (checkEnum && this.isEnumChanged(oldColumn, newColumn)) ) } + protected isEnumChanged(oldColumn: TableColumn,newColumn: TableColumn,){ + return !OrmUtils.isArraysEqual(oldColumn.enum || [], newColumn.enum || []); + } + /** * Checks if column length is by default. */ diff --git a/test/github-issues/9457/entity/ExampleEntity.ts b/test/github-issues/9457/entity/ExampleEntity.ts new file mode 100644 index 0000000000..a1f1aad157 --- /dev/null +++ b/test/github-issues/9457/entity/ExampleEntity.ts @@ -0,0 +1,25 @@ +import { Entity } from "../../../../src/decorator/entity/Entity" +import { Column } from "../../../../src/decorator/columns/Column" +import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn" +import {Generated} from "../../../../src"; + +export enum ExampleEnum { + EnumValue1 = "enumvalue1", + EnumValue2 = "enumvalue2", + EnumValue3 = "enumvalue3", + EnumValue4 = "enumvalue4", +} + +@Entity() +export class ExampleEntity { + @Generated("increment") + @PrimaryGeneratedColumn() + id: number + + @Column({ + length: 255, + type: "simple-enum", + enum: ExampleEnum, + }) + enumcolumn: ExampleEnum +} diff --git a/test/github-issues/9457/issue-9457.ts b/test/github-issues/9457/issue-9457.ts new file mode 100644 index 0000000000..550428926e --- /dev/null +++ b/test/github-issues/9457/issue-9457.ts @@ -0,0 +1,43 @@ +import "reflect-metadata" +import { + createTestingConnections, + closeTestingConnections, +} from "../../utils/test-utils" +import {DataSource} from "../../../src/data-source/DataSource" +import {expect} from "chai"; + +describe("github issues > #9457 No changes in database schema were found, when simple-enum is changed.", () => { + let dataSources: DataSource[] + before( + async () => + (dataSources = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + migrations: [__dirname + "/migration/*{.js,.ts}"], + schemaCreate: false, + dropSchema: true, + enabledDrivers: ["mssql"] + })), + ) + after(() => closeTestingConnections(dataSources)) + + it("should drop and recreate 'CHECK' constraint to match enum values", async () => { + await Promise.all( + dataSources.map(async (dataSource) => { + await dataSource.runMigrations() + + const sqlInMemory = await dataSource.driver + .createSchemaBuilder() + .log() + + expect(sqlInMemory.upQueries.length).to.eql(2); + expect(sqlInMemory.upQueries[0].query).to.eql('ALTER TABLE "example_entity" DROP CONSTRAINT "CHK_be8ed063b3976da24df4213baf_ENUM"') + expect(sqlInMemory.upQueries[1].query).to.eql(`ALTER TABLE "example_entity" ADD CONSTRAINT "CHK_be8ed063b3976da24df4213baf_ENUM" CHECK ( enumcolumn IN ('enumvalue1','enumvalue2','enumvalue3','enumvalue4') )`) + + expect(sqlInMemory.downQueries.length).to.eql(2); + expect(sqlInMemory.downQueries[0].query).to.eql('ALTER TABLE "example_entity" DROP CONSTRAINT "CHK_be8ed063b3976da24df4213baf_ENUM"') + expect(sqlInMemory.downQueries[1].query).to.eql(`ALTER TABLE "example_entity" ADD CONSTRAINT "CHK_be8ed063b3976da24df4213baf_ENUM" CHECK ( enumcolumn IN ('enumvalue1','enumvalue2','enumvalue3') )`) + })) + }) + + // you can add additional tests if needed +}) diff --git a/test/github-issues/9457/migration/1676011161422-init.ts b/test/github-issues/9457/migration/1676011161422-init.ts new file mode 100644 index 0000000000..1238d49f47 --- /dev/null +++ b/test/github-issues/9457/migration/1676011161422-init.ts @@ -0,0 +1,20 @@ +import {MigrationInterface, QueryRunner} from "../../../../src" + +export class init1676011161422 implements MigrationInterface { + name = "init1676011161422" + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "example_entity" + ( + "id" int NOT NULL IDENTITY(1,1), + "enumcolumn" nvarchar(255) CONSTRAINT CHK_be8ed063b3976da24df4213baf_ENUM CHECK (enumcolumn IN ('enumvalue1','enumvalue2','enumvalue3')) NOT NULL, + CONSTRAINT "PK_fccd73330168066a434dbac114f" PRIMARY KEY ("id") + )`, + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "example_entity"`) + } +}