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

typeorm / typeorm / 14796576772

02 May 2025 01:52PM UTC coverage: 45.367% (-30.9%) from 76.309%
14796576772

Pull #11434

github

web-flow
Merge ec4ce2d00 into fadad1a74
Pull Request #11434: feat: release PR releases using pkg.pr.new

5216 of 12761 branches covered (40.87%)

Branch coverage included in aggregate %.

11439 of 23951 relevant lines covered (47.76%)

15712.55 hits per line

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

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

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

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

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

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

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

104
    setFindOptions(findOptions: FindManyOptions<Entity>) {
105
        this.findOptions = findOptions
19,816✔
106
        this.applyFindOptions()
19,816✔
107
        return this
19,768✔
108
    }
109

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

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

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

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

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

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

176
        return this
68,003✔
177
    }
178

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

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

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

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

209
        if (Array.isArray(selection)) {
287,983✔
210
            this.expressionMap.selects = this.expressionMap.selects.concat(
1,507✔
211
                selection.map((selection) => ({ selection: selection })),
2,057✔
212
            )
213
        } else if (typeof selection === "function") {
286,476✔
214
            const subQueryBuilder = selection(this.subQuery())
4✔
215
            this.setParameters(subQueryBuilder.getParameters())
4✔
216
            this.expressionMap.selects.push({
4✔
217
                selection: subQueryBuilder.getQuery(),
218
                aliasName: selectionAliasName,
219
            })
220
        } else if (selection) {
286,472✔
221
            this.expressionMap.selects.push({
286,472✔
222
                selection: selection,
223
                aliasName: selectionAliasName,
224
            })
225
        }
226

227
        return this
287,983✔
228
    }
229

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1063
        relationIdAttribute.queryBuilderFactory = queryBuilderFactory
4,556✔
1064
        this.expressionMap.relationIdAttributes.push(relationIdAttribute)
4,556✔
1065

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

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

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

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

1130
            this.loadRelationIdAndMap(
4,056✔
1131
                this.expressionMap.mainAlias!.name +
1132
                    "." +
1133
                    relation.propertyPath,
1134
                this.expressionMap.mainAlias!.name +
1135
                    "." +
1136
                    relation.propertyPath,
1137
                options,
1138
            )
1139
        })
1140
        return this
16,489✔
1141
    }
1142

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1348
        return this
2,068✔
1349
    }
1350

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

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

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

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

1401
        if (sort) {
3,238✔
1402
            if (typeof sort === "object") {
1,980✔
1403
                this.expressionMap.orderBys = sort as OrderByCondition
1,038✔
1404
            } else {
1405
                if (nulls) {
942!
1406
                    this.expressionMap.orderBys = {
×
1407
                        [sort as string]: { order, nulls },
1408
                    }
1409
                } else {
1410
                    this.expressionMap.orderBys = { [sort as string]: order }
942✔
1411
                }
1412
            }
1413
        } else {
1414
            this.expressionMap.orderBys = {}
1,258✔
1415
        }
1416
        return this
3,238✔
1417
    }
1418

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

1440
        if (nulls) {
2,488✔
1441
            this.expressionMap.orderBys[sort] = { order, nulls }
8✔
1442
        } else {
1443
            this.expressionMap.orderBys[sort] = order
2,480✔
1444
        }
1445
        return this
2,488✔
1446
    }
1447

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

1464
        return this
1,318✔
1465
    }
1466

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

1483
        return this
1,282✔
1484
    }
1485

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

1499
        return this
2,454✔
1500
    }
1501

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

1515
        return this
328✔
1516
    }
1517

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

1526
        return this
×
1527
    }
1528

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

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

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

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

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

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

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

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

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

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

1627
            const results = await this.loadRawResults(queryRunner)
3,545✔
1628

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

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

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

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

1673
            // close transaction if we started it
1674
            if (transactionStartedByUs) {
23,975✔
1675
                await queryRunner.commitTransaction()
4✔
1676
            }
1677

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

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

1701
        if (
3,123✔
1702
            result &&
6,094✔
1703
            this.expressionMap.lockMode === "optimistic" &&
1704
            this.expressionMap.lockVersion
1705
        ) {
1706
            const metadata = this.expressionMap.mainAlias!.metadata
24✔
1707

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

1732
        if (result === undefined) {
3,115✔
1733
            return null
176✔
1734
        }
1735
        return result
2,939✔
1736
    }
1737

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

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

1751
        return entity
1✔
1752
    }
1753

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

1761
        const results = await this.getRawAndEntities()
20,474✔
1762
        return results.entities
20,474✔
1763
    }
1764

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1970
    /**
1971
     * Enables or disables query result caching.
1972
     */
1973
    cache(
1974
        enabledOrMillisecondsOrId: boolean | number | string,
1975
        maybeMilliseconds?: number,
1976
    ): this {
1977
        if (typeof enabledOrMillisecondsOrId === "boolean") {
1,322✔
1978
            this.expressionMap.cache = enabledOrMillisecondsOrId
220✔
1979
        } else if (typeof enabledOrMillisecondsOrId === "number") {
1,102✔
1980
            this.expressionMap.cache = true
16✔
1981
            this.expressionMap.cacheDuration = enabledOrMillisecondsOrId
16✔
1982
        } else if (
1,086✔
1983
            typeof enabledOrMillisecondsOrId === "string" ||
2,108✔
1984
            typeof enabledOrMillisecondsOrId === "number"
1985
        ) {
1986
            this.expressionMap.cache = true
64✔
1987
            this.expressionMap.cacheId = enabledOrMillisecondsOrId
64✔
1988
        }
1989

1990
        if (maybeMilliseconds) {
1,322✔
1991
            this.expressionMap.cacheDuration = maybeMilliseconds
64✔
1992
        }
1993

1994
        return this
1,322✔
1995
    }
1996

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

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

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

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

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

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

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

2104
        const allSelects: SelectQuery[] = []
69,548✔
2105
        const excludedSelects: SelectQuery[] = []
69,548✔
2106

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

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

2154
        // add all other selects
2155
        this.expressionMap.selects
69,548✔
2156
            .filter((select) => excludedSelects.indexOf(select) === -1)
358,033✔
2157
            .forEach((select) =>
2158
                allSelects.push({
1,989✔
2159
                    selection: this.replacePropertyNames(select.selection),
2160
                    aliasName: select.aliasName,
2161
                }),
2162
            )
2163

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

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

2175
        // create a selection query
2176
        const froms = this.expressionMap.aliases
69,548✔
2177
            .filter(
2178
                (alias) =>
2179
                    alias.type === "from" &&
639,590✔
2180
                    (alias.tablePath || alias.subQuery),
2181
            )
2182
            .map((alias) => {
2183
                if (alias.subQuery)
69,560✔
2184
                    return alias.subQuery + " " + this.escape(alias.name)
1,077✔
2185

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

2193
        const select = this.createSelectDistinctExpression()
69,548✔
2194
        const selection = allSelects
69,548✔
2195
            .map(
2196
                (select) =>
2197
                    select.selection +
749,529✔
2198
                    (select.aliasName
749,529✔
2199
                        ? " AS " + this.escape(select.aliasName)
2200
                        : ""),
2201
            )
2202
            .join(", ")
2203

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

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

2222
        let select = "SELECT "
69,548✔
2223

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

2230
        if (
69,548!
2231
            DriverUtils.isPostgresFamily(driver) &&
69,548!
2232
            selectDistinctOn.length > 0
2233
        ) {
2234
            const selectDistinctOnMap = selectDistinctOn
×
2235
                .map((on) => this.replacePropertyNames(on))
×
2236
                .join(", ")
2237

2238
            select = `SELECT DISTINCT ON (${selectDistinctOnMap}) `
×
2239
        } else if (selectDistinct) {
69,548✔
2240
            select = "SELECT DISTINCT "
9✔
2241
        }
2242

2243
        return select
69,548✔
2244
    }
2245

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

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

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

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

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

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

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

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

2373
                const junctionAlias = joinAttr.junctionAlias
281,258✔
2374
                let junctionCondition = "",
281,258✔
2375
                    destinationCondition = ""
281,258✔
2376

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

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

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

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

2466
        return joins.join(" ")
69,548✔
2467
    }
2468

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

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

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

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

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

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

2570
            if (limit && offset)
×
2571
                return (
×
2572
                    prefix +
2573
                    " OFFSET " +
2574
                    offset +
2575
                    " ROWS FETCH NEXT " +
2576
                    limit +
2577
                    " ROWS ONLY"
2578
                )
2579
            if (limit)
×
2580
                return (
×
2581
                    prefix + " OFFSET 0 ROWS FETCH NEXT " + limit + " ROWS ONLY"
2582
                )
2583
            if (offset) return prefix + " OFFSET " + offset + " ROWS"
×
2584
        } else if (
69,548!
2585
            DriverUtils.isMySQLFamily(this.connection.driver) ||
278,192✔
2586
            this.connection.driver.options.type === "aurora-mysql" ||
2587
            this.connection.driver.options.type === "sap" ||
2588
            this.connection.driver.options.type === "spanner"
2589
        ) {
2590
            if (limit && offset) return " LIMIT " + limit + " OFFSET " + offset
×
2591
            if (limit) return " LIMIT " + limit
×
2592
            if (offset) throw new OffsetWithoutLimitNotSupportedError()
×
2593
        } else if (DriverUtils.isSQLiteFamily(this.connection.driver)) {
69,548✔
2594
            if (limit && offset) return " LIMIT " + limit + " OFFSET " + offset
52,589✔
2595
            if (limit) return " LIMIT " + limit
52,526✔
2596
            if (offset) return " LIMIT -1 OFFSET " + offset
50,852✔
2597
        } else if (this.connection.driver.options.type === "oracle") {
16,959!
2598
            if (limit && offset)
16,959✔
2599
                return (
21✔
2600
                    " OFFSET " +
2601
                    offset +
2602
                    " ROWS FETCH NEXT " +
2603
                    limit +
2604
                    " ROWS ONLY"
2605
                )
2606
            if (limit) return " FETCH NEXT " + limit + " ROWS ONLY"
16,938✔
2607
            if (offset) return " OFFSET " + offset + " ROWS"
16,420✔
2608
        } else {
2609
            if (limit && offset) return " LIMIT " + limit + " OFFSET " + offset
×
2610
            if (limit) return " LIMIT " + limit
×
2611
            if (offset) return " OFFSET " + offset
×
2612
        }
2613

2614
        return ""
67,268✔
2615
    }
2616

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

2637
        return ""
636,802✔
2638
    }
2639

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

2646
        let lockTablesClause = ""
69,548✔
2647

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

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

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

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

2788
    protected buildEscapedEntityColumnSelects(
2789
        aliasName: string,
2790
        metadata: EntityMetadata,
2791
    ): SelectQuery[] {
2792
        const hasMainAlias = this.expressionMap.selects.some(
354,124✔
2793
            (select) => select.selection === aliasName,
1,486,968✔
2794
        )
2795

2796
        const columns: ColumnMetadata[] = []
354,124✔
2797
        if (hasMainAlias) {
354,124✔
2798
            columns.push(
347,777✔
2799
                ...metadata.columns.filter(
2800
                    (column) => column.isSelect === true,
738,958✔
2801
                ),
2802
            )
2803
        }
2804
        columns.push(
354,124✔
2805
            ...metadata.columns.filter((column) => {
2806
                return this.expressionMap.selects.some(
761,694✔
2807
                    (select) =>
2808
                        select.selection ===
5,300,327✔
2809
                        aliasName + "." + column.propertyPath,
2810
                )
2811
            }),
2812
        )
2813

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

2821
        const nonSelectedPrimaryColumns = this.expressionMap.queryEntity
352,100✔
2822
            ? metadata.primaryColumns.filter(
2823
                  (primaryColumn) => columns.indexOf(primaryColumn) === -1,
27,849✔
2824
              )
2825
            : []
2826
        const allColumns = [...columns, ...nonSelectedPrimaryColumns]
352,100✔
2827
        const finalSelects: SelectQuery[] = []
352,100✔
2828

2829
        const escapedAliasName = this.escape(aliasName)
352,100✔
2830
        allColumns.forEach((column) => {
352,100✔
2831
            let selectionPath =
2832
                escapedAliasName + "." + this.escape(column.databaseName)
747,218✔
2833

2834
            if (column.isVirtualProperty && column.query) {
747,218✔
2835
                selectionPath = `(${column.query(escapedAliasName)})`
60✔
2836
            }
2837

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

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

2865
            const selections = this.expressionMap.selects.filter(
747,218✔
2866
                (select) =>
2867
                    select.selection === aliasName + "." + column.propertyPath,
5,280,622✔
2868
            )
2869
            if (selections.length) {
747,218✔
2870
                selections.forEach((selection) => {
8,268✔
2871
                    finalSelects.push({
8,268✔
2872
                        selection: selectionPath,
2873
                        aliasName: selection.aliasName
8,268✔
2874
                            ? selection.aliasName
2875
                            : DriverUtils.buildAlias(
2876
                                  this.connection.driver,
2877
                                  undefined,
2878
                                  aliasName,
2879
                                  column.databaseName,
2880
                              ),
2881
                        // todo: need to keep in mind that custom selection.aliasName breaks hydrator. fix it later!
2882
                        virtual: selection.virtual,
2883
                    })
2884
                })
2885
            } else {
2886
                finalSelects.push({
738,950✔
2887
                    selection: selectionPath,
2888
                    aliasName: DriverUtils.buildAlias(
2889
                        this.connection.driver,
2890
                        undefined,
2891
                        aliasName,
2892
                        column.databaseName,
2893
                    ),
2894
                    // todo: need to keep in mind that custom selection.aliasName breaks hydrator. fix it later!
2895
                    virtual: hasMainAlias,
2896
                })
2897
            }
2898
        })
2899
        return finalSelects
352,100✔
2900
    }
2901

2902
    protected findEntityColumnSelects(
2903
        aliasName: string,
2904
        metadata: EntityMetadata,
2905
    ): SelectQuery[] {
2906
        const mainSelect = this.expressionMap.selects.find(
354,124✔
2907
            (select) => select.selection === aliasName,
1,486,968✔
2908
        )
2909
        if (mainSelect) return [mainSelect]
354,124✔
2910

2911
        return this.expressionMap.selects.filter((select) => {
6,347✔
2912
            return metadata.columns.some(
12,994✔
2913
                (column) =>
2914
                    select.selection === aliasName + "." + column.propertyPath,
34,159✔
2915
            )
2916
        })
2917
    }
2918

2919
    private computeCountExpression() {
2920
        const mainAlias = this.expressionMap.mainAlias!.name // todo: will this work with "fromTableName"?
196✔
2921
        const metadata = this.expressionMap.mainAlias!.metadata
196✔
2922

2923
        const primaryColumns = metadata.primaryColumns
196✔
2924
        const distinctAlias = this.escape(mainAlias)
196✔
2925

2926
        // If we aren't doing anything that will create a join, we can use a simpler `COUNT` instead
2927
        // so we prevent poor query patterns in the most likely cases
2928
        if (
196✔
2929
            this.expressionMap.joinAttributes.length === 0 &&
556✔
2930
            this.expressionMap.relationIdAttributes.length === 0 &&
2931
            this.expressionMap.relationCountAttributes.length === 0
2932
        ) {
2933
            return "COUNT(1)"
180✔
2934
        }
2935

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

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

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

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

2977
            const columnsExpression = primaryColumns
×
2978
                .map(
2979
                    (primaryColumn) =>
2980
                        `${distinctAlias}.${this.escape(
×
2981
                            primaryColumn.databaseName,
2982
                        )}`,
2983
                )
2984
                .join(", '|;|', ")
2985

2986
            if (primaryColumns.length === 1) {
×
2987
                return `COUNT(DISTINCT(${columnsExpression}))`
×
2988
            }
2989

2990
            return `COUNT(DISTINCT(CONCAT(${columnsExpression})))`
×
2991
        }
2992

2993
        if (this.connection.driver.options.type === "spanner") {
16!
2994
            // spanner also has gotta be different from everyone else.
2995
            // they do not support concatenation of different column types without casting them to string
2996

2997
            if (primaryColumns.length === 1) {
×
2998
                return `COUNT(DISTINCT(${distinctAlias}.${this.escape(
×
2999
                    primaryColumns[0].databaseName,
3000
                )}))`
3001
            }
3002

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

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

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

3021
        return (
16✔
3022
            `COUNT(DISTINCT(` +
3023
            primaryColumns
3024
                .map((c) => `${distinctAlias}.${this.escape(c.databaseName)}`)
28✔
3025
                .join(" || '|;|' || ") +
3026
            "))"
3027
        )
3028
    }
3029

3030
    protected async executeCountQuery(
3031
        queryRunner: QueryRunner,
3032
    ): Promise<number> {
3033
        const countSql = this.computeCountExpression()
196✔
3034

3035
        const results = await this.clone()
196✔
3036
            .orderBy()
3037
            .groupBy()
3038
            .offset(undefined)
3039
            .limit(undefined)
3040
            .skip(undefined)
3041
            .take(undefined)
3042
            .select(countSql, "cnt")
3043
            .setOption("disable-global-order")
3044
            .loadRawResults(queryRunner)
3045

3046
        if (!results || !results[0] || !results[0]["cnt"]) return 0
196✔
3047

3048
        return parseInt(results[0]["cnt"])
188✔
3049
    }
3050

3051
    protected async executeExistsQuery(
3052
        queryRunner: QueryRunner,
3053
    ): Promise<boolean> {
3054
        const results = await this.connection
28✔
3055
            .createQueryBuilder()
3056
            .fromDummy()
3057
            .select("1", "row_exists")
3058
            .whereExists(this)
3059
            .limit(1)
3060
            .loadRawResults(queryRunner)
3061

3062
        return results.length > 0
28✔
3063
    }
3064

3065
    protected applyFindOptions() {
3066
        // todo: convert relations: string[] to object map to simplify code
3067
        // todo: same with selects
3068

3069
        if (this.expressionMap.mainAlias!.metadata) {
19,816✔
3070
            if (this.findOptions.relationLoadStrategy) {
19,816!
3071
                this.expressionMap.relationLoadStrategy =
×
3072
                    this.findOptions.relationLoadStrategy
3073
            }
3074

3075
            if (this.findOptions.comment) {
19,816✔
3076
                this.comment(this.findOptions.comment)
4✔
3077
            }
3078

3079
            if (this.findOptions.withDeleted) {
19,816✔
3080
                this.withDeleted()
16,537✔
3081
            }
3082

3083
            if (this.findOptions.select) {
19,816✔
3084
                const select = Array.isArray(this.findOptions.select)
103!
3085
                    ? OrmUtils.propertyPathsToTruthyObject(
3086
                          this.findOptions.select as string[],
3087
                      )
3088
                    : this.findOptions.select
3089

3090
                this.buildSelect(
103✔
3091
                    select,
3092
                    this.expressionMap.mainAlias!.metadata,
3093
                    this.expressionMap.mainAlias!.name,
3094
                )
3095
            }
3096

3097
            if (this.selects.length) {
19,812✔
3098
                this.select(this.selects)
91✔
3099
            }
3100

3101
            this.selects = []
19,812✔
3102
            if (this.findOptions.relations) {
19,812✔
3103
                const relations = Array.isArray(this.findOptions.relations)
1,010✔
3104
                    ? OrmUtils.propertyPathsToTruthyObject(
3105
                          this.findOptions.relations,
3106
                      )
3107
                    : this.findOptions.relations
3108

3109
                this.buildRelations(
1,010✔
3110
                    relations,
3111
                    typeof this.findOptions.select === "object"
1,010✔
3112
                        ? (this.findOptions.select as FindOptionsSelect<any>)
3113
                        : undefined,
3114
                    this.expressionMap.mainAlias!.metadata,
3115
                    this.expressionMap.mainAlias!.name,
3116
                )
3117
                if (
986✔
3118
                    this.findOptions.loadEagerRelations !== false &&
1,972✔
3119
                    this.expressionMap.relationLoadStrategy === "join"
3120
                ) {
3121
                    this.buildEagerRelations(
986✔
3122
                        relations,
3123
                        typeof this.findOptions.select === "object"
986✔
3124
                            ? (this.findOptions
3125
                                  .select as FindOptionsSelect<any>)
3126
                            : undefined,
3127
                        this.expressionMap.mainAlias!.metadata,
3128
                        this.expressionMap.mainAlias!.name,
3129
                    )
3130
                }
3131
            }
3132
            if (this.selects.length) {
19,788✔
3133
                this.addSelect(this.selects)
32✔
3134
            }
3135

3136
            if (this.findOptions.where) {
19,788✔
3137
                this.conditions = this.buildWhere(
2,573✔
3138
                    this.findOptions.where,
3139
                    this.expressionMap.mainAlias!.metadata,
3140
                    this.expressionMap.mainAlias!.name,
3141
                )
3142

3143
                if (this.conditions.length)
2,565✔
3144
                    this.andWhere(
2,481✔
3145
                        this.conditions.substr(0, 1) !== "("
2,481!
3146
                            ? "(" + this.conditions + ")"
3147
                            : this.conditions,
3148
                    ) // temporary and where and braces
3149
            }
3150

3151
            if (this.findOptions.order) {
19,780✔
3152
                this.buildOrder(
350✔
3153
                    this.findOptions.order,
3154
                    this.expressionMap.mainAlias!.metadata,
3155
                    this.expressionMap.mainAlias!.name,
3156
                )
3157
            }
3158

3159
            // apply joins
3160
            if (this.joins.length) {
19,776✔
3161
                this.joins.forEach((join) => {
1,058✔
3162
                    if (join.select && !join.selection) {
1,329✔
3163
                        // if (join.selection) {
3164
                        //
3165
                        // } else {
3166
                        if (join.type === "inner") {
1,185!
3167
                            this.innerJoinAndSelect(
×
3168
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3169
                                join.alias,
3170
                            )
3171
                        } else {
3172
                            this.leftJoinAndSelect(
1,185✔
3173
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3174
                                join.alias,
3175
                            )
3176
                        }
3177
                        // }
3178
                    } else {
3179
                        if (join.type === "inner") {
144!
3180
                            this.innerJoin(
×
3181
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3182
                                join.alias,
3183
                            )
3184
                        } else {
3185
                            this.leftJoin(
144✔
3186
                                `${join.parentAlias}.${join.relationMetadata.propertyPath}`,
3187
                                join.alias,
3188
                            )
3189
                        }
3190
                    }
3191

3192
                    // if (join.select) {
3193
                    //     if (this.findOptions.loadEagerRelations !== false) {
3194
                    //         FindOptionsUtils.joinEagerRelations(
3195
                    //             this,
3196
                    //             join.alias,
3197
                    //             join.relationMetadata.inverseEntityMetadata
3198
                    //         );
3199
                    //     }
3200
                    // }
3201
                })
3202
            }
3203

3204
            // if (this.conditions.length) {
3205
            //     this.where(this.conditions.join(" AND "));
3206
            // }
3207

3208
            // apply offset
3209
            if (this.findOptions.skip !== undefined) {
19,776✔
3210
                // if (this.findOptions.options && this.findOptions.options.pagination === false) {
3211
                //     this.offset(this.findOptions.skip);
3212
                // } else {
3213
                this.skip(this.findOptions.skip)
24✔
3214
                // }
3215
            }
3216

3217
            // apply limit
3218
            if (this.findOptions.take !== undefined) {
19,772✔
3219
                // if (this.findOptions.options && this.findOptions.options.pagination === false) {
3220
                //     this.limit(this.findOptions.take);
3221
                // } else {
3222
                this.take(this.findOptions.take)
2,132✔
3223
                // }
3224
            }
3225

3226
            // apply caching options
3227
            if (typeof this.findOptions.cache === "number") {
19,768✔
3228
                this.cache(this.findOptions.cache)
8✔
3229
            } else if (typeof this.findOptions.cache === "boolean") {
19,760✔
3230
                this.cache(this.findOptions.cache)
12✔
3231
            } else if (typeof this.findOptions.cache === "object") {
19,748✔
3232
                this.cache(
20✔
3233
                    this.findOptions.cache.id,
3234
                    this.findOptions.cache.milliseconds,
3235
                )
3236
            }
3237

3238
            if (this.findOptions.join) {
19,768✔
3239
                if (this.findOptions.join.leftJoin)
92!
3240
                    Object.keys(this.findOptions.join.leftJoin).forEach(
×
3241
                        (key) => {
3242
                            this.leftJoin(
×
3243
                                this.findOptions.join!.leftJoin![key],
3244
                                key,
3245
                            )
3246
                        },
3247
                    )
3248

3249
                if (this.findOptions.join.innerJoin)
92!
3250
                    Object.keys(this.findOptions.join.innerJoin).forEach(
×
3251
                        (key) => {
3252
                            this.innerJoin(
×
3253
                                this.findOptions.join!.innerJoin![key],
3254
                                key,
3255
                            )
3256
                        },
3257
                    )
3258

3259
                if (this.findOptions.join.leftJoinAndSelect)
92✔
3260
                    Object.keys(
56✔
3261
                        this.findOptions.join.leftJoinAndSelect,
3262
                    ).forEach((key) => {
3263
                        this.leftJoinAndSelect(
84✔
3264
                            this.findOptions.join!.leftJoinAndSelect![key],
3265
                            key,
3266
                        )
3267
                    })
3268

3269
                if (this.findOptions.join.innerJoinAndSelect)
92✔
3270
                    Object.keys(
36✔
3271
                        this.findOptions.join.innerJoinAndSelect,
3272
                    ).forEach((key) => {
3273
                        this.innerJoinAndSelect(
36✔
3274
                            this.findOptions.join!.innerJoinAndSelect![key],
3275
                            key,
3276
                        )
3277
                    })
3278
            }
3279

3280
            if (this.findOptions.lock) {
19,768✔
3281
                if (this.findOptions.lock.mode === "optimistic") {
48✔
3282
                    this.setLock(
36✔
3283
                        this.findOptions.lock.mode,
3284
                        this.findOptions.lock.version,
3285
                    )
3286
                } else if (
12✔
3287
                    this.findOptions.lock.mode === "pessimistic_read" ||
18!
3288
                    this.findOptions.lock.mode === "pessimistic_write" ||
3289
                    this.findOptions.lock.mode === "dirty_read" ||
3290
                    this.findOptions.lock.mode ===
3291
                        "pessimistic_partial_write" ||
3292
                    this.findOptions.lock.mode ===
3293
                        "pessimistic_write_or_fail" ||
3294
                    this.findOptions.lock.mode === "for_no_key_update" ||
3295
                    this.findOptions.lock.mode === "for_key_share"
3296
                ) {
3297
                    const tableNames = this.findOptions.lock.tables
12!
3298
                        ? this.findOptions.lock.tables.map((table) => {
3299
                              const tableAlias =
3300
                                  this.expressionMap.aliases.find((alias) => {
×
3301
                                      return (
×
3302
                                          alias.metadata
3303
                                              .tableNameWithoutPrefix === table
3304
                                      )
3305
                                  })
3306
                              if (!tableAlias) {
×
3307
                                  throw new TypeORMError(
×
3308
                                      `"${table}" is not part of this query`,
3309
                                  )
3310
                              }
3311
                              return this.escape(tableAlias.name)
×
3312
                          })
3313
                        : undefined
3314
                    this.setLock(
12✔
3315
                        this.findOptions.lock.mode,
3316
                        undefined,
3317
                        tableNames,
3318
                    )
3319

3320
                    if (this.findOptions.lock.onLocked) {
12!
3321
                        this.setOnLocked(this.findOptions.lock.onLocked)
×
3322
                    }
3323
                }
3324
            }
3325

3326
            if (this.findOptions.loadRelationIds === true) {
19,768✔
3327
                this.loadAllRelationIds()
4✔
3328
            } else if (typeof this.findOptions.loadRelationIds === "object") {
19,764✔
3329
                this.loadAllRelationIds(this.findOptions.loadRelationIds as any)
16,485✔
3330
            }
3331

3332
            if (this.findOptions.loadEagerRelations !== false) {
19,768✔
3333
                FindOptionsUtils.joinEagerRelations(
3,275✔
3334
                    this,
3335
                    this.expressionMap.mainAlias!.name,
3336
                    this.expressionMap.mainAlias!.metadata,
3337
                )
3338
            }
3339

3340
            if (this.findOptions.transaction === true) {
19,768✔
3341
                this.expressionMap.useTransaction = true
4✔
3342
            }
3343

3344
            // if (this.orderBys.length) {
3345
            //     this.orderBys.forEach(orderBy => {
3346
            //         this.addOrderBy(orderBy.alias, orderBy.direction, orderBy.nulls);
3347
            //     });
3348
            // }
3349

3350
            // todo
3351
            // if (this.options.options && this.options.options.eagerRelations) {
3352
            //     this.queryBuilder
3353
            // }
3354

3355
            // todo
3356
            // if (this.findOptions.options && this.findOptions.listeners === false) {
3357
            //     this.callListeners(false);
3358
            // }
3359
        }
3360
    }
3361

3362
    public concatRelationMetadata(relationMetadata: RelationMetadata) {
3363
        this.relationMetadatas.push(relationMetadata)
×
3364
    }
3365

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

3377
        if (
24,083✔
3378
            (this.expressionMap.lockMode === "pessimistic_read" ||
144,425✔
3379
                this.expressionMap.lockMode === "pessimistic_write" ||
3380
                this.expressionMap.lockMode === "pessimistic_partial_write" ||
3381
                this.expressionMap.lockMode === "pessimistic_write_or_fail" ||
3382
                this.expressionMap.lockMode === "for_no_key_update" ||
3383
                this.expressionMap.lockMode === "for_key_share") &&
3384
            !queryRunner.isTransactionActive
3385
        )
3386
            throw new PessimisticLockTransactionRequiredError()
4✔
3387

3388
        if (this.expressionMap.lockMode === "optimistic") {
24,079✔
3389
            const metadata = this.expressionMap.mainAlias.metadata
36✔
3390
            if (!metadata.versionColumn && !metadata.updateDateColumn)
36✔
3391
                throw new NoVersionOrUpdateDateColumnError(metadata.name)
4✔
3392
        }
3393

3394
        const relationIdLoader = new RelationIdLoader(
24,075✔
3395
            this.connection,
3396
            queryRunner,
3397
            this.expressionMap.relationIdAttributes,
3398
        )
3399
        const relationCountLoader = new RelationCountLoader(
24,075✔
3400
            this.connection,
3401
            queryRunner,
3402
            this.expressionMap.relationCountAttributes,
3403
        )
3404
        const relationIdMetadataTransformer =
3405
            new RelationIdMetadataToAttributeTransformer(this.expressionMap)
24,075✔
3406
        relationIdMetadataTransformer.transform()
24,075✔
3407
        const relationCountMetadataTransformer =
3408
            new RelationCountMetadataToAttributeTransformer(this.expressionMap)
24,075✔
3409
        relationCountMetadataTransformer.transform()
24,075✔
3410

3411
        let rawResults: any[] = [],
24,075✔
3412
            entities: any[] = []
24,075✔
3413

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

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

3444
                    const alias = DriverUtils.buildAlias(
1,046✔
3445
                        this.connection.driver,
3446
                        undefined,
3447
                        "ids_" + mainAliasName,
3448
                        primaryColumn.databaseName,
3449
                    )
3450

3451
                    return `${distinctAlias}.${columnAlias} AS ${this.escape(
1,046✔
3452
                        alias,
3453
                    )}`
3454
                },
3455
            )
3456

3457
            const originalQuery = this.clone()
1,034✔
3458

3459
            // preserve original timeTravel value since we set it to "false" in subquery
3460
            const originalQueryTimeTravel =
3461
                originalQuery.expressionMap.timeTravel
1,034✔
3462

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

3490
            if (rawResults.length > 0) {
1,034✔
3491
                let condition = ""
1,022✔
3492
                const parameters: ObjectLiteral = {}
1,022✔
3493
                if (metadata.hasMultiplePrimaryKeys) {
1,022✔
3494
                    condition = rawResults
12✔
3495
                        .map((result, index) => {
3496
                            return metadata.primaryColumns
64✔
3497
                                .map((primaryColumn) => {
3498
                                    const paramKey = `orm_distinct_ids_${index}_${primaryColumn.databaseName}`
128✔
3499
                                    const paramKeyResult =
3500
                                        DriverUtils.buildAlias(
128✔
3501
                                            this.connection.driver,
3502
                                            undefined,
3503
                                            "ids_" + mainAliasName,
3504
                                            primaryColumn.databaseName,
3505
                                        )
3506
                                    parameters[paramKey] =
128✔
3507
                                        result[paramKeyResult]
3508
                                    return `${mainAliasName}.${primaryColumn.propertyPath}=:${paramKey}`
128✔
3509
                                })
3510
                                .join(" AND ")
3511
                        })
3512
                        .join(" OR ")
3513
                } else {
3514
                    const alias = DriverUtils.buildAlias(
1,010✔
3515
                        this.connection.driver,
3516
                        undefined,
3517
                        "ids_" + mainAliasName,
3518
                        metadata.primaryColumns[0].databaseName,
3519
                    )
3520

3521
                    const ids = rawResults.map((result) => result[alias])
3,076✔
3522
                    const areAllNumbers = ids.every(
1,010✔
3523
                        (id: any) => typeof id === "number",
3,076✔
3524
                    )
3525
                    if (areAllNumbers) {
1,010✔
3526
                        // fixes #190. if all numbers then its safe to perform query without parameter
3527
                        condition = `${mainAliasName}.${
998✔
3528
                            metadata.primaryColumns[0].propertyPath
3529
                        } IN (${ids.join(", ")})`
3530
                    } else {
3531
                        parameters["orm_distinct_ids"] = ids
12✔
3532
                        condition =
12✔
3533
                            mainAliasName +
3534
                            "." +
3535
                            metadata.primaryColumns[0].propertyPath +
3536
                            " IN (:...orm_distinct_ids)"
3537
                    }
3538
                }
3539
                rawResults = await this.clone()
1,022✔
3540
                    .mergeExpressionMap({
3541
                        extraAppendedAndWhereCondition: condition,
3542
                    })
3543
                    .setParameters(parameters)
3544
                    .loadRawResults(queryRunner)
3545
            }
3546
        } else {
3547
            rawResults = await this.loadRawResults(queryRunner)
23,041✔
3548
        }
3549

3550
        if (rawResults.length > 0) {
24,055✔
3551
            // transform raw results into entities
3552
            const rawRelationIdResults = await relationIdLoader.load(rawResults)
8,295✔
3553
            const rawRelationCountResults = await relationCountLoader.load(
8,295✔
3554
                rawResults,
3555
            )
3556
            const transformer = new RawSqlResultsToEntityTransformer(
8,295✔
3557
                this.expressionMap,
3558
                this.connection.driver,
3559
                rawRelationIdResults,
3560
                rawRelationCountResults,
3561
                this.queryRunner,
3562
            )
3563
            entities = transformer.transform(
8,295✔
3564
                rawResults,
3565
                this.expressionMap.mainAlias!,
3566
            )
3567

3568
            // broadcast all "after load" events
3569
            if (
8,295✔
3570
                this.expressionMap.callListeners === true &&
16,590✔
3571
                this.expressionMap.mainAlias.hasMetadata
3572
            ) {
3573
                await queryRunner.broadcaster.broadcast(
8,295✔
3574
                    "Load",
3575
                    this.expressionMap.mainAlias.metadata,
3576
                    entities,
3577
                )
3578
            }
3579
        }
3580

3581
        if (this.expressionMap.relationLoadStrategy === "query") {
24,055!
3582
            const queryStrategyRelationIdLoader =
3583
                new QueryStrategyRelationIdLoader(this.connection, queryRunner)
×
3584

3585
            await Promise.all(
×
3586
                this.relationMetadatas.map(async (relation) => {
3587
                    const relationTarget = relation.inverseEntityMetadata.target
×
3588
                    const relationAlias =
3589
                        relation.inverseEntityMetadata.targetName
×
3590

3591
                    const select = Array.isArray(this.findOptions.select)
×
3592
                        ? OrmUtils.propertyPathsToTruthyObject(
3593
                              this.findOptions.select as string[],
3594
                          )
3595
                        : this.findOptions.select
3596
                    const relations = Array.isArray(this.findOptions.relations)
×
3597
                        ? OrmUtils.propertyPathsToTruthyObject(
3598
                              this.findOptions.relations,
3599
                          )
3600
                        : this.findOptions.relations
3601

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

3653
        return {
24,055✔
3654
            raw: rawResults,
3655
            entities: entities,
3656
        }
3657
    }
3658

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

3699
                    return ""
×
3700
                }
3701
            })
3702
            .join(", ")
3703

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

3744
        return [selectString, orderByObject]
1,034✔
3745
    }
3746

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

3801
        const results = await queryRunner.query(sql, parameters, true)
27,728✔
3802

3803
        if (
27,728✔
3804
            !cacheError &&
55,836✔
3805
            this.connection.queryResultCache &&
3806
            isCachingEnabled
3807
        ) {
3808
            try {
112✔
3809
                await this.connection.queryResultCache.storeInCache(
112✔
3810
                    {
3811
                        identifier: this.expressionMap.cacheId,
3812
                        query: queryId,
3813
                        time: Date.now(),
3814
                        duration:
3815
                            this.expressionMap.cacheDuration ||
232✔
3816
                            cacheOptions.duration ||
3817
                            1000,
3818
                        result: JSON.stringify(results.records),
3819
                    },
3820
                    savedQueryResultCacheOptions,
3821
                    queryRunner,
3822
                )
3823
            } catch (error) {
3824
                if (!cacheOptions.ignoreErrors) {
×
3825
                    throw error
×
3826
                }
3827
            }
3828
        }
3829

3830
        return results.records
27,728✔
3831
    }
3832

3833
    /**
3834
     * Merges into expression map given expression map properties.
3835
     */
3836
    protected mergeExpressionMap(
3837
        expressionMap: Partial<QueryExpressionMap>,
3838
    ): this {
3839
        ObjectUtils.assign(this.expressionMap, expressionMap)
1,022✔
3840
        return this
1,022✔
3841
    }
3842

3843
    /**
3844
     * Normalizes a give number - converts to int if possible.
3845
     */
3846
    protected normalizeNumber(num: any) {
3847
        if (typeof num === "number" || num === undefined || num === null)
5,398✔
3848
            return num
5,382✔
3849

3850
        return Number(num)
16✔
3851
    }
3852

3853
    /**
3854
     * Creates a query builder used to execute sql queries inside this query builder.
3855
     */
3856
    protected obtainQueryRunner() {
3857
        return (
27,776✔
3858
            this.queryRunner ||
34,068✔
3859
            this.connection.createQueryRunner(
3860
                this.connection.defaultReplicationModeForReads(),
3861
            )
3862
        )
3863
    }
3864

3865
    protected buildSelect(
3866
        select: FindOptionsSelect<any>,
3867
        metadata: EntityMetadata,
3868
        alias: string,
3869
        embedPrefix?: string,
3870
    ) {
3871
        for (const key in select) {
157✔
3872
            if (select[key] === undefined || select[key] === false) continue
284✔
3873

3874
            const propertyPath = embedPrefix ? embedPrefix + "." + key : key
272✔
3875
            const column =
3876
                metadata.findColumnWithPropertyPathStrict(propertyPath)
272✔
3877
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
272✔
3878
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
272✔
3879

3880
            if (!embed && !column && !relation)
272✔
3881
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
4✔
3882

3883
            if (column) {
268✔
3884
                this.selects.push(alias + "." + propertyPath)
206✔
3885
                // this.addSelect(alias + "." + propertyPath);
3886
            } else if (embed) {
62✔
3887
                this.buildSelect(
10✔
3888
                    select[key] as FindOptionsSelect<any>,
3889
                    metadata,
3890
                    alias,
3891
                    propertyPath,
3892
                )
3893

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

3911
    protected buildRelations(
3912
        relations: FindOptionsRelations<any>,
3913
        selection: FindOptionsSelect<any> | undefined,
3914
        metadata: EntityMetadata,
3915
        alias: string,
3916
        embedPrefix?: string,
3917
    ) {
3918
        if (!relations) return
1,156!
3919

3920
        Object.keys(relations).forEach((relationName) => {
1,156✔
3921
            const relationValue = (relations as any)[relationName]
1,249✔
3922
            const propertyPath = embedPrefix
1,249✔
3923
                ? embedPrefix + "." + relationName
3924
                : relationName
3925
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
1,249✔
3926
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
1,249✔
3927
            if (!embed && !relation)
1,249✔
3928
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
24✔
3929

3930
            if (embed) {
1,225✔
3931
                this.buildRelations(
20✔
3932
                    relationValue,
3933
                    typeof selection === "object"
20!
3934
                        ? OrmUtils.deepValue(selection, embed.propertyPath)
3935
                        : undefined,
3936
                    metadata,
3937
                    alias,
3938
                    propertyPath,
3939
                )
3940
            } else if (relation) {
1,205✔
3941
                let joinAlias = alias + "_" + propertyPath.replace(".", "_")
1,205✔
3942
                joinAlias = DriverUtils.buildAlias(
1,205✔
3943
                    this.connection.driver,
3944
                    { joiner: "__" },
3945
                    alias,
3946
                    joinAlias,
3947
                )
3948
                if (
1,205✔
3949
                    relationValue === true ||
1,331✔
3950
                    typeof relationValue === "object"
3951
                ) {
3952
                    if (this.expressionMap.relationLoadStrategy === "query") {
1,205!
3953
                        this.concatRelationMetadata(relation)
×
3954
                    } else {
3955
                        // join
3956
                        this.joins.push({
1,205✔
3957
                            type: "left",
3958
                            select: true,
3959
                            selection:
3960
                                selection &&
2,474✔
3961
                                typeof selection[relationName] === "object"
3962
                                    ? (selection[
3963
                                          relationName
3964
                                      ] as FindOptionsSelect<any>)
3965
                                    : undefined,
3966
                            alias: joinAlias,
3967
                            parentAlias: alias,
3968
                            relationMetadata: relation,
3969
                        })
3970

3971
                        if (
1,205✔
3972
                            selection &&
1,269✔
3973
                            typeof selection[relationName] === "object"
3974
                        ) {
3975
                            this.buildSelect(
44✔
3976
                                selection[
3977
                                    relationName
3978
                                ] as FindOptionsSelect<any>,
3979
                                relation.inverseEntityMetadata,
3980
                                joinAlias,
3981
                            )
3982
                        }
3983
                    }
3984
                }
3985

3986
                if (
1,205✔
3987
                    typeof relationValue === "object" &&
1,331✔
3988
                    this.expressionMap.relationLoadStrategy === "join"
3989
                ) {
3990
                    this.buildRelations(
126✔
3991
                        relationValue,
3992
                        typeof selection === "object"
126✔
3993
                            ? OrmUtils.deepValue(
3994
                                  selection,
3995
                                  relation.propertyPath,
3996
                              )
3997
                            : undefined,
3998
                        relation.inverseEntityMetadata,
3999
                        joinAlias,
4000
                        undefined,
4001
                    )
4002
                }
4003
            }
4004
        })
4005
    }
4006

4007
    protected buildEagerRelations(
4008
        relations: FindOptionsRelations<any>,
4009
        selection: FindOptionsSelect<any> | undefined,
4010
        metadata: EntityMetadata,
4011
        alias: string,
4012
        embedPrefix?: string,
4013
    ) {
4014
        if (!relations) return
1,120!
4015

4016
        Object.keys(relations).forEach((relationName) => {
1,120✔
4017
            const relationValue = (relations as any)[relationName]
1,205✔
4018
            const propertyPath = embedPrefix
1,205✔
4019
                ? embedPrefix + "." + relationName
4020
                : relationName
4021
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
1,205✔
4022
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
1,205✔
4023
            if (!embed && !relation)
1,205!
4024
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
×
4025

4026
            if (embed) {
1,205✔
4027
                this.buildEagerRelations(
16✔
4028
                    relationValue,
4029
                    typeof selection === "object"
16!
4030
                        ? OrmUtils.deepValue(selection, embed.propertyPath)
4031
                        : undefined,
4032
                    metadata,
4033
                    alias,
4034
                    propertyPath,
4035
                )
4036
            } else if (relation) {
1,189✔
4037
                let joinAlias = alias + "_" + propertyPath.replace(".", "_")
1,189✔
4038
                joinAlias = DriverUtils.buildAlias(
1,189✔
4039
                    this.connection.driver,
4040
                    { joiner: "__" },
4041
                    alias,
4042
                    joinAlias,
4043
                )
4044

4045
                if (
1,189✔
4046
                    relationValue === true ||
1,307✔
4047
                    typeof relationValue === "object"
4048
                ) {
4049
                    relation.inverseEntityMetadata.eagerRelations.forEach(
1,189✔
4050
                        (eagerRelation) => {
4051
                            let eagerRelationJoinAlias =
4052
                                joinAlias +
60✔
4053
                                "_" +
4054
                                eagerRelation.propertyPath.replace(".", "_")
4055
                            eagerRelationJoinAlias = DriverUtils.buildAlias(
60✔
4056
                                this.connection.driver,
4057
                                { joiner: "__" },
4058
                                joinAlias,
4059
                                eagerRelationJoinAlias,
4060
                            )
4061

4062
                            const existJoin = this.joins.find(
60✔
4063
                                (join) => join.alias === eagerRelationJoinAlias,
300✔
4064
                            )
4065
                            if (!existJoin) {
60✔
4066
                                this.joins.push({
40✔
4067
                                    type: "left",
4068
                                    select: true,
4069
                                    alias: eagerRelationJoinAlias,
4070
                                    parentAlias: joinAlias,
4071
                                    selection: undefined,
4072
                                    relationMetadata: eagerRelation,
4073
                                })
4074
                            }
4075

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

4092
                if (typeof relationValue === "object") {
1,189✔
4093
                    this.buildEagerRelations(
118✔
4094
                        relationValue,
4095
                        typeof selection === "object"
118✔
4096
                            ? OrmUtils.deepValue(
4097
                                  selection,
4098
                                  relation.propertyPath,
4099
                              )
4100
                            : undefined,
4101
                        relation.inverseEntityMetadata,
4102
                        joinAlias,
4103
                        undefined,
4104
                    )
4105
                }
4106
            }
4107
        })
4108
    }
4109

4110
    protected buildOrder(
4111
        order: FindOptionsOrder<any>,
4112
        metadata: EntityMetadata,
4113
        alias: string,
4114
        embedPrefix?: string,
4115
    ) {
4116
        for (const key in order) {
442✔
4117
            if (order[key] === undefined) continue
494!
4118

4119
            const propertyPath = embedPrefix ? embedPrefix + "." + key : key
494✔
4120
            const column =
4121
                metadata.findColumnWithPropertyPathStrict(propertyPath)
494✔
4122
            const embed = metadata.findEmbeddedWithPropertyPath(propertyPath)
494✔
4123
            const relation = metadata.findRelationWithPropertyPath(propertyPath)
494✔
4124

4125
            if (!embed && !column && !relation)
494✔
4126
                throw new EntityPropertyNotFoundError(propertyPath, metadata)
4✔
4127

4128
            if (column) {
490✔
4129
                let direction =
4130
                    typeof order[key] === "object"
398✔
4131
                        ? (order[key] as any).direction
4132
                        : order[key]
4133
                direction =
398✔
4134
                    direction === "DESC" ||
1,504✔
4135
                    direction === "desc" ||
4136
                    direction === -1
4137
                        ? "DESC"
4138
                        : "ASC"
4139
                let nulls =
4140
                    typeof order[key] === "object"
398✔
4141
                        ? (order[key] as any).nulls
4142
                        : undefined
4143
                nulls =
398✔
4144
                    nulls?.toLowerCase() === "first"
398✔
4145
                        ? "NULLS FIRST"
4146
                        : nulls?.toLowerCase() === "last"
394✔
4147
                        ? "NULLS LAST"
4148
                        : undefined
4149

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

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

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

4215
    protected buildWhere(
4216
        where: FindOptionsWhere<any>[] | FindOptionsWhere<any>,
4217
        metadata: EntityMetadata,
4218
        alias: string,
4219
        embedPrefix?: string,
4220
    ) {
4221
        let condition: string = ""
2,798✔
4222
        // let parameterIndex = Object.keys(this.expressionMap.nativeParameters).length;
4223
        if (Array.isArray(where)) {
2,798✔
4224
            if (where.length) {
28✔
4225
                condition = where
28✔
4226
                    .map((whereItem) => {
4227
                        return this.buildWhere(
56✔
4228
                            whereItem,
4229
                            metadata,
4230
                            alias,
4231
                            embedPrefix,
4232
                        )
4233
                    })
4234
                    .filter((condition) => !!condition)
56✔
4235
                    .map((condition) => "(" + condition + ")")
56✔
4236
                    .join(" OR ")
4237
            }
4238
        } else {
4239
            const andConditions: string[] = []
2,770✔
4240
            for (const key in where) {
2,770✔
4241
                if (where[key] === undefined || where[key] === null) continue
2,920✔
4242

4243
                const propertyPath = embedPrefix ? embedPrefix + "." + key : key
2,908✔
4244
                const column =
4245
                    metadata.findColumnWithPropertyPathStrict(propertyPath)
2,908✔
4246
                const embed =
4247
                    metadata.findEmbeddedWithPropertyPath(propertyPath)
2,908✔
4248
                const relation =
4249
                    metadata.findRelationWithPropertyPath(propertyPath)
2,908✔
4250

4251
                if (!embed && !column && !relation)
2,908✔
4252
                    throw new EntityPropertyNotFoundError(
8✔
4253
                        propertyPath,
4254
                        metadata,
4255
                    )
4256

4257
                if (column) {
2,900✔
4258
                    let aliasPath = `${alias}.${propertyPath}`
2,639✔
4259
                    if (column.isVirtualProperty && column.query) {
2,639✔
4260
                        aliasPath = `(${column.query(this.escape(alias))})`
16✔
4261
                    }
4262
                    // const parameterName = alias + "_" + propertyPath.split(".").join("_") + "_" + parameterIndex;
4263

4264
                    // todo: we need to handle other operators as well?
4265
                    let parameterValue = where[key]
2,639✔
4266
                    if (InstanceChecker.isEqualOperator(where[key])) {
2,639✔
4267
                        parameterValue = where[key].value
8✔
4268
                    }
4269

4270
                    if (column.transformer) {
2,639✔
4271
                        if (parameterValue instanceof FindOperator) {
16✔
4272
                            parameterValue.transformValue(column.transformer)
4✔
4273
                        } else {
4274
                            parameterValue = ApplyValueTransformers.transformTo(
12✔
4275
                                column.transformer,
4276
                                parameterValue,
4277
                            )
4278
                        }
4279
                    }
4280

4281
                    // MSSQL requires parameters to carry extra type information
4282
                    if (this.connection.driver.options.type === "mssql") {
2,639!
4283
                        const driver = this.connection.driver as SqlServerDriver
×
4284
                        if (parameterValue instanceof FindOperator) {
×
4285
                            if (parameterValue.type !== "raw") {
×
4286
                                parameterValue.transformValue({
×
4287
                                    to: (v) =>
4288
                                        driver.parametrizeValue(column, v),
×
4289
                                    from: (v) => v,
×
4290
                                })
4291
                            }
4292
                        } else {
4293
                            parameterValue = driver.parametrizeValue(
×
4294
                                column,
4295
                                parameterValue,
4296
                            )
4297
                        }
4298
                    }
4299

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

4332
                    andConditions.push(
2,639✔
4333
                        this.createWhereConditionExpression(
4334
                            this.getWherePredicateCondition(
4335
                                aliasPath,
4336
                                parameterValue,
4337
                            ),
4338
                        ),
4339
                        // parameterValue.toSql(this.connection, aliasPath, parameters));
4340
                    )
4341

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

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

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

4501
                        const existJoin = this.joins.find(
99✔
4502
                            (join) => join.alias === joinAlias,
59✔
4503
                        )
4504
                        if (!existJoin) {
99✔
4505
                            this.joins.push({
72✔
4506
                                type: "left",
4507
                                select: false,
4508
                                selection: undefined,
4509
                                alias: joinAlias,
4510
                                parentAlias: alias,
4511
                                relationMetadata: relation,
4512
                            })
4513
                        }
4514

4515
                        const condition = this.buildWhere(
99✔
4516
                            where[key],
4517
                            relation.inverseEntityMetadata,
4518
                            joinAlias,
4519
                        )
4520
                        if (condition) {
99✔
4521
                            andConditions.push(condition)
87✔
4522
                            // parameterIndex = Object.keys(this.expressionMap.nativeParameters).length;
4523
                        }
4524
                    }
4525
                }
4526
            }
4527
            condition = andConditions.length
2,762✔
4528
                ? "(" + andConditions.join(") AND (") + ")"
4529
                : andConditions.join(" AND ")
4530
        }
4531
        return condition.length ? "(" + condition + ")" : condition
2,790✔
4532
    }
4533
}
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

© 2025 Coveralls, Inc