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

typeorm / typeorm / 12968994847

25 Jan 2025 10:33PM UTC coverage: 71.671% (-0.7%) from 72.369%
12968994847

Pull #11262

github

web-flow
Merge f05a3d4e8 into 79960e136
Pull Request #11262: ci(PR): adjust timeouts and add concurrency

8567 of 12650 branches covered (67.72%)

Branch coverage included in aggregate %.

17727 of 24037 relevant lines covered (73.75%)

135711.91 hits per line

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

89.74
/src/query-builder/SelectQueryBuilder.ts
1
import { RawSqlResultsToEntityTransformer } from "./transformer/RawSqlResultsToEntityTransformer"
29✔
2
import { ObjectLiteral } from "../common/ObjectLiteral"
3
import { PessimisticLockTransactionRequiredError } from "../error/PessimisticLockTransactionRequiredError"
29✔
4
import { NoVersionOrUpdateDateColumnError } from "../error/NoVersionOrUpdateDateColumnError"
29✔
5
import { OptimisticLockVersionMismatchError } from "../error/OptimisticLockVersionMismatchError"
29✔
6
import { OptimisticLockCanNotBeUsedError } from "../error/OptimisticLockCanNotBeUsedError"
29✔
7
import { JoinAttribute } from "./JoinAttribute"
29✔
8
import { RelationIdAttribute } from "./relation-id/RelationIdAttribute"
29✔
9
import { RelationCountAttribute } from "./relation-count/RelationCountAttribute"
29✔
10
import { RelationIdLoader } from "./relation-id/RelationIdLoader"
29✔
11
import { RelationIdLoader as QueryStrategyRelationIdLoader } from "./RelationIdLoader"
29✔
12
import { RelationIdMetadataToAttributeTransformer } from "./relation-id/RelationIdMetadataToAttributeTransformer"
29✔
13
import { RelationCountLoader } from "./relation-count/RelationCountLoader"
29✔
14
import { RelationCountMetadataToAttributeTransformer } from "./relation-count/RelationCountMetadataToAttributeTransformer"
29✔
15
import { QueryBuilder } from "./QueryBuilder"
29✔
16
import { ReadStream } from "../platform/PlatformTools"
17
import { LockNotSupportedOnGivenDriverError } from "../error/LockNotSupportedOnGivenDriverError"
29✔
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"
29✔
30
import { SelectQueryBuilderOption } from "./SelectQueryBuilderOption"
31
import { ObjectUtils } from "../util/ObjectUtils"
29✔
32
import { DriverUtils } from "../driver/DriverUtils"
29✔
33
import { EntityNotFoundError } from "../error/EntityNotFoundError"
29✔
34
import { TypeORMError } from "../error"
29✔
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"
29✔
41
import { FindOptionsRelations } from "../find-options/FindOptionsRelations"
42
import { OrmUtils } from "../util/OrmUtils"
29✔
43
import { EntityPropertyNotFoundError } from "../error/EntityPropertyNotFoundError"
29✔
44
import { AuroraMysqlDriver } from "../driver/aurora-mysql/AuroraMysqlDriver"
45
import { InstanceChecker } from "../util/InstanceChecker"
29✔
46
import { FindOperator } from "../find-options/FindOperator"
29✔
47
import { ApplyValueTransformers } from "../util/ApplyValueTransformers"
29✔
48

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

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

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

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

99
    // -------------------------------------------------------------------------
100
    // Public Methods
101
    // -------------------------------------------------------------------------
102

103
    setFindOptions(findOptions: FindManyOptions<Entity>) {
104
        this.findOptions = findOptions
176,399✔
105
        this.applyFindOptions()
176,399✔
106
        return this
176,045✔
107
    }
108

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

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

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

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

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

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

175
        return this
525,009✔
176
    }
177

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

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

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

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

208
        if (Array.isArray(selection)) {
2,354,801✔
209
            this.expressionMap.selects = this.expressionMap.selects.concat(
5,461✔
210
                selection.map((selection) => ({ selection: selection })),
7,674✔
211
            )
212
        } else if (typeof selection === "function") {
2,349,340✔
213
            const subQueryBuilder = selection(this.subQuery())
29✔
214
            this.setParameters(subQueryBuilder.getParameters())
29✔
215
            this.expressionMap.selects.push({
29✔
216
                selection: subQueryBuilder.getQuery(),
217
                aliasName: selectionAliasName,
218
            })
219
        } else if (selection) {
2,349,311✔
220
            this.expressionMap.selects.push({
2,349,311✔
221
                selection: selection,
222
                aliasName: selectionAliasName,
223
            })
224
        }
225

226
        return this
2,354,801✔
227
    }
228

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

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

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

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

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

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

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

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

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

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

330
        return this as any as SelectQueryBuilder<T>
62✔
331
    }
332

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1062
        relationIdAttribute.queryBuilderFactory = queryBuilderFactory
34,109✔
1063
        this.expressionMap.relationIdAttributes.push(relationIdAttribute)
34,109✔
1064

1065
        if (relationIdAttribute.relation.junctionEntityMetadata) {
34,109✔
1066
            this.expressionMap.createAlias({
17,186✔
1067
                type: "other",
1068
                name: relationIdAttribute.junctionAlias,
1069
                metadata: relationIdAttribute.relation.junctionEntityMetadata,
1070
            })
1071
        }
1072
        return this
34,109✔
1073
    }
1074

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

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

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

1129
            this.loadRelationIdAndMap(
30,484✔
1130
                this.expressionMap.mainAlias!.name +
1131
                    "." +
1132
                    relation.propertyPath,
1133
                this.expressionMap.mainAlias!.name +
1134
                    "." +
1135
                    relation.propertyPath,
1136
                options,
1137
            )
1138
        })
1139
        return this
121,646✔
1140
    }
1141

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1347
        return this
15,059✔
1348
    }
1349

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

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

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

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

1400
        if (sort) {
24,016✔
1401
            if (typeof sort === "object") {
14,876✔
1402
                this.expressionMap.orderBys = sort as OrderByCondition
7,557✔
1403
            } else {
1404
                if (nulls) {
7,319!
1405
                    this.expressionMap.orderBys = {
×
1406
                        [sort as string]: { order, nulls },
1407
                    }
1408
                } else {
1409
                    this.expressionMap.orderBys = { [sort as string]: order }
7,319✔
1410
                }
1411
            }
1412
        } else {
1413
            this.expressionMap.orderBys = {}
9,140✔
1414
        }
1415
        return this
24,016✔
1416
    }
1417

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

1439
        if (nulls) {
18,123✔
1440
            this.expressionMap.orderBys[sort] = { order, nulls }
33✔
1441
        } else {
1442
            this.expressionMap.orderBys[sort] = order
18,090✔
1443
        }
1444
        return this
18,123✔
1445
    }
1446

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

1463
        return this
9,581✔
1464
    }
1465

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

1482
        return this
9,320✔
1483
    }
1484

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

1498
        return this
18,389✔
1499
    }
1500

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

1514
        return this
2,396✔
1515
    }
1516

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

1525
        return this
×
1526
    }
1527

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

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

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

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

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

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

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

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

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

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

1626
            const results = await this.loadRawResults(queryRunner)
28,363✔
1627

1628
            // close transaction if we started it
1629
            if (transactionStartedByUs) {
28,351!
1630
                await queryRunner.commitTransaction()
×
1631
            }
1632

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

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

1669
            this.expressionMap.queryEntity = true
172,823✔
1670
            const results = await this.executeEntitiesAndRawResults(queryRunner)
172,823✔
1671

1672
            // close transaction if we started it
1673
            if (transactionStartedByUs) {
172,575✔
1674
                await queryRunner.commitTransaction()
29✔
1675
            }
1676

1677
            return results
172,575✔
1678
        } catch (error) {
1679
            // rollback transaction if we started it
1680
            if (transactionStartedByUs) {
248!
1681
                try {
×
1682
                    await queryRunner.rollbackTransaction()
×
1683
                } catch (rollbackError) {}
1684
            }
1685
            throw error
248✔
1686
        } finally {
1687
            if (queryRunner !== this.queryRunner)
172,823✔
1688
                // means we created our own query runner
1689
                await queryRunner.release()
45,165✔
1690
        }
1691
    }
1692

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

1700
        if (
23,268✔
1701
            result &&
45,280✔
1702
            this.expressionMap.lockMode === "optimistic" &&
1703
            this.expressionMap.lockVersion
1704
        ) {
1705
            const metadata = this.expressionMap.mainAlias!.metadata
138✔
1706

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

1731
        if (result === undefined) {
23,219✔
1732
            return null
1,394✔
1733
        }
1734
        return result
21,825✔
1735
    }
1736

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

1743
        if (!entity) {
18!
1744
            throw new EntityNotFoundError(
×
1745
                this.expressionMap.mainAlias!.target,
1746
                this.expressionMap.parameters,
1747
            )
1748
        }
1749

1750
        return entity
18✔
1751
    }
1752

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

1760
        const results = await this.getRawAndEntities()
146,559✔
1761
        return results.entities
146,556✔
1762
    }
1763

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

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

1784
            this.expressionMap.queryEntity = false
838✔
1785
            const results = await this.executeCountQuery(queryRunner)
838✔
1786

1787
            // close transaction if we started it
1788
            if (transactionStartedByUs) {
838!
1789
                await queryRunner.commitTransaction()
×
1790
            }
1791

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1969
    /**
1970
     * Enables or disables query result caching.
1971
     */
1972
    cache(
1973
        enabledOrMillisecondsOrId: boolean | number | string,
1974
        maybeMilliseconds?: number,
1975
    ): this {
1976
        if (typeof enabledOrMillisecondsOrId === "boolean") {
9,634✔
1977
            this.expressionMap.cache = enabledOrMillisecondsOrId
1,619✔
1978
        } else if (typeof enabledOrMillisecondsOrId === "number") {
8,015✔
1979
            this.expressionMap.cache = true
116✔
1980
            this.expressionMap.cacheDuration = enabledOrMillisecondsOrId
116✔
1981
        } else if (
7,899✔
1982
            typeof enabledOrMillisecondsOrId === "string" ||
15,334✔
1983
            typeof enabledOrMillisecondsOrId === "number"
1984
        ) {
1985
            this.expressionMap.cache = true
464✔
1986
            this.expressionMap.cacheId = enabledOrMillisecondsOrId
464✔
1987
        }
1988

1989
        if (maybeMilliseconds) {
9,634✔
1990
            this.expressionMap.cacheDuration = maybeMilliseconds
464✔
1991
        }
1992

1993
        return this
9,634✔
1994
    }
1995

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

2004
    // -------------------------------------------------------------------------
2005
    // Protected Methods
2006
    // -------------------------------------------------------------------------
2007

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

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

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

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

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

2103
        const allSelects: SelectQuery[] = []
537,387✔
2104
        const excludedSelects: SelectQuery[] = []
537,387✔
2105

2106
        if (this.expressionMap.mainAlias.hasMetadata) {
537,387✔
2107
            const metadata = this.expressionMap.mainAlias.metadata
526,306✔
2108
            allSelects.push(
526,306✔
2109
                ...this.buildEscapedEntityColumnSelects(
2110
                    this.expressionMap.mainAlias.name,
2111
                    metadata,
2112
                ),
2113
            )
2114
            excludedSelects.push(
526,306✔
2115
                ...this.findEntityColumnSelects(
2116
                    this.expressionMap.mainAlias.name,
2117
                    metadata,
2118
                ),
2119
            )
2120
        }
2121

2122
        // add selects from joins
2123
        this.expressionMap.joinAttributes.forEach((join) => {
537,387✔
2124
            if (join.metadata) {
2,343,904✔
2125
                allSelects.push(
2,343,808✔
2126
                    ...this.buildEscapedEntityColumnSelects(
2127
                        join.alias.name!,
2128
                        join.metadata,
2129
                    ),
2130
                )
2131
                excludedSelects.push(
2,343,808✔
2132
                    ...this.findEntityColumnSelects(
2133
                        join.alias.name!,
2134
                        join.metadata,
2135
                    ),
2136
                )
2137
            } else {
2138
                const hasMainAlias = this.expressionMap.selects.some(
96✔
2139
                    (select) => select.selection === join.alias.name,
108✔
2140
                )
2141
                if (hasMainAlias) {
96✔
2142
                    allSelects.push({
12✔
2143
                        selection: this.escape(join.alias.name!) + ".*",
2144
                    })
2145
                    const excludedSelect = this.expressionMap.selects.find(
12✔
2146
                        (select) => select.selection === join.alias.name,
24✔
2147
                    )
2148
                    excludedSelects.push(excludedSelect!)
12✔
2149
                }
2150
            }
2151
        })
2152

2153
        // add all other selects
2154
        this.expressionMap.selects
537,387✔
2155
            .filter((select) => excludedSelects.indexOf(select) === -1)
2,892,396✔
2156
            .forEach((select) =>
2157
                allSelects.push({
15,225✔
2158
                    selection: this.replacePropertyNames(select.selection),
2159
                    aliasName: select.aliasName,
2160
                }),
2161
            )
2162

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

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

2174
        // create a selection query
2175
        const froms = this.expressionMap.aliases
537,387✔
2176
            .filter(
2177
                (alias) =>
2178
                    alias.type === "from" &&
4,940,653✔
2179
                    (alias.tablePath || alias.subQuery),
2180
            )
2181
            .map((alias) => {
2182
                if (alias.subQuery)
537,540✔
2183
                    return alias.subQuery + " " + this.escape(alias.name)
8,067✔
2184

2185
                return (
529,473✔
2186
                    this.getTableName(alias.tablePath!) +
2187
                    " " +
2188
                    this.escape(alias.name)
2189
                )
2190
            })
2191

2192
        const select = this.createSelectDistinctExpression()
537,387✔
2193
        const selection = allSelects
537,387✔
2194
            .map(
2195
                (select) =>
2196
                    select.selection +
8,450,291✔
2197
                    (select.aliasName
8,450,291✔
2198
                        ? " AS " + this.escape(select.aliasName)
2199
                        : ""),
2200
            )
2201
            .join(", ")
2202

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

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

2221
        let select = "SELECT "
537,387✔
2222

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

2229
        if (
537,387✔
2230
            DriverUtils.isPostgresFamily(driver) &&
672,756✔
2231
            selectDistinctOn.length > 0
2232
        ) {
2233
            const selectDistinctOnMap = selectDistinctOn
9✔
2234
                .map((on) => this.replacePropertyNames(on))
15✔
2235
                .join(", ")
2236

2237
            select = `SELECT DISTINCT ON (${selectDistinctOnMap}) `
9✔
2238
        } else if (selectDistinct) {
537,378✔
2239
            select = "SELECT DISTINCT "
58✔
2240
        }
2241

2242
        return select
537,387✔
2243
    }
2244

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

2257
        const joins = this.expressionMap.joinAttributes.map((joinAttr) => {
537,387✔
2258
            const relation = joinAttr.relation
2,343,904✔
2259
            const destinationTableName = joinAttr.tablePath
2,343,904✔
2260
            const destinationTableAlias = joinAttr.alias.name
2,343,904✔
2261
            let appendedCondition = joinAttr.condition
2,343,904✔
2262
                ? " AND (" + joinAttr.condition + ")"
2263
                : ""
2264
            const parentAlias = joinAttr.parentAlias
2,343,904✔
2265

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

2286
            // if real entity relation is involved
2287
            if (relation.isManyToOne || relation.isOneToOneOwner) {
2,330,626✔
2288
                // JOIN `category` `category` ON `category`.`id` = `post`.`categoryId`
2289
                const condition = relation.joinColumns
15,485✔
2290
                    .map((joinColumn) => {
2291
                        return (
16,593✔
2292
                            destinationTableAlias +
2293
                            "." +
2294
                            joinColumn.referencedColumn!.propertyPath +
2295
                            "=" +
2296
                            parentAlias +
2297
                            "." +
2298
                            relation.propertyPath +
2299
                            "." +
2300
                            joinColumn.referencedColumn!.propertyPath
2301
                        )
2302
                    })
2303
                    .join(" AND ")
2304

2305
                return (
15,485✔
2306
                    " " +
2307
                    joinAttr.direction +
2308
                    " JOIN " +
2309
                    this.getTableName(destinationTableName) +
2310
                    " " +
2311
                    this.escape(destinationTableAlias) +
2312
                    this.createTableLockExpression() +
2313
                    " ON " +
2314
                    this.replacePropertyNames(condition + appendedCondition)
2315
                )
2316
            } else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
2,315,141✔
2317
                // JOIN `post` `post` ON `post`.`categoryId` = `category`.`id`
2318
                const condition = relation
275,960✔
2319
                    .inverseRelation!.joinColumns.map((joinColumn) => {
2320
                        if (
276,946✔
2321
                            relation.inverseEntityMetadata.tableType ===
277,062✔
2322
                                "entity-child" &&
2323
                            relation.inverseEntityMetadata.discriminatorColumn
2324
                        ) {
2325
                            appendedCondition +=
116✔
2326
                                " AND " +
2327
                                destinationTableAlias +
2328
                                "." +
2329
                                relation.inverseEntityMetadata
2330
                                    .discriminatorColumn.databaseName +
2331
                                "='" +
2332
                                relation.inverseEntityMetadata
2333
                                    .discriminatorValue +
2334
                                "'"
2335
                        }
2336

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

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

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

2372
                const junctionAlias = joinAttr.junctionAlias
2,039,181✔
2373
                let junctionCondition = "",
2,039,181✔
2374
                    destinationCondition = ""
2,039,181✔
2375

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

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

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

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

2465
        return joins.join(" ")
537,387✔
2466
    }
2467

2468
    /**
2469
     * Creates "GROUP BY" part of SQL query.
2470
     */
2471
    protected createGroupByExpression() {
2472
        if (!this.expressionMap.groupBys || !this.expressionMap.groupBys.length)
537,387✔
2473
            return ""
534,056✔
2474
        return (
3,331✔
2475
            " GROUP BY " +
2476
            this.replacePropertyNames(this.expressionMap.groupBys.join(", "))
2477
        )
2478
    }
2479

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

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

2530
                    return (
33,027✔
2531
                        this.replacePropertyNames(columnName) + " " + orderValue
2532
                    )
2533
                })
2534
                .join(", ")
2535
        )
2536
    }
2537

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

2555
        if (this.connection.driver.options.type === "mssql") {
537,387✔
2556
            // Due to a limitation in SQL Server's parser implementation it does not support using
2557
            // OFFSET or FETCH NEXT without an ORDER BY clause being provided. In cases where the
2558
            // user does not request one we insert a dummy ORDER BY that does nothing and should
2559
            // have no effect on the query planner or on the order of the results returned.
2560
            // https://dba.stackexchange.com/a/193799
2561
            let prefix = ""
156,501✔
2562
            if (
156,501✔
2563
                (limit || offset) &&
313,011✔
2564
                Object.keys(this.expressionMap.allOrderBys).length <= 0
2565
            ) {
2566
                prefix = " ORDER BY (SELECT NULL)"
2,475✔
2567
            }
2568

2569
            if (limit && offset)
156,501✔
2570
                return (
189✔
2571
                    prefix +
2572
                    " OFFSET " +
2573
                    offset +
2574
                    " ROWS FETCH NEXT " +
2575
                    limit +
2576
                    " ROWS ONLY"
2577
                )
2578
            if (limit)
156,312✔
2579
                return (
4,851✔
2580
                    prefix + " OFFSET 0 ROWS FETCH NEXT " + limit + " ROWS ONLY"
2581
                )
2582
            if (offset) return prefix + " OFFSET " + offset + " ROWS"
151,461✔
2583
        } else if (
380,886✔
2584
            DriverUtils.isMySQLFamily(this.connection.driver) ||
1,203,774✔
2585
            this.connection.driver.options.type === "aurora-mysql" ||
2586
            this.connection.driver.options.type === "sap" ||
2587
            this.connection.driver.options.type === "spanner"
2588
        ) {
2589
            if (limit && offset) return " LIMIT " + limit + " OFFSET " + offset
106,590✔
2590
            if (limit) return " LIMIT " + limit
106,464✔
2591
            if (offset) throw new OffsetWithoutLimitNotSupportedError()
102,987✔
2592
        } else if (DriverUtils.isSQLiteFamily(this.connection.driver)) {
274,296✔
2593
            if (limit && offset) return " LIMIT " + limit + " OFFSET " + offset
105,027✔
2594
            if (limit) return " LIMIT " + limit
104,901✔
2595
            if (offset) return " LIMIT -1 OFFSET " + offset
101,610✔
2596
        } else if (this.connection.driver.options.type === "oracle") {
169,269✔
2597
            if (limit && offset)
33,900✔
2598
                return (
42✔
2599
                    " OFFSET " +
2600
                    offset +
2601
                    " ROWS FETCH NEXT " +
2602
                    limit +
2603
                    " ROWS ONLY"
2604
                )
2605
            if (limit) return " FETCH NEXT " + limit + " ROWS ONLY"
33,858✔
2606
            if (offset) return " OFFSET " + offset + " ROWS"
32,826✔
2607
        } else {
2608
            if (limit && offset) return " LIMIT " + limit + " OFFSET " + offset
135,369✔
2609
            if (limit) return " LIMIT " + limit
135,222✔
2610
            if (offset) return " OFFSET " + offset
131,427✔
2611
        }
2612

2613
        return ""
520,282✔
2614
    }
2615

2616
    /**
2617
     * Creates "LOCK" part of SELECT Query after table Clause
2618
     * ex.
2619
     *  SELECT 1
2620
     *  FROM USER U WITH (NOLOCK)
2621
     *  JOIN ORDER O WITH (NOLOCK)
2622
     *      ON U.ID=O.OrderID
2623
     */
2624
    private createTableLockExpression(): string {
2625
        if (this.connection.driver.options.type === "mssql") {
4,920,472✔
2626
            switch (this.expressionMap.lockMode) {
1,432,881✔
2627
                case "pessimistic_read":
2628
                    return " WITH (HOLDLOCK, ROWLOCK)"
45✔
2629
                case "pessimistic_write":
2630
                    return " WITH (UPDLOCK, ROWLOCK)"
63✔
2631
                case "dirty_read":
2632
                    return " WITH (NOLOCK)"
99✔
2633
            }
2634
        }
2635

2636
        return ""
4,920,265✔
2637
    }
2638

2639
    /**
2640
     * Creates "LOCK" part of SQL query.
2641
     */
2642
    protected createLockExpression(): string {
2643
        const driver = this.connection.driver
537,381✔
2644

2645
        let lockTablesClause = ""
537,381✔
2646

2647
        if (this.expressionMap.lockTables) {
537,381✔
2648
            if (
87!
2649
                !(
2650
                    DriverUtils.isPostgresFamily(driver) ||
87!
2651
                    driver.options.type === "cockroachdb"
2652
                )
2653
            ) {
2654
                throw new TypeORMError(
×
2655
                    "Lock tables not supported in selected driver",
2656
                )
2657
            }
2658
            if (this.expressionMap.lockTables.length < 1) {
87✔
2659
                throw new TypeORMError("lockTables cannot be an empty array")
12✔
2660
            }
2661
            lockTablesClause = " OF " + this.expressionMap.lockTables.join(", ")
75✔
2662
        }
2663

2664
        let onLockExpression = ""
537,369✔
2665
        if (this.expressionMap.onLocked === "nowait") {
537,369✔
2666
            onLockExpression = " NOWAIT"
12✔
2667
        } else if (this.expressionMap.onLocked === "skip_locked") {
537,357✔
2668
            onLockExpression = " SKIP LOCKED"
30✔
2669
        }
2670
        switch (this.expressionMap.lockMode) {
537,369✔
2671
            case "pessimistic_read":
2672
                if (
122✔
2673
                    driver.options.type === "mysql" ||
229✔
2674
                    driver.options.type === "aurora-mysql"
2675
                ) {
2676
                    if (
15!
2677
                        DriverUtils.isReleaseVersionOrGreater(driver, "8.0.0")
2678
                    ) {
2679
                        return (
×
2680
                            " FOR SHARE" + lockTablesClause + onLockExpression
2681
                        )
2682
                    } else {
2683
                        return " LOCK IN SHARE MODE"
15✔
2684
                    }
2685
                } else if (driver.options.type === "mariadb") {
107✔
2686
                    return " LOCK IN SHARE MODE"
12✔
2687
                } else if (DriverUtils.isPostgresFamily(driver)) {
95✔
2688
                    return " FOR SHARE" + lockTablesClause + onLockExpression
30✔
2689
                } else if (driver.options.type === "oracle") {
65✔
2690
                    return " FOR UPDATE"
8✔
2691
                } else if (driver.options.type === "mssql") {
57✔
2692
                    return ""
45✔
2693
                } else {
2694
                    throw new LockNotSupportedOnGivenDriverError()
12✔
2695
                }
2696
            case "pessimistic_write":
2697
                if (
188✔
2698
                    DriverUtils.isMySQLFamily(driver) ||
510✔
2699
                    driver.options.type === "aurora-mysql" ||
2700
                    driver.options.type === "oracle"
2701
                ) {
2702
                    return " FOR UPDATE" + onLockExpression
35✔
2703
                } else if (
153✔
2704
                    DriverUtils.isPostgresFamily(driver) ||
219✔
2705
                    driver.options.type === "cockroachdb"
2706
                ) {
2707
                    return " FOR UPDATE" + lockTablesClause + onLockExpression
87✔
2708
                } else if (driver.options.type === "mssql") {
66✔
2709
                    return ""
54✔
2710
                } else {
2711
                    throw new LockNotSupportedOnGivenDriverError()
12✔
2712
                }
2713
            case "pessimistic_partial_write":
2714
                if (DriverUtils.isPostgresFamily(driver)) {
27✔
2715
                    return " FOR UPDATE" + lockTablesClause + " SKIP LOCKED"
15✔
2716
                } else if (DriverUtils.isMySQLFamily(driver)) {
12!
2717
                    return " FOR UPDATE SKIP LOCKED"
12✔
2718
                } else {
2719
                    throw new LockNotSupportedOnGivenDriverError()
×
2720
                }
2721
            case "pessimistic_write_or_fail":
2722
                if (
36✔
2723
                    DriverUtils.isPostgresFamily(driver) ||
51✔
2724
                    driver.options.type === "cockroachdb"
2725
                ) {
2726
                    return " FOR UPDATE" + lockTablesClause + " NOWAIT"
21✔
2727
                } else if (DriverUtils.isMySQLFamily(driver)) {
15!
2728
                    return " FOR UPDATE NOWAIT"
15✔
2729
                } else {
2730
                    throw new LockNotSupportedOnGivenDriverError()
×
2731
                }
2732
            case "for_no_key_update":
2733
                if (
44✔
2734
                    DriverUtils.isPostgresFamily(driver) ||
67✔
2735
                    driver.options.type === "cockroachdb"
2736
                ) {
2737
                    return (
21✔
2738
                        " FOR NO KEY UPDATE" +
2739
                        lockTablesClause +
2740
                        onLockExpression
2741
                    )
2742
                } else {
2743
                    throw new LockNotSupportedOnGivenDriverError()
23✔
2744
                }
2745
            case "for_key_share":
2746
                if (DriverUtils.isPostgresFamily(driver)) {
38✔
2747
                    return (
15✔
2748
                        " FOR KEY SHARE" + lockTablesClause + onLockExpression
2749
                    )
2750
                } else {
2751
                    throw new LockNotSupportedOnGivenDriverError()
23✔
2752
                }
2753
            default:
2754
                return ""
536,914✔
2755
        }
2756
    }
2757

2758
    /**
2759
     * Creates "HAVING" part of SQL query.
2760
     */
2761
    protected createHavingExpression() {
2762
        if (!this.expressionMap.havings || !this.expressionMap.havings.length)
537,387✔
2763
            return ""
537,387✔
2764
        const conditions = this.expressionMap.havings
×
2765
            .map((having, index) => {
2766
                switch (having.type) {
×
2767
                    case "and":
2768
                        return (
×
2769
                            (index > 0 ? "AND " : "") +
×
2770
                            this.replacePropertyNames(having.condition)
2771
                        )
2772
                    case "or":
2773
                        return (
×
2774
                            (index > 0 ? "OR " : "") +
×
2775
                            this.replacePropertyNames(having.condition)
2776
                        )
2777
                    default:
2778
                        return this.replacePropertyNames(having.condition)
×
2779
                }
2780
            })
2781
            .join(" ")
2782

2783
        if (!conditions.length) return ""
×
2784
        return " HAVING " + conditions
×
2785
    }
2786

2787
    protected buildEscapedEntityColumnSelects(
2788
        aliasName: string,
2789
        metadata: EntityMetadata,
2790
    ): SelectQuery[] {
2791
        const hasMainAlias = this.expressionMap.selects.some(
2,870,114✔
2792
            (select) => select.selection === aliasName,
12,426,246✔
2793
        )
2794

2795
        const columns: ColumnMetadata[] = []
2,870,114✔
2796
        if (hasMainAlias) {
2,870,114✔
2797
            columns.push(
2,825,712✔
2798
                ...metadata.columns.filter(
2799
                    (column) => column.isSelect === true,
8,380,731✔
2800
                ),
2801
            )
2802
        }
2803
        columns.push(
2,870,114✔
2804
            ...metadata.columns.filter((column) => {
2805
                return this.expressionMap.selects.some(
8,541,562✔
2806
                    (select) =>
2807
                        select.selection ===
68,427,459✔
2808
                        aliasName + "." + column.propertyPath,
2809
                )
2810
            }),
2811
        )
2812

2813
        // if user used partial selection and did not select some primary columns which are required to be selected
2814
        // we select those primary columns and mark them as "virtual". Later virtual column values will be removed from final entity
2815
        // to make entity contain exactly what user selected
2816
        if (columns.length === 0)
2,870,114✔
2817
            // however not in the case when nothing (even partial) was selected from this target (for example joins without selection)
2818
            return []
15,626✔
2819

2820
        const nonSelectedPrimaryColumns = this.expressionMap.queryEntity
2,854,488✔
2821
            ? metadata.primaryColumns.filter(
2822
                  (primaryColumn) => columns.indexOf(primaryColumn) === -1,
202,017✔
2823
              )
2824
            : []
2825
        const allColumns = [...columns, ...nonSelectedPrimaryColumns]
2,854,488✔
2826
        const finalSelects: SelectQuery[] = []
2,854,488✔
2827

2828
        const escapedAliasName = this.escape(aliasName)
2,854,488✔
2829
        allColumns.forEach((column) => {
2,854,488✔
2830
            let selectionPath =
2831
                escapedAliasName + "." + this.escape(column.databaseName)
8,432,091✔
2832

2833
            if (column.isVirtualProperty && column.query) {
8,432,091✔
2834
                selectionPath = `(${column.query(escapedAliasName)})`
97✔
2835
            }
2836

2837
            if (
8,432,091✔
2838
                this.connection.driver.spatialTypes.indexOf(column.type) !== -1
2839
            ) {
2840
                if (
438✔
2841
                    DriverUtils.isMySQLFamily(this.connection.driver) ||
816✔
2842
                    this.connection.driver.options.type === "aurora-mysql"
2843
                ) {
2844
                    const useLegacy = (
2845
                        this.connection.driver as
60✔
2846
                            | MysqlDriver
2847
                            | AuroraMysqlDriver
2848
                    ).options.legacySpatialSupport
2849
                    const asText = useLegacy ? "AsText" : "ST_AsText"
60✔
2850
                    selectionPath = `${asText}(${selectionPath})`
60✔
2851
                }
2852

2853
                if (DriverUtils.isPostgresFamily(this.connection.driver))
438✔
2854
                    if (column.precision) {
252!
2855
                        // cast to JSON to trigger parsing in the driver
2856
                        selectionPath = `ST_AsGeoJSON(${selectionPath}, ${column.precision})::json`
×
2857
                    } else {
2858
                        selectionPath = `ST_AsGeoJSON(${selectionPath})::json`
252✔
2859
                    }
2860
                if (this.connection.driver.options.type === "mssql")
438✔
2861
                    selectionPath = `${selectionPath}.ToString()`
126✔
2862
            }
2863

2864
            const selections = this.expressionMap.selects.filter(
8,432,091✔
2865
                (select) =>
2866
                    select.selection === aliasName + "." + column.propertyPath,
68,279,544✔
2867
            )
2868
            if (selections.length) {
8,432,091✔
2869
                selections.forEach((selection) => {
51,476✔
2870
                    finalSelects.push({
51,476✔
2871
                        selection: selectionPath,
2872
                        aliasName: selection.aliasName
51,476✔
2873
                            ? selection.aliasName
2874
                            : DriverUtils.buildAlias(
2875
                                  this.connection.driver,
2876
                                  undefined,
2877
                                  aliasName,
2878
                                  column.databaseName,
2879
                              ),
2880
                        // todo: need to keep in mind that custom selection.aliasName breaks hydrator. fix it later!
2881
                        virtual: selection.virtual,
2882
                    })
2883
                })
2884
            } else {
2885
                if (column.isVirtualProperty) {
8,380,615✔
2886
                    // Do not add unselected virtual properties to final select
2887
                    return
64✔
2888
                }
2889

2890
                finalSelects.push({
8,380,551✔
2891
                    selection: selectionPath,
2892
                    aliasName: DriverUtils.buildAlias(
2893
                        this.connection.driver,
2894
                        undefined,
2895
                        aliasName,
2896
                        column.databaseName,
2897
                    ),
2898
                    // todo: need to keep in mind that custom selection.aliasName breaks hydrator. fix it later!
2899
                    virtual: hasMainAlias,
2900
                })
2901
            }
2902
        })
2903
        return finalSelects
2,854,488✔
2904
    }
2905

2906
    protected findEntityColumnSelects(
2907
        aliasName: string,
2908
        metadata: EntityMetadata,
2909
    ): SelectQuery[] {
2910
        const mainSelect = this.expressionMap.selects.find(
2,870,114✔
2911
            (select) => select.selection === aliasName,
12,426,246✔
2912
        )
2913
        if (mainSelect) return [mainSelect]
2,870,114✔
2914

2915
        return this.expressionMap.selects.filter((select) => {
44,402✔
2916
            return metadata.columns.some(
86,105✔
2917
                (column) =>
2918
                    select.selection === aliasName + "." + column.propertyPath,
236,374✔
2919
            )
2920
        })
2921
    }
2922

2923
    private computeCountExpression() {
2924
        const mainAlias = this.expressionMap.mainAlias!.name // todo: will this work with "fromTableName"?
1,415✔
2925
        const metadata = this.expressionMap.mainAlias!.metadata
1,415✔
2926

2927
        const primaryColumns = metadata.primaryColumns
1,415✔
2928
        const distinctAlias = this.escape(mainAlias)
1,415✔
2929

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

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

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

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

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

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

2990
            if (primaryColumns.length === 1) {
36✔
2991
                return `COUNT(DISTINCT(${columnsExpression}))`
18✔
2992
            }
2993

2994
            return `COUNT(DISTINCT(CONCAT(${columnsExpression})))`
18✔
2995
        }
2996

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

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

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

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

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

3025
        return (
32✔
3026
            `COUNT(DISTINCT(` +
3027
            primaryColumns
3028
                .map((c) => `${distinctAlias}.${this.escape(c.databaseName)}`)
56✔
3029
                .join(" || '|;|' || ") +
3030
            "))"
3031
        )
3032
    }
3033

3034
    protected async executeCountQuery(
3035
        queryRunner: QueryRunner,
3036
    ): Promise<number> {
3037
        const countSql = this.computeCountExpression()
1,415✔
3038

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

3050
        if (!results || !results[0] || !results[0]["cnt"]) return 0
1,415✔
3051

3052
        return parseInt(results[0]["cnt"])
1,381✔
3053
    }
3054

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

3066
        return results.length > 0
203✔
3067
    }
3068

3069
    protected applyFindOptions() {
3070
        // todo: convert relations: string[] to object map to simplify code
3071
        // todo: same with selects
3072

3073
        if (this.expressionMap.mainAlias!.metadata) {
176,399✔
3074
            if (this.findOptions.relationLoadStrategy) {
176,399✔
3075
                this.expressionMap.relationLoadStrategy =
18✔
3076
                    this.findOptions.relationLoadStrategy
3077
            }
3078

3079
            if (this.findOptions.comment) {
176,399✔
3080
                this.comment(this.findOptions.comment)
29✔
3081
            }
3082

3083
            if (this.findOptions.withDeleted) {
176,399✔
3084
                this.withDeleted()
121,997✔
3085
            }
3086

3087
            if (this.findOptions.select) {
176,399✔
3088
                const select = Array.isArray(this.findOptions.select)
505!
3089
                    ? OrmUtils.propertyPathsToTruthyObject(
3090
                          this.findOptions.select as string[],
3091
                      )
3092
                    : this.findOptions.select
3093

3094
                this.buildSelect(
505✔
3095
                    select,
3096
                    this.expressionMap.mainAlias!.metadata,
3097
                    this.expressionMap.mainAlias!.name,
3098
                )
3099
            }
3100

3101
            if (this.selects.length) {
176,370✔
3102
                this.select(this.selects)
418✔
3103
            }
3104

3105
            this.selects = []
176,370✔
3106
            if (this.findOptions.relations) {
176,370✔
3107
                const relations = Array.isArray(this.findOptions.relations)
37,215✔
3108
                    ? OrmUtils.propertyPathsToTruthyObject(
3109
                          this.findOptions.relations,
3110
                      )
3111
                    : this.findOptions.relations
3112

3113
                this.buildRelations(
37,215✔
3114
                    relations,
3115
                    typeof this.findOptions.select === "object"
37,215✔
3116
                        ? (this.findOptions.select as FindOptionsSelect<any>)
3117
                        : undefined,
3118
                    this.expressionMap.mainAlias!.metadata,
3119
                    this.expressionMap.mainAlias!.name,
3120
                )
3121
                if (
37,041✔
3122
                    this.findOptions.loadEagerRelations !== false &&
74,082✔
3123
                    this.expressionMap.relationLoadStrategy === "join"
3124
                ) {
3125
                    this.buildEagerRelations(
37,023✔
3126
                        relations,
3127
                        typeof this.findOptions.select === "object"
37,023✔
3128
                            ? (this.findOptions
3129
                                  .select as FindOptionsSelect<any>)
3130
                            : undefined,
3131
                        this.expressionMap.mainAlias!.metadata,
3132
                        this.expressionMap.mainAlias!.name,
3133
                    )
3134
                }
3135
            }
3136
            if (this.selects.length) {
176,196✔
3137
                this.addSelect(this.selects)
67✔
3138
            }
3139

3140
            if (this.findOptions.where) {
176,196✔
3141
                this.conditions = this.buildWhere(
49,394✔
3142
                    this.findOptions.where,
3143
                    this.expressionMap.mainAlias!.metadata,
3144
                    this.expressionMap.mainAlias!.name,
3145
                )
3146

3147
                if (this.conditions.length)
49,336✔
3148
                    this.andWhere(
48,709✔
3149
                        this.conditions.substr(0, 1) !== "("
48,709!
3150
                            ? "(" + this.conditions + ")"
3151
                            : this.conditions,
3152
                    ) // temporary and where and braces
3153
            }
3154

3155
            if (this.findOptions.order) {
176,138✔
3156
                this.buildOrder(
2,484✔
3157
                    this.findOptions.order,
3158
                    this.expressionMap.mainAlias!.metadata,
3159
                    this.expressionMap.mainAlias!.name,
3160
                )
3161
            }
3162

3163
            // apply joins
3164
            if (this.joins.length) {
176,109✔
3165
                this.joins.forEach((join) => {
37,547✔
3166
                    if (join.select && !join.selection) {
279,504✔
3167
                        // if (join.selection) {
3168
                        //
3169
                        // } else {
3170
                        if (join.type === "inner") {
278,698!
3171
                            this.innerJoinAndSelect(
×
3172
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3173
                                join.alias,
3174
                            )
3175
                        } else {
3176
                            this.leftJoinAndSelect(
278,698✔
3177
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3178
                                join.alias,
3179
                            )
3180
                        }
3181
                        // }
3182
                    } else {
3183
                        if (join.type === "inner") {
806!
3184
                            this.innerJoin(
×
3185
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3186
                                join.alias,
3187
                            )
3188
                        } else {
3189
                            this.leftJoin(
806✔
3190
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3191
                                join.alias,
3192
                            )
3193
                        }
3194
                    }
3195

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

3208
            // if (this.conditions.length) {
3209
            //     this.where(this.conditions.join(" AND "));
3210
            // }
3211

3212
            // apply offset
3213
            if (this.findOptions.skip !== undefined) {
176,109✔
3214
                // if (this.findOptions.options && this.findOptions.options.pagination === false) {
3215
                //     this.offset(this.findOptions.skip);
3216
                // } else {
3217
                this.skip(this.findOptions.skip)
177✔
3218
                // }
3219
            }
3220

3221
            // apply limit
3222
            if (this.findOptions.take !== undefined) {
176,080✔
3223
                // if (this.findOptions.options && this.findOptions.options.pagination === false) {
3224
                //     this.limit(this.findOptions.take);
3225
                // } else {
3226
                this.take(this.findOptions.take)
16,042✔
3227
                // }
3228
            }
3229

3230
            // apply caching options
3231
            if (typeof this.findOptions.cache === "number") {
176,051✔
3232
                this.cache(this.findOptions.cache)
58✔
3233
            } else if (typeof this.findOptions.cache === "boolean") {
175,993✔
3234
                this.cache(this.findOptions.cache)
87✔
3235
            } else if (typeof this.findOptions.cache === "object") {
175,906✔
3236
                this.cache(
145✔
3237
                    this.findOptions.cache.id,
3238
                    this.findOptions.cache.milliseconds,
3239
                )
3240
            }
3241

3242
            if (this.findOptions.join) {
176,051✔
3243
                if (this.findOptions.join.leftJoin)
673!
3244
                    Object.keys(this.findOptions.join.leftJoin).forEach(
×
3245
                        (key) => {
3246
                            this.leftJoin(
×
3247
                                this.findOptions.join!.leftJoin![key],
3248
                                key,
3249
                            )
3250
                        },
3251
                    )
3252

3253
                if (this.findOptions.join.innerJoin)
673!
3254
                    Object.keys(this.findOptions.join.innerJoin).forEach(
×
3255
                        (key) => {
3256
                            this.innerJoin(
×
3257
                                this.findOptions.join!.innerJoin![key],
3258
                                key,
3259
                            )
3260
                        },
3261
                    )
3262

3263
                if (this.findOptions.join.leftJoinAndSelect)
673✔
3264
                    Object.keys(
406✔
3265
                        this.findOptions.join.leftJoinAndSelect,
3266
                    ).forEach((key) => {
3267
                        this.leftJoinAndSelect(
609✔
3268
                            this.findOptions.join!.leftJoinAndSelect![key],
3269
                            key,
3270
                        )
3271
                    })
3272

3273
                if (this.findOptions.join.innerJoinAndSelect)
673✔
3274
                    Object.keys(
267✔
3275
                        this.findOptions.join.innerJoinAndSelect,
3276
                    ).forEach((key) => {
3277
                        this.innerJoinAndSelect(
273✔
3278
                            this.findOptions.join!.innerJoinAndSelect![key],
3279
                            key,
3280
                        )
3281
                    })
3282
            }
3283

3284
            if (this.findOptions.lock) {
176,051✔
3285
                if (this.findOptions.lock.mode === "optimistic") {
438✔
3286
                    this.setLock(
225✔
3287
                        this.findOptions.lock.mode,
3288
                        this.findOptions.lock.version,
3289
                    )
3290
                } else if (
213✔
3291
                    this.findOptions.lock.mode === "pessimistic_read" ||
432✔
3292
                    this.findOptions.lock.mode === "pessimistic_write" ||
3293
                    this.findOptions.lock.mode === "dirty_read" ||
3294
                    this.findOptions.lock.mode ===
3295
                        "pessimistic_partial_write" ||
3296
                    this.findOptions.lock.mode ===
3297
                        "pessimistic_write_or_fail" ||
3298
                    this.findOptions.lock.mode === "for_no_key_update" ||
3299
                    this.findOptions.lock.mode === "for_key_share"
3300
                ) {
3301
                    const tableNames = this.findOptions.lock.tables
213✔
3302
                        ? this.findOptions.lock.tables.map((table) => {
3303
                              const tableAlias =
3304
                                  this.expressionMap.aliases.find((alias) => {
36✔
3305
                                      return (
60✔
3306
                                          alias.metadata
3307
                                              .tableNameWithoutPrefix === table
3308
                                      )
3309
                                  })
3310
                              if (!tableAlias) {
36✔
3311
                                  throw new TypeORMError(
6✔
3312
                                      `"${table}" is not part of this query`,
3313
                                  )
3314
                              }
3315
                              return this.escape(tableAlias.name)
30✔
3316
                          })
3317
                        : undefined
3318
                    this.setLock(
207✔
3319
                        this.findOptions.lock.mode,
3320
                        undefined,
3321
                        tableNames,
3322
                    )
3323

3324
                    if (this.findOptions.lock.onLocked) {
207✔
3325
                        this.setOnLocked(this.findOptions.lock.onLocked)
9✔
3326
                    }
3327
                }
3328
            }
3329

3330
            if (this.findOptions.loadRelationIds === true) {
176,045✔
3331
                this.loadAllRelationIds()
29✔
3332
            } else if (typeof this.findOptions.loadRelationIds === "object") {
176,016✔
3333
                this.loadAllRelationIds(this.findOptions.loadRelationIds as any)
121,617✔
3334
            }
3335

3336
            if (this.findOptions.loadEagerRelations !== false) {
176,045✔
3337
                FindOptionsUtils.joinEagerRelations(
54,370✔
3338
                    this,
3339
                    this.expressionMap.mainAlias!.name,
3340
                    this.expressionMap.mainAlias!.metadata,
3341
                )
3342
            }
3343

3344
            if (this.findOptions.transaction === true) {
176,045✔
3345
                this.expressionMap.useTransaction = true
29✔
3346
            }
3347

3348
            // if (this.orderBys.length) {
3349
            //     this.orderBys.forEach(orderBy => {
3350
            //         this.addOrderBy(orderBy.alias, orderBy.direction, orderBy.nulls);
3351
            //     });
3352
            // }
3353

3354
            // todo
3355
            // if (this.options.options && this.options.options.eagerRelations) {
3356
            //     this.queryBuilder
3357
            // }
3358

3359
            // todo
3360
            // if (this.findOptions.options && this.findOptions.listeners === false) {
3361
            //     this.callListeners(false);
3362
            // }
3363
        }
3364
    }
3365

3366
    public concatRelationMetadata(relationMetadata: RelationMetadata) {
3367
        this.relationMetadatas.push(relationMetadata)
15✔
3368
    }
3369

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

3381
        if (
173,406✔
3382
            (this.expressionMap.lockMode === "pessimistic_read" ||
1,039,339✔
3383
                this.expressionMap.lockMode === "pessimistic_write" ||
3384
                this.expressionMap.lockMode === "pessimistic_partial_write" ||
3385
                this.expressionMap.lockMode === "pessimistic_write_or_fail" ||
3386
                this.expressionMap.lockMode === "for_no_key_update" ||
3387
                this.expressionMap.lockMode === "for_key_share") &&
3388
            !queryRunner.isTransactionActive
3389
        )
3390
            throw new PessimisticLockTransactionRequiredError()
110✔
3391

3392
        if (this.expressionMap.lockMode === "optimistic") {
173,296✔
3393
            const metadata = this.expressionMap.mainAlias.metadata
225✔
3394
            if (!metadata.versionColumn && !metadata.updateDateColumn)
225✔
3395
                throw new NoVersionOrUpdateDateColumnError(metadata.name)
29✔
3396
        }
3397

3398
        const relationIdLoader = new RelationIdLoader(
173,267✔
3399
            this.connection,
3400
            queryRunner,
3401
            this.expressionMap.relationIdAttributes,
3402
        )
3403
        const relationCountLoader = new RelationCountLoader(
173,267✔
3404
            this.connection,
3405
            queryRunner,
3406
            this.expressionMap.relationCountAttributes,
3407
        )
3408
        const relationIdMetadataTransformer =
3409
            new RelationIdMetadataToAttributeTransformer(this.expressionMap)
173,267✔
3410
        relationIdMetadataTransformer.transform()
173,267✔
3411
        const relationCountMetadataTransformer =
3412
            new RelationCountMetadataToAttributeTransformer(this.expressionMap)
173,267✔
3413
        relationCountMetadataTransformer.transform()
173,267✔
3414

3415
        let rawResults: any[] = [],
173,267✔
3416
            entities: any[] = []
173,267✔
3417

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

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

3448
                    const alias = DriverUtils.buildAlias(
7,612✔
3449
                        this.connection.driver,
3450
                        undefined,
3451
                        "ids_" + mainAliasName,
3452
                        primaryColumn.databaseName,
3453
                    )
3454

3455
                    return `${distinctAlias}.${columnAlias} AS ${this.escape(
7,612✔
3456
                        alias,
3457
                    )}`
3458
                },
3459
            )
3460

3461
            const originalQuery = this.clone()
7,522✔
3462

3463
            // preserve original timeTravel value since we set it to "false" in subquery
3464
            const originalQueryTimeTravel =
3465
                originalQuery.expressionMap.timeTravel
7,522✔
3466

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

3494
            if (rawResults.length > 0) {
7,510✔
3495
                let condition = ""
7,390✔
3496
                const parameters: ObjectLiteral = {}
7,390✔
3497
                if (metadata.hasMultiplePrimaryKeys) {
7,390✔
3498
                    condition = rawResults
90✔
3499
                        .map((result, index) => {
3500
                            return metadata.primaryColumns
467✔
3501
                                .map((primaryColumn) => {
3502
                                    const paramKey = `orm_distinct_ids_${index}_${primaryColumn.databaseName}`
934✔
3503
                                    const paramKeyResult =
3504
                                        DriverUtils.buildAlias(
934✔
3505
                                            this.connection.driver,
3506
                                            undefined,
3507
                                            "ids_" + mainAliasName,
3508
                                            primaryColumn.databaseName,
3509
                                        )
3510
                                    parameters[paramKey] =
934✔
3511
                                        result[paramKeyResult]
3512
                                    return `${mainAliasName}.${primaryColumn.propertyPath}=:${paramKey}`
934✔
3513
                                })
3514
                                .join(" AND ")
3515
                        })
3516
                        .join(" OR ")
3517
                } else {
3518
                    const alias = DriverUtils.buildAlias(
7,300✔
3519
                        this.connection.driver,
3520
                        undefined,
3521
                        "ids_" + mainAliasName,
3522
                        metadata.primaryColumns[0].databaseName,
3523
                    )
3524

3525
                    const ids = rawResults.map((result) => result[alias])
10,817✔
3526
                    const areAllNumbers = ids.every(
7,300✔
3527
                        (id: any) => typeof id === "number",
10,754✔
3528
                    )
3529
                    if (areAllNumbers) {
7,300✔
3530
                        // fixes #190. if all numbers then its safe to perform query without parameter
3531
                        condition = `${mainAliasName}.${
6,454✔
3532
                            metadata.primaryColumns[0].propertyPath
3533
                        } IN (${ids.join(", ")})`
3534
                    } else {
3535
                        parameters["orm_distinct_ids"] = ids
846✔
3536
                        condition =
846✔
3537
                            mainAliasName +
3538
                            "." +
3539
                            metadata.primaryColumns[0].propertyPath +
3540
                            " IN (:...orm_distinct_ids)"
3541
                    }
3542
                }
3543
                rawResults = await this.clone()
7,390✔
3544
                    .mergeExpressionMap({
3545
                        extraAppendedAndWhereCondition: condition,
3546
                    })
3547
                    .setParameters(parameters)
3548
                    .loadRawResults(queryRunner)
3549
            }
3550
        } else {
3551
            rawResults = await this.loadRawResults(queryRunner)
165,745✔
3552
        }
3553

3554
        if (rawResults.length > 0) {
173,158✔
3555
            // transform raw results into entities
3556
            const rawRelationIdResults = await relationIdLoader.load(rawResults)
57,907✔
3557
            const rawRelationCountResults = await relationCountLoader.load(
57,907✔
3558
                rawResults,
3559
            )
3560
            const transformer = new RawSqlResultsToEntityTransformer(
57,907✔
3561
                this.expressionMap,
3562
                this.connection.driver,
3563
                rawRelationIdResults,
3564
                rawRelationCountResults,
3565
                this.queryRunner,
3566
            )
3567
            entities = transformer.transform(
57,907✔
3568
                rawResults,
3569
                this.expressionMap.mainAlias!,
3570
            )
3571

3572
            // broadcast all "after load" events
3573
            if (
57,901✔
3574
                this.expressionMap.callListeners === true &&
115,802✔
3575
                this.expressionMap.mainAlias.hasMetadata
3576
            ) {
3577
                await queryRunner.broadcaster.broadcast(
57,901✔
3578
                    "Load",
3579
                    this.expressionMap.mainAlias.metadata,
3580
                    entities,
3581
                )
3582
            }
3583
        }
3584

3585
        if (this.expressionMap.relationLoadStrategy === "query") {
173,152✔
3586
            const queryStrategyRelationIdLoader =
3587
                new QueryStrategyRelationIdLoader(this.connection, queryRunner)
54✔
3588

3589
            await Promise.all(
54✔
3590
                this.relationMetadatas.map(async (relation) => {
3591
                    const relationTarget = relation.inverseEntityMetadata.target
15✔
3592
                    const relationAlias =
3593
                        relation.inverseEntityMetadata.targetName
15✔
3594

3595
                    const select = Array.isArray(this.findOptions.select)
15!
3596
                        ? OrmUtils.propertyPathsToTruthyObject(
3597
                              this.findOptions.select as string[],
3598
                          )
3599
                        : this.findOptions.select
3600
                    const relations = Array.isArray(this.findOptions.relations)
15!
3601
                        ? OrmUtils.propertyPathsToTruthyObject(
3602
                              this.findOptions.relations,
3603
                          )
3604
                        : this.findOptions.relations
3605

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

3657
        return {
173,152✔
3658
            raw: rawResults,
3659
            entities: entities,
3660
        }
3661
    }
3662

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

3703
                    return ""
6✔
3704
                }
3705
            })
3706
            .join(", ")
3707

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

3748
        return [selectString, orderByObject]
7,522✔
3749
    }
3750

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

3805
        const results = await queryRunner.query(sql, parameters, true)
202,413✔
3806

3807
        if (
202,392✔
3808
            !cacheError &&
407,557✔
3809
            this.connection.queryResultCache &&
3810
            isCachingEnabled
3811
        ) {
3812
            try {
818✔
3813
                await this.connection.queryResultCache.storeInCache(
818✔
3814
                    {
3815
                        identifier: this.expressionMap.cacheId,
3816
                        query: queryId,
3817
                        time: new Date().getTime(),
3818
                        duration:
3819
                            this.expressionMap.cacheDuration ||
1,700✔
3820
                            cacheOptions.duration ||
3821
                            1000,
3822
                        result: JSON.stringify(results.records),
3823
                    },
3824
                    savedQueryResultCacheOptions,
3825
                    queryRunner,
3826
                )
3827
            } catch (error) {
3828
                if (!cacheOptions.ignoreErrors) {
×
3829
                    throw error
×
3830
                }
3831
            }
3832
        }
3833

3834
        return results.records
202,392✔
3835
    }
3836

3837
    /**
3838
     * Merges into expression map given expression map properties.
3839
     */
3840
    protected mergeExpressionMap(
3841
        expressionMap: Partial<QueryExpressionMap>,
3842
    ): this {
3843
        ObjectUtils.assign(this.expressionMap, expressionMap)
7,390✔
3844
        return this
7,390✔
3845
    }
3846

3847
    /**
3848
     * Normalizes a give number - converts to int if possible.
3849
     */
3850
    protected normalizeNumber(num: any) {
3851
        if (typeof num === "number" || num === undefined || num === null)
39,802✔
3852
            return num
39,686✔
3853

3854
        return Number(num)
116✔
3855
    }
3856

3857
    /**
3858
     * Creates a query builder used to execute sql queries inside this query builder.
3859
     */
3860
    protected obtainQueryRunner() {
3861
        return (
202,857✔
3862
            this.queryRunner ||
250,539✔
3863
            this.connection.createQueryRunner(
3864
                this.connection.defaultReplicationModeForReads(),
3865
            )
3866
        )
3867
    }
3868

3869
    protected buildSelect(
3870
        select: FindOptionsSelect<any>,
3871
        metadata: EntityMetadata,
3872
        alias: string,
3873
        embedPrefix?: string,
3874
    ) {
3875
        for (let key in select) {
662✔
3876
            if (select[key] === undefined || select[key] === false) continue
1,043✔
3877

3878
            const propertyPath = embedPrefix ? embedPrefix + "." + key : key
982✔
3879
            const column =
3880
                metadata.findColumnWithPropertyPathStrict(propertyPath)
982✔
3881
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
982✔
3882
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
982✔
3883

3884
            if (!embed && !column && !relation)
982✔
3885
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
29✔
3886

3887
            if (column) {
953✔
3888
                this.selects.push(alias + "." + propertyPath)
796✔
3889
                // this.addSelect(alias + "." + propertyPath);
3890
            } else if (embed) {
157✔
3891
                this.buildSelect(
81✔
3892
                    select[key] as FindOptionsSelect<any>,
3893
                    metadata,
3894
                    alias,
3895
                    propertyPath,
3896
                )
3897

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

3915
    protected buildRelations(
3916
        relations: FindOptionsRelations<any>,
3917
        selection: FindOptionsSelect<any> | undefined,
3918
        metadata: EntityMetadata,
3919
        alias: string,
3920
        embedPrefix?: string,
3921
    ) {
3922
        if (!relations) return
38,296!
3923

3924
        Object.keys(relations).forEach((relationName) => {
38,296✔
3925
            const relationValue = (relations as any)[relationName]
278,925✔
3926
            const propertyPath = embedPrefix
278,925✔
3927
                ? embedPrefix + "." + relationName
3928
                : relationName
3929
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
278,925✔
3930
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
278,925✔
3931
            if (!embed && !relation)
278,925✔
3932
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
174✔
3933

3934
            if (embed) {
278,751✔
3935
                this.buildRelations(
145✔
3936
                    relationValue,
3937
                    typeof selection === "object"
145!
3938
                        ? OrmUtils.deepValue(selection, embed.propertyPath)
3939
                        : undefined,
3940
                    metadata,
3941
                    alias,
3942
                    propertyPath,
3943
                )
3944
            } else if (relation) {
278,606✔
3945
                let joinAlias = alias + "_" + propertyPath.replace(".", "_")
278,606✔
3946
                joinAlias = DriverUtils.buildAlias(
278,606✔
3947
                    this.connection.driver,
3948
                    { joiner: "__" },
3949
                    alias,
3950
                    joinAlias,
3951
                )
3952
                if (
278,606✔
3953
                    relationValue === true ||
279,542✔
3954
                    typeof relationValue === "object"
3955
                ) {
3956
                    if (this.expressionMap.relationLoadStrategy === "query") {
278,606✔
3957
                        this.concatRelationMetadata(relation)
9✔
3958
                    } else {
3959
                        // join
3960
                        this.joins.push({
278,597✔
3961
                            type: "left",
3962
                            select: true,
3963
                            selection:
3964
                                selection &&
557,357✔
3965
                                typeof selection[relationName] === "object"
3966
                                    ? (selection[
3967
                                          relationName
3968
                                      ] as FindOptionsSelect<any>)
3969
                                    : undefined,
3970
                            alias: joinAlias,
3971
                            parentAlias: alias,
3972
                            relationMetadata: relation,
3973
                        })
3974

3975
                        if (
278,597✔
3976
                            selection &&
278,760✔
3977
                            typeof selection[relationName] === "object"
3978
                        ) {
3979
                            this.buildSelect(
76✔
3980
                                selection[
3981
                                    relationName
3982
                                ] as FindOptionsSelect<any>,
3983
                                relation.inverseEntityMetadata,
3984
                                joinAlias,
3985
                            )
3986
                        }
3987
                    }
3988
                }
3989

3990
                if (
278,606✔
3991
                    typeof relationValue === "object" &&
279,542✔
3992
                    this.expressionMap.relationLoadStrategy === "join"
3993
                ) {
3994
                    this.buildRelations(
936✔
3995
                        relationValue,
3996
                        typeof selection === "object"
936✔
3997
                            ? OrmUtils.deepValue(
3998
                                  selection,
3999
                                  relation.propertyPath,
4000
                              )
4001
                            : undefined,
4002
                        relation.inverseEntityMetadata,
4003
                        joinAlias,
4004
                        undefined,
4005
                    )
4006
                }
4007
            }
4008
        })
4009
    }
4010

4011
    protected buildEagerRelations(
4012
        relations: FindOptionsRelations<any>,
4013
        selection: FindOptionsSelect<any> | undefined,
4014
        metadata: EntityMetadata,
4015
        alias: string,
4016
        embedPrefix?: string,
4017
    ) {
4018
        if (!relations) return
38,017!
4019

4020
        Object.keys(relations).forEach((relationName) => {
38,017✔
4021
            const relationValue = (relations as any)[relationName]
278,597✔
4022
            const propertyPath = embedPrefix
278,597✔
4023
                ? embedPrefix + "." + relationName
4024
                : relationName
4025
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
278,597✔
4026
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
278,597✔
4027
            if (!embed && !relation)
278,597!
4028
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
×
4029

4030
            if (embed) {
278,597✔
4031
                this.buildEagerRelations(
116✔
4032
                    relationValue,
4033
                    typeof selection === "object"
116!
4034
                        ? OrmUtils.deepValue(selection, embed.propertyPath)
4035
                        : undefined,
4036
                    metadata,
4037
                    alias,
4038
                    propertyPath,
4039
                )
4040
            } else if (relation) {
278,481✔
4041
                let joinAlias = alias + "_" + propertyPath.replace(".", "_")
278,481✔
4042
                joinAlias = DriverUtils.buildAlias(
278,481✔
4043
                    this.connection.driver,
4044
                    { joiner: "__" },
4045
                    alias,
4046
                    joinAlias,
4047
                )
4048

4049
                if (
278,481✔
4050
                    relationValue === true ||
279,359✔
4051
                    typeof relationValue === "object"
4052
                ) {
4053
                    relation.inverseEntityMetadata.eagerRelations.forEach(
278,481✔
4054
                        (eagerRelation) => {
4055
                            let eagerRelationJoinAlias =
4056
                                joinAlias +
438✔
4057
                                "_" +
4058
                                eagerRelation.propertyPath.replace(".", "_")
4059
                            eagerRelationJoinAlias = DriverUtils.buildAlias(
438✔
4060
                                this.connection.driver,
4061
                                { joiner: "__" },
4062
                                joinAlias,
4063
                                eagerRelationJoinAlias,
4064
                            )
4065

4066
                            const existJoin = this.joins.find(
438✔
4067
                                (join) => join.alias === eagerRelationJoinAlias,
2,178✔
4068
                            )
4069
                            if (!existJoin) {
438✔
4070
                                this.joins.push({
293✔
4071
                                    type: "left",
4072
                                    select: true,
4073
                                    alias: eagerRelationJoinAlias,
4074
                                    parentAlias: joinAlias,
4075
                                    selection: undefined,
4076
                                    relationMetadata: eagerRelation,
4077
                                })
4078
                            }
4079

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

4096
                if (typeof relationValue === "object") {
278,481✔
4097
                    this.buildEagerRelations(
878✔
4098
                        relationValue,
4099
                        typeof selection === "object"
878✔
4100
                            ? OrmUtils.deepValue(
4101
                                  selection,
4102
                                  relation.propertyPath,
4103
                              )
4104
                            : undefined,
4105
                        relation.inverseEntityMetadata,
4106
                        joinAlias,
4107
                        undefined,
4108
                    )
4109
                }
4110
            }
4111
        })
4112
    }
4113

4114
    protected buildOrder(
4115
        order: FindOptionsOrder<any>,
4116
        metadata: EntityMetadata,
4117
        alias: string,
4118
        embedPrefix?: string,
4119
    ) {
4120
        for (let key in order) {
3,047✔
4121
            if (order[key] === undefined) continue
3,372!
4122

4123
            const propertyPath = embedPrefix ? embedPrefix + "." + key : key
3,372✔
4124
            const column =
4125
                metadata.findColumnWithPropertyPathStrict(propertyPath)
3,372✔
4126
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
3,372✔
4127
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
3,372✔
4128

4129
            if (!embed && !column && !relation)
3,372✔
4130
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
29✔
4131

4132
            if (column) {
3,343✔
4133
                let direction =
4134
                    typeof order[key] === "object"
2,780✔
4135
                        ? (order[key] as any).direction
4136
                        : order[key]
4137
                direction =
2,780✔
4138
                    direction === "DESC" ||
10,630✔
4139
                    direction === "desc" ||
4140
                    direction === -1
4141
                        ? "DESC"
4142
                        : "ASC"
4143
                let nulls =
4144
                    typeof order[key] === "object"
2,780✔
4145
                        ? (order[key] as any).nulls
4146
                        : undefined
4147
                nulls =
2,780✔
4148
                    nulls?.toLowerCase() === "first"
2,780✔
4149
                        ? "NULLS FIRST"
4150
                        : nulls?.toLowerCase() === "last"
2,768✔
4151
                        ? "NULLS LAST"
4152
                        : undefined
4153

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

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

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

4219
    protected buildWhere(
4220
        where: FindOptionsWhere<any>[] | FindOptionsWhere<any>,
4221
        metadata: EntityMetadata,
4222
        alias: string,
4223
        embedPrefix?: string,
4224
    ) {
4225
        let condition: string = ""
50,900✔
4226
        // let parameterIndex = Object.keys(this.expressionMap.nativeParameters).length;
4227
        if (Array.isArray(where)) {
50,900✔
4228
            if (where.length) {
206✔
4229
                condition = where
203✔
4230
                    .map((whereItem) => {
4231
                        return this.buildWhere(
406✔
4232
                            whereItem,
4233
                            metadata,
4234
                            alias,
4235
                            embedPrefix,
4236
                        )
4237
                    })
4238
                    .filter((condition) => !!condition)
406✔
4239
                    .map((condition) => "(" + condition + ")")
406✔
4240
                    .join(" OR ")
4241
            }
4242
        } else {
4243
            let andConditions: string[] = []
50,694✔
4244
            for (let key in where) {
50,694✔
4245
                if (where[key] === undefined || where[key] === null) continue
51,685✔
4246

4247
                const propertyPath = embedPrefix ? embedPrefix + "." + key : key
51,595✔
4248
                const column =
4249
                    metadata.findColumnWithPropertyPathStrict(propertyPath)
51,595✔
4250
                const embed =
4251
                    metadata.findEmbeddedWithPropertyPath(propertyPath)
51,595✔
4252
                const relation =
4253
                    metadata.findRelationWithPropertyPath(propertyPath)
51,595✔
4254

4255
                if (!embed && !column && !relation)
51,595✔
4256
                    throw new EntityPropertyNotFoundError(
58✔
4257
                        propertyPath,
4258
                        metadata,
4259
                    )
4260

4261
                if (column) {
51,537✔
4262
                    let aliasPath = `${alias}.${propertyPath}`
49,770✔
4263
                    if (column.isVirtualProperty && column.query) {
49,770✔
4264
                        aliasPath = `(${column.query(alias)})`
12✔
4265
                    }
4266
                    // const parameterName = alias + "_" + propertyPath.split(".").join("_") + "_" + parameterIndex;
4267

4268
                    // todo: we need to handle other operators as well?
4269
                    let parameterValue = where[key]
49,770✔
4270
                    if (InstanceChecker.isEqualOperator(where[key])) {
49,770✔
4271
                        parameterValue = where[key].value
52✔
4272
                    }
4273
                    if (column.transformer) {
49,770✔
4274
                        parameterValue instanceof FindOperator
119✔
4275
                            ? parameterValue.transformValue(column.transformer)
4276
                            : (parameterValue =
4277
                                  ApplyValueTransformers.transformTo(
4278
                                      column.transformer,
4279
                                      parameterValue,
4280
                                  ))
4281
                    }
4282

4283
                    // if (parameterValue === null) {
4284
                    //     andConditions.push(`${aliasPath} IS NULL`);
4285
                    //
4286
                    // } else if (parameterValue instanceof FindOperator) {
4287
                    //     // let parameters: any[] = [];
4288
                    //     // if (parameterValue.useParameter) {
4289
                    //     //     const realParameterValues: any[] = parameterValue.multipleParameters ? parameterValue.value : [parameterValue.value];
4290
                    //     //     realParameterValues.forEach((realParameterValue, realParameterValueIndex) => {
4291
                    //     //
4292
                    //     //         // don't create parameters for number to prevent max number of variables issues as much as possible
4293
                    //     //         if (typeof realParameterValue === "number") {
4294
                    //     //             parameters.push(realParameterValue);
4295
                    //     //
4296
                    //     //         } else {
4297
                    //     //             this.expressionMap.nativeParameters[parameterName + realParameterValueIndex] = realParameterValue;
4298
                    //     //             parameterIndex++;
4299
                    //     //             parameters.push(this.connection.driver.createParameter(parameterName + realParameterValueIndex, parameterIndex - 1));
4300
                    //     //         }
4301
                    //     //     });
4302
                    //     // }
4303
                    //     andConditions.push(
4304
                    //         this.createWhereConditionExpression(this.getWherePredicateCondition(aliasPath, parameterValue))
4305
                    //         // parameterValue.toSql(this.connection, aliasPath, parameters));
4306
                    //     )
4307
                    //
4308
                    // } else {
4309
                    //     this.expressionMap.nativeParameters[parameterName] = parameterValue;
4310
                    //     parameterIndex++;
4311
                    //     const parameter = this.connection.driver.createParameter(parameterName, parameterIndex - 1);
4312
                    //     andConditions.push(`${aliasPath} = ${parameter}`);
4313
                    // }
4314

4315
                    andConditions.push(
49,770✔
4316
                        this.createWhereConditionExpression(
4317
                            this.getWherePredicateCondition(
4318
                                aliasPath,
4319
                                parameterValue,
4320
                            ),
4321
                        ),
4322
                        // parameterValue.toSql(this.connection, aliasPath, parameters));
4323
                    )
4324

4325
                    // this.conditions.push(`${alias}.${propertyPath} = :${paramName}`);
4326
                    // this.expressionMap.parameters[paramName] = where[key]; // todo: handle functions and other edge cases
4327
                } else if (embed) {
1,767✔
4328
                    const condition = this.buildWhere(
485✔
4329
                        where[key],
4330
                        metadata,
4331
                        alias,
4332
                        propertyPath,
4333
                    )
4334
                    if (condition) andConditions.push(condition)
485✔
4335
                } else if (relation) {
1,282✔
4336
                    // if all properties of where are undefined we don't need to join anything
4337
                    // this can happen when user defines map with conditional queries inside
4338
                    if (typeof where[key] === "object") {
1,282✔
4339
                        const allAllUndefined = Object.keys(where[key]).every(
1,253✔
4340
                            (k) => where[key][k] === undefined,
1,282✔
4341
                        )
4342
                        if (allAllUndefined) {
1,253✔
4343
                            continue
29✔
4344
                        }
4345
                    }
4346

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

4457
                                andConditions.push(
232✔
4458
                                    this.createWhereConditionExpression(
4459
                                        this.getWherePredicateCondition(
4460
                                            aliasPath,
4461
                                            where[key],
4462
                                        ),
4463
                                    ),
4464
                                )
4465
                            } else {
4466
                                throw new Error(
×
4467
                                    `This relation isn't supported by given find operator`,
4468
                                )
4469
                            }
4470
                        }
4471
                    } else {
4472
                        // const joinAlias = alias + "_" + relation.propertyName;
4473
                        let joinAlias =
4474
                            alias +
615✔
4475
                            "_" +
4476
                            relation.propertyPath.replace(".", "_")
4477
                        joinAlias = DriverUtils.buildAlias(
615✔
4478
                            this.connection.driver,
4479
                            { joiner: "__" },
4480
                            alias,
4481
                            joinAlias,
4482
                        )
4483

4484
                        const existJoin = this.joins.find(
615✔
4485
                            (join) => join.alias === joinAlias,
271✔
4486
                        )
4487
                        if (!existJoin) {
615✔
4488
                            this.joins.push({
527✔
4489
                                type: "left",
4490
                                select: false,
4491
                                selection: undefined,
4492
                                alias: joinAlias,
4493
                                parentAlias: alias,
4494
                                relationMetadata: relation,
4495
                            })
4496
                        }
4497

4498
                        const condition = this.buildWhere(
615✔
4499
                            where[key],
4500
                            relation.inverseEntityMetadata,
4501
                            joinAlias,
4502
                        )
4503
                        if (condition) {
615✔
4504
                            andConditions.push(condition)
528✔
4505
                            // parameterIndex = Object.keys(this.expressionMap.nativeParameters).length;
4506
                        }
4507
                    }
4508
                }
4509
            }
4510
            condition = andConditions.length
50,636✔
4511
                ? "(" + andConditions.join(") AND (") + ")"
4512
                : andConditions.join(" AND ")
4513
        }
4514
        return condition.length ? "(" + condition + ")" : condition
50,842✔
4515
    }
4516
}
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