Skip to content
This repository was archived by the owner on Mar 18, 2022. It is now read-only.

Commit db8074a

Browse files
HNicolaspleerock
authored andcommitted
feat: add support for ON CONFLICT for cockroach (typeorm#4518)
Closes: typeorm#4513
1 parent 19e2179 commit db8074a

File tree

3 files changed

+156
-3
lines changed

3 files changed

+156
-3
lines changed

src/query-builder/InsertQueryBuilder.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
220220
}
221221

222222
/**
223-
* Adds additional ON CONFLICT statement supported in postgres.
223+
* Adds additional ON CONFLICT statement supported in postgres and cockroach.
224224
*/
225225
onConflict(statement: string): this {
226226
this.expressionMap.onConflict = statement;
@@ -249,7 +249,7 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
249249
if (statement && statement.overwrite instanceof Array) {
250250
if (this.connection.driver instanceof MysqlDriver) {
251251
this.expressionMap.onUpdate.overwrite = statement.overwrite.map(column => `${column} = VALUES(${column})`).join(", ");
252-
} else if (this.connection.driver instanceof PostgresDriver || this.connection.driver instanceof AbstractSqliteDriver) {
252+
} else if (this.connection.driver instanceof PostgresDriver || this.connection.driver instanceof AbstractSqliteDriver || this.connection.driver instanceof CockroachDriver) {
253253
this.expressionMap.onUpdate.overwrite = statement.overwrite.map(column => `${column} = EXCLUDED.${column}`).join(", ");
254254
}
255255
}
@@ -300,7 +300,7 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
300300
query += ` DEFAULT VALUES`;
301301
}
302302
}
303-
if (this.connection.driver instanceof PostgresDriver || this.connection.driver instanceof AbstractSqliteDriver) {
303+
if (this.connection.driver instanceof PostgresDriver || this.connection.driver instanceof AbstractSqliteDriver || this.connection.driver instanceof CockroachDriver) {
304304
query += `${this.expressionMap.onIgnore ? " ON CONFLICT DO NOTHING " : ""}`;
305305
query += `${this.expressionMap.onConflict ? " ON CONFLICT " + this.expressionMap.onConflict : ""}`;
306306
if (this.expressionMap.onUpdate) {
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Entity, PrimaryColumn, Column } from "../../../../src";
2+
3+
@Entity()
4+
export class User {
5+
@PrimaryColumn()
6+
name: string;
7+
8+
@PrimaryColumn()
9+
email: string;
10+
11+
@Column()
12+
age: number;
13+
}

test/github-issues/4513/issue-4513.ts

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import "reflect-metadata";
2+
import { createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils";
3+
import { Connection } from "../../../src/connection/Connection";
4+
import { User } from "./entity/User";
5+
6+
describe("github issues > #4513 CockroachDB support for onConflict", () => {
7+
8+
let connections: Connection[];
9+
before(async () => connections = await createTestingConnections({
10+
entities: [__dirname + "/entity/*{.js,.ts}"],
11+
schemaCreate: true,
12+
dropSchema: true,
13+
enabledDrivers: ["cockroachdb"]
14+
}));
15+
beforeEach(() => reloadTestingDatabases(connections));
16+
after(() => closeTestingConnections(connections));
17+
18+
it("should insert if no conflict", () => Promise.all(connections.map(async connection => {
19+
const user1 = new User();
20+
user1.name = "example";
21+
user1.email = "example@example.com";
22+
user1.age = 30;
23+
24+
await connection.createQueryBuilder()
25+
.insert()
26+
.into(User)
27+
.values(user1)
28+
.execute();
29+
30+
const user2 = new User();
31+
user2.name = "example2";
32+
user2.email = "example2@example.com";
33+
user2.age = 42;
34+
35+
await connection.createQueryBuilder()
36+
.insert()
37+
.into(User)
38+
.values(user2)
39+
.onConflict(`("name", "email") DO NOTHING`)
40+
.execute();
41+
42+
await connection.manager.find(User).should.eventually.have.lengthOf(2);
43+
})));
44+
45+
it("should update on conflict with do update", () => Promise.all(connections.map(async connection => {
46+
const user1 = new User();
47+
user1.name = "example";
48+
user1.email = "example@example.com";
49+
user1.age = 30;
50+
51+
await connection.createQueryBuilder()
52+
.insert()
53+
.into(User)
54+
.values(user1)
55+
.execute();
56+
57+
const user2 = new User();
58+
user2.name = "example";
59+
user2.email = "example@example.com";
60+
user2.age = 42;
61+
62+
await connection.createQueryBuilder()
63+
.insert()
64+
.into(User)
65+
.values(user2)
66+
.onConflict(`("name", "email") DO UPDATE SET age = EXCLUDED.age`)
67+
.execute();
68+
69+
await connection.manager.findOne(User, { name: "example", email: "example@example.com" }).should.eventually.be.eql({
70+
name: "example",
71+
email: "example@example.com",
72+
age: 42,
73+
});
74+
})));
75+
76+
it("should not update on conflict with do nothing", () => Promise.all(connections.map(async connection => {
77+
const user1 = new User();
78+
user1.name = "example";
79+
user1.email = "example@example.com";
80+
user1.age = 30;
81+
82+
await connection.createQueryBuilder()
83+
.insert()
84+
.into(User)
85+
.values(user1)
86+
.execute();
87+
88+
const user2 = new User();
89+
user2.name = "example";
90+
user2.email = "example@example.com";
91+
user2.age = 42;
92+
93+
await connection.createQueryBuilder()
94+
.insert()
95+
.into(User)
96+
.values(user2)
97+
.onConflict(`("name", "email") DO NOTHING`)
98+
.execute();
99+
100+
await connection.manager.findOne(User, { name: "example", email: "example@example.com" }).should.eventually.be.eql({
101+
name: "example",
102+
email: "example@example.com",
103+
age: 30,
104+
});
105+
})));
106+
107+
it("should update with orUpdate", () => Promise.all(connections.map(async connection => {
108+
const user1 = new User();
109+
user1.name = "example";
110+
user1.email = "example@example.com";
111+
user1.age = 30;
112+
113+
await connection.createQueryBuilder()
114+
.insert()
115+
.into(User)
116+
.values(user1)
117+
.execute();
118+
119+
const user2 = new User();
120+
user2.name = "example";
121+
user2.email = "example@example.com";
122+
user2.age = 42;
123+
124+
await connection.createQueryBuilder()
125+
.insert()
126+
.into(User)
127+
.values(user2)
128+
.orUpdate({
129+
conflict_target: ["name", "email"],
130+
overwrite: ["age"],
131+
})
132+
.execute();
133+
134+
await connection.manager.findOne(User, { name: "example", email: "example@example.com" }).should.eventually.be.eql({
135+
name: "example",
136+
email: "example@example.com",
137+
age: 42,
138+
});
139+
})));
140+
});

0 commit comments

Comments
 (0)