This commit is contained in:
Lucian Mocanu 2025-02-07 00:03:20 +01:00
parent 50a660aecc
commit 759cf7cf54
8 changed files with 156 additions and 30 deletions

View File

@ -1,15 +1,17 @@
import "reflect-metadata"
import { expect } from "chai"
import { DataSource } from "../../../src/data-source/DataSource"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { DataSource } from "../../../src/data-source/DataSource"
import { Company } from "./entity/Company"
import { Office } from "./entity/Office"
import { expect } from "chai"
describe("deferrable uq constraints should be check at the end of transaction", () => {
describe("deferrable unique constraint", () => {
let connections: DataSource[]
before(
async () =>
@ -21,7 +23,7 @@ describe("deferrable uq constraints should be check at the end of transaction",
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
it("use initially deferred deferrable uq constraints", () =>
it("initially deferred unique should be validated at the end of transaction", () =>
Promise.all(
connections.map(async (connection) => {
await connection.manager.transaction(async (entityManager) => {
@ -63,7 +65,7 @@ describe("deferrable uq constraints should be check at the end of transaction",
}),
))
it("use initially immediated deferrable uq constraints", () =>
it("initially immediate unique should be validated at the end at transaction with deferred check time", () =>
Promise.all(
connections.map(async (connection) => {
await connection.manager.transaction(async (entityManager) => {

View File

@ -1,28 +1,30 @@
import "reflect-metadata"
import { expect } from "chai"
import { DataSource } from "../../../src/data-source/DataSource"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { DataSource } from "../../../src/data-source/DataSource"
import { Company } from "./entity/Company"
import { Office } from "./entity/Office"
import { User } from "./entity/User"
import { expect } from "chai"
describe("deferrable fk constraints should be check at the end of transaction (#2191)", () => {
describe("deferrable foreign key constraint", () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
enabledDrivers: ["postgres"],
enabledDrivers: ["better-sqlite3", "postgres", "sap", "sqlite"],
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
it("use initially deferred deferrable fk constraints", () =>
it("initially deferred fk should be validated at the end of transaction", () =>
Promise.all(
connections.map(async (connection) => {
await connection.manager.transaction(async (entityManager) => {
@ -40,7 +42,7 @@ describe("deferrable fk constraints should be check at the end of transaction (#
company.name = "Acme"
await entityManager.save(company)
})
}).should.not.be.rejected
// now check
const user = await connection.manager.findOne(User, {
@ -48,9 +50,7 @@ describe("deferrable fk constraints should be check at the end of transaction (#
where: { id: 1 },
})
expect(user).not.to.be.null
user!.should.be.eql({
expect(user).to.deep.equal({
id: 1,
name: "Bob",
company: {
@ -61,9 +61,12 @@ describe("deferrable fk constraints should be check at the end of transaction (#
}),
))
it("use initially immediated deferrable fk constraints", () =>
it("initially immediate fk should be validated at the end at transaction with deferred check time", () =>
Promise.all(
connections.map(async (connection) => {
// changing the constraint check time is only supported on postgres
if (connection.driver.options.type !== "postgres") return
await connection.manager.transaction(async (entityManager) => {
// first set constraints deferred manually
await entityManager.query("SET CONSTRAINTS ALL DEFERRED")
@ -82,7 +85,7 @@ describe("deferrable fk constraints should be check at the end of transaction (#
company.name = "Emca"
await entityManager.save(company)
})
}).should.not.be.rejected
// now check
const office = await connection.manager.findOne(Office, {
@ -90,9 +93,7 @@ describe("deferrable fk constraints should be check at the end of transaction (#
where: { id: 2 },
})
expect(office).not.to.be.null
office!.should.be.eql({
expect(office).to.deep.equal({
id: 2,
name: "Barcelona",
company: {

View File

@ -1,6 +1,6 @@
import { Entity } from "../../../../src/decorator/entity/Entity"
import { Column } from "../../../../src/decorator/columns/Column"
import { PrimaryColumn } from "../../../../src/decorator/columns/PrimaryColumn"
import { Entity } from "../../../../src/decorator/entity/Entity"
import { Unique } from "../../../../src/decorator/Unique"
@Entity()

View File

@ -1,8 +1,9 @@
import { Entity } from "../../../../src/decorator/entity/Entity"
import { Column } from "../../../../src/decorator/columns/Column"
import { ManyToOne } from "../../../../src/decorator/relations/ManyToOne"
import { PrimaryColumn } from "../../../../src/decorator/columns/PrimaryColumn"
import { Entity } from "../../../../src/decorator/entity/Entity"
import { ManyToOne } from "../../../../src/decorator/relations/ManyToOne"
import { Unique } from "../../../../src/decorator/Unique"
import { Company } from "./Company"
@Entity()
@ -14,8 +15,6 @@ export class Office {
@Column()
name: string
@ManyToOne((type) => Company, (company) => company.id, {
deferrable: "INITIALLY IMMEDIATE",
})
@ManyToOne(() => Company, { deferrable: "INITIALLY IMMEDIATE" })
company: Company
}

View File

@ -1,7 +1,8 @@
import { Entity } from "../../../../src/decorator/entity/Entity"
import { Column } from "../../../../src/decorator/columns/Column"
import { ManyToOne } from "../../../../src/decorator/relations/ManyToOne"
import { PrimaryColumn } from "../../../../src/decorator/columns/PrimaryColumn"
import { Entity } from "../../../../src/decorator/entity/Entity"
import { ManyToOne } from "../../../../src/decorator/relations/ManyToOne"
import { Company } from "./Company"
@Entity()
@ -12,8 +13,6 @@ export class User {
@Column()
name: string
@ManyToOne((type) => Company, (company) => company.id, {
deferrable: "INITIALLY DEFERRED",
})
@ManyToOne(() => Company, { deferrable: "INITIALLY DEFERRED" })
company: Company
}

View File

@ -0,0 +1,14 @@
import { Column } from "../../../../src/decorator/columns/Column"
import { PrimaryColumn } from "../../../../src/decorator/columns/PrimaryColumn"
import { Entity } from "../../../../src/decorator/entity/Entity"
import { Unique } from "../../../../src/decorator/Unique"
@Entity()
@Unique(["name"], { deferrable: "INITIALLY DEFERRED" })
export class Company {
@PrimaryColumn()
id: number
@Column()
name?: string
}

View File

@ -0,0 +1,18 @@
import { Column } from "../../../../src/decorator/columns/Column"
import { PrimaryColumn } from "../../../../src/decorator/columns/PrimaryColumn"
import { Entity } from "../../../../src/decorator/entity/Entity"
import { ManyToOne } from "../../../../src/decorator/relations/ManyToOne"
import { Company } from "./Company"
@Entity()
export class User {
@PrimaryColumn()
id: number
@Column()
name: string
@ManyToOne(() => Company, { deferrable: "INITIALLY DEFERRED" })
company: Company
}

View File

@ -0,0 +1,93 @@
import "reflect-metadata"
import { expect } from "chai"
import { DataSource } from "../../../src/data-source/DataSource"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { EntityManager } from "../../../src"
import { BaseQueryRunner } from "../../../src/query-runner/BaseQueryRunner"
import { Company } from "./entity/Company"
import { User } from "./entity/User"
describe("github issues > #10626 Regression in transactionDepth handling", () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
enabledDrivers: ["better-sqlite3", "postgres", "sqlite"],
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
it("transactionDepth should be updated correctly when commit fails", () =>
Promise.all(
connections.map(async (connection) => {
const queryRunner = connection.createQueryRunner()
const transactionDepths: Record<string, number> = {}
const recordDepth = (mark: string) => {
transactionDepths[mark] = (
queryRunner as unknown as BaseQueryRunner
)["transactionDepth"]
}
recordDepth("initial")
await queryRunner.startTransaction()
recordDepth("startTransaction")
const runInTransaction = async (
entityManager: EntityManager,
) => {
// first save user
const user = new User()
user.id = 1
user.company = { id: 100 }
user.name = "Bob"
await entityManager.save(user)
// then save company
const company = new Company()
company.id = 200
company.name = "Acme"
await entityManager.save(company)
}
await runInTransaction(queryRunner.manager).should.not.rejected
recordDepth("afterStatements")
await queryRunner.commitTransaction().should.be.rejected
recordDepth("afterCommit")
await queryRunner.rollbackTransaction().should.not.be.rejected
recordDepth("afterRollback")
await queryRunner.release()
recordDepth("afterRelease")
expect(transactionDepths).to.deep.equal({
initial: 0,
startTransaction: 1,
afterStatements: 1,
afterCommit: 1,
afterRollback: 0,
afterRelease: 0,
})
// check data
const user = await connection.manager.findOneBy(User, { id: 1 })
expect(user).to.equal(null)
const company = await connection.manager.findOneBy(Company, {
id: 200,
})
expect(company).to.equal(null)
}),
))
})