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,453 @@
1
+ import {
2
+ defaultsKey,
3
+ Query,
4
+ SetQueryReturnsAll,
5
+ SetQueryReturnsOne,
6
+ SetQueryReturnsRowCount,
7
+ } from '../query';
8
+ import { pushQueryArray } from '../queryDataUtils';
9
+ import { isRaw, RawExpression } from '../common';
10
+ import {
11
+ BelongsToNestedInsert,
12
+ BelongsToRelation,
13
+ HasOneNestedInsert,
14
+ HasOneRelation,
15
+ NestedInsertItem,
16
+ NestedInsertOneItem,
17
+ Relation,
18
+ } from '../relations';
19
+ import { SetOptional } from '../utils';
20
+ import { InsertQueryData, OnConflictItem, OnConflictMergeUpdate } from '../sql';
21
+ import { WhereArg } from './where';
22
+ import { parseResult, queryMethodByReturnType } from './then';
23
+
24
+ export type OptionalKeys<T extends Query> = {
25
+ [K in keyof T['shape']]: T['shape'][K]['isPrimaryKey'] extends true
26
+ ? K
27
+ : T['shape'][K]['isNullable'] extends true
28
+ ? K
29
+ : never;
30
+ }[keyof T['shape']];
31
+
32
+ export type InsertData<
33
+ T extends Query,
34
+ DefaultKeys extends string = T[defaultsKey] extends string
35
+ ? T[defaultsKey]
36
+ : never,
37
+ Data = SetOptional<SetOptional<T['inputType'], OptionalKeys<T>>, DefaultKeys>,
38
+ > = [keyof T['relations']] extends [never]
39
+ ? Data
40
+ : Omit<
41
+ Data,
42
+ {
43
+ [K in keyof T['relations']]: T['relations'][K] extends BelongsToRelation
44
+ ? T['relations'][K]['options']['foreignKey']
45
+ : never;
46
+ }[keyof T['relations']]
47
+ > &
48
+ {
49
+ [Key in keyof T['relations']]: T['relations'][Key] extends BelongsToRelation
50
+ ?
51
+ | SetOptional<
52
+ {
53
+ [K in T['relations'][Key]['options']['foreignKey']]: T['relations'][Key]['options']['foreignKey'] extends keyof T['inputType']
54
+ ? T['inputType'][T['relations'][Key]['options']['foreignKey']]
55
+ : never;
56
+ },
57
+ DefaultKeys
58
+ >
59
+ | {
60
+ [K in Key]: {
61
+ create?: InsertData<
62
+ T['relations'][Key]['nestedCreateQuery']
63
+ >;
64
+ connect?: WhereArg<T['relations'][Key]['model']>;
65
+ };
66
+ }
67
+ : T['relations'][Key] extends HasOneRelation
68
+ ? 'through' extends T['relations'][Key]['options']
69
+ ? // eslint-disable-next-line @typescript-eslint/ban-types
70
+ {}
71
+ : {
72
+ [K in Key]?: {
73
+ create?: InsertData<T['relations'][Key]['nestedCreateQuery']>;
74
+ connect?: WhereArg<T['relations'][Key]['model']>;
75
+ };
76
+ }
77
+ : T['relations'][Key] extends Relation
78
+ ? 'through' extends T['relations'][Key]['options']
79
+ ? // eslint-disable-next-line @typescript-eslint/ban-types
80
+ {}
81
+ : {
82
+ [K in Key]?: {
83
+ create?: InsertData<
84
+ T['relations'][Key]['nestedCreateQuery']
85
+ >[];
86
+ connect?: WhereArg<T['relations'][Key]['model']>[];
87
+ connectOrCreate?: {
88
+ where: WhereArg<T['relations'][Key]['model']>;
89
+ create: InsertData<
90
+ T['relations'][Key]['nestedCreateQuery']
91
+ >;
92
+ }[];
93
+ };
94
+ }
95
+ : // eslint-disable-next-line @typescript-eslint/ban-types
96
+ {};
97
+ }[keyof T['relations']];
98
+
99
+ type InsertOneResult<T extends Query> = T['hasSelect'] extends false
100
+ ? SetQueryReturnsRowCount<T>
101
+ : T['returnType'] extends 'all'
102
+ ? SetQueryReturnsOne<T>
103
+ : T['returnType'] extends 'one'
104
+ ? SetQueryReturnsOne<T>
105
+ : T;
106
+
107
+ type InsertManyResult<T extends Query> = T['hasSelect'] extends false
108
+ ? SetQueryReturnsRowCount<T>
109
+ : T['returnType'] extends 'one' | 'oneOrThrow'
110
+ ? SetQueryReturnsAll<T>
111
+ : T;
112
+
113
+ type OnConflictArg<T extends Query> =
114
+ | keyof T['shape']
115
+ | (keyof T['shape'])[]
116
+ | RawExpression;
117
+
118
+ type PrependRelations = Record<
119
+ string,
120
+ [rowIndex: number, columnIndex: number, data: Record<string, unknown>][]
121
+ >;
122
+
123
+ type AppendRelations = Record<
124
+ string,
125
+ [rowIndex: number, data: NestedInsertItem][]
126
+ >;
127
+
128
+ const processInsertItem = (
129
+ item: Record<string, unknown>,
130
+ rowIndex: number,
131
+ relations: Record<string, Relation>,
132
+ prependRelations: PrependRelations,
133
+ appendRelations: AppendRelations,
134
+ requiredReturning: Record<string, boolean>,
135
+ columns: string[],
136
+ columnsMap: Record<string, number>,
137
+ ) => {
138
+ Object.keys(item).forEach((key) => {
139
+ if (relations[key]) {
140
+ if (relations[key].type === 'belongsTo') {
141
+ const foreignKey = (relations[key] as BelongsToRelation).options
142
+ .foreignKey;
143
+
144
+ let columnIndex = columnsMap[foreignKey];
145
+ if (columnIndex === undefined) {
146
+ columnsMap[foreignKey] = columnIndex = columns.length;
147
+ columns.push(foreignKey);
148
+ }
149
+
150
+ if (!prependRelations[key]) prependRelations[key] = [];
151
+
152
+ prependRelations[key].push([
153
+ rowIndex,
154
+ columnIndex,
155
+ item[key] as Record<string, unknown>,
156
+ ]);
157
+ } else {
158
+ requiredReturning[relations[key].primaryKey] = true;
159
+
160
+ if (!appendRelations[key]) appendRelations[key] = [];
161
+
162
+ appendRelations[key].push([rowIndex, item[key] as NestedInsertItem]);
163
+ }
164
+ } else if (columnsMap[key] === undefined) {
165
+ columnsMap[key] = columns.length;
166
+ columns.push(key);
167
+ }
168
+ });
169
+ };
170
+
171
+ export class Insert {
172
+ insert<T extends Query>(this: T, data: InsertData<T>): InsertOneResult<T>;
173
+ insert<T extends Query>(
174
+ this: T,
175
+ data: InsertData<T>[] | { columns: string[]; values: RawExpression },
176
+ ): InsertManyResult<T>;
177
+ insert(this: Query, data: InsertData<Query> & InsertData<Query>[]) {
178
+ return this.clone()._insert(data) as unknown as InsertOneResult<Query> &
179
+ InsertManyResult<Query>;
180
+ }
181
+
182
+ _insert<T extends Query>(this: T, data: InsertData<T>): InsertOneResult<T>;
183
+ _insert<T extends Query>(
184
+ this: T,
185
+ data: InsertData<T>[] | { columns: string[]; values: RawExpression },
186
+ ): InsertManyResult<T>;
187
+ _insert(
188
+ data:
189
+ | Record<string, unknown>
190
+ | Record<string, unknown>[]
191
+ | { columns: string[]; values: RawExpression },
192
+ ) {
193
+ const q = this as unknown as Query & { query: InsertQueryData };
194
+ const returning = q.query.select;
195
+
196
+ delete q.query.and;
197
+ delete q.query.or;
198
+
199
+ let columns: string[];
200
+ const prependRelations: PrependRelations = {};
201
+ const appendRelations: AppendRelations = {};
202
+ const requiredReturning: Record<string, boolean> = {};
203
+ const relations = (this as unknown as Query).relations as unknown as Record<
204
+ string,
205
+ Relation
206
+ >;
207
+ let values: unknown[][] | RawExpression;
208
+
209
+ let returnType = q.query.returnType;
210
+ if (returning) {
211
+ if (Array.isArray(data)) {
212
+ if (returnType === 'one' || returnType === 'oneOrThrow') {
213
+ returnType = 'all';
214
+ }
215
+ } else {
216
+ if (returnType === 'all') {
217
+ returnType = 'one';
218
+ }
219
+ }
220
+ } else {
221
+ returnType = 'rowCount';
222
+ }
223
+
224
+ if (
225
+ 'values' in data &&
226
+ typeof data.values === 'object' &&
227
+ data.values &&
228
+ isRaw(data.values)
229
+ ) {
230
+ columns = (data as { columns: string[] }).columns;
231
+ values = data.values;
232
+ } else {
233
+ columns = [];
234
+ const columnsMap: Record<string, number> = {};
235
+ const defaults = q.query.defaults;
236
+
237
+ if (Array.isArray(data)) {
238
+ if (defaults) {
239
+ data = data.map((item) => ({ ...defaults, ...item }));
240
+ }
241
+
242
+ data.forEach((item, i) => {
243
+ processInsertItem(
244
+ item,
245
+ i,
246
+ relations,
247
+ prependRelations,
248
+ appendRelations,
249
+ requiredReturning,
250
+ columns,
251
+ columnsMap,
252
+ );
253
+ });
254
+
255
+ values = Array(data.length);
256
+
257
+ data.forEach((item, i) => {
258
+ (values as unknown[][])[i] = columns.map((key) => item[key]);
259
+ });
260
+ } else {
261
+ if (defaults) {
262
+ data = { ...defaults, ...data };
263
+ }
264
+
265
+ processInsertItem(
266
+ data,
267
+ 0,
268
+ relations,
269
+ prependRelations,
270
+ appendRelations,
271
+ requiredReturning,
272
+ columns,
273
+ columnsMap,
274
+ );
275
+
276
+ values = [columns.map((key) => (data as Record<string, unknown>)[key])];
277
+ }
278
+ }
279
+
280
+ const prependRelationsKeys = Object.keys(prependRelations);
281
+ if (prependRelationsKeys.length) {
282
+ pushQueryArray(
283
+ q,
284
+ 'beforeQuery',
285
+ prependRelationsKeys.map((relationName) => {
286
+ return async (q: Query) => {
287
+ const relationData = prependRelations[relationName];
288
+ const relation = relations[relationName];
289
+
290
+ const inserted = await (
291
+ relation.nestedInsert as BelongsToNestedInsert
292
+ )(
293
+ q,
294
+ relationData.map(([, , data]) => data as NestedInsertOneItem),
295
+ );
296
+
297
+ const primaryKey = (relation as BelongsToRelation).options
298
+ .primaryKey;
299
+ relationData.forEach(([rowIndex, columnIndex], index) => {
300
+ (values as unknown[][])[rowIndex][columnIndex] =
301
+ inserted[index][primaryKey];
302
+ });
303
+ };
304
+ }),
305
+ );
306
+ }
307
+
308
+ const appendRelationsKeys = Object.keys(appendRelations);
309
+ if (appendRelationsKeys.length) {
310
+ if (!returning?.includes('*')) {
311
+ const requiredColumns = Object.keys(requiredReturning);
312
+
313
+ if (!returning) {
314
+ q.query.select = requiredColumns;
315
+ } else {
316
+ q.query.select = [
317
+ ...new Set([...(returning as string[]), ...requiredColumns]),
318
+ ];
319
+ }
320
+ }
321
+
322
+ let resultOfTypeAll: Record<string, unknown>[] | undefined;
323
+ if (returnType !== 'all') {
324
+ const { handleResult } = q.query;
325
+ q.query.handleResult = async (q, queryResult) => {
326
+ resultOfTypeAll = (await handleResult(q, queryResult)) as Record<
327
+ string,
328
+ unknown
329
+ >[];
330
+
331
+ if (queryMethodByReturnType[returnType] === 'arrays') {
332
+ queryResult.rows.forEach(
333
+ (row, i) =>
334
+ ((queryResult.rows as unknown as unknown[][])[i] =
335
+ Object.values(row)),
336
+ );
337
+ }
338
+
339
+ return parseResult(q, returnType, queryResult);
340
+ };
341
+ }
342
+
343
+ pushQueryArray(
344
+ q,
345
+ 'afterQuery',
346
+ appendRelationsKeys.map((relationName) => {
347
+ return (q: Query, result: Record<string, unknown>[]) => {
348
+ const all = resultOfTypeAll || result;
349
+ return (
350
+ relations[relationName].nestedInsert as HasOneNestedInsert
351
+ )?.(
352
+ q,
353
+ appendRelations[relationName].map(([rowIndex, data]) => [
354
+ all[rowIndex],
355
+ data as NestedInsertOneItem,
356
+ ]),
357
+ );
358
+ };
359
+ }),
360
+ );
361
+ }
362
+
363
+ q.query.type = 'insert';
364
+ q.query.columns = columns;
365
+ q.query.values = values;
366
+ if (prependRelationsKeys.length || appendRelationsKeys.length) {
367
+ q.query.wrapInTransaction = true;
368
+ }
369
+
370
+ q.query.returnType = appendRelationsKeys.length ? 'all' : returnType;
371
+
372
+ return q as unknown as InsertOneResult<Query> & InsertManyResult<Query>;
373
+ }
374
+
375
+ create<T extends Query>(this: T, data: InsertData<T>): SetQueryReturnsOne<T>;
376
+ create<T extends Query>(
377
+ this: T,
378
+ data: InsertData<T>[] | { columns: string[]; values: RawExpression },
379
+ ): SetQueryReturnsAll<T>;
380
+ create(this: Query, data: InsertData<Query> & InsertData<Query>[]) {
381
+ return this.clone()._create(data) as unknown as SetQueryReturnsOne<Query> &
382
+ SetQueryReturnsAll<Query>;
383
+ }
384
+
385
+ _create<T extends Query>(this: T, data: InsertData<T>): SetQueryReturnsOne<T>;
386
+ _create<T extends Query>(
387
+ this: T,
388
+ data: InsertData<T>[] | { columns: string[]; values: RawExpression },
389
+ ): SetQueryReturnsAll<T>;
390
+ _create(this: Query, data: InsertData<Query> & InsertData<Query>[]) {
391
+ if (!this.query.select) {
392
+ this.query.select = ['*'];
393
+ }
394
+ return this.insert(data) as unknown as never;
395
+ }
396
+
397
+ defaults<T extends Query, Data extends Partial<InsertData<T>>>(
398
+ this: T,
399
+ data: Data,
400
+ ): T & { [defaultsKey]: keyof Data } {
401
+ return (this.clone() as T)._defaults(data);
402
+ }
403
+ _defaults<T extends Query, Data extends Partial<InsertData<T>>>(
404
+ this: T,
405
+ data: Data,
406
+ ): T & { [defaultsKey]: keyof Data } {
407
+ this.query.defaults = data;
408
+ return this as T & { [defaultsKey]: keyof Data };
409
+ }
410
+
411
+ onConflict<T extends Query, Arg extends OnConflictArg<T>>(
412
+ this: T,
413
+ arg?: Arg,
414
+ ): OnConflictQueryBuilder<T, Arg> {
415
+ return this.clone()._onConflict(arg);
416
+ }
417
+ _onConflict<
418
+ T extends Query,
419
+ Arg extends OnConflictArg<T> | undefined = undefined,
420
+ >(this: T, arg?: Arg): OnConflictQueryBuilder<T, Arg> {
421
+ return new OnConflictQueryBuilder(this, arg as Arg);
422
+ }
423
+ }
424
+
425
+ export class OnConflictQueryBuilder<
426
+ T extends Query,
427
+ Arg extends OnConflictArg<T> | undefined,
428
+ > {
429
+ constructor(private query: T, private onConflict: Arg) {}
430
+
431
+ ignore(): T {
432
+ (this.query.query as InsertQueryData).onConflict = {
433
+ type: 'ignore',
434
+ expr: this.onConflict as OnConflictItem,
435
+ };
436
+ return this.query;
437
+ }
438
+
439
+ merge(
440
+ update?:
441
+ | keyof T['shape']
442
+ | (keyof T['shape'])[]
443
+ | Partial<T['inputType']>
444
+ | RawExpression,
445
+ ): T {
446
+ (this.query.query as InsertQueryData).onConflict = {
447
+ type: 'merge',
448
+ expr: this.onConflict as OnConflictItem,
449
+ update: update as OnConflictMergeUpdate,
450
+ };
451
+ return this.query;
452
+ }
453
+ }
@@ -0,0 +1,150 @@
1
+ import { expectQueryNotMutated, expectSql, Message, User } from '../test-utils';
2
+ import { OnQueryBuilder } from './join';
3
+ import { testJoin, testWhere } from './where.test';
4
+ import { Sql } from '../sql';
5
+ import { Query } from '../query';
6
+
7
+ describe.each`
8
+ method | sql
9
+ ${'join'} | ${'JOIN'}
10
+ ${'innerJoin'} | ${'INNER JOIN'}
11
+ ${'leftJoin'} | ${'LEFT JOIN'}
12
+ ${'leftOuterJoin'} | ${'LEFT OUTER JOIN'}
13
+ ${'rightJoin'} | ${'RIGHT JOIN'}
14
+ ${'rightOuterJoin'} | ${'RIGHT OUTER JOIN'}
15
+ ${'fullOuterJoin'} | ${'FULL OUTER JOIN'}
16
+ `('$method', ({ method, sql }) => {
17
+ testJoin(
18
+ method,
19
+ (target: string, conditions: string) => `
20
+ SELECT "user".* FROM "user"
21
+ ${sql} ${target} ON ${conditions}
22
+ `,
23
+ );
24
+ });
25
+
26
+ describe('join callback with query builder', () => {
27
+ it('should have .on and .onOr properly working', () => {
28
+ const q = User.all();
29
+
30
+ const expectedSql = `
31
+ SELECT "user".* FROM "user"
32
+ JOIN "message"
33
+ ON "message"."authorId" = "user"."id"
34
+ OR "message"."text" = "user"."name"
35
+ `;
36
+
37
+ expectSql(
38
+ q
39
+ .join(Message, (q) =>
40
+ q.on('message.authorId', 'user.id').orOn('message.text', 'user.name'),
41
+ )
42
+ .toSql(),
43
+ expectedSql,
44
+ );
45
+
46
+ expectSql(
47
+ q
48
+ .join(Message, (q) =>
49
+ q
50
+ .on('message.authorId', '=', 'user.id')
51
+ .orOn('message.text', '=', 'user.name'),
52
+ )
53
+ .toSql(),
54
+ expectedSql,
55
+ );
56
+
57
+ expectQueryNotMutated(q);
58
+ });
59
+
60
+ it('should have .onJsonPathEquals method', () => {
61
+ const q = User.all();
62
+
63
+ expectSql(
64
+ q
65
+ .join(User.as('otherUser'), (q) =>
66
+ q.onJsonPathEquals('user.data', '$.name', 'otherUser.data', '$.name'),
67
+ )
68
+ .toSql(),
69
+ `
70
+ SELECT "user".* FROM "user"
71
+ JOIN "user" AS "otherUser"
72
+ ON jsonb_path_query_first("user"."data", $1) = jsonb_path_query_first("otherUser"."data", $2)
73
+ `,
74
+ ['$.name', '$.name'],
75
+ );
76
+ });
77
+
78
+ describe('where methods', () => {
79
+ describe('and', () => {
80
+ let query: OnQueryBuilder;
81
+ let where: OnQueryBuilder['where'];
82
+ let _where: OnQueryBuilder['_where'];
83
+ User.join(Message, (q) => {
84
+ query = q;
85
+ where = q.where;
86
+ _where = q._where;
87
+ return q;
88
+ }).toSql();
89
+ beforeEach(() => {
90
+ query.where = jest.fn();
91
+ query._where = jest.fn();
92
+ });
93
+ afterAll(() => {
94
+ query.where = where;
95
+ query._where = _where;
96
+ });
97
+
98
+ it('is alias for where', () => {
99
+ query.and({});
100
+ expect(query.where).toBeCalled();
101
+ });
102
+
103
+ it('has modifier', () => {
104
+ query._and({});
105
+ expect(query._where).toBeCalled();
106
+ });
107
+ });
108
+
109
+ describe('andNot', () => {
110
+ let query: OnQueryBuilder;
111
+ let whereNot: OnQueryBuilder['whereNot'];
112
+ let _whereNot: OnQueryBuilder['_whereNot'];
113
+ User.join(Message, (q) => {
114
+ query = q;
115
+ whereNot = q.whereNot;
116
+ _whereNot = q._whereNot;
117
+ return q;
118
+ }).toSql();
119
+ beforeEach(() => {
120
+ query.whereNot = jest.fn();
121
+ query._whereNot = jest.fn();
122
+ });
123
+ afterAll(() => {
124
+ query.whereNot = whereNot;
125
+ query._whereNot = _whereNot;
126
+ });
127
+
128
+ it('is alias for where', () => {
129
+ query.andNot({});
130
+ expect(query.whereNot).toBeCalled();
131
+ });
132
+
133
+ it('has modifier', () => {
134
+ query._andNot({});
135
+ expect(query._whereNot).toBeCalled();
136
+ });
137
+ });
138
+
139
+ const buildSql = (cb: (q: OnQueryBuilder) => OnQueryBuilder): Sql => {
140
+ return User.join(Message, cb).toSql();
141
+ };
142
+
143
+ const startSql = `SELECT "user".* FROM "user" JOIN "message" ON`;
144
+
145
+ testWhere(
146
+ buildSql as unknown as (cb: (q: Query) => Query) => Sql,
147
+ startSql,
148
+ );
149
+ });
150
+ });