diff --git a/src/migration/MigrationExecutor.ts b/src/migration/MigrationExecutor.ts index 455c0d0d43..5e7e3d521f 100644 --- a/src/migration/MigrationExecutor.ts +++ b/src/migration/MigrationExecutor.ts @@ -323,7 +323,10 @@ export class MigrationExecutor { }); // sort them by timestamp - return migrations.sort((a, b) => a.timestamp - b.timestamp); + const sorted = migrations.sort((a, b) => a.timestamp - b.timestamp); + + // remove duplicates + return sorted.filter((migration, index) => sorted.findIndex((unique) => unique.name === migration.name) === index); } /** @@ -354,9 +357,9 @@ export class MigrationExecutor { values["timestamp"] = migration.timestamp; values["name"] = migration.name; } - if (this.connection.driver instanceof MongoDriver) { + if (this.connection.driver instanceof MongoDriver) { const mongoRunner = queryRunner as MongoQueryRunner; - mongoRunner.databaseConnection.db(this.connection.driver.database!).collection(this.migrationsTableName).insert(values); + mongoRunner.databaseConnection.db(this.connection.driver.database!).collection(this.migrationsTableName).insert(values); } else { const qb = queryRunner.manager.createQueryBuilder(); await qb.insert() @@ -382,7 +385,7 @@ export class MigrationExecutor { if (this.connection.driver instanceof MongoDriver) { const mongoRunner = queryRunner as MongoQueryRunner; - mongoRunner.databaseConnection.db(this.connection.driver.database!).collection(this.migrationsTableName).deleteOne(conditions); + mongoRunner.databaseConnection.db(this.connection.driver.database!).collection(this.migrationsTableName).deleteOne(conditions); } else { const qb = queryRunner.manager.createQueryBuilder(); await qb.delete() diff --git a/test/github-issues/4701/issue-4701.ts b/test/github-issues/4701/issue-4701.ts new file mode 100644 index 0000000000..069fcf1e33 --- /dev/null +++ b/test/github-issues/4701/issue-4701.ts @@ -0,0 +1,26 @@ +import "reflect-metadata"; +import { + createTestingConnections, + closeTestingConnections, + reloadTestingDatabases +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { Migration } from "../../../src/migration/Migration"; + +describe("github issues > #4701 Duplicate migrations are executed.", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + migrations: [__dirname + "/migration/*.js"], + enabledDrivers: ["postgres"], + schemaCreate: true, + dropSchema: true + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should skip duplicate pending migrations", () => Promise.all(connections.map(async connection => { + const mymigr: Migration[] = await connection.runMigrations(); + mymigr.length.should.be.equal(1); + mymigr[0].name.should.be.equal("ExampleMigration1567759789051"); + }))); +}); diff --git a/test/github-issues/4701/migration/1567759789051-ExampleMigration.ts b/test/github-issues/4701/migration/1567759789051-ExampleMigration.ts new file mode 100644 index 0000000000..bab47b747f --- /dev/null +++ b/test/github-issues/4701/migration/1567759789051-ExampleMigration.ts @@ -0,0 +1,9 @@ +import { MigrationInterface } from "../../../../src/migration/MigrationInterface"; +import { QueryRunner } from "../../../../src/query-runner/QueryRunner"; + +export class ExampleMigration1567759789051 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + } + public async down(queryRunner: QueryRunner): Promise { + } +} diff --git a/test/github-issues/4701/migration/1567759832591-ExampleMigrationTwo.ts b/test/github-issues/4701/migration/1567759832591-ExampleMigrationTwo.ts new file mode 100644 index 0000000000..bab47b747f --- /dev/null +++ b/test/github-issues/4701/migration/1567759832591-ExampleMigrationTwo.ts @@ -0,0 +1,9 @@ +import { MigrationInterface } from "../../../../src/migration/MigrationInterface"; +import { QueryRunner } from "../../../../src/query-runner/QueryRunner"; + +export class ExampleMigration1567759789051 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + } + public async down(queryRunner: QueryRunner): Promise { + } +}