pqb 0.0.1

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 (122) hide show
  1. package/dist/index.d.ts +3630 -0
  2. package/dist/index.esm.js +4587 -0
  3. package/dist/index.esm.js.map +1 -0
  4. package/dist/index.js +4691 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +59 -0
  7. package/rollup.config.js +35 -0
  8. package/src/adapter.test.ts +10 -0
  9. package/src/adapter.ts +171 -0
  10. package/src/columnSchema/array.ts +21 -0
  11. package/src/columnSchema/boolean.ts +10 -0
  12. package/src/columnSchema/columnType.test.ts +129 -0
  13. package/src/columnSchema/columnType.ts +77 -0
  14. package/src/columnSchema/columnTypes.ts +145 -0
  15. package/src/columnSchema/columnsSchema.test.ts +32 -0
  16. package/src/columnSchema/columnsSchema.ts +100 -0
  17. package/src/columnSchema/commonMethods.ts +130 -0
  18. package/src/columnSchema/dateTime.ts +104 -0
  19. package/src/columnSchema/enum.ts +13 -0
  20. package/src/columnSchema/index.ts +11 -0
  21. package/src/columnSchema/json/array.ts +55 -0
  22. package/src/columnSchema/json/discriminatedUnion.ts +91 -0
  23. package/src/columnSchema/json/enum.ts +29 -0
  24. package/src/columnSchema/json/instanceOf.ts +16 -0
  25. package/src/columnSchema/json/intersection.ts +23 -0
  26. package/src/columnSchema/json/lazy.ts +22 -0
  27. package/src/columnSchema/json/literal.ts +12 -0
  28. package/src/columnSchema/json/map.ts +29 -0
  29. package/src/columnSchema/json/nativeEnum.ts +30 -0
  30. package/src/columnSchema/json/nullable.ts +33 -0
  31. package/src/columnSchema/json/nullish.ts +30 -0
  32. package/src/columnSchema/json/object.ts +206 -0
  33. package/src/columnSchema/json/optional.ts +28 -0
  34. package/src/columnSchema/json/record.ts +40 -0
  35. package/src/columnSchema/json/scalarTypes.ts +117 -0
  36. package/src/columnSchema/json/set.ts +34 -0
  37. package/src/columnSchema/json/tuple.ts +40 -0
  38. package/src/columnSchema/json/typeBase.ts +202 -0
  39. package/src/columnSchema/json/union.ts +16 -0
  40. package/src/columnSchema/json.ts +64 -0
  41. package/src/columnSchema/number.ts +122 -0
  42. package/src/columnSchema/string.ts +222 -0
  43. package/src/columnSchema/utils.ts +27 -0
  44. package/src/common.ts +86 -0
  45. package/src/db.test.ts +67 -0
  46. package/src/db.ts +212 -0
  47. package/src/errors.ts +7 -0
  48. package/src/index.ts +18 -0
  49. package/src/operators.test.ts +608 -0
  50. package/src/operators.ts +177 -0
  51. package/src/query.ts +292 -0
  52. package/src/queryDataUtils.ts +50 -0
  53. package/src/queryMethods/aggregate.test.ts +583 -0
  54. package/src/queryMethods/aggregate.ts +878 -0
  55. package/src/queryMethods/callbacks.test.ts +69 -0
  56. package/src/queryMethods/callbacks.ts +55 -0
  57. package/src/queryMethods/clear.test.ts +64 -0
  58. package/src/queryMethods/clear.ts +58 -0
  59. package/src/queryMethods/columnInfo.test.ts +45 -0
  60. package/src/queryMethods/columnInfo.ts +67 -0
  61. package/src/queryMethods/delete.test.ts +135 -0
  62. package/src/queryMethods/delete.ts +50 -0
  63. package/src/queryMethods/for.test.ts +57 -0
  64. package/src/queryMethods/for.ts +99 -0
  65. package/src/queryMethods/from.test.ts +66 -0
  66. package/src/queryMethods/from.ts +58 -0
  67. package/src/queryMethods/get.test.ts +66 -0
  68. package/src/queryMethods/get.ts +88 -0
  69. package/src/queryMethods/having.test.ts +247 -0
  70. package/src/queryMethods/having.ts +99 -0
  71. package/src/queryMethods/insert.test.ts +555 -0
  72. package/src/queryMethods/insert.ts +453 -0
  73. package/src/queryMethods/join.test.ts +150 -0
  74. package/src/queryMethods/join.ts +508 -0
  75. package/src/queryMethods/json.test.ts +398 -0
  76. package/src/queryMethods/json.ts +259 -0
  77. package/src/queryMethods/log.test.ts +172 -0
  78. package/src/queryMethods/log.ts +123 -0
  79. package/src/queryMethods/queryMethods.test.ts +629 -0
  80. package/src/queryMethods/queryMethods.ts +428 -0
  81. package/src/queryMethods/select.test.ts +479 -0
  82. package/src/queryMethods/select.ts +249 -0
  83. package/src/queryMethods/then.ts +236 -0
  84. package/src/queryMethods/transaction.test.ts +66 -0
  85. package/src/queryMethods/transaction.ts +66 -0
  86. package/src/queryMethods/union.test.ts +59 -0
  87. package/src/queryMethods/union.ts +89 -0
  88. package/src/queryMethods/update.test.ts +417 -0
  89. package/src/queryMethods/update.ts +350 -0
  90. package/src/queryMethods/upsert.test.ts +56 -0
  91. package/src/queryMethods/upsert.ts +43 -0
  92. package/src/queryMethods/where.test.ts +1594 -0
  93. package/src/queryMethods/where.ts +450 -0
  94. package/src/queryMethods/window.test.ts +66 -0
  95. package/src/queryMethods/window.ts +108 -0
  96. package/src/queryMethods/with.test.ts +191 -0
  97. package/src/queryMethods/with.ts +92 -0
  98. package/src/quote.ts +36 -0
  99. package/src/relations.ts +194 -0
  100. package/src/sql/aggregate.ts +80 -0
  101. package/src/sql/columnInfo.ts +22 -0
  102. package/src/sql/common.ts +42 -0
  103. package/src/sql/delete.ts +41 -0
  104. package/src/sql/distinct.ts +19 -0
  105. package/src/sql/fromAndAs.ts +51 -0
  106. package/src/sql/having.ts +140 -0
  107. package/src/sql/index.ts +2 -0
  108. package/src/sql/insert.ts +102 -0
  109. package/src/sql/join.ts +242 -0
  110. package/src/sql/orderBy.ts +41 -0
  111. package/src/sql/select.ts +153 -0
  112. package/src/sql/toSql.ts +153 -0
  113. package/src/sql/truncate.ts +13 -0
  114. package/src/sql/types.ts +355 -0
  115. package/src/sql/update.ts +62 -0
  116. package/src/sql/where.ts +314 -0
  117. package/src/sql/window.ts +38 -0
  118. package/src/sql/with.ts +32 -0
  119. package/src/test-utils.ts +172 -0
  120. package/src/utils.ts +140 -0
  121. package/tsconfig.build.json +6 -0
  122. package/tsconfig.json +8 -0
@@ -0,0 +1,583 @@
1
+ import {
2
+ User,
3
+ expectQueryNotMutated,
4
+ expectSql,
5
+ AssertEqual,
6
+ useTestDatabase,
7
+ userData,
8
+ } from '../test-utils';
9
+ import { raw } from '../common';
10
+
11
+ describe('aggregate', () => {
12
+ useTestDatabase();
13
+
14
+ describe('aggregate options', () => {
15
+ test('without options', async () => {
16
+ expectSql(User.count('*').toSql(), 'SELECT count(*) FROM "user"');
17
+ });
18
+
19
+ test('as', () => {
20
+ const q = User.count('*', { as: 'a' });
21
+ expectSql(q.toSql(), 'SELECT count(*) AS "a" FROM "user"');
22
+ });
23
+
24
+ test('distinct', () => {
25
+ expectSql(
26
+ User.count('name', { distinct: true }).toSql(),
27
+ 'SELECT count(DISTINCT "user"."name") FROM "user"',
28
+ );
29
+ });
30
+
31
+ test('order', () => {
32
+ expectSql(
33
+ User.count('name', { order: { name: 'DESC' } }).toSql(),
34
+ 'SELECT count("user"."name" ORDER BY "user"."name" DESC) FROM "user"',
35
+ );
36
+ });
37
+
38
+ test('filter', () => {
39
+ expectSql(
40
+ User.count('name', { filter: { age: { not: null } } }).toSql(),
41
+ 'SELECT count("user"."name") FILTER (WHERE "user"."age" IS NOT NULL) FROM "user"',
42
+ );
43
+ });
44
+
45
+ describe('over', () => {
46
+ test('with column partitionBy', () => {
47
+ expectSql(
48
+ User.count('name', {
49
+ over: {
50
+ partitionBy: 'id',
51
+ order: {
52
+ id: 'DESC',
53
+ },
54
+ },
55
+ }).toSql(),
56
+ `
57
+ SELECT count("user"."name") OVER (PARTITION BY "user"."id" ORDER BY "user"."id" DESC)
58
+ FROM "user"
59
+ `,
60
+ );
61
+ });
62
+
63
+ test('with columns array partitionBy', () => {
64
+ expectSql(
65
+ User.count('name', {
66
+ over: {
67
+ partitionBy: ['id', 'name'],
68
+ order: {
69
+ id: 'DESC',
70
+ },
71
+ },
72
+ }).toSql(),
73
+ `
74
+ SELECT count("user"."name") OVER (PARTITION BY "user"."id", "user"."name" ORDER BY "user"."id" DESC)
75
+ FROM "user"
76
+ `,
77
+ );
78
+ });
79
+ });
80
+
81
+ test('all options', () => {
82
+ expectSql(
83
+ User.count('name', {
84
+ as: 'a',
85
+ distinct: true,
86
+ order: { name: 'DESC' },
87
+ filter: { age: { not: null } },
88
+ over: {
89
+ partitionBy: 'id',
90
+ order: {
91
+ id: 'DESC',
92
+ },
93
+ },
94
+ }).toSql(),
95
+ `
96
+ SELECT
97
+ count(DISTINCT "user"."name" ORDER BY "user"."name" DESC)
98
+ FILTER (WHERE "user"."age" IS NOT NULL)
99
+ OVER (
100
+ PARTITION BY "user"."id"
101
+ ORDER BY "user"."id" DESC
102
+ ) AS "a"
103
+ FROM "user"
104
+ `,
105
+ );
106
+ });
107
+
108
+ test('withinGroup', () => {
109
+ expectSql(
110
+ User.count('name', {
111
+ distinct: true,
112
+ order: { name: 'DESC' },
113
+ filter: { age: { not: null } },
114
+ withinGroup: true,
115
+ }).toSql(),
116
+ `
117
+ SELECT count("user"."name")
118
+ WITHIN GROUP (ORDER BY "user"."name" DESC)
119
+ FILTER (WHERE "user"."age" IS NOT NULL) FROM "user"
120
+ `,
121
+ );
122
+ });
123
+ });
124
+
125
+ describe('count', () => {
126
+ it('should return a number', async () => {
127
+ const count = await User.count();
128
+
129
+ const eq: AssertEqual<typeof count, number> = true;
130
+ expect(eq).toBe(true);
131
+
132
+ expect(typeof count).toBe('number');
133
+ });
134
+
135
+ describe('selectCount', () => {
136
+ it('should select number', async () => {
137
+ await User.insert(userData);
138
+
139
+ const user = await User.selectCount().take();
140
+ expect(user.count).toBe(1);
141
+
142
+ const eq: AssertEqual<typeof user.count, number> = true;
143
+ expect(eq).toBe(true);
144
+ });
145
+ });
146
+ });
147
+
148
+ describe.each`
149
+ method | functionName
150
+ ${'avg'} | ${'avg'}
151
+ ${'min'} | ${'min'}
152
+ ${'max'} | ${'max'}
153
+ ${'sum'} | ${'sum'}
154
+ ${'bitAnd'} | ${'bit_and'}
155
+ ${'bitOr'} | ${'bit_or'}
156
+ `('$method', ({ method, functionName }) => {
157
+ it('should return null when no records', async () => {
158
+ const value = await User[method as 'avg']('id');
159
+
160
+ const eq: AssertEqual<typeof value, number | null> = true;
161
+ expect(eq).toBe(true);
162
+
163
+ expect(value).toBe(null);
164
+ });
165
+
166
+ it('should return number when have records', async () => {
167
+ await User.insert(userData);
168
+
169
+ const value = await User[method as 'avg']('id');
170
+
171
+ const eq: AssertEqual<typeof value, number | null> = true;
172
+ expect(eq).toBe(true);
173
+
174
+ expect(typeof value).toBe('number');
175
+ });
176
+
177
+ const selectMethod = `select${method[0].toUpperCase()}${method.slice(
178
+ 1,
179
+ )}` as 'selectAvg';
180
+ describe(selectMethod, () => {
181
+ it('should select null when no record', async () => {
182
+ const value = await User[selectMethod]('id').take();
183
+
184
+ const eq: AssertEqual<typeof value, { avg: number | null }> = true;
185
+ expect(eq).toBe(true);
186
+
187
+ expect(value).toEqual({ [functionName]: null });
188
+ });
189
+
190
+ it('should return number when have records', async () => {
191
+ const id = await User.get('id').insert(userData);
192
+
193
+ const value = await User[selectMethod]('id').take();
194
+
195
+ const eq: AssertEqual<typeof value, { avg: number | null }> = true;
196
+ expect(eq).toBe(true);
197
+
198
+ expect(value).toEqual({ [functionName]: id });
199
+ });
200
+ });
201
+ });
202
+
203
+ describe.each`
204
+ method | functionName
205
+ ${'boolAnd'} | ${'bool_and'}
206
+ ${'boolOr'} | ${'bool_or'}
207
+ ${'every'} | ${'every'}
208
+ `('$method', ({ method, functionName }) => {
209
+ it('should return null when no records', async () => {
210
+ const value = await User[method as 'boolAnd']('active');
211
+
212
+ const eq: AssertEqual<typeof value, boolean | null> = true;
213
+ expect(eq).toBe(true);
214
+
215
+ expect(value).toBe(null);
216
+ });
217
+
218
+ it('should return boolean when have records', async () => {
219
+ await User.insert({ ...userData, active: true });
220
+
221
+ const value = await User[method as 'boolAnd']('active');
222
+
223
+ const eq: AssertEqual<typeof value, boolean | null> = true;
224
+ expect(eq).toBe(true);
225
+
226
+ expect(typeof value).toBe('boolean');
227
+ });
228
+
229
+ const selectMethod = `select${method[0].toUpperCase()}${method.slice(
230
+ 1,
231
+ )}` as 'selectBoolAnd';
232
+ describe(selectMethod, () => {
233
+ it('should select null when no record', async () => {
234
+ const value = await User[selectMethod]('active').take();
235
+
236
+ const eq: AssertEqual<typeof value, { bool_and: boolean | null }> =
237
+ true;
238
+ expect(eq).toBe(true);
239
+
240
+ expect(value).toEqual({ [functionName]: null });
241
+ });
242
+
243
+ it('should return boolean when have records', async () => {
244
+ await User.insert({ ...userData, active: true });
245
+
246
+ const value = await User[selectMethod]('active').take();
247
+
248
+ const eq: AssertEqual<typeof value, { bool_and: boolean | null }> =
249
+ true;
250
+ expect(eq).toBe(true);
251
+
252
+ expect(value).toEqual({ [functionName]: true });
253
+ });
254
+ });
255
+ });
256
+
257
+ describe.each`
258
+ method | functionName
259
+ ${'jsonAgg'} | ${'json_agg'}
260
+ ${'jsonbAgg'} | ${'jsonb_agg'}
261
+ `('$method', ({ method, functionName }) => {
262
+ const data = { name: 'name', tags: [] };
263
+
264
+ it('should return null when no records', async () => {
265
+ const value = await User[method as 'jsonAgg']('data');
266
+
267
+ const eq: AssertEqual<
268
+ typeof value,
269
+ ({ name: string; tags: string[] } | null)[] | null
270
+ > = true;
271
+ expect(eq).toBe(true);
272
+
273
+ expect(value).toBe(null);
274
+ });
275
+
276
+ it('should return json array when have records', async () => {
277
+ await User.insert({ ...userData, data });
278
+
279
+ const value = await User[method as 'jsonAgg']('data');
280
+
281
+ const eq: AssertEqual<
282
+ typeof value,
283
+ ({ name: string; tags: string[] } | null)[] | null
284
+ > = true;
285
+ expect(eq).toBe(true);
286
+
287
+ expect(value).toEqual([data]);
288
+ });
289
+
290
+ const selectMethod = `select${method[0].toUpperCase()}${method.slice(
291
+ 1,
292
+ )}` as 'selectJsonAgg';
293
+ describe(selectMethod, () => {
294
+ it('should select null when no record', async () => {
295
+ const value = await User[selectMethod]('data').take();
296
+
297
+ const eq: AssertEqual<
298
+ typeof value,
299
+ { json_agg: ({ name: string; tags: string[] } | null)[] | null }
300
+ > = true;
301
+ expect(eq).toBe(true);
302
+
303
+ expect(value).toEqual({ [functionName]: null });
304
+ });
305
+
306
+ it('should return json array when have records', async () => {
307
+ await User.insert({ ...userData, data });
308
+
309
+ const value = await User[selectMethod]('data').take();
310
+
311
+ const eq: AssertEqual<
312
+ typeof value,
313
+ { json_agg: ({ name: string; tags: string[] } | null)[] | null }
314
+ > = true;
315
+ expect(eq).toBe(true);
316
+
317
+ expect(value).toEqual({ [functionName]: [data] });
318
+ });
319
+ });
320
+ });
321
+
322
+ describe.each`
323
+ method | functionName
324
+ ${'count'} | ${'count'}
325
+ ${'avg'} | ${'avg'}
326
+ ${'min'} | ${'min'}
327
+ ${'max'} | ${'max'}
328
+ ${'sum'} | ${'sum'}
329
+ ${'bitAnd'} | ${'bit_and'}
330
+ ${'bitOr'} | ${'bit_or'}
331
+ ${'boolAnd'} | ${'bool_and'}
332
+ ${'boolOr'} | ${'bool_or'}
333
+ ${'every'} | ${'every'}
334
+ ${'jsonAgg'} | ${'json_agg'}
335
+ ${'jsonbAgg'} | ${'jsonb_agg'}
336
+ ${'xmlAgg'} | ${'xmlagg'}
337
+ `('$method', ({ method, functionName }) => {
338
+ const getSql = (arg: string, as?: string) => {
339
+ let select = `${functionName}(${arg})`;
340
+
341
+ if (as) select += ` AS "${as}"`;
342
+
343
+ return `SELECT ${select} FROM "user"`;
344
+ };
345
+
346
+ it(`should perform ${method} query for a column`, () => {
347
+ const q = User.clone();
348
+
349
+ const expectedSql = getSql('"user"."name"');
350
+ expectSql(q[method as 'count']('name').toSql(), expectedSql);
351
+ expectQueryNotMutated(q);
352
+
353
+ q[`_${method}` as `_count`]('name');
354
+ expectSql(q.toSql(), expectedSql);
355
+ });
356
+
357
+ it('should support raw sql parameter', () => {
358
+ const q = User.all();
359
+ expectSql(q[method as 'count'](raw('name')).toSql(), getSql('name'));
360
+ expectQueryNotMutated(q);
361
+ });
362
+
363
+ const selectMethod = `select${method[0].toUpperCase()}${method.slice(1)}`;
364
+ it(`.${selectMethod} should select aggregated value`, () => {
365
+ const q = User.all();
366
+ const expectedSql = getSql('"user"."name"', 'name');
367
+ expectSql(
368
+ q[selectMethod as 'selectCount']('name', { as: 'name' }).toSql(),
369
+ expectedSql,
370
+ );
371
+ expectQueryNotMutated(q);
372
+ });
373
+
374
+ it(`.${selectMethod} supports raw sql`, () => {
375
+ const q = User.all();
376
+ const expectedSql = getSql('name', 'name');
377
+ expectSql(
378
+ q[selectMethod as 'selectCount'](raw('name'), { as: 'name' }).toSql(),
379
+ expectedSql,
380
+ );
381
+ expectQueryNotMutated(q);
382
+ });
383
+ });
384
+
385
+ describe.each`
386
+ method | functionName
387
+ ${'jsonObjectAgg'} | ${'json_object_agg'}
388
+ ${'jsonbObjectAgg'} | ${'jsonb_object_agg'}
389
+ `('$method', ({ method, functionName }) => {
390
+ it('should return null when no records', async () => {
391
+ const value = await User[method as 'jsonObjectAgg']({ alias: 'name' });
392
+
393
+ const eq: AssertEqual<typeof value, { alias: string } | null> = true;
394
+ expect(eq).toBe(true);
395
+
396
+ expect(value).toBe(null);
397
+ });
398
+
399
+ it('should return json object when have records', async () => {
400
+ await User.insert(userData);
401
+
402
+ const value = await User[method as 'jsonObjectAgg']({ alias: 'name' });
403
+
404
+ const eq: AssertEqual<typeof value, { alias: string } | null> = true;
405
+ expect(eq).toBe(true);
406
+
407
+ expect(value).toEqual({ alias: 'name' });
408
+ });
409
+
410
+ const selectMethod = `select${method[0].toUpperCase()}${method.slice(
411
+ 1,
412
+ )}` as 'selectJsonObjectAgg';
413
+ describe(selectMethod, () => {
414
+ it('should select null when no record', async () => {
415
+ const value = await User[selectMethod]({ alias: 'name' }).take();
416
+
417
+ const eq: AssertEqual<
418
+ typeof value,
419
+ { json_object_agg: { alias: string } | null }
420
+ > = true;
421
+ expect(eq).toBe(true);
422
+
423
+ expect(value).toEqual({ [functionName]: null });
424
+ });
425
+
426
+ it('should return json object when have records', async () => {
427
+ await User.insert(userData);
428
+
429
+ const value = await User[selectMethod]({ alias: 'name' }).take();
430
+
431
+ const eq: AssertEqual<
432
+ typeof value,
433
+ { json_object_agg: { alias: string } | null }
434
+ > = true;
435
+ expect(eq).toBe(true);
436
+
437
+ expect(value).toEqual({ [functionName]: { alias: 'name' } });
438
+ });
439
+ });
440
+
441
+ it(`should perform ${method} query for a column`, () => {
442
+ const q = User.clone();
443
+ const expectedSql = `SELECT ${functionName}($1::text, "user"."name") FROM "user"`;
444
+ expectSql(
445
+ q[method as 'jsonObjectAgg']({ alias: 'name' }).toSql(),
446
+ expectedSql,
447
+ ['alias'],
448
+ );
449
+ expectQueryNotMutated(q);
450
+
451
+ q[`_${method}` as '_jsonObjectAgg']({ alias: 'name' });
452
+ expectSql(q.toSql(), expectedSql, ['alias']);
453
+ });
454
+
455
+ it('should support raw sql parameter', () => {
456
+ const q = User.clone();
457
+ expectSql(
458
+ q[method as 'jsonObjectAgg']({
459
+ alias: raw('name'),
460
+ }).toSql(),
461
+ `SELECT ${functionName}($1::text, name) FROM "user"`,
462
+ ['alias'],
463
+ );
464
+ expectQueryNotMutated(q);
465
+ });
466
+
467
+ it(`.${selectMethod} should select aggregated value`, () => {
468
+ const q = User.all();
469
+ const expectedSql = `SELECT ${functionName}($1::text, "user"."name") AS "name" FROM "user"`;
470
+ expectSql(
471
+ q[selectMethod as 'jsonObjectAgg'](
472
+ { alias: 'name' },
473
+ { as: 'name' },
474
+ ).toSql(),
475
+ expectedSql,
476
+ ['alias'],
477
+ );
478
+ expectQueryNotMutated(q);
479
+ });
480
+
481
+ it(`.${selectMethod} supports raw sql`, () => {
482
+ const q = User.all();
483
+ const expectedSql = `SELECT ${functionName}($1::text, name) AS "name" FROM "user"`;
484
+ expectSql(
485
+ q[selectMethod as 'jsonObjectAgg'](
486
+ { alias: raw('name') },
487
+ { as: 'name' },
488
+ ).toSql(),
489
+ expectedSql,
490
+ ['alias'],
491
+ );
492
+ expectQueryNotMutated(q);
493
+ });
494
+ });
495
+
496
+ describe('stringAgg', () => {
497
+ it('should return null when no records', async () => {
498
+ const value = await User.stringAgg('name', ', ');
499
+
500
+ const eq: AssertEqual<typeof value, string | null> = true;
501
+ expect(eq).toBe(true);
502
+
503
+ expect(value).toBe(null);
504
+ });
505
+
506
+ it('should return json object when have records', async () => {
507
+ await User.insert([userData, userData]);
508
+
509
+ const value = await User.stringAgg('name', ', ');
510
+
511
+ const eq: AssertEqual<typeof value, string | null> = true;
512
+ expect(eq).toBe(true);
513
+
514
+ expect(value).toEqual('name, name');
515
+ });
516
+
517
+ describe('selectStringAgg', () => {
518
+ it('should select null when no record', async () => {
519
+ const value = await User.selectStringAgg('name', ', ').take();
520
+
521
+ const eq: AssertEqual<typeof value, { string_agg: string | null }> =
522
+ true;
523
+ expect(eq).toBe(true);
524
+
525
+ expect(value).toEqual({ string_agg: null });
526
+ });
527
+
528
+ it('should return json object when have records', async () => {
529
+ await User.insert([userData, userData]);
530
+
531
+ const value = await User.selectStringAgg('name', ', ').take();
532
+
533
+ const eq: AssertEqual<typeof value, { string_agg: string | null }> =
534
+ true;
535
+ expect(eq).toBe(true);
536
+
537
+ expect(value).toEqual({ string_agg: 'name, name' });
538
+ });
539
+ });
540
+
541
+ it('makes stringAgg query', () => {
542
+ const q = User.clone();
543
+ const expectedSql = `SELECT string_agg("user"."name", $1) FROM "user"`;
544
+ expectSql(q.stringAgg('name', ' & ').toSql(), expectedSql, [' & ']);
545
+ expectQueryNotMutated(q);
546
+
547
+ q._stringAgg('name', ' & ');
548
+ expectSql(q.toSql(), expectedSql, [' & ']);
549
+ });
550
+
551
+ it('should support raw sql parameter', async () => {
552
+ const q = User.all();
553
+ expectSql(
554
+ q.stringAgg(raw('name'), ' & ').toSql(),
555
+ `SELECT string_agg(name, $1) FROM "user"`,
556
+ [' & '],
557
+ );
558
+ expectQueryNotMutated(q);
559
+ });
560
+
561
+ it(`.stringAgg should select aggregated value`, () => {
562
+ const q = User.all();
563
+ const expectedSql = `SELECT string_agg("user"."name", $1) AS "name" FROM "user"`;
564
+ expectSql(
565
+ q.stringAgg('name', ' & ', { as: 'name' }).toSql(),
566
+ expectedSql,
567
+ [' & '],
568
+ );
569
+ expectQueryNotMutated(q);
570
+ });
571
+
572
+ it(`.stringAgg supports raw sql`, () => {
573
+ const q = User.all();
574
+ const expectedSql = `SELECT string_agg(name, $1) AS "name" FROM "user"`;
575
+ expectSql(
576
+ q.stringAgg(raw('name'), ' & ', { as: 'name' }).toSql(),
577
+ expectedSql,
578
+ [' & '],
579
+ );
580
+ expectQueryNotMutated(q);
581
+ });
582
+ });
583
+ });