• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 15675468506

16 Jun 2025 08:14AM UTC coverage: 76.35% (+0.02%) from 76.328%
15675468506

Pull #11422

github

web-flow
Merge f8e29bdc2 into 03faa7867
Pull Request #11422: fix(tree-entity): closure junction table primary key definition should match parent table

9286 of 12872 branches covered (72.14%)

Branch coverage included in aggregate %.

18997 of 24172 relevant lines covered (78.59%)

196908.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.98
/src/query-builder/SelectQueryBuilder.ts
1
import { RawSqlResultsToEntityTransformer } from "./transformer/RawSqlResultsToEntityTransformer"
40✔
2
import { ObjectLiteral } from "../common/ObjectLiteral"
3
import { PessimisticLockTransactionRequiredError } from "../error/PessimisticLockTransactionRequiredError"
40✔
4
import { NoVersionOrUpdateDateColumnError } from "../error/NoVersionOrUpdateDateColumnError"
40✔
5
import { OptimisticLockVersionMismatchError } from "../error/OptimisticLockVersionMismatchError"
40✔
6
import { OptimisticLockCanNotBeUsedError } from "../error/OptimisticLockCanNotBeUsedError"
40✔
7
import { JoinAttribute } from "./JoinAttribute"
40✔
8
import { RelationIdAttribute } from "./relation-id/RelationIdAttribute"
40✔
9
import { RelationCountAttribute } from "./relation-count/RelationCountAttribute"
40✔
10
import { RelationIdLoader } from "./relation-id/RelationIdLoader"
40✔
11
import { RelationIdLoader as QueryStrategyRelationIdLoader } from "./RelationIdLoader"
40✔
12
import { RelationIdMetadataToAttributeTransformer } from "./relation-id/RelationIdMetadataToAttributeTransformer"
40✔
13
import { RelationCountLoader } from "./relation-count/RelationCountLoader"
40✔
14
import { RelationCountMetadataToAttributeTransformer } from "./relation-count/RelationCountMetadataToAttributeTransformer"
40✔
15
import { QueryBuilder } from "./QueryBuilder"
40✔
16
import { ReadStream } from "../platform/PlatformTools"
17
import { LockNotSupportedOnGivenDriverError } from "../error/LockNotSupportedOnGivenDriverError"
40✔
18
import { MysqlDriver } from "../driver/mysql/MysqlDriver"
19
import { SelectQuery } from "./SelectQuery"
20
import { EntityMetadata } from "../metadata/EntityMetadata"
21
import { ColumnMetadata } from "../metadata/ColumnMetadata"
22
import { OrderByCondition } from "../find-options/OrderByCondition"
23
import { QueryExpressionMap } from "./QueryExpressionMap"
24
import { EntityTarget } from "../common/EntityTarget"
25
import { QueryRunner } from "../query-runner/QueryRunner"
26
import { WhereExpressionBuilder } from "./WhereExpressionBuilder"
27
import { Brackets } from "./Brackets"
28
import { QueryResultCacheOptions } from "../cache/QueryResultCacheOptions"
29
import { OffsetWithoutLimitNotSupportedError } from "../error/OffsetWithoutLimitNotSupportedError"
40✔
30
import { SelectQueryBuilderOption } from "./SelectQueryBuilderOption"
31
import { ObjectUtils } from "../util/ObjectUtils"
40✔
32
import { DriverUtils } from "../driver/DriverUtils"
40✔
33
import { EntityNotFoundError } from "../error/EntityNotFoundError"
40✔
34
import { TypeORMError } from "../error"
40✔
35
import { FindManyOptions } from "../find-options/FindManyOptions"
36
import { FindOptionsSelect } from "../find-options/FindOptionsSelect"
37
import { RelationMetadata } from "../metadata/RelationMetadata"
38
import { FindOptionsOrder } from "../find-options/FindOptionsOrder"
39
import { FindOptionsWhere } from "../find-options/FindOptionsWhere"
40
import { FindOptionsUtils } from "../find-options/FindOptionsUtils"
40✔
41
import { FindOptionsRelations } from "../find-options/FindOptionsRelations"
42
import { OrmUtils } from "../util/OrmUtils"
40✔
43
import { EntityPropertyNotFoundError } from "../error/EntityPropertyNotFoundError"
40✔
44
import { AuroraMysqlDriver } from "../driver/aurora-mysql/AuroraMysqlDriver"
45
import { InstanceChecker } from "../util/InstanceChecker"
40✔
46
import { FindOperator } from "../find-options/FindOperator"
40✔
47
import { ApplyValueTransformers } from "../util/ApplyValueTransformers"
40✔
48
import { SqlServerDriver } from "../driver/sqlserver/SqlServerDriver"
49

50
/**
51
 * Allows to build complex sql queries in a fashion way and execute those queries.
52
 */
53
export class SelectQueryBuilder<Entity extends ObjectLiteral>
40✔
54
    extends QueryBuilder<Entity>
55
    implements WhereExpressionBuilder
56
{
57
    readonly "@instanceof" = Symbol.for("SelectQueryBuilder")
1,196,102✔
58

59
    protected findOptions: FindManyOptions = {}
1,196,102✔
60
    protected selects: string[] = []
1,196,102✔
61
    protected joins: {
1,196,102✔
62
        type: "inner" | "left"
63
        alias: string
64
        parentAlias: string
65
        relationMetadata: RelationMetadata
66
        select: boolean
67
        selection: FindOptionsSelect<any> | undefined
68
    }[] = []
69
    protected conditions: string = ""
1,196,102✔
70
    protected orderBys: {
1,196,102✔
71
        alias: string
72
        direction: "ASC" | "DESC"
73
        nulls?: "NULLS FIRST" | "NULLS LAST"
74
    }[] = []
75
    protected relationMetadatas: RelationMetadata[] = []
1,196,102✔
76

77
    // -------------------------------------------------------------------------
78
    // Public Implemented Methods
79
    // -------------------------------------------------------------------------
80

81
    /**
82
     * Gets generated SQL query without parameters being replaced.
83
     */
84
    getQuery(): string {
85
        let sql = this.createComment()
763,640✔
86
        sql += this.createCteExpression()
763,640✔
87
        sql += this.createSelectExpression()
763,640✔
88
        sql += this.createJoinExpression()
763,640✔
89
        sql += this.createWhereExpression()
763,640✔
90
        sql += this.createGroupByExpression()
763,640✔
91
        sql += this.createHavingExpression()
763,640✔
92
        sql += this.createOrderByExpression()
763,640✔
93
        sql += this.createLimitOffsetExpression()
763,640✔
94
        sql += this.createLockExpression()
763,632✔
95
        sql = sql.trim()
763,496✔
96
        if (this.expressionMap.subQuery) sql = "(" + sql + ")"
763,496✔
97
        return this.replacePropertyNamesForTheWholeQuery(sql)
763,496✔
98
    }
99

100
    // -------------------------------------------------------------------------
101
    // Public Methods
102
    // -------------------------------------------------------------------------
103

104
    setFindOptions(findOptions: FindManyOptions<Entity>) {
105
        this.findOptions = findOptions
264,181✔
106
        this.applyFindOptions()
264,181✔
107
        return this
263,692✔
108
    }
109

110
    /**
111
     * Creates a subquery - query that can be used inside other queries.
112
     */
113
    subQuery(): SelectQueryBuilder<any> {
114
        const qb = this.createQueryBuilder()
3,999✔
115
        qb.expressionMap.subQuery = true
3,999✔
116
        qb.parentQueryBuilder = this
3,999✔
117
        return qb
3,999✔
118
    }
119

120
    /**
121
     * Creates SELECT query.
122
     * Replaces all previous selections if they exist.
123
     */
124
    select(): this
125

126
    /**
127
     * Creates SELECT query.
128
     * Replaces all previous selections if they exist.
129
     */
130
    select(
131
        selection: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>,
132
        selectionAliasName?: string,
133
    ): this
134

135
    /**
136
     * Creates SELECT query and selects given data.
137
     * Replaces all previous selections if they exist.
138
     */
139
    select(selection: string, selectionAliasName?: string): this
140

141
    /**
142
     * Creates SELECT query and selects given data.
143
     * Replaces all previous selections if they exist.
144
     */
145
    select(selection: string[]): this
146

147
    /**
148
     * Creates SELECT query and selects given data.
149
     * Replaces all previous selections if they exist.
150
     */
151
    select(
152
        selection?:
153
            | string
154
            | string[]
155
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
156
        selectionAliasName?: string,
157
    ): SelectQueryBuilder<Entity> {
158
        this.expressionMap.queryType = "select"
747,192✔
159
        if (Array.isArray(selection)) {
747,192✔
160
            this.expressionMap.selects = selection.map((selection) => ({
11,445✔
161
                selection: selection,
162
            }))
163
        } else if (typeof selection === "function") {
736,686!
164
            const subQueryBuilder = selection(this.subQuery())
×
165
            this.setParameters(subQueryBuilder.getParameters())
×
166
            this.expressionMap.selects.push({
×
167
                selection: subQueryBuilder.getQuery(),
168
                aliasName: selectionAliasName,
169
            })
170
        } else if (selection) {
736,686✔
171
            this.expressionMap.selects = [
732,617✔
172
                { selection: selection, aliasName: selectionAliasName },
173
            ]
174
        }
175

176
        return this
747,192✔
177
    }
178

179
    /**
180
     * Adds new selection to the SELECT query.
181
     */
182
    addSelect(
183
        selection: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>,
184
        selectionAliasName?: string,
185
    ): this
186

187
    /**
188
     * Adds new selection to the SELECT query.
189
     */
190
    addSelect(selection: string, selectionAliasName?: string): this
191

192
    /**
193
     * Adds new selection to the SELECT query.
194
     */
195
    addSelect(selection: string[]): this
196

197
    /**
198
     * Adds new selection to the SELECT query.
199
     */
200
    addSelect(
201
        selection:
202
            | string
203
            | string[]
204
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
205
        selectionAliasName?: string,
206
    ): this {
207
        if (!selection) return this
3,428,225✔
208

209
        if (Array.isArray(selection)) {
3,418,079✔
210
            this.expressionMap.selects = this.expressionMap.selects.concat(
9,750✔
211
                selection.map((selection) => ({ selection: selection })),
13,267✔
212
            )
213
        } else if (typeof selection === "function") {
3,408,329✔
214
            const subQueryBuilder = selection(this.subQuery())
40✔
215
            this.setParameters(subQueryBuilder.getParameters())
40✔
216
            this.expressionMap.selects.push({
40✔
217
                selection: subQueryBuilder.getQuery(),
218
                aliasName: selectionAliasName,
219
            })
220
        } else if (selection) {
3,408,289✔
221
            this.expressionMap.selects.push({
3,408,289✔
222
                selection: selection,
223
                aliasName: selectionAliasName,
224
            })
225
        }
226

227
        return this
3,418,079✔
228
    }
229

230
    /**
231
     * Set max execution time.
232
     * @param milliseconds
233
     */
234
    maxExecutionTime(milliseconds: number): this {
235
        this.expressionMap.maxExecutionTime = milliseconds
×
236
        return this
×
237
    }
238

239
    /**
240
     * Sets whether the selection is DISTINCT.
241
     */
242
    distinct(distinct: boolean = true): this {
44✔
243
        this.expressionMap.selectDistinct = distinct
44✔
244
        return this
44✔
245
    }
246

247
    /**
248
     * Sets the distinct on clause for Postgres.
249
     */
250
    distinctOn(distinctOn: string[]): this {
251
        this.expressionMap.selectDistinctOn = distinctOn
18✔
252
        return this
18✔
253
    }
254

255
    fromDummy(): SelectQueryBuilder<any> {
256
        return this.from(
354✔
257
            this.connection.driver.dummyTableName ??
672✔
258
                "(SELECT 1 AS dummy_column)",
259
            "dummy_table",
260
        )
261
    }
262

263
    /**
264
     * Specifies FROM which entity's table select/update/delete will be executed.
265
     * Also sets a main string alias of the selection data.
266
     * Removes all previously set from-s.
267
     */
268
    from<T extends ObjectLiteral>(
269
        entityTarget: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>,
270
        aliasName: string,
271
    ): SelectQueryBuilder<T>
272

273
    /**
274
     * Specifies FROM which entity's table select/update/delete will be executed.
275
     * Also sets a main string alias of the selection data.
276
     * Removes all previously set from-s.
277
     */
278
    from<T extends ObjectLiteral>(
279
        entityTarget: EntityTarget<T>,
280
        aliasName: string,
281
    ): SelectQueryBuilder<T>
282

283
    /**
284
     * Specifies FROM which entity's table select/update/delete will be executed.
285
     * Also sets a main string alias of the selection data.
286
     * Removes all previously set from-s.
287
     */
288
    from<T extends ObjectLiteral>(
289
        entityTarget:
290
            | EntityTarget<T>
291
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
292
        aliasName: string,
293
    ): SelectQueryBuilder<T> {
294
        const mainAlias = this.createFromAlias(entityTarget, aliasName)
755,273✔
295
        this.expressionMap.setMainAlias(mainAlias)
755,273✔
296
        return this as any as SelectQueryBuilder<T>
755,273✔
297
    }
298

299
    /**
300
     * Specifies FROM which entity's table select/update/delete will be executed.
301
     * Also sets a main string alias of the selection data.
302
     */
303
    addFrom<T extends ObjectLiteral>(
304
        entityTarget: (qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>,
305
        aliasName: string,
306
    ): SelectQueryBuilder<T>
307

308
    /**
309
     * Specifies FROM which entity's table select/update/delete will be executed.
310
     * Also sets a main string alias of the selection data.
311
     */
312
    addFrom<T extends ObjectLiteral>(
313
        entityTarget: EntityTarget<T>,
314
        aliasName: string,
315
    ): SelectQueryBuilder<T>
316

317
    /**
318
     * Specifies FROM which entity's table select/update/delete will be executed.
319
     * Also sets a main string alias of the selection data.
320
     */
321
    addFrom<T extends ObjectLiteral>(
322
        entityTarget:
323
            | EntityTarget<T>
324
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
325
        aliasName: string,
326
    ): SelectQueryBuilder<T> {
327
        const alias = this.createFromAlias(entityTarget, aliasName)
79✔
328
        if (!this.expressionMap.mainAlias)
79!
329
            this.expressionMap.setMainAlias(alias)
×
330

331
        return this as any as SelectQueryBuilder<T>
79✔
332
    }
333

334
    /**
335
     * INNER JOINs (without selection) given subquery.
336
     * You also need to specify an alias of the joined data.
337
     * Optionally, you can add condition and parameters used in condition.
338
     */
339
    innerJoin(
340
        subQueryFactory: (
341
            qb: SelectQueryBuilder<any>,
342
        ) => SelectQueryBuilder<any>,
343
        alias: string,
344
        condition?: string,
345
        parameters?: ObjectLiteral,
346
    ): this
347

348
    /**
349
     * INNER JOINs (without selection) entity's property.
350
     * Given entity property should be a relation.
351
     * You also need to specify an alias of the joined data.
352
     * Optionally, you can add condition and parameters used in condition.
353
     */
354
    innerJoin(
355
        property: string,
356
        alias: string,
357
        condition?: string,
358
        parameters?: ObjectLiteral,
359
    ): this
360

361
    /**
362
     * INNER JOINs (without selection) given entity's table.
363
     * You also need to specify an alias of the joined data.
364
     * Optionally, you can add condition and parameters used in condition.
365
     */
366
    innerJoin(
367
        entity: Function | string,
368
        alias: string,
369
        condition?: string,
370
        parameters?: ObjectLiteral,
371
    ): this
372

373
    /**
374
     * INNER JOINs (without selection) given table.
375
     * You also need to specify an alias of the joined data.
376
     * Optionally, you can add condition and parameters used in condition.
377
     */
378
    innerJoin(
379
        tableName: string,
380
        alias: string,
381
        condition?: string,
382
        parameters?: ObjectLiteral,
383
    ): this
384

385
    /**
386
     * INNER JOINs (without selection).
387
     * You also need to specify an alias of the joined data.
388
     * Optionally, you can add condition and parameters used in condition.
389
     */
390
    innerJoin(
391
        entityOrProperty:
392
            | Function
393
            | string
394
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
395
        alias: string,
396
        condition?: string,
397
        parameters?: ObjectLiteral,
398
    ): this {
399
        this.join("INNER", entityOrProperty, alias, condition, parameters)
16,804✔
400
        return this
16,804✔
401
    }
402

403
    /**
404
     * LEFT JOINs (without selection) given subquery.
405
     * You also need to specify an alias of the joined data.
406
     * Optionally, you can add condition and parameters used in condition.
407
     */
408
    leftJoin(
409
        subQueryFactory: (
410
            qb: SelectQueryBuilder<any>,
411
        ) => SelectQueryBuilder<any>,
412
        alias: string,
413
        condition?: string,
414
        parameters?: ObjectLiteral,
415
    ): this
416

417
    /**
418
     * LEFT JOINs (without selection) entity's property.
419
     * Given entity property should be a relation.
420
     * You also need to specify an alias of the joined data.
421
     * Optionally, you can add condition and parameters used in condition.
422
     */
423
    leftJoin(
424
        property: string,
425
        alias: string,
426
        condition?: string,
427
        parameters?: ObjectLiteral,
428
    ): this
429

430
    /**
431
     * LEFT JOINs (without selection) entity's table.
432
     * You also need to specify an alias of the joined data.
433
     * Optionally, you can add condition and parameters used in condition.
434
     */
435
    leftJoin(
436
        entity: Function | string,
437
        alias: string,
438
        condition?: string,
439
        parameters?: ObjectLiteral,
440
    ): this
441

442
    /**
443
     * LEFT JOINs (without selection) given table.
444
     * You also need to specify an alias of the joined data.
445
     * Optionally, you can add condition and parameters used in condition.
446
     */
447
    leftJoin(
448
        tableName: string,
449
        alias: string,
450
        condition?: string,
451
        parameters?: ObjectLiteral,
452
    ): this
453

454
    /**
455
     * LEFT JOINs (without selection).
456
     * You also need to specify an alias of the joined data.
457
     * Optionally, you can add condition and parameters used in condition.
458
     */
459
    leftJoin(
460
        entityOrProperty:
461
            | Function
462
            | string
463
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
464
        alias: string,
465
        condition?: string,
466
        parameters?: ObjectLiteral,
467
    ): this {
468
        this.join("LEFT", entityOrProperty, alias, condition, parameters)
3,370,240✔
469
        return this
3,370,240✔
470
    }
471

472
    /**
473
     * INNER JOINs given subquery and adds all selection properties to SELECT..
474
     * You also need to specify an alias of the joined data.
475
     * Optionally, you can add condition and parameters used in condition.
476
     */
477
    innerJoinAndSelect(
478
        subQueryFactory: (
479
            qb: SelectQueryBuilder<any>,
480
        ) => SelectQueryBuilder<any>,
481
        alias: string,
482
        condition?: string,
483
        parameters?: ObjectLiteral,
484
    ): this
485

486
    /**
487
     * INNER JOINs entity's property and adds all selection properties to SELECT.
488
     * Given entity property should be a relation.
489
     * You also need to specify an alias of the joined data.
490
     * Optionally, you can add condition and parameters used in condition.
491
     */
492
    innerJoinAndSelect(
493
        property: string,
494
        alias: string,
495
        condition?: string,
496
        parameters?: ObjectLiteral,
497
    ): this
498

499
    /**
500
     * INNER JOINs entity and adds all selection properties to SELECT.
501
     * You also need to specify an alias of the joined data.
502
     * Optionally, you can add condition and parameters used in condition.
503
     */
504
    innerJoinAndSelect(
505
        entity: Function | string,
506
        alias: string,
507
        condition?: string,
508
        parameters?: ObjectLiteral,
509
    ): this
510

511
    /**
512
     * INNER JOINs table and adds all selection properties to SELECT.
513
     * You also need to specify an alias of the joined data.
514
     * Optionally, you can add condition and parameters used in condition.
515
     */
516
    innerJoinAndSelect(
517
        tableName: string,
518
        alias: string,
519
        condition?: string,
520
        parameters?: ObjectLiteral,
521
    ): this
522

523
    /**
524
     * INNER JOINs and adds all selection properties to SELECT.
525
     * You also need to specify an alias of the joined data.
526
     * Optionally, you can add condition and parameters used in condition.
527
     */
528
    innerJoinAndSelect(
529
        entityOrProperty:
530
            | Function
531
            | string
532
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
533
        alias: string,
534
        condition?: string,
535
        parameters?: ObjectLiteral,
536
    ): this {
537
        this.addSelect(alias)
1,099✔
538
        this.innerJoin(entityOrProperty, alias, condition, parameters)
1,099✔
539
        return this
1,099✔
540
    }
541

542
    /**
543
     * LEFT JOINs given subquery and adds all selection properties to SELECT..
544
     * You also need to specify an alias of the joined data.
545
     * Optionally, you can add condition and parameters used in condition.
546
     */
547
    leftJoinAndSelect(
548
        subQueryFactory: (
549
            qb: SelectQueryBuilder<any>,
550
        ) => SelectQueryBuilder<any>,
551
        alias: string,
552
        condition?: string,
553
        parameters?: ObjectLiteral,
554
    ): this
555

556
    /**
557
     * LEFT JOINs entity's property and adds all selection properties to SELECT.
558
     * Given entity property should be a relation.
559
     * You also need to specify an alias of the joined data.
560
     * Optionally, you can add condition and parameters used in condition.
561
     */
562
    leftJoinAndSelect(
563
        property: string,
564
        alias: string,
565
        condition?: string,
566
        parameters?: ObjectLiteral,
567
    ): this
568

569
    /**
570
     * LEFT JOINs entity and adds all selection properties to SELECT.
571
     * You also need to specify an alias of the joined data.
572
     * Optionally, you can add condition and parameters used in condition.
573
     */
574
    leftJoinAndSelect(
575
        entity: Function | string,
576
        alias: string,
577
        condition?: string,
578
        parameters?: ObjectLiteral,
579
    ): this
580

581
    /**
582
     * LEFT JOINs table and adds all selection properties to SELECT.
583
     * You also need to specify an alias of the joined data.
584
     * Optionally, you can add condition and parameters used in condition.
585
     */
586
    leftJoinAndSelect(
587
        tableName: string,
588
        alias: string,
589
        condition?: string,
590
        parameters?: ObjectLiteral,
591
    ): this
592

593
    /**
594
     * LEFT JOINs and adds all selection properties to SELECT.
595
     * You also need to specify an alias of the joined data.
596
     * Optionally, you can add condition and parameters used in condition.
597
     */
598
    leftJoinAndSelect(
599
        entityOrProperty:
600
            | Function
601
            | string
602
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
603
        alias: string,
604
        condition?: string,
605
        parameters?: ObjectLiteral,
606
    ): this {
607
        this.addSelect(alias)
3,363,970✔
608
        this.leftJoin(entityOrProperty, alias, condition, parameters)
3,363,970✔
609
        return this
3,363,970✔
610
    }
611

612
    /**
613
     * INNER JOINs given subquery, SELECTs the data returned by a join and MAPs all that data to some entity's property.
614
     * This is extremely useful when you want to select some data and map it to some virtual property.
615
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
616
     * Given entity property should be a relation.
617
     * You also need to specify an alias of the joined data.
618
     * Optionally, you can add condition and parameters used in condition.
619
     */
620
    innerJoinAndMapMany(
621
        mapToProperty: string,
622
        subQueryFactory: (
623
            qb: SelectQueryBuilder<any>,
624
        ) => SelectQueryBuilder<any>,
625
        alias: string,
626
        condition?: string,
627
        parameters?: ObjectLiteral,
628
    ): this
629

630
    /**
631
     * INNER JOINs entity's property, SELECTs the data returned by a join and MAPs all that data to some entity's property.
632
     * This is extremely useful when you want to select some data and map it to some virtual property.
633
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
634
     * Given entity property should be a relation.
635
     * You also need to specify an alias of the joined data.
636
     * Optionally, you can add condition and parameters used in condition.
637
     */
638
    innerJoinAndMapMany(
639
        mapToProperty: string,
640
        property: string,
641
        alias: string,
642
        condition?: string,
643
        parameters?: ObjectLiteral,
644
    ): this
645

646
    /**
647
     * INNER JOINs entity's table, SELECTs the data returned by a join and MAPs all that data to some entity's property.
648
     * This is extremely useful when you want to select some data and map it to some virtual property.
649
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
650
     * You also need to specify an alias of the joined data.
651
     * Optionally, you can add condition and parameters used in condition.
652
     */
653
    innerJoinAndMapMany(
654
        mapToProperty: string,
655
        entity: Function | string,
656
        alias: string,
657
        condition?: string,
658
        parameters?: ObjectLiteral,
659
    ): this
660

661
    /**
662
     * INNER JOINs table, SELECTs the data returned by a join and MAPs all that data to some entity's property.
663
     * This is extremely useful when you want to select some data and map it to some virtual property.
664
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
665
     * You also need to specify an alias of the joined data.
666
     * Optionally, you can add condition and parameters used in condition.
667
     */
668
    innerJoinAndMapMany(
669
        mapToProperty: string,
670
        tableName: string,
671
        alias: string,
672
        condition?: string,
673
        parameters?: ObjectLiteral,
674
    ): this
675

676
    /**
677
     * INNER JOINs, SELECTs the data returned by a join and MAPs all that data to some entity's property.
678
     * This is extremely useful when you want to select some data and map it to some virtual property.
679
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
680
     * You also need to specify an alias of the joined data.
681
     * Optionally, you can add condition and parameters used in condition.
682
     */
683
    innerJoinAndMapMany(
684
        mapToProperty: string,
685
        entityOrProperty:
686
            | Function
687
            | string
688
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
689
        alias: string,
690
        condition?: string,
691
        parameters?: ObjectLiteral,
692
    ): this {
693
        this.addSelect(alias)
400✔
694
        this.join(
400✔
695
            "INNER",
696
            entityOrProperty,
697
            alias,
698
            condition,
699
            parameters,
700
            mapToProperty,
701
            true,
702
        )
703
        return this
400✔
704
    }
705

706
    /**
707
     * INNER JOINs given subquery, SELECTs the data returned by a join and MAPs all that data to some entity's property.
708
     * This is extremely useful when you want to select some data and map it to some virtual property.
709
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
710
     * Given entity property should be a relation.
711
     * You also need to specify an alias of the joined data.
712
     * Optionally, you can add condition and parameters used in condition.
713
     */
714
    innerJoinAndMapOne(
715
        mapToProperty: string,
716
        subQueryFactory: (
717
            qb: SelectQueryBuilder<any>,
718
        ) => SelectQueryBuilder<any>,
719
        alias: string,
720
        condition?: string,
721
        parameters?: ObjectLiteral,
722
        mapAsEntity?: Function | string,
723
    ): this
724

725
    /**
726
     * INNER JOINs entity's property, SELECTs the data returned by a join and MAPs all that data to some entity's property.
727
     * This is extremely useful when you want to select some data and map it to some virtual property.
728
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
729
     * Given entity property should be a relation.
730
     * You also need to specify an alias of the joined data.
731
     * Optionally, you can add condition and parameters used in condition.
732
     */
733
    innerJoinAndMapOne(
734
        mapToProperty: string,
735
        property: string,
736
        alias: string,
737
        condition?: string,
738
        parameters?: ObjectLiteral,
739
    ): this
740

741
    /**
742
     * INNER JOINs entity's table, SELECTs the data returned by a join and MAPs all that data to some entity's property.
743
     * This is extremely useful when you want to select some data and map it to some virtual property.
744
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
745
     * You also need to specify an alias of the joined data.
746
     * Optionally, you can add condition and parameters used in condition.
747
     */
748
    innerJoinAndMapOne(
749
        mapToProperty: string,
750
        entity: Function | string,
751
        alias: string,
752
        condition?: string,
753
        parameters?: ObjectLiteral,
754
    ): this
755

756
    /**
757
     * INNER JOINs table, SELECTs the data returned by a join and MAPs all that data to some entity's property.
758
     * This is extremely useful when you want to select some data and map it to some virtual property.
759
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
760
     * You also need to specify an alias of the joined data.
761
     * Optionally, you can add condition and parameters used in condition.
762
     */
763
    innerJoinAndMapOne(
764
        mapToProperty: string,
765
        tableName: string,
766
        alias: string,
767
        condition?: string,
768
        parameters?: ObjectLiteral,
769
    ): this
770

771
    /**
772
     * INNER JOINs, SELECTs the data returned by a join and MAPs all that data to some entity's property.
773
     * This is extremely useful when you want to select some data and map it to some virtual property.
774
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
775
     * You also need to specify an alias of the joined data.
776
     * Optionally, you can add condition and parameters used in condition.
777
     */
778
    innerJoinAndMapOne(
779
        mapToProperty: string,
780
        entityOrProperty:
781
            | Function
782
            | string
783
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
784
        alias: string,
785
        condition?: string,
786
        parameters?: ObjectLiteral,
787
        mapAsEntity?: Function | string,
788
    ): this {
789
        this.addSelect(alias)
280✔
790
        this.join(
280✔
791
            "INNER",
792
            entityOrProperty,
793
            alias,
794
            condition,
795
            parameters,
796
            mapToProperty,
797
            false,
798
            mapAsEntity,
799
        )
800
        return this
280✔
801
    }
802

803
    /**
804
     * LEFT JOINs given subquery, SELECTs the data returned by a join and MAPs all that data to some entity's property.
805
     * This is extremely useful when you want to select some data and map it to some virtual property.
806
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
807
     * Given entity property should be a relation.
808
     * You also need to specify an alias of the joined data.
809
     * Optionally, you can add condition and parameters used in condition.
810
     */
811
    leftJoinAndMapMany(
812
        mapToProperty: string,
813
        subQueryFactory: (
814
            qb: SelectQueryBuilder<any>,
815
        ) => SelectQueryBuilder<any>,
816
        alias: string,
817
        condition?: string,
818
        parameters?: ObjectLiteral,
819
    ): this
820

821
    /**
822
     * LEFT JOINs entity's property, SELECTs the data returned by a join and MAPs all that data to some entity's property.
823
     * This is extremely useful when you want to select some data and map it to some virtual property.
824
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
825
     * Given entity property should be a relation.
826
     * You also need to specify an alias of the joined data.
827
     * Optionally, you can add condition and parameters used in condition.
828
     */
829
    leftJoinAndMapMany(
830
        mapToProperty: string,
831
        property: string,
832
        alias: string,
833
        condition?: string,
834
        parameters?: ObjectLiteral,
835
    ): this
836

837
    /**
838
     * LEFT JOINs entity's table, SELECTs the data returned by a join and MAPs all that data to some entity's property.
839
     * This is extremely useful when you want to select some data and map it to some virtual property.
840
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
841
     * You also need to specify an alias of the joined data.
842
     * Optionally, you can add condition and parameters used in condition.
843
     */
844
    leftJoinAndMapMany(
845
        mapToProperty: string,
846
        entity: Function | string,
847
        alias: string,
848
        condition?: string,
849
        parameters?: ObjectLiteral,
850
    ): this
851

852
    /**
853
     * LEFT JOINs table, SELECTs the data returned by a join and MAPs all that data to some entity's property.
854
     * This is extremely useful when you want to select some data and map it to some virtual property.
855
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
856
     * You also need to specify an alias of the joined data.
857
     * Optionally, you can add condition and parameters used in condition.
858
     */
859
    leftJoinAndMapMany(
860
        mapToProperty: string,
861
        tableName: string,
862
        alias: string,
863
        condition?: string,
864
        parameters?: ObjectLiteral,
865
    ): this
866

867
    /**
868
     * LEFT JOINs, SELECTs the data returned by a join and MAPs all that data to some entity's property.
869
     * This is extremely useful when you want to select some data and map it to some virtual property.
870
     * It will assume that there are multiple rows of selecting data, and mapped result will be an array.
871
     * You also need to specify an alias of the joined data.
872
     * Optionally, you can add condition and parameters used in condition.
873
     */
874
    leftJoinAndMapMany(
875
        mapToProperty: string,
876
        entityOrProperty:
877
            | Function
878
            | string
879
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
880
        alias: string,
881
        condition?: string,
882
        parameters?: ObjectLiteral,
883
    ): this {
884
        this.addSelect(alias)
603✔
885
        this.join(
603✔
886
            "LEFT",
887
            entityOrProperty,
888
            alias,
889
            condition,
890
            parameters,
891
            mapToProperty,
892
            true,
893
        )
894
        return this
603✔
895
    }
896

897
    /**
898
     * LEFT JOINs given subquery, SELECTs the data returned by a join and MAPs all that data to some entity's property.
899
     * This is extremely useful when you want to select some data and map it to some virtual property.
900
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
901
     * Given entity property should be a relation.
902
     * You also need to specify an alias of the joined data.
903
     * Optionally, you can add condition and parameters used in condition.
904
     */
905
    leftJoinAndMapOne(
906
        mapToProperty: string,
907
        subQueryFactory: (
908
            qb: SelectQueryBuilder<any>,
909
        ) => SelectQueryBuilder<any>,
910
        alias: string,
911
        condition?: string,
912
        parameters?: ObjectLiteral,
913
        mapAsEntity?: Function | string,
914
    ): this
915

916
    /**
917
     * LEFT JOINs entity's property, SELECTs the data returned by a join and MAPs all that data to some entity's property.
918
     * This is extremely useful when you want to select some data and map it to some virtual property.
919
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
920
     * Given entity property should be a relation.
921
     * You also need to specify an alias of the joined data.
922
     * Optionally, you can add condition and parameters used in condition.
923
     */
924
    leftJoinAndMapOne(
925
        mapToProperty: string,
926
        property: string,
927
        alias: string,
928
        condition?: string,
929
        parameters?: ObjectLiteral,
930
    ): this
931

932
    /**
933
     * LEFT JOINs entity's table, SELECTs the data returned by a join and MAPs all that data to some entity's property.
934
     * This is extremely useful when you want to select some data and map it to some virtual property.
935
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
936
     * You also need to specify an alias of the joined data.
937
     * Optionally, you can add condition and parameters used in condition.
938
     */
939
    leftJoinAndMapOne(
940
        mapToProperty: string,
941
        entity: Function | string,
942
        alias: string,
943
        condition?: string,
944
        parameters?: ObjectLiteral,
945
    ): this
946

947
    /**
948
     * LEFT JOINs table, SELECTs the data returned by a join and MAPs all that data to some entity's property.
949
     * This is extremely useful when you want to select some data and map it to some virtual property.
950
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
951
     * You also need to specify an alias of the joined data.
952
     * Optionally, you can add condition and parameters used in condition.
953
     */
954
    leftJoinAndMapOne(
955
        mapToProperty: string,
956
        tableName: string,
957
        alias: string,
958
        condition?: string,
959
        parameters?: ObjectLiteral,
960
    ): this
961

962
    /**
963
     * LEFT JOINs, SELECTs the data returned by a join and MAPs all that data to some entity's property.
964
     * This is extremely useful when you want to select some data and map it to some virtual property.
965
     * It will assume that there is a single row of selecting data, and mapped result will be a single selected value.
966
     * You also need to specify an alias of the joined data.
967
     * Optionally, you can add condition and parameters used in condition.
968
     */
969
    leftJoinAndMapOne(
970
        mapToProperty: string,
971
        entityOrProperty:
972
            | Function
973
            | string
974
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
975
        alias: string,
976
        condition?: string,
977
        parameters?: ObjectLiteral,
978
        mapAsEntity?: Function | string,
979
    ): this {
980
        this.addSelect(alias)
403✔
981
        this.join(
403✔
982
            "LEFT",
983
            entityOrProperty,
984
            alias,
985
            condition,
986
            parameters,
987
            mapToProperty,
988
            false,
989
            mapAsEntity,
990
        )
991
        return this
403✔
992
    }
993

994
    /**
995
     */
996
    // selectAndMap(mapToProperty: string, property: string, aliasName: string, qbFactory: ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>)): this;
997

998
    /**
999
     */
1000
    // selectAndMap(mapToProperty: string, entity: Function|string, aliasName: string, qbFactory: ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>)): this;
1001

1002
    /**
1003
     */
1004
    // selectAndMap(mapToProperty: string, tableName: string, aliasName: string, qbFactory: ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>)): this;
1005

1006
    /**
1007
     */
1008
    // selectAndMap(mapToProperty: string, entityOrProperty: Function|string, aliasName: string, qbFactory: ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>)): this {
1009
    //     const select = new SelectAttribute(this.expressionMap);
1010
    //     select.mapToProperty = mapToProperty;
1011
    //     select.entityOrProperty = entityOrProperty;
1012
    //     select.aliasName = aliasName;
1013
    //     select.qbFactory = qbFactory;
1014
    //     return this;
1015
    // }
1016

1017
    /**
1018
     * LEFT JOINs relation id and maps it into some entity's property.
1019
     * Optionally, you can add condition and parameters used in condition.
1020
     */
1021
    loadRelationIdAndMap(
1022
        mapToProperty: string,
1023
        relationName: string,
1024
        options?: { disableMixedMap?: boolean },
1025
    ): this
1026

1027
    /**
1028
     * LEFT JOINs relation id and maps it into some entity's property.
1029
     * Optionally, you can add condition and parameters used in condition.
1030
     */
1031
    loadRelationIdAndMap(
1032
        mapToProperty: string,
1033
        relationName: string,
1034
        alias: string,
1035
        queryBuilderFactory: (
1036
            qb: SelectQueryBuilder<any>,
1037
        ) => SelectQueryBuilder<any>,
1038
    ): this
1039

1040
    /**
1041
     * LEFT JOINs relation id and maps it into some entity's property.
1042
     * Optionally, you can add condition and parameters used in condition.
1043
     */
1044
    loadRelationIdAndMap(
1045
        mapToProperty: string,
1046
        relationName: string,
1047
        aliasNameOrOptions?: string | { disableMixedMap?: boolean },
1048
        queryBuilderFactory?: (
1049
            qb: SelectQueryBuilder<any>,
1050
        ) => SelectQueryBuilder<any>,
1051
    ): this {
1052
        const relationIdAttribute = new RelationIdAttribute(this.expressionMap)
47,727✔
1053
        relationIdAttribute.mapToProperty = mapToProperty
47,727✔
1054
        relationIdAttribute.relationName = relationName
47,727✔
1055
        if (typeof aliasNameOrOptions === "string")
47,727✔
1056
            relationIdAttribute.alias = aliasNameOrOptions
760✔
1057
        if (
47,727✔
1058
            typeof aliasNameOrOptions === "object" &&
90,414✔
1059
            (aliasNameOrOptions as any).disableMixedMap
1060
        )
1061
            relationIdAttribute.disableMixedMap = true
42,687✔
1062

1063
        relationIdAttribute.queryBuilderFactory = queryBuilderFactory
47,727✔
1064
        this.expressionMap.relationIdAttributes.push(relationIdAttribute)
47,727✔
1065

1066
        if (relationIdAttribute.relation.junctionEntityMetadata) {
47,727✔
1067
            this.expressionMap.createAlias({
24,187✔
1068
                type: "other",
1069
                name: relationIdAttribute.junctionAlias,
1070
                metadata: relationIdAttribute.relation.junctionEntityMetadata,
1071
            })
1072
        }
1073
        return this
47,727✔
1074
    }
1075

1076
    /**
1077
     * Counts number of entities of entity's relation and maps the value into some entity's property.
1078
     * Optionally, you can add condition and parameters used in condition.
1079
     */
1080
    loadRelationCountAndMap(
1081
        mapToProperty: string,
1082
        relationName: string,
1083
        aliasName?: string,
1084
        queryBuilderFactory?: (
1085
            qb: SelectQueryBuilder<any>,
1086
        ) => SelectQueryBuilder<any>,
1087
    ): this {
1088
        const relationCountAttribute = new RelationCountAttribute(
2,160✔
1089
            this.expressionMap,
1090
        )
1091
        relationCountAttribute.mapToProperty = mapToProperty
2,160✔
1092
        relationCountAttribute.relationName = relationName
2,160✔
1093
        relationCountAttribute.alias = aliasName
2,160✔
1094
        relationCountAttribute.queryBuilderFactory = queryBuilderFactory
2,160✔
1095
        this.expressionMap.relationCountAttributes.push(relationCountAttribute)
2,160✔
1096

1097
        this.expressionMap.createAlias({
2,160✔
1098
            type: "other",
1099
            name: relationCountAttribute.junctionAlias,
1100
        })
1101
        if (relationCountAttribute.relation.junctionEntityMetadata) {
2,160✔
1102
            this.expressionMap.createAlias({
1,760✔
1103
                type: "other",
1104
                name: relationCountAttribute.junctionAlias,
1105
                metadata:
1106
                    relationCountAttribute.relation.junctionEntityMetadata,
1107
            })
1108
        }
1109
        return this
2,160✔
1110
    }
1111

1112
    /**
1113
     * Loads all relation ids for all relations of the selected entity.
1114
     * All relation ids will be mapped to relation property themself.
1115
     * If array of strings is given then loads only relation ids of the given properties.
1116
     */
1117
    loadAllRelationIds(options?: {
1118
        relations?: string[]
1119
        disableMixedMap?: boolean
1120
    }): this {
1121
        // todo: add skip relations
1122
        this.expressionMap.mainAlias!.metadata.relations.forEach((relation) => {
168,378✔
1123
            if (
93,120✔
1124
                options !== undefined &&
279,280✔
1125
                options.relations !== undefined &&
1126
                options.relations.indexOf(relation.propertyPath) === -1
1127
            )
1128
                return
50,393✔
1129

1130
            this.loadRelationIdAndMap(
42,727✔
1131
                this.expressionMap.mainAlias!.name +
1132
                    "." +
1133
                    relation.propertyPath,
1134
                this.expressionMap.mainAlias!.name +
1135
                    "." +
1136
                    relation.propertyPath,
1137
                options,
1138
            )
1139
        })
1140
        return this
168,378✔
1141
    }
1142

1143
    /**
1144
     * Sets WHERE condition in the query builder.
1145
     * If you had previously WHERE expression defined,
1146
     * calling this function will override previously set WHERE conditions.
1147
     * Additionally you can add parameters used in where expression.
1148
     */
1149
    where(
1150
        where:
1151
            | Brackets
1152
            | string
1153
            | ((qb: this) => string)
1154
            | ObjectLiteral
1155
            | ObjectLiteral[],
1156
        parameters?: ObjectLiteral,
1157
    ): this {
1158
        this.expressionMap.wheres = [] // don't move this block below since computeWhereParameter can add where expressions
652,281✔
1159
        const condition = this.getWhereCondition(where)
652,281✔
1160
        if (condition) {
652,265✔
1161
            this.expressionMap.wheres = [
652,265✔
1162
                { type: "simple", condition: condition },
1163
            ]
1164
        }
1165
        if (parameters) this.setParameters(parameters)
652,265✔
1166
        return this
652,225✔
1167
    }
1168

1169
    /**
1170
     * Adds new AND WHERE condition in the query builder.
1171
     * Additionally you can add parameters used in where expression.
1172
     */
1173
    andWhere(
1174
        where:
1175
            | string
1176
            | Brackets
1177
            | ((qb: this) => string)
1178
            | ObjectLiteral
1179
            | ObjectLiteral[],
1180
        parameters?: ObjectLiteral,
1181
    ): this {
1182
        this.expressionMap.wheres.push({
98,283✔
1183
            type: "and",
1184
            condition: this.getWhereCondition(where),
1185
        })
1186
        if (parameters) this.setParameters(parameters)
98,283✔
1187
        return this
98,283✔
1188
    }
1189

1190
    /**
1191
     * Adds new OR WHERE condition in the query builder.
1192
     * Additionally you can add parameters used in where expression.
1193
     */
1194
    orWhere(
1195
        where:
1196
            | Brackets
1197
            | string
1198
            | ((qb: this) => string)
1199
            | ObjectLiteral
1200
            | ObjectLiteral[],
1201
        parameters?: ObjectLiteral,
1202
    ): this {
1203
        this.expressionMap.wheres.push({
28,463✔
1204
            type: "or",
1205
            condition: this.getWhereCondition(where),
1206
        })
1207
        if (parameters) this.setParameters(parameters)
28,463✔
1208
        return this
28,463✔
1209
    }
1210

1211
    /**
1212
     * Sets a new where EXISTS clause
1213
     */
1214
    whereExists(subQuery: SelectQueryBuilder<any>): this {
1215
        return this.where(...this.getExistsCondition(subQuery))
280✔
1216
    }
1217

1218
    /**
1219
     * Adds a new AND where EXISTS clause
1220
     */
1221
    andWhereExists(subQuery: SelectQueryBuilder<any>): this {
1222
        return this.andWhere(...this.getExistsCondition(subQuery))
×
1223
    }
1224

1225
    /**
1226
     * Adds a new OR where EXISTS clause
1227
     */
1228
    orWhereExists(subQuery: SelectQueryBuilder<any>): this {
1229
        return this.orWhere(...this.getExistsCondition(subQuery))
×
1230
    }
1231

1232
    /**
1233
     * Adds new AND WHERE with conditions for the given ids.
1234
     *
1235
     * Ids are mixed.
1236
     * It means if you have single primary key you can pass a simple id values, for example [1, 2, 3].
1237
     * If you have multiple primary keys you need to pass object with property names and values specified,
1238
     * for example [{ firstId: 1, secondId: 2 }, { firstId: 2, secondId: 3 }, ...]
1239
     */
1240
    whereInIds(ids: any | any[]): this {
1241
        return this.where(this.getWhereInIdsCondition(ids))
175,354✔
1242
    }
1243

1244
    /**
1245
     * Adds new AND WHERE with conditions for the given ids.
1246
     *
1247
     * Ids are mixed.
1248
     * It means if you have single primary key you can pass a simple id values, for example [1, 2, 3].
1249
     * If you have multiple primary keys you need to pass object with property names and values specified,
1250
     * for example [{ firstId: 1, secondId: 2 }, { firstId: 2, secondId: 3 }, ...]
1251
     */
1252
    andWhereInIds(ids: any | any[]): this {
1253
        return this.andWhere(this.getWhereInIdsCondition(ids))
286✔
1254
    }
1255

1256
    /**
1257
     * Adds new OR WHERE with conditions for the given ids.
1258
     *
1259
     * Ids are mixed.
1260
     * It means if you have single primary key you can pass a simple id values, for example [1, 2, 3].
1261
     * If you have multiple primary keys you need to pass object with property names and values specified,
1262
     * for example [{ firstId: 1, secondId: 2 }, { firstId: 2, secondId: 3 }, ...]
1263
     */
1264
    orWhereInIds(ids: any | any[]): this {
1265
        return this.orWhere(this.getWhereInIdsCondition(ids))
80✔
1266
    }
1267

1268
    /**
1269
     * Sets HAVING condition in the query builder.
1270
     * If you had previously HAVING expression defined,
1271
     * calling this function will override previously set HAVING conditions.
1272
     * Additionally you can add parameters used in where expression.
1273
     */
1274
    having(having: string, parameters?: ObjectLiteral): this {
1275
        this.expressionMap.havings.push({ type: "simple", condition: having })
3✔
1276
        if (parameters) this.setParameters(parameters)
3!
1277
        return this
3✔
1278
    }
1279

1280
    /**
1281
     * Adds new AND HAVING condition in the query builder.
1282
     * Additionally you can add parameters used in where expression.
1283
     */
1284
    andHaving(having: string, parameters?: ObjectLiteral): this {
1285
        this.expressionMap.havings.push({ type: "and", condition: having })
×
1286
        if (parameters) this.setParameters(parameters)
×
1287
        return this
×
1288
    }
1289

1290
    /**
1291
     * Adds new OR HAVING condition in the query builder.
1292
     * Additionally you can add parameters used in where expression.
1293
     */
1294
    orHaving(having: string, parameters?: ObjectLiteral): this {
1295
        this.expressionMap.havings.push({ type: "or", condition: having })
3✔
1296
        if (parameters) this.setParameters(parameters)
3!
1297
        return this
3✔
1298
    }
1299

1300
    /**
1301
     * Sets GROUP BY condition in the query builder.
1302
     * If you had previously GROUP BY expression defined,
1303
     * calling this function will override previously set GROUP BY conditions.
1304
     */
1305
    groupBy(): this
1306

1307
    /**
1308
     * Sets GROUP BY condition in the query builder.
1309
     * If you had previously GROUP BY expression defined,
1310
     * calling this function will override previously set GROUP BY conditions.
1311
     */
1312
    groupBy(groupBy: string): this
1313

1314
    /**
1315
     * Sets GROUP BY condition in the query builder.
1316
     * If you had previously GROUP BY expression defined,
1317
     * calling this function will override previously set GROUP BY conditions.
1318
     */
1319
    groupBy(groupBy?: string): this {
1320
        if (groupBy) {
2,390✔
1321
            this.expressionMap.groupBys = [groupBy]
149✔
1322
        } else {
1323
            this.expressionMap.groupBys = []
2,241✔
1324
        }
1325
        return this
2,390✔
1326
    }
1327

1328
    /**
1329
     * Adds GROUP BY condition in the query builder.
1330
     */
1331
    addGroupBy(groupBy: string): this {
1332
        this.expressionMap.groupBys.push(groupBy)
4,480✔
1333
        return this
4,480✔
1334
    }
1335

1336
    /**
1337
     * Enables time travelling for the current query (only supported by cockroach currently)
1338
     */
1339
    timeTravelQuery(timeTravelFn?: string | boolean): this {
1340
        if (this.connection.driver.options.type === "cockroachdb") {
20,971✔
1341
            if (timeTravelFn === undefined) {
1,593✔
1342
                this.expressionMap.timeTravel = "follower_read_timestamp()"
3✔
1343
            } else {
1344
                this.expressionMap.timeTravel = timeTravelFn
1,590✔
1345
            }
1346
        }
1347

1348
        return this
20,971✔
1349
    }
1350

1351
    /**
1352
     * Sets ORDER BY condition in the query builder.
1353
     * If you had previously ORDER BY expression defined,
1354
     * calling this function will override previously set ORDER BY conditions.
1355
     *
1356
     * Calling order by without order set will remove all previously set order bys.
1357
     */
1358
    orderBy(): this
1359

1360
    /**
1361
     * Sets ORDER BY condition in the query builder.
1362
     * If you had previously ORDER BY expression defined,
1363
     * calling this function will override previously set ORDER BY conditions.
1364
     */
1365
    orderBy(
1366
        sort: string,
1367
        order?: "ASC" | "DESC",
1368
        nulls?: "NULLS FIRST" | "NULLS LAST",
1369
    ): this
1370

1371
    /**
1372
     * Sets ORDER BY condition in the query builder.
1373
     * If you had previously ORDER BY expression defined,
1374
     * calling this function will override previously set ORDER BY conditions.
1375
     */
1376
    orderBy(order: OrderByCondition): this
1377

1378
    /**
1379
     * Sets ORDER BY condition in the query builder.
1380
     * If you had previously ORDER BY expression defined,
1381
     * calling this function will override previously set ORDER BY conditions.
1382
     */
1383
    orderBy(
1384
        sort?: string | OrderByCondition,
1385
        order: "ASC" | "DESC" = "ASC",
31,870✔
1386
        nulls?: "NULLS FIRST" | "NULLS LAST",
1387
    ): this {
1388
        if (order !== undefined && order !== "ASC" && order !== "DESC")
33,505✔
1389
            throw new TypeORMError(
40✔
1390
                `SelectQueryBuilder.addOrderBy "order" can accept only "ASC" and "DESC" values.`,
1391
            )
1392
        if (
33,465✔
1393
            nulls !== undefined &&
33,545✔
1394
            nulls !== "NULLS FIRST" &&
1395
            nulls !== "NULLS LAST"
1396
        )
1397
            throw new TypeORMError(
40✔
1398
                `SelectQueryBuilder.addOrderBy "nulls" can accept only "NULLS FIRST" and "NULLS LAST" values.`,
1399
            )
1400

1401
        if (sort) {
33,425✔
1402
            if (typeof sort === "object") {
20,706✔
1403
                this.expressionMap.orderBys = sort as OrderByCondition
10,527✔
1404
            } else {
1405
                if (nulls) {
10,179!
1406
                    this.expressionMap.orderBys = {
×
1407
                        [sort as string]: { order, nulls },
1408
                    }
1409
                } else {
1410
                    this.expressionMap.orderBys = { [sort as string]: order }
10,179✔
1411
                }
1412
            }
1413
        } else {
1414
            this.expressionMap.orderBys = {}
12,719✔
1415
        }
1416
        return this
33,425✔
1417
    }
1418

1419
    /**
1420
     * Adds ORDER BY condition in the query builder.
1421
     */
1422
    addOrderBy(
1423
        sort: string,
1424
        order: "ASC" | "DESC" = "ASC",
21,087✔
1425
        nulls?: "NULLS FIRST" | "NULLS LAST",
1426
    ): this {
1427
        if (order !== undefined && order !== "ASC" && order !== "DESC")
25,315!
1428
            throw new TypeORMError(
×
1429
                `SelectQueryBuilder.addOrderBy "order" can accept only "ASC" and "DESC" values.`,
1430
            )
1431
        if (
25,315!
1432
            nulls !== undefined &&
25,429✔
1433
            nulls !== "NULLS FIRST" &&
1434
            nulls !== "NULLS LAST"
1435
        )
1436
            throw new TypeORMError(
×
1437
                `SelectQueryBuilder.addOrderBy "nulls" can accept only "NULLS FIRST" and "NULLS LAST" values.`,
1438
            )
1439

1440
        if (nulls) {
25,315✔
1441
            this.expressionMap.orderBys[sort] = { order, nulls }
74✔
1442
        } else {
1443
            this.expressionMap.orderBys[sort] = order
25,241✔
1444
        }
1445
        return this
25,315✔
1446
    }
1447

1448
    /**
1449
     * Sets LIMIT - maximum number of rows to be selected.
1450
     * NOTE that it may not work as you expect if you are using joins.
1451
     * If you want to implement pagination, and you are having join in your query,
1452
     * then use the take method instead.
1453
     */
1454
    limit(limit?: number): this {
1455
        this.expressionMap.limit = this.normalizeNumber(limit)
13,349✔
1456
        if (
13,349!
1457
            this.expressionMap.limit !== undefined &&
24,417✔
1458
            isNaN(this.expressionMap.limit)
1459
        )
1460
            throw new TypeORMError(
×
1461
                `Provided "limit" value is not a number. Please provide a numeric value.`,
1462
            )
1463

1464
        return this
13,349✔
1465
    }
1466

1467
    /**
1468
     * Sets OFFSET - selection offset.
1469
     * NOTE that it may not work as you expect if you are using joins.
1470
     * If you want to implement pagination, and you are having join in your query,
1471
     * then use the skip method instead.
1472
     */
1473
    offset(offset?: number): this {
1474
        this.expressionMap.offset = this.normalizeNumber(offset)
12,981✔
1475
        if (
12,981!
1476
            this.expressionMap.offset !== undefined &&
13,335✔
1477
            isNaN(this.expressionMap.offset)
1478
        )
1479
            throw new TypeORMError(
×
1480
                `Provided "offset" value is not a number. Please provide a numeric value.`,
1481
            )
1482

1483
        return this
12,981✔
1484
    }
1485

1486
    /**
1487
     * Sets maximal number of entities to take.
1488
     */
1489
    take(take?: number): this {
1490
        this.expressionMap.take = this.normalizeNumber(take)
26,289✔
1491
        if (
26,289✔
1492
            this.expressionMap.take !== undefined &&
50,337✔
1493
            isNaN(this.expressionMap.take)
1494
        )
1495
            throw new TypeORMError(
80✔
1496
                `Provided "take" value is not a number. Please provide a numeric value.`,
1497
            )
1498

1499
        return this
26,209✔
1500
    }
1501

1502
    /**
1503
     * Sets number of entities to skip.
1504
     */
1505
    skip(skip?: number): this {
1506
        this.expressionMap.skip = this.normalizeNumber(skip)
3,395✔
1507
        if (
3,395✔
1508
            this.expressionMap.skip !== undefined &&
4,549✔
1509
            isNaN(this.expressionMap.skip)
1510
        )
1511
            throw new TypeORMError(
80✔
1512
                `Provided "skip" value is not a number. Please provide a numeric value.`,
1513
            )
1514

1515
        return this
3,315✔
1516
    }
1517

1518
    /**
1519
     * Set certain index to be used by the query.
1520
     *
1521
     * @param index Name of index to be used.
1522
     */
1523
    useIndex(index: string): this {
1524
        this.expressionMap.useIndex = index
×
1525

1526
        return this
×
1527
    }
1528

1529
    /**
1530
     * Sets locking mode.
1531
     */
1532
    setLock(lockMode: "optimistic", lockVersion: number | Date): this
1533

1534
    /**
1535
     * Sets locking mode.
1536
     */
1537
    setLock(
1538
        lockMode:
1539
            | "pessimistic_read"
1540
            | "pessimistic_write"
1541
            | "dirty_read"
1542
            /*
1543
                "pessimistic_partial_write" and "pessimistic_write_or_fail" are deprecated and
1544
                will be removed in a future version.
1545

1546
                Use setOnLocked instead.
1547
             */
1548
            | "pessimistic_partial_write"
1549
            | "pessimistic_write_or_fail"
1550
            | "for_no_key_update"
1551
            | "for_key_share",
1552
        lockVersion?: undefined,
1553
        lockTables?: string[],
1554
    ): this
1555

1556
    /**
1557
     * Sets locking mode.
1558
     */
1559
    setLock(
1560
        lockMode:
1561
            | "optimistic"
1562
            | "pessimistic_read"
1563
            | "pessimistic_write"
1564
            | "dirty_read"
1565
            /*
1566
                "pessimistic_partial_write" and "pessimistic_write_or_fail" are deprecated and
1567
                will be removed in a future version.
1568

1569
                Use setOnLocked instead.
1570
             */
1571
            | "pessimistic_partial_write"
1572
            | "pessimistic_write_or_fail"
1573
            | "for_no_key_update"
1574
            | "for_key_share",
1575
        lockVersion?: number | Date,
1576
        lockTables?: string[],
1577
    ): this {
1578
        this.expressionMap.lockMode = lockMode
1,411✔
1579
        this.expressionMap.lockVersion = lockVersion
1,411✔
1580
        this.expressionMap.lockTables = lockTables
1,411✔
1581
        return this
1,411✔
1582
    }
1583

1584
    /**
1585
     * Sets lock handling by adding NO WAIT or SKIP LOCKED.
1586
     */
1587
    setOnLocked(onLocked: "nowait" | "skip_locked"): this {
1588
        this.expressionMap.onLocked = onLocked
66✔
1589
        return this
66✔
1590
    }
1591

1592
    /**
1593
     * Disables the global condition of "non-deleted" for the entity with delete date columns.
1594
     */
1595
    withDeleted(): this {
1596
        this.expressionMap.withDeleted = true
169,649✔
1597
        return this
169,649✔
1598
    }
1599

1600
    /**
1601
     * Gets first raw result returned by execution of generated query builder sql.
1602
     */
1603
    async getRawOne<T = any>(): Promise<T | undefined> {
1604
        return (await this.getRawMany())[0]
11,101✔
1605
    }
1606

1607
    /**
1608
     * Gets all raw results returned by execution of generated query builder sql.
1609
     */
1610
    async getRawMany<T = any>(): Promise<T[]> {
1611
        if (this.expressionMap.lockMode === "optimistic")
38,893✔
1612
            throw new OptimisticLockCanNotBeUsedError()
80✔
1613

1614
        this.expressionMap.queryEntity = false
38,813✔
1615
        const queryRunner = this.obtainQueryRunner()
38,813✔
1616
        let transactionStartedByUs: boolean = false
38,813✔
1617
        try {
38,813✔
1618
            // start transaction if it was enabled
1619
            if (
38,813!
1620
                this.expressionMap.useTransaction === true &&
38,813!
1621
                queryRunner.isTransactionActive === false
1622
            ) {
1623
                await queryRunner.startTransaction()
×
1624
                transactionStartedByUs = true
×
1625
            }
1626

1627
            const results = await this.loadRawResults(queryRunner)
38,813✔
1628

1629
            // close transaction if we started it
1630
            if (transactionStartedByUs) {
38,796!
1631
                await queryRunner.commitTransaction()
×
1632
            }
1633

1634
            return results
38,796✔
1635
        } catch (error) {
1636
            // rollback transaction if we started it
1637
            if (transactionStartedByUs) {
17!
1638
                try {
×
1639
                    await queryRunner.rollbackTransaction()
×
1640
                } catch (rollbackError) {}
1641
            }
1642
            throw error
17✔
1643
        } finally {
1644
            if (queryRunner !== this.queryRunner) {
38,813✔
1645
                // means we created our own query runner
1646
                await queryRunner.release()
1,191✔
1647
            }
1648
        }
1649
    }
1650

1651
    /**
1652
     * Executes sql generated by query builder and returns object with raw results and entities created from them.
1653
     */
1654
    async getRawAndEntities<T = any>(): Promise<{
1655
        entities: Entity[]
1656
        raw: T[]
1657
    }> {
1658
        const queryRunner = this.obtainQueryRunner()
242,397✔
1659
        let transactionStartedByUs: boolean = false
242,397✔
1660
        try {
242,397✔
1661
            // start transaction if it was enabled
1662
            if (
242,397✔
1663
                this.expressionMap.useTransaction === true &&
242,437✔
1664
                queryRunner.isTransactionActive === false
1665
            ) {
1666
                await queryRunner.startTransaction()
40✔
1667
                transactionStartedByUs = true
40✔
1668
            }
1669

1670
            this.expressionMap.queryEntity = true
242,397✔
1671
            const results = await this.executeEntitiesAndRawResults(queryRunner)
242,397✔
1672

1673
            // close transaction if we started it
1674
            if (transactionStartedByUs) {
242,039✔
1675
                await queryRunner.commitTransaction()
40✔
1676
            }
1677

1678
            return results
242,039✔
1679
        } catch (error) {
1680
            // rollback transaction if we started it
1681
            if (transactionStartedByUs) {
358!
1682
                try {
×
1683
                    await queryRunner.rollbackTransaction()
×
1684
                } catch (rollbackError) {}
1685
            }
1686
            throw error
358✔
1687
        } finally {
1688
            if (queryRunner !== this.queryRunner)
242,397✔
1689
                // means we created our own query runner
1690
                await queryRunner.release()
63,674✔
1691
        }
1692
    }
1693

1694
    /**
1695
     * Gets single entity returned by execution of generated query builder sql.
1696
     */
1697
    async getOne(): Promise<Entity | null> {
1698
        const results = await this.getRawAndEntities()
33,118✔
1699
        const result = results.entities[0] as any
32,775✔
1700

1701
        if (
32,775✔
1702
            result &&
63,685✔
1703
            this.expressionMap.lockMode === "optimistic" &&
1704
            this.expressionMap.lockVersion
1705
        ) {
1706
            const metadata = this.expressionMap.mainAlias!.metadata
204✔
1707

1708
            if (this.expressionMap.lockVersion instanceof Date) {
204✔
1709
                const actualVersion =
1710
                    metadata.updateDateColumn!.getEntityValue(result) // what if columns arent set?
93✔
1711
                if (
93✔
1712
                    actualVersion.getTime() !==
1713
                    this.expressionMap.lockVersion.getTime()
1714
                )
1715
                    throw new OptimisticLockVersionMismatchError(
31✔
1716
                        metadata.name,
1717
                        this.expressionMap.lockVersion,
1718
                        actualVersion,
1719
                    )
1720
            } else {
1721
                const actualVersion =
1722
                    metadata.versionColumn!.getEntityValue(result) // what if columns arent set?
111✔
1723
                if (actualVersion !== this.expressionMap.lockVersion)
111✔
1724
                    throw new OptimisticLockVersionMismatchError(
40✔
1725
                        metadata.name,
1726
                        this.expressionMap.lockVersion,
1727
                        actualVersion,
1728
                    )
1729
            }
1730
        }
1731

1732
        if (result === undefined) {
32,704✔
1733
            return null
2,069✔
1734
        }
1735
        return result
30,635✔
1736
    }
1737

1738
    /**
1739
     * Gets the first entity returned by execution of generated query builder sql or rejects the returned promise on error.
1740
     */
1741
    async getOneOrFail(): Promise<Entity> {
1742
        const entity = await this.getOne()
26✔
1743

1744
        if (!entity) {
26✔
1745
            throw new EntityNotFoundError(
4✔
1746
                this.expressionMap.mainAlias!.target,
1747
                this.expressionMap.parameters,
1748
            )
1749
        }
1750

1751
        return entity
22✔
1752
    }
1753

1754
    /**
1755
     * Gets entities returned by execution of generated query builder sql.
1756
     */
1757
    async getMany(): Promise<Entity[]> {
1758
        if (this.expressionMap.lockMode === "optimistic")
205,567✔
1759
            throw new OptimisticLockCanNotBeUsedError()
80✔
1760

1761
        const results = await this.getRawAndEntities()
205,487✔
1762
        return results.entities
205,472✔
1763
    }
1764

1765
    /**
1766
     * Gets count - number of entities selected by sql generated by this query builder.
1767
     * Count excludes all limitations set by offset, limit, skip, and take.
1768
     */
1769
    async getCount(): Promise<number> {
1770
        if (this.expressionMap.lockMode === "optimistic")
1,203✔
1771
            throw new OptimisticLockCanNotBeUsedError()
40✔
1772

1773
        const queryRunner = this.obtainQueryRunner()
1,163✔
1774
        let transactionStartedByUs: boolean = false
1,163✔
1775
        try {
1,163✔
1776
            // start transaction if it was enabled
1777
            if (
1,163!
1778
                this.expressionMap.useTransaction === true &&
1,163!
1779
                queryRunner.isTransactionActive === false
1780
            ) {
1781
                await queryRunner.startTransaction()
×
1782
                transactionStartedByUs = true
×
1783
            }
1784

1785
            this.expressionMap.queryEntity = false
1,163✔
1786
            const results = await this.executeCountQuery(queryRunner)
1,163✔
1787

1788
            // close transaction if we started it
1789
            if (transactionStartedByUs) {
1,163!
1790
                await queryRunner.commitTransaction()
×
1791
            }
1792

1793
            return results
1,163✔
1794
        } catch (error) {
1795
            // rollback transaction if we started it
1796
            if (transactionStartedByUs) {
×
1797
                try {
×
1798
                    await queryRunner.rollbackTransaction()
×
1799
                } catch (rollbackError) {}
1800
            }
1801
            throw error
×
1802
        } finally {
1803
            if (queryRunner !== this.queryRunner)
1,163✔
1804
                // means we created our own query runner
1805
                await queryRunner.release()
1,163✔
1806
        }
1807
    }
1808

1809
    /**
1810
     * Gets exists
1811
     * Returns whether any rows exists matching current query.
1812
     */
1813
    async getExists(): Promise<boolean> {
1814
        if (this.expressionMap.lockMode === "optimistic")
280!
1815
            throw new OptimisticLockCanNotBeUsedError()
×
1816

1817
        const queryRunner = this.obtainQueryRunner()
280✔
1818
        let transactionStartedByUs: boolean = false
280✔
1819
        try {
280✔
1820
            // start transaction if it was enabled
1821
            if (
280!
1822
                this.expressionMap.useTransaction === true &&
280!
1823
                queryRunner.isTransactionActive === false
1824
            ) {
1825
                await queryRunner.startTransaction()
×
1826
                transactionStartedByUs = true
×
1827
            }
1828

1829
            this.expressionMap.queryEntity = false
280✔
1830
            const results = await this.executeExistsQuery(queryRunner)
280✔
1831

1832
            // close transaction if we started it
1833
            if (transactionStartedByUs) {
280!
1834
                await queryRunner.commitTransaction()
×
1835
            }
1836

1837
            return results
280✔
1838
        } catch (error) {
1839
            // rollback transaction if we started it
1840
            if (transactionStartedByUs) {
×
1841
                try {
×
1842
                    await queryRunner.rollbackTransaction()
×
1843
                } catch (rollbackError) {}
1844
            }
1845
            throw error
×
1846
        } finally {
1847
            if (queryRunner !== this.queryRunner)
280✔
1848
                // means we created our own query runner
1849
                await queryRunner.release()
280✔
1850
        }
1851
    }
1852

1853
    /**
1854
     * Executes built SQL query and returns entities and overall entities count (without limitation).
1855
     * This method is useful to build pagination.
1856
     */
1857
    async getManyAndCount(): Promise<[Entity[], number]> {
1858
        if (this.expressionMap.lockMode === "optimistic")
846✔
1859
            throw new OptimisticLockCanNotBeUsedError()
40✔
1860

1861
        const queryRunner = this.obtainQueryRunner()
806✔
1862
        let transactionStartedByUs: boolean = false
806✔
1863
        try {
806✔
1864
            // start transaction if it was enabled
1865
            if (
806!
1866
                this.expressionMap.useTransaction === true &&
806!
1867
                queryRunner.isTransactionActive === false
1868
            ) {
1869
                await queryRunner.startTransaction()
×
1870
                transactionStartedByUs = true
×
1871
            }
1872

1873
            this.expressionMap.queryEntity = true
806✔
1874
            const entitiesAndRaw = await this.executeEntitiesAndRawResults(
806✔
1875
                queryRunner,
1876
            )
1877
            this.expressionMap.queryEntity = false
798✔
1878
            const cacheId = this.expressionMap.cacheId
798✔
1879
            // Creates a new cacheId for the count query, or it will retreive the above query results
1880
            // and count will return 0.
1881
            this.expressionMap.cacheId = cacheId ? `${cacheId}-count` : cacheId
798✔
1882
            const count = await this.executeCountQuery(queryRunner)
798✔
1883
            const results: [Entity[], number] = [entitiesAndRaw.entities, count]
798✔
1884

1885
            // close transaction if we started it
1886
            if (transactionStartedByUs) {
798!
1887
                await queryRunner.commitTransaction()
×
1888
            }
1889

1890
            return results
798✔
1891
        } catch (error) {
1892
            // rollback transaction if we started it
1893
            if (transactionStartedByUs) {
8!
1894
                try {
×
1895
                    await queryRunner.rollbackTransaction()
×
1896
                } catch (rollbackError) {}
1897
            }
1898
            throw error
8✔
1899
        } finally {
1900
            if (queryRunner !== this.queryRunner)
806✔
1901
                // means we created our own query runner
1902
                await queryRunner.release()
806✔
1903
        }
1904
    }
1905

1906
    /**
1907
     * Executes built SQL query and returns raw data stream.
1908
     */
1909
    async stream(): Promise<ReadStream> {
1910
        this.expressionMap.queryEntity = false
15✔
1911
        const [sql, parameters] = this.getQueryAndParameters()
15✔
1912
        const queryRunner = this.obtainQueryRunner()
15✔
1913
        let transactionStartedByUs: boolean = false
15✔
1914
        try {
15✔
1915
            // start transaction if it was enabled
1916
            if (
15!
1917
                this.expressionMap.useTransaction === true &&
15!
1918
                queryRunner.isTransactionActive === false
1919
            ) {
1920
                await queryRunner.startTransaction()
×
1921
                transactionStartedByUs = true
×
1922
            }
1923

1924
            const releaseFn = () => {
15✔
1925
                if (queryRunner !== this.queryRunner)
15✔
1926
                    // means we created our own query runner
1927
                    return queryRunner.release()
15✔
1928
                return
×
1929
            }
1930
            const results = queryRunner.stream(
15✔
1931
                sql,
1932
                parameters,
1933
                releaseFn,
1934
                releaseFn,
1935
            )
1936

1937
            // close transaction if we started it
1938
            if (transactionStartedByUs) {
15!
1939
                await queryRunner.commitTransaction()
×
1940
            }
1941

1942
            return results
15✔
1943
        } catch (error) {
1944
            // rollback transaction if we started it
1945
            if (transactionStartedByUs) {
×
1946
                try {
×
1947
                    await queryRunner.rollbackTransaction()
×
1948
                } catch (rollbackError) {}
1949
            }
1950
            throw error
×
1951
        }
1952
    }
1953

1954
    /**
1955
     * Enables or disables query result caching.
1956
     */
1957
    cache(enabled: boolean): this
1958

1959
    /**
1960
     * Enables query result caching and sets in milliseconds in which cache will expire.
1961
     * If not set then global caching time will be used.
1962
     */
1963
    cache(milliseconds: number): this
1964

1965
    /**
1966
     * Enables query result caching and sets cache id and milliseconds in which cache will expire.
1967
     */
1968
    cache(id: any, milliseconds?: number): this
1969

1970
    /**
1971
     * Enables or disables query result caching.
1972
     */
1973
    cache(
1974
        enabledOrMillisecondsOrId: boolean | number | string,
1975
        maybeMilliseconds?: number,
1976
    ): this {
1977
        if (typeof enabledOrMillisecondsOrId === "boolean") {
13,406✔
1978
            this.expressionMap.cache = enabledOrMillisecondsOrId
2,248✔
1979
        } else if (typeof enabledOrMillisecondsOrId === "number") {
11,158✔
1980
            this.expressionMap.cache = true
160✔
1981
            this.expressionMap.cacheDuration = enabledOrMillisecondsOrId
160✔
1982
        } else if (
10,998✔
1983
            typeof enabledOrMillisecondsOrId === "string" ||
21,356✔
1984
            typeof enabledOrMillisecondsOrId === "number"
1985
        ) {
1986
            this.expressionMap.cache = true
640✔
1987
            this.expressionMap.cacheId = enabledOrMillisecondsOrId
640✔
1988
        }
1989

1990
        if (maybeMilliseconds) {
13,406✔
1991
            this.expressionMap.cacheDuration = maybeMilliseconds
640✔
1992
        }
1993

1994
        return this
13,406✔
1995
    }
1996

1997
    /**
1998
     * Sets extra options that can be used to configure how query builder works.
1999
     */
2000
    setOption(option: SelectQueryBuilderOption): this {
2001
        this.expressionMap.options.push(option)
11,671✔
2002
        return this
11,671✔
2003
    }
2004

2005
    // -------------------------------------------------------------------------
2006
    // Protected Methods
2007
    // -------------------------------------------------------------------------
2008

2009
    protected join(
2010
        direction: "INNER" | "LEFT",
2011
        entityOrProperty:
2012
            | Function
2013
            | string
2014
            | ((qb: SelectQueryBuilder<any>) => SelectQueryBuilder<any>),
2015
        aliasName: string,
2016
        condition?: string,
2017
        parameters?: ObjectLiteral,
2018
        mapToProperty?: string,
2019
        isMappingMany?: boolean,
2020
        mapAsEntity?: Function | string,
2021
    ): void {
2022
        if (parameters) {
3,388,730!
2023
            this.setParameters(parameters)
×
2024
        }
2025

2026
        const joinAttribute = new JoinAttribute(
3,388,730✔
2027
            this.connection,
2028
            this.expressionMap,
2029
        )
2030
        joinAttribute.direction = direction
3,388,730✔
2031
        joinAttribute.mapAsEntity = mapAsEntity
3,388,730✔
2032
        joinAttribute.mapToProperty = mapToProperty
3,388,730✔
2033
        joinAttribute.isMappingMany = isMappingMany
3,388,730✔
2034
        joinAttribute.entityOrProperty = entityOrProperty // relationName
3,388,730✔
2035
        joinAttribute.condition = condition // joinInverseSideCondition
3,388,730✔
2036
        // joinAttribute.junctionAlias = joinAttribute.relation.isOwning ? parentAlias + "_" + destinationTableAlias : destinationTableAlias + "_" + parentAlias;
2037
        this.expressionMap.joinAttributes.push(joinAttribute)
3,388,730✔
2038

2039
        const joinAttributeMetadata = joinAttribute.metadata
3,388,730✔
2040
        if (joinAttributeMetadata) {
3,388,730✔
2041
            if (
3,388,592✔
2042
                joinAttributeMetadata.deleteDateColumn &&
3,388,946✔
2043
                !this.expressionMap.withDeleted
2044
            ) {
2045
                const conditionDeleteColumn = `${aliasName}.${joinAttributeMetadata.deleteDateColumn.propertyName} IS NULL`
274✔
2046
                joinAttribute.condition = joinAttribute.condition
274✔
2047
                    ? ` ${joinAttribute.condition} AND ${conditionDeleteColumn}`
2048
                    : `${conditionDeleteColumn}`
2049
            }
2050
            // todo: find and set metadata right there?
2051
            joinAttribute.alias = this.expressionMap.createAlias({
3,388,592✔
2052
                type: "join",
2053
                name: aliasName,
2054
                metadata: joinAttributeMetadata,
2055
            })
2056
            if (
3,388,592✔
2057
                joinAttribute.relation &&
6,758,671✔
2058
                joinAttribute.relation.junctionEntityMetadata
2059
            ) {
2060
                this.expressionMap.createAlias({
2,809,342✔
2061
                    type: "join",
2062
                    name: joinAttribute.junctionAlias,
2063
                    metadata: joinAttribute.relation.junctionEntityMetadata,
2064
                })
2065
            }
2066
        } else {
2067
            let subQuery: string = ""
138✔
2068
            if (typeof entityOrProperty === "function") {
138✔
2069
                const subQueryBuilder: SelectQueryBuilder<any> = (
2070
                    entityOrProperty as any
58✔
2071
                )((this as any as SelectQueryBuilder<any>).subQuery())
2072
                this.setParameters(subQueryBuilder.getParameters())
58✔
2073
                subQuery = subQueryBuilder.getQuery()
58✔
2074
            } else {
2075
                subQuery = entityOrProperty
80✔
2076
            }
2077
            const isSubQuery =
2078
                typeof entityOrProperty === "function" ||
138✔
2079
                (entityOrProperty.substr(0, 1) === "(" &&
2080
                    entityOrProperty.substr(-1) === ")")
2081
            joinAttribute.alias = this.expressionMap.createAlias({
138✔
2082
                type: "join",
2083
                name: aliasName,
2084
                tablePath:
2085
                    isSubQuery === false
138✔
2086
                        ? (entityOrProperty as string)
2087
                        : undefined,
2088
                subQuery: isSubQuery === true ? subQuery : undefined,
138✔
2089
            })
2090
        }
2091
    }
2092

2093
    /**
2094
     * Creates "SELECT FROM" part of SQL query.
2095
     */
2096
    protected createSelectExpression() {
2097
        if (!this.expressionMap.mainAlias)
763,640!
2098
            throw new TypeORMError(
×
2099
                "Cannot build query because main alias is not set (call qb#from method)",
2100
            )
2101

2102
        // todo throw exception if selects or from is missing
2103

2104
        const allSelects: SelectQuery[] = []
763,640✔
2105
        const excludedSelects: SelectQuery[] = []
763,640✔
2106

2107
        if (this.expressionMap.mainAlias.hasMetadata) {
763,640✔
2108
            const metadata = this.expressionMap.mainAlias.metadata
748,336✔
2109
            allSelects.push(
748,336✔
2110
                ...this.buildEscapedEntityColumnSelects(
2111
                    this.expressionMap.mainAlias.name,
2112
                    metadata,
2113
                ),
2114
            )
2115
            excludedSelects.push(
748,336✔
2116
                ...this.findEntityColumnSelects(
2117
                    this.expressionMap.mainAlias.name,
2118
                    metadata,
2119
                ),
2120
            )
2121
        }
2122

2123
        // add selects from joins
2124
        this.expressionMap.joinAttributes.forEach((join) => {
763,640✔
2125
            if (join.metadata) {
3,401,876✔
2126
                allSelects.push(
3,401,741✔
2127
                    ...this.buildEscapedEntityColumnSelects(
2128
                        join.alias.name!,
2129
                        join.metadata,
2130
                    ),
2131
                )
2132
                excludedSelects.push(
3,401,741✔
2133
                    ...this.findEntityColumnSelects(
2134
                        join.alias.name!,
2135
                        join.metadata,
2136
                    ),
2137
                )
2138
            } else {
2139
                const hasMainAlias = this.expressionMap.selects.some(
135✔
2140
                    (select) => select.selection === join.alias.name,
153✔
2141
                )
2142
                if (hasMainAlias) {
135✔
2143
                    allSelects.push({
18✔
2144
                        selection: this.escape(join.alias.name!) + ".*",
2145
                    })
2146
                    const excludedSelect = this.expressionMap.selects.find(
18✔
2147
                        (select) => select.selection === join.alias.name,
36✔
2148
                    )
2149
                    excludedSelects.push(excludedSelect!)
18✔
2150
                }
2151
            }
2152
        })
2153

2154
        // add all other selects
2155
        this.expressionMap.selects
763,640✔
2156
            .filter((select) => excludedSelects.indexOf(select) === -1)
4,183,291✔
2157
            .forEach((select) =>
2158
                allSelects.push({
21,199✔
2159
                    selection: this.replacePropertyNames(select.selection),
2160
                    aliasName: select.aliasName,
2161
                }),
2162
            )
2163

2164
        // if still selection is empty, then simply set it to all (*)
2165
        if (allSelects.length === 0) allSelects.push({ selection: "*" })
763,640✔
2166

2167
        // Use certain index
2168
        let useIndex: string = ""
763,640✔
2169
        if (this.expressionMap.useIndex) {
763,640!
2170
            if (DriverUtils.isMySQLFamily(this.connection.driver)) {
×
2171
                useIndex = ` USE INDEX (${this.expressionMap.useIndex})`
×
2172
            }
2173
        }
2174

2175
        // create a selection query
2176
        const froms = this.expressionMap.aliases
763,640✔
2177
            .filter(
2178
                (alias) =>
2179
                    alias.type === "from" &&
7,006,571✔
2180
                    (alias.tablePath || alias.subQuery),
2181
            )
2182
            .map((alias) => {
2183
                if (alias.subQuery)
763,838✔
2184
                    return alias.subQuery + " " + this.escape(alias.name)
11,244✔
2185

2186
                return (
752,594✔
2187
                    this.getTableName(alias.tablePath!) +
2188
                    " " +
2189
                    this.escape(alias.name)
2190
                )
2191
            })
2192

2193
        const select = this.createSelectDistinctExpression()
763,640✔
2194
        const selection = allSelects
763,640✔
2195
            .map(
2196
                (select) =>
2197
                    select.selection +
13,528,582✔
2198
                    (select.aliasName
13,528,582✔
2199
                        ? " AS " + this.escape(select.aliasName)
2200
                        : ""),
2201
            )
2202
            .join(", ")
2203

2204
        return (
763,640✔
2205
            select +
2206
            selection +
2207
            " FROM " +
2208
            froms.join(", ") +
2209
            this.createTableLockExpression() +
2210
            useIndex
2211
        )
2212
    }
2213

2214
    /**
2215
     * Creates select | select distinct part of SQL query.
2216
     */
2217
    protected createSelectDistinctExpression(): string {
2218
        const { selectDistinct, selectDistinctOn, maxExecutionTime } =
2219
            this.expressionMap
763,640✔
2220
        const { driver } = this.connection
763,640✔
2221

2222
        let select = "SELECT "
763,640✔
2223

2224
        if (maxExecutionTime > 0) {
763,640!
2225
            if (DriverUtils.isMySQLFamily(driver)) {
×
2226
                select += `/*+ MAX_EXECUTION_TIME(${this.expressionMap.maxExecutionTime}) */ `
×
2227
            }
2228
        }
2229

2230
        if (
763,640✔
2231
            DriverUtils.isPostgresFamily(driver) &&
983,417✔
2232
            selectDistinctOn.length > 0
2233
        ) {
2234
            const selectDistinctOnMap = selectDistinctOn
18✔
2235
                .map((on) => this.replacePropertyNames(on))
30✔
2236
                .join(", ")
2237

2238
            select = `SELECT DISTINCT ON (${selectDistinctOnMap}) `
18✔
2239
        } else if (selectDistinct) {
763,622✔
2240
            select = "SELECT DISTINCT "
84✔
2241
        }
2242

2243
        return select
763,640✔
2244
    }
2245

2246
    /**
2247
     * Creates "JOIN" part of SQL query.
2248
     */
2249
    protected createJoinExpression(): string {
2250
        // examples:
2251
        // select from owning side
2252
        // qb.select("post")
2253
        //     .leftJoinAndSelect("post.category", "category");
2254
        // select from non-owning side
2255
        // qb.select("category")
2256
        //     .leftJoinAndSelect("category.post", "post");
2257

2258
        const joins = this.expressionMap.joinAttributes.map((joinAttr) => {
763,640✔
2259
            const relation = joinAttr.relation
3,401,876✔
2260
            const destinationTableName = joinAttr.tablePath
3,401,876✔
2261
            const destinationTableAlias = joinAttr.alias.name
3,401,876✔
2262
            let appendedCondition = joinAttr.condition
3,401,876✔
2263
                ? " AND (" + joinAttr.condition + ")"
2264
                : ""
2265
            const parentAlias = joinAttr.parentAlias
3,401,876✔
2266

2267
            // if join was build without relation (e.g. without "post.category") then it means that we have direct
2268
            // table to join, without junction table involved. This means we simply join direct table.
2269
            if (!parentAlias || !relation) {
3,401,876✔
2270
                const destinationJoin = joinAttr.alias.subQuery
18,648✔
2271
                    ? joinAttr.alias.subQuery
2272
                    : this.getTableName(destinationTableName)
2273
                return (
18,648✔
2274
                    " " +
2275
                    joinAttr.direction +
2276
                    " JOIN " +
2277
                    destinationJoin +
2278
                    " " +
2279
                    this.escape(destinationTableAlias) +
2280
                    this.createTableLockExpression() +
2281
                    (joinAttr.condition
18,648✔
2282
                        ? " ON " + this.replacePropertyNames(joinAttr.condition)
2283
                        : "")
2284
                )
2285
            }
2286

2287
            // if real entity relation is involved
2288
            if (relation.isManyToOne || relation.isOneToOneOwner) {
3,383,228✔
2289
                // JOIN `category` `category` ON `category`.`id` = `post`.`categoryId`
2290
                const condition = relation.joinColumns
21,873✔
2291
                    .map((joinColumn) => {
2292
                        return (
23,405✔
2293
                            destinationTableAlias +
2294
                            "." +
2295
                            joinColumn.referencedColumn!.propertyPath +
2296
                            "=" +
2297
                            parentAlias +
2298
                            "." +
2299
                            relation.propertyPath +
2300
                            "." +
2301
                            joinColumn.referencedColumn!.propertyPath
2302
                        )
2303
                    })
2304
                    .join(" AND ")
2305

2306
                return (
21,873✔
2307
                    " " +
2308
                    joinAttr.direction +
2309
                    " JOIN " +
2310
                    this.getTableName(destinationTableName) +
2311
                    " " +
2312
                    this.escape(destinationTableAlias) +
2313
                    this.createTableLockExpression() +
2314
                    " ON " +
2315
                    this.replacePropertyNames(condition + appendedCondition)
2316
                )
2317
            } else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
3,361,355✔
2318
                // JOIN `post` `post` ON `post`.`categoryId` = `category`.`id`
2319
                const condition = relation
548,605✔
2320
                    .inverseRelation!.joinColumns.map((joinColumn) => {
2321
                        if (
549,965✔
2322
                            relation.inverseEntityMetadata.tableType ===
550,125✔
2323
                                "entity-child" &&
2324
                            relation.inverseEntityMetadata.discriminatorColumn
2325
                        ) {
2326
                            appendedCondition +=
160✔
2327
                                " AND " +
2328
                                destinationTableAlias +
2329
                                "." +
2330
                                relation.inverseEntityMetadata
2331
                                    .discriminatorColumn.databaseName +
2332
                                "='" +
2333
                                relation.inverseEntityMetadata
2334
                                    .discriminatorValue +
2335
                                "'"
2336
                        }
2337

2338
                        return (
549,965✔
2339
                            destinationTableAlias +
2340
                            "." +
2341
                            relation.inverseRelation!.propertyPath +
2342
                            "." +
2343
                            joinColumn.referencedColumn!.propertyPath +
2344
                            "=" +
2345
                            parentAlias +
2346
                            "." +
2347
                            joinColumn.referencedColumn!.propertyPath
2348
                        )
2349
                    })
2350
                    .join(" AND ")
2351

2352
                if (!condition)
548,605!
2353
                    throw new TypeORMError(
×
2354
                        `Relation ${relation.entityMetadata.name}.${relation.propertyName} does not have join columns.`,
2355
                    )
2356

2357
                return (
548,605✔
2358
                    " " +
2359
                    joinAttr.direction +
2360
                    " JOIN " +
2361
                    this.getTableName(destinationTableName) +
2362
                    " " +
2363
                    this.escape(destinationTableAlias) +
2364
                    this.createTableLockExpression() +
2365
                    " ON " +
2366
                    this.replacePropertyNames(condition + appendedCondition)
2367
                )
2368
            } else {
2369
                // means many-to-many
2370
                const junctionTableName =
2371
                    relation.junctionEntityMetadata!.tablePath
2,812,750✔
2372

2373
                const junctionAlias = joinAttr.junctionAlias
2,812,750✔
2374
                let junctionCondition = "",
2,812,750✔
2375
                    destinationCondition = ""
2,812,750✔
2376

2377
                if (relation.isOwning) {
2,812,750✔
2378
                    junctionCondition = relation.joinColumns
2,811,390✔
2379
                        .map((joinColumn) => {
2380
                            // `post_category`.`postId` = `post`.`id`
2381
                            return (
2,812,350✔
2382
                                junctionAlias +
2383
                                "." +
2384
                                joinColumn.propertyPath +
2385
                                "=" +
2386
                                parentAlias +
2387
                                "." +
2388
                                joinColumn.referencedColumn!.propertyPath
2389
                            )
2390
                        })
2391
                        .join(" AND ")
2392

2393
                    destinationCondition = relation.inverseJoinColumns
2,811,390✔
2394
                        .map((joinColumn) => {
2395
                            // `category`.`id` = `post_category`.`categoryId`
2396
                            return (
2,812,390✔
2397
                                destinationTableAlias +
2398
                                "." +
2399
                                joinColumn.referencedColumn!.propertyPath +
2400
                                "=" +
2401
                                junctionAlias +
2402
                                "." +
2403
                                joinColumn.propertyPath
2404
                            )
2405
                        })
2406
                        .join(" AND ")
2407
                } else {
2408
                    junctionCondition = relation
1,360✔
2409
                        .inverseRelation!.inverseJoinColumns.map(
2410
                            (joinColumn) => {
2411
                                // `post_category`.`categoryId` = `category`.`id`
2412
                                return (
2,240✔
2413
                                    junctionAlias +
2414
                                    "." +
2415
                                    joinColumn.propertyPath +
2416
                                    "=" +
2417
                                    parentAlias +
2418
                                    "." +
2419
                                    joinColumn.referencedColumn!.propertyPath
2420
                                )
2421
                            },
2422
                        )
2423
                        .join(" AND ")
2424

2425
                    destinationCondition = relation
1,360✔
2426
                        .inverseRelation!.joinColumns.map((joinColumn) => {
2427
                            // `post`.`id` = `post_category`.`postId`
2428
                            return (
2,160✔
2429
                                destinationTableAlias +
2430
                                "." +
2431
                                joinColumn.referencedColumn!.propertyPath +
2432
                                "=" +
2433
                                junctionAlias +
2434
                                "." +
2435
                                joinColumn.propertyPath
2436
                            )
2437
                        })
2438
                        .join(" AND ")
2439
                }
2440

2441
                return (
2,812,750✔
2442
                    " " +
2443
                    joinAttr.direction +
2444
                    " JOIN " +
2445
                    this.getTableName(junctionTableName) +
2446
                    " " +
2447
                    this.escape(junctionAlias) +
2448
                    this.createTableLockExpression() +
2449
                    " ON " +
2450
                    this.replacePropertyNames(junctionCondition) +
2451
                    " " +
2452
                    joinAttr.direction +
2453
                    " JOIN " +
2454
                    this.getTableName(destinationTableName) +
2455
                    " " +
2456
                    this.escape(destinationTableAlias) +
2457
                    this.createTableLockExpression() +
2458
                    " ON " +
2459
                    this.replacePropertyNames(
2460
                        destinationCondition + appendedCondition,
2461
                    )
2462
                )
2463
            }
2464
        })
2465

2466
        return joins.join(" ")
763,640✔
2467
    }
2468

2469
    /**
2470
     * Creates "GROUP BY" part of SQL query.
2471
     */
2472
    protected createGroupByExpression() {
2473
        if (!this.expressionMap.groupBys || !this.expressionMap.groupBys.length)
763,640✔
2474
            return ""
759,014✔
2475
        return (
4,626✔
2476
            " GROUP BY " +
2477
            this.replacePropertyNames(this.expressionMap.groupBys.join(", "))
2478
        )
2479
    }
2480

2481
    /**
2482
     * Creates "ORDER BY" part of SQL query.
2483
     */
2484
    protected createOrderByExpression() {
2485
        const orderBys = this.expressionMap.allOrderBys
763,640✔
2486
        if (Object.keys(orderBys).length === 0) return ""
763,640✔
2487

2488
        return (
34,280✔
2489
            " ORDER BY " +
2490
            Object.keys(orderBys)
2491
                .map((columnName) => {
2492
                    const orderValue =
2493
                        typeof orderBys[columnName] === "string"
46,568✔
2494
                            ? orderBys[columnName]
2495
                            : (orderBys[columnName] as any).order +
2496
                              " " +
2497
                              (orderBys[columnName] as any).nulls
2498
                    const selection = this.expressionMap.selects.find(
46,568✔
2499
                        (s) => s.selection === columnName,
74,373✔
2500
                    )
2501
                    if (
46,568✔
2502
                        selection &&
66,713✔
2503
                        !selection.aliasName &&
2504
                        columnName.indexOf(".") !== -1
2505
                    ) {
2506
                        const criteriaParts = columnName.split(".")
778✔
2507
                        const aliasName = criteriaParts[0]
778✔
2508
                        const propertyPath = criteriaParts.slice(1).join(".")
778✔
2509
                        const alias = this.expressionMap.aliases.find(
778✔
2510
                            (alias) => alias.name === aliasName,
1,258✔
2511
                        )
2512
                        if (alias) {
778✔
2513
                            const column =
2514
                                alias.metadata.findColumnWithPropertyPath(
486✔
2515
                                    propertyPath,
2516
                                )
2517
                            if (column) {
486✔
2518
                                const orderAlias = DriverUtils.buildAlias(
486✔
2519
                                    this.connection.driver,
2520
                                    undefined,
2521
                                    aliasName,
2522
                                    column.databaseName,
2523
                                )
2524
                                return (
486✔
2525
                                    this.escape(orderAlias) + " " + orderValue
2526
                                )
2527
                            }
2528
                        }
2529
                    }
2530

2531
                    return (
46,082✔
2532
                        this.replacePropertyNames(columnName) + " " + orderValue
2533
                    )
2534
                })
2535
                .join(", ")
2536
        )
2537
    }
2538

2539
    /**
2540
     * Creates "LIMIT" and "OFFSET" parts of SQL query.
2541
     */
2542
    protected createLimitOffsetExpression(): string {
2543
        // in the case if nothing is joined in the query builder we don't need to make two requests to get paginated results
2544
        // we can use regular limit / offset, that's why we add offset and limit construction here based on skip and take values
2545
        let offset: number | undefined = this.expressionMap.offset,
763,640✔
2546
            limit: number | undefined = this.expressionMap.limit
763,640✔
2547
        if (
763,640✔
2548
            offset === undefined &&
2,279,458✔
2549
            limit === undefined &&
2550
            this.expressionMap.joinAttributes.length === 0
2551
        ) {
2552
            offset = this.expressionMap.skip
241,397✔
2553
            limit = this.expressionMap.take
241,397✔
2554
        }
2555

2556
        // Helper functions to check if values are set (including 0)
2557
        const hasLimit = limit !== undefined && limit !== null
763,640✔
2558
        const hasOffset = offset !== undefined && offset !== null
763,640✔
2559

2560
        if (this.connection.driver.options.type === "mssql") {
763,640✔
2561
            // Due to a limitation in SQL Server's parser implementation it does not support using
2562
            // OFFSET or FETCH NEXT without an ORDER BY clause being provided. In cases where the
2563
            // user does not request one we insert a dummy ORDER BY that does nothing and should
2564
            // have no effect on the query planner or on the order of the results returned.
2565
            // https://dba.stackexchange.com/a/193799
2566
            let prefix = ""
157,014✔
2567
            if (
157,014✔
2568
                (hasLimit || hasOffset) &&
314,037✔
2569
                Object.keys(this.expressionMap.allOrderBys).length <= 0
2570
            ) {
2571
                prefix = " ORDER BY (SELECT NULL)"
2,601✔
2572
            }
2573

2574
            if (hasLimit && hasOffset)
157,014✔
2575
                return (
261✔
2576
                    prefix +
2577
                    " OFFSET " +
2578
                    offset +
2579
                    " ROWS FETCH NEXT " +
2580
                    limit +
2581
                    " ROWS ONLY"
2582
                )
2583
            if (hasLimit)
156,753✔
2584
                return (
4,941✔
2585
                    prefix + " OFFSET 0 ROWS FETCH NEXT " + limit + " ROWS ONLY"
2586
                )
2587
            if (hasOffset) return prefix + " OFFSET " + offset + " ROWS"
151,812✔
2588
        } else if (
606,626✔
2589
            DriverUtils.isMySQLFamily(this.connection.driver) ||
2,071,321✔
2590
            this.connection.driver.options.type === "aurora-mysql" ||
2591
            this.connection.driver.options.type === "sap" ||
2592
            this.connection.driver.options.type === "spanner"
2593
        ) {
2594
            if (hasLimit && hasOffset)
141,709✔
2595
                return " LIMIT " + limit + " OFFSET " + offset
238✔
2596
            if (hasLimit) return " LIMIT " + limit
141,471✔
2597
            if (hasOffset) throw new OffsetWithoutLimitNotSupportedError()
136,940✔
2598
        } else if (DriverUtils.isSQLiteFamily(this.connection.driver)) {
464,917✔
2599
            if (hasLimit && hasOffset)
211,104✔
2600
                return " LIMIT " + limit + " OFFSET " + offset
368✔
2601
            if (hasLimit) return " LIMIT " + limit
210,736✔
2602
            if (hasOffset) return " LIMIT -1 OFFSET " + offset
204,024✔
2603
        } else if (this.connection.driver.options.type === "oracle") {
253,813✔
2604
            if (hasLimit && hasOffset)
34,036✔
2605
                return (
58✔
2606
                    " OFFSET " +
2607
                    offset +
2608
                    " ROWS FETCH NEXT " +
2609
                    limit +
2610
                    " ROWS ONLY"
2611
                )
2612
            if (hasLimit) return " FETCH NEXT " + limit + " ROWS ONLY"
33,978✔
2613
            if (hasOffset) return " OFFSET " + offset + " ROWS"
32,898✔
2614
        } else {
2615
            if (hasLimit && hasOffset)
219,777✔
2616
                return " LIMIT " + limit + " OFFSET " + offset
291✔
2617
            if (hasLimit) return " LIMIT " + limit
219,486✔
2618
            if (hasOffset) return " OFFSET " + offset
213,546✔
2619
        }
2620

2621
        return ""
739,180✔
2622
    }
2623

2624
    /**
2625
     * Creates "LOCK" part of SELECT Query after table Clause
2626
     * ex.
2627
     *  SELECT 1
2628
     *  FROM USER U WITH (NOLOCK)
2629
     *  JOIN ORDER O WITH (NOLOCK)
2630
     *      ON U.ID=O.OrderID
2631
     */
2632
    private createTableLockExpression(): string {
2633
        if (this.connection.driver.options.type === "mssql") {
6,978,266✔
2634
            switch (this.expressionMap.lockMode) {
1,433,718✔
2635
                case "pessimistic_read":
2636
                    return " WITH (HOLDLOCK, ROWLOCK)"
45✔
2637
                case "pessimistic_write":
2638
                    return " WITH (UPDLOCK, ROWLOCK)"
63✔
2639
                case "dirty_read":
2640
                    return " WITH (NOLOCK)"
99✔
2641
            }
2642
        }
2643

2644
        return ""
6,978,059✔
2645
    }
2646

2647
    /**
2648
     * Creates "LOCK" part of SQL query.
2649
     */
2650
    protected createLockExpression(): string {
2651
        const driver = this.connection.driver
763,632✔
2652

2653
        let lockTablesClause = ""
763,632✔
2654

2655
        if (this.expressionMap.lockTables) {
763,632✔
2656
            if (
147!
2657
                !(
2658
                    DriverUtils.isPostgresFamily(driver) ||
147!
2659
                    driver.options.type === "cockroachdb"
2660
                )
2661
            ) {
2662
                throw new TypeORMError(
×
2663
                    "Lock tables not supported in selected driver",
2664
                )
2665
            }
2666
            if (this.expressionMap.lockTables.length < 1) {
147✔
2667
                throw new TypeORMError("lockTables cannot be an empty array")
18✔
2668
            }
2669
            lockTablesClause = " OF " + this.expressionMap.lockTables.join(", ")
129✔
2670
        }
2671

2672
        let onLockExpression = ""
763,614✔
2673
        if (this.expressionMap.onLocked === "nowait") {
763,614✔
2674
            onLockExpression = " NOWAIT"
18✔
2675
        } else if (this.expressionMap.onLocked === "skip_locked") {
763,596✔
2676
            onLockExpression = " SKIP LOCKED"
48✔
2677
        }
2678
        switch (this.expressionMap.lockMode) {
763,614✔
2679
            case "pessimistic_read":
2680
                if (
168✔
2681
                    driver.options.type === "mysql" ||
321✔
2682
                    driver.options.type === "aurora-mysql"
2683
                ) {
2684
                    if (
15!
2685
                        DriverUtils.isReleaseVersionOrGreater(driver, "8.0.0")
2686
                    ) {
2687
                        return (
×
2688
                            " FOR SHARE" + lockTablesClause + onLockExpression
2689
                        )
2690
                    } else {
2691
                        return " LOCK IN SHARE MODE"
15✔
2692
                    }
2693
                } else if (driver.options.type === "mariadb") {
153✔
2694
                    return " LOCK IN SHARE MODE"
12✔
2695
                } else if (DriverUtils.isPostgresFamily(driver)) {
141✔
2696
                    return " FOR SHARE" + lockTablesClause + onLockExpression
60✔
2697
                } else if (driver.options.type === "oracle") {
81✔
2698
                    return " FOR UPDATE"
8✔
2699
                } else if (driver.options.type === "mssql") {
73✔
2700
                    return ""
45✔
2701
                } else {
2702
                    throw new LockNotSupportedOnGivenDriverError()
28✔
2703
                }
2704
            case "pessimistic_write":
2705
                if (
252✔
2706
                    DriverUtils.isMySQLFamily(driver) ||
702✔
2707
                    driver.options.type === "aurora-mysql" ||
2708
                    driver.options.type === "oracle"
2709
                ) {
2710
                    return " FOR UPDATE" + onLockExpression
35✔
2711
                } else if (
217✔
2712
                    DriverUtils.isPostgresFamily(driver) ||
299✔
2713
                    driver.options.type === "cockroachdb"
2714
                ) {
2715
                    return " FOR UPDATE" + lockTablesClause + onLockExpression
135✔
2716
                } else if (driver.options.type === "mssql") {
82✔
2717
                    return ""
54✔
2718
                } else {
2719
                    throw new LockNotSupportedOnGivenDriverError()
28✔
2720
                }
2721
            case "pessimistic_partial_write":
2722
                if (DriverUtils.isPostgresFamily(driver)) {
42✔
2723
                    return " FOR UPDATE" + lockTablesClause + " SKIP LOCKED"
30✔
2724
                } else if (DriverUtils.isMySQLFamily(driver)) {
12!
2725
                    return " FOR UPDATE SKIP LOCKED"
12✔
2726
                } else {
2727
                    throw new LockNotSupportedOnGivenDriverError()
×
2728
                }
2729
            case "pessimistic_write_or_fail":
2730
                if (
51✔
2731
                    DriverUtils.isPostgresFamily(driver) ||
66✔
2732
                    driver.options.type === "cockroachdb"
2733
                ) {
2734
                    return " FOR UPDATE" + lockTablesClause + " NOWAIT"
36✔
2735
                } else if (DriverUtils.isMySQLFamily(driver)) {
15!
2736
                    return " FOR UPDATE NOWAIT"
15✔
2737
                } else {
2738
                    throw new LockNotSupportedOnGivenDriverError()
×
2739
                }
2740
            case "for_no_key_update":
2741
                if (
67✔
2742
                    DriverUtils.isPostgresFamily(driver) ||
98✔
2743
                    driver.options.type === "cockroachdb"
2744
                ) {
2745
                    return (
36✔
2746
                        " FOR NO KEY UPDATE" +
2747
                        lockTablesClause +
2748
                        onLockExpression
2749
                    )
2750
                } else {
2751
                    throw new LockNotSupportedOnGivenDriverError()
31✔
2752
                }
2753
            case "for_key_share":
2754
                if (DriverUtils.isPostgresFamily(driver)) {
61✔
2755
                    return (
30✔
2756
                        " FOR KEY SHARE" + lockTablesClause + onLockExpression
2757
                    )
2758
                } else {
2759
                    throw new LockNotSupportedOnGivenDriverError()
31✔
2760
                }
2761
            default:
2762
                return ""
762,973✔
2763
        }
2764
    }
2765

2766
    /**
2767
     * Creates "HAVING" part of SQL query.
2768
     */
2769
    protected createHavingExpression() {
2770
        if (!this.expressionMap.havings || !this.expressionMap.havings.length)
763,640✔
2771
            return ""
763,640✔
2772
        const conditions = this.expressionMap.havings
×
2773
            .map((having, index) => {
2774
                switch (having.type) {
×
2775
                    case "and":
2776
                        return (
×
2777
                            (index > 0 ? "AND " : "") +
×
2778
                            this.replacePropertyNames(having.condition)
2779
                        )
2780
                    case "or":
2781
                        return (
×
2782
                            (index > 0 ? "OR " : "") +
×
2783
                            this.replacePropertyNames(having.condition)
2784
                        )
2785
                    default:
2786
                        return this.replacePropertyNames(having.condition)
×
2787
                }
2788
            })
2789
            .join(" ")
2790

2791
        if (!conditions.length) return ""
×
2792
        return " HAVING " + conditions
×
2793
    }
2794

2795
    protected buildEscapedEntityColumnSelects(
2796
        aliasName: string,
2797
        metadata: EntityMetadata,
2798
    ): SelectQuery[] {
2799
        const hasMainAlias = this.expressionMap.selects.some(
4,150,077✔
2800
            (select) => select.selection === aliasName,
18,175,400✔
2801
        )
2802

2803
        const columns: ColumnMetadata[] = []
4,150,077✔
2804
        if (hasMainAlias) {
4,150,077✔
2805
            columns.push(
4,086,260✔
2806
                ...metadata.columns.filter(
2807
                    (column) => column.isSelect === true,
13,427,538✔
2808
                ),
2809
            )
2810
        }
2811
        columns.push(
4,150,077✔
2812
            ...metadata.columns.filter((column) => {
2813
                return this.expressionMap.selects.some(
13,656,978✔
2814
                    (select) =>
2815
                        select.selection ===
113,035,105✔
2816
                        aliasName + "." + column.propertyPath,
2817
                )
2818
            }),
2819
        )
2820

2821
        // if user used partial selection and did not select some primary columns which are required to be selected
2822
        // we select those primary columns and mark them as "virtual". Later virtual column values will be removed from final entity
2823
        // to make entity contain exactly what user selected
2824
        if (columns.length === 0)
4,150,077✔
2825
            // however not in the case when nothing (even partial) was selected from this target (for example joins without selection)
2826
            return []
21,749✔
2827

2828
        const nonSelectedPrimaryColumns = this.expressionMap.queryEntity
4,128,328✔
2829
            ? metadata.primaryColumns.filter(
2830
                  (primaryColumn) => columns.indexOf(primaryColumn) === -1,
282,876✔
2831
              )
2832
            : []
2833
        const allColumns = [...columns, ...nonSelectedPrimaryColumns]
4,128,328✔
2834
        const finalSelects: SelectQuery[] = []
4,128,328✔
2835

2836
        const escapedAliasName = this.escape(aliasName)
4,128,328✔
2837
        allColumns.forEach((column) => {
4,128,328✔
2838
            let selectionPath =
2839
                escapedAliasName + "." + this.escape(column.databaseName)
13,503,312✔
2840

2841
            if (column.isVirtualProperty && column.query) {
13,503,312✔
2842
                selectionPath = `(${column.query(escapedAliasName)})`
600✔
2843
            }
2844

2845
            if (
13,503,312✔
2846
                this.connection.driver.spatialTypes.indexOf(column.type) !== -1
2847
            ) {
2848
                if (
522✔
2849
                    DriverUtils.isMySQLFamily(this.connection.driver) ||
984✔
2850
                    this.connection.driver.options.type === "aurora-mysql"
2851
                ) {
2852
                    const useLegacy = (
2853
                        this.connection.driver as
60✔
2854
                            | MysqlDriver
2855
                            | AuroraMysqlDriver
2856
                    ).options.legacySpatialSupport
2857
                    const asText = useLegacy ? "AsText" : "ST_AsText"
60✔
2858
                    selectionPath = `${asText}(${selectionPath})`
60✔
2859
                }
2860

2861
                if (DriverUtils.isPostgresFamily(this.connection.driver))
522✔
2862
                    if (column.precision) {
336!
2863
                        // cast to JSON to trigger parsing in the driver
2864
                        selectionPath = `ST_AsGeoJSON(${selectionPath}, ${column.precision})::json`
×
2865
                    } else {
2866
                        selectionPath = `ST_AsGeoJSON(${selectionPath})::json`
336✔
2867
                    }
2868
                if (this.connection.driver.options.type === "mssql")
522✔
2869
                    selectionPath = `${selectionPath}.ToString()`
126✔
2870
            }
2871

2872
            const selections = this.expressionMap.selects.filter(
13,503,312✔
2873
                (select) =>
2874
                    select.selection === aliasName + "." + column.propertyPath,
112,827,546✔
2875
            )
2876
            if (selections.length) {
13,503,312✔
2877
                selections.forEach((selection) => {
75,854✔
2878
                    finalSelects.push({
75,854✔
2879
                        selection: selectionPath,
2880
                        aliasName: selection.aliasName
75,854✔
2881
                            ? selection.aliasName
2882
                            : DriverUtils.buildAlias(
2883
                                  this.connection.driver,
2884
                                  undefined,
2885
                                  aliasName,
2886
                                  column.databaseName,
2887
                              ),
2888
                        // todo: need to keep in mind that custom selection.aliasName breaks hydrator. fix it later!
2889
                        virtual: selection.virtual,
2890
                    })
2891
                })
2892
            } else {
2893
                finalSelects.push({
13,427,458✔
2894
                    selection: selectionPath,
2895
                    aliasName: DriverUtils.buildAlias(
2896
                        this.connection.driver,
2897
                        undefined,
2898
                        aliasName,
2899
                        column.databaseName,
2900
                    ),
2901
                    // todo: need to keep in mind that custom selection.aliasName breaks hydrator. fix it later!
2902
                    virtual: hasMainAlias,
2903
                })
2904
            }
2905
        })
2906
        return finalSelects
4,128,328✔
2907
    }
2908

2909
    protected findEntityColumnSelects(
2910
        aliasName: string,
2911
        metadata: EntityMetadata,
2912
    ): SelectQuery[] {
2913
        const mainSelect = this.expressionMap.selects.find(
4,150,077✔
2914
            (select) => select.selection === aliasName,
18,175,400✔
2915
        )
2916
        if (mainSelect) return [mainSelect]
4,150,077✔
2917

2918
        return this.expressionMap.selects.filter((select) => {
63,817✔
2919
            return metadata.columns.some(
126,299✔
2920
                (column) =>
2921
                    select.selection === aliasName + "." + column.propertyPath,
341,094✔
2922
            )
2923
        })
2924
    }
2925

2926
    private computeCountExpression() {
2927
        const mainAlias = this.expressionMap.mainAlias!.name // todo: will this work with "fromTableName"?
1,961✔
2928
        const metadata = this.expressionMap.mainAlias!.metadata
1,961✔
2929

2930
        const primaryColumns = metadata.primaryColumns
1,961✔
2931
        const distinctAlias = this.escape(mainAlias)
1,961✔
2932

2933
        // If we aren't doing anything that will create a join, we can use a simpler `COUNT` instead
2934
        // so we prevent poor query patterns in the most likely cases
2935
        if (
1,961✔
2936
            this.expressionMap.joinAttributes.length === 0 &&
5,579✔
2937
            this.expressionMap.relationIdAttributes.length === 0 &&
2938
            this.expressionMap.relationCountAttributes.length === 0
2939
        ) {
2940
            return "COUNT(1)"
1,809✔
2941
        }
2942

2943
        // For everything else, we'll need to do some hackery to get the correct count values.
2944

2945
        if (
152✔
2946
            this.connection.driver.options.type === "cockroachdb" ||
292✔
2947
            DriverUtils.isPostgresFamily(this.connection.driver)
2948
        ) {
2949
            // Postgres and CockroachDB can pass multiple parameters to the `DISTINCT` function
2950
            // https://www.postgresql.org/docs/9.5/sql-select.html#SQL-DISTINCT
2951
            return (
36✔
2952
                "COUNT(DISTINCT(" +
2953
                primaryColumns
2954
                    .map(
2955
                        (c) =>
2956
                            `${distinctAlias}.${this.escape(c.databaseName)}`,
63✔
2957
                    )
2958
                    .join(", ") +
2959
                "))"
2960
            )
2961
        }
2962

2963
        if (DriverUtils.isMySQLFamily(this.connection.driver)) {
116✔
2964
            // MySQL & MariaDB can pass multiple parameters to the `DISTINCT` language construct
2965
            // https://mariadb.com/kb/en/count-distinct/
2966
            return (
18✔
2967
                "COUNT(DISTINCT " +
2968
                primaryColumns
2969
                    .map(
2970
                        (c) =>
2971
                            `${distinctAlias}.${this.escape(c.databaseName)}`,
36✔
2972
                    )
2973
                    .join(", ") +
2974
                ")"
2975
            )
2976
        }
2977

2978
        if (this.connection.driver.options.type === "mssql") {
98✔
2979
            // SQL Server has gotta be different from everyone else.  They don't support
2980
            // distinct counting multiple columns & they don't have the same operator
2981
            // characteristic for concatenating, so we gotta use the `CONCAT` function.
2982
            // However, If it's exactly 1 column we can omit the `CONCAT` for better performance.
2983

2984
            const columnsExpression = primaryColumns
36✔
2985
                .map(
2986
                    (primaryColumn) =>
2987
                        `${distinctAlias}.${this.escape(
63✔
2988
                            primaryColumn.databaseName,
2989
                        )}`,
2990
                )
2991
                .join(", '|;|', ")
2992

2993
            if (primaryColumns.length === 1) {
36✔
2994
                return `COUNT(DISTINCT(${columnsExpression}))`
18✔
2995
            }
2996

2997
            return `COUNT(DISTINCT(CONCAT(${columnsExpression})))`
18✔
2998
        }
2999

3000
        if (this.connection.driver.options.type === "spanner") {
62!
3001
            // spanner also has gotta be different from everyone else.
3002
            // they do not support concatenation of different column types without casting them to string
3003

3004
            if (primaryColumns.length === 1) {
×
3005
                return `COUNT(DISTINCT(${distinctAlias}.${this.escape(
×
3006
                    primaryColumns[0].databaseName,
3007
                )}))`
3008
            }
3009

3010
            const columnsExpression = primaryColumns
×
3011
                .map(
3012
                    (primaryColumn) =>
3013
                        `CAST(${distinctAlias}.${this.escape(
×
3014
                            primaryColumn.databaseName,
3015
                        )} AS STRING)`,
3016
                )
3017
                .join(", '|;|', ")
3018
            return `COUNT(DISTINCT(CONCAT(${columnsExpression})))`
×
3019
        }
3020

3021
        // If all else fails, fall back to a `COUNT` and `DISTINCT` across all the primary columns concatenated.
3022
        // Per the SQL spec, this is the canonical string concatenation mechanism which is most
3023
        // likely to work across servers implementing the SQL standard.
3024

3025
        // Please note, if there is only one primary column that the concatenation does not occur in this
3026
        // query and the query is a standard `COUNT DISTINCT` in that case.
3027

3028
        return (
62✔
3029
            `COUNT(DISTINCT(` +
3030
            primaryColumns
3031
                .map((c) => `${distinctAlias}.${this.escape(c.databaseName)}`)
110✔
3032
                .join(" || '|;|' || ") +
3033
            "))"
3034
        )
3035
    }
3036

3037
    protected async executeCountQuery(
3038
        queryRunner: QueryRunner,
3039
    ): Promise<number> {
3040
        const countSql = this.computeCountExpression()
1,961✔
3041

3042
        const results = await this.clone()
1,961✔
3043
            .orderBy()
3044
            .groupBy()
3045
            .offset(undefined)
3046
            .limit(undefined)
3047
            .skip(undefined)
3048
            .take(undefined)
3049
            .select(countSql, "cnt")
3050
            .setOption("disable-global-order")
3051
            .loadRawResults(queryRunner)
3052

3053
        if (!results || !results[0] || !results[0]["cnt"]) return 0
1,961✔
3054

3055
        return parseInt(results[0]["cnt"])
1,911✔
3056
    }
3057

3058
    protected async executeExistsQuery(
3059
        queryRunner: QueryRunner,
3060
    ): Promise<boolean> {
3061
        const results = await this.connection
280✔
3062
            .createQueryBuilder()
3063
            .fromDummy()
3064
            .select("1", "row_exists")
3065
            .whereExists(this)
3066
            .limit(1)
3067
            .loadRawResults(queryRunner)
3068

3069
        return results.length > 0
280✔
3070
    }
3071

3072
    protected applyFindOptions() {
3073
        // todo: convert relations: string[] to object map to simplify code
3074
        // todo: same with selects
3075

3076
        if (this.expressionMap.mainAlias!.metadata) {
264,181✔
3077
            if (this.findOptions.relationLoadStrategy) {
264,181✔
3078
                this.expressionMap.relationLoadStrategy =
36✔
3079
                    this.findOptions.relationLoadStrategy
3080
            }
3081

3082
            if (this.findOptions.comment) {
264,181✔
3083
                this.comment(this.findOptions.comment)
40✔
3084
            }
3085

3086
            if (this.findOptions.withDeleted) {
264,181✔
3087
                this.withDeleted()
168,861✔
3088
            }
3089

3090
            if (this.findOptions.select) {
264,181✔
3091
                const select = Array.isArray(this.findOptions.select)
1,034!
3092
                    ? OrmUtils.propertyPathsToTruthyObject(
3093
                          this.findOptions.select as string[],
3094
                      )
3095
                    : this.findOptions.select
3096

3097
                this.buildSelect(
1,034✔
3098
                    select,
3099
                    this.expressionMap.mainAlias!.metadata,
3100
                    this.expressionMap.mainAlias!.name,
3101
                )
3102
            }
3103

3104
            if (this.selects.length) {
264,141✔
3105
                this.select(this.selects)
914✔
3106
            }
3107

3108
            this.selects = []
264,141✔
3109
            if (this.findOptions.relations) {
264,141✔
3110
                const relations = Array.isArray(this.findOptions.relations)
70,330✔
3111
                    ? OrmUtils.propertyPathsToTruthyObject(
3112
                          this.findOptions.relations,
3113
                      )
3114
                    : this.findOptions.relations
3115

3116
                this.buildRelations(
70,330✔
3117
                    relations,
3118
                    typeof this.findOptions.select === "object"
70,330✔
3119
                        ? (this.findOptions.select as FindOptionsSelect<any>)
3120
                        : undefined,
3121
                    this.expressionMap.mainAlias!.metadata,
3122
                    this.expressionMap.mainAlias!.name,
3123
                )
3124
                if (
70,090✔
3125
                    this.findOptions.loadEagerRelations !== false &&
140,180✔
3126
                    this.expressionMap.relationLoadStrategy === "join"
3127
                ) {
3128
                    this.buildEagerRelations(
70,054✔
3129
                        relations,
3130
                        typeof this.findOptions.select === "object"
70,054✔
3131
                            ? (this.findOptions
3132
                                  .select as FindOptionsSelect<any>)
3133
                            : undefined,
3134
                        this.expressionMap.mainAlias!.metadata,
3135
                        this.expressionMap.mainAlias!.name,
3136
                    )
3137
                }
3138
            }
3139
            if (this.selects.length) {
263,901✔
3140
                this.addSelect(this.selects)
320✔
3141
            }
3142

3143
            if (this.findOptions.where) {
263,901✔
3144
                this.conditions = this.buildWhere(
87,748✔
3145
                    this.findOptions.where,
3146
                    this.expressionMap.mainAlias!.metadata,
3147
                    this.expressionMap.mainAlias!.name,
3148
                )
3149

3150
                if (this.conditions.length)
87,668✔
3151
                    this.andWhere(
86,807✔
3152
                        this.conditions.substr(0, 1) !== "("
86,807!
3153
                            ? "(" + this.conditions + ")"
3154
                            : this.conditions,
3155
                    ) // temporary and where and braces
3156
            }
3157

3158
            if (this.findOptions.order) {
263,821✔
3159
                this.buildOrder(
3,678✔
3160
                    this.findOptions.order,
3161
                    this.expressionMap.mainAlias!.metadata,
3162
                    this.expressionMap.mainAlias!.name,
3163
                )
3164
            }
3165

3166
            // apply joins
3167
            if (this.joins.length) {
263,781✔
3168
                this.joins.forEach((join) => {
70,849✔
3169
                    if (join.select && !join.selection) {
553,736✔
3170
                        // if (join.selection) {
3171
                        //
3172
                        // } else {
3173
                        if (join.type === "inner") {
552,215!
3174
                            this.innerJoinAndSelect(
×
3175
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3176
                                join.alias,
3177
                            )
3178
                        } else {
3179
                            this.leftJoinAndSelect(
552,215✔
3180
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3181
                                join.alias,
3182
                            )
3183
                        }
3184
                        // }
3185
                    } else {
3186
                        if (join.type === "inner") {
1,521!
3187
                            this.innerJoin(
×
3188
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3189
                                join.alias,
3190
                            )
3191
                        } else {
3192
                            this.leftJoin(
1,521✔
3193
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3194
                                join.alias,
3195
                            )
3196
                        }
3197
                    }
3198

3199
                    // if (join.select) {
3200
                    //     if (this.findOptions.loadEagerRelations !== false) {
3201
                    //         FindOptionsUtils.joinEagerRelations(
3202
                    //             this,
3203
                    //             join.alias,
3204
                    //             join.relationMetadata.inverseEntityMetadata
3205
                    //         );
3206
                    //     }
3207
                    // }
3208
                })
3209
            }
3210

3211
            // if (this.conditions.length) {
3212
            //     this.where(this.conditions.join(" AND "));
3213
            // }
3214

3215
            // apply offset
3216
            if (this.findOptions.skip !== undefined) {
263,781✔
3217
                // if (this.findOptions.options && this.findOptions.options.pagination === false) {
3218
                //     this.offset(this.findOptions.skip);
3219
                // } else {
3220
                this.skip(this.findOptions.skip)
246✔
3221
                // }
3222
            }
3223

3224
            // apply limit
3225
            if (this.findOptions.take !== undefined) {
263,741✔
3226
                // if (this.findOptions.options && this.findOptions.options.pagination === false) {
3227
                //     this.limit(this.findOptions.take);
3228
                // } else {
3229
                this.take(this.findOptions.take)
22,949✔
3230
                // }
3231
            }
3232

3233
            // apply caching options
3234
            if (typeof this.findOptions.cache === "number") {
263,701✔
3235
                this.cache(this.findOptions.cache)
80✔
3236
            } else if (typeof this.findOptions.cache === "boolean") {
263,621✔
3237
                this.cache(this.findOptions.cache)
120✔
3238
            } else if (typeof this.findOptions.cache === "object") {
263,501✔
3239
                this.cache(
200✔
3240
                    this.findOptions.cache.id,
3241
                    this.findOptions.cache.milliseconds,
3242
                )
3243
            }
3244

3245
            if (this.findOptions.join) {
263,701✔
3246
                if (this.findOptions.join.leftJoin)
929!
3247
                    Object.keys(this.findOptions.join.leftJoin).forEach(
×
3248
                        (key) => {
3249
                            this.leftJoin(
×
3250
                                this.findOptions.join!.leftJoin![key],
3251
                                key,
3252
                            )
3253
                        },
3254
                    )
3255

3256
                if (this.findOptions.join.innerJoin)
929!
3257
                    Object.keys(this.findOptions.join.innerJoin).forEach(
×
3258
                        (key) => {
3259
                            this.innerJoin(
×
3260
                                this.findOptions.join!.innerJoin![key],
3261
                                key,
3262
                            )
3263
                        },
3264
                    )
3265

3266
                if (this.findOptions.join.leftJoinAndSelect)
929✔
3267
                    Object.keys(
560✔
3268
                        this.findOptions.join.leftJoinAndSelect,
3269
                    ).forEach((key) => {
3270
                        this.leftJoinAndSelect(
840✔
3271
                            this.findOptions.join!.leftJoinAndSelect![key],
3272
                            key,
3273
                        )
3274
                    })
3275

3276
                if (this.findOptions.join.innerJoinAndSelect)
929✔
3277
                    Object.keys(
369✔
3278
                        this.findOptions.join.innerJoinAndSelect,
3279
                    ).forEach((key) => {
3280
                        this.innerJoinAndSelect(
378✔
3281
                            this.findOptions.join!.innerJoinAndSelect![key],
3282
                            key,
3283
                        )
3284
                    })
3285
            }
3286

3287
            if (this.findOptions.lock) {
263,701✔
3288
                if (this.findOptions.lock.mode === "optimistic") {
616✔
3289
                    this.setLock(
324✔
3290
                        this.findOptions.lock.mode,
3291
                        this.findOptions.lock.version,
3292
                    )
3293
                } else if (
292✔
3294
                    this.findOptions.lock.mode === "pessimistic_read" ||
636✔
3295
                    this.findOptions.lock.mode === "pessimistic_write" ||
3296
                    this.findOptions.lock.mode === "dirty_read" ||
3297
                    this.findOptions.lock.mode ===
3298
                        "pessimistic_partial_write" ||
3299
                    this.findOptions.lock.mode ===
3300
                        "pessimistic_write_or_fail" ||
3301
                    this.findOptions.lock.mode === "for_no_key_update" ||
3302
                    this.findOptions.lock.mode === "for_key_share"
3303
                ) {
3304
                    const tableNames = this.findOptions.lock.tables
292✔
3305
                        ? this.findOptions.lock.tables.map((table) => {
3306
                              const tableAlias =
3307
                                  this.expressionMap.aliases.find((alias) => {
63✔
3308
                                      return (
99✔
3309
                                          alias.metadata
3310
                                              .tableNameWithoutPrefix === table
3311
                                      )
3312
                                  })
3313
                              if (!tableAlias) {
63✔
3314
                                  throw new TypeORMError(
9✔
3315
                                      `"${table}" is not part of this query`,
3316
                                  )
3317
                              }
3318
                              return this.escape(tableAlias.name)
54✔
3319
                          })
3320
                        : undefined
3321
                    this.setLock(
283✔
3322
                        this.findOptions.lock.mode,
3323
                        undefined,
3324
                        tableNames,
3325
                    )
3326

3327
                    if (this.findOptions.lock.onLocked) {
283✔
3328
                        this.setOnLocked(this.findOptions.lock.onLocked)
15✔
3329
                    }
3330
                }
3331
            }
3332

3333
            if (this.findOptions.loadRelationIds === true) {
263,692✔
3334
                this.loadAllRelationIds()
40✔
3335
            } else if (typeof this.findOptions.loadRelationIds === "object") {
263,652✔
3336
                this.loadAllRelationIds(this.findOptions.loadRelationIds as any)
168,338✔
3337
            }
3338

3339
            if (this.findOptions.loadEagerRelations !== false) {
263,692✔
3340
                FindOptionsUtils.joinEagerRelations(
95,274✔
3341
                    this,
3342
                    this.expressionMap.mainAlias!.name,
3343
                    this.expressionMap.mainAlias!.metadata,
3344
                )
3345
            }
3346

3347
            if (this.findOptions.transaction === true) {
263,692✔
3348
                this.expressionMap.useTransaction = true
40✔
3349
            }
3350

3351
            // if (this.orderBys.length) {
3352
            //     this.orderBys.forEach(orderBy => {
3353
            //         this.addOrderBy(orderBy.alias, orderBy.direction, orderBy.nulls);
3354
            //     });
3355
            // }
3356

3357
            // todo
3358
            // if (this.options.options && this.options.options.eagerRelations) {
3359
            //     this.queryBuilder
3360
            // }
3361

3362
            // todo
3363
            // if (this.findOptions.options && this.findOptions.listeners === false) {
3364
            //     this.callListeners(false);
3365
            // }
3366
        }
3367
    }
3368

3369
    public concatRelationMetadata(relationMetadata: RelationMetadata) {
3370
        this.relationMetadatas.push(relationMetadata)
24✔
3371
    }
3372

3373
    /**
3374
     * Executes sql generated by query builder and returns object with raw results and entities created from them.
3375
     */
3376
    protected async executeEntitiesAndRawResults(
3377
        queryRunner: QueryRunner,
3378
    ): Promise<{ entities: Entity[]; raw: any[] }> {
3379
        if (!this.expressionMap.mainAlias)
243,203!
3380
            throw new TypeORMError(
×
3381
                `Alias is not set. Use "from" method to set an alias.`,
3382
            )
3383

3384
        if (
243,203✔
3385
            (this.expressionMap.lockMode === "pessimistic_read" ||
1,457,735✔
3386
                this.expressionMap.lockMode === "pessimistic_write" ||
3387
                this.expressionMap.lockMode === "pessimistic_partial_write" ||
3388
                this.expressionMap.lockMode === "pessimistic_write_or_fail" ||
3389
                this.expressionMap.lockMode === "for_no_key_update" ||
3390
                this.expressionMap.lockMode === "for_key_share") &&
3391
            !queryRunner.isTransactionActive
3392
        )
3393
            throw new PessimisticLockTransactionRequiredError()
134✔
3394

3395
        if (this.expressionMap.lockMode === "optimistic") {
243,069✔
3396
            const metadata = this.expressionMap.mainAlias.metadata
324✔
3397
            if (!metadata.versionColumn && !metadata.updateDateColumn)
324✔
3398
                throw new NoVersionOrUpdateDateColumnError(metadata.name)
40✔
3399
        }
3400

3401
        const relationIdLoader = new RelationIdLoader(
243,029✔
3402
            this.connection,
3403
            queryRunner,
3404
            this.expressionMap.relationIdAttributes,
3405
        )
3406
        const relationCountLoader = new RelationCountLoader(
243,029✔
3407
            this.connection,
3408
            queryRunner,
3409
            this.expressionMap.relationCountAttributes,
3410
        )
3411
        const relationIdMetadataTransformer =
3412
            new RelationIdMetadataToAttributeTransformer(this.expressionMap)
243,029✔
3413
        relationIdMetadataTransformer.transform()
243,029✔
3414
        const relationCountMetadataTransformer =
3415
            new RelationCountMetadataToAttributeTransformer(this.expressionMap)
243,029✔
3416
        relationCountMetadataTransformer.transform()
243,029✔
3417

3418
        let rawResults: any[] = [],
243,029✔
3419
            entities: any[] = []
243,029✔
3420

3421
        // for pagination enabled (e.g. skip and take) its much more complicated - its a special process
3422
        // where we make two queries to find the data we need
3423
        // first query find ids in skip and take range
3424
        // and second query loads the actual data in given ids range
3425
        if (
243,029✔
3426
            (this.expressionMap.skip || this.expressionMap.take) &&
508,978✔
3427
            this.expressionMap.joinAttributes.length > 0
3428
        ) {
3429
            // we are skipping order by here because its not working in subqueries anyway
3430
            // to make order by working we need to apply it on a distinct query
3431
            const [selects, orderBys] =
3432
                this.createOrderByCombinedWithSelectExpression("distinctAlias")
10,478✔
3433
            const metadata = this.expressionMap.mainAlias.metadata
10,478✔
3434
            const mainAliasName = this.expressionMap.mainAlias.name
10,478✔
3435

3436
            const querySelects = metadata.primaryColumns.map(
10,478✔
3437
                (primaryColumn) => {
3438
                    const distinctAlias = this.escape("distinctAlias")
10,604✔
3439
                    const columnAlias = this.escape(
10,604✔
3440
                        DriverUtils.buildAlias(
3441
                            this.connection.driver,
3442
                            undefined,
3443
                            mainAliasName,
3444
                            primaryColumn.databaseName,
3445
                        ),
3446
                    )
3447
                    if (!orderBys[columnAlias])
10,604✔
3448
                        // make sure we aren't overriding user-defined order in inverse direction
3449
                        orderBys[columnAlias] = "ASC"
10,604✔
3450

3451
                    const alias = DriverUtils.buildAlias(
10,604✔
3452
                        this.connection.driver,
3453
                        undefined,
3454
                        "ids_" + mainAliasName,
3455
                        primaryColumn.databaseName,
3456
                    )
3457

3458
                    return `${distinctAlias}.${columnAlias} AS ${this.escape(
10,604✔
3459
                        alias,
3460
                    )}`
3461
                },
3462
            )
3463

3464
            const originalQuery = this.clone()
10,478✔
3465

3466
            // preserve original timeTravel value since we set it to "false" in subquery
3467
            const originalQueryTimeTravel =
3468
                originalQuery.expressionMap.timeTravel
10,478✔
3469

3470
            rawResults = await new SelectQueryBuilder(
10,478✔
3471
                this.connection,
3472
                queryRunner,
3473
            )
3474
                .select(`DISTINCT ${querySelects.join(", ")}`)
3475
                .addSelect(selects)
3476
                .from(
3477
                    `(${originalQuery
3478
                        .orderBy()
3479
                        .timeTravelQuery(false) // set it to "false" since time travel clause must appear at the very end and applies to the entire SELECT clause.
3480
                        .getQuery()})`,
3481
                    "distinctAlias",
3482
                )
3483
                .timeTravelQuery(originalQueryTimeTravel)
3484
                .offset(this.expressionMap.skip)
3485
                .limit(this.expressionMap.take)
3486
                .orderBy(orderBys)
3487
                .cache(
3488
                    this.expressionMap.cache && this.expressionMap.cacheId
21,076✔
3489
                        ? `${this.expressionMap.cacheId}-pagination`
3490
                        : this.expressionMap.cache,
3491
                    this.expressionMap.cacheDuration,
3492
                )
3493
                .setParameters(this.getParameters())
3494
                .setNativeParameters(this.expressionMap.nativeParameters)
3495
                .getRawMany()
3496

3497
            if (rawResults.length > 0) {
10,461✔
3498
                let condition = ""
10,281✔
3499
                const parameters: ObjectLiteral = {}
10,281✔
3500
                if (metadata.hasMultiplePrimaryKeys) {
10,281✔
3501
                    condition = rawResults
126✔
3502
                        .map((result, index) => {
3503
                            return metadata.primaryColumns
646✔
3504
                                .map((primaryColumn) => {
3505
                                    const paramKey = `orm_distinct_ids_${index}_${primaryColumn.databaseName}`
1,292✔
3506
                                    const paramKeyResult =
3507
                                        DriverUtils.buildAlias(
1,292✔
3508
                                            this.connection.driver,
3509
                                            undefined,
3510
                                            "ids_" + mainAliasName,
3511
                                            primaryColumn.databaseName,
3512
                                        )
3513
                                    parameters[paramKey] =
1,292✔
3514
                                        result[paramKeyResult]
3515
                                    return `${mainAliasName}.${primaryColumn.propertyPath}=:${paramKey}`
1,292✔
3516
                                })
3517
                                .join(" AND ")
3518
                        })
3519
                        .join(" OR ")
3520
                } else {
3521
                    const alias = DriverUtils.buildAlias(
10,155✔
3522
                        this.connection.driver,
3523
                        undefined,
3524
                        "ids_" + mainAliasName,
3525
                        metadata.primaryColumns[0].databaseName,
3526
                    )
3527

3528
                    const ids = rawResults.map((result) => result[alias])
18,874✔
3529
                    const areAllNumbers = ids.every(
10,155✔
3530
                        (id: any) => typeof id === "number",
18,811✔
3531
                    )
3532
                    if (areAllNumbers) {
10,155✔
3533
                        // fixes #190. if all numbers then its safe to perform query without parameter
3534
                        condition = `${mainAliasName}.${
9,276✔
3535
                            metadata.primaryColumns[0].propertyPath
3536
                        } IN (${ids.join(", ")})`
3537
                    } else {
3538
                        parameters["orm_distinct_ids"] = ids
879✔
3539
                        condition =
879✔
3540
                            mainAliasName +
3541
                            "." +
3542
                            metadata.primaryColumns[0].propertyPath +
3543
                            " IN (:...orm_distinct_ids)"
3544
                    }
3545
                }
3546
                rawResults = await this.clone()
10,281✔
3547
                    .mergeExpressionMap({
3548
                        extraAppendedAndWhereCondition: condition,
3549
                    })
3550
                    .setParameters(parameters)
3551
                    .loadRawResults(queryRunner)
3552
            }
3553
        } else {
3554
            rawResults = await this.loadRawResults(queryRunner)
232,551✔
3555
        }
3556

3557
        if (rawResults.length > 0) {
242,843✔
3558
            // transform raw results into entities
3559
            const rawRelationIdResults = await relationIdLoader.load(rawResults)
82,207✔
3560
            const rawRelationCountResults = await relationCountLoader.load(
82,207✔
3561
                rawResults,
3562
            )
3563
            const transformer = new RawSqlResultsToEntityTransformer(
82,207✔
3564
                this.expressionMap,
3565
                this.connection.driver,
3566
                rawRelationIdResults,
3567
                rawRelationCountResults,
3568
                this.queryRunner,
3569
            )
3570
            entities = transformer.transform(
82,207✔
3571
                rawResults,
3572
                this.expressionMap.mainAlias!,
3573
            )
3574

3575
            // broadcast all "after load" events
3576
            if (
82,201✔
3577
                this.expressionMap.callListeners === true &&
164,402✔
3578
                this.expressionMap.mainAlias.hasMetadata
3579
            ) {
3580
                await queryRunner.broadcaster.broadcast(
82,201✔
3581
                    "Load",
3582
                    this.expressionMap.mainAlias.metadata,
3583
                    entities,
3584
                )
3585
            }
3586
        }
3587

3588
        if (this.expressionMap.relationLoadStrategy === "query") {
242,837✔
3589
            const queryStrategyRelationIdLoader =
3590
                new QueryStrategyRelationIdLoader(this.connection, queryRunner)
75✔
3591

3592
            await Promise.all(
75✔
3593
                this.relationMetadatas.map(async (relation) => {
3594
                    const relationTarget = relation.inverseEntityMetadata.target
24✔
3595
                    const relationAlias =
3596
                        relation.inverseEntityMetadata.targetName
24✔
3597

3598
                    const select = Array.isArray(this.findOptions.select)
24!
3599
                        ? OrmUtils.propertyPathsToTruthyObject(
3600
                              this.findOptions.select as string[],
3601
                          )
3602
                        : this.findOptions.select
3603
                    const relations = Array.isArray(this.findOptions.relations)
24!
3604
                        ? OrmUtils.propertyPathsToTruthyObject(
3605
                              this.findOptions.relations,
3606
                          )
3607
                        : this.findOptions.relations
3608

3609
                    const queryBuilder = this.createQueryBuilder(queryRunner)
24✔
3610
                        .select(relationAlias)
3611
                        .from(relationTarget, relationAlias)
3612
                        .setFindOptions({
3613
                            select: select
24!
3614
                                ? OrmUtils.deepValue(
3615
                                      select,
3616
                                      relation.propertyPath,
3617
                                  )
3618
                                : undefined,
3619
                            order: this.findOptions.order
24!
3620
                                ? OrmUtils.deepValue(
3621
                                      this.findOptions.order,
3622
                                      relation.propertyPath,
3623
                                  )
3624
                                : undefined,
3625
                            relations: relations
24✔
3626
                                ? OrmUtils.deepValue(
3627
                                      relations,
3628
                                      relation.propertyPath,
3629
                                  )
3630
                                : undefined,
3631
                            withDeleted: this.findOptions.withDeleted,
3632
                            relationLoadStrategy:
3633
                                this.findOptions.relationLoadStrategy,
3634
                        })
3635
                    if (entities.length > 0) {
24✔
3636
                        const relatedEntityGroups: any[] =
3637
                            await queryStrategyRelationIdLoader.loadManyToManyRelationIdsAndGroup(
24✔
3638
                                relation,
3639
                                entities,
3640
                                undefined,
3641
                                queryBuilder,
3642
                            )
3643
                        entities.forEach((entity) => {
24✔
3644
                            const relatedEntityGroup = relatedEntityGroups.find(
30✔
3645
                                (group) => group.entity === entity,
36✔
3646
                            )
3647
                            if (relatedEntityGroup) {
30✔
3648
                                const value =
3649
                                    relatedEntityGroup.related === undefined
30!
3650
                                        ? null
3651
                                        : relatedEntityGroup.related
3652
                                relation.setEntityValue(entity, value)
30✔
3653
                            }
3654
                        })
3655
                    }
3656
                }),
3657
            )
3658
        }
3659

3660
        return {
242,837✔
3661
            raw: rawResults,
3662
            entities: entities,
3663
        }
3664
    }
3665

3666
    protected createOrderByCombinedWithSelectExpression(
3667
        parentAlias: string,
3668
    ): [string, OrderByCondition] {
3669
        // if table has a default order then apply it
3670
        const orderBys = this.expressionMap.allOrderBys
10,478✔
3671
        const selectString = Object.keys(orderBys)
10,478✔
3672
            .map((orderCriteria) => {
3673
                if (orderCriteria.indexOf(".") !== -1) {
378✔
3674
                    const criteriaParts = orderCriteria.split(".")
360✔
3675
                    const aliasName = criteriaParts[0]
360✔
3676
                    const propertyPath = criteriaParts.slice(1).join(".")
360✔
3677
                    const alias = this.expressionMap.findAliasByName(aliasName)
360✔
3678
                    const column =
3679
                        alias.metadata.findColumnWithPropertyPath(propertyPath)
360✔
3680
                    return (
360✔
3681
                        this.escape(parentAlias) +
3682
                        "." +
3683
                        this.escape(
3684
                            DriverUtils.buildAlias(
3685
                                this.connection.driver,
3686
                                undefined,
3687
                                aliasName,
3688
                                column!.databaseName,
3689
                            ),
3690
                        )
3691
                    )
3692
                } else {
3693
                    if (
18✔
3694
                        this.expressionMap.selects.find(
3695
                            (select) =>
3696
                                select.selection === orderCriteria ||
36✔
3697
                                select.aliasName === orderCriteria,
3698
                        )
3699
                    )
3700
                        return (
12✔
3701
                            this.escape(parentAlias) +
3702
                            "." +
3703
                            this.escape(orderCriteria)
3704
                        )
3705

3706
                    return ""
6✔
3707
                }
3708
            })
3709
            .join(", ")
3710

3711
        const orderByObject: OrderByCondition = {}
10,478✔
3712
        Object.keys(orderBys).forEach((orderCriteria) => {
10,478✔
3713
            if (orderCriteria.indexOf(".") !== -1) {
378✔
3714
                const criteriaParts = orderCriteria.split(".")
360✔
3715
                const aliasName = criteriaParts[0]
360✔
3716
                const propertyPath = criteriaParts.slice(1).join(".")
360✔
3717
                const alias = this.expressionMap.findAliasByName(aliasName)
360✔
3718
                const column =
3719
                    alias.metadata.findColumnWithPropertyPath(propertyPath)
360✔
3720
                orderByObject[
360✔
3721
                    this.escape(parentAlias) +
3722
                        "." +
3723
                        this.escape(
3724
                            DriverUtils.buildAlias(
3725
                                this.connection.driver,
3726
                                undefined,
3727
                                aliasName,
3728
                                column!.databaseName,
3729
                            ),
3730
                        )
3731
                ] = orderBys[orderCriteria]
3732
            } else {
3733
                if (
18✔
3734
                    this.expressionMap.selects.find(
3735
                        (select) =>
3736
                            select.selection === orderCriteria ||
36✔
3737
                            select.aliasName === orderCriteria,
3738
                    )
3739
                ) {
3740
                    orderByObject[
12✔
3741
                        this.escape(parentAlias) +
3742
                            "." +
3743
                            this.escape(orderCriteria)
3744
                    ] = orderBys[orderCriteria]
3745
                } else {
3746
                    orderByObject[orderCriteria] = orderBys[orderCriteria]
6✔
3747
                }
3748
            }
3749
        })
3750

3751
        return [selectString, orderByObject]
10,478✔
3752
    }
3753

3754
    /**
3755
     * Loads raw results from the database.
3756
     */
3757
    protected async loadRawResults(queryRunner: QueryRunner) {
3758
        const [sql, parameters] = this.getQueryAndParameters()
283,886✔
3759
        const queryId =
3760
            sql +
283,742✔
3761
            " -- PARAMETERS: " +
3762
            JSON.stringify(parameters, (_, value) =>
3763
                typeof value === "bigint" ? value.toString() : value,
620,499!
3764
            )
3765
        const cacheOptions =
3766
            typeof this.connection.options.cache === "object"
283,742✔
3767
                ? this.connection.options.cache
3768
                : {}
3769
        let savedQueryResultCacheOptions: QueryResultCacheOptions | undefined =
3770
            undefined
283,742✔
3771
        const isCachingEnabled =
3772
            // Caching is enabled globally and isn't disabled locally.
3773
            (cacheOptions.alwaysEnabled &&
283,742✔
3774
                this.expressionMap.cache !== false) ||
3775
            // ...or it's enabled locally explicitly.
3776
            this.expressionMap.cache === true
3777
        let cacheError = false
283,742✔
3778
        if (this.connection.queryResultCache && isCachingEnabled) {
283,742✔
3779
            try {
1,984✔
3780
                savedQueryResultCacheOptions =
1,984✔
3781
                    await this.connection.queryResultCache.getFromCache(
3782
                        {
3783
                            identifier: this.expressionMap.cacheId,
3784
                            query: queryId,
3785
                            duration:
3786
                                this.expressionMap.cacheDuration ||
3,872✔
3787
                                cacheOptions.duration ||
3788
                                1000,
3789
                        },
3790
                        queryRunner,
3791
                    )
3792
                if (
1,984✔
3793
                    savedQueryResultCacheOptions &&
3,196✔
3794
                    !this.connection.queryResultCache.isExpired(
3795
                        savedQueryResultCacheOptions,
3796
                    )
3797
                ) {
3798
                    return JSON.parse(savedQueryResultCacheOptions.result)
852✔
3799
                }
3800
            } catch (error) {
3801
                if (!cacheOptions.ignoreErrors) {
×
3802
                    throw error
×
3803
                }
3804
                cacheError = true
×
3805
            }
3806
        }
3807

3808
        const results = await queryRunner.query(sql, parameters, true)
282,890✔
3809

3810
        if (
282,848✔
3811
            !cacheError &&
569,532✔
3812
            this.connection.queryResultCache &&
3813
            isCachingEnabled
3814
        ) {
3815
            try {
1,132✔
3816
                await this.connection.queryResultCache.storeInCache(
1,132✔
3817
                    {
3818
                        identifier: this.expressionMap.cacheId,
3819
                        query: queryId,
3820
                        time: Date.now(),
3821
                        duration:
3822
                            this.expressionMap.cacheDuration ||
2,356✔
3823
                            cacheOptions.duration ||
3824
                            1000,
3825
                        result: JSON.stringify(results.records),
3826
                    },
3827
                    savedQueryResultCacheOptions,
3828
                    queryRunner,
3829
                )
3830
            } catch (error) {
3831
                if (!cacheOptions.ignoreErrors) {
×
3832
                    throw error
×
3833
                }
3834
            }
3835
        }
3836

3837
        return results.records
282,848✔
3838
    }
3839

3840
    /**
3841
     * Merges into expression map given expression map properties.
3842
     */
3843
    protected mergeExpressionMap(
3844
        expressionMap: Partial<QueryExpressionMap>,
3845
    ): this {
3846
        ObjectUtils.assign(this.expressionMap, expressionMap)
10,281✔
3847
        return this
10,281✔
3848
    }
3849

3850
    /**
3851
     * Normalizes a give number - converts to int if possible.
3852
     */
3853
    protected normalizeNumber(num: any) {
3854
        if (typeof num === "number" || num === undefined || num === null)
56,014✔
3855
            return num
55,854✔
3856

3857
        return Number(num)
160✔
3858
    }
3859

3860
    /**
3861
     * Creates a query builder used to execute sql queries inside this query builder.
3862
     */
3863
    protected obtainQueryRunner() {
3864
        return (
283,532✔
3865
            this.queryRunner ||
350,719✔
3866
            this.connection.createQueryRunner(
3867
                this.connection.defaultReplicationModeForReads(),
3868
            )
3869
        )
3870
    }
3871

3872
    protected buildSelect(
3873
        select: FindOptionsSelect<any>,
3874
        metadata: EntityMetadata,
3875
        alias: string,
3876
        embedPrefix?: string,
3877
    ) {
3878
        for (const key in select) {
1,582✔
3879
            if (select[key] === undefined || select[key] === false) continue
2,856✔
3880

3881
            const propertyPath = embedPrefix ? embedPrefix + "." + key : key
2,736✔
3882
            const column =
3883
                metadata.findColumnWithPropertyPathStrict(propertyPath)
2,736✔
3884
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
2,736✔
3885
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
2,736✔
3886

3887
            if (!embed && !column && !relation)
2,736✔
3888
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
40✔
3889

3890
            if (column) {
2,696✔
3891
                this.selects.push(alias + "." + propertyPath)
2,068✔
3892
                // this.addSelect(alias + "." + propertyPath);
3893
            } else if (embed) {
628✔
3894
                this.buildSelect(
108✔
3895
                    select[key] as FindOptionsSelect<any>,
3896
                    metadata,
3897
                    alias,
3898
                    propertyPath,
3899
                )
3900

3901
                // } else if (relation) {
3902
                //     const joinAlias = alias + "_" + relation.propertyName;
3903
                //     const existJoin = this.joins.find(join => join.alias === joinAlias);
3904
                //     if (!existJoin) {
3905
                //         this.joins.push({
3906
                //             type: "left",
3907
                //             select: false,
3908
                //             alias: joinAlias,
3909
                //             parentAlias: alias,
3910
                //             relationMetadata: relation
3911
                //         });
3912
                //     }
3913
                //     this.buildOrder(select[key] as FindOptionsOrder<any>, relation.inverseEntityMetadata, joinAlias);
3914
            }
3915
        }
3916
    }
3917

3918
    protected buildRelations(
3919
        relations: FindOptionsRelations<any>,
3920
        selection: FindOptionsSelect<any> | undefined,
3921
        metadata: EntityMetadata,
3922
        alias: string,
3923
        embedPrefix?: string,
3924
    ) {
3925
        if (!relations) return
71,922!
3926

3927
        Object.keys(relations).forEach((relationName) => {
71,922✔
3928
            const relationValue = (relations as any)[relationName]
552,870✔
3929
            const propertyPath = embedPrefix
552,870✔
3930
                ? embedPrefix + "." + relationName
3931
                : relationName
3932
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
552,870✔
3933
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
552,870✔
3934
            if (!embed && !relation)
552,870✔
3935
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
240✔
3936

3937
            if (embed) {
552,630✔
3938
                this.buildRelations(
200✔
3939
                    relationValue,
3940
                    typeof selection === "object"
200!
3941
                        ? OrmUtils.deepValue(selection, embed.propertyPath)
3942
                        : undefined,
3943
                    metadata,
3944
                    alias,
3945
                    propertyPath,
3946
                )
3947
            } else if (relation) {
552,430✔
3948
                let joinAlias = alias + "_" + propertyPath.replace(".", "_")
552,430✔
3949
                joinAlias = DriverUtils.buildAlias(
552,430✔
3950
                    this.connection.driver,
3951
                    { joiner: "__" },
3952
                    alias,
3953
                    joinAlias,
3954
                )
3955
                if (
552,430✔
3956
                    relationValue === true ||
553,822✔
3957
                    typeof relationValue === "object"
3958
                ) {
3959
                    if (this.expressionMap.relationLoadStrategy === "query") {
552,430✔
3960
                        this.concatRelationMetadata(relation)
18✔
3961
                    } else {
3962
                        // join
3963
                        this.joins.push({
552,412✔
3964
                            type: "left",
3965
                            select: true,
3966
                            selection:
3967
                                selection &&
1,105,464✔
3968
                                typeof selection[relationName] === "object"
3969
                                    ? (selection[
3970
                                          relationName
3971
                                      ] as FindOptionsSelect<any>)
3972
                                    : undefined,
3973
                            alias: joinAlias,
3974
                            parentAlias: alias,
3975
                            relationMetadata: relation,
3976
                        })
3977

3978
                        if (
552,412✔
3979
                            selection &&
553,052✔
3980
                            typeof selection[relationName] === "object"
3981
                        ) {
3982
                            this.buildSelect(
440✔
3983
                                selection[
3984
                                    relationName
3985
                                ] as FindOptionsSelect<any>,
3986
                                relation.inverseEntityMetadata,
3987
                                joinAlias,
3988
                            )
3989
                        }
3990
                    }
3991
                }
3992

3993
                if (
552,430✔
3994
                    typeof relationValue === "object" &&
553,822✔
3995
                    this.expressionMap.relationLoadStrategy === "join"
3996
                ) {
3997
                    this.buildRelations(
1,392✔
3998
                        relationValue,
3999
                        typeof selection === "object"
1,392✔
4000
                            ? OrmUtils.deepValue(
4001
                                  selection,
4002
                                  relation.propertyPath,
4003
                              )
4004
                            : undefined,
4005
                        relation.inverseEntityMetadata,
4006
                        joinAlias,
4007
                        undefined,
4008
                    )
4009
                }
4010
            }
4011
        })
4012
    }
4013

4014
    protected buildEagerRelations(
4015
        relations: FindOptionsRelations<any>,
4016
        selection: FindOptionsSelect<any> | undefined,
4017
        metadata: EntityMetadata,
4018
        alias: string,
4019
        embedPrefix?: string,
4020
    ) {
4021
        if (!relations) return
71,526!
4022

4023
        Object.keys(relations).forEach((relationName) => {
71,526✔
4024
            const relationValue = (relations as any)[relationName]
552,412✔
4025
            const propertyPath = embedPrefix
552,412✔
4026
                ? embedPrefix + "." + relationName
4027
                : relationName
4028
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
552,412✔
4029
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
552,412✔
4030
            if (!embed && !relation)
552,412!
4031
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
×
4032

4033
            if (embed) {
552,412✔
4034
                this.buildEagerRelations(
160✔
4035
                    relationValue,
4036
                    typeof selection === "object"
160!
4037
                        ? OrmUtils.deepValue(selection, embed.propertyPath)
4038
                        : undefined,
4039
                    metadata,
4040
                    alias,
4041
                    propertyPath,
4042
                )
4043
            } else if (relation) {
552,252✔
4044
                let joinAlias = alias + "_" + propertyPath.replace(".", "_")
552,252✔
4045
                joinAlias = DriverUtils.buildAlias(
552,252✔
4046
                    this.connection.driver,
4047
                    { joiner: "__" },
4048
                    alias,
4049
                    joinAlias,
4050
                )
4051

4052
                if (
552,252✔
4053
                    relationValue === true ||
553,564✔
4054
                    typeof relationValue === "object"
4055
                ) {
4056
                    relation.inverseEntityMetadata.eagerRelations.forEach(
552,252✔
4057
                        (eagerRelation) => {
4058
                            let eagerRelationJoinAlias =
4059
                                joinAlias +
603✔
4060
                                "_" +
4061
                                eagerRelation.propertyPath.replace(".", "_")
4062
                            eagerRelationJoinAlias = DriverUtils.buildAlias(
603✔
4063
                                this.connection.driver,
4064
                                { joiner: "__" },
4065
                                joinAlias,
4066
                                eagerRelationJoinAlias,
4067
                            )
4068

4069
                            const existJoin = this.joins.find(
603✔
4070
                                (join) => join.alias === eagerRelationJoinAlias,
3,003✔
4071
                            )
4072
                            if (!existJoin) {
603✔
4073
                                this.joins.push({
403✔
4074
                                    type: "left",
4075
                                    select: true,
4076
                                    alias: eagerRelationJoinAlias,
4077
                                    parentAlias: joinAlias,
4078
                                    selection: undefined,
4079
                                    relationMetadata: eagerRelation,
4080
                                })
4081
                            }
4082

4083
                            if (
603!
4084
                                selection &&
603!
4085
                                typeof selection[relationName] === "object"
4086
                            ) {
4087
                                this.buildSelect(
×
4088
                                    selection[
4089
                                        relationName
4090
                                    ] as FindOptionsSelect<any>,
4091
                                    relation.inverseEntityMetadata,
4092
                                    joinAlias,
4093
                                )
4094
                            }
4095
                        },
4096
                    )
4097
                }
4098

4099
                if (typeof relationValue === "object") {
552,252✔
4100
                    this.buildEagerRelations(
1,312✔
4101
                        relationValue,
4102
                        typeof selection === "object"
1,312✔
4103
                            ? OrmUtils.deepValue(
4104
                                  selection,
4105
                                  relation.propertyPath,
4106
                              )
4107
                            : undefined,
4108
                        relation.inverseEntityMetadata,
4109
                        joinAlias,
4110
                        undefined,
4111
                    )
4112
                }
4113
            }
4114
        })
4115
    }
4116

4117
    protected buildOrder(
4118
        order: FindOptionsOrder<any>,
4119
        metadata: EntityMetadata,
4120
        alias: string,
4121
        embedPrefix?: string,
4122
    ) {
4123
        for (const key in order) {
4,598✔
4124
            if (order[key] === undefined) continue
5,118!
4125

4126
            const propertyPath = embedPrefix ? embedPrefix + "." + key : key
5,118✔
4127
            const column =
4128
                metadata.findColumnWithPropertyPathStrict(propertyPath)
5,118✔
4129
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
5,118✔
4130
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
5,118✔
4131

4132
            if (!embed && !column && !relation)
5,118✔
4133
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
40✔
4134

4135
            if (column) {
5,078✔
4136
                let direction =
4137
                    typeof order[key] === "object"
4,158✔
4138
                        ? (order[key] as any).direction
4139
                        : order[key]
4140
                direction =
4,158✔
4141
                    direction === "DESC" ||
15,758✔
4142
                    direction === "desc" ||
4143
                    direction === -1
4144
                        ? "DESC"
4145
                        : "ASC"
4146
                let nulls =
4147
                    typeof order[key] === "object"
4,158✔
4148
                        ? (order[key] as any).nulls
4149
                        : undefined
4150
                nulls =
4,158✔
4151
                    nulls?.toLowerCase() === "first"
4,158✔
4152
                        ? "NULLS FIRST"
4153
                        : nulls?.toLowerCase() === "last"
4,130✔
4154
                        ? "NULLS LAST"
4155
                        : undefined
4156

4157
                const aliasPath = `${alias}.${propertyPath}`
4,158✔
4158
                // const selection = this.expressionMap.selects.find(
4159
                //     (s) => s.selection === aliasPath,
4160
                // )
4161
                // if (selection) {
4162
                //     // this is not building correctly now???
4163
                //     aliasPath = this.escape(
4164
                //         DriverUtils.buildAlias(
4165
                //             this.connection.driver,
4166
                //             undefined,
4167
                //             alias,
4168
                //             column.databaseName,
4169
                //         ),
4170
                //     )
4171
                //     // selection.aliasName = aliasPath
4172
                // } else {
4173
                //     if (column.isVirtualProperty && column.query) {
4174
                //         aliasPath = `(${column.query(alias)})`
4175
                //     }
4176
                // }
4177

4178
                // console.log("add sort", selection, aliasPath, direction, nulls)
4179
                this.addOrderBy(aliasPath, direction, nulls)
4,158✔
4180
                // this.orderBys.push({ alias: alias + "." + propertyPath, direction, nulls });
4181
            } else if (embed) {
920✔
4182
                this.buildOrder(
120✔
4183
                    order[key] as FindOptionsOrder<any>,
4184
                    metadata,
4185
                    alias,
4186
                    propertyPath,
4187
                )
4188
            } else if (relation) {
800✔
4189
                let joinAlias = alias + "_" + propertyPath.replace(".", "_")
800✔
4190
                joinAlias = DriverUtils.buildAlias(
800✔
4191
                    this.connection.driver,
4192
                    { joiner: "__" },
4193
                    alias,
4194
                    joinAlias,
4195
                )
4196
                // console.log("joinAlias", joinAlias, joinAlias.length, this.connection.driver.maxAliasLength)
4197
                // todo: use expressionMap.joinAttributes, and create a new one using
4198
                //  const joinAttribute = new JoinAttribute(this.connection, this.expressionMap);
4199

4200
                const existJoin = this.joins.find(
800✔
4201
                    (join) => join.alias === joinAlias,
960✔
4202
                )
4203
                if (!existJoin) {
800✔
4204
                    this.joins.push({
280✔
4205
                        type: "left",
4206
                        select: false,
4207
                        alias: joinAlias,
4208
                        parentAlias: alias,
4209
                        selection: undefined,
4210
                        relationMetadata: relation,
4211
                    })
4212
                }
4213
                this.buildOrder(
800✔
4214
                    order[key] as FindOptionsOrder<any>,
4215
                    relation.inverseEntityMetadata,
4216
                    joinAlias,
4217
                )
4218
            }
4219
        }
4220
    }
4221

4222
    protected buildWhere(
4223
        where: FindOptionsWhere<any>[] | FindOptionsWhere<any>,
4224
        metadata: EntityMetadata,
4225
        alias: string,
4226
        embedPrefix?: string,
4227
    ) {
4228
        let condition: string = ""
90,189✔
4229
        // let parameterIndex = Object.keys(this.expressionMap.nativeParameters).length;
4230
        if (Array.isArray(where)) {
90,189✔
4231
            if (where.length) {
286✔
4232
                condition = where
280✔
4233
                    .map((whereItem) => {
4234
                        return this.buildWhere(
560✔
4235
                            whereItem,
4236
                            metadata,
4237
                            alias,
4238
                            embedPrefix,
4239
                        )
4240
                    })
4241
                    .filter((condition) => !!condition)
560✔
4242
                    .map((condition) => "(" + condition + ")")
560✔
4243
                    .join(" OR ")
4244
            }
4245
        } else {
4246
            const andConditions: string[] = []
89,903✔
4247
            for (const key in where) {
89,903✔
4248
                if (where[key] === undefined || where[key] === null) continue
91,450✔
4249

4250
                const propertyPath = embedPrefix ? embedPrefix + "." + key : key
91,327✔
4251
                const column =
4252
                    metadata.findColumnWithPropertyPathStrict(propertyPath)
91,327✔
4253
                const embed =
4254
                    metadata.findEmbeddedWithPropertyPath(propertyPath)
91,327✔
4255
                const relation =
4256
                    metadata.findRelationWithPropertyPath(propertyPath)
91,327✔
4257

4258
                if (!embed && !column && !relation)
91,327✔
4259
                    throw new EntityPropertyNotFoundError(
80✔
4260
                        propertyPath,
4261
                        metadata,
4262
                    )
4263

4264
                if (column) {
91,247✔
4265
                    let aliasPath = `${alias}.${propertyPath}`
88,446✔
4266
                    if (column.isVirtualProperty && column.query) {
88,446✔
4267
                        aliasPath = `(${column.query(this.escape(alias))})`
160✔
4268
                    }
4269
                    // const parameterName = alias + "_" + propertyPath.split(".").join("_") + "_" + parameterIndex;
4270

4271
                    // todo: we need to handle other operators as well?
4272
                    let parameterValue = where[key]
88,446✔
4273
                    if (InstanceChecker.isEqualOperator(where[key])) {
88,446✔
4274
                        parameterValue = where[key].value
74✔
4275
                    }
4276

4277
                    if (column.transformer) {
88,446✔
4278
                        if (parameterValue instanceof FindOperator) {
175✔
4279
                            parameterValue.transformValue(column.transformer)
64✔
4280
                        } else {
4281
                            parameterValue = ApplyValueTransformers.transformTo(
111✔
4282
                                column.transformer,
4283
                                parameterValue,
4284
                            )
4285
                        }
4286
                    }
4287

4288
                    // MSSQL requires parameters to carry extra type information
4289
                    if (this.connection.driver.options.type === "mssql") {
88,446✔
4290
                        parameterValue = (
6,120✔
4291
                            this.connection.driver as SqlServerDriver
4292
                        ).parametrizeValues(column, parameterValue)
4293
                    }
4294

4295
                    // if (parameterValue === null) {
4296
                    //     andConditions.push(`${aliasPath} IS NULL`);
4297
                    //
4298
                    // } else if (parameterValue instanceof FindOperator) {
4299
                    //     // let parameters: any[] = [];
4300
                    //     // if (parameterValue.useParameter) {
4301
                    //     //     const realParameterValues: any[] = parameterValue.multipleParameters ? parameterValue.value : [parameterValue.value];
4302
                    //     //     realParameterValues.forEach((realParameterValue, realParameterValueIndex) => {
4303
                    //     //
4304
                    //     //         // don't create parameters for number to prevent max number of variables issues as much as possible
4305
                    //     //         if (typeof realParameterValue === "number") {
4306
                    //     //             parameters.push(realParameterValue);
4307
                    //     //
4308
                    //     //         } else {
4309
                    //     //             this.expressionMap.nativeParameters[parameterName + realParameterValueIndex] = realParameterValue;
4310
                    //     //             parameterIndex++;
4311
                    //     //             parameters.push(this.connection.driver.createParameter(parameterName + realParameterValueIndex, parameterIndex - 1));
4312
                    //     //         }
4313
                    //     //     });
4314
                    //     // }
4315
                    //     andConditions.push(
4316
                    //         this.createWhereConditionExpression(this.getWherePredicateCondition(aliasPath, parameterValue))
4317
                    //         // parameterValue.toSql(this.connection, aliasPath, parameters));
4318
                    //     )
4319
                    //
4320
                    // } else {
4321
                    //     this.expressionMap.nativeParameters[parameterName] = parameterValue;
4322
                    //     parameterIndex++;
4323
                    //     const parameter = this.connection.driver.createParameter(parameterName, parameterIndex - 1);
4324
                    //     andConditions.push(`${aliasPath} = ${parameter}`);
4325
                    // }
4326

4327
                    andConditions.push(
88,446✔
4328
                        this.createWhereConditionExpression(
4329
                            this.getWherePredicateCondition(
4330
                                aliasPath,
4331
                                parameterValue,
4332
                            ),
4333
                        ),
4334
                        // parameterValue.toSql(this.connection, aliasPath, parameters));
4335
                    )
4336

4337
                    // this.conditions.push(`${alias}.${propertyPath} = :${paramName}`);
4338
                    // this.expressionMap.parameters[paramName] = where[key]; // todo: handle functions and other edge cases
4339
                } else if (embed) {
2,801✔
4340
                    const condition = this.buildWhere(
800✔
4341
                        where[key],
4342
                        metadata,
4343
                        alias,
4344
                        propertyPath,
4345
                    )
4346
                    if (condition) andConditions.push(condition)
800✔
4347
                } else if (relation) {
2,001✔
4348
                    // if all properties of where are undefined we don't need to join anything
4349
                    // this can happen when user defines map with conditional queries inside
4350
                    if (typeof where[key] === "object") {
2,001✔
4351
                        const allAllUndefined = Object.keys(where[key]).every(
1,961✔
4352
                            (k) => where[key][k] === undefined,
2,001✔
4353
                        )
4354
                        if (allAllUndefined) {
1,961✔
4355
                            continue
40✔
4356
                        }
4357
                    }
4358

4359
                    if (InstanceChecker.isFindOperator(where[key])) {
1,961✔
4360
                        if (
880✔
4361
                            where[key].type === "moreThan" ||
2,480✔
4362
                            where[key].type === "lessThan" ||
4363
                            where[key].type === "moreThanOrEqual" ||
4364
                            where[key].type === "lessThanOrEqual"
4365
                        ) {
4366
                            let sqlOperator = ""
560✔
4367
                            if (where[key].type === "moreThan") {
560✔
4368
                                sqlOperator = ">"
240✔
4369
                            } else if (where[key].type === "lessThan") {
320✔
4370
                                sqlOperator = "<"
40✔
4371
                            } else if (where[key].type === "moreThanOrEqual") {
280✔
4372
                                sqlOperator = ">="
240✔
4373
                            } else if (where[key].type === "lessThanOrEqual") {
40✔
4374
                                sqlOperator = "<="
40✔
4375
                            }
4376
                            // basically relation count functionality
4377
                            const qb: QueryBuilder<any> = this.subQuery()
560✔
4378
                            if (relation.isManyToManyOwner) {
560✔
4379
                                qb.select("COUNT(*)")
240✔
4380
                                    .from(
4381
                                        relation.joinTableName,
4382
                                        relation.joinTableName,
4383
                                    )
4384
                                    .where(
4385
                                        relation.joinColumns
4386
                                            .map((column) => {
4387
                                                return `${
240✔
4388
                                                    relation.joinTableName
4389
                                                }.${
4390
                                                    column.propertyName
4391
                                                } = ${alias}.${
4392
                                                    column.referencedColumn!
4393
                                                        .propertyName
4394
                                                }`
4395
                                            })
4396
                                            .join(" AND "),
4397
                                    )
4398
                            } else if (relation.isManyToManyNotOwner) {
320✔
4399
                                qb.select("COUNT(*)")
160✔
4400
                                    .from(
4401
                                        relation.inverseRelation!.joinTableName,
4402
                                        relation.inverseRelation!.joinTableName,
4403
                                    )
4404
                                    .where(
4405
                                        relation
4406
                                            .inverseRelation!.inverseJoinColumns.map(
4407
                                                (column) => {
4408
                                                    return `${
160✔
4409
                                                        relation.inverseRelation!
4410
                                                            .joinTableName
4411
                                                    }.${
4412
                                                        column.propertyName
4413
                                                    } = ${alias}.${
4414
                                                        column.referencedColumn!
4415
                                                            .propertyName
4416
                                                    }`
4417
                                                },
4418
                                            )
4419
                                            .join(" AND "),
4420
                                    )
4421
                            } else if (relation.isOneToMany) {
160!
4422
                                qb.select("COUNT(*)")
160✔
4423
                                    .from(
4424
                                        relation.inverseEntityMetadata.target,
4425
                                        relation.inverseEntityMetadata
4426
                                            .tableName,
4427
                                    )
4428
                                    .where(
4429
                                        relation
4430
                                            .inverseRelation!.joinColumns.map(
4431
                                                (column) => {
4432
                                                    return `${
160✔
4433
                                                        relation
4434
                                                            .inverseEntityMetadata
4435
                                                            .tableName
4436
                                                    }.${
4437
                                                        column.propertyName
4438
                                                    } = ${alias}.${
4439
                                                        column.referencedColumn!
4440
                                                            .propertyName
4441
                                                    }`
4442
                                                },
4443
                                            )
4444
                                            .join(" AND "),
4445
                                    )
4446
                            } else {
4447
                                throw new Error(
×
4448
                                    `This relation isn't supported by given find operator`,
4449
                                )
4450
                            }
4451
                            // this
4452
                            //     .addSelect(qb.getSql(), relation.propertyAliasName + "_cnt")
4453
                            //     .andWhere(this.escape(relation.propertyAliasName + "_cnt") + " " + sqlOperator + " " + parseInt(where[key].value));
4454
                            this.andWhere(
560✔
4455
                                qb.getSql() +
4456
                                    " " +
4457
                                    sqlOperator +
4458
                                    " " +
4459
                                    parseInt(where[key].value),
4460
                            )
4461
                        } else {
4462
                            if (
320!
4463
                                relation.isManyToOne ||
640✔
4464
                                (relation.isOneToOne &&
4465
                                    relation.isOneToOneOwner)
4466
                            ) {
4467
                                const aliasPath = `${alias}.${propertyPath}`
320✔
4468

4469
                                andConditions.push(
320✔
4470
                                    this.createWhereConditionExpression(
4471
                                        this.getWherePredicateCondition(
4472
                                            aliasPath,
4473
                                            where[key],
4474
                                        ),
4475
                                    ),
4476
                                )
4477
                            } else {
4478
                                throw new Error(
×
4479
                                    `This relation isn't supported by given find operator`,
4480
                                )
4481
                            }
4482
                        }
4483
                    } else {
4484
                        // const joinAlias = alias + "_" + relation.propertyName;
4485
                        let joinAlias =
4486
                            alias +
1,081✔
4487
                            "_" +
4488
                            relation.propertyPath.replace(".", "_")
4489
                        joinAlias = DriverUtils.buildAlias(
1,081✔
4490
                            this.connection.driver,
4491
                            { joiner: "__" },
4492
                            alias,
4493
                            joinAlias,
4494
                        )
4495

4496
                        const existJoin = this.joins.find(
1,081✔
4497
                            (join) => join.alias === joinAlias,
606✔
4498
                        )
4499
                        if (!existJoin) {
1,081✔
4500
                            this.joins.push({
801✔
4501
                                type: "left",
4502
                                select: false,
4503
                                selection: undefined,
4504
                                alias: joinAlias,
4505
                                parentAlias: alias,
4506
                                relationMetadata: relation,
4507
                            })
4508
                        }
4509

4510
                        const condition = this.buildWhere(
1,081✔
4511
                            where[key],
4512
                            relation.inverseEntityMetadata,
4513
                            joinAlias,
4514
                        )
4515
                        if (condition) {
1,081✔
4516
                            andConditions.push(condition)
961✔
4517
                            // parameterIndex = Object.keys(this.expressionMap.nativeParameters).length;
4518
                        }
4519
                    }
4520
                }
4521
            }
4522
            condition = andConditions.length
89,823✔
4523
                ? "(" + andConditions.join(") AND (") + ")"
4524
                : andConditions.join(" AND ")
4525
        }
4526
        return condition.length ? "(" + condition + ")" : condition
90,109✔
4527
    }
4528
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc