@simplysm/orm-common 13.0.75 → 13.0.77

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 (135) hide show
  1. package/README.md +575 -50
  2. package/dist/create-db-context.d.ts +1 -1
  3. package/dist/create-db-context.d.ts.map +1 -1
  4. package/dist/create-db-context.js +34 -27
  5. package/dist/create-db-context.js.map +1 -1
  6. package/dist/ddl/initialize.js +4 -4
  7. package/dist/ddl/initialize.js.map +1 -1
  8. package/dist/ddl/relation-ddl.d.ts +7 -7
  9. package/dist/ddl/relation-ddl.d.ts.map +1 -1
  10. package/dist/ddl/relation-ddl.js +18 -18
  11. package/dist/ddl/relation-ddl.js.map +1 -1
  12. package/dist/ddl/schema-ddl.d.ts +1 -1
  13. package/dist/ddl/schema-ddl.d.ts.map +1 -1
  14. package/dist/ddl/schema-ddl.js +2 -2
  15. package/dist/ddl/schema-ddl.js.map +1 -1
  16. package/dist/ddl/table-ddl.js +2 -2
  17. package/dist/ddl/table-ddl.js.map +1 -1
  18. package/dist/exec/queryable.d.ts +24 -24
  19. package/dist/exec/queryable.d.ts.map +1 -1
  20. package/dist/exec/queryable.js +37 -37
  21. package/dist/exec/queryable.js.map +1 -1
  22. package/dist/expr/expr-unit.js +1 -1
  23. package/dist/expr/expr-unit.js.map +1 -1
  24. package/dist/expr/expr.d.ts +9 -9
  25. package/dist/expr/expr.d.ts.map +1 -1
  26. package/dist/expr/expr.js +10 -10
  27. package/dist/expr/expr.js.map +1 -1
  28. package/dist/query-builder/base/expr-renderer-base.d.ts +2 -2
  29. package/dist/query-builder/base/expr-renderer-base.d.ts.map +1 -1
  30. package/dist/query-builder/base/query-builder-base.d.ts +7 -15
  31. package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
  32. package/dist/query-builder/base/query-builder-base.js +2 -2
  33. package/dist/query-builder/base/query-builder-base.js.map +1 -1
  34. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +4 -4
  35. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
  36. package/dist/query-builder/mssql/mssql-expr-renderer.js +8 -8
  37. package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
  38. package/dist/query-builder/mssql/mssql-query-builder.d.ts +7 -7
  39. package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
  40. package/dist/query-builder/mssql/mssql-query-builder.js +7 -7
  41. package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
  42. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +4 -4
  43. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
  44. package/dist/query-builder/mysql/mysql-expr-renderer.js +9 -9
  45. package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
  46. package/dist/query-builder/mysql/mysql-query-builder.d.ts +7 -7
  47. package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
  48. package/dist/query-builder/mysql/mysql-query-builder.js +11 -11
  49. package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
  50. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +4 -4
  51. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
  52. package/dist/query-builder/postgresql/postgresql-expr-renderer.js +8 -8
  53. package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
  54. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +7 -7
  55. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
  56. package/dist/query-builder/postgresql/postgresql-query-builder.js +7 -7
  57. package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
  58. package/dist/schema/procedure-builder.d.ts +1 -1
  59. package/dist/schema/table-builder.d.ts +1 -1
  60. package/dist/schema/table-builder.d.ts.map +1 -1
  61. package/dist/schema/table-builder.js +1 -1
  62. package/dist/schema/view-builder.d.ts +1 -1
  63. package/dist/schema/view-builder.d.ts.map +1 -1
  64. package/dist/schema/view-builder.js +1 -1
  65. package/dist/types/db-context-def.d.ts +18 -18
  66. package/dist/types/db-context-def.d.ts.map +1 -1
  67. package/dist/types/expr.d.ts +6 -6
  68. package/dist/types/expr.d.ts.map +1 -1
  69. package/dist/types/query-def.d.ts +15 -15
  70. package/dist/types/query-def.d.ts.map +1 -1
  71. package/dist/types/query-def.js +6 -6
  72. package/dist/utils/result-parser.d.ts.map +1 -1
  73. package/dist/utils/result-parser.js +44 -16
  74. package/dist/utils/result-parser.js.map +1 -1
  75. package/package.json +2 -2
  76. package/src/create-db-context.ts +36 -29
  77. package/src/ddl/initialize.ts +4 -4
  78. package/src/ddl/relation-ddl.ts +16 -16
  79. package/src/ddl/schema-ddl.ts +2 -2
  80. package/src/ddl/table-ddl.ts +2 -2
  81. package/src/exec/queryable.ts +58 -58
  82. package/src/expr/expr-unit.ts +1 -1
  83. package/src/expr/expr.ts +13 -13
  84. package/src/query-builder/base/expr-renderer-base.ts +2 -2
  85. package/src/query-builder/base/query-builder-base.ts +18 -14
  86. package/src/query-builder/mssql/mssql-expr-renderer.ts +11 -10
  87. package/src/query-builder/mssql/mssql-query-builder.ts +13 -13
  88. package/src/query-builder/mysql/mysql-expr-renderer.ts +12 -11
  89. package/src/query-builder/mysql/mysql-query-builder.ts +17 -17
  90. package/src/query-builder/postgresql/postgresql-expr-renderer.ts +11 -10
  91. package/src/query-builder/postgresql/postgresql-query-builder.ts +13 -13
  92. package/src/schema/procedure-builder.ts +1 -1
  93. package/src/schema/table-builder.ts +1 -1
  94. package/src/schema/view-builder.ts +1 -1
  95. package/src/types/db-context-def.ts +18 -18
  96. package/src/types/expr.ts +6 -6
  97. package/src/types/query-def.ts +31 -31
  98. package/src/utils/result-parser.ts +60 -16
  99. package/tests/db-context/create-db-context.spec.ts +6 -37
  100. package/tests/db-context/define-db-context.spec.ts +0 -51
  101. package/tests/ddl/basic.expected.ts +8 -8
  102. package/tests/ddl/basic.spec.ts +24 -181
  103. package/tests/ddl/column-builder.spec.ts +0 -112
  104. package/tests/ddl/index-builder.spec.ts +10 -64
  105. package/tests/ddl/procedure-builder.spec.ts +0 -106
  106. package/tests/ddl/relation-builder.spec.ts +4 -205
  107. package/tests/ddl/table-builder.spec.ts +0 -34
  108. package/tests/ddl/view-builder.spec.ts +0 -60
  109. package/tests/dml/delete.spec.ts +0 -33
  110. package/tests/dml/insert.spec.ts +0 -78
  111. package/tests/dml/update.spec.ts +2 -98
  112. package/tests/dml/upsert.spec.ts +0 -52
  113. package/tests/errors/queryable-errors.spec.ts +0 -51
  114. package/tests/escape.spec.ts +0 -41
  115. package/tests/examples/pivot.spec.ts +0 -333
  116. package/tests/examples/sampling.spec.ts +0 -63
  117. package/tests/examples/unpivot.spec.ts +0 -65
  118. package/tests/exec/search-parser.spec.ts +0 -16
  119. package/tests/expr/comparison.spec.ts +0 -66
  120. package/tests/expr/conditional.expected.ts +2 -2
  121. package/tests/expr/conditional.spec.ts +8 -35
  122. package/tests/expr/date.spec.ts +5 -72
  123. package/tests/expr/math.spec.ts +0 -47
  124. package/tests/expr/string.spec.ts +0 -56
  125. package/tests/expr/utility.spec.ts +0 -27
  126. package/tests/select/basic.spec.ts +5 -74
  127. package/tests/select/filter.spec.ts +0 -114
  128. package/tests/select/group.spec.ts +0 -85
  129. package/tests/select/join.spec.ts +0 -113
  130. package/tests/select/order.spec.ts +0 -49
  131. package/tests/select/subquery.spec.ts +0 -96
  132. package/tests/select/window.spec.ts +0 -185
  133. package/tests/types/nullable-queryable-record.spec.ts +0 -48
  134. package/tests/utils/result-parser-perf.spec.ts +0 -67
  135. package/tests/utils/result-parser.spec.ts +4 -38
@@ -41,42 +41,6 @@ describe("UPDATE - Basic", () => {
41
41
  });
42
42
  });
43
43
 
44
- describe("UPDATE multiple columns", () => {
45
- const db = createTestDb();
46
-
47
- const def = db
48
- .employee()
49
- .where((e) => [expr.eq(e.id, 1)])
50
- .getUpdateQueryDef(() => ({
51
- name: expr.val("string", "New Name"),
52
- departmentId: expr.val("number", 2),
53
- }));
54
-
55
- it("Verify QueryDef", () => {
56
- expect(def).toEqual({
57
- type: "update",
58
- table: { database: "TestDb", schema: "TestSchema", name: "Employee" },
59
- as: "T1",
60
- record: {
61
- name: { type: "value", value: "New Name" },
62
- departmentId: { type: "value", value: 2 },
63
- },
64
- where: [
65
- {
66
- type: "eq",
67
- source: { type: "column", path: ["T1", "id"] },
68
- target: { type: "value", value: 1 },
69
- },
70
- ],
71
- });
72
- });
73
-
74
- it.each(dialects)("[%s] should validate SQL", (dialect) => {
75
- const builder = createQueryBuilder(dialect);
76
- expect(builder.build(def)).toMatchSql(expected.updateMultiCol[dialect]);
77
- });
78
- });
79
-
80
44
  describe("UPDATE with literal values (without expr.val)", () => {
81
45
  const db = createTestDb();
82
46
 
@@ -153,45 +117,6 @@ describe("UPDATE - Basic", () => {
153
117
  });
154
118
  });
155
119
 
156
- describe("UPDATE referencing current values (e.g., count = count + 1)", () => {
157
- const db = createTestDb();
158
-
159
- const def = db
160
- .employee()
161
- .where((e) => [expr.eq(e.id, 1)])
162
- .getUpdateQueryDef((e) => ({
163
- // managerId = managerId + 1 (example)
164
- managerId: expr.raw("number")`${e.managerId} + 1`,
165
- }));
166
-
167
- it("Verify QueryDef", () => {
168
- expect(def).toEqual({
169
- type: "update",
170
- table: { database: "TestDb", schema: "TestSchema", name: "Employee" },
171
- as: "T1",
172
- record: {
173
- managerId: {
174
- type: "raw",
175
- sql: "$1 + 1",
176
- params: [{ type: "column", path: ["T1", "managerId"] }],
177
- },
178
- },
179
- where: [
180
- {
181
- type: "eq",
182
- source: { type: "column", path: ["T1", "id"] },
183
- target: { type: "value", value: 1 },
184
- },
185
- ],
186
- });
187
- });
188
-
189
- it.each(dialects)("[%s] should validate SQL", (dialect) => {
190
- const builder = createQueryBuilder(dialect);
191
- expect(builder.build(def)).toMatchSql(expected.updateWithRef[dialect]);
192
- });
193
- });
194
-
195
120
  describe("Specify output column", () => {
196
121
  const db = createTestDb();
197
122
 
@@ -277,14 +202,14 @@ describe("FK switch", () => {
277
202
 
278
203
  const def = db.getSwitchFkQueryDef(
279
204
  { database: "TestDb", schema: "TestSchema", name: "Employee" },
280
- "off",
205
+ false,
281
206
  );
282
207
 
283
208
  it("Verify QueryDef", () => {
284
209
  expect(def).toEqual({
285
210
  type: "switchFk",
286
211
  table: { database: "TestDb", schema: "TestSchema", name: "Employee" },
287
- switch: "off",
212
+ enabled: false,
288
213
  });
289
214
  });
290
215
 
@@ -294,25 +219,4 @@ describe("FK switch", () => {
294
219
  });
295
220
  });
296
221
 
297
- describe("FK on", () => {
298
- const db = createTestDb();
299
-
300
- const def = db.getSwitchFkQueryDef(
301
- { database: "TestDb", schema: "TestSchema", name: "Employee" },
302
- "on",
303
- );
304
-
305
- it("Verify QueryDef", () => {
306
- expect(def).toEqual({
307
- type: "switchFk",
308
- table: { database: "TestDb", schema: "TestSchema", name: "Employee" },
309
- switch: "on",
310
- });
311
- });
312
-
313
- it.each(dialects)("[%s] should validate SQL", (dialect) => {
314
- const builder = createQueryBuilder(dialect);
315
- expect(builder.build(def)).toMatchSql(expected.fkOn[dialect]);
316
- });
317
- });
318
222
  });
@@ -146,58 +146,6 @@ describe("UPSERT - Basic", () => {
146
146
  });
147
147
  });
148
148
 
149
- describe("Complex WHERE condition", () => {
150
- const db = createTestDb();
151
- const def = db
152
- .employee()
153
- .where((e) => [expr.eq(e.name, "Gildong Hong"), expr.eq(e.departmentId, 1)])
154
- .getUpsertQueryDef(
155
- () => ({ managerId: expr.val("number", 10) }),
156
- (upd) => ({
157
- name: expr.val("string", "Gildong Hong"),
158
- departmentId: expr.val("number", 1),
159
- managerId: upd.managerId,
160
- }),
161
- );
162
-
163
- it("Verify QueryDef", () => {
164
- expect(def).toEqual({
165
- type: "upsert",
166
- table: { database: "TestDb", schema: "TestSchema", name: "Employee" },
167
- existsSelectQuery: {
168
- type: "select",
169
- as: "T1",
170
- from: { database: "TestDb", schema: "TestSchema", name: "Employee" },
171
- where: [
172
- {
173
- type: "eq",
174
- source: { type: "column", path: ["T1", "name"] },
175
- target: { type: "value", value: "Gildong Hong" },
176
- },
177
- {
178
- type: "eq",
179
- source: { type: "column", path: ["T1", "departmentId"] },
180
- target: { type: "value", value: 1 },
181
- },
182
- ],
183
- },
184
- updateRecord: {
185
- managerId: { type: "value", value: 10 },
186
- },
187
- insertRecord: {
188
- name: { type: "value", value: "Gildong Hong" },
189
- departmentId: { type: "value", value: 1 },
190
- managerId: { type: "value", value: 10 },
191
- },
192
- });
193
- });
194
-
195
- it.each(dialects)("[%s] should validate SQL", (dialect) => {
196
- const builder = createQueryBuilder(dialect);
197
- expect(builder.build(def)).toMatchSql(expected.upsertMultiWhere[dialect]);
198
- });
199
- });
200
-
201
149
  describe("UPSERT with literal values (without expr.val)", () => {
202
150
  const db = createTestDb();
203
151
  const def = db
@@ -10,9 +10,6 @@ describe("Queryable error cases", () => {
10
10
  expect(() => expr.and([])).toThrow("empty arrays are not allowed");
11
11
  });
12
12
 
13
- it("ArgumentError when calling or() with empty array", () => {
14
- expect(() => expr.or([])).toThrow("empty arrays are not allowed");
15
- });
16
13
  });
17
14
 
18
15
  describe("executable errors", () => {
@@ -97,21 +94,6 @@ describe("Queryable error cases", () => {
97
94
  }).toThrow("inQuery subquery must SELECT only a single column.");
98
95
  });
99
96
 
100
- it("Error when using subquery without column specification", () => {
101
- const db = createTestDb();
102
-
103
- expect(() => {
104
- db.user()
105
- .where((u) => [
106
- expr.inQuery(
107
- u.id,
108
- // @ts-expect-error - subquery without SELECT
109
- db.post(),
110
- ),
111
- ])
112
- .getSelectQueryDef();
113
- }).toThrow("inQuery subquery must SELECT only a single column.");
114
- });
115
97
  });
116
98
 
117
99
  describe("countAsync() errors", () => {
@@ -141,37 +123,4 @@ describe("Queryable error cases", () => {
141
123
  });
142
124
  });
143
125
 
144
- describe("RecursiveQueryable.union() errors", () => {
145
- it("Error when calling union with a single queryable", () => {
146
- const db = createTestDb();
147
-
148
- expect(() => {
149
- db.employee()
150
- .where((e) => [expr.null(e.managerId)])
151
- .recursive((cte) => cte.union(db.employee()));
152
- }).toThrow("union requires at least 2 queryables.");
153
- });
154
-
155
- it("Error when calling union with zero queryables", () => {
156
- const db = createTestDb();
157
-
158
- expect(() => {
159
- db.employee()
160
- .where((e) => [expr.null(e.managerId)])
161
- .recursive((cte) => cte.union());
162
- }).toThrow("union requires at least 2 queryables.");
163
- });
164
- });
165
-
166
- describe("JoinQueryable.union() errors", () => {
167
- it("Error when calling union with a single queryable inside join", () => {
168
- const db = createTestDb();
169
-
170
- expect(() => {
171
- db.user().join("posts", (j, u) =>
172
- j.union(db.post().where((p) => [expr.eq(p.userId, u.id)])),
173
- );
174
- }).toThrow("union requires at least 2 queryables.");
175
- });
176
- });
177
126
  });
@@ -23,45 +23,14 @@ describe("MysqlExprRenderer.escapeString", () => {
23
23
 
24
24
  //#endregion
25
25
 
26
- //#region ========== Control Character Escaping ==========
27
-
28
- it("should escape newlines", () => {
29
- const result = renderer.escapeString("line1\nline2");
30
- expect(result).toBe("line1\\nline2");
31
- });
32
-
33
- it("should escape carriage returns", () => {
34
- const result = renderer.escapeString("line1\rline2");
35
- expect(result).toBe("line1\\rline2");
36
- });
37
-
38
- it("should escape tabs", () => {
39
- const result = renderer.escapeString("col1\tcol2");
40
- expect(result).toBe("col1\\tcol2");
41
- });
42
-
43
- //#endregion
44
-
45
26
  //#region ========== Combined Attack Test ==========
46
27
 
47
- it("should defend against SQL injection attempts", () => {
48
- const malicious = "'; DROP TABLE users; --";
49
- const result = renderer.escapeString(malicious);
50
- expect(result).toBe("''; DROP TABLE users; --");
51
- });
52
-
53
28
  it("should defend against backslash + quote combination", () => {
54
29
  const malicious = "\\'";
55
30
  const result = renderer.escapeString(malicious);
56
31
  expect(result).toBe("\\\\''");
57
32
  });
58
33
 
59
- it("should defend against NULL byte + SQL comment combination", () => {
60
- const malicious = "admin\0-- ";
61
- const result = renderer.escapeString(malicious);
62
- expect(result).toBe("admin\\0-- ");
63
- });
64
-
65
34
  //#endregion
66
35
  });
67
36
 
@@ -73,16 +42,6 @@ describe("MysqlExprRenderer.escapeValue", () => {
73
42
  expect(result).toBe("'O''Reilly'");
74
43
  });
75
44
 
76
- it("correctly escapes string containing backslashes", () => {
77
- const result = renderer.escapeValue("C:\\path");
78
- expect(result).toBe("'C:\\\\path'");
79
- });
80
-
81
- it("defends against SQL injection attempts", () => {
82
- const result = renderer.escapeValue("'; DROP TABLE users; --");
83
- expect(result).toBe("'''; DROP TABLE users; --'");
84
- });
85
-
86
45
  it("returns 'NULL' string for null", () => {
87
46
  const result = renderer.escapeValue(null);
88
47
  expect(result).toBe("NULL");
@@ -68,339 +68,6 @@ describe("SELECT - PIVOT (groupBy + switch)", () => {
68
68
  });
69
69
  });
70
70
 
71
- describe("COUNT", () => {
72
- const db = createTestDb();
73
- const def = db
74
- .sales()
75
- .groupBy((item) => [item.id, item.category])
76
- .select((item) => ({
77
- id: item.id,
78
- category: item.category,
79
- y2020: expr.count(expr.if(expr.eq(item.year, 2020), item.amount, undefined)),
80
- y2021: expr.count(expr.if(expr.eq(item.year, 2021), item.amount, undefined)),
81
- }))
82
- .getSelectQueryDef();
83
-
84
- it("Verify QueryDef", () => {
85
- expect(def).toEqual({
86
- type: "select",
87
- as: "T1",
88
- from: { database: "TestDb", schema: "TestSchema", name: "Sales" },
89
- groupBy: [
90
- { type: "column", path: ["T1", "id"] },
91
- { type: "column", path: ["T1", "category"] },
92
- ],
93
- select: {
94
- id: { type: "column", path: ["T1", "id"] },
95
- category: { type: "column", path: ["T1", "category"] },
96
- y2020: {
97
- type: "count",
98
- arg: {
99
- type: "if",
100
- condition: {
101
- type: "eq",
102
- source: { type: "column", path: ["T1", "year"] },
103
- target: { type: "value", value: 2020 },
104
- },
105
- then: { type: "column", path: ["T1", "amount"] },
106
- else: { type: "value", value: undefined },
107
- },
108
- },
109
- y2021: {
110
- type: "count",
111
- arg: {
112
- type: "if",
113
- condition: {
114
- type: "eq",
115
- source: { type: "column", path: ["T1", "year"] },
116
- target: { type: "value", value: 2021 },
117
- },
118
- then: { type: "column", path: ["T1", "amount"] },
119
- else: { type: "value", value: undefined },
120
- },
121
- },
122
- },
123
- });
124
- });
125
-
126
- it.each(dialects)("[%s] Verify SQL", (dialect) => {
127
- const builder = createQueryBuilder(dialect);
128
- expect(builder.build(def)).toMatchSql(expected.pivotCount[dialect]);
129
- });
130
- });
131
-
132
- describe("AVG", () => {
133
- const db = createTestDb();
134
- const def = db
135
- .sales()
136
- .groupBy((item) => [item.id, item.category])
137
- .select((item) => ({
138
- id: item.id,
139
- category: item.category,
140
- y2020: expr.avg(expr.if(expr.eq(item.year, 2020), item.amount, undefined)),
141
- y2021: expr.avg(expr.if(expr.eq(item.year, 2021), item.amount, undefined)),
142
- }))
143
- .getSelectQueryDef();
144
-
145
- it("Verify QueryDef", () => {
146
- expect(def).toEqual({
147
- type: "select",
148
- as: "T1",
149
- from: { database: "TestDb", schema: "TestSchema", name: "Sales" },
150
- groupBy: [
151
- { type: "column", path: ["T1", "id"] },
152
- { type: "column", path: ["T1", "category"] },
153
- ],
154
- select: {
155
- id: { type: "column", path: ["T1", "id"] },
156
- category: { type: "column", path: ["T1", "category"] },
157
- y2020: {
158
- type: "avg",
159
- arg: {
160
- type: "if",
161
- condition: {
162
- type: "eq",
163
- source: { type: "column", path: ["T1", "year"] },
164
- target: { type: "value", value: 2020 },
165
- },
166
- then: { type: "column", path: ["T1", "amount"] },
167
- else: { type: "value", value: undefined },
168
- },
169
- },
170
- y2021: {
171
- type: "avg",
172
- arg: {
173
- type: "if",
174
- condition: {
175
- type: "eq",
176
- source: { type: "column", path: ["T1", "year"] },
177
- target: { type: "value", value: 2021 },
178
- },
179
- then: { type: "column", path: ["T1", "amount"] },
180
- else: { type: "value", value: undefined },
181
- },
182
- },
183
- },
184
- });
185
- });
186
-
187
- it.each(dialects)("[%s] Verify SQL", (dialect) => {
188
- const builder = createQueryBuilder(dialect);
189
- expect(builder.build(def)).toMatchSql(expected.pivotAvg[dialect]);
190
- });
191
- });
192
-
193
- describe("MAX", () => {
194
- const db = createTestDb();
195
- const def = db
196
- .sales()
197
- .groupBy((item) => [item.id, item.category])
198
- .select((item) => ({
199
- id: item.id,
200
- category: item.category,
201
- y2020: expr.max(expr.if(expr.eq(item.year, 2020), item.amount, undefined)),
202
- y2021: expr.max(expr.if(expr.eq(item.year, 2021), item.amount, undefined)),
203
- }))
204
- .getSelectQueryDef();
205
-
206
- it("Verify QueryDef", () => {
207
- expect(def).toEqual({
208
- type: "select",
209
- as: "T1",
210
- from: { database: "TestDb", schema: "TestSchema", name: "Sales" },
211
- groupBy: [
212
- { type: "column", path: ["T1", "id"] },
213
- { type: "column", path: ["T1", "category"] },
214
- ],
215
- select: {
216
- id: { type: "column", path: ["T1", "id"] },
217
- category: { type: "column", path: ["T1", "category"] },
218
- y2020: {
219
- type: "max",
220
- arg: {
221
- type: "if",
222
- condition: {
223
- type: "eq",
224
- source: { type: "column", path: ["T1", "year"] },
225
- target: { type: "value", value: 2020 },
226
- },
227
- then: { type: "column", path: ["T1", "amount"] },
228
- else: { type: "value", value: undefined },
229
- },
230
- },
231
- y2021: {
232
- type: "max",
233
- arg: {
234
- type: "if",
235
- condition: {
236
- type: "eq",
237
- source: { type: "column", path: ["T1", "year"] },
238
- target: { type: "value", value: 2021 },
239
- },
240
- then: { type: "column", path: ["T1", "amount"] },
241
- else: { type: "value", value: undefined },
242
- },
243
- },
244
- },
245
- });
246
- });
247
-
248
- it.each(dialects)("[%s] Verify SQL", (dialect) => {
249
- const builder = createQueryBuilder(dialect);
250
- expect(builder.build(def)).toMatchSql(expected.pivotMax[dialect]);
251
- });
252
- });
253
-
254
- describe("MIN", () => {
255
- const db = createTestDb();
256
- const def = db
257
- .sales()
258
- .groupBy((item) => [item.id, item.category])
259
- .select((item) => ({
260
- id: item.id,
261
- category: item.category,
262
- y2020: expr.min(expr.if(expr.eq(item.year, 2020), item.amount, undefined)),
263
- y2021: expr.min(expr.if(expr.eq(item.year, 2021), item.amount, undefined)),
264
- }))
265
- .getSelectQueryDef();
266
-
267
- it("Verify QueryDef", () => {
268
- expect(def).toEqual({
269
- type: "select",
270
- as: "T1",
271
- from: { database: "TestDb", schema: "TestSchema", name: "Sales" },
272
- groupBy: [
273
- { type: "column", path: ["T1", "id"] },
274
- { type: "column", path: ["T1", "category"] },
275
- ],
276
- select: {
277
- id: { type: "column", path: ["T1", "id"] },
278
- category: { type: "column", path: ["T1", "category"] },
279
- y2020: {
280
- type: "min",
281
- arg: {
282
- type: "if",
283
- condition: {
284
- type: "eq",
285
- source: { type: "column", path: ["T1", "year"] },
286
- target: { type: "value", value: 2020 },
287
- },
288
- then: { type: "column", path: ["T1", "amount"] },
289
- else: { type: "value", value: undefined },
290
- },
291
- },
292
- y2021: {
293
- type: "min",
294
- arg: {
295
- type: "if",
296
- condition: {
297
- type: "eq",
298
- source: { type: "column", path: ["T1", "year"] },
299
- target: { type: "value", value: 2021 },
300
- },
301
- then: { type: "column", path: ["T1", "amount"] },
302
- else: { type: "value", value: undefined },
303
- },
304
- },
305
- },
306
- });
307
- });
308
-
309
- it.each(dialects)("[%s] Verify SQL", (dialect) => {
310
- const builder = createQueryBuilder(dialect);
311
- expect(builder.build(def)).toMatchSql(expected.pivotMin[dialect]);
312
- });
313
- });
314
-
315
- describe("3 or more pivot values", () => {
316
- const db = createTestDb();
317
- const def = db
318
- .sales()
319
- .groupBy((item) => [item.id, item.category])
320
- .select((item) => ({
321
- id: item.id,
322
- category: item.category,
323
- y2019: expr.sum(expr.if(expr.eq(item.year, 2019), item.amount, undefined)),
324
- y2020: expr.sum(expr.if(expr.eq(item.year, 2020), item.amount, undefined)),
325
- y2021: expr.sum(expr.if(expr.eq(item.year, 2021), item.amount, undefined)),
326
- y2022: expr.sum(expr.if(expr.eq(item.year, 2022), item.amount, undefined)),
327
- }))
328
- .getSelectQueryDef();
329
-
330
- it("Verify QueryDef", () => {
331
- expect(def).toEqual({
332
- type: "select",
333
- as: "T1",
334
- from: { database: "TestDb", schema: "TestSchema", name: "Sales" },
335
- groupBy: [
336
- { type: "column", path: ["T1", "id"] },
337
- { type: "column", path: ["T1", "category"] },
338
- ],
339
- select: {
340
- id: { type: "column", path: ["T1", "id"] },
341
- category: { type: "column", path: ["T1", "category"] },
342
- y2019: {
343
- type: "sum",
344
- arg: {
345
- type: "if",
346
- condition: {
347
- type: "eq",
348
- source: { type: "column", path: ["T1", "year"] },
349
- target: { type: "value", value: 2019 },
350
- },
351
- then: { type: "column", path: ["T1", "amount"] },
352
- else: { type: "value", value: undefined },
353
- },
354
- },
355
- y2020: {
356
- type: "sum",
357
- arg: {
358
- type: "if",
359
- condition: {
360
- type: "eq",
361
- source: { type: "column", path: ["T1", "year"] },
362
- target: { type: "value", value: 2020 },
363
- },
364
- then: { type: "column", path: ["T1", "amount"] },
365
- else: { type: "value", value: undefined },
366
- },
367
- },
368
- y2021: {
369
- type: "sum",
370
- arg: {
371
- type: "if",
372
- condition: {
373
- type: "eq",
374
- source: { type: "column", path: ["T1", "year"] },
375
- target: { type: "value", value: 2021 },
376
- },
377
- then: { type: "column", path: ["T1", "amount"] },
378
- else: { type: "value", value: undefined },
379
- },
380
- },
381
- y2022: {
382
- type: "sum",
383
- arg: {
384
- type: "if",
385
- condition: {
386
- type: "eq",
387
- source: { type: "column", path: ["T1", "year"] },
388
- target: { type: "value", value: 2022 },
389
- },
390
- then: { type: "column", path: ["T1", "amount"] },
391
- else: { type: "value", value: undefined },
392
- },
393
- },
394
- },
395
- });
396
- });
397
-
398
- it.each(dialects)("[%s] Verify SQL", (dialect) => {
399
- const builder = createQueryBuilder(dialect);
400
- expect(builder.build(def)).toMatchSql(expected.pivotMultipleYears[dialect]);
401
- });
402
- });
403
-
404
71
  describe("String pivot column", () => {
405
72
  const db = createTestDb();
406
73
  const def = db