Skip to content

Commit 3768a40

Browse files
committed
Update SQL query formatting in SelectQueryBuilder to use double quotes for table and column names, ensuring compatibility with case-sensitive identifiers.
1 parent 945dd4c commit 3768a40

File tree

2 files changed

+259
-3
lines changed

2 files changed

+259
-3
lines changed

__tests__/select-builder.test.ts

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import { SelectQueryBuilder, SqlExecutor } from "../src";
2+
import { Join, OrderBy, SimpleWhere, CompositeWhere } from "../src/types/query";
3+
4+
// Mock SqlExecutor
5+
const mockExecutor: SqlExecutor = {
6+
executeSQL: jest.fn(),
7+
};
8+
9+
interface User {
10+
id: string;
11+
name: string;
12+
email: string;
13+
age?: number;
14+
roleId?: string;
15+
}
16+
17+
interface Role {
18+
id: string;
19+
name: string;
20+
description: string;
21+
}
22+
23+
describe("SelectQueryBuilder", () => {
24+
let builder: SelectQueryBuilder<User>;
25+
26+
beforeEach(() => {
27+
builder = new SelectQueryBuilder("users", mockExecutor);
28+
jest.clearAllMocks();
29+
});
30+
31+
describe("Basic Select", () => {
32+
it("should build correct SQL for basic select", () => {
33+
const result = builder.build();
34+
35+
expect(result.sql).toContain(`SELECT "users".*`);
36+
expect(result.sql).toContain(`FROM "users"`);
37+
expect(result.values).toEqual([]);
38+
});
39+
40+
it("should build correct SQL for specific columns", () => {
41+
const result = builder.select(["name", "email"]).build();
42+
43+
expect(result.sql).toContain(`SELECT "users"."name","users"."email"`);
44+
expect(result.sql).toContain(`FROM "users"`);
45+
expect(result.values).toEqual([]);
46+
});
47+
});
48+
49+
describe("Where Conditions", () => {
50+
it("should build correct SQL for simple where condition", () => {
51+
const where: SimpleWhere<User> = {
52+
key: "age",
53+
operator: ">",
54+
value: 18,
55+
};
56+
57+
const result = builder.where(where).build();
58+
59+
expect(result.sql).toContain("WHERE users.age > $1");
60+
expect(result.values).toEqual([18]);
61+
});
62+
63+
it("should build correct SQL for composite AND condition", () => {
64+
const where: CompositeWhere<User> = {
65+
AND: [
66+
{ key: "age", operator: ">", value: 18 } as SimpleWhere<User>,
67+
{ key: "name", operator: "=", value: "John" } as SimpleWhere<User>,
68+
],
69+
};
70+
71+
const result = builder.where(where).build();
72+
73+
expect(result.sql).toContain(
74+
"WHERE (users.age > $1 AND users.name = $2)"
75+
);
76+
expect(result.values).toEqual([18, "John"]);
77+
});
78+
79+
it("should build correct SQL for composite OR condition", () => {
80+
const where: CompositeWhere<User> = {
81+
OR: [
82+
{ key: "age", operator: ">", value: 18 } as SimpleWhere<User>,
83+
{ key: "name", operator: "=", value: "John" } as SimpleWhere<User>,
84+
],
85+
};
86+
87+
const result = builder.where(where).build();
88+
89+
expect(result.sql).toContain("WHERE (users.age > $1 OR users.name = $2)");
90+
expect(result.values).toEqual([18, "John"]);
91+
});
92+
93+
it("should build correct SQL for complex nested conditions", () => {
94+
const where: CompositeWhere<User> = {
95+
AND: [
96+
{ key: "age", operator: ">", value: 18 } as SimpleWhere<User>,
97+
{
98+
OR: [
99+
{
100+
key: "name",
101+
operator: "=",
102+
value: "John",
103+
} as SimpleWhere<User>,
104+
{
105+
key: "name",
106+
operator: "=",
107+
value: "Jane",
108+
} as SimpleWhere<User>,
109+
],
110+
} as CompositeWhere<User>,
111+
],
112+
};
113+
114+
const result = builder.where(where).build();
115+
116+
expect(result.sql).toContain(
117+
"WHERE (users.age > $1 AND (users.name = $2 OR users.name = $3))"
118+
);
119+
expect(result.values).toEqual([18, "John", "Jane"]);
120+
});
121+
});
122+
123+
describe("Joins", () => {
124+
it("should build correct SQL for inner join", () => {
125+
const join: Join<User, Role> = {
126+
table: "roles",
127+
type: "inner",
128+
on: {
129+
localField: "roleId",
130+
foreignField: "id",
131+
},
132+
};
133+
134+
const result = builder.join(join).build();
135+
136+
expect(result.sql).toContain(
137+
"INNER JOIN roles ON users.role_id = roles.id"
138+
);
139+
expect(result.values).toEqual([]);
140+
});
141+
142+
it("should build correct SQL for left join with specific columns", () => {
143+
const join: Join<User, Role> = {
144+
table: "roles",
145+
type: "left",
146+
on: {
147+
localField: "roleId",
148+
foreignField: "id",
149+
},
150+
columns: ["name", "description"],
151+
};
152+
153+
const result = builder.join(join).build();
154+
155+
expect(result.sql).toContain(
156+
"LEFT JOIN roles ON users.role_id = roles.id"
157+
);
158+
expect(result.sql).toContain("roles.name,roles.description");
159+
expect(result.values).toEqual([]);
160+
});
161+
});
162+
163+
describe("Order By", () => {
164+
it("should build correct SQL for single order by", () => {
165+
const orderBy: OrderBy<User> = {
166+
key: "name",
167+
direction: "asc",
168+
};
169+
170+
const result = builder.orderBy(orderBy).build();
171+
172+
expect(result.sql).toContain("ORDER BY users.name ASC");
173+
expect(result.values).toEqual([]);
174+
});
175+
176+
it("should build correct SQL for multiple order by", () => {
177+
const orderBy: OrderBy<User>[] = [
178+
{ key: "name", direction: "asc" },
179+
{ key: "age", direction: "desc" },
180+
];
181+
182+
const result = builder.orderBy(orderBy).build();
183+
184+
expect(result.sql).toContain("ORDER BY users.name ASC, users.age DESC");
185+
expect(result.values).toEqual([]);
186+
});
187+
});
188+
189+
describe("Limit and Offset", () => {
190+
it("should build correct SQL for limit", () => {
191+
const result = builder.limit(10).build();
192+
193+
expect(result.sql).toContain("LIMIT 10");
194+
expect(result.values).toEqual([]);
195+
});
196+
197+
it("should build correct SQL for offset", () => {
198+
const result = builder.offset(20).build();
199+
200+
expect(result.sql).toContain("OFFSET 20");
201+
expect(result.values).toEqual([]);
202+
});
203+
204+
it("should build correct SQL for limit and offset", () => {
205+
const result = builder.limit(10).offset(20).build();
206+
207+
expect(result.sql).toContain("LIMIT 10");
208+
expect(result.sql).toContain("OFFSET 20");
209+
expect(result.values).toEqual([]);
210+
});
211+
});
212+
213+
describe("Complex Queries", () => {
214+
it("should build correct SQL for complex query with all clauses", () => {
215+
const where: SimpleWhere<User> = {
216+
key: "age",
217+
operator: ">",
218+
value: 18,
219+
};
220+
221+
const join: Join<User, Role> = {
222+
table: "roles",
223+
type: "inner",
224+
on: {
225+
localField: "roleId",
226+
foreignField: "id",
227+
},
228+
};
229+
230+
const orderBy: OrderBy<User> = {
231+
key: "name",
232+
direction: "asc",
233+
};
234+
235+
const result = builder
236+
.select(["name", "email"])
237+
.where(where)
238+
.join(join)
239+
.orderBy(orderBy)
240+
.limit(10)
241+
.offset(20)
242+
.build();
243+
244+
expect(result.sql).toContain("SELECT users.name,users.email");
245+
expect(result.sql).toContain("FROM users");
246+
expect(result.sql).toContain(
247+
"INNER JOIN roles ON users.role_id = roles.id"
248+
);
249+
expect(result.sql).toContain("WHERE users.age > $1");
250+
expect(result.sql).toContain("ORDER BY users.name ASC");
251+
expect(result.sql).toContain("LIMIT 10");
252+
expect(result.sql).toContain("OFFSET 20");
253+
expect(result.values).toEqual([18]);
254+
});
255+
});
256+
});

src/query-builder/select.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ export class SelectQueryBuilder<T> extends BaseQueryBuilder<T> {
6565
// Default columns to '*' if none are provided
6666
const columns =
6767
this.payload.columns
68-
?.map((col) => `${this.tableName}.${col.toString()}`)
69-
.join(",") ?? `${this.tableName}.*`;
68+
?.map((col) => `"${this.tableName}"."${col.toString()}"`)
69+
.join(",") ?? `"${this.tableName}".*`;
7070

7171
const { whereClause, values } = buildWhereClause(
7272
this.payload.where,
@@ -85,7 +85,7 @@ export class SelectQueryBuilder<T> extends BaseQueryBuilder<T> {
8585
const sql = `
8686
SELECT ${columns}
8787
${joinSelectClause ? `${joinSelectClause.join(",")}` : ""}
88-
FROM ${this.tableName}
88+
FROM "${this.tableName}"
8989
${joinConditionClause ? joinConditionClause : ""}
9090
${whereClause ? `WHERE ${whereClause}` : ""}
9191
${orderByClause ? orderByClause : ""}

0 commit comments

Comments
 (0)