-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Description
Issue Description
Conditions in an andWhere are not isolated with parentheses. Thus, they can affect other conditions when building a query with a QueryBuilder that results in unexpected sql begin generated and unexpected results.
Expected Behavior
The condition argument to andWhere should get wrapped in parentheses in the generated SQL to ensure the entire condition is truly captured.
Suppose I have a blog with Posts by Users. I have Permissions that join Users to Posts they are allowed to read. There are also admin users (user.admin === true) who can view everything (even though they have no Permission entity).
const readablePostsInCategory = await db.createQueryBuilder(Post, "post")
.leftJoin("post.permissions", "permission")
.where("post.category = :category", { category })
.andWhere(":userIsAdmin = TRUE OR permission.userId = :userId", { userIsAdmin: user.admin, userId: user.id })
.getMany();This should create sql with a where condition that looks like
WHERE post.category = $1 AND ($2 = TRUE OR permission.userId = $3)
Note the parentheses. Without them, the condition becomes
WHERE (post.category = $1 AND $2 = TRUE) OR permission.userId = $3
Actual Behavior
The andWhere condition is not isolated, and so the OR inside it overrides the first where condition.
It generates a where condition that looks like
WHERE post.category = $1 AND $2 = TRUE OR permission.userId = $3
Note the lack of parentheses around the andWhere condition. This results in a bypass of the first where (post.category = $1) if permission.userId = $3.
Steps to Reproduce
- create entities
- generate a query with an
andWherecontaining anOR. - notice the condition is not isolated with parentheses and the result set is incorrect b/c of it.
createConnection()
.then(async (connection) => {
// this query builder generates an incorrect query
const sql = connection
.createQueryBuilder(Post, 'post')
.leftJoin('post.permissions', 'permission')
.where('post.category = :category', { category: "code" })
.andWhere(':userIsAdmin = TRUE OR permission.userId = :userId', {
userIsAdmin: false,
userId: 1,
})
.getSql();
console.log(sql);
})
.catch((error) => console.log(error));
// entities
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
admin: boolean;
@Column()
name: string;
}
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
content: string;
@Column()
category: string;
@OneToMany(() => Permission, (p) => p.post)
permissions: Permission[];
}
@Entity()
export class Permission {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => User)
user: User;
@Column()
userId: number;
@ManyToOne(() => Post, (p) => p.permissions)
post: Post;
@Column()
postId: number;
}The above outputs
SELECT "post"."id" AS "post_id", "post"."content" AS "post_content"
FROM "post" "post"
LEFT JOIN "permission" "permission" ON "permission"."postId"="post"."id"
WHERE post.category = ? AND ? = TRUE OR "permission"."userId" = ? -- <--- no parentheses herebut should output
SELECT "post"."id" AS "post_id", "post"."content" AS "post_content"
FROM "post" "post"
LEFT JOIN "permission" "permission" ON "permission"."postId"="post"."id"
WHERE post.category = ? AND (? = TRUE OR "permission"."userId" = ?)My Environment
| Dependency | Version |
|---|---|
| Operating System | |
| Node.js version | v14.9.0 |
| Typescript version | v4.1.3 |
| TypeORM version | v0.2.25 |
Additional Context
Relevant Database Driver(s)
-
aurora-data-api -
aurora-data-api-pg -
better-sqlite3 -
cockroachdb -
cordova -
expo -
mongodb -
mysql -
nativescript -
oracle -
postgres -
react-native -
sap -
sqlite -
sqlite-abstract -
sqljs -
sqlserver
Are you willing to resolve this issue by submitting a Pull Request?
- Yes, I have the time, and I know how to start.
- Yes, I have the time, but I don't know how to start. I would need guidance.
- No, I don't have the time, although I believe I could do it if I had the time...
- No, I don't have the time and I wouldn't even know how to start.
Thanks for your time! Great project here!