diff --git a/docker-compose.yml b/docker-compose.yml index bbd2fa9aac..bbcd235ea6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,6 @@ version: "3" services: # mysql mysql: - platform: linux/amd64 image: "mysql:5.7.37" container_name: "typeorm-mysql" ports: diff --git a/src/driver/aurora-mysql/AuroraMysqlQueryRunner.ts b/src/driver/aurora-mysql/AuroraMysqlQueryRunner.ts index 0d9ef69084..b7b7acbff3 100644 --- a/src/driver/aurora-mysql/AuroraMysqlQueryRunner.ts +++ b/src/driver/aurora-mysql/AuroraMysqlQueryRunner.ts @@ -99,12 +99,11 @@ export class AuroraMysqlQueryRunner } if (this.transactionDepth === 0) { - this.transactionDepth += 1 await this.client.startTransaction() } else { - this.transactionDepth += 1 - await this.query(`SAVEPOINT typeorm_${this.transactionDepth - 1}`) + await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`) } + this.transactionDepth += 1 await this.broadcaster.broadcast("AfterTransactionStart") } @@ -119,15 +118,14 @@ export class AuroraMysqlQueryRunner await this.broadcaster.broadcast("BeforeTransactionCommit") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `RELEASE SAVEPOINT typeorm_${this.transactionDepth}`, + `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.client.commitTransaction() this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionCommit") } @@ -142,15 +140,14 @@ export class AuroraMysqlQueryRunner await this.broadcaster.broadcast("BeforeTransactionRollback") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth}`, + `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.client.rollbackTransaction() this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionRollback") } diff --git a/src/driver/aurora-postgres/AuroraPostgresQueryRunner.ts b/src/driver/aurora-postgres/AuroraPostgresQueryRunner.ts index 8269738939..d114804b6b 100644 --- a/src/driver/aurora-postgres/AuroraPostgresQueryRunner.ts +++ b/src/driver/aurora-postgres/AuroraPostgresQueryRunner.ts @@ -110,12 +110,11 @@ export class AuroraPostgresQueryRunner } if (this.transactionDepth === 0) { - this.transactionDepth += 1 await this.client.startTransaction() } else { - this.transactionDepth += 1 - await this.query(`SAVEPOINT typeorm_${this.transactionDepth} - 1`) + await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`) } + this.transactionDepth += 1 await this.broadcaster.broadcast("AfterTransactionStart") } @@ -130,15 +129,14 @@ export class AuroraPostgresQueryRunner await this.broadcaster.broadcast("BeforeTransactionCommit") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `RELEASE SAVEPOINT typeorm_${this.transactionDepth}`, + `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.client.commitTransaction() this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionCommit") } @@ -153,15 +151,14 @@ export class AuroraPostgresQueryRunner await this.broadcaster.broadcast("BeforeTransactionRollback") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth}`, + `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.client.rollbackTransaction() this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionRollback") } diff --git a/src/driver/cockroachdb/CockroachQueryRunner.ts b/src/driver/cockroachdb/CockroachQueryRunner.ts index 45e3b250a9..7b912c0a80 100644 --- a/src/driver/cockroachdb/CockroachQueryRunner.ts +++ b/src/driver/cockroachdb/CockroachQueryRunner.ts @@ -190,7 +190,6 @@ export class CockroachQueryRunner } if (this.transactionDepth === 0) { - this.transactionDepth += 1 await this.query("START TRANSACTION") await this.query("SAVEPOINT cockroach_restart") if (isolationLevel) { @@ -199,10 +198,10 @@ export class CockroachQueryRunner ) } } else { - this.transactionDepth += 1 - await this.query(`SAVEPOINT typeorm_${this.transactionDepth - 1}`) + await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`) } + this.transactionDepth += 1 this.storeQueries = true await this.broadcaster.broadcast("AfterTransactionStart") @@ -218,20 +217,18 @@ export class CockroachQueryRunner await this.broadcaster.broadcast("BeforeTransactionCommit") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `RELEASE SAVEPOINT typeorm_${this.transactionDepth}`, + `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) + this.transactionDepth -= 1 } else { this.storeQueries = false - this.transactionDepth -= 1 - // This was disabled because it failed tests after update to CRDB 24.2 - // https://github.com/typeorm/typeorm/pull/11190 - // await this.query("RELEASE SAVEPOINT cockroach_restart") + await this.query("RELEASE SAVEPOINT cockroach_restart") await this.query("COMMIT") this.queries = [] this.isTransactionActive = false this.transactionRetries = 0 + this.transactionDepth -= 1 } await this.broadcaster.broadcast("AfterTransactionCommit") @@ -247,18 +244,17 @@ export class CockroachQueryRunner await this.broadcaster.broadcast("BeforeTransactionRollback") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth}`, + `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { this.storeQueries = false - this.transactionDepth -= 1 await this.query("ROLLBACK") this.queries = [] this.isTransactionActive = false this.transactionRetries = 0 } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionRollback") } diff --git a/src/driver/mysql/MysqlQueryRunner.ts b/src/driver/mysql/MysqlQueryRunner.ts index cd70ec9b33..7fee112a80 100644 --- a/src/driver/mysql/MysqlQueryRunner.ts +++ b/src/driver/mysql/MysqlQueryRunner.ts @@ -119,7 +119,6 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { throw err } if (this.transactionDepth === 0) { - this.transactionDepth += 1 if (isolationLevel) { await this.query( "SET TRANSACTION ISOLATION LEVEL " + isolationLevel, @@ -127,9 +126,9 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { } await this.query("START TRANSACTION") } else { - this.transactionDepth += 1 - await this.query(`SAVEPOINT typeorm_${this.transactionDepth - 1}`) + await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`) } + this.transactionDepth += 1 await this.broadcaster.broadcast("AfterTransactionStart") } @@ -144,15 +143,14 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { await this.broadcaster.broadcast("BeforeTransactionCommit") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `RELEASE SAVEPOINT typeorm_${this.transactionDepth}`, + `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.query("COMMIT") this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionCommit") } @@ -167,15 +165,14 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { await this.broadcaster.broadcast("BeforeTransactionRollback") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth}`, + `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.query("ROLLBACK") this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionRollback") } diff --git a/src/driver/oracle/OracleQueryRunner.ts b/src/driver/oracle/OracleQueryRunner.ts index 6693a9c443..5dd67a9638 100644 --- a/src/driver/oracle/OracleQueryRunner.ts +++ b/src/driver/oracle/OracleQueryRunner.ts @@ -136,14 +136,13 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { } if (this.transactionDepth === 0) { - this.transactionDepth += 1 await this.query( "SET TRANSACTION ISOLATION LEVEL " + isolationLevel, ) } else { - this.transactionDepth += 1 - await this.query(`SAVEPOINT typeorm_${this.transactionDepth - 1}`) + await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`) } + this.transactionDepth += 1 await this.broadcaster.broadcast("AfterTransactionStart") } @@ -176,15 +175,14 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { await this.broadcaster.broadcast("BeforeTransactionRollback") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth}`, + `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.query("ROLLBACK") this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionRollback") } diff --git a/src/driver/postgres/PostgresQueryRunner.ts b/src/driver/postgres/PostgresQueryRunner.ts index 481cf4943c..fcfd6adcf6 100644 --- a/src/driver/postgres/PostgresQueryRunner.ts +++ b/src/driver/postgres/PostgresQueryRunner.ts @@ -174,7 +174,6 @@ export class PostgresQueryRunner } if (this.transactionDepth === 0) { - this.transactionDepth += 1 await this.query("START TRANSACTION") if (isolationLevel) { await this.query( @@ -182,9 +181,9 @@ export class PostgresQueryRunner ) } } else { - this.transactionDepth += 1 - await this.query(`SAVEPOINT typeorm_${this.transactionDepth - 1}`) + await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`) } + this.transactionDepth += 1 await this.broadcaster.broadcast("AfterTransactionStart") } @@ -199,15 +198,14 @@ export class PostgresQueryRunner await this.broadcaster.broadcast("BeforeTransactionCommit") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `RELEASE SAVEPOINT typeorm_${this.transactionDepth}`, + `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.query("COMMIT") this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionCommit") } @@ -222,15 +220,14 @@ export class PostgresQueryRunner await this.broadcaster.broadcast("BeforeTransactionRollback") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth}`, + `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.query("ROLLBACK") this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionRollback") } diff --git a/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts b/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts index 4e3d1f6cc3..b3b8fedecf 100644 --- a/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts +++ b/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts @@ -101,7 +101,6 @@ export abstract class AbstractSqliteQueryRunner } if (this.transactionDepth === 0) { - this.transactionDepth += 1 if (isolationLevel) { if (isolationLevel === "READ UNCOMMITTED") { await this.query("PRAGMA read_uncommitted = true") @@ -111,9 +110,9 @@ export abstract class AbstractSqliteQueryRunner } await this.query("BEGIN TRANSACTION") } else { - this.transactionDepth += 1 - await this.query(`SAVEPOINT typeorm_${this.transactionDepth - 1}`) + await this.query(`SAVEPOINT typeorm_${this.transactionDepth}`) } + this.transactionDepth += 1 await this.broadcaster.broadcast("AfterTransactionStart") } @@ -128,15 +127,14 @@ export abstract class AbstractSqliteQueryRunner await this.broadcaster.broadcast("BeforeTransactionCommit") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `RELEASE SAVEPOINT typeorm_${this.transactionDepth}`, + `RELEASE SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.query("COMMIT") this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionCommit") } @@ -151,15 +149,14 @@ export abstract class AbstractSqliteQueryRunner await this.broadcaster.broadcast("BeforeTransactionRollback") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth}`, + `ROLLBACK TO SAVEPOINT typeorm_${this.transactionDepth - 1}`, ) } else { - this.transactionDepth -= 1 await this.query("ROLLBACK") this.isTransactionActive = false } + this.transactionDepth -= 1 await this.broadcaster.broadcast("AfterTransactionRollback") } diff --git a/src/driver/sqlserver/SqlServerQueryRunner.ts b/src/driver/sqlserver/SqlServerQueryRunner.ts index 118a863bb7..cf8c27d10d 100644 --- a/src/driver/sqlserver/SqlServerQueryRunner.ts +++ b/src/driver/sqlserver/SqlServerQueryRunner.ts @@ -107,7 +107,6 @@ export class SqlServerQueryRunner } if (this.transactionDepth === 0) { - this.transactionDepth += 1 const pool = await (this.mode === "slave" ? this.driver.obtainSlaveConnection() : this.driver.obtainMasterConnection()) @@ -125,12 +124,12 @@ export class SqlServerQueryRunner this.databaseConnection.begin(transactionCallback) } } else { - this.transactionDepth += 1 await this.query( - `SAVE TRANSACTION typeorm_${this.transactionDepth - 1}`, + `SAVE TRANSACTION typeorm_${this.transactionDepth}`, ) ok() } + this.transactionDepth += 1 }) await this.broadcaster.broadcast("AfterTransactionStart") @@ -149,7 +148,6 @@ export class SqlServerQueryRunner if (this.transactionDepth === 1) { return new Promise((ok, fail) => { - this.transactionDepth -= 1 this.databaseConnection.commit(async (err: any) => { if (err) return fail(err) this.isTransactionActive = false @@ -159,6 +157,7 @@ export class SqlServerQueryRunner ok() this.connection.logger.logQuery("COMMIT") + this.transactionDepth -= 1 }) }) } @@ -177,13 +176,12 @@ export class SqlServerQueryRunner await this.broadcaster.broadcast("BeforeTransactionRollback") if (this.transactionDepth > 1) { - this.transactionDepth -= 1 await this.query( - `ROLLBACK TRANSACTION typeorm_${this.transactionDepth}`, + `ROLLBACK TRANSACTION typeorm_${this.transactionDepth - 1}`, ) + this.transactionDepth -= 1 } else { return new Promise((ok, fail) => { - this.transactionDepth -= 1 this.databaseConnection.rollback(async (err: any) => { if (err) return fail(err) this.isTransactionActive = false @@ -193,6 +191,7 @@ export class SqlServerQueryRunner ok() this.connection.logger.logQuery("ROLLBACK") + this.transactionDepth -= 1 }) }) } diff --git a/test/github-issues/10209/entity/asset.ts b/test/github-issues/10209/entity/asset.ts deleted file mode 100644 index 1b37ad2789..0000000000 --- a/test/github-issues/10209/entity/asset.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { - Column, - CreateDateColumn, - DeleteDateColumn, - Entity, - JoinColumn, - ManyToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from "../../../../src" -import { ConfigurationEntity } from "./configuration" - -export enum AssetStatus { - new = 0, - deleted = -999, -} - -@Entity("assets") -export class AssetEntity { - @PrimaryGeneratedColumn("uuid") - id!: string - - @Column({ length: 255 }) - name!: string - - @Column({ type: "uuid" }) - configuration_id!: string - - @Column() - status!: AssetStatus - - @CreateDateColumn() - created_at!: Date - - @UpdateDateColumn() - updated_at!: Date - - @DeleteDateColumn() - deleted_at!: Date | null - - @ManyToOne(() => ConfigurationEntity, { nullable: false }) - @JoinColumn({ name: "configuration_id" }) - configuration!: ConfigurationEntity -} diff --git a/test/github-issues/10209/entity/configuration.ts b/test/github-issues/10209/entity/configuration.ts deleted file mode 100644 index e56f96c7df..0000000000 --- a/test/github-issues/10209/entity/configuration.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from "../../../../src" -import { AssetEntity } from "./asset" -import { LocationEntity } from "./location" - -export enum ConfigurationStatus { - deleted = -999, - new = 0, -} - -@Entity("configurations") -export class ConfigurationEntity { - @PrimaryGeneratedColumn("uuid") - id!: string - - @Column({ length: 255 }) - name!: string - - @Column() - status!: ConfigurationStatus - - @Column({ type: "uuid", nullable: false }) - location_id!: string - - @ManyToOne(() => LocationEntity, { nullable: false }) - @JoinColumn({ name: "location_id" }) - location!: LocationEntity - - @Column({ default: true }) - active!: boolean - - @OneToMany(() => AssetEntity, (asset) => asset.configuration, { - cascade: true, - }) - assets!: AssetEntity[] - - @CreateDateColumn() - created_at!: Date - - @UpdateDateColumn() - updated_at!: Date -} diff --git a/test/github-issues/10209/entity/location.ts b/test/github-issues/10209/entity/location.ts deleted file mode 100644 index b6f6f60f84..0000000000 --- a/test/github-issues/10209/entity/location.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - OneToMany, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from "../../../../src" -import { ConfigurationEntity } from "./configuration" - -@Entity("locations") -export class LocationEntity { - @PrimaryGeneratedColumn("uuid") - id!: string - - @Column({ length: 255 }) - name!: string - - @Column({ default: true }) - active!: boolean - - @CreateDateColumn() - created_at!: Date - - @UpdateDateColumn() - updated_at!: Date - - @OneToMany( - () => ConfigurationEntity, - (configuration) => configuration.location, - { cascade: true }, - ) - configurations!: ConfigurationEntity[] -} diff --git a/test/github-issues/10209/issue-10209.ts b/test/github-issues/10209/issue-10209.ts deleted file mode 100644 index fb30c38d9b..0000000000 --- a/test/github-issues/10209/issue-10209.ts +++ /dev/null @@ -1,90 +0,0 @@ -import "reflect-metadata" -import { - createTestingConnections, - closeTestingConnections, - reloadTestingDatabases, -} from "../../utils/test-utils" -import { DataSource } from "../../../src/data-source/DataSource" -import { expect } from "chai" -import { LocationEntity } from "./entity/location" -import { - ConfigurationEntity, - ConfigurationStatus, -} from "./entity/configuration" -import { AssetEntity, AssetStatus } from "./entity/asset" - -describe("github issues > #10209", () => { - let dataSources: DataSource[] - before( - async () => - (dataSources = await createTestingConnections({ - entities: [__dirname + "/entity/*{.js,.ts}"], - schemaCreate: true, - dropSchema: true, - })), - ) - beforeEach(() => reloadTestingDatabases(dataSources)) - after(() => closeTestingConnections(dataSources)) - - it("should not fail to run multiple nested transactions in parallel", function () { - this.retries(3) // Fix for SQLite - return Promise.all( - dataSources.map(async (dataSource) => { - const manager = dataSource.createEntityManager() - - await manager.transaction(async (txManager) => { - const location = txManager.create(LocationEntity) - location.name = "location-0" - location.configurations = [] - for (let c = 0; c < 3; c++) { - const config = txManager.create(ConfigurationEntity) - config.name = `config-${c}` - config.status = ConfigurationStatus.new - config.assets = [] - for (let a = 0; a < 5; a++) { - const asset = txManager.create(AssetEntity) - asset.name = `asset-${c}-${a}` - asset.status = AssetStatus.new - config.assets.push(asset) - } - location.configurations.push(config) - } - - await txManager.save(location) - }) - - const location = - (await manager.findOne(LocationEntity, { - where: { - name: "location-0", - }, - relations: ["configurations", "configurations.assets"], - })) || ({} as LocationEntity) - - await manager.transaction(async (txManager) => { - return Promise.all( - location.configurations.map(async (config) => { - await txManager.transaction(async (txManager2) => { - await Promise.all( - config.assets.map(async (asset) => { - asset.status = AssetStatus.deleted - await txManager2.save(asset) - await txManager2.softDelete( - AssetEntity, - asset, - ) - }), - ) - }) - - config.status = ConfigurationStatus.deleted - return await txManager.save(config) - }), - ) - }) - // We only care that the transaction above didn't fail - expect(true).to.be.true - }), - ) - }) -})