metal-orm 1.0.43 → 1.0.45
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.
- package/README.md +700 -557
- package/dist/index.cjs +896 -476
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1146 -275
- package/dist/index.d.ts +1146 -275
- package/dist/index.js +896 -474
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ast/adapters.ts +8 -2
- package/src/core/ast/builders.ts +105 -81
- package/src/core/ast/expression-builders.ts +430 -390
- package/src/core/ast/expression-visitor.ts +47 -8
- package/src/core/ast/helpers.ts +23 -0
- package/src/core/ast/join-node.ts +17 -1
- package/src/core/ddl/dialects/base-schema-dialect.ts +7 -1
- package/src/core/ddl/dialects/index.ts +1 -0
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -0
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +2 -0
- package/src/core/ddl/introspect/context.ts +6 -0
- package/src/core/ddl/introspect/functions/postgres.ts +13 -0
- package/src/core/ddl/introspect/mssql.ts +11 -0
- package/src/core/ddl/introspect/mysql.ts +2 -0
- package/src/core/ddl/introspect/postgres.ts +14 -0
- package/src/core/ddl/introspect/registry.ts +14 -0
- package/src/core/ddl/introspect/run-select.ts +13 -0
- package/src/core/ddl/introspect/sqlite.ts +22 -0
- package/src/core/ddl/introspect/utils.ts +18 -0
- package/src/core/ddl/naming-strategy.ts +6 -0
- package/src/core/ddl/schema-dialect.ts +19 -6
- package/src/core/ddl/schema-diff.ts +22 -0
- package/src/core/ddl/schema-generator.ts +22 -0
- package/src/core/ddl/schema-plan-executor.ts +6 -0
- package/src/core/ddl/schema-types.ts +6 -0
- package/src/core/dialect/abstract.ts +2 -2
- package/src/core/execution/pooling/pool.ts +12 -7
- package/src/core/functions/datetime.ts +57 -33
- package/src/core/functions/numeric.ts +95 -30
- package/src/core/functions/standard-strategy.ts +35 -0
- package/src/core/functions/text.ts +83 -22
- package/src/core/functions/types.ts +23 -8
- package/src/decorators/bootstrap.ts +16 -4
- package/src/decorators/column.ts +17 -0
- package/src/decorators/decorator-metadata.ts +27 -0
- package/src/decorators/entity.ts +8 -0
- package/src/decorators/index.ts +3 -0
- package/src/decorators/relations.ts +32 -0
- package/src/orm/als.ts +34 -9
- package/src/orm/entity-context.ts +54 -0
- package/src/orm/entity-metadata.ts +122 -9
- package/src/orm/execute.ts +15 -0
- package/src/orm/lazy-batch.ts +158 -98
- package/src/orm/relations/has-many.ts +44 -0
- package/src/orm/save-graph.ts +45 -0
- package/src/query/index.ts +74 -0
- package/src/query/target.ts +46 -0
- package/src/query-builder/delete-query-state.ts +30 -0
- package/src/query-builder/delete.ts +64 -19
- package/src/query-builder/hydration-manager.ts +46 -0
- package/src/query-builder/insert-query-state.ts +30 -0
- package/src/query-builder/insert.ts +46 -2
- package/src/query-builder/query-ast-service.ts +5 -0
- package/src/query-builder/query-resolution.ts +78 -0
- package/src/query-builder/raw-column-parser.ts +5 -0
- package/src/query-builder/relation-alias.ts +7 -0
- package/src/query-builder/relation-conditions.ts +61 -48
- package/src/query-builder/relation-service.ts +68 -63
- package/src/query-builder/relation-utils.ts +3 -0
- package/src/query-builder/select/cte-facet.ts +40 -0
- package/src/query-builder/select/from-facet.ts +80 -0
- package/src/query-builder/select/join-facet.ts +62 -0
- package/src/query-builder/select/predicate-facet.ts +103 -0
- package/src/query-builder/select/projection-facet.ts +69 -0
- package/src/query-builder/select/relation-facet.ts +81 -0
- package/src/query-builder/select/setop-facet.ts +36 -0
- package/src/query-builder/select-helpers.ts +13 -0
- package/src/query-builder/select-query-builder-deps.ts +19 -1
- package/src/query-builder/select-query-state.ts +2 -1
- package/src/query-builder/select.ts +795 -1163
- package/src/query-builder/update-query-state.ts +52 -0
- package/src/query-builder/update.ts +69 -19
- package/src/schema/table-guards.ts +31 -0
|
@@ -1,1163 +1,795 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table.js';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} from '
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
*
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
context
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
* @
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
*
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
|
|
347
|
-
*
|
|
348
|
-
|
|
349
|
-
* @param
|
|
350
|
-
|
|
351
|
-
* @param
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
return this.clone(nextContext);
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
* @
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
*
|
|
419
|
-
|
|
420
|
-
* @
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
* @param
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
*
|
|
552
|
-
|
|
553
|
-
* @
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
*
|
|
584
|
-
|
|
585
|
-
* @param
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
const nextContext = this.
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
*
|
|
615
|
-
*/
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
*
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
*
|
|
731
|
-
* @
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
/**
|
|
762
|
-
*
|
|
763
|
-
* @param
|
|
764
|
-
* @
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
*
|
|
790
|
-
|
|
791
|
-
*/
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
/**
|
|
802
|
-
|
|
803
|
-
* Adds a LIMIT clause to the query
|
|
804
|
-
|
|
805
|
-
* @param n - Maximum number of rows to return
|
|
806
|
-
|
|
807
|
-
* @returns New query builder instance with the LIMIT clause
|
|
808
|
-
|
|
809
|
-
*/
|
|
810
|
-
|
|
811
|
-
limit(n: number): SelectQueryBuilder<T, TTable> {
|
|
812
|
-
|
|
813
|
-
const nextContext = this.applyAst(this.context, service => service.withLimit(n));
|
|
814
|
-
|
|
815
|
-
return this.clone(nextContext);
|
|
816
|
-
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
/**
|
|
822
|
-
|
|
823
|
-
* Adds an OFFSET clause to the query
|
|
824
|
-
|
|
825
|
-
* @param n - Number of rows to skip
|
|
826
|
-
|
|
827
|
-
* @returns New query builder instance with the OFFSET clause
|
|
828
|
-
|
|
829
|
-
*/
|
|
830
|
-
|
|
831
|
-
offset(n: number): SelectQueryBuilder<T, TTable> {
|
|
832
|
-
|
|
833
|
-
const nextContext = this.applyAst(this.context, service => service.withOffset(n));
|
|
834
|
-
|
|
835
|
-
return this.clone(nextContext);
|
|
836
|
-
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
|
|
843
|
-
* Combines this query with another using UNION
|
|
844
|
-
|
|
845
|
-
* @param query - Query to union with
|
|
846
|
-
|
|
847
|
-
* @returns New query builder instance with the set operation
|
|
848
|
-
|
|
849
|
-
*/
|
|
850
|
-
|
|
851
|
-
union<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
852
|
-
|
|
853
|
-
return this.clone(this.applySetOperation('UNION', query));
|
|
854
|
-
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
/**
|
|
860
|
-
|
|
861
|
-
* Combines this query with another using UNION ALL
|
|
862
|
-
|
|
863
|
-
* @param query - Query to union with
|
|
864
|
-
|
|
865
|
-
* @returns New query builder instance with the set operation
|
|
866
|
-
|
|
867
|
-
*/
|
|
868
|
-
|
|
869
|
-
unionAll<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
870
|
-
|
|
871
|
-
return this.clone(this.applySetOperation('UNION ALL', query));
|
|
872
|
-
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
/**
|
|
878
|
-
|
|
879
|
-
* Combines this query with another using INTERSECT
|
|
880
|
-
|
|
881
|
-
* @param query - Query to intersect with
|
|
882
|
-
|
|
883
|
-
* @returns New query builder instance with the set operation
|
|
884
|
-
|
|
885
|
-
*/
|
|
886
|
-
|
|
887
|
-
intersect<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
888
|
-
|
|
889
|
-
return this.clone(this.applySetOperation('INTERSECT', query));
|
|
890
|
-
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
/**
|
|
896
|
-
|
|
897
|
-
* Combines this query with another using EXCEPT
|
|
898
|
-
|
|
899
|
-
* @param query - Query to subtract
|
|
900
|
-
|
|
901
|
-
* @returns New query builder instance with the set operation
|
|
902
|
-
|
|
903
|
-
*/
|
|
904
|
-
|
|
905
|
-
except<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
906
|
-
|
|
907
|
-
return this.clone(this.applySetOperation('EXCEPT', query));
|
|
908
|
-
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
/**
|
|
914
|
-
|
|
915
|
-
* Adds a WHERE EXISTS condition to the query
|
|
916
|
-
|
|
917
|
-
* @param subquery - Subquery to check for existence
|
|
918
|
-
|
|
919
|
-
* @returns New query builder instance with the WHERE EXISTS condition
|
|
920
|
-
|
|
921
|
-
*/
|
|
922
|
-
|
|
923
|
-
whereExists<TSub extends TableDef>(
|
|
924
|
-
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
925
|
-
correlate?: ExpressionNode
|
|
926
|
-
): SelectQueryBuilder<T, TTable> {
|
|
927
|
-
const subAst = this.resolveQueryNode(subquery);
|
|
928
|
-
const correlated = this.applyCorrelation(subAst, correlate);
|
|
929
|
-
return this.where(exists(correlated));
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
/**
|
|
935
|
-
|
|
936
|
-
* Adds a WHERE NOT EXISTS condition to the query
|
|
937
|
-
|
|
938
|
-
* @param subquery - Subquery to check for non-existence
|
|
939
|
-
|
|
940
|
-
* @returns New query builder instance with the WHERE NOT EXISTS condition
|
|
941
|
-
|
|
942
|
-
*/
|
|
943
|
-
|
|
944
|
-
whereNotExists<TSub extends TableDef>(
|
|
945
|
-
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
946
|
-
correlate?: ExpressionNode
|
|
947
|
-
): SelectQueryBuilder<T, TTable> {
|
|
948
|
-
const subAst = this.resolveQueryNode(subquery);
|
|
949
|
-
const correlated = this.applyCorrelation(subAst, correlate);
|
|
950
|
-
return this.where(notExists(correlated));
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
/**
|
|
956
|
-
|
|
957
|
-
* Adds a WHERE EXISTS condition based on a relationship
|
|
958
|
-
|
|
959
|
-
* @param relationName - Name of the relationship to check
|
|
960
|
-
|
|
961
|
-
* @param callback - Optional callback to modify the relationship query
|
|
962
|
-
|
|
963
|
-
* @returns New query builder instance with the relationship existence check
|
|
964
|
-
|
|
965
|
-
*/
|
|
966
|
-
|
|
967
|
-
whereHas(
|
|
968
|
-
|
|
969
|
-
relationName: string,
|
|
970
|
-
|
|
971
|
-
callbackOrOptions?: RelationCallback | WhereHasOptions,
|
|
972
|
-
|
|
973
|
-
maybeOptions?: WhereHasOptions
|
|
974
|
-
|
|
975
|
-
): SelectQueryBuilder<T, TTable> {
|
|
976
|
-
|
|
977
|
-
const relation = this.env.table.relations[relationName];
|
|
978
|
-
|
|
979
|
-
if (!relation) {
|
|
980
|
-
|
|
981
|
-
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
982
|
-
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions as RelationCallback : undefined;
|
|
988
|
-
const options = (typeof callbackOrOptions === 'function' ? maybeOptions : callbackOrOptions) as WhereHasOptions | undefined;
|
|
989
|
-
|
|
990
|
-
let subQb = this.createChildBuilder<unknown, typeof relation.target>(relation.target);
|
|
991
|
-
|
|
992
|
-
if (callback) {
|
|
993
|
-
|
|
994
|
-
subQb = callback(subQb);
|
|
995
|
-
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
const subAst = subQb.getAST();
|
|
1001
|
-
|
|
1002
|
-
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
|
|
1003
|
-
|
|
1004
|
-
return this.where(exists(finalSubAst));
|
|
1005
|
-
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
/**
|
|
1011
|
-
|
|
1012
|
-
* Adds a WHERE NOT EXISTS condition based on a relationship
|
|
1013
|
-
|
|
1014
|
-
* @param relationName - Name of the relationship to check
|
|
1015
|
-
|
|
1016
|
-
* @param callback - Optional callback to modify the relationship query
|
|
1017
|
-
|
|
1018
|
-
* @returns New query builder instance with the relationship non-existence check
|
|
1019
|
-
|
|
1020
|
-
*/
|
|
1021
|
-
|
|
1022
|
-
whereHasNot(
|
|
1023
|
-
|
|
1024
|
-
relationName: string,
|
|
1025
|
-
|
|
1026
|
-
callbackOrOptions?: RelationCallback | WhereHasOptions,
|
|
1027
|
-
|
|
1028
|
-
maybeOptions?: WhereHasOptions
|
|
1029
|
-
|
|
1030
|
-
): SelectQueryBuilder<T, TTable> {
|
|
1031
|
-
|
|
1032
|
-
const relation = this.env.table.relations[relationName];
|
|
1033
|
-
|
|
1034
|
-
if (!relation) {
|
|
1035
|
-
|
|
1036
|
-
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
1037
|
-
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions as RelationCallback : undefined;
|
|
1043
|
-
const options = (typeof callbackOrOptions === 'function' ? maybeOptions : callbackOrOptions) as WhereHasOptions | undefined;
|
|
1044
|
-
|
|
1045
|
-
let subQb = this.createChildBuilder<unknown, typeof relation.target>(relation.target);
|
|
1046
|
-
|
|
1047
|
-
if (callback) {
|
|
1048
|
-
|
|
1049
|
-
subQb = callback(subQb);
|
|
1050
|
-
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
const subAst = subQb.getAST();
|
|
1056
|
-
|
|
1057
|
-
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
|
|
1058
|
-
|
|
1059
|
-
return this.where(notExists(finalSubAst));
|
|
1060
|
-
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
/**
|
|
1066
|
-
|
|
1067
|
-
* Compiles the query to SQL for a specific dialect
|
|
1068
|
-
|
|
1069
|
-
* @param dialect - Database dialect to compile for
|
|
1070
|
-
|
|
1071
|
-
* @returns Compiled query with SQL and parameters
|
|
1072
|
-
|
|
1073
|
-
*/
|
|
1074
|
-
|
|
1075
|
-
compile(dialect: SelectDialectInput): CompiledQuery {
|
|
1076
|
-
|
|
1077
|
-
const resolved = resolveDialectInput(dialect);
|
|
1078
|
-
|
|
1079
|
-
return resolved.compileSelect(this.context.state.ast);
|
|
1080
|
-
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
/**
|
|
1086
|
-
|
|
1087
|
-
* Converts the query to SQL string for a specific dialect
|
|
1088
|
-
|
|
1089
|
-
* @param dialect - Database dialect to generate SQL for
|
|
1090
|
-
|
|
1091
|
-
* @returns SQL string representation of the query
|
|
1092
|
-
|
|
1093
|
-
*/
|
|
1094
|
-
|
|
1095
|
-
toSql(dialect: SelectDialectInput): string {
|
|
1096
|
-
|
|
1097
|
-
return this.compile(dialect).sql;
|
|
1098
|
-
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
/**
|
|
1104
|
-
|
|
1105
|
-
* Gets the hydration plan for the query
|
|
1106
|
-
|
|
1107
|
-
* @returns Hydration plan or undefined if none exists
|
|
1108
|
-
|
|
1109
|
-
*/
|
|
1110
|
-
|
|
1111
|
-
getHydrationPlan(): HydrationPlan | undefined {
|
|
1112
|
-
|
|
1113
|
-
return this.context.hydration.getPlan();
|
|
1114
|
-
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
/**
|
|
1120
|
-
|
|
1121
|
-
* Gets the Abstract Syntax Tree (AST) representation of the query
|
|
1122
|
-
|
|
1123
|
-
* @returns Query AST with hydration applied
|
|
1124
|
-
|
|
1125
|
-
*/
|
|
1126
|
-
|
|
1127
|
-
getAST(): SelectQueryNode {
|
|
1128
|
-
|
|
1129
|
-
return this.context.hydration.applyToAst(this.context.state.ast);
|
|
1130
|
-
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
/**
|
|
1138
|
-
|
|
1139
|
-
* Creates a column node for use in expressions
|
|
1140
|
-
|
|
1141
|
-
* @param table - Table name
|
|
1142
|
-
|
|
1143
|
-
* @param name - Column name
|
|
1144
|
-
|
|
1145
|
-
* @returns ColumnNode with the specified table and name
|
|
1146
|
-
|
|
1147
|
-
*/
|
|
1148
|
-
|
|
1149
|
-
export const createColumn = (table: string, name: string): ColumnNode => ({ type: 'Column', table, name });
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
/**
|
|
1154
|
-
|
|
1155
|
-
* Creates a literal value node for use in expressions
|
|
1156
|
-
|
|
1157
|
-
* @param val - Literal value (string or number)
|
|
1158
|
-
|
|
1159
|
-
* @returns LiteralNode with the specified value
|
|
1160
|
-
|
|
1161
|
-
*/
|
|
1162
|
-
|
|
1163
|
-
export const createLiteral = (val: string | number): LiteralNode => ({ type: 'Literal', value: val });
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { ColumnDef } from '../schema/column.js';
|
|
3
|
+
import { OrderingTerm, SelectQueryNode, SetOperationKind } from '../core/ast/query.js';
|
|
4
|
+
import { HydrationPlan } from '../core/hydration/types.js';
|
|
5
|
+
import {
|
|
6
|
+
ColumnNode,
|
|
7
|
+
ExpressionNode,
|
|
8
|
+
FunctionNode,
|
|
9
|
+
BinaryExpressionNode,
|
|
10
|
+
CaseExpressionNode,
|
|
11
|
+
WindowFunctionNode,
|
|
12
|
+
and,
|
|
13
|
+
exists,
|
|
14
|
+
notExists,
|
|
15
|
+
OperandNode
|
|
16
|
+
} from '../core/ast/expression.js';
|
|
17
|
+
import { derivedTable, fnTable } from '../core/ast/builders.js';
|
|
18
|
+
import { CompiledQuery, Dialect } from '../core/dialect/abstract.js';
|
|
19
|
+
import { DialectKey, resolveDialectInput } from '../core/dialect/dialect-factory.js';
|
|
20
|
+
|
|
21
|
+
type SelectDialectInput = Dialect | DialectKey;
|
|
22
|
+
|
|
23
|
+
import { SelectQueryState } from './select-query-state.js';
|
|
24
|
+
import { HydrationManager } from './hydration-manager.js';
|
|
25
|
+
import {
|
|
26
|
+
resolveSelectQueryBuilderDependencies,
|
|
27
|
+
SelectQueryBuilderContext,
|
|
28
|
+
SelectQueryBuilderDependencies,
|
|
29
|
+
SelectQueryBuilderEnvironment
|
|
30
|
+
} from './select-query-builder-deps.js';
|
|
31
|
+
import { QueryAstService } from './query-ast-service.js';
|
|
32
|
+
import { ColumnSelector } from './column-selector.js';
|
|
33
|
+
import { RelationManager } from './relation-manager.js';
|
|
34
|
+
import { RelationIncludeOptions } from './relation-types.js';
|
|
35
|
+
import type { RelationDef } from '../schema/relation.js';
|
|
36
|
+
import { JOIN_KINDS, JoinKind, ORDER_DIRECTIONS, OrderDirection } from '../core/sql/sql.js';
|
|
37
|
+
import { EntityInstance, RelationMap, RelationTargetTable } from '../schema/types.js';
|
|
38
|
+
import { OrmSession } from '../orm/orm-session.ts';
|
|
39
|
+
import { ExecutionContext } from '../orm/execution-context.js';
|
|
40
|
+
import { HydrationContext } from '../orm/hydration-context.js';
|
|
41
|
+
import { executeHydrated, executeHydratedWithContexts } from '../orm/execute.js';
|
|
42
|
+
import { createJoinNode } from '../core/ast/join-node.js';
|
|
43
|
+
import { resolveSelectQuery } from './query-resolution.js';
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
type ColumnSelectionValue = ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode;
|
|
47
|
+
|
|
48
|
+
type DeepSelectEntry<TTable extends TableDef> = {
|
|
49
|
+
type: 'root';
|
|
50
|
+
columns: (keyof TTable['columns'] & string)[];
|
|
51
|
+
} | {
|
|
52
|
+
type: 'relation';
|
|
53
|
+
relationName: keyof TTable['relations'] & string;
|
|
54
|
+
columns: string[];
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
type DeepSelectConfig<TTable extends TableDef> = DeepSelectEntry<TTable>[];
|
|
58
|
+
|
|
59
|
+
type WhereHasOptions = {
|
|
60
|
+
correlate?: ExpressionNode;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type RelationCallback = <TChildTable extends TableDef>(
|
|
64
|
+
qb: SelectQueryBuilder<unknown, TChildTable>
|
|
65
|
+
) => SelectQueryBuilder<unknown, TChildTable>;
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Main query builder class for constructing SQL SELECT queries
|
|
70
|
+
* @typeParam T - Result type for projections (unused)
|
|
71
|
+
* @typeParam TTable - Table definition being queried
|
|
72
|
+
*/
|
|
73
|
+
export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef> {
|
|
74
|
+
private readonly env: SelectQueryBuilderEnvironment;
|
|
75
|
+
private readonly context: SelectQueryBuilderContext;
|
|
76
|
+
private readonly columnSelector: ColumnSelector;
|
|
77
|
+
private readonly relationManager: RelationManager;
|
|
78
|
+
private readonly lazyRelations: Set<string>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Creates a new SelectQueryBuilder instance
|
|
82
|
+
* @param table - Table definition to query
|
|
83
|
+
* @param state - Optional initial query state
|
|
84
|
+
* @param hydration - Optional hydration manager
|
|
85
|
+
* @param dependencies - Optional query builder dependencies
|
|
86
|
+
*/
|
|
87
|
+
constructor(
|
|
88
|
+
table: TTable,
|
|
89
|
+
state?: SelectQueryState,
|
|
90
|
+
hydration?: HydrationManager,
|
|
91
|
+
dependencies?: Partial<SelectQueryBuilderDependencies>,
|
|
92
|
+
lazyRelations?: Set<string>
|
|
93
|
+
) {
|
|
94
|
+
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
95
|
+
this.env = { table, deps };
|
|
96
|
+
const initialState = state ?? deps.createState(table);
|
|
97
|
+
const initialHydration = hydration ?? deps.createHydration(table);
|
|
98
|
+
this.context = {
|
|
99
|
+
state: initialState,
|
|
100
|
+
hydration: initialHydration
|
|
101
|
+
};
|
|
102
|
+
this.lazyRelations = new Set(lazyRelations ?? []);
|
|
103
|
+
this.columnSelector = deps.createColumnSelector(this.env);
|
|
104
|
+
this.relationManager = deps.createRelationManager(this.env);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Creates a new SelectQueryBuilder instance with updated context and lazy relations
|
|
109
|
+
* @param context - Updated query context
|
|
110
|
+
* @param lazyRelations - Updated lazy relations set
|
|
111
|
+
* @returns New SelectQueryBuilder instance
|
|
112
|
+
*/
|
|
113
|
+
private clone(
|
|
114
|
+
context: SelectQueryBuilderContext = this.context,
|
|
115
|
+
lazyRelations = new Set(this.lazyRelations)
|
|
116
|
+
): SelectQueryBuilder<T, TTable> {
|
|
117
|
+
return new SelectQueryBuilder(this.env.table as TTable, context.state, context.hydration, this.env.deps, lazyRelations);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Applies an alias to the root FROM table.
|
|
122
|
+
* @param alias - Alias to apply
|
|
123
|
+
*/
|
|
124
|
+
as(alias: string): SelectQueryBuilder<T, TTable> {
|
|
125
|
+
const from = this.context.state.ast.from;
|
|
126
|
+
if (from.type !== 'Table') {
|
|
127
|
+
throw new Error('Cannot alias non-table FROM sources');
|
|
128
|
+
}
|
|
129
|
+
const nextFrom = { ...from, alias };
|
|
130
|
+
const nextContext = this.applyAst(this.context, service => service.withFrom(nextFrom));
|
|
131
|
+
return this.clone(nextContext);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Applies correlation expression to the query AST
|
|
138
|
+
* @param ast - Query AST to modify
|
|
139
|
+
* @param correlation - Correlation expression
|
|
140
|
+
* @returns Modified AST with correlation applied
|
|
141
|
+
*/
|
|
142
|
+
private applyCorrelation(ast: SelectQueryNode, correlation?: ExpressionNode): SelectQueryNode {
|
|
143
|
+
if (!correlation) return ast;
|
|
144
|
+
const combinedWhere = ast.where ? and(correlation, ast.where) : correlation;
|
|
145
|
+
return {
|
|
146
|
+
...ast,
|
|
147
|
+
where: combinedWhere
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Creates a new child query builder for a related table
|
|
153
|
+
* @param table - Table definition for the child builder
|
|
154
|
+
* @returns New SelectQueryBuilder instance for the child table
|
|
155
|
+
*/
|
|
156
|
+
private createChildBuilder<R, TChild extends TableDef>(table: TChild): SelectQueryBuilder<R, TChild> {
|
|
157
|
+
return new SelectQueryBuilder(table, undefined, undefined, this.env.deps);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Applies an AST mutation using the query AST service
|
|
162
|
+
* @param context - Current query context
|
|
163
|
+
* @param mutator - Function that mutates the AST
|
|
164
|
+
* @returns Updated query context
|
|
165
|
+
*/
|
|
166
|
+
private applyAst(
|
|
167
|
+
context: SelectQueryBuilderContext,
|
|
168
|
+
mutator: (service: QueryAstService) => SelectQueryState
|
|
169
|
+
): SelectQueryBuilderContext {
|
|
170
|
+
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
171
|
+
const nextState = mutator(astService);
|
|
172
|
+
return { state: nextState, hydration: context.hydration };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Applies a join to the query context
|
|
177
|
+
* @param context - Current query context
|
|
178
|
+
* @param table - Table to join
|
|
179
|
+
* @param condition - Join condition
|
|
180
|
+
* @param kind - Join kind
|
|
181
|
+
* @returns Updated query context with join applied
|
|
182
|
+
*/
|
|
183
|
+
private applyJoin(
|
|
184
|
+
context: SelectQueryBuilderContext,
|
|
185
|
+
table: TableDef,
|
|
186
|
+
condition: BinaryExpressionNode,
|
|
187
|
+
kind: JoinKind
|
|
188
|
+
): SelectQueryBuilderContext {
|
|
189
|
+
const joinNode = createJoinNode(kind, { type: 'Table', name: table.name, schema: table.schema }, condition);
|
|
190
|
+
return this.applyAst(context, service => service.withJoin(joinNode));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Applies a set operation to the query
|
|
195
|
+
* @param operator - Set operation kind
|
|
196
|
+
* @param query - Query to combine with
|
|
197
|
+
* @returns Updated query context with set operation
|
|
198
|
+
*/
|
|
199
|
+
private applySetOperation<TSub extends TableDef>(
|
|
200
|
+
operator: SetOperationKind,
|
|
201
|
+
query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode
|
|
202
|
+
): SelectQueryBuilderContext {
|
|
203
|
+
const subAst = resolveSelectQuery(query);
|
|
204
|
+
return this.applyAst(this.context, service => service.withSetOperation(operator, subAst));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Selects columns for the query (unified overloaded method).
|
|
210
|
+
* Can be called with column names or a projection object.
|
|
211
|
+
* @param args - Column names or projection object
|
|
212
|
+
* @returns New query builder instance with selected columns
|
|
213
|
+
*/
|
|
214
|
+
select<K extends keyof TTable['columns'] & string>(
|
|
215
|
+
...args: K[]
|
|
216
|
+
): SelectQueryBuilder<T, TTable>;
|
|
217
|
+
select(columns: Record<string, ColumnSelectionValue>): SelectQueryBuilder<T, TTable>;
|
|
218
|
+
select<K extends keyof TTable['columns'] & string>(
|
|
219
|
+
...args: K[] | [Record<string, ColumnSelectionValue>]
|
|
220
|
+
): SelectQueryBuilder<T, TTable> {
|
|
221
|
+
// If first arg is an object (not a string), treat as projection map
|
|
222
|
+
if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null && typeof args[0] !== 'string') {
|
|
223
|
+
const columns = args[0] as Record<string, ColumnSelectionValue>;
|
|
224
|
+
return this.clone(this.columnSelector.select(this.context, columns));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Otherwise, treat as column names
|
|
228
|
+
const cols = args as K[];
|
|
229
|
+
const selection: Record<string, ColumnDef> = {};
|
|
230
|
+
for (const key of cols) {
|
|
231
|
+
const col = this.env.table.columns[key];
|
|
232
|
+
if (!col) {
|
|
233
|
+
throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
|
|
234
|
+
}
|
|
235
|
+
selection[key] = col;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return this.clone(this.columnSelector.select(this.context, selection));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Selects raw column expressions
|
|
243
|
+
* @param cols - Column expressions as strings
|
|
244
|
+
* @returns New query builder instance with raw column selections
|
|
245
|
+
*/
|
|
246
|
+
selectRaw(...cols: string[]): SelectQueryBuilder<T, TTable> {
|
|
247
|
+
return this.clone(this.columnSelector.selectRaw(this.context, cols));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Adds a Common Table Expression (CTE) to the query
|
|
252
|
+
* @param name - Name of the CTE
|
|
253
|
+
* @param query - Query builder or query node for the CTE
|
|
254
|
+
* @param columns - Optional column names for the CTE
|
|
255
|
+
* @returns New query builder instance with the CTE
|
|
256
|
+
*/
|
|
257
|
+
with<TSub extends TableDef>(name: string, query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
|
|
258
|
+
const subAst = resolveSelectQuery(query);
|
|
259
|
+
const nextContext = this.applyAst(this.context, service => service.withCte(name, subAst, columns, false));
|
|
260
|
+
return this.clone(nextContext);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Adds a recursive Common Table Expression (CTE) to the query
|
|
265
|
+
* @param name - Name of the CTE
|
|
266
|
+
* @param query - Query builder or query node for the CTE
|
|
267
|
+
* @param columns - Optional column names for the CTE
|
|
268
|
+
* @returns New query builder instance with the recursive CTE
|
|
269
|
+
*/
|
|
270
|
+
withRecursive<TSub extends TableDef>(name: string, query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable> {
|
|
271
|
+
const subAst = resolveSelectQuery(query);
|
|
272
|
+
const nextContext = this.applyAst(this.context, service => service.withCte(name, subAst, columns, true));
|
|
273
|
+
return this.clone(nextContext);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Replaces the FROM clause with a derived table (subquery with alias)
|
|
278
|
+
* @param subquery - Subquery to use as the FROM source
|
|
279
|
+
* @param alias - Alias for the derived table
|
|
280
|
+
* @param columnAliases - Optional column alias list
|
|
281
|
+
* @returns New query builder instance with updated FROM
|
|
282
|
+
*/
|
|
283
|
+
fromSubquery<TSub extends TableDef>(
|
|
284
|
+
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
285
|
+
alias: string,
|
|
286
|
+
columnAliases?: string[]
|
|
287
|
+
): SelectQueryBuilder<T, TTable> {
|
|
288
|
+
const subAst = resolveSelectQuery(subquery);
|
|
289
|
+
const fromNode = derivedTable(subAst, alias, columnAliases);
|
|
290
|
+
const nextContext = this.applyAst(this.context, service => service.withFrom(fromNode));
|
|
291
|
+
return this.clone(nextContext);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Replaces the FROM clause with a function table expression.
|
|
296
|
+
* @param name - Function name
|
|
297
|
+
* @param args - Optional function arguments
|
|
298
|
+
* @param alias - Optional alias for the function table
|
|
299
|
+
* @param options - Optional function-table metadata (lateral, ordinality, column aliases, schema)
|
|
300
|
+
*/
|
|
301
|
+
fromFunctionTable(
|
|
302
|
+
name: string,
|
|
303
|
+
args: OperandNode[] = [],
|
|
304
|
+
alias?: string,
|
|
305
|
+
options?: { lateral?: boolean; withOrdinality?: boolean; columnAliases?: string[]; schema?: string }
|
|
306
|
+
): SelectQueryBuilder<T, TTable> {
|
|
307
|
+
const functionTable = fnTable(name, args, alias, options);
|
|
308
|
+
const nextContext = this.applyAst(this.context, service => service.withFrom(functionTable));
|
|
309
|
+
return this.clone(nextContext);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Selects a subquery as a column
|
|
314
|
+
* @param alias - Alias for the subquery column
|
|
315
|
+
* @param sub - Query builder or query node for the subquery
|
|
316
|
+
* @returns New query builder instance with the subquery selection
|
|
317
|
+
*/
|
|
318
|
+
selectSubquery<TSub extends TableDef>(alias: string, sub: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
319
|
+
const query = resolveSelectQuery(sub);
|
|
320
|
+
return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Adds a JOIN against a derived table (subquery with alias)
|
|
325
|
+
* @param subquery - Subquery to join
|
|
326
|
+
* @param alias - Alias for the derived table
|
|
327
|
+
* @param condition - Join condition expression
|
|
328
|
+
* @param joinKind - Join kind (defaults to INNER)
|
|
329
|
+
* @param columnAliases - Optional column alias list for the derived table
|
|
330
|
+
* @returns New query builder instance with the derived-table join
|
|
331
|
+
*/
|
|
332
|
+
joinSubquery<TSub extends TableDef>(
|
|
333
|
+
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
334
|
+
alias: string,
|
|
335
|
+
condition: BinaryExpressionNode,
|
|
336
|
+
joinKind: JoinKind = JOIN_KINDS.INNER,
|
|
337
|
+
columnAliases?: string[]
|
|
338
|
+
): SelectQueryBuilder<T, TTable> {
|
|
339
|
+
const subAst = resolveSelectQuery(subquery);
|
|
340
|
+
const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
|
|
341
|
+
const nextContext = this.applyAst(this.context, service => service.withJoin(joinNode));
|
|
342
|
+
return this.clone(nextContext);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Adds a join against a function table (e.g., `generate_series`) using `fnTable` internally.
|
|
347
|
+
* @param name - Function name
|
|
348
|
+
* @param args - Optional arguments passed to the function
|
|
349
|
+
* @param alias - Alias for the function table so columns can be referenced
|
|
350
|
+
* @param condition - Join condition expression
|
|
351
|
+
* @param joinKind - Kind of join (defaults to INNER)
|
|
352
|
+
* @param options - Optional metadata (lateral, ordinality, column aliases, schema)
|
|
353
|
+
*/
|
|
354
|
+
joinFunctionTable(
|
|
355
|
+
name: string,
|
|
356
|
+
args: OperandNode[] = [],
|
|
357
|
+
alias: string,
|
|
358
|
+
condition: BinaryExpressionNode,
|
|
359
|
+
joinKind: JoinKind = JOIN_KINDS.INNER,
|
|
360
|
+
options?: { lateral?: boolean; withOrdinality?: boolean; columnAliases?: string[]; schema?: string }
|
|
361
|
+
): SelectQueryBuilder<T, TTable> {
|
|
362
|
+
const functionTable = fnTable(name, args, alias, options);
|
|
363
|
+
const joinNode = createJoinNode(joinKind, functionTable, condition);
|
|
364
|
+
const nextContext = this.applyAst(this.context, service => service.withJoin(joinNode));
|
|
365
|
+
return this.clone(nextContext);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Adds an INNER JOIN to the query
|
|
370
|
+
* @param table - Table to join
|
|
371
|
+
* @param condition - Join condition expression
|
|
372
|
+
* @returns New query builder instance with the INNER JOIN
|
|
373
|
+
*/
|
|
374
|
+
innerJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
375
|
+
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
|
|
376
|
+
return this.clone(nextContext);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Adds a LEFT JOIN to the query
|
|
381
|
+
* @param table - Table to join
|
|
382
|
+
* @param condition - Join condition expression
|
|
383
|
+
* @returns New query builder instance with the LEFT JOIN
|
|
384
|
+
*/
|
|
385
|
+
leftJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
386
|
+
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
|
|
387
|
+
return this.clone(nextContext);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Adds a RIGHT JOIN to the query
|
|
392
|
+
* @param table - Table to join
|
|
393
|
+
* @param condition - Join condition expression
|
|
394
|
+
* @returns New query builder instance with the RIGHT JOIN
|
|
395
|
+
*/
|
|
396
|
+
rightJoin(table: TableDef, condition: BinaryExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
397
|
+
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
|
|
398
|
+
return this.clone(nextContext);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Matches records based on a relationship
|
|
403
|
+
* @param relationName - Name of the relationship to match
|
|
404
|
+
* @param predicate - Optional predicate expression
|
|
405
|
+
* @returns New query builder instance with the relationship match
|
|
406
|
+
*/
|
|
407
|
+
match<K extends keyof TTable['relations'] & string>(
|
|
408
|
+
relationName: K,
|
|
409
|
+
predicate?: ExpressionNode
|
|
410
|
+
): SelectQueryBuilder<T, TTable> {
|
|
411
|
+
const nextContext = this.relationManager.match(this.context, relationName, predicate);
|
|
412
|
+
return this.clone(nextContext);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Joins a related table
|
|
417
|
+
* @param relationName - Name of the relationship to join
|
|
418
|
+
* @param joinKind - Type of join (defaults to INNER)
|
|
419
|
+
* @param extraCondition - Optional additional join condition
|
|
420
|
+
* @returns New query builder instance with the relationship join
|
|
421
|
+
*/
|
|
422
|
+
joinRelation<K extends keyof TTable['relations'] & string>(
|
|
423
|
+
relationName: K,
|
|
424
|
+
joinKind: JoinKind = JOIN_KINDS.INNER,
|
|
425
|
+
extraCondition?: ExpressionNode
|
|
426
|
+
): SelectQueryBuilder<T, TTable> {
|
|
427
|
+
const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
|
|
428
|
+
return this.clone(nextContext);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Includes related data in the query results
|
|
433
|
+
* @param relationName - Name of the relationship to include
|
|
434
|
+
* @param options - Optional include options
|
|
435
|
+
* @returns New query builder instance with the relationship inclusion
|
|
436
|
+
*/
|
|
437
|
+
include<K extends keyof TTable['relations'] & string>(
|
|
438
|
+
relationName: K,
|
|
439
|
+
options?: RelationIncludeOptions
|
|
440
|
+
): SelectQueryBuilder<T, TTable> {
|
|
441
|
+
const nextContext = this.relationManager.include(this.context, relationName, options);
|
|
442
|
+
return this.clone(nextContext);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Includes a relation lazily in the query results
|
|
447
|
+
* @param relationName - Name of the relation to include lazily
|
|
448
|
+
* @returns New query builder instance with lazy relation inclusion
|
|
449
|
+
*/
|
|
450
|
+
includeLazy<K extends keyof RelationMap<TTable>>(relationName: K): SelectQueryBuilder<T, TTable> {
|
|
451
|
+
const nextLazy = new Set(this.lazyRelations);
|
|
452
|
+
nextLazy.add(relationName as string);
|
|
453
|
+
return this.clone(this.context, nextLazy);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Selects columns for a related table in a single hop.
|
|
458
|
+
*/
|
|
459
|
+
selectRelationColumns<
|
|
460
|
+
K extends keyof TTable['relations'] & string,
|
|
461
|
+
TRel extends RelationDef = TTable['relations'][K],
|
|
462
|
+
TTarget extends TableDef = RelationTargetTable<TRel>,
|
|
463
|
+
C extends keyof TTarget['columns'] & string = keyof TTarget['columns'] & string
|
|
464
|
+
>(relationName: K, ...cols: C[]): SelectQueryBuilder<T, TTable> {
|
|
465
|
+
const relation = this.env.table.relations[relationName] as RelationDef | undefined;
|
|
466
|
+
if (!relation) {
|
|
467
|
+
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
468
|
+
}
|
|
469
|
+
const target = relation.target;
|
|
470
|
+
|
|
471
|
+
for (const col of cols) {
|
|
472
|
+
if (!target.columns[col]) {
|
|
473
|
+
throw new Error(
|
|
474
|
+
`Column '${col}' not found on related table '${target.name}' for relation '${relationName}'`
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return this.include(relationName as string, { columns: cols as string[] });
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Convenience alias for selecting specific columns from a relation.
|
|
484
|
+
*/
|
|
485
|
+
includePick<
|
|
486
|
+
K extends keyof TTable['relations'] & string,
|
|
487
|
+
TRel extends RelationDef = TTable['relations'][K],
|
|
488
|
+
TTarget extends TableDef = RelationTargetTable<TRel>,
|
|
489
|
+
C extends keyof TTarget['columns'] & string = keyof TTarget['columns'] & string
|
|
490
|
+
>(relationName: K, cols: C[]): SelectQueryBuilder<T, TTable> {
|
|
491
|
+
return this.selectRelationColumns(relationName, ...cols);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Selects columns for the root table and relations from an array of entries
|
|
497
|
+
* @param config - Configuration array for deep column selection
|
|
498
|
+
* @returns New query builder instance with deep column selections
|
|
499
|
+
*/
|
|
500
|
+
selectColumnsDeep(config: DeepSelectConfig<TTable>): SelectQueryBuilder<T, TTable> {
|
|
501
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
502
|
+
let currBuilder: SelectQueryBuilder<T, TTable> = this;
|
|
503
|
+
|
|
504
|
+
for (const entry of config) {
|
|
505
|
+
if (entry.type === 'root') {
|
|
506
|
+
currBuilder = currBuilder.select(...entry.columns);
|
|
507
|
+
} else {
|
|
508
|
+
currBuilder = currBuilder.selectRelationColumns(entry.relationName, ...(entry.columns as string[]));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return currBuilder;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Gets the list of lazy relations
|
|
517
|
+
* @returns Array of lazy relation names
|
|
518
|
+
*/
|
|
519
|
+
getLazyRelations(): (keyof RelationMap<TTable>)[] {
|
|
520
|
+
return Array.from(this.lazyRelations) as (keyof RelationMap<TTable>)[];
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Gets the table definition for this query builder
|
|
525
|
+
* @returns Table definition
|
|
526
|
+
*/
|
|
527
|
+
getTable(): TTable {
|
|
528
|
+
return this.env.table as TTable;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Executes the query and returns hydrated results
|
|
533
|
+
* @param ctx - ORM session context
|
|
534
|
+
* @returns Promise of entity instances
|
|
535
|
+
*/
|
|
536
|
+
async execute(ctx: OrmSession): Promise<EntityInstance<TTable>[]> {
|
|
537
|
+
return executeHydrated(ctx, this);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Executes the query with provided execution and hydration contexts
|
|
542
|
+
* @param execCtx - Execution context
|
|
543
|
+
* @param hydCtx - Hydration context
|
|
544
|
+
* @returns Promise of entity instances
|
|
545
|
+
*/
|
|
546
|
+
async executeWithContexts(execCtx: ExecutionContext, hydCtx: HydrationContext): Promise<EntityInstance<TTable>[]> {
|
|
547
|
+
return executeHydratedWithContexts(execCtx, hydCtx, this);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Adds a WHERE condition to the query
|
|
552
|
+
* @param expr - Expression for the WHERE clause
|
|
553
|
+
* @returns New query builder instance with the WHERE condition
|
|
554
|
+
*/
|
|
555
|
+
where(expr: ExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
556
|
+
const nextContext = this.applyAst(this.context, service => service.withWhere(expr));
|
|
557
|
+
return this.clone(nextContext);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Adds a GROUP BY clause to the query
|
|
562
|
+
* @param term - Column definition or ordering term to group by
|
|
563
|
+
* @returns New query builder instance with the GROUP BY clause
|
|
564
|
+
*/
|
|
565
|
+
groupBy(term: ColumnDef | OrderingTerm): SelectQueryBuilder<T, TTable> {
|
|
566
|
+
const nextContext = this.applyAst(this.context, service => service.withGroupBy(term));
|
|
567
|
+
return this.clone(nextContext);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Adds a HAVING condition to the query
|
|
572
|
+
* @param expr - Expression for the HAVING clause
|
|
573
|
+
* @returns New query builder instance with the HAVING condition
|
|
574
|
+
*/
|
|
575
|
+
having(expr: ExpressionNode): SelectQueryBuilder<T, TTable> {
|
|
576
|
+
const nextContext = this.applyAst(this.context, service => service.withHaving(expr));
|
|
577
|
+
return this.clone(nextContext);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Adds an ORDER BY clause to the query
|
|
584
|
+
* @param term - Column definition or ordering term to order by
|
|
585
|
+
* @param directionOrOptions - Order direction or options (defaults to ASC)
|
|
586
|
+
* @returns New query builder instance with the ORDER BY clause
|
|
587
|
+
*/
|
|
588
|
+
orderBy(
|
|
589
|
+
term: ColumnDef | OrderingTerm,
|
|
590
|
+
directionOrOptions: OrderDirection | { direction?: OrderDirection; nulls?: 'FIRST' | 'LAST'; collation?: string } = ORDER_DIRECTIONS.ASC
|
|
591
|
+
): SelectQueryBuilder<T, TTable> {
|
|
592
|
+
const options = typeof directionOrOptions === 'string' ? { direction: directionOrOptions } : directionOrOptions;
|
|
593
|
+
const dir = options.direction ?? ORDER_DIRECTIONS.ASC;
|
|
594
|
+
|
|
595
|
+
const nextContext = this.applyAst(this.context, service =>
|
|
596
|
+
service.withOrderBy(term, dir, options.nulls, options.collation)
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
return this.clone(nextContext);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Adds a DISTINCT clause to the query
|
|
604
|
+
* @param cols - Columns to make distinct
|
|
605
|
+
* @returns New query builder instance with the DISTINCT clause
|
|
606
|
+
*/
|
|
607
|
+
distinct(...cols: (ColumnDef | ColumnNode)[]): SelectQueryBuilder<T, TTable> {
|
|
608
|
+
return this.clone(this.columnSelector.distinct(this.context, cols));
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Adds a LIMIT clause to the query
|
|
613
|
+
* @param n - Maximum number of rows to return
|
|
614
|
+
* @returns New query builder instance with the LIMIT clause
|
|
615
|
+
*/
|
|
616
|
+
limit(n: number): SelectQueryBuilder<T, TTable> {
|
|
617
|
+
const nextContext = this.applyAst(this.context, service => service.withLimit(n));
|
|
618
|
+
return this.clone(nextContext);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Adds an OFFSET clause to the query
|
|
623
|
+
* @param n - Number of rows to skip
|
|
624
|
+
* @returns New query builder instance with the OFFSET clause
|
|
625
|
+
*/
|
|
626
|
+
offset(n: number): SelectQueryBuilder<T, TTable> {
|
|
627
|
+
const nextContext = this.applyAst(this.context, service => service.withOffset(n));
|
|
628
|
+
return this.clone(nextContext);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Combines this query with another using UNION
|
|
633
|
+
* @param query - Query to union with
|
|
634
|
+
* @returns New query builder instance with the set operation
|
|
635
|
+
*/
|
|
636
|
+
union<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
637
|
+
return this.clone(this.applySetOperation('UNION', query));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Combines this query with another using UNION ALL
|
|
642
|
+
* @param query - Query to union with
|
|
643
|
+
* @returns New query builder instance with the set operation
|
|
644
|
+
*/
|
|
645
|
+
unionAll<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
646
|
+
return this.clone(this.applySetOperation('UNION ALL', query));
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Combines this query with another using INTERSECT
|
|
651
|
+
* @param query - Query to intersect with
|
|
652
|
+
* @returns New query builder instance with the set operation
|
|
653
|
+
*/
|
|
654
|
+
intersect<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
655
|
+
return this.clone(this.applySetOperation('INTERSECT', query));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Combines this query with another using EXCEPT
|
|
660
|
+
* @param query - Query to subtract
|
|
661
|
+
* @returns New query builder instance with the set operation
|
|
662
|
+
*/
|
|
663
|
+
except<TSub extends TableDef>(query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode): SelectQueryBuilder<T, TTable> {
|
|
664
|
+
return this.clone(this.applySetOperation('EXCEPT', query));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Adds a WHERE EXISTS condition to the query
|
|
669
|
+
* @param subquery - Subquery to check for existence
|
|
670
|
+
* @returns New query builder instance with the WHERE EXISTS condition
|
|
671
|
+
*/
|
|
672
|
+
whereExists<TSub extends TableDef>(
|
|
673
|
+
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
674
|
+
correlate?: ExpressionNode
|
|
675
|
+
): SelectQueryBuilder<T, TTable> {
|
|
676
|
+
const subAst = resolveSelectQuery(subquery);
|
|
677
|
+
const correlated = this.applyCorrelation(subAst, correlate);
|
|
678
|
+
return this.where(exists(correlated));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Adds a WHERE NOT EXISTS condition to the query
|
|
683
|
+
* @param subquery - Subquery to check for non-existence
|
|
684
|
+
* @returns New query builder instance with the WHERE NOT EXISTS condition
|
|
685
|
+
*/
|
|
686
|
+
whereNotExists<TSub extends TableDef>(
|
|
687
|
+
subquery: SelectQueryBuilder<unknown, TSub> | SelectQueryNode,
|
|
688
|
+
correlate?: ExpressionNode
|
|
689
|
+
): SelectQueryBuilder<T, TTable> {
|
|
690
|
+
const subAst = resolveSelectQuery(subquery);
|
|
691
|
+
const correlated = this.applyCorrelation(subAst, correlate);
|
|
692
|
+
return this.where(notExists(correlated));
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Adds a WHERE EXISTS condition based on a relationship
|
|
697
|
+
* @param relationName - Name of the relationship to check
|
|
698
|
+
* @param callback - Optional callback to modify the relationship query
|
|
699
|
+
* @returns New query builder instance with the relationship existence check
|
|
700
|
+
*/
|
|
701
|
+
whereHas<K extends keyof TTable['relations'] & string>(
|
|
702
|
+
relationName: K,
|
|
703
|
+
callbackOrOptions?: RelationCallback | WhereHasOptions,
|
|
704
|
+
maybeOptions?: WhereHasOptions
|
|
705
|
+
): SelectQueryBuilder<T, TTable> {
|
|
706
|
+
const relation = this.env.table.relations[relationName];
|
|
707
|
+
|
|
708
|
+
if (!relation) {
|
|
709
|
+
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions as RelationCallback : undefined;
|
|
713
|
+
const options = (typeof callbackOrOptions === 'function' ? maybeOptions : callbackOrOptions) as WhereHasOptions | undefined;
|
|
714
|
+
|
|
715
|
+
let subQb = this.createChildBuilder<unknown, typeof relation.target>(relation.target);
|
|
716
|
+
|
|
717
|
+
if (callback) {
|
|
718
|
+
subQb = callback(subQb);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const subAst = subQb.getAST();
|
|
722
|
+
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
|
|
723
|
+
|
|
724
|
+
return this.where(exists(finalSubAst));
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Adds a WHERE NOT EXISTS condition based on a relationship
|
|
729
|
+
* @param relationName - Name of the relationship to check
|
|
730
|
+
* @param callback - Optional callback to modify the relationship query
|
|
731
|
+
* @returns New query builder instance with the relationship non-existence check
|
|
732
|
+
*/
|
|
733
|
+
whereHasNot<K extends keyof TTable['relations'] & string>(
|
|
734
|
+
relationName: K,
|
|
735
|
+
callbackOrOptions?: RelationCallback | WhereHasOptions,
|
|
736
|
+
maybeOptions?: WhereHasOptions
|
|
737
|
+
): SelectQueryBuilder<T, TTable> {
|
|
738
|
+
const relation = this.env.table.relations[relationName];
|
|
739
|
+
|
|
740
|
+
if (!relation) {
|
|
741
|
+
throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const callback = typeof callbackOrOptions === 'function' ? callbackOrOptions as RelationCallback : undefined;
|
|
745
|
+
const options = (typeof callbackOrOptions === 'function' ? maybeOptions : callbackOrOptions) as WhereHasOptions | undefined;
|
|
746
|
+
|
|
747
|
+
let subQb = this.createChildBuilder<unknown, typeof relation.target>(relation.target);
|
|
748
|
+
|
|
749
|
+
if (callback) {
|
|
750
|
+
subQb = callback(subQb);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const subAst = subQb.getAST();
|
|
754
|
+
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
|
|
755
|
+
|
|
756
|
+
return this.where(notExists(finalSubAst));
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Compiles the query to SQL for a specific dialect
|
|
763
|
+
* @param dialect - Database dialect to compile for
|
|
764
|
+
* @returns Compiled query with SQL and parameters
|
|
765
|
+
*/
|
|
766
|
+
compile(dialect: SelectDialectInput): CompiledQuery {
|
|
767
|
+
const resolved = resolveDialectInput(dialect);
|
|
768
|
+
return resolved.compileSelect(this.getAST());
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Converts the query to SQL string for a specific dialect
|
|
773
|
+
* @param dialect - Database dialect to generate SQL for
|
|
774
|
+
* @returns SQL string representation of the query
|
|
775
|
+
*/
|
|
776
|
+
toSql(dialect: SelectDialectInput): string {
|
|
777
|
+
return this.compile(dialect).sql;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Gets the hydration plan for the query
|
|
782
|
+
* @returns Hydration plan or undefined if none exists
|
|
783
|
+
*/
|
|
784
|
+
getHydrationPlan(): HydrationPlan | undefined {
|
|
785
|
+
return this.context.hydration.getPlan();
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Gets the Abstract Syntax Tree (AST) representation of the query
|
|
790
|
+
* @returns Query AST with hydration applied
|
|
791
|
+
*/
|
|
792
|
+
getAST(): SelectQueryNode {
|
|
793
|
+
return this.context.hydration.applyToAst(this.context.state.ast);
|
|
794
|
+
}
|
|
795
|
+
}
|