@simplysm/orm-common 13.0.69 → 13.0.70

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 +104 -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,537 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createTestDb } from "../setup/TestDbContext";
3
+ import { expr } from "../../src/expr/expr";
4
+ import { Queryable } from "../../src/exec/queryable";
5
+ import { createQueryBuilder } from "../../src/query-builder/query-builder";
6
+ import { dialects } from "../setup/test-utils";
7
+ import "../setup/test-utils"; // toMatchSql matcher
8
+ import * as expected from "./subquery.expected";
9
+
10
+ describe("SELECT - WRAP (subquery)", () => {
11
+ describe("Basic", () => {
12
+ const db = createTestDb();
13
+ const def = db.user().wrap().getSelectQueryDef();
14
+
15
+ it("Verify QueryDef", () => {
16
+ expect(def).toEqual({
17
+ type: "select",
18
+ as: "T2",
19
+ from: {
20
+ type: "select",
21
+ as: "T1",
22
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
23
+ },
24
+ });
25
+ });
26
+
27
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
28
+ const builder = createQueryBuilder(dialect);
29
+ expect(builder.build(def)).toMatchSql(expected.wrapBasic[dialect]);
30
+ });
31
+ });
32
+
33
+ describe("WRAP -> SELECT", () => {
34
+ const db = createTestDb();
35
+ const def = db
36
+ .user()
37
+ .wrap()
38
+ .select((item) => ({ id: item.id, name: item.name }))
39
+ .getSelectQueryDef();
40
+
41
+ it("Verify QueryDef", () => {
42
+ expect(def).toEqual({
43
+ type: "select",
44
+ as: "T2",
45
+ from: {
46
+ type: "select",
47
+ as: "T1",
48
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
49
+ },
50
+ select: {
51
+ id: { type: "column", path: ["T2", "id"] },
52
+ name: { type: "column", path: ["T2", "name"] },
53
+ },
54
+ });
55
+ });
56
+
57
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
58
+ const builder = createQueryBuilder(dialect);
59
+ expect(builder.build(def)).toMatchSql(expected.wrapThenSelect[dialect]);
60
+ });
61
+ });
62
+
63
+ describe("SELECT -> WRAP", () => {
64
+ const db = createTestDb();
65
+ const def = db
66
+ .user()
67
+ .select((item) => ({ id: item.id, name: item.name }))
68
+ .wrap()
69
+ .getSelectQueryDef();
70
+
71
+ it("Verify QueryDef", () => {
72
+ expect(def).toEqual({
73
+ type: "select",
74
+ as: "T2",
75
+ from: {
76
+ type: "select",
77
+ as: "T1",
78
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
79
+ select: {
80
+ id: { type: "column", path: ["T1", "id"] },
81
+ name: { type: "column", path: ["T1", "name"] },
82
+ },
83
+ },
84
+ });
85
+ });
86
+
87
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
88
+ const builder = createQueryBuilder(dialect);
89
+ expect(builder.build(def)).toMatchSql(expected.selectThenWrap[dialect]);
90
+ });
91
+ });
92
+
93
+ describe("WHERE -> WRAP -> WHERE", () => {
94
+ const db = createTestDb();
95
+ const def = db
96
+ .user()
97
+ .where((item) => [expr.eq(item.isActive, true)])
98
+ .wrap()
99
+ .where((item) => [expr.gt(item.age, 20)])
100
+ .getSelectQueryDef();
101
+
102
+ it("Verify QueryDef", () => {
103
+ expect(def).toEqual({
104
+ type: "select",
105
+ as: "T2",
106
+ from: {
107
+ type: "select",
108
+ as: "T1",
109
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
110
+ where: [
111
+ {
112
+ type: "eq",
113
+ source: { type: "column", path: ["T1", "isActive"] },
114
+ target: { type: "value", value: true },
115
+ },
116
+ ],
117
+ },
118
+ where: [
119
+ {
120
+ type: "gt",
121
+ source: { type: "column", path: ["T2", "age"] },
122
+ target: { type: "value", value: 20 },
123
+ },
124
+ ],
125
+ });
126
+ });
127
+
128
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
129
+ const builder = createQueryBuilder(dialect);
130
+ expect(builder.build(def)).toMatchSql(expected.whereThenWrapThenWhere[dialect]);
131
+ });
132
+ });
133
+
134
+ describe("INCLUDE -> WRAP -> SELECT", () => {
135
+ const db = createTestDb();
136
+ const def = db
137
+ .user()
138
+ .include((item) => item.posts)
139
+ .wrap()
140
+ .select((item) => ({ postUserId: item.posts![0].userId }))
141
+ .getSelectQueryDef();
142
+
143
+ it("Verify QueryDef", () => {
144
+ expect(def).toEqual({
145
+ type: "select",
146
+ as: "T2",
147
+ from: {
148
+ type: "select",
149
+ as: "T1",
150
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
151
+ select: {
152
+ "id": { type: "column", path: ["T1", "id"] },
153
+ "name": { type: "column", path: ["T1", "name"] },
154
+ "email": { type: "column", path: ["T1", "email"] },
155
+ "age": { type: "column", path: ["T1", "age"] },
156
+ "isActive": { type: "column", path: ["T1", "isActive"] },
157
+ "companyId": { type: "column", path: ["T1", "companyId"] },
158
+ "createdAt": { type: "column", path: ["T1", "createdAt"] },
159
+ "posts.id": { type: "column", path: ["T1.posts", "id"] },
160
+ "posts.userId": { type: "column", path: ["T1.posts", "userId"] },
161
+ "posts.title": { type: "column", path: ["T1.posts", "title"] },
162
+ "posts.content": { type: "column", path: ["T1.posts", "content"] },
163
+ "posts.viewCount": { type: "column", path: ["T1.posts", "viewCount"] },
164
+ "posts.publishedAt": { type: "column", path: ["T1.posts", "publishedAt"] },
165
+ },
166
+ joins: [
167
+ {
168
+ type: "select",
169
+ as: "T1.posts",
170
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
171
+ isSingle: false,
172
+ where: [
173
+ {
174
+ type: "eq",
175
+ source: { type: "column", path: ["T1.posts", "userId"] },
176
+ target: { type: "column", path: ["T1", "id"] },
177
+ },
178
+ ],
179
+ },
180
+ ],
181
+ },
182
+ select: {
183
+ postUserId: { type: "column", path: ["T2", "posts.userId"] },
184
+ },
185
+ });
186
+ });
187
+
188
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
189
+ const builder = createQueryBuilder(dialect);
190
+ expect(builder.build(def)).toMatchSql(expected.includeThenWrapThenSelect[dialect]);
191
+ });
192
+ });
193
+
194
+ describe("GROUP BY -> WRAP -> ORDER BY", () => {
195
+ const db = createTestDb();
196
+ const def = db
197
+ .user()
198
+ .select((item) => ({
199
+ name: item.name,
200
+ cnt: expr.count(item.id),
201
+ }))
202
+ .groupBy((item) => [item.name])
203
+ .wrap()
204
+ .orderBy((item) => item.cnt, "DESC")
205
+ .getSelectQueryDef();
206
+
207
+ it("Verify QueryDef", () => {
208
+ expect(def).toEqual({
209
+ type: "select",
210
+ as: "T2",
211
+ from: {
212
+ type: "select",
213
+ as: "T1",
214
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
215
+ select: {
216
+ name: { type: "column", path: ["T1", "name"] },
217
+ cnt: {
218
+ type: "count",
219
+ arg: { type: "column", path: ["T1", "id"] },
220
+ distinct: undefined,
221
+ },
222
+ },
223
+ groupBy: [{ type: "column", path: ["T1", "name"] }],
224
+ },
225
+ orderBy: [[{ type: "column", path: ["T2", "cnt"] }, "DESC"]],
226
+ });
227
+ });
228
+
229
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
230
+ const builder = createQueryBuilder(dialect);
231
+ expect(builder.build(def)).toMatchSql(expected.groupByThenWrapThenOrderBy[dialect]);
232
+ });
233
+ });
234
+ });
235
+
236
+ describe("SELECT - UNION", () => {
237
+ describe("Basic (2 items)", () => {
238
+ const db = createTestDb();
239
+ const qr1 = db.user().where((item) => [expr.eq(item.isActive, true)]);
240
+ const qr2 = db.user().where((item) => [expr.gt(item.age, 30)]);
241
+ const def = Queryable.union(qr1, qr2).getSelectQueryDef();
242
+
243
+ it("Verify QueryDef", () => {
244
+ expect(def).toEqual({
245
+ type: "select",
246
+ as: "T3",
247
+ from: [
248
+ {
249
+ type: "select",
250
+ as: "T1",
251
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
252
+ where: [
253
+ {
254
+ type: "eq",
255
+ source: { type: "column", path: ["T1", "isActive"] },
256
+ target: { type: "value", value: true },
257
+ },
258
+ ],
259
+ },
260
+ {
261
+ type: "select",
262
+ as: "T2",
263
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
264
+ where: [
265
+ {
266
+ type: "gt",
267
+ source: { type: "column", path: ["T2", "age"] },
268
+ target: { type: "value", value: 30 },
269
+ },
270
+ ],
271
+ },
272
+ ],
273
+ });
274
+ });
275
+
276
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
277
+ const builder = createQueryBuilder(dialect);
278
+ expect(builder.build(def)).toMatchSql(expected.unionBasic[dialect]);
279
+ });
280
+ });
281
+
282
+ describe("3 or more", () => {
283
+ const db = createTestDb();
284
+ const qr1 = db.user().where((item) => [expr.eq(item.age, 20)]);
285
+ const qr2 = db.user().where((item) => [expr.eq(item.age, 30)]);
286
+ const qr3 = db.user().where((item) => [expr.eq(item.age, 40)]);
287
+ const def = Queryable.union(qr1, qr2, qr3).getSelectQueryDef();
288
+
289
+ it("Verify QueryDef", () => {
290
+ expect(def).toEqual({
291
+ type: "select",
292
+ as: "T4",
293
+ from: [
294
+ {
295
+ type: "select",
296
+ as: "T1",
297
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
298
+ where: [
299
+ {
300
+ type: "eq",
301
+ source: { type: "column", path: ["T1", "age"] },
302
+ target: { type: "value", value: 20 },
303
+ },
304
+ ],
305
+ },
306
+ {
307
+ type: "select",
308
+ as: "T2",
309
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
310
+ where: [
311
+ {
312
+ type: "eq",
313
+ source: { type: "column", path: ["T2", "age"] },
314
+ target: { type: "value", value: 30 },
315
+ },
316
+ ],
317
+ },
318
+ {
319
+ type: "select",
320
+ as: "T3",
321
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
322
+ where: [
323
+ {
324
+ type: "eq",
325
+ source: { type: "column", path: ["T3", "age"] },
326
+ target: { type: "value", value: 40 },
327
+ },
328
+ ],
329
+ },
330
+ ],
331
+ });
332
+ });
333
+
334
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
335
+ const builder = createQueryBuilder(dialect);
336
+ expect(builder.build(def)).toMatchSql(expected.unionThree[dialect]);
337
+ });
338
+ });
339
+
340
+ describe("UNION -> WHERE (apply to each query)", () => {
341
+ const db = createTestDb();
342
+ const qr1 = db.user();
343
+ const qr2 = db.user();
344
+ const def = Queryable.union(qr1, qr2)
345
+ .where((item) => [expr.eq(item.isActive, true)])
346
+ .getSelectQueryDef();
347
+
348
+ it("Verify QueryDef", () => {
349
+ expect(def).toEqual({
350
+ type: "select",
351
+ as: "T3",
352
+ from: [
353
+ {
354
+ type: "select",
355
+ as: "T1",
356
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
357
+ where: [
358
+ {
359
+ type: "eq",
360
+ source: { type: "column", path: ["T1", "isActive"] },
361
+ target: { type: "value", value: true },
362
+ },
363
+ ],
364
+ },
365
+ {
366
+ type: "select",
367
+ as: "T2",
368
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
369
+ where: [
370
+ {
371
+ type: "eq",
372
+ source: { type: "column", path: ["T2", "isActive"] },
373
+ target: { type: "value", value: true },
374
+ },
375
+ ],
376
+ },
377
+ ],
378
+ });
379
+ });
380
+
381
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
382
+ const builder = createQueryBuilder(dialect);
383
+ expect(builder.build(def)).toMatchSql(expected.unionThenWhere[dialect]);
384
+ });
385
+ });
386
+
387
+ describe("UNION -> WRAP -> ORDER BY + LIMIT", () => {
388
+ const db = createTestDb();
389
+ const qr1 = db.user().where((item) => [expr.eq(item.isActive, true)]);
390
+ const qr2 = db.user().where((item) => [expr.gt(item.age, 30)]);
391
+ const def = Queryable.union(qr1, qr2)
392
+ .wrap()
393
+ .orderBy((item) => item.id, "DESC")
394
+ .limit(0, 10)
395
+ .getSelectQueryDef();
396
+
397
+ it("Verify QueryDef", () => {
398
+ expect(def).toEqual({
399
+ type: "select",
400
+ as: "T4",
401
+ from: {
402
+ type: "select",
403
+ as: "T3",
404
+ from: [
405
+ {
406
+ type: "select",
407
+ as: "T1",
408
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
409
+ where: [
410
+ {
411
+ type: "eq",
412
+ source: { type: "column", path: ["T1", "isActive"] },
413
+ target: { type: "value", value: true },
414
+ },
415
+ ],
416
+ },
417
+ {
418
+ type: "select",
419
+ as: "T2",
420
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
421
+ where: [
422
+ {
423
+ type: "gt",
424
+ source: { type: "column", path: ["T2", "age"] },
425
+ target: { type: "value", value: 30 },
426
+ },
427
+ ],
428
+ },
429
+ ],
430
+ },
431
+ orderBy: [[{ type: "column", path: ["T4", "id"] }, "DESC"]],
432
+ limit: [0, 10],
433
+ });
434
+ });
435
+
436
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
437
+ const builder = createQueryBuilder(dialect);
438
+ expect(builder.build(def)).toMatchSql(expected.unionThenWrapThenOrderByLimit[dialect]);
439
+ });
440
+ });
441
+
442
+ describe("UNION -> SELECT", () => {
443
+ const db = createTestDb();
444
+ const qr1 = db.user().select((item) => ({ id: item.id, name: item.name }));
445
+ const qr2 = db.user().select((item) => ({ id: item.id, name: item.name }));
446
+ const def = Queryable.union(qr1, qr2).getSelectQueryDef();
447
+
448
+ it("Verify QueryDef", () => {
449
+ expect(def).toEqual({
450
+ type: "select",
451
+ as: "T3",
452
+ from: [
453
+ {
454
+ type: "select",
455
+ as: "T1",
456
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
457
+ select: {
458
+ id: { type: "column", path: ["T1", "id"] },
459
+ name: { type: "column", path: ["T1", "name"] },
460
+ },
461
+ },
462
+ {
463
+ type: "select",
464
+ as: "T2",
465
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
466
+ select: {
467
+ id: { type: "column", path: ["T2", "id"] },
468
+ name: { type: "column", path: ["T2", "name"] },
469
+ },
470
+ },
471
+ ],
472
+ });
473
+ });
474
+
475
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
476
+ const builder = createQueryBuilder(dialect);
477
+ expect(builder.build(def)).toMatchSql(expected.unionThenSelect[dialect]);
478
+ });
479
+ });
480
+ });
481
+
482
+ //#region ========== SCALAR SUBQUERY ==========
483
+
484
+ describe("SELECT - SCALAR SUBQUERY (expr.subquery)", () => {
485
+ describe("Basic (COUNT)", () => {
486
+ const db = createTestDb();
487
+ const def = db
488
+ .user()
489
+ .select((u) => ({
490
+ id: u.id,
491
+ postCount: expr.subquery(
492
+ "number",
493
+ db
494
+ .post()
495
+ .where((p) => [expr.eq(p.userId, u.id)])
496
+ .select(() => ({ cnt: expr.count() })),
497
+ ),
498
+ }))
499
+ .getSelectQueryDef();
500
+
501
+ it("Verify QueryDef", () => {
502
+ expect(def).toEqual({
503
+ type: "select",
504
+ as: "T1",
505
+ from: { database: "TestDb", schema: "TestSchema", name: "User" },
506
+ select: {
507
+ id: { type: "column", path: ["T1", "id"] },
508
+ postCount: {
509
+ type: "subquery",
510
+ queryDef: {
511
+ type: "select",
512
+ as: "T2",
513
+ from: { database: "TestDb", schema: "TestSchema", name: "Post" },
514
+ where: [
515
+ {
516
+ type: "eq",
517
+ source: { type: "column", path: ["T2", "userId"] },
518
+ target: { type: "column", path: ["T1", "id"] },
519
+ },
520
+ ],
521
+ select: {
522
+ cnt: { type: "count" },
523
+ },
524
+ },
525
+ },
526
+ },
527
+ });
528
+ });
529
+
530
+ it.each(dialects)("[%s] Verify SQL", (dialect) => {
531
+ const builder = createQueryBuilder(dialect);
532
+ expect(builder.build(def)).toMatchSql(expected.scalarSubquery[dialect]);
533
+ });
534
+ });
535
+ });
536
+
537
+ //#endregion
@@ -0,0 +1,155 @@
1
+ import { mysql, pgsql, tsql } from "@simplysm/core-common";
2
+ import type { ExpectedSql } from "../setup/test-utils";
3
+
4
+ //#region ========== View - Basic ==========
5
+
6
+ export const viewSelect: ExpectedSql = {
7
+ mysql: mysql`SELECT * FROM \`TestDb\`.\`ActiveUsers\` AS \`T1\``,
8
+ mssql: tsql`SELECT * FROM [TestDb].[TestSchema].[ActiveUsers] AS [T1]`,
9
+ postgresql: pgsql`SELECT * FROM "TestSchema"."ActiveUsers" AS "T1"`,
10
+ };
11
+
12
+ export const viewSelectWhere: ExpectedSql = {
13
+ mysql: mysql`
14
+ SELECT * FROM \`TestDb\`.\`ActiveUsers\` AS \`T1\`
15
+ WHERE \`T1\`.\`age\` > 20
16
+ `,
17
+ mssql: tsql`
18
+ SELECT * FROM [TestDb].[TestSchema].[ActiveUsers] AS [T1]
19
+ WHERE [T1].[age] > 20
20
+ `,
21
+ postgresql: pgsql`
22
+ SELECT * FROM "TestSchema"."ActiveUsers" AS "T1"
23
+ WHERE "T1"."age" > 20
24
+ `,
25
+ };
26
+
27
+ export const viewSelectColumns: ExpectedSql = {
28
+ mysql: mysql`
29
+ SELECT
30
+ \`T1\`.\`id\` AS \`id\`,
31
+ \`T1\`.\`name\` AS \`name\`
32
+ FROM \`TestDb\`.\`ActiveUsers\` AS \`T1\`
33
+ `,
34
+ mssql: tsql`
35
+ SELECT
36
+ [T1].[id] AS [id],
37
+ [T1].[name] AS [name]
38
+ FROM [TestDb].[TestSchema].[ActiveUsers] AS [T1]
39
+ `,
40
+ postgresql: pgsql`
41
+ SELECT
42
+ "T1"."id" AS "id",
43
+ "T1"."name" AS "name"
44
+ FROM "TestSchema"."ActiveUsers" AS "T1"
45
+ `,
46
+ };
47
+
48
+ export const viewSelectOrderBy: ExpectedSql = {
49
+ mysql: mysql`
50
+ SELECT * FROM \`TestDb\`.\`ActiveUsers\` AS \`T1\`
51
+ ORDER BY \`T1\`.\`name\` ASC
52
+ `,
53
+ mssql: tsql`
54
+ SELECT * FROM [TestDb].[TestSchema].[ActiveUsers] AS [T1]
55
+ ORDER BY [T1].[name] ASC
56
+ `,
57
+ postgresql: pgsql`
58
+ SELECT * FROM "TestSchema"."ActiveUsers" AS "T1"
59
+ ORDER BY "T1"."name" ASC
60
+ `,
61
+ };
62
+
63
+ export const viewSelectOrderByLimit: ExpectedSql = {
64
+ mysql: mysql`
65
+ SELECT * FROM \`TestDb\`.\`ActiveUsers\` AS \`T1\`
66
+ ORDER BY \`T1\`.\`id\`
67
+ LIMIT 0, 10
68
+ `,
69
+ mssql: tsql`
70
+ SELECT * FROM [TestDb].[TestSchema].[ActiveUsers] AS [T1]
71
+ ORDER BY [T1].[id]
72
+ OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
73
+ `,
74
+ postgresql: pgsql`
75
+ SELECT * FROM "TestSchema"."ActiveUsers" AS "T1"
76
+ ORDER BY "T1"."id"
77
+ LIMIT 10 OFFSET 0
78
+ `,
79
+ };
80
+
81
+ //#endregion
82
+
83
+ //#region ========== View - SELECT가 있는 뷰 ==========
84
+
85
+ export const userSummarySelect: ExpectedSql = {
86
+ mysql: mysql`SELECT * FROM \`TestDb\`.\`UserSummary\` AS \`T1\``,
87
+ mssql: tsql`SELECT * FROM [TestDb].[TestSchema].[UserSummary] AS [T1]`,
88
+ postgresql: pgsql`SELECT * FROM "TestSchema"."UserSummary" AS "T1"`,
89
+ };
90
+
91
+ export const userSummarySelectColumn: ExpectedSql = {
92
+ mysql: mysql`
93
+ SELECT \`T1\`.\`name\` AS \`userName\`
94
+ FROM \`TestDb\`.\`UserSummary\` AS \`T1\`
95
+ `,
96
+ mssql: tsql`
97
+ SELECT [T1].[name] AS [userName]
98
+ FROM [TestDb].[TestSchema].[UserSummary] AS [T1]
99
+ `,
100
+ postgresql: pgsql`
101
+ SELECT "T1"."name" AS "userName"
102
+ FROM "TestSchema"."UserSummary" AS "T1"
103
+ `,
104
+ };
105
+
106
+ //#endregion
107
+
108
+ //#region ========== View - DDL ==========
109
+
110
+ export const createActiveUsersView: ExpectedSql = {
111
+ mysql: mysql`
112
+ CREATE OR REPLACE VIEW \`TestDb\`.\`ActiveUsers\` AS
113
+ SELECT * FROM \`TestDb\`.\`User\` AS \`T1\`
114
+ WHERE \`T1\`.\`isActive\` <=> TRUE
115
+ `,
116
+ mssql: tsql`
117
+ CREATE OR ALTER VIEW [TestDb].[TestSchema].[ActiveUsers] AS
118
+ SELECT * FROM [TestDb].[TestSchema].[User] AS [T1]
119
+ WHERE (([T1].[isActive] IS NULL AND 1 IS NULL) OR [T1].[isActive] = 1)
120
+ `,
121
+ postgresql: pgsql`
122
+ CREATE OR REPLACE VIEW "TestSchema"."ActiveUsers" AS
123
+ SELECT * FROM "TestSchema"."User" AS "T1"
124
+ WHERE "T1"."isActive" IS NOT DISTINCT FROM TRUE
125
+ `,
126
+ };
127
+
128
+ export const createUserSummaryView: ExpectedSql = {
129
+ mysql: mysql`
130
+ CREATE OR REPLACE VIEW \`TestDb\`.\`UserSummary\` AS
131
+ SELECT
132
+ \`T1\`.\`id\` AS \`id\`,
133
+ \`T1\`.\`name\` AS \`name\`,
134
+ \`T1\`.\`email\` AS \`email\`
135
+ FROM \`TestDb\`.\`User\` AS \`T1\`
136
+ `,
137
+ mssql: tsql`
138
+ CREATE OR ALTER VIEW [TestDb].[TestSchema].[UserSummary] AS
139
+ SELECT
140
+ [T1].[id] AS [id],
141
+ [T1].[name] AS [name],
142
+ [T1].[email] AS [email]
143
+ FROM [TestDb].[TestSchema].[User] AS [T1]
144
+ `,
145
+ postgresql: pgsql`
146
+ CREATE OR REPLACE VIEW "TestSchema"."UserSummary" AS
147
+ SELECT
148
+ "T1"."id" AS "id",
149
+ "T1"."name" AS "name",
150
+ "T1"."email" AS "email"
151
+ FROM "TestSchema"."User" AS "T1"
152
+ `,
153
+ };
154
+
155
+ //#endregion