feat: add isolated where statements (#10213)

- Add `isolateWhereStatements` to the `BaseDataSourceOptions` object
  and automatically isolate where statements in the query builder
  if it is true
This commit is contained in:
Tait Clarridge 2024-01-02 01:54:43 -05:00 committed by GitHub
parent 149226dd67
commit 3cda7ec39d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 106 additions and 2 deletions

View File

@ -98,6 +98,9 @@ Different RDBMS-es have their own specific options.
- `cache` - Enables entity result caching. You can also configure cache type and other cache options here.
Read more about caching [here](caching.md).
- `isolateWhereStatements` - Enables where statement isolation, wrapping each where clause in brackets automatically.
eg. `.where("user.firstName = :search OR user.lastName = :search")` becomes `WHERE (user.firstName = ? OR user.lastName = ?)` instead of `WHERE user.firstName = ? OR user.lastName = ?`
## `mysql` / `mariadb` data source options
- `url` - Connection url where perform connection to. Please note that other data source options will override parameters set from url.

View File

@ -208,4 +208,9 @@ export interface BaseDataSourceOptions {
*/
readonly ignoreErrors?: boolean
}
/**
* Allows automatic isolation of where clauses
*/
readonly isolateWhereStatements?: boolean
}

View File

@ -1008,9 +1008,31 @@ export abstract class QueryBuilder<Entity extends ObjectLiteral> {
switch (clause.type) {
case "and":
return (index > 0 ? "AND " : "") + expression
return (
(index > 0 ? "AND " : "") +
`${
this.connection.options.isolateWhereStatements
? "("
: ""
}${expression}${
this.connection.options.isolateWhereStatements
? ")"
: ""
}`
)
case "or":
return (index > 0 ? "OR " : "") + expression
return (
(index > 0 ? "OR " : "") +
`${
this.connection.options.isolateWhereStatements
? "("
: ""
}${expression}${
this.connection.options.isolateWhereStatements
? ")"
: ""
}`
)
}
return expression

View File

@ -0,0 +1,18 @@
import { Entity } from "../../../../../src/decorator/entity/Entity"
import { PrimaryGeneratedColumn } from "../../../../../src/decorator/columns/PrimaryGeneratedColumn"
import { Column } from "../../../../../src/decorator/columns/Column"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
isAdmin: boolean
}

View File

@ -0,0 +1,48 @@
import "../../../utils/test-setup"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../../utils/test-utils"
import { expect } from "chai"
import { DataSource } from "../../../../src"
import { User } from "./entity/User"
describe("query builder > isolated-where", () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [User],
enabledDrivers: ["sqlite"],
isolateWhereStatements: true,
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
it("should correctly apply brackets when where statement isolation is enabled", () =>
Promise.all(
connections.map(async (connection) => {
const sql = connection.manager
.createQueryBuilder(User, "user")
.where("user.id = :userId", { userId: "user-id" })
.andWhere(
"user.firstName = :search OR user.lastName = :search",
{
search: "search-term",
},
)
.disableEscaping()
.getSql()
expect(sql).to.be.equal(
"SELECT user.id AS user_id, user.firstName AS user_firstName, " +
"user.lastName AS user_lastName, user.isAdmin AS user_isAdmin " +
"FROM user user " +
"WHERE user.id = ? " +
"AND (user.firstName = ? OR user.lastName = ?)",
)
}),
))
})

View File

@ -159,6 +159,11 @@ export interface TestingOptions {
| Logger
relationLoadStrategy?: "join" | "query"
/**
* Allows automatic isolation of where clauses
*/
isolateWhereStatements?: boolean
}
/**
@ -295,6 +300,9 @@ export function setupTestingConnections(
newOptions.metadataTableName = options.metadataTableName
if (options && options.relationLoadStrategy)
newOptions.relationLoadStrategy = options.relationLoadStrategy
if (options && options.isolateWhereStatements)
newOptions.isolateWhereStatements =
options.isolateWhereStatements
newOptions.baseDirectory = path.dirname(getOrmFilepath())