Skip to content

Commit

Permalink
feat: support MAX_EXECUTION_TIME for MySQL driver. (#7638)
Browse files Browse the repository at this point in the history
  • Loading branch information
andytruong authored May 15, 2021
1 parent dba327d commit 0564c34
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
12 changes: 12 additions & 0 deletions docs/select-query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* [Streaming result data](#streaming-result-data)
* [Using pagination](#using-pagination)
* [Set locking](#set-locking)
* [Max execution time](#max-execution-time)
* [Partial selection](#partial-selection)
* [Using subqueries](#using-subqueries)
* [Hidden Columns](#hidden-columns)
Expand Down Expand Up @@ -914,6 +915,17 @@ const users = await getRepository(User)

Optimistic locking works in conjunction with both `@Version` and `@UpdatedDate` decorators.

## Max execution time

We can drop slow query to avoid crashing the server. Only MySQL driver is supported at the moment:

```typescript
const users = await getRepository(User)
.createQueryBuilder("user")
.maxExecutionTime(1000) // milliseconds.
.getMany();
```

## Partial selection

If you want to select only some entity properties, you can use the following syntax:
Expand Down
6 changes: 6 additions & 0 deletions src/query-builder/QueryExpressionMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ export class QueryExpressionMap {
*/
selects: SelectQuery[] = [];

/**
* Max execution time in millisecond.
*/
maxExecutionTime: number = 0;

/**
* Whether SELECT is DISTINCT.
*/
Expand Down Expand Up @@ -399,6 +404,7 @@ export class QueryExpressionMap {
const map = new QueryExpressionMap(this.connection);
map.queryType = this.queryType;
map.selects = this.selects.map(select => select);
map.maxExecutionTime = this.maxExecutionTime;
map.selectDistinct = this.selectDistinct;
map.selectDistinctOn = this.selectDistinctOn;
this.aliases.forEach(alias => map.aliases.push(new Alias(alias)));
Expand Down
18 changes: 17 additions & 1 deletion src/query-builder/SelectQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
return this;
}

/**
* Set max execution time.
* @param milliseconds
*/
maxExecutionTime(milliseconds: number): this {
this.expressionMap.maxExecutionTime = milliseconds;
return this;
}

/**
* Sets whether the selection is DISTINCT.
*/
Expand Down Expand Up @@ -1448,10 +1457,17 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
* Creates select | select distinct part of SQL query.
*/
protected createSelectDistinctExpression(): string {
const {selectDistinct, selectDistinctOn} = this.expressionMap;
const {selectDistinct, selectDistinctOn, maxExecutionTime} = this.expressionMap;
const {driver} = this.connection;

let select = "SELECT ";

if (maxExecutionTime > 0) {
if (driver instanceof MysqlDriver) {
select += ` /*+ MAX_EXECUTION_TIME(${ this.expressionMap.maxExecutionTime }) */`;
}
}

if (driver instanceof PostgresDriver && selectDistinctOn.length > 0) {
const selectDistinctOnMap = selectDistinctOn.map(
(on) => this.replacePropertyNames(on)
Expand Down
13 changes: 13 additions & 0 deletions test/functional/query-builder/select/query-builder-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {Connection} from "../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {expect} from "chai";
import {EntityNotFoundError} from "../../../../src/error/EntityNotFoundError";
import { MysqlDriver } from '../../../../src/driver/mysql/MysqlDriver';

describe("query builder > select", () => {

Expand Down Expand Up @@ -153,4 +154,16 @@ describe("query builder > select", () => {
.getOneOrFail()
).to.be.rejectedWith(EntityNotFoundError);
})));

it("Support max execution time", async () => Promise.all(
connections
.filter(connection => connection.driver instanceof MysqlDriver)
.map(async connection => {
const sql = connection
.createQueryBuilder(Post, "post")
.maxExecutionTime(1000)
.getSql();
expect(sql).contains("SELECT /*+ MAX_EXECUTION_TIME(1000) */");
})
));
});

0 comments on commit 0564c34

Please sign in to comment.