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,479 @@
1
+ import {
2
+ AssertEqual,
3
+ Chat,
4
+ chatData,
5
+ expectQueryNotMutated,
6
+ expectSql,
7
+ Message,
8
+ messageData,
9
+ now,
10
+ Profile,
11
+ profileData,
12
+ User,
13
+ userData,
14
+ useTestDatabase,
15
+ } from '../test-utils';
16
+ import { raw, rawColumn } from '../common';
17
+ import { DateColumn } from '../columnSchema';
18
+ import { addQueryOn } from './join';
19
+ import { RelationQuery, relationQueryKey } from '../relations';
20
+
21
+ const insertUserAndProfile = async () => {
22
+ const id = await User.get('id').insert(userData);
23
+ await Profile.insert({ ...profileData, userId: id });
24
+ };
25
+
26
+ describe('selectMethods', () => {
27
+ useTestDatabase();
28
+
29
+ describe('select', () => {
30
+ it('should have no effect if no columns provided', () => {
31
+ const q = User.all();
32
+ expectSql(
33
+ q.select().toSql(),
34
+ `
35
+ SELECT * FROM "user"
36
+ `,
37
+ );
38
+ expectSql(
39
+ q.select('id').select().toSql(),
40
+ `
41
+ SELECT "user"."id" FROM "user"
42
+ `,
43
+ );
44
+ expectQueryNotMutated(q);
45
+ });
46
+
47
+ it('should select provided columns', () => {
48
+ const q = User.all();
49
+ expectSql(
50
+ q.select('id', 'name').toSql(),
51
+ `
52
+ SELECT "user"."id", "user"."name" FROM "user"
53
+ `,
54
+ );
55
+ expectQueryNotMutated(q);
56
+ });
57
+
58
+ it('should select table.column', () => {
59
+ const q = User.all();
60
+ expectSql(
61
+ q.select('user.id', 'user.name').toSql(),
62
+ `
63
+ SELECT "user"."id", "user"."name" FROM "user"
64
+ `,
65
+ );
66
+ expectQueryNotMutated(q);
67
+ });
68
+
69
+ it('should select joined columns', () => {
70
+ const q = User.all();
71
+
72
+ expectSql(
73
+ q
74
+ .join(Profile, 'profile.userId', '=', 'user.id')
75
+ .select('user.id', 'profile.userId')
76
+ .toSql(),
77
+ `
78
+ SELECT "user"."id", "profile"."userId" FROM "user"
79
+ JOIN "profile" ON "profile"."userId" = "user"."id"
80
+ `,
81
+ );
82
+ expectQueryNotMutated(q);
83
+ });
84
+
85
+ it('should select joined columns with alias', () => {
86
+ const q = User.all();
87
+ expectSql(
88
+ q
89
+ .join(Profile.as('p'), 'p.userId', '=', 'user.id')
90
+ .select('user.id', 'p.userId')
91
+ .toSql(),
92
+ `
93
+ SELECT "user"."id", "p"."userId" FROM "user"
94
+ JOIN "profile" AS "p" ON "p"."userId" = "user"."id"
95
+ `,
96
+ );
97
+ expectQueryNotMutated(q);
98
+ });
99
+
100
+ describe('select relation', () => {
101
+ const profileQuery = Profile.takeOptional();
102
+ const profileRelationQuery = addQueryOn(
103
+ profileQuery,
104
+ profileQuery,
105
+ User,
106
+ 'userId',
107
+ 'id',
108
+ );
109
+ profileRelationQuery.query[relationQueryKey] = 'profile';
110
+
111
+ const profileRelation = new Proxy(() => undefined, {
112
+ get(_, key) {
113
+ return (
114
+ profileRelationQuery as unknown as Record<string | symbol, unknown>
115
+ )[key];
116
+ },
117
+ }) as unknown as RelationQuery<
118
+ 'profile',
119
+ Record<string, unknown>,
120
+ never,
121
+ typeof profileQuery
122
+ >;
123
+
124
+ it('should select relation which returns one record', () => {
125
+ const q = User.all();
126
+
127
+ const query = q.select('id', profileRelation.where({ bio: 'bio' }));
128
+ const eq: AssertEqual<
129
+ Awaited<typeof query>,
130
+ { id: number; profile: typeof Profile['type'] | null }[]
131
+ > = true;
132
+ expect(eq).toBe(true);
133
+
134
+ expectSql(
135
+ query.toSql(),
136
+ `
137
+ SELECT
138
+ "user"."id",
139
+ (
140
+ SELECT row_to_json("t".*)
141
+ FROM (
142
+ SELECT *
143
+ FROM "profile"
144
+ WHERE "profile"."userId" = "user"."id"
145
+ AND "profile"."bio" = $1
146
+ LIMIT $2
147
+ ) AS "t"
148
+ ) AS "profile"
149
+ FROM "user"
150
+ `,
151
+ ['bio', 1],
152
+ );
153
+
154
+ expectQueryNotMutated(q);
155
+ });
156
+
157
+ it('should have proper type for required relation', () => {
158
+ const q = User.all();
159
+
160
+ const query = q.select(
161
+ 'id',
162
+ profileRelation as unknown as RelationQuery<
163
+ 'profile',
164
+ Record<string, unknown>,
165
+ never,
166
+ typeof profileRelationQuery,
167
+ true
168
+ >,
169
+ );
170
+ const eq: AssertEqual<
171
+ Awaited<typeof query>,
172
+ { id: number; profile: typeof Profile['type'] }[]
173
+ > = true;
174
+ expect(eq).toBe(true);
175
+ });
176
+
177
+ it('should parse columns in single relation record result', async () => {
178
+ const userId = await User.get('id').insert(userData);
179
+ const now = new Date();
180
+ await Profile.insert({ userId, updatedAt: now, createdAt: now });
181
+
182
+ const [record] = await User.select('id', profileRelation);
183
+ expect(record.profile).toMatchObject({
184
+ updatedAt: now,
185
+ createdAt: now,
186
+ });
187
+ });
188
+
189
+ const messagesQuery = Message.as('messages');
190
+ const messageRelationQuery = addQueryOn(
191
+ messagesQuery,
192
+ messagesQuery,
193
+ User,
194
+ 'authorId',
195
+ 'id',
196
+ );
197
+ messageRelationQuery.query[relationQueryKey] = 'messages';
198
+
199
+ const messageRelation = new Proxy(() => undefined, {
200
+ get(_, key) {
201
+ return (
202
+ messageRelationQuery as unknown as Record<string | symbol, unknown>
203
+ )[key];
204
+ },
205
+ }) as unknown as RelationQuery<
206
+ 'messages',
207
+ Record<string, unknown>,
208
+ never,
209
+ typeof messageRelationQuery
210
+ >;
211
+
212
+ it('should select relation which returns many records', () => {
213
+ const q = User.all();
214
+
215
+ const query = q.select('id', messageRelation.where({ text: 'text' }));
216
+ const eq: AssertEqual<
217
+ Awaited<typeof query>,
218
+ { id: number; messages: typeof Message['type'][] }[]
219
+ > = true;
220
+ expect(eq).toBe(true);
221
+
222
+ expectSql(
223
+ query.toSql(),
224
+ `
225
+ SELECT
226
+ "user"."id",
227
+ (
228
+ SELECT COALESCE(json_agg(row_to_json("t".*)), '[]')
229
+ FROM (
230
+ SELECT *
231
+ FROM "message" AS "messages"
232
+ WHERE "messages"."authorId" = "user"."id"
233
+ AND "messages"."text" = $1
234
+ ) AS "t"
235
+ ) AS "messages"
236
+ FROM "user"
237
+ `,
238
+ ['text'],
239
+ );
240
+
241
+ expectQueryNotMutated(q);
242
+ });
243
+
244
+ it('should parse columns in multiple relation records result', async () => {
245
+ const { id: authorId } = await User.select('id').insert(userData);
246
+ const { id: chatId } = await Chat.select('id').insert(chatData);
247
+ await Message.insert({
248
+ authorId,
249
+ chatId,
250
+ ...messageData,
251
+ });
252
+
253
+ const [record] = await User.select('id', messageRelation);
254
+ expect(record.messages[0]).toMatchObject({
255
+ updatedAt: now,
256
+ createdAt: now,
257
+ });
258
+ });
259
+ });
260
+
261
+ describe('parse columns', () => {
262
+ beforeEach(insertUserAndProfile);
263
+
264
+ it('should parse columns of the table', async () => {
265
+ const q = User.select('createdAt');
266
+
267
+ expect((await q.all())[0].createdAt instanceof Date).toBe(true);
268
+ expect((await q.take()).createdAt instanceof Date).toBe(true);
269
+ expect((await q.rows())[0][0] instanceof Date).toBe(true);
270
+ expect((await q.get('createdAt')) instanceof Date).toBe(true);
271
+ });
272
+
273
+ it('should parse columns of the table, selected by column name and table name', async () => {
274
+ const q = User.select('user.createdAt');
275
+
276
+ expect((await q.all())[0].createdAt instanceof Date).toBe(true);
277
+ expect((await q.take()).createdAt instanceof Date).toBe(true);
278
+ expect((await q.rows())[0][0] instanceof Date).toBe(true);
279
+ expect((await q.get('user.createdAt')) instanceof Date).toBe(true);
280
+ });
281
+
282
+ it('should parse columns of joined table', async () => {
283
+ const q = Profile.join(User, 'user.id', '=', 'profile.userId').select(
284
+ 'user.createdAt',
285
+ );
286
+
287
+ expect((await q.all())[0].createdAt instanceof Date).toBe(true);
288
+ expect((await q.take()).createdAt instanceof Date).toBe(true);
289
+ expect((await q.rows())[0][0] instanceof Date).toBe(true);
290
+ expect((await q.get('user.createdAt')) instanceof Date).toBe(true);
291
+ });
292
+ });
293
+
294
+ it('should select columns with aliases', async () => {
295
+ const q = User.all();
296
+
297
+ const query = q.select({ aliasedId: 'id', aliasedName: 'name' });
298
+
299
+ const eq: AssertEqual<
300
+ Awaited<typeof query>,
301
+ { aliasedId: number; aliasedName: string }[]
302
+ > = true;
303
+ expect(eq).toBe(true);
304
+
305
+ expectSql(
306
+ query.toSql(),
307
+ `
308
+ SELECT "user"."id" AS "aliasedId", "user"."name" AS "aliasedName"
309
+ FROM "user"
310
+ `,
311
+ );
312
+ expectQueryNotMutated(q);
313
+ });
314
+
315
+ it('should select table.column with aliases', () => {
316
+ const q = User.all();
317
+
318
+ const query = q.select({
319
+ aliasedId: 'user.id',
320
+ aliasedName: 'user.name',
321
+ });
322
+
323
+ const eq: AssertEqual<
324
+ Awaited<typeof query>,
325
+ { aliasedId: number; aliasedName: string }[]
326
+ > = true;
327
+ expect(eq).toBe(true);
328
+
329
+ expectSql(
330
+ query.toSql(),
331
+ `
332
+ SELECT "user"."id" AS "aliasedId", "user"."name" AS "aliasedName"
333
+ FROM "user"
334
+ `,
335
+ );
336
+ expectQueryNotMutated(q);
337
+ });
338
+
339
+ it('should select joined columns', () => {
340
+ const q = User.all();
341
+ expectSql(
342
+ q
343
+ .join(Profile, 'profile.userId', '=', 'user.id')
344
+ .select({
345
+ aliasedId: 'user.id',
346
+ aliasedUserId: 'profile.userId',
347
+ })
348
+ .toSql(),
349
+ `
350
+ SELECT "user"."id" AS "aliasedId", "profile"."userId" AS "aliasedUserId"
351
+ FROM "user"
352
+ JOIN "profile" ON "profile"."userId" = "user"."id"
353
+ `,
354
+ );
355
+ expectQueryNotMutated(q);
356
+ });
357
+
358
+ it('should select joined columns with alias', () => {
359
+ const q = User.all();
360
+ expectSql(
361
+ q
362
+ .join(Profile.as('p'), 'p.userId', '=', 'user.id')
363
+ .select({
364
+ aliasedId: 'user.id',
365
+ aliasedUserId: 'p.userId',
366
+ })
367
+ .toSql(),
368
+ `
369
+ SELECT "user"."id" AS "aliasedId", "p"."userId" AS "aliasedUserId"
370
+ FROM "user"
371
+ JOIN "profile" AS "p" ON "p"."userId" = "user"."id"
372
+ `,
373
+ );
374
+ expectQueryNotMutated(q);
375
+ });
376
+
377
+ it('can select raw', () => {
378
+ const q = User.all();
379
+ expectSql(
380
+ q.select({ one: raw('1') }).toSql(),
381
+ `
382
+ SELECT 1 AS "one" FROM "user"
383
+ `,
384
+ );
385
+ expectQueryNotMutated(q);
386
+ });
387
+
388
+ it('can select subquery', () => {
389
+ const q = User.all();
390
+ expectSql(
391
+ q.select({ subquery: User.all() }).toSql(),
392
+ `
393
+ SELECT
394
+ (
395
+ SELECT COALESCE(json_agg(row_to_json("t".*)), '[]')
396
+ FROM (SELECT * FROM "user") AS "t"
397
+ ) AS "subquery"
398
+ FROM "user"
399
+ `,
400
+ );
401
+ expectQueryNotMutated(q);
402
+ });
403
+ });
404
+
405
+ describe('selectAll', () => {
406
+ it('should select all columns', () => {
407
+ const query = User.select('id', 'name').selectAll();
408
+
409
+ expectSql(query.toSql(), `SELECT * FROM "user"`);
410
+ });
411
+ });
412
+
413
+ describe('parse columns', () => {
414
+ beforeEach(insertUserAndProfile);
415
+
416
+ it('should parse columns of the table', async () => {
417
+ const q = User.select({
418
+ date: 'createdAt',
419
+ });
420
+
421
+ expect((await q.all())[0].date instanceof Date).toBe(true);
422
+ expect((await q.take()).date instanceof Date).toBe(true);
423
+ expect((await q.rows())[0][0] instanceof Date).toBe(true);
424
+ });
425
+
426
+ it('should parse columns of the table, selected by column name and table name', async () => {
427
+ const q = User.select({
428
+ date: 'user.createdAt',
429
+ });
430
+
431
+ expect((await q.all())[0].date instanceof Date).toBe(true);
432
+ expect((await q.take()).date instanceof Date).toBe(true);
433
+ expect((await q.rows())[0][0] instanceof Date).toBe(true);
434
+ });
435
+
436
+ it('should parse columns of joined table', async () => {
437
+ const q = Profile.join(User, 'user.id', '=', 'profile.userId').select({
438
+ date: 'user.createdAt',
439
+ });
440
+
441
+ expect((await q.all())[0].date instanceof Date).toBe(true);
442
+ expect((await q.take()).date instanceof Date).toBe(true);
443
+ expect((await q.rows())[0][0] instanceof Date).toBe(true);
444
+ });
445
+
446
+ it('should parse subquery array columns', async () => {
447
+ const q = User.select({
448
+ users: User.all(),
449
+ });
450
+
451
+ expect((await q.all())[0].users[0].createdAt instanceof Date).toBe(true);
452
+ expect((await q.take()).users[0].createdAt instanceof Date).toBe(true);
453
+ expect((await q.rows())[0][0][0].createdAt instanceof Date).toBe(true);
454
+ });
455
+
456
+ it('should parse subquery item columns', async () => {
457
+ const q = User.select({
458
+ user: User.take(),
459
+ });
460
+
461
+ expect((await q.all())[0].user.createdAt instanceof Date).toBe(true);
462
+ expect((await q.take()).user.createdAt instanceof Date).toBe(true);
463
+ expect((await q.rows())[0][0].createdAt instanceof Date).toBe(true);
464
+ });
465
+
466
+ it('should parse raw column', async () => {
467
+ const q = User.select({
468
+ date: rawColumn(
469
+ new DateColumn().parse((input) => new Date(input)),
470
+ '"createdAt"',
471
+ ),
472
+ });
473
+
474
+ expect((await q.all())[0].date instanceof Date).toBe(true);
475
+ expect((await q.take()).date instanceof Date).toBe(true);
476
+ expect((await q.rows())[0][0] instanceof Date).toBe(true);
477
+ });
478
+ });
479
+ });