Skip to content

Commit 360122f

Browse files
ohjeyongJeyongOh
andauthored
feat: add FOR NO KEY UPDATE lock mode for postgresql (#5971)
* [Add] FOR NO KEY UPDATE lock mode for postgresql * [Add] for no key update lock test * [Fix] lint * [Fix] test Co-authored-by: JeyongOh <jeyong.oh@gogo-ssing.com>
1 parent 37fd946 commit 360122f

File tree

3 files changed

+71
-4
lines changed

3 files changed

+71
-4
lines changed

src/query-builder/QueryExpressionMap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ export class QueryExpressionMap {
150150
/**
151151
* Locking mode.
152152
*/
153-
lockMode?: "optimistic"|"pessimistic_read"|"pessimistic_write"|"dirty_read";
153+
lockMode?: "optimistic"|"pessimistic_read"|"pessimistic_write"|"dirty_read"|"for_no_key_update";
154154

155155
/**
156156
* Current version of the entity, used for locking.

src/query-builder/SelectQueryBuilder.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -967,12 +967,12 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
967967
/**
968968
* Sets locking mode.
969969
*/
970-
setLock(lockMode: "pessimistic_read"|"pessimistic_write"|"dirty_read"): this;
970+
setLock(lockMode: "pessimistic_read"|"pessimistic_write"|"dirty_read"|"for_no_key_update"): this;
971971

972972
/**
973973
* Sets locking mode.
974974
*/
975-
setLock(lockMode: "optimistic"|"pessimistic_read"|"pessimistic_write"|"dirty_read", lockVersion?: number|Date): this {
975+
setLock(lockMode: "optimistic"|"pessimistic_read"|"pessimistic_write"|"dirty_read"|"for_no_key_update", lockVersion?: number|Date): this {
976976
this.expressionMap.lockMode = lockMode;
977977
this.expressionMap.lockVersion = lockVersion;
978978
return this;
@@ -1672,6 +1672,12 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
16721672
} else {
16731673
throw new LockNotSupportedOnGivenDriverError();
16741674
}
1675+
case "for_no_key_update":
1676+
if (driver instanceof PostgresDriver) {
1677+
return " FOR NO KEY UPDATE";
1678+
} else {
1679+
throw new LockNotSupportedOnGivenDriverError();
1680+
}
16751681
default:
16761682
return "";
16771683
}
@@ -1806,7 +1812,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
18061812
if (!this.expressionMap.mainAlias)
18071813
throw new Error(`Alias is not set. Use "from" method to set an alias.`);
18081814

1809-
if ((this.expressionMap.lockMode === "pessimistic_read" || this.expressionMap.lockMode === "pessimistic_write") && !queryRunner.isTransactionActive)
1815+
if ((this.expressionMap.lockMode === "pessimistic_read" || this.expressionMap.lockMode === "pessimistic_write" || this.expressionMap.lockMode === "for_no_key_update") && !queryRunner.isTransactionActive)
18101816
throw new PessimisticLockTransactionRequiredError();
18111817

18121818
if (this.expressionMap.lockMode === "optimistic") {

test/functional/query-builder/locking/query-builder-locking.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,28 @@ describe("query builder > locking", () => {
7777
});
7878
})));
7979

80+
it("should throw error if for no key update lock used without transaction", () => Promise.all(connections.map(async connection => {
81+
if (connection.driver instanceof PostgresDriver) {
82+
return connection.createQueryBuilder(PostWithVersion, "post")
83+
.setLock("for_no_key_update")
84+
.where("post.id = :id", { id: 1 })
85+
.getOne().should.be.rejectedWith(PessimisticLockTransactionRequiredError);
86+
}
87+
return;
88+
})));
89+
90+
it("should not throw error if for no key update lock used with transaction", () => Promise.all(connections.map(async connection => {
91+
if (connection.driver instanceof PostgresDriver) {
92+
return connection.manager.transaction(entityManager => {
93+
return Promise.all([entityManager.createQueryBuilder(PostWithVersion, "post")
94+
.setLock("for_no_key_update")
95+
.where("post.id = :id", { id: 1})
96+
.getOne().should.not.be.rejected]);
97+
});
98+
}
99+
return;
100+
})));
101+
80102
it("should attach pessimistic read lock statement on query if locking enabled", () => Promise.all(connections.map(async connection => {
81103
if (connection.driver instanceof AbstractSqliteDriver || connection.driver instanceof CockroachDriver || connection.driver instanceof SapDriver)
82104
return;
@@ -141,6 +163,30 @@ describe("query builder > locking", () => {
141163

142164
})));
143165

166+
it("should not attach for no key update lock statement on query if locking is not used", () => Promise.all(connections.map(async connection => {
167+
if (connection.driver instanceof PostgresDriver) {
168+
const sql = connection.createQueryBuilder(PostWithVersion, "post")
169+
.where("post.id = :id", { id: 1 })
170+
.getSql();
171+
172+
expect(sql.indexOf("FOR NO KEY UPDATE") === -1).to.be.true;
173+
}
174+
return;
175+
})));
176+
177+
it("should attach for no key update lock statement on query if locking enabled", () => Promise.all(connections.map(async connection => {
178+
if (connection.driver instanceof PostgresDriver) {
179+
const sql = connection.createQueryBuilder(PostWithVersion, "post")
180+
.setLock("for_no_key_update")
181+
.where("post.id = :id", { id: 1 })
182+
.getSql();
183+
184+
expect(sql.indexOf("FOR NO KEY UPDATE") !== -1).to.be.true;
185+
}
186+
return;
187+
188+
})));
189+
144190
it("should throw error if optimistic lock used with getMany method", () => Promise.all(connections.map(async connection => {
145191

146192
return connection.createQueryBuilder(PostWithVersion, "post")
@@ -291,4 +337,19 @@ describe("query builder > locking", () => {
291337
return;
292338
})));
293339

340+
it("should throw error if for no key update locking not supported by given driver", () => Promise.all(connections.map(async connection => {
341+
if (!(connection.driver instanceof PostgresDriver)) {
342+
return connection.manager.transaction(entityManager => {
343+
return Promise.all([
344+
entityManager.createQueryBuilder(PostWithVersion, "post")
345+
.setLock("for_no_key_update")
346+
.where("post.id = :id", { id: 1 })
347+
.getOne().should.be.rejectedWith(LockNotSupportedOnGivenDriverError),
348+
]);
349+
});
350+
}
351+
352+
return;
353+
})));
354+
294355
});

0 commit comments

Comments
 (0)