Skip to content

Commit 938f94b

Browse files
authored
fix: add onDelete option validation for oracle (#9786)
* fix: add onDelete option validation for oracle Closes: #9189 * refactor: move fk validation logic to EntityMetadataValidator.ts * fix: skip assertion for other databases * fix: styles --------- Co-authored-by: ke <[email protected]>
1 parent a188b1d commit 938f94b

File tree

7 files changed

+119
-4
lines changed

7 files changed

+119
-4
lines changed

src/driver/Driver.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { Table } from "../schema-builder/table/Table"
1414
import { View } from "../schema-builder/view/View"
1515
import { TableForeignKey } from "../schema-builder/table/TableForeignKey"
1616
import { UpsertType } from "./types/UpsertType"
17+
import { OnDeleteType } from "../metadata/types/OnDeleteType"
18+
import { OnUpdateType } from "../metadata/types/OnUpdateType"
1719

1820
export type ReturningType = "insert" | "update" | "delete"
1921

@@ -68,6 +70,16 @@ export interface Driver {
6870
*/
6971
supportedUpsertTypes: UpsertType[]
7072

73+
/**
74+
* Returns list of supported onDelete types by driver
75+
*/
76+
supportedOnDeleteTypes?: OnDeleteType[]
77+
78+
/**
79+
* Returns list of supported onUpdate types by driver
80+
*/
81+
supportedOnUpdateTypes?: OnUpdateType[]
82+
7183
/**
7284
* Default values of length, precision and scale depends on column data type.
7385
* Used in the cases when length/precision/scale is not specified by user.

src/driver/oracle/OracleDriver.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
2626
import { TypeORMError } from "../../error"
2727
import { InstanceChecker } from "../../util/InstanceChecker"
2828
import { UpsertType } from "../types/UpsertType"
29+
import { OnDeleteType } from "../../metadata/types/OnDeleteType"
30+
import { OnUpdateType } from "../../metadata/types/OnUpdateType"
2931

3032
/**
3133
* Organizes communication with Oracle RDBMS.
@@ -133,6 +135,23 @@ export class OracleDriver implements Driver {
133135
*/
134136
supportedUpsertTypes: UpsertType[] = []
135137

138+
/**
139+
* Returns list of supported onDelete types by driver.
140+
* https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/sql-language-reference.pdf
141+
* Oracle does not support NO ACTION, but NO ACTION is set by default in EntityMetadata
142+
*/
143+
supportedOnDeleteTypes: OnDeleteType[] = [
144+
"CASCADE",
145+
"SET NULL",
146+
"NO ACTION",
147+
]
148+
149+
/**
150+
* Returns list of supported onUpdate types by driver.
151+
* Oracle does not have onUpdate option, but we allow NO ACTION since it is set by default in EntityMetadata
152+
*/
153+
supportedOnUpdateTypes: OnUpdateType[] = ["NO ACTION"]
154+
136155
/**
137156
* Gets list of spatial column data types.
138157
*/

src/driver/oracle/OracleQueryRunner.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,10 +2785,10 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {
27852785
}" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(
27862786
this.getTablePath(fk),
27872787
)} (${referencedColumnNames})`
2788-
if (fk.onDelete && fk.onDelete !== "NO ACTION")
2788+
if (fk.onDelete && fk.onDelete !== "NO ACTION") {
27892789
// Oracle does not support NO ACTION, but we set NO ACTION by default in EntityMetadata
27902790
constraint += ` ON DELETE ${fk.onDelete}`
2791-
2791+
}
27922792
return constraint
27932793
})
27942794
.join(", ")
@@ -3038,9 +3038,9 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {
30383038
this.getTablePath(foreignKey),
30393039
)} (${referencedColumnNames})`
30403040
// Oracle does not support NO ACTION, but we set NO ACTION by default in EntityMetadata
3041-
if (foreignKey.onDelete && foreignKey.onDelete !== "NO ACTION")
3041+
if (foreignKey.onDelete && foreignKey.onDelete !== "NO ACTION") {
30423042
sql += ` ON DELETE ${foreignKey.onDelete}`
3043-
3043+
}
30443044
return new Query(sql)
30453045
}
30463046

src/metadata-builder/EntityMetadataValidator.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,28 @@ export class EntityMetadataValidator {
226226

227227
// validate relations
228228
entityMetadata.relations.forEach((relation) => {
229+
// check OnDeleteTypes
230+
if (
231+
driver.supportedOnDeleteTypes &&
232+
relation.onDelete &&
233+
!driver.supportedOnDeleteTypes.includes(relation.onDelete)
234+
) {
235+
throw new TypeORMError(
236+
`OnDeleteType "${relation.onDelete}" is not supported for ${driver.options.type}!`,
237+
)
238+
}
239+
240+
// check OnUpdateTypes
241+
if (
242+
driver.supportedOnUpdateTypes &&
243+
relation.onUpdate &&
244+
!driver.supportedOnUpdateTypes.includes(relation.onUpdate)
245+
) {
246+
throw new TypeORMError(
247+
`OnUpdateType "${relation.onUpdate}" is not valid for ${driver.options.type}!`,
248+
)
249+
}
250+
229251
// check join tables:
230252
// using JoinTable is possible only on one side of the many-to-many relation
231253
// todo(dima): fix
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Entity, ManyToOne } from "../../../../src"
2+
import { PrimaryGeneratedColumn } from "../../../../src"
3+
import type { UserEntity } from "./UserEntity"
4+
5+
@Entity()
6+
export class GroupEntity {
7+
@PrimaryGeneratedColumn()
8+
id: number
9+
10+
@ManyToOne("UserEntity", "group", { onDelete: "RESTRICT" })
11+
user: UserEntity
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Entity, OneToMany } from "../../../../src"
2+
import { PrimaryGeneratedColumn } from "../../../../src"
3+
import { GroupEntity } from "./GroupEntity"
4+
5+
@Entity()
6+
export class UserEntity {
7+
@PrimaryGeneratedColumn()
8+
id: number
9+
10+
@OneToMany("GroupEntity", "user")
11+
group: GroupEntity
12+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import "reflect-metadata"
2+
import {
3+
closeTestingConnections,
4+
createTestingConnections,
5+
} from "../../utils/test-utils"
6+
import { DataSource, TypeORMError } from "../../../src"
7+
import { expect } from "chai"
8+
9+
describe("github issues > #9189 check invalid constraint options", () => {
10+
let dataSources: DataSource[] = []
11+
12+
after(() => closeTestingConnections(dataSources))
13+
14+
it("should throw an exception, when invalid option is configured", async () => {
15+
let err
16+
try {
17+
await Promise.all(
18+
(dataSources = await createTestingConnections({
19+
entities: [__dirname + "/entity/*{.js,.ts}"],
20+
schemaCreate: false,
21+
dropSchema: true,
22+
enabledDrivers: ["oracle"],
23+
})),
24+
)
25+
} catch (e) {
26+
err = e
27+
}
28+
if (err)
29+
// skip for other databases
30+
expect(err).to.eql(
31+
new TypeORMError(
32+
'OnDeleteType "RESTRICT" is not supported for oracle!',
33+
),
34+
)
35+
})
36+
37+
// you can add additional tests if needed
38+
})

0 commit comments

Comments
 (0)