@simplysm/orm-common 13.0.69 → 13.0.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/README.md +54 -1447
  2. package/dist/create-db-context.d.ts +10 -10
  3. package/dist/create-db-context.js +9 -9
  4. package/dist/create-db-context.js.map +1 -1
  5. package/dist/ddl/column-ddl.d.ts +4 -4
  6. package/dist/ddl/initialize.d.ts +17 -17
  7. package/dist/ddl/initialize.js +2 -2
  8. package/dist/ddl/initialize.js.map +1 -1
  9. package/dist/ddl/relation-ddl.d.ts +6 -6
  10. package/dist/ddl/schema-ddl.d.ts +4 -4
  11. package/dist/ddl/table-ddl.d.ts +24 -24
  12. package/dist/ddl/table-ddl.js +4 -4
  13. package/dist/ddl/table-ddl.js.map +1 -1
  14. package/dist/errors/db-transaction-error.d.ts +15 -15
  15. package/dist/errors/db-transaction-error.d.ts.map +1 -1
  16. package/dist/exec/executable.d.ts +23 -23
  17. package/dist/exec/executable.js +3 -3
  18. package/dist/exec/executable.js.map +1 -1
  19. package/dist/exec/queryable.d.ts +160 -160
  20. package/dist/exec/queryable.js +119 -119
  21. package/dist/exec/queryable.js.map +1 -1
  22. package/dist/exec/search-parser.d.ts +37 -37
  23. package/dist/exec/search-parser.d.ts.map +1 -1
  24. package/dist/expr/expr-unit.d.ts +4 -4
  25. package/dist/expr/expr.d.ts +257 -257
  26. package/dist/expr/expr.js +265 -265
  27. package/dist/expr/expr.js.map +1 -1
  28. package/dist/query-builder/base/expr-renderer-base.d.ts +9 -9
  29. package/dist/query-builder/base/expr-renderer-base.js +2 -2
  30. package/dist/query-builder/base/expr-renderer-base.js.map +1 -1
  31. package/dist/query-builder/base/query-builder-base.d.ts +26 -26
  32. package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
  33. package/dist/query-builder/base/query-builder-base.js +22 -22
  34. package/dist/query-builder/base/query-builder-base.js.map +1 -1
  35. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +4 -4
  36. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
  37. package/dist/query-builder/mssql/mssql-expr-renderer.js +18 -18
  38. package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
  39. package/dist/query-builder/mssql/mssql-query-builder.d.ts +2 -2
  40. package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
  41. package/dist/query-builder/mssql/mssql-query-builder.js +11 -11
  42. package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
  43. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +4 -4
  44. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
  45. package/dist/query-builder/mysql/mysql-expr-renderer.js +17 -17
  46. package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
  47. package/dist/query-builder/mysql/mysql-query-builder.d.ts +8 -8
  48. package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
  49. package/dist/query-builder/mysql/mysql-query-builder.js +5 -5
  50. package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
  51. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +4 -4
  52. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
  53. package/dist/query-builder/postgresql/postgresql-expr-renderer.js +17 -17
  54. package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
  55. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +5 -5
  56. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
  57. package/dist/query-builder/postgresql/postgresql-query-builder.js +8 -8
  58. package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
  59. package/dist/query-builder/query-builder.d.ts +1 -1
  60. package/dist/schema/factory/column-builder.d.ts +79 -79
  61. package/dist/schema/factory/column-builder.js +42 -42
  62. package/dist/schema/factory/index-builder.d.ts +39 -39
  63. package/dist/schema/factory/index-builder.js +26 -26
  64. package/dist/schema/factory/relation-builder.d.ts +99 -99
  65. package/dist/schema/factory/relation-builder.d.ts.map +1 -1
  66. package/dist/schema/factory/relation-builder.js +38 -38
  67. package/dist/schema/procedure-builder.d.ts +49 -49
  68. package/dist/schema/procedure-builder.d.ts.map +1 -1
  69. package/dist/schema/procedure-builder.js +33 -33
  70. package/dist/schema/table-builder.d.ts +59 -59
  71. package/dist/schema/table-builder.d.ts.map +1 -1
  72. package/dist/schema/table-builder.js +43 -43
  73. package/dist/schema/view-builder.d.ts +49 -49
  74. package/dist/schema/view-builder.d.ts.map +1 -1
  75. package/dist/schema/view-builder.js +32 -32
  76. package/dist/types/column.d.ts +22 -22
  77. package/dist/types/column.js +1 -1
  78. package/dist/types/column.js.map +1 -1
  79. package/dist/types/db.d.ts +40 -40
  80. package/dist/types/expr.d.ts +59 -59
  81. package/dist/types/expr.d.ts.map +1 -1
  82. package/dist/types/query-def.d.ts +44 -44
  83. package/dist/types/query-def.d.ts.map +1 -1
  84. package/dist/utils/result-parser.d.ts +11 -11
  85. package/dist/utils/result-parser.js +3 -3
  86. package/dist/utils/result-parser.js.map +1 -1
  87. package/package.json +5 -5
  88. package/src/create-db-context.ts +20 -20
  89. package/src/ddl/column-ddl.ts +4 -4
  90. package/src/ddl/initialize.ts +259 -259
  91. package/src/ddl/relation-ddl.ts +89 -89
  92. package/src/ddl/schema-ddl.ts +4 -4
  93. package/src/ddl/table-ddl.ts +189 -189
  94. package/src/errors/db-transaction-error.ts +13 -13
  95. package/src/exec/executable.ts +25 -25
  96. package/src/exec/queryable.ts +2033 -2033
  97. package/src/exec/search-parser.ts +57 -57
  98. package/src/expr/expr-unit.ts +4 -4
  99. package/src/expr/expr.ts +2140 -2140
  100. package/src/query-builder/base/expr-renderer-base.ts +237 -237
  101. package/src/query-builder/base/query-builder-base.ts +213 -213
  102. package/src/query-builder/mssql/mssql-expr-renderer.ts +607 -607
  103. package/src/query-builder/mssql/mssql-query-builder.ts +650 -650
  104. package/src/query-builder/mysql/mysql-expr-renderer.ts +613 -613
  105. package/src/query-builder/mysql/mysql-query-builder.ts +759 -759
  106. package/src/query-builder/postgresql/postgresql-expr-renderer.ts +611 -611
  107. package/src/query-builder/postgresql/postgresql-query-builder.ts +686 -686
  108. package/src/query-builder/query-builder.ts +19 -19
  109. package/src/schema/factory/column-builder.ts +423 -423
  110. package/src/schema/factory/index-builder.ts +164 -164
  111. package/src/schema/factory/relation-builder.ts +453 -453
  112. package/src/schema/procedure-builder.ts +232 -232
  113. package/src/schema/table-builder.ts +319 -319
  114. package/src/schema/view-builder.ts +221 -221
  115. package/src/types/column.ts +188 -188
  116. package/src/types/db.ts +208 -208
  117. package/src/types/expr.ts +697 -697
  118. package/src/types/query-def.ts +513 -513
  119. package/src/utils/result-parser.ts +458 -458
  120. package/tests/db-context/create-db-context.spec.ts +224 -0
  121. package/tests/db-context/define-db-context.spec.ts +68 -0
  122. package/tests/ddl/basic.expected.ts +341 -0
  123. package/tests/ddl/basic.spec.ts +714 -0
  124. package/tests/ddl/column-builder.expected.ts +310 -0
  125. package/tests/ddl/column-builder.spec.ts +637 -0
  126. package/tests/ddl/index-builder.expected.ts +38 -0
  127. package/tests/ddl/index-builder.spec.ts +202 -0
  128. package/tests/ddl/procedure-builder.expected.ts +52 -0
  129. package/tests/ddl/procedure-builder.spec.ts +234 -0
  130. package/tests/ddl/relation-builder.expected.ts +36 -0
  131. package/tests/ddl/relation-builder.spec.ts +372 -0
  132. package/tests/ddl/table-builder.expected.ts +113 -0
  133. package/tests/ddl/table-builder.spec.ts +433 -0
  134. package/tests/ddl/view-builder.expected.ts +38 -0
  135. package/tests/ddl/view-builder.spec.ts +176 -0
  136. package/tests/dml/delete.expected.ts +96 -0
  137. package/tests/dml/delete.spec.ts +160 -0
  138. package/tests/dml/insert.expected.ts +192 -0
  139. package/tests/dml/insert.spec.ts +288 -0
  140. package/tests/dml/update.expected.ts +176 -0
  141. package/tests/dml/update.spec.ts +318 -0
  142. package/tests/dml/upsert.expected.ts +215 -0
  143. package/tests/dml/upsert.spec.ts +242 -0
  144. package/tests/errors/queryable-errors.spec.ts +177 -0
  145. package/tests/escape.spec.ts +100 -0
  146. package/tests/examples/pivot.expected.ts +211 -0
  147. package/tests/examples/pivot.spec.ts +533 -0
  148. package/tests/examples/sampling.expected.ts +69 -0
  149. package/tests/examples/sampling.spec.ts +105 -0
  150. package/tests/examples/unpivot.expected.ts +120 -0
  151. package/tests/examples/unpivot.spec.ts +226 -0
  152. package/tests/exec/search-parser.spec.ts +283 -0
  153. package/tests/executable/basic.expected.ts +18 -0
  154. package/tests/executable/basic.spec.ts +54 -0
  155. package/tests/expr/comparison.expected.ts +282 -0
  156. package/tests/expr/comparison.spec.ts +400 -0
  157. package/tests/expr/conditional.expected.ts +134 -0
  158. package/tests/expr/conditional.spec.ts +276 -0
  159. package/tests/expr/date.expected.ts +332 -0
  160. package/tests/expr/date.spec.ts +526 -0
  161. package/tests/expr/math.expected.ts +62 -0
  162. package/tests/expr/math.spec.ts +106 -0
  163. package/tests/expr/string.expected.ts +218 -0
  164. package/tests/expr/string.spec.ts +356 -0
  165. package/tests/expr/utility.expected.ts +147 -0
  166. package/tests/expr/utility.spec.ts +182 -0
  167. package/tests/select/basic.expected.ts +322 -0
  168. package/tests/select/basic.spec.ts +502 -0
  169. package/tests/select/filter.expected.ts +357 -0
  170. package/tests/select/filter.spec.ts +1068 -0
  171. package/tests/select/group.expected.ts +169 -0
  172. package/tests/select/group.spec.ts +244 -0
  173. package/tests/select/join.expected.ts +582 -0
  174. package/tests/select/join.spec.ts +805 -0
  175. package/tests/select/order.expected.ts +150 -0
  176. package/tests/select/order.spec.ts +189 -0
  177. package/tests/select/recursive-cte.expected.ts +244 -0
  178. package/tests/select/recursive-cte.spec.ts +514 -0
  179. package/tests/select/result-meta.spec.ts +270 -0
  180. package/tests/select/subquery.expected.ts +363 -0
  181. package/tests/select/subquery.spec.ts +537 -0
  182. package/tests/select/view.expected.ts +155 -0
  183. package/tests/select/view.spec.ts +235 -0
  184. package/tests/select/window.expected.ts +345 -0
  185. package/tests/select/window.spec.ts +618 -0
  186. package/tests/setup/MockExecutor.ts +18 -0
  187. package/tests/setup/TestDbContext.ts +59 -0
  188. package/tests/setup/models/Company.ts +13 -0
  189. package/tests/setup/models/Employee.ts +10 -0
  190. package/tests/setup/models/MonthlySales.ts +11 -0
  191. package/tests/setup/models/Post.ts +16 -0
  192. package/tests/setup/models/Sales.ts +10 -0
  193. package/tests/setup/models/User.ts +19 -0
  194. package/tests/setup/procedure/GetAllUsers.ts +9 -0
  195. package/tests/setup/procedure/GetUserById.ts +12 -0
  196. package/tests/setup/test-utils.ts +72 -0
  197. package/tests/setup/views/ActiveUsers.ts +8 -0
  198. package/tests/setup/views/UserSummary.ts +11 -0
  199. package/tests/types/nullable-queryable-record.spec.ts +145 -0
  200. package/tests/utils/result-parser-perf.spec.ts +210 -0
  201. package/tests/utils/result-parser.spec.ts +701 -0
  202. package/docs/expressions.md +0 -172
  203. package/docs/queries.md +0 -444
  204. package/docs/schema.md +0 -245
@@ -0,0 +1,1068 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createTestDb } from "../setup/TestDbContext";
3
+ import { expr } from "../../src/expr/expr";
4
+ import { createQueryBuilder } from "../../src/query-builder/query-builder";
5
+ import { dialects } from "../setup/test-utils";
6
+ import "../setup/test-utils"; // toMatchSql matcher
7
+ import * as expected from "./filter.expected";
8
+
9
+ //#region ========== comparison operations ==========
10
+
11
+ describe("SELECT - WHERE - comparison operations", () => {
12
+ describe("eq (equal)", () => {
13
+ const db = createTestDb();
14
+ const def = db
15
+ .user()
16
+ .where((item) => [expr.eq(item.id, 1)])
17
+ .getSelectQueryDef();
18
+
19
+ it("Verify QueryDef", () => {
20
+ expect(def).toEqual({
21
+ type: "select",
22
+ as: "T1",
23
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
24
+ where: [
25
+ {
26
+ type: "eq",
27
+ source: { type: "column", path: ["T1", "id"] },
28
+ target: { type: "value", value: 1 },
29
+ },
30
+ ],
31
+ });
32
+ });
33
+
34
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
35
+ const builder = createQueryBuilder(dialect);
36
+ expect(builder.build(def)).toMatchSql(expected.whereEq[dialect]);
37
+ });
38
+ });
39
+
40
+ describe("not eq (not equal)", () => {
41
+ const db = createTestDb();
42
+ const def = db
43
+ .user()
44
+ .where((item) => [expr.not(expr.eq(item.id, 1))])
45
+ .getSelectQueryDef();
46
+
47
+ it("Verify QueryDef", () => {
48
+ expect(def).toEqual({
49
+ type: "select",
50
+ as: "T1",
51
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
52
+ where: [
53
+ {
54
+ type: "not",
55
+ arg: {
56
+ type: "eq",
57
+ source: { type: "column", path: ["T1", "id"] },
58
+ target: { type: "value", value: 1 },
59
+ },
60
+ },
61
+ ],
62
+ });
63
+ });
64
+
65
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
66
+ const builder = createQueryBuilder(dialect);
67
+ expect(builder.build(def)).toMatchSql(expected.whereNotEq[dialect]);
68
+ });
69
+ });
70
+
71
+ describe("gt (greater than)", () => {
72
+ const db = createTestDb();
73
+ const def = db
74
+ .user()
75
+ .where((item) => [expr.gt(item.age, 20)])
76
+ .getSelectQueryDef();
77
+
78
+ it("Verify QueryDef", () => {
79
+ expect(def).toEqual({
80
+ type: "select",
81
+ as: "T1",
82
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
83
+ where: [
84
+ {
85
+ type: "gt",
86
+ source: { type: "column", path: ["T1", "age"] },
87
+ target: { type: "value", value: 20 },
88
+ },
89
+ ],
90
+ });
91
+ });
92
+
93
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
94
+ const builder = createQueryBuilder(dialect);
95
+ expect(builder.build(def)).toMatchSql(expected.whereGt[dialect]);
96
+ });
97
+ });
98
+
99
+ describe("gte (greater than or equal)", () => {
100
+ const db = createTestDb();
101
+ const def = db
102
+ .user()
103
+ .where((item) => [expr.gte(item.age, 20)])
104
+ .getSelectQueryDef();
105
+
106
+ it("Verify QueryDef", () => {
107
+ expect(def).toEqual({
108
+ type: "select",
109
+ as: "T1",
110
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
111
+ where: [
112
+ {
113
+ type: "gte",
114
+ source: { type: "column", path: ["T1", "age"] },
115
+ target: { type: "value", value: 20 },
116
+ },
117
+ ],
118
+ });
119
+ });
120
+
121
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
122
+ const builder = createQueryBuilder(dialect);
123
+ expect(builder.build(def)).toMatchSql(expected.whereGte[dialect]);
124
+ });
125
+ });
126
+
127
+ describe("lt (less than)", () => {
128
+ const db = createTestDb();
129
+ const def = db
130
+ .user()
131
+ .where((item) => [expr.lt(item.age, 30)])
132
+ .getSelectQueryDef();
133
+
134
+ it("Verify QueryDef", () => {
135
+ expect(def).toEqual({
136
+ type: "select",
137
+ as: "T1",
138
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
139
+ where: [
140
+ {
141
+ type: "lt",
142
+ source: { type: "column", path: ["T1", "age"] },
143
+ target: { type: "value", value: 30 },
144
+ },
145
+ ],
146
+ });
147
+ });
148
+
149
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
150
+ const builder = createQueryBuilder(dialect);
151
+ expect(builder.build(def)).toMatchSql(expected.whereLt[dialect]);
152
+ });
153
+ });
154
+
155
+ describe("lte (less than or equal)", () => {
156
+ const db = createTestDb();
157
+ const def = db
158
+ .user()
159
+ .where((item) => [expr.lte(item.age, 30)])
160
+ .getSelectQueryDef();
161
+
162
+ it("Verify QueryDef", () => {
163
+ expect(def).toEqual({
164
+ type: "select",
165
+ as: "T1",
166
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
167
+ where: [
168
+ {
169
+ type: "lte",
170
+ source: { type: "column", path: ["T1", "age"] },
171
+ target: { type: "value", value: 30 },
172
+ },
173
+ ],
174
+ });
175
+ });
176
+
177
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
178
+ const builder = createQueryBuilder(dialect);
179
+ expect(builder.build(def)).toMatchSql(expected.whereLte[dialect]);
180
+ });
181
+ });
182
+ });
183
+
184
+ //#endregion
185
+
186
+ //#region ========== NULL check ==========
187
+
188
+ describe("SELECT - WHERE - NULL check", () => {
189
+ describe("null", () => {
190
+ const db = createTestDb();
191
+ const def = db
192
+ .user()
193
+ .where((item) => [expr.null(item.email)])
194
+ .getSelectQueryDef();
195
+
196
+ it("Verify QueryDef", () => {
197
+ expect(def).toEqual({
198
+ type: "select",
199
+ as: "T1",
200
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
201
+ where: [
202
+ {
203
+ type: "null",
204
+ arg: { type: "column", path: ["T1", "email"] },
205
+ },
206
+ ],
207
+ });
208
+ });
209
+
210
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
211
+ const builder = createQueryBuilder(dialect);
212
+ expect(builder.build(def)).toMatchSql(expected.whereNull[dialect]);
213
+ });
214
+ });
215
+
216
+ describe("not null", () => {
217
+ const db = createTestDb();
218
+ const def = db
219
+ .user()
220
+ .where((item) => [expr.not(expr.null(item.email))])
221
+ .getSelectQueryDef();
222
+
223
+ it("Verify QueryDef", () => {
224
+ expect(def).toEqual({
225
+ type: "select",
226
+ as: "T1",
227
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
228
+ where: [
229
+ {
230
+ type: "not",
231
+ arg: {
232
+ type: "null",
233
+ arg: { type: "column", path: ["T1", "email"] },
234
+ },
235
+ },
236
+ ],
237
+ });
238
+ });
239
+
240
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
241
+ const builder = createQueryBuilder(dialect);
242
+ expect(builder.build(def)).toMatchSql(expected.whereNotNull[dialect]);
243
+ });
244
+ });
245
+ });
246
+
247
+ //#endregion
248
+
249
+ //#region ========== IN ==========
250
+
251
+ describe("SELECT - WHERE - IN", () => {
252
+ describe("in", () => {
253
+ const db = createTestDb();
254
+ const def = db
255
+ .user()
256
+ .where((item) => [expr.in(item.id, [1, 2, 3])])
257
+ .getSelectQueryDef();
258
+
259
+ it("Verify QueryDef", () => {
260
+ expect(def).toEqual({
261
+ type: "select",
262
+ as: "T1",
263
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
264
+ where: [
265
+ {
266
+ type: "in",
267
+ source: { type: "column", path: ["T1", "id"] },
268
+ values: [
269
+ { type: "value", value: 1 },
270
+ { type: "value", value: 2 },
271
+ { type: "value", value: 3 },
272
+ ],
273
+ },
274
+ ],
275
+ });
276
+ });
277
+
278
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
279
+ const builder = createQueryBuilder(dialect);
280
+ expect(builder.build(def)).toMatchSql(expected.whereIn[dialect]);
281
+ });
282
+ });
283
+
284
+ describe("not in", () => {
285
+ const db = createTestDb();
286
+ const def = db
287
+ .user()
288
+ .where((item) => [expr.not(expr.in(item.id, [1, 2]))])
289
+ .getSelectQueryDef();
290
+
291
+ it("Verify QueryDef", () => {
292
+ expect(def).toEqual({
293
+ type: "select",
294
+ as: "T1",
295
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
296
+ where: [
297
+ {
298
+ type: "not",
299
+ arg: {
300
+ type: "in",
301
+ source: { type: "column", path: ["T1", "id"] },
302
+ values: [
303
+ { type: "value", value: 1 },
304
+ { type: "value", value: 2 },
305
+ ],
306
+ },
307
+ },
308
+ ],
309
+ });
310
+ });
311
+
312
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
313
+ const builder = createQueryBuilder(dialect);
314
+ expect(builder.build(def)).toMatchSql(expected.whereNotIn[dialect]);
315
+ });
316
+ });
317
+ });
318
+
319
+ //#endregion
320
+
321
+ //#region ========== LIKE ==========
322
+
323
+ describe("SELECT - WHERE - LIKE", () => {
324
+ describe("like", () => {
325
+ const db = createTestDb();
326
+ const def = db
327
+ .user()
328
+ .where((item) => [expr.like(item.name, "%Hong%")])
329
+ .getSelectQueryDef();
330
+
331
+ it("Verify QueryDef", () => {
332
+ expect(def).toEqual({
333
+ type: "select",
334
+ as: "T1",
335
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
336
+ where: [
337
+ {
338
+ type: "like",
339
+ source: { type: "column", path: ["T1", "name"] },
340
+ pattern: { type: "value", value: "%Hong%" },
341
+ },
342
+ ],
343
+ });
344
+ });
345
+
346
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
347
+ const builder = createQueryBuilder(dialect);
348
+ expect(builder.build(def)).toMatchSql(expected.whereLike[dialect]);
349
+ });
350
+ });
351
+
352
+ describe("not like", () => {
353
+ const db = createTestDb();
354
+ const def = db
355
+ .user()
356
+ .where((item) => [expr.not(expr.like(item.name, "%Test%"))])
357
+ .getSelectQueryDef();
358
+
359
+ it("Verify QueryDef", () => {
360
+ expect(def).toEqual({
361
+ type: "select",
362
+ as: "T1",
363
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
364
+ where: [
365
+ {
366
+ type: "not",
367
+ arg: {
368
+ type: "like",
369
+ source: { type: "column", path: ["T1", "name"] },
370
+ pattern: { type: "value", value: "%Test%" },
371
+ },
372
+ },
373
+ ],
374
+ });
375
+ });
376
+
377
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
378
+ const builder = createQueryBuilder(dialect);
379
+ expect(builder.build(def)).toMatchSql(expected.whereNotLike[dialect]);
380
+ });
381
+ });
382
+ });
383
+
384
+ //#endregion
385
+
386
+ //#region ========== logical operations ==========
387
+
388
+ describe("SELECT - WHERE - logical operations", () => {
389
+ describe("multiple conditions (AND)", () => {
390
+ const db = createTestDb();
391
+ const def = db
392
+ .user()
393
+ .where((item) => [expr.eq(item.isActive, true), expr.gt(item.age, 20)])
394
+ .getSelectQueryDef();
395
+
396
+ it("Verify QueryDef", () => {
397
+ expect(def).toEqual({
398
+ type: "select",
399
+ as: "T1",
400
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
401
+ where: [
402
+ {
403
+ type: "eq",
404
+ source: { type: "column", path: ["T1", "isActive"] },
405
+ target: { type: "value", value: true },
406
+ },
407
+ {
408
+ type: "gt",
409
+ source: { type: "column", path: ["T1", "age"] },
410
+ target: { type: "value", value: 20 },
411
+ },
412
+ ],
413
+ });
414
+ });
415
+
416
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
417
+ const builder = createQueryBuilder(dialect);
418
+ expect(builder.build(def)).toMatchSql(expected.whereMultipleAnd[dialect]);
419
+ });
420
+ });
421
+
422
+ describe("or condition", () => {
423
+ const db = createTestDb();
424
+ const def = db
425
+ .user()
426
+ .where((item) => [expr.or([expr.eq(item.age, 20), expr.eq(item.age, 30)])])
427
+ .getSelectQueryDef();
428
+
429
+ it("Verify QueryDef", () => {
430
+ expect(def).toEqual({
431
+ type: "select",
432
+ as: "T1",
433
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
434
+ where: [
435
+ {
436
+ type: "or",
437
+ conditions: [
438
+ {
439
+ type: "eq",
440
+ source: { type: "column", path: ["T1", "age"] },
441
+ target: { type: "value", value: 20 },
442
+ },
443
+ {
444
+ type: "eq",
445
+ source: { type: "column", path: ["T1", "age"] },
446
+ target: { type: "value", value: 30 },
447
+ },
448
+ ],
449
+ },
450
+ ],
451
+ });
452
+ });
453
+
454
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
455
+ const builder = createQueryBuilder(dialect);
456
+ expect(builder.build(def)).toMatchSql(expected.whereOr[dialect]);
457
+ });
458
+ });
459
+
460
+ describe("and condition (explicit)", () => {
461
+ const db = createTestDb();
462
+ const def = db
463
+ .user()
464
+ .where((item) => [expr.and([expr.gt(item.age, 20), expr.lt(item.age, 30)])])
465
+ .getSelectQueryDef();
466
+
467
+ it("Verify QueryDef", () => {
468
+ expect(def).toEqual({
469
+ type: "select",
470
+ as: "T1",
471
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
472
+ where: [
473
+ {
474
+ type: "and",
475
+ conditions: [
476
+ {
477
+ type: "gt",
478
+ source: { type: "column", path: ["T1", "age"] },
479
+ target: { type: "value", value: 20 },
480
+ },
481
+ {
482
+ type: "lt",
483
+ source: { type: "column", path: ["T1", "age"] },
484
+ target: { type: "value", value: 30 },
485
+ },
486
+ ],
487
+ },
488
+ ],
489
+ });
490
+ });
491
+
492
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
493
+ const builder = createQueryBuilder(dialect);
494
+ expect(builder.build(def)).toMatchSql(expected.whereAndExplicit[dialect]);
495
+ });
496
+ });
497
+
498
+ it("chained where (AND combination)", () => {
499
+ const db = createTestDb();
500
+ const def = db
501
+ .user()
502
+ .where((item) => [expr.eq(item.isActive, true)])
503
+ .where((item) => [expr.gt(item.age, 20)])
504
+ .getSelectQueryDef();
505
+
506
+ expect(def).toEqual({
507
+ type: "select",
508
+ as: "T1",
509
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
510
+ where: [
511
+ {
512
+ type: "eq",
513
+ source: { type: "column", path: ["T1", "isActive"] },
514
+ target: { type: "value", value: true },
515
+ },
516
+ {
517
+ type: "gt",
518
+ source: { type: "column", path: ["T1", "age"] },
519
+ target: { type: "value", value: 20 },
520
+ },
521
+ ],
522
+ });
523
+ });
524
+ });
525
+
526
+ //#endregion
527
+
528
+ //#region ========== BETWEEN ==========
529
+
530
+ describe("SELECT - WHERE - BETWEEN", () => {
531
+ describe("between", () => {
532
+ const db = createTestDb();
533
+ const def = db
534
+ .user()
535
+ .where((item) => [expr.between(item.age, 20, 30)])
536
+ .getSelectQueryDef();
537
+
538
+ it("Verify QueryDef", () => {
539
+ expect(def).toEqual({
540
+ type: "select",
541
+ as: "T1",
542
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
543
+ where: [
544
+ {
545
+ type: "between",
546
+ source: { type: "column", path: ["T1", "age"] },
547
+ from: { type: "value", value: 20 },
548
+ to: { type: "value", value: 30 },
549
+ },
550
+ ],
551
+ });
552
+ });
553
+
554
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
555
+ const builder = createQueryBuilder(dialect);
556
+ expect(builder.build(def)).toMatchSql(expected.whereBetween[dialect]);
557
+ });
558
+ });
559
+ });
560
+
561
+ //#endregion
562
+
563
+ //#region ========== EXISTS / IN subquery ==========
564
+
565
+ describe("SELECT - WHERE - EXISTS / IN subquery", () => {
566
+ describe("exists", () => {
567
+ const db = createTestDb();
568
+ const def = db
569
+ .user()
570
+ .where((item) => [expr.exists(db.post().where((p) => [expr.eq(p.userId, item.id)]))])
571
+ .getSelectQueryDef();
572
+
573
+ it("Verify QueryDef", () => {
574
+ expect(def).toEqual({
575
+ type: "select",
576
+ as: "T1",
577
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
578
+ where: [
579
+ {
580
+ type: "exists",
581
+ query: {
582
+ type: "select",
583
+ as: "T2",
584
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
585
+ where: [
586
+ {
587
+ type: "eq",
588
+ source: { type: "column", path: ["T2", "userId"] },
589
+ target: { type: "column", path: ["T1", "id"] },
590
+ },
591
+ ],
592
+ },
593
+ },
594
+ ],
595
+ });
596
+ });
597
+
598
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
599
+ const builder = createQueryBuilder(dialect);
600
+ expect(builder.build(def)).toMatchSql(expected.whereExists[dialect]);
601
+ });
602
+ });
603
+
604
+ it("not exists - verifies QueryDef", () => {
605
+ const db = createTestDb();
606
+ const def = db
607
+ .user()
608
+ .where((item) => [
609
+ expr.not(expr.exists(db.post().where((p) => [expr.eq(p.userId, item.id)]))),
610
+ ])
611
+ .getSelectQueryDef();
612
+
613
+ expect(def).toEqual({
614
+ type: "select",
615
+ as: "T1",
616
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
617
+ where: [
618
+ {
619
+ type: "not",
620
+ arg: {
621
+ type: "exists",
622
+ query: {
623
+ type: "select",
624
+ as: "T2",
625
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
626
+ where: [
627
+ {
628
+ type: "eq",
629
+ source: { type: "column", path: ["T2", "userId"] },
630
+ target: { type: "column", path: ["T1", "id"] },
631
+ },
632
+ ],
633
+ },
634
+ },
635
+ },
636
+ ],
637
+ });
638
+ });
639
+
640
+ describe("inQuery (IN subquery)", () => {
641
+ const db = createTestDb();
642
+ const def = db
643
+ .user()
644
+ .where((item) => [
645
+ expr.inQuery(
646
+ item.id,
647
+ db.post().select((p) => ({ userId: p.userId })),
648
+ ),
649
+ ])
650
+ .getSelectQueryDef();
651
+
652
+ it("Verify QueryDef", () => {
653
+ expect(def).toEqual({
654
+ type: "select",
655
+ as: "T1",
656
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
657
+ where: [
658
+ {
659
+ type: "inQuery",
660
+ source: { type: "column", path: ["T1", "id"] },
661
+ query: {
662
+ type: "select",
663
+ as: "T2",
664
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
665
+ select: {
666
+ userId: { type: "column", path: ["T2", "userId"] },
667
+ },
668
+ },
669
+ },
670
+ ],
671
+ });
672
+ });
673
+
674
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
675
+ const builder = createQueryBuilder(dialect);
676
+ expect(builder.build(def)).toMatchSql(expected.whereInQuery[dialect]);
677
+ });
678
+ });
679
+ });
680
+
681
+ //#endregion
682
+
683
+ //#region ========== SEARCH ==========
684
+
685
+ describe("SELECT - SEARCH", () => {
686
+ it("single keyword - single column", () => {
687
+ const db = createTestDb();
688
+ const def = db
689
+ .post()
690
+ .search((item) => [item.title], "Apple")
691
+ .getSelectQueryDef();
692
+
693
+ expect(def).toEqual({
694
+ type: "select",
695
+ as: "T1",
696
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
697
+ where: [
698
+ {
699
+ type: "and",
700
+ conditions: [
701
+ {
702
+ type: "or",
703
+ conditions: [
704
+ {
705
+ type: "like",
706
+ source: {
707
+ type: "lower",
708
+ arg: { type: "column", path: ["T1", "title"] },
709
+ },
710
+ pattern: { type: "value", value: "%apple%" },
711
+ },
712
+ ],
713
+ },
714
+ ],
715
+ },
716
+ ],
717
+ });
718
+ });
719
+
720
+ it("multiple keywords (OR)", () => {
721
+ const db = createTestDb();
722
+ const def = db
723
+ .post()
724
+ .search((item) => [item.title], "사과 바나나")
725
+ .getSelectQueryDef();
726
+
727
+ expect(def).toEqual({
728
+ type: "select",
729
+ as: "T1",
730
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
731
+ where: [
732
+ {
733
+ type: "and",
734
+ conditions: [
735
+ {
736
+ type: "or",
737
+ conditions: [
738
+ {
739
+ type: "or",
740
+ conditions: [
741
+ {
742
+ type: "like",
743
+ source: {
744
+ type: "lower",
745
+ arg: { type: "column", path: ["T1", "title"] },
746
+ },
747
+ pattern: { type: "value", value: "%사과%" },
748
+ },
749
+ ],
750
+ },
751
+ {
752
+ type: "or",
753
+ conditions: [
754
+ {
755
+ type: "like",
756
+ source: {
757
+ type: "lower",
758
+ arg: { type: "column", path: ["T1", "title"] },
759
+ },
760
+ pattern: { type: "value", value: "%바나나%" },
761
+ },
762
+ ],
763
+ },
764
+ ],
765
+ },
766
+ ],
767
+ },
768
+ ],
769
+ });
770
+ });
771
+
772
+ it("phrase search (quotes)", () => {
773
+ const db = createTestDb();
774
+ const def = db
775
+ .post()
776
+ .search((item) => [item.title], '"Delicious Fruit"')
777
+ .getSelectQueryDef();
778
+
779
+ expect(def).toEqual({
780
+ type: "select",
781
+ as: "T1",
782
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
783
+ where: [
784
+ {
785
+ type: "and",
786
+ conditions: [
787
+ {
788
+ type: "or",
789
+ conditions: [
790
+ {
791
+ type: "like",
792
+ source: {
793
+ type: "lower",
794
+ arg: { type: "column", path: ["T1", "title"] },
795
+ },
796
+ pattern: { type: "value", value: "%delicious fruit%" },
797
+ },
798
+ ],
799
+ },
800
+ ],
801
+ },
802
+ ],
803
+ });
804
+ });
805
+
806
+ it("wildcard (*)", () => {
807
+ const db = createTestDb();
808
+ const def = db
809
+ .post()
810
+ .search((item) => [item.title], "test*")
811
+ .getSelectQueryDef();
812
+
813
+ expect(def).toEqual({
814
+ type: "select",
815
+ as: "T1",
816
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
817
+ where: [
818
+ {
819
+ type: "and",
820
+ conditions: [
821
+ {
822
+ type: "or",
823
+ conditions: [
824
+ {
825
+ type: "like",
826
+ source: {
827
+ type: "lower",
828
+ arg: { type: "column", path: ["T1", "title"] },
829
+ },
830
+ pattern: { type: "value", value: "test%" },
831
+ },
832
+ ],
833
+ },
834
+ ],
835
+ },
836
+ ],
837
+ });
838
+ });
839
+
840
+ it("escape (\\*)", () => {
841
+ const db = createTestDb();
842
+ const def = db
843
+ .post()
844
+ .search((item) => [item.title], "app\\*")
845
+ .getSelectQueryDef();
846
+
847
+ expect(def).toEqual({
848
+ type: "select",
849
+ as: "T1",
850
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
851
+ where: [
852
+ {
853
+ type: "and",
854
+ conditions: [
855
+ {
856
+ type: "or",
857
+ conditions: [
858
+ {
859
+ type: "like",
860
+ source: {
861
+ type: "lower",
862
+ arg: { type: "column", path: ["T1", "title"] },
863
+ },
864
+ pattern: { type: "value", value: "%app*%" },
865
+ },
866
+ ],
867
+ },
868
+ ],
869
+ },
870
+ ],
871
+ });
872
+ });
873
+
874
+ it("multiple column search (OR)", () => {
875
+ const db = createTestDb();
876
+ const def = db
877
+ .post()
878
+ .search((item) => [item.title, item.content], "Apple")
879
+ .getSelectQueryDef();
880
+
881
+ expect(def).toEqual({
882
+ type: "select",
883
+ as: "T1",
884
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
885
+ where: [
886
+ {
887
+ type: "and",
888
+ conditions: [
889
+ {
890
+ type: "or",
891
+ conditions: [
892
+ {
893
+ type: "like",
894
+ source: {
895
+ type: "lower",
896
+ arg: { type: "column", path: ["T1", "title"] },
897
+ },
898
+ pattern: { type: "value", value: "%apple%" },
899
+ },
900
+ {
901
+ type: "like",
902
+ source: {
903
+ type: "lower",
904
+ arg: { type: "column", path: ["T1", "content"] },
905
+ },
906
+ pattern: { type: "value", value: "%apple%" },
907
+ },
908
+ ],
909
+ },
910
+ ],
911
+ },
912
+ ],
913
+ });
914
+ });
915
+
916
+ it("exclusion search (-)", () => {
917
+ const db = createTestDb();
918
+ const def = db
919
+ .post()
920
+ .search((item) => [item.title], "사과 -바나나")
921
+ .getSelectQueryDef();
922
+
923
+ expect(def).toEqual({
924
+ type: "select",
925
+ as: "T1",
926
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
927
+ where: [
928
+ {
929
+ type: "and",
930
+ conditions: [
931
+ {
932
+ type: "or",
933
+ conditions: [
934
+ {
935
+ type: "like",
936
+ source: {
937
+ type: "lower",
938
+ arg: { type: "column", path: ["T1", "title"] },
939
+ },
940
+ pattern: { type: "value", value: "%사과%" },
941
+ },
942
+ ],
943
+ },
944
+ {
945
+ type: "not",
946
+ arg: {
947
+ type: "or",
948
+ conditions: [
949
+ {
950
+ type: "like",
951
+ source: {
952
+ type: "lower",
953
+ arg: { type: "column", path: ["T1", "title"] },
954
+ },
955
+ pattern: { type: "value", value: "%바나나%" },
956
+ },
957
+ ],
958
+ },
959
+ },
960
+ ],
961
+ },
962
+ ],
963
+ });
964
+ });
965
+
966
+ it("complex search (include, exclude, phrase)", () => {
967
+ const db = createTestDb();
968
+ const def = db
969
+ .post()
970
+ .search((item) => [item.title, item.content], '사과 "Delicious Fruit" -바나나')
971
+ .getSelectQueryDef();
972
+
973
+ expect(def).toEqual({
974
+ type: "select",
975
+ as: "T1",
976
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
977
+ where: [
978
+ {
979
+ type: "and",
980
+ conditions: [
981
+ {
982
+ type: "or",
983
+ conditions: [
984
+ {
985
+ type: "like",
986
+ source: {
987
+ type: "lower",
988
+ arg: { type: "column", path: ["T1", "title"] },
989
+ },
990
+ pattern: { type: "value", value: "%사과%" },
991
+ },
992
+ {
993
+ type: "like",
994
+ source: {
995
+ type: "lower",
996
+ arg: { type: "column", path: ["T1", "content"] },
997
+ },
998
+ pattern: { type: "value", value: "%사과%" },
999
+ },
1000
+ ],
1001
+ },
1002
+ {
1003
+ type: "or",
1004
+ conditions: [
1005
+ {
1006
+ type: "like",
1007
+ source: {
1008
+ type: "lower",
1009
+ arg: { type: "column", path: ["T1", "title"] },
1010
+ },
1011
+ pattern: { type: "value", value: "%delicious fruit%" },
1012
+ },
1013
+ {
1014
+ type: "like",
1015
+ source: {
1016
+ type: "lower",
1017
+ arg: { type: "column", path: ["T1", "content"] },
1018
+ },
1019
+ pattern: { type: "value", value: "%delicious fruit%" },
1020
+ },
1021
+ ],
1022
+ },
1023
+ {
1024
+ type: "not",
1025
+ arg: {
1026
+ type: "or",
1027
+ conditions: [
1028
+ {
1029
+ type: "like",
1030
+ source: {
1031
+ type: "lower",
1032
+ arg: { type: "column", path: ["T1", "title"] },
1033
+ },
1034
+ pattern: { type: "value", value: "%바나나%" },
1035
+ },
1036
+ {
1037
+ type: "like",
1038
+ source: {
1039
+ type: "lower",
1040
+ arg: { type: "column", path: ["T1", "content"] },
1041
+ },
1042
+ pattern: { type: "value", value: "%바나나%" },
1043
+ },
1044
+ ],
1045
+ },
1046
+ },
1047
+ ],
1048
+ },
1049
+ ],
1050
+ });
1051
+ });
1052
+
1053
+ it("empty search term", () => {
1054
+ const db = createTestDb();
1055
+ const def = db
1056
+ .post()
1057
+ .search((item) => [item.title], " ")
1058
+ .getSelectQueryDef();
1059
+
1060
+ expect(def).toEqual({
1061
+ type: "select",
1062
+ as: "T1",
1063
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
1064
+ });
1065
+ });
1066
+ });
1067
+
1068
+ //#endregion