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

typeorm / typeorm / 19549987525

20 Nov 2025 08:11PM UTC coverage: 80.769% (+4.3%) from 76.433%
19549987525

push

github

web-flow
ci: run tests on commits to master and next (#11783)

Co-authored-by: Oleg "OSA413" Sokolov <OSA413@users.noreply.github.com>

26500 of 32174 branches covered (82.36%)

Branch coverage included in aggregate %.

91252 of 113615 relevant lines covered (80.32%)

88980.79 hits per line

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

78.05
/src/query-builder/InsertQueryBuilder.ts
1
import { v4 as uuidv4 } from "uuid"
26✔
2
import { EntityTarget } from "../common/EntityTarget"
26✔
3
import { ObjectLiteral } from "../common/ObjectLiteral"
26✔
4
import { AuroraMysqlDriver } from "../driver/aurora-mysql/AuroraMysqlDriver"
26✔
5
import { DriverUtils } from "../driver/DriverUtils"
26✔
6
import { MysqlDriver } from "../driver/mysql/MysqlDriver"
26✔
7
import { SqlServerDriver } from "../driver/sqlserver/SqlServerDriver"
26✔
8
import { TypeORMError } from "../error"
26✔
9
import { InsertValuesMissingError } from "../error/InsertValuesMissingError"
26✔
10
import { ReturningStatementNotSupportedError } from "../error/ReturningStatementNotSupportedError"
26✔
11
import { ColumnMetadata } from "../metadata/ColumnMetadata"
26✔
12
import { BroadcasterResult } from "../subscriber/BroadcasterResult"
26✔
13
import { InstanceChecker } from "../util/InstanceChecker"
26✔
14
import { ObjectUtils } from "../util/ObjectUtils"
26✔
15
import { InsertOrUpdateOptions } from "./InsertOrUpdateOptions"
26✔
16
import { QueryBuilder } from "./QueryBuilder"
26✔
17
import { QueryDeepPartialEntity } from "./QueryPartialEntity"
26✔
18
import { InsertResult } from "./result/InsertResult"
26✔
19
import { ReturningResultsEntityUpdator } from "./ReturningResultsEntityUpdator"
26✔
20
import { WhereClause } from "./WhereClause"
26✔
21

26✔
22
/**
26✔
23
 * Allows to build complex sql queries in a fashion way and execute those queries.
26✔
24
 */
26✔
25
export class InsertQueryBuilder<
241,087✔
26
    Entity extends ObjectLiteral,
241,087✔
27
> extends QueryBuilder<Entity> {
241,087✔
28
    readonly "@instanceof" = Symbol.for("InsertQueryBuilder")
241,087✔
29

241,087✔
30
    // -------------------------------------------------------------------------
241,087✔
31
    // Public Implemented Methods
241,087✔
32
    // -------------------------------------------------------------------------
241,087✔
33

241,087✔
34
    /**
241,087✔
35
     * Gets generated SQL query without parameters being replaced.
241,087✔
36
     */
241,087✔
37
    getQuery(): string {
241,087✔
38
        let sql = this.createComment()
241,036✔
39
        sql += this.createCteExpression()
241,036✔
40
        sql += this.createInsertExpression()
241,036✔
41
        return this.replacePropertyNamesForTheWholeQuery(sql.trim())
241,036✔
42
    }
241,036✔
43

241,087✔
44
    /**
241,087✔
45
     * Executes sql generated by query builder and returns raw database results.
241,087✔
46
     */
241,087✔
47
    async execute(): Promise<InsertResult> {
241,087✔
48
        // console.time(".value sets");
239,779✔
49
        const valueSets: ObjectLiteral[] = this.getValueSets()
239,779✔
50
        // console.timeEnd(".value sets");
239,779✔
51

239,779✔
52
        // If user passed empty array of entities then we don't need to do
239,779✔
53
        // anything.
239,779✔
54
        //
239,779✔
55
        // Fixes GitHub issues #3111 and #5734. If we were to let this through
239,779✔
56
        // we would run into problems downstream, like subscribers getting
239,779✔
57
        // invoked with the empty array where they expect an entity, and SQL
239,779✔
58
        // queries with an empty VALUES clause.
239,779✔
59
        if (valueSets.length === 0) return new InsertResult()
239,779✔
60

239,695✔
61
        // console.time("QueryBuilder.execute");
239,695✔
62
        // console.time(".database stuff");
239,695✔
63
        const queryRunner = this.obtainQueryRunner()
239,695✔
64
        let transactionStartedByUs: boolean = false
239,695✔
65

239,695✔
66
        try {
239,695✔
67
            // start transaction if it was enabled
239,695✔
68
            if (
239,695✔
69
                this.expressionMap.useTransaction === true &&
239,695✔
70
                queryRunner.isTransactionActive === false
28✔
71
            ) {
239,779✔
72
                await queryRunner.startTransaction()
28✔
73
                transactionStartedByUs = true
28✔
74
            }
28✔
75

239,695✔
76
            // console.timeEnd(".database stuff");
239,695✔
77

239,695✔
78
            // call before insertion methods in listeners and subscribers
239,695✔
79
            if (
239,695✔
80
                this.expressionMap.callListeners === true &&
239,695✔
81
                this.expressionMap.mainAlias!.hasMetadata
2,711✔
82
            ) {
239,779✔
83
                const broadcastResult = new BroadcasterResult()
2,091✔
84
                valueSets.forEach((valueSet) => {
2,091✔
85
                    queryRunner.broadcaster.broadcastBeforeInsertEvent(
402,952✔
86
                        broadcastResult,
402,952✔
87
                        this.expressionMap.mainAlias!.metadata,
402,952✔
88
                        valueSet,
402,952✔
89
                    )
402,952✔
90
                })
2,091✔
91
                await broadcastResult.wait()
2,091✔
92
            }
2,091✔
93

239,695✔
94
            let declareSql: string | null = null
239,695✔
95
            let selectOutputSql: string | null = null
239,695✔
96

239,695✔
97
            // if update entity mode is enabled we may need extra columns for the returning statement
239,695✔
98
            // console.time(".prepare returning statement");
239,695✔
99
            const returningResultsEntityUpdator =
239,695✔
100
                new ReturningResultsEntityUpdator(
239,695✔
101
                    queryRunner,
239,695✔
102
                    this.expressionMap,
239,695✔
103
                )
239,695✔
104

239,695✔
105
            const returningColumns: ColumnMetadata[] = []
239,695✔
106

239,695✔
107
            if (
239,695✔
108
                Array.isArray(this.expressionMap.returning) &&
239,695✔
109
                this.expressionMap.mainAlias!.hasMetadata
6✔
110
            ) {
239,779✔
111
                for (const columnPath of this.expressionMap.returning) {
6✔
112
                    returningColumns.push(
10✔
113
                        ...this.expressionMap.mainAlias!.metadata.findColumnsWithPropertyPath(
10✔
114
                            columnPath,
10✔
115
                        ),
10✔
116
                    )
10✔
117
                }
10✔
118
            }
6✔
119

239,695✔
120
            if (
239,695✔
121
                this.expressionMap.updateEntity === true &&
239,695✔
122
                this.expressionMap.mainAlias!.hasMetadata
235,831✔
123
            ) {
239,779✔
124
                if (
235,211✔
125
                    !(
235,211✔
126
                        valueSets.length > 1 &&
235,211✔
127
                        this.connection.driver.options.type === "oracle"
16,363✔
128
                    )
235,211✔
129
                ) {
235,211✔
130
                    this.expressionMap.extraReturningColumns =
235,189✔
131
                        this.expressionMap.mainAlias!.metadata.getInsertionReturningColumns()
235,189✔
132
                }
235,189✔
133

235,211✔
134
                returningColumns.push(
235,211✔
135
                    ...this.expressionMap.extraReturningColumns.filter(
235,211✔
136
                        (c) => !returningColumns.includes(c),
235,211✔
137
                    ),
235,211✔
138
                )
235,211✔
139
            }
235,211✔
140

239,695✔
141
            if (
239,695✔
142
                returningColumns.length > 0 &&
239,695✔
143
                this.connection.driver.options.type === "mssql"
101,662✔
144
            ) {
239,779!
145
                declareSql = (
5,686✔
146
                    this.connection.driver as SqlServerDriver
5,686✔
147
                ).buildTableVariableDeclaration(
5,686✔
148
                    "@OutputTable",
5,686✔
149
                    returningColumns,
5,686✔
150
                )
5,686✔
151
                selectOutputSql = `SELECT * FROM @OutputTable`
5,686✔
152
            }
5,686✔
153
            // console.timeEnd(".prepare returning statement");
239,695✔
154

239,695✔
155
            // execute query
239,695✔
156
            // console.time(".getting query and parameters");
239,695✔
157
            const [insertSql, parameters] = this.getQueryAndParameters()
239,695✔
158
            // console.timeEnd(".getting query and parameters");
239,695✔
159

239,695✔
160
            // console.time(".query execution by database");
239,695✔
161
            const statements = [declareSql, insertSql, selectOutputSql]
239,695✔
162
            const sql = statements.filter((s) => s != null).join(";\n\n")
239,695✔
163

239,695✔
164
            const queryResult = await queryRunner.query(sql, parameters, true)
239,695✔
165

239,592✔
166
            const insertResult = InsertResult.from(queryResult)
239,592✔
167

239,592✔
168
            // console.timeEnd(".query execution by database");
239,592✔
169

239,592✔
170
            // load returning results and set them to the entity if entity updation is enabled
239,592✔
171
            if (
239,592✔
172
                this.expressionMap.updateEntity === true &&
239,592✔
173
                this.expressionMap.mainAlias!.hasMetadata
235,728✔
174
            ) {
239,779✔
175
                // console.time(".updating entity");
235,112✔
176
                await returningResultsEntityUpdator.insert(
235,112✔
177
                    insertResult,
235,112✔
178
                    valueSets,
235,112✔
179
                )
235,112✔
180
                // console.timeEnd(".updating entity");
235,112✔
181
            }
235,112✔
182

239,592✔
183
            // call after insertion methods in listeners and subscribers
239,592✔
184
            if (
239,592✔
185
                this.expressionMap.callListeners === true &&
239,592✔
186
                this.expressionMap.mainAlias!.hasMetadata
2,690✔
187
            ) {
239,779✔
188
                const broadcastResult = new BroadcasterResult()
2,074✔
189
                valueSets.forEach((valueSet) => {
2,074✔
190
                    queryRunner.broadcaster.broadcastAfterInsertEvent(
402,933✔
191
                        broadcastResult,
402,933✔
192
                        this.expressionMap.mainAlias!.metadata,
402,933✔
193
                        valueSet,
402,933✔
194
                    )
402,933✔
195
                })
2,074✔
196
                await broadcastResult.wait()
2,074✔
197
            }
2,074✔
198

239,592✔
199
            // close transaction if we started it
239,592✔
200
            // console.time(".commit");
239,592✔
201
            if (transactionStartedByUs) {
239,779✔
202
                await queryRunner.commitTransaction()
28✔
203
            }
28✔
204
            // console.timeEnd(".commit");
239,592✔
205

239,592✔
206
            return insertResult
239,592✔
207
        } catch (error) {
239,779✔
208
            // rollback transaction if we started it
103✔
209
            if (transactionStartedByUs) {
103!
210
                try {
×
211
                    await queryRunner.rollbackTransaction()
×
212
                } catch (rollbackError) {}
×
213
            }
×
214
            throw error
103✔
215
        } finally {
239,779!
216
            // console.time(".releasing connection");
239,695✔
217
            if (queryRunner !== this.queryRunner) {
239,695✔
218
                // means we created our own query runner
2,095✔
219
                await queryRunner.release()
2,095✔
220
            }
2,095✔
221
            // console.timeEnd(".releasing connection");
239,695✔
222
            // console.timeEnd("QueryBuilder.execute");
239,695✔
223
        }
239,695✔
224
    }
239,779✔
225

241,087✔
226
    // -------------------------------------------------------------------------
241,087✔
227
    // Public Methods
241,087✔
228
    // -------------------------------------------------------------------------
241,087✔
229

241,087✔
230
    /**
241,087✔
231
     * Specifies INTO which entity's table insertion will be executed.
241,087✔
232
     */
241,087✔
233
    into<T extends ObjectLiteral>(
241,087✔
234
        entityTarget: EntityTarget<T>,
240,916✔
235
        columns?: string[],
240,916✔
236
    ): InsertQueryBuilder<T> {
240,916✔
237
        entityTarget = InstanceChecker.isEntitySchema(entityTarget)
240,916✔
238
            ? entityTarget.options.name
240,916!
239
            : entityTarget
240,916✔
240
        const mainAlias = this.createFromAlias(entityTarget)
240,916✔
241
        this.expressionMap.setMainAlias(mainAlias)
240,916✔
242
        this.expressionMap.insertColumns = columns || []
240,916✔
243
        return this as any as InsertQueryBuilder<T>
240,916✔
244
    }
240,916✔
245

241,087✔
246
    /**
241,087✔
247
     * Values needs to be inserted into table.
241,087✔
248
     */
241,087✔
249
    values(
241,087✔
250
        values:
241,059✔
251
            | QueryDeepPartialEntity<Entity>
241,059✔
252
            | QueryDeepPartialEntity<Entity>[],
241,059✔
253
    ): this {
241,059✔
254
        this.expressionMap.valuesSet = values
241,059✔
255
        return this
241,059✔
256
    }
241,059✔
257

241,087✔
258
    /**
241,087✔
259
     * Optional returning/output clause.
241,087✔
260
     * This will return given column values.
241,087✔
261
     */
241,087✔
262
    output(columns: string[]): this
241,087✔
263

241,087✔
264
    /**
241,087✔
265
     * Optional returning/output clause.
241,087✔
266
     * Returning is a SQL string containing returning statement.
241,087✔
267
     */
241,087✔
268
    output(output: string): this
241,087✔
269

241,087✔
270
    /**
241,087✔
271
     * Optional returning/output clause.
241,087✔
272
     */
241,087✔
273
    output(output: string | string[]): this
241,087✔
274

241,087✔
275
    /**
241,087✔
276
     * Optional returning/output clause.
241,087✔
277
     */
241,087✔
278
    output(output: string | string[]): this {
241,087✔
279
        return this.returning(output)
×
280
    }
×
281

241,087✔
282
    /**
241,087✔
283
     * Optional returning/output clause.
241,087✔
284
     * This will return given column values.
241,087✔
285
     */
241,087✔
286
    returning(columns: string[]): this
241,087✔
287

241,087✔
288
    /**
241,087✔
289
     * Optional returning/output clause.
241,087✔
290
     * Returning is a SQL string containing returning statement.
241,087✔
291
     */
241,087✔
292
    returning(returning: string): this
241,087✔
293

241,087✔
294
    /**
241,087✔
295
     * Optional returning/output clause.
241,087✔
296
     */
241,087✔
297
    returning(returning: string | string[]): this
241,087✔
298

241,087✔
299
    /**
241,087✔
300
     * Optional returning/output clause.
241,087✔
301
     */
241,087✔
302
    returning(returning: string | string[]): this {
241,087✔
303
        // not all databases support returning/output cause
29✔
304
        if (!this.connection.driver.isReturningSqlSupported("insert")) {
29!
305
            throw new ReturningStatementNotSupportedError()
×
306
        }
×
307

29✔
308
        this.expressionMap.returning = returning
29✔
309
        return this
29✔
310
    }
29✔
311

241,087✔
312
    /**
241,087✔
313
     * Indicates if entity must be updated after insertion operations.
241,087✔
314
     * This may produce extra query or use RETURNING / OUTPUT statement (depend on database).
241,087✔
315
     * Enabled by default.
241,087✔
316
     */
241,087✔
317
    updateEntity(enabled: boolean): this {
241,087✔
318
        this.expressionMap.updateEntity = enabled
237,068✔
319
        return this
237,068✔
320
    }
237,068✔
321

241,087✔
322
    /**
241,087✔
323
     * Adds additional ON CONFLICT statement supported in postgres and cockroach.
241,087✔
324
     *
241,087✔
325
     * @deprecated Use `orIgnore` or `orUpdate`
241,087✔
326
     */
241,087✔
327
    onConflict(statement: string): this {
241,087✔
328
        this.expressionMap.onConflict = statement
30✔
329
        return this
30✔
330
    }
30✔
331

241,087✔
332
    /**
241,087✔
333
     * Adds additional ignore statement supported in databases.
241,087✔
334
     */
241,087✔
335
    orIgnore(statement: string | boolean = true): this {
241,087✔
336
        this.expressionMap.onIgnore = !!statement
46✔
337
        return this
46✔
338
    }
46✔
339

241,087✔
340
    /**
241,087✔
341
     * @deprecated
241,087✔
342
     *
241,087✔
343
     * `.orUpdate({ columns: [ "is_updated" ] }).setParameter("is_updated", value)`
241,087✔
344
     *
241,087✔
345
     * is now `.orUpdate(["is_updated"])`
241,087✔
346
     *
241,087✔
347
     * `.orUpdate({ conflict_target: ['date'], overwrite: ['title'] })`
241,087✔
348
     *
241,087✔
349
     * is now `.orUpdate(['title'], ['date'])`
241,087✔
350
     *
241,087✔
351
     */
241,087✔
352
    orUpdate(statement?: {
241,087✔
353
        columns?: string[]
241,087✔
354
        overwrite?: string[]
241,087✔
355
        conflict_target?: string | string[]
241,087✔
356
    }): this
241,087✔
357

241,087✔
358
    orUpdate(
241,087✔
359
        overwrite: string[],
241,087✔
360
        conflictTarget?: string | string[],
241,087✔
361
        orUpdateOptions?: InsertOrUpdateOptions,
241,087✔
362
    ): this
241,087✔
363

241,087✔
364
    /**
241,087✔
365
     * Adds additional update statement supported in databases.
241,087✔
366
     */
241,087✔
367
    orUpdate(
241,087✔
368
        statementOrOverwrite?:
682✔
369
            | {
682✔
370
                  columns?: string[]
682✔
371
                  overwrite?: string[]
682✔
372
                  conflict_target?: string | string[]
682✔
373
              }
682✔
374
            | string[],
682✔
375
        conflictTarget?: string | string[],
682✔
376
        orUpdateOptions?: InsertOrUpdateOptions,
682✔
377
    ): this {
682✔
378
        const { where, parameters } = orUpdateOptions?.overwriteCondition ?? {}
682✔
379
        let wheres: WhereClause[] | undefined
682✔
380
        if (where) {
682!
381
            const condition = this.getWhereCondition(where)
17✔
382
            if (Array.isArray(condition) ? condition.length !== 0 : condition)
17!
383
                wheres = [{ type: "simple", condition: condition }]
17✔
384
        }
17✔
385
        if (parameters) this.setParameters(parameters)
682!
386

682✔
387
        if (!Array.isArray(statementOrOverwrite)) {
682!
388
            this.expressionMap.onUpdate = {
×
389
                conflict: statementOrOverwrite?.conflict_target,
×
390
                columns: statementOrOverwrite?.columns,
×
391
                overwrite: statementOrOverwrite?.overwrite,
×
392
                skipUpdateIfNoValuesChanged:
×
393
                    orUpdateOptions?.skipUpdateIfNoValuesChanged,
×
394
                upsertType: orUpdateOptions?.upsertType,
×
395
                overwriteCondition: wheres,
×
396
            }
×
397
            return this
×
398
        }
×
399

682✔
400
        this.expressionMap.onUpdate = {
682✔
401
            overwrite: statementOrOverwrite,
682✔
402
            conflict: conflictTarget,
682✔
403
            skipUpdateIfNoValuesChanged:
682✔
404
                orUpdateOptions?.skipUpdateIfNoValuesChanged,
682✔
405
            indexPredicate: orUpdateOptions?.indexPredicate,
682✔
406
            upsertType: orUpdateOptions?.upsertType,
682✔
407
            overwriteCondition: wheres,
682✔
408
        }
682✔
409
        return this
682✔
410
    }
682✔
411

241,087✔
412
    // -------------------------------------------------------------------------
241,087✔
413
    // Protected Methods
241,087✔
414
    // -------------------------------------------------------------------------
241,087✔
415

241,087✔
416
    /**
241,087✔
417
     * Creates INSERT express used to perform insert query.
241,087✔
418
     */
241,087✔
419
    protected createInsertExpression() {
241,087✔
420
        if (this.expressionMap.onUpdate || this.expressionMap.onIgnore) {
241,036✔
421
            if (
743✔
422
                (this.expressionMap.onUpdate?.upsertType ?? "merge-into") ===
743!
423
                    "merge-into" &&
743!
424
                this.connection.driver.supportedUpsertTypes.includes(
279✔
425
                    "merge-into",
279✔
426
                )
743✔
427
            )
743✔
428
                return this.createMergeExpression()
743!
429
        }
743✔
430
        const tableName = this.getTableName(this.getMainTableName())
240,898✔
431
        const tableOrAliasName =
240,898✔
432
            this.alias !== this.getMainTableName()
240,898✔
433
                ? this.escape(this.alias)
241,036✔
434
                : tableName
241,036✔
435
        const valuesExpression = this.createValuesExpression() // its important to get values before returning expression because oracle rely on native parameters and ordering of them is important
241,036✔
436
        const returningExpression =
241,036✔
437
            this.connection.driver.options.type === "oracle" &&
241,036!
438
            this.getValueSets().length > 1
18,996✔
439
                ? null
241,036!
440
                : this.createReturningExpression("insert") // oracle doesnt support returning with multi-row insert
241,036✔
441
        const columnsExpression = this.createColumnNamesExpression()
241,036✔
442
        let query = "INSERT "
241,036✔
443

241,036✔
444
        if (this.expressionMap.onUpdate?.upsertType === "primary-key") {
241,036!
445
            query = "UPSERT "
×
446
        }
×
447

240,898✔
448
        if (
240,898✔
449
            DriverUtils.isMySQLFamily(this.connection.driver) ||
240,898!
450
            this.connection.driver.options.type === "aurora-mysql"
175,018✔
451
        ) {
241,036!
452
            query += `${this.expressionMap.onIgnore ? " IGNORE " : ""}`
65,880✔
453
        }
65,880✔
454

240,898✔
455
        query += `INTO ${tableName}`
240,898✔
456

240,898✔
457
        if (
240,898✔
458
            this.alias !== this.getMainTableName() &&
240,898✔
459
            DriverUtils.isPostgresFamily(this.connection.driver)
69✔
460
        ) {
241,036!
461
            query += ` AS "${this.alias}"`
34✔
462
        }
34✔
463

240,898✔
464
        // add columns expression
240,898✔
465
        if (columnsExpression) {
240,990✔
466
            query += `(${columnsExpression})`
240,792✔
467
        } else {
241,036!
468
            if (
106✔
469
                !valuesExpression &&
106✔
470
                (DriverUtils.isMySQLFamily(this.connection.driver) ||
79✔
471
                    this.connection.driver.options.type === "aurora-mysql")
79✔
472
            )
106✔
473
                // special syntax for mysql DEFAULT VALUES insertion
106✔
474
                query += "()"
106!
475
        }
106✔
476

240,898✔
477
        // add OUTPUT expression
240,898✔
478
        if (
240,898✔
479
            returningExpression &&
240,898!
480
            this.connection.driver.options.type === "mssql"
39,356✔
481
        ) {
241,036!
482
            query += ` OUTPUT ${returningExpression}`
5,664✔
483
        }
5,664✔
484

240,898✔
485
        // add VALUES expression
240,898✔
486
        if (valuesExpression) {
240,944✔
487
            if (
240,819✔
488
                (this.connection.driver.options.type === "oracle" ||
240,819!
489
                    this.connection.driver.options.type === "sap") &&
240,819!
490
                this.getValueSets().length > 1
37,962✔
491
            ) {
240,819!
492
                query += ` ${valuesExpression}`
18✔
493
            } else {
240,819✔
494
                query += ` VALUES ${valuesExpression}`
240,801✔
495
            }
240,801✔
496
        } else {
241,036!
497
            if (
79✔
498
                DriverUtils.isMySQLFamily(this.connection.driver) ||
79✔
499
                this.connection.driver.options.type === "aurora-mysql"
79✔
500
            ) {
79!
501
                // special syntax for mysql DEFAULT VALUES insertion
×
502
                query += " VALUES ()"
×
503
            } else {
79✔
504
                query += ` DEFAULT VALUES`
79✔
505
            }
79✔
506
        }
79✔
507
        if (this.expressionMap.onUpdate?.upsertType !== "primary-key") {
241,036!
508
            if (
240,898✔
509
                this.connection.driver.supportedUpsertTypes.includes(
240,898✔
510
                    "on-conflict-do-update",
240,898✔
511
                )
240,898✔
512
            ) {
240,898!
513
                if (this.expressionMap.onIgnore) {
121,800!
514
                    query += " ON CONFLICT DO NOTHING "
24✔
515
                } else if (this.expressionMap.onConflict) {
121,800!
516
                    query += ` ON CONFLICT ${this.expressionMap.onConflict} `
30✔
517
                } else if (this.expressionMap.onUpdate) {
121,776✔
518
                    const {
389✔
519
                        overwrite,
389✔
520
                        columns,
389✔
521
                        conflict,
389✔
522
                        skipUpdateIfNoValuesChanged,
389✔
523
                        indexPredicate,
389✔
524
                    } = this.expressionMap.onUpdate
389✔
525

389✔
526
                    let conflictTarget = "ON CONFLICT"
389✔
527

389✔
528
                    if (Array.isArray(conflict)) {
389✔
529
                        conflictTarget += ` ( ${conflict
381✔
530
                            .map((column) => this.escape(column))
381✔
531
                            .join(", ")} )`
381✔
532
                        if (
381✔
533
                            indexPredicate &&
381✔
534
                            !DriverUtils.isPostgresFamily(
26✔
535
                                this.connection.driver,
26✔
536
                            )
381✔
537
                        ) {
381!
538
                            throw new TypeORMError(
9✔
539
                                `indexPredicate option is not supported by the current database driver`,
9✔
540
                            )
9✔
541
                        }
9✔
542
                        if (
372✔
543
                            indexPredicate &&
372!
544
                            DriverUtils.isPostgresFamily(this.connection.driver)
17✔
545
                        ) {
381!
546
                            conflictTarget += ` WHERE ( ${indexPredicate} )`
17✔
547
                        }
17✔
548
                    } else if (conflict) {
389!
549
                        conflictTarget += ` ON CONSTRAINT ${this.escape(
8✔
550
                            conflict,
8✔
551
                        )}`
8✔
552
                    }
8✔
553

380✔
554
                    const updatePart: string[] = []
380✔
555

380✔
556
                    if (Array.isArray(overwrite)) {
380✔
557
                        updatePart.push(
380✔
558
                            ...overwrite.map(
380✔
559
                                (column) =>
380✔
560
                                    `${this.escape(
761✔
561
                                        column,
761✔
562
                                    )} = EXCLUDED.${this.escape(column)}`,
380✔
563
                            ),
380✔
564
                        )
380✔
565
                    } else if (columns) {
389!
566
                        updatePart.push(
×
567
                            ...columns.map(
×
568
                                (column) =>
×
569
                                    `${this.escape(column)} = :${column}`,
×
570
                            ),
×
571
                        )
×
572
                    }
×
573

380✔
574
                    if (updatePart.length > 0) {
380✔
575
                        query += ` ${conflictTarget} DO UPDATE SET `
380✔
576

380✔
577
                        updatePart.push(
380✔
578
                            ...this.expressionMap
380✔
579
                                .mainAlias!.metadata.columns.filter(
380✔
580
                                    (column) =>
380✔
581
                                        column.isUpdateDate &&
1,587✔
582
                                        !overwrite?.includes(
76✔
583
                                            column.databaseName,
76✔
584
                                        ) &&
1,587✔
585
                                        !(
76✔
586
                                            (this.connection.driver.options
76✔
587
                                                .type === "oracle" &&
76!
588
                                                this.getValueSets().length >
×
589
                                                    1) ||
76✔
590
                                            DriverUtils.isSQLiteFamily(
76✔
591
                                                this.connection.driver,
76✔
592
                                            ) ||
76!
593
                                            this.connection.driver.options
40✔
594
                                                .type === "sap" ||
76!
595
                                            this.connection.driver.options
40✔
596
                                                .type === "spanner"
40✔
597
                                        ),
380✔
598
                                )
380✔
599
                                .map(
380✔
600
                                    (column) =>
380✔
601
                                        `${this.escape(
40✔
602
                                            column.databaseName,
40✔
603
                                        )} = DEFAULT`,
380✔
604
                                ),
380✔
605
                        )
380✔
606

380✔
607
                        query += updatePart.join(", ")
380✔
608
                    }
380✔
609

380✔
610
                    if (
380✔
611
                        Array.isArray(overwrite) &&
380✔
612
                        skipUpdateIfNoValuesChanged
380✔
613
                    ) {
389!
614
                        this.expressionMap.onUpdate.overwriteCondition ??= []
57✔
615
                        const wheres = overwrite.map<WhereClause>((column) => ({
57✔
616
                            type: "or",
98✔
617
                            condition: `${tableOrAliasName}.${this.escape(
98✔
618
                                column,
98✔
619
                            )} IS DISTINCT FROM EXCLUDED.${this.escape(
98✔
620
                                column,
98✔
621
                            )}`,
98✔
622
                        }))
57✔
623
                        this.expressionMap.onUpdate.overwriteCondition.push({
57✔
624
                            type: "and",
57✔
625
                            condition: wheres,
57✔
626
                        })
57✔
627
                    }
57✔
628
                    if (
380✔
629
                        DriverUtils.isPostgresFamily(this.connection.driver) &&
380!
630
                        this.expressionMap.onUpdate.overwriteCondition &&
389!
631
                        this.expressionMap.onUpdate.overwriteCondition.length >
67✔
632
                            0
67✔
633
                    ) {
389!
634
                        query += ` WHERE ${this.createUpsertConditionExpression()}`
67✔
635
                    }
67✔
636
                }
389✔
637
            } else if (
240,898!
638
                this.connection.driver.supportedUpsertTypes.includes(
119,098✔
639
                    "on-duplicate-key-update",
119,098✔
640
                )
119,098✔
641
            ) {
119,098!
642
                if (this.expressionMap.onUpdate) {
65,880✔
643
                    const { overwrite, columns } = this.expressionMap.onUpdate
176✔
644

176✔
645
                    if (Array.isArray(overwrite)) {
176✔
646
                        query += " ON DUPLICATE KEY UPDATE "
176✔
647
                        query += overwrite
176✔
648
                            .map(
176✔
649
                                (column) =>
176✔
650
                                    `${this.escape(
368✔
651
                                        column,
368✔
652
                                    )} = VALUES(${this.escape(column)})`,
176✔
653
                            )
176✔
654
                            .join(", ")
176✔
655
                        query += " "
176✔
656
                    } else if (Array.isArray(columns)) {
176!
657
                        query += " ON DUPLICATE KEY UPDATE "
×
658
                        query += columns
×
659
                            .map(
×
660
                                (column) =>
×
661
                                    `${this.escape(column)} = :${column}`,
×
662
                            )
×
663
                            .join(", ")
×
664
                        query += " "
×
665
                    }
×
666
                }
176✔
667
            } else {
119,098!
668
                if (this.expressionMap.onUpdate) {
53,218!
669
                    throw new TypeORMError(
×
670
                        `onUpdate is not supported by the current database driver`,
×
671
                    )
×
672
                }
×
673
            }
53,218✔
674
        }
240,898✔
675

240,889✔
676
        // add RETURNING expression
240,889✔
677
        if (
240,889✔
678
            returningExpression &&
240,889!
679
            (DriverUtils.isPostgresFamily(this.connection.driver) ||
39,356!
680
                this.connection.driver.options.type === "oracle" ||
39,356!
681
                this.connection.driver.options.type === "cockroachdb" ||
39,356!
682
                DriverUtils.isMySQLFamily(this.connection.driver))
39,356✔
683
        ) {
241,036!
684
            query += ` RETURNING ${returningExpression}`
33,692✔
685
        }
33,692✔
686

240,889✔
687
        if (
240,889✔
688
            returningExpression &&
240,889!
689
            this.connection.driver.options.type === "spanner"
39,356✔
690
        ) {
241,036!
691
            query += ` THEN RETURN ${returningExpression}`
×
692
        }
×
693

240,889✔
694
        // Inserting a specific value for an auto-increment primary key in mssql requires enabling IDENTITY_INSERT
240,889✔
695
        // IDENTITY_INSERT can only be enabled for tables where there is an IDENTITY column and only if there is a value to be inserted (i.e. supplying DEFAULT is prohibited if IDENTITY_INSERT is enabled)
240,889✔
696
        if (
240,889✔
697
            this.connection.driver.options.type === "mssql" &&
240,889!
698
            this.expressionMap.mainAlias!.hasMetadata &&
241,036!
699
            this.expressionMap
15,126✔
700
                .mainAlias!.metadata.columns.filter((column) =>
15,126✔
701
                    this.expressionMap.insertColumns.length > 0
50,182✔
702
                        ? this.expressionMap.insertColumns.indexOf(
50,182!
703
                              column.propertyPath,
×
704
                          ) !== -1
×
705
                        : column.isInsert,
15,126✔
706
                )
15,126✔
707
                .some((column) =>
15,126✔
708
                    this.isOverridingAutoIncrementBehavior(column),
15,126✔
709
                )
241,036✔
710
        ) {
241,036!
711
            query = `SET IDENTITY_INSERT ${tableName} ON; ${query}; SET IDENTITY_INSERT ${tableName} OFF`
88✔
712
        }
88✔
713

240,889✔
714
        return query
240,889✔
715
    }
240,889✔
716

241,087✔
717
    /**
241,087✔
718
     * Gets list of columns where values must be inserted to.
241,087✔
719
     */
241,087✔
720
    protected getInsertedColumns(): ColumnMetadata[] {
241,087✔
721
        if (!this.expressionMap.mainAlias!.hasMetadata) return []
482,348✔
722

478,644✔
723
        return this.expressionMap.mainAlias!.metadata.columns.filter(
478,644✔
724
            (column) => {
478,644✔
725
                // if user specified list of columns he wants to insert to, then we filter only them
1,522,954✔
726
                if (this.expressionMap.insertColumns.length)
1,522,954✔
727
                    return (
1,522,954!
728
                        this.expressionMap.insertColumns.indexOf(
×
729
                            column.propertyPath,
×
730
                        ) !== -1
×
731
                    )
×
732

1,522,954✔
733
                // skip columns the user doesn't want included by default
1,522,954✔
734
                if (!column.isInsert) {
1,522,954✔
735
                    return false
1,094✔
736
                }
1,094✔
737

1,521,860✔
738
                // if user did not specified such list then return all columns except auto-increment one
1,521,860✔
739
                // for Oracle we return auto-increment column as well because Oracle does not support DEFAULT VALUES expression
1,521,860✔
740
                if (
1,521,860✔
741
                    column.isGenerated &&
1,521,860✔
742
                    column.generationStrategy === "increment" &&
1,522,954✔
743
                    !(this.connection.driver.options.type === "spanner") &&
1,522,954✔
744
                    !(this.connection.driver.options.type === "oracle") &&
1,522,954!
745
                    !DriverUtils.isSQLiteFamily(this.connection.driver) &&
1,522,954!
746
                    !DriverUtils.isMySQLFamily(this.connection.driver) &&
1,522,954!
747
                    !(this.connection.driver.options.type === "aurora-mysql") &&
1,522,954!
748
                    !(
53,686✔
749
                        this.connection.driver.options.type === "mssql" &&
53,686!
750
                        this.isOverridingAutoIncrementBehavior(column)
10,600✔
751
                    )
1,522,954✔
752
                )
1,522,954✔
753
                    return false
1,522,954!
754

1,468,350✔
755
                return true
1,468,350✔
756
            },
478,644✔
757
        )
478,644✔
758
    }
478,644✔
759

241,087✔
760
    /**
241,087✔
761
     * Creates a columns string where values must be inserted to for INSERT INTO expression.
241,087✔
762
     */
241,087✔
763
    protected createColumnNamesExpression(): string {
241,087✔
764
        const columns = this.getInsertedColumns()
241,036✔
765
        if (columns.length > 0)
241,036✔
766
            return columns
241,036✔
767
                .map((column) => this.escape(column.databaseName))
239,082✔
768
                .join(", ")
239,082✔
769

1,954✔
770
        // in the case if there are no insert columns specified and table without metadata used
1,954✔
771
        // we get columns from the inserted value map, in the case if only one inserted map is specified
1,954✔
772
        if (
1,954✔
773
            !this.expressionMap.mainAlias!.hasMetadata &&
1,954✔
774
            !this.expressionMap.insertColumns.length
1,852✔
775
        ) {
241,036✔
776
            const valueSets = this.getValueSets()
1,848✔
777
            if (valueSets.length === 1)
1,848✔
778
                return Object.keys(valueSets[0])
1,848✔
779
                    .map((columnName) => this.escape(columnName))
1,844✔
780
                    .join(", ")
1,844✔
781
        }
1,848✔
782

110!
783
        // get a table name and all column database names
110✔
784
        return this.expressionMap.insertColumns
110✔
785
            .map((columnName) => this.escape(columnName))
110✔
786
            .join(", ")
110✔
787
    }
110✔
788

241,087✔
789
    /**
241,087✔
790
     * Creates list of values needs to be inserted in the VALUES expression.
241,087✔
791
     */
241,087✔
792
    protected createValuesExpression(): string {
241,087✔
793
        const valueSets = this.getValueSets()
240,898✔
794
        const columns = this.getInsertedColumns()
240,898✔
795

240,898✔
796
        // if column metadatas are given then apply all necessary operations with values
240,898✔
797
        if (columns.length > 0) {
240,898✔
798
            let expression = ""
238,944✔
799
            valueSets.forEach((valueSet, valueSetIndex) => {
238,944✔
800
                columns.forEach((column, columnIndex) => {
892,690✔
801
                    if (columnIndex === 0) {
2,333,108✔
802
                        if (
892,690✔
803
                            this.connection.driver.options.type === "oracle" &&
892,690!
804
                            valueSets.length > 1
18,868✔
805
                        ) {
892,690!
806
                            expression += " SELECT "
34✔
807
                        } else if (
892,690✔
808
                            this.connection.driver.options.type === "sap" &&
892,656!
809
                            valueSets.length > 1
18,926✔
810
                        ) {
892,656!
811
                            expression += " SELECT "
12✔
812
                        } else {
892,656✔
813
                            expression += "("
892,644✔
814
                        }
892,644✔
815
                    }
892,690✔
816

2,333,108✔
817
                    expression += this.createColumnValueExpression(
2,333,108✔
818
                        valueSets,
2,333,108✔
819
                        valueSetIndex,
2,333,108✔
820
                        column,
2,333,108✔
821
                    )
2,333,108✔
822

2,333,108✔
823
                    if (columnIndex === columns.length - 1) {
2,333,108✔
824
                        if (valueSetIndex === valueSets.length - 1) {
892,690✔
825
                            if (
238,944✔
826
                                ["oracle", "sap"].includes(
238,944✔
827
                                    this.connection.driver.options.type,
238,944✔
828
                                ) &&
238,944!
829
                                valueSets.length > 1
37,766✔
830
                            ) {
238,944!
831
                                expression +=
18✔
832
                                    " FROM " +
18✔
833
                                    this.connection.driver.dummyTableName
18✔
834
                            } else {
238,944✔
835
                                expression += ")"
238,926✔
836
                            }
238,926✔
837
                        } else {
892,690✔
838
                            if (
653,746✔
839
                                ["oracle", "sap"].includes(
653,746✔
840
                                    this.connection.driver.options.type,
653,746✔
841
                                ) &&
653,746!
842
                                valueSets.length > 1
28✔
843
                            ) {
653,746!
844
                                expression +=
28✔
845
                                    " FROM " +
28✔
846
                                    this.connection.driver.dummyTableName +
28✔
847
                                    " UNION ALL "
28✔
848
                            } else {
653,746!
849
                                expression += "), "
653,718✔
850
                            }
653,718✔
851
                        }
653,746✔
852
                    } else {
2,333,108✔
853
                        expression += ", "
1,440,418✔
854
                    }
1,440,418✔
855
                })
892,690✔
856
            })
238,944✔
857
            if (expression === "()") return ""
238,944!
858

238,944✔
859
            return expression
238,944✔
860
        } else {
240,898✔
861
            // for tables without metadata
1,954✔
862
            // get values needs to be inserted
1,954✔
863
            let expression = ""
1,954✔
864

1,954✔
865
            valueSets.forEach((valueSet, insertionIndex) => {
1,954✔
866
                const columns = Object.keys(valueSet)
41,958✔
867
                columns.forEach((columnName, columnIndex) => {
41,958✔
868
                    if (columnIndex === 0) {
210,019✔
869
                        expression += "("
41,879✔
870
                    }
41,879✔
871

210,019✔
872
                    const value = valueSet[columnName]
210,019✔
873

210,019✔
874
                    // support for SQL expressions in queries
210,019✔
875
                    if (typeof value === "function") {
210,019!
876
                        expression += value()
×
877

×
878
                        // if value for this column was not provided then insert default value
×
879
                    } else if (value === undefined) {
210,019✔
880
                        if (
1,897✔
881
                            (this.connection.driver.options.type === "oracle" &&
1,897!
882
                                valueSets.length > 1) ||
1,897✔
883
                            DriverUtils.isSQLiteFamily(
1,897✔
884
                                this.connection.driver,
1,897✔
885
                            ) ||
1,897!
886
                            this.connection.driver.options.type === "sap" ||
1,897!
887
                            this.connection.driver.options.type === "spanner"
1,390✔
888
                        ) {
1,897!
889
                            expression += "NULL"
507✔
890
                        } else {
1,897!
891
                            expression += "DEFAULT"
1,390✔
892
                        }
1,390✔
893
                    } else if (
210,019✔
894
                        value === null &&
208,122!
895
                        this.connection.driver.options.type === "spanner"
×
896
                    ) {
208,122!
897
                        // just any other regular value
×
898
                    } else {
208,122✔
899
                        expression += this.createParameter(value)
208,122✔
900
                    }
208,122✔
901

210,019✔
902
                    if (columnIndex === Object.keys(valueSet).length - 1) {
210,019✔
903
                        if (insertionIndex === valueSets.length - 1) {
41,879✔
904
                            expression += ")"
1,875✔
905
                        } else {
41,879!
906
                            expression += "), "
40,004✔
907
                        }
40,004✔
908
                    } else {
210,019✔
909
                        expression += ", "
168,140✔
910
                    }
168,140✔
911
                })
41,958✔
912
            })
1,954✔
913
            if (expression === "()") return ""
1,954!
914
            return expression
1,954✔
915
        }
1,954✔
916
    }
240,898✔
917

241,087✔
918
    /**
241,087✔
919
     * Gets array of values need to be inserted into the target table.
241,087✔
920
     */
241,087✔
921
    protected getValueSets(): ObjectLiteral[] {
241,087✔
922
        if (Array.isArray(this.expressionMap.valuesSet))
555,497✔
923
            return this.expressionMap.valuesSet
555,497✔
924

171,013✔
925
        if (ObjectUtils.isObject(this.expressionMap.valuesSet))
171,013✔
926
            return [this.expressionMap.valuesSet]
555,497✔
927

28✔
928
        throw new InsertValuesMissingError()
28✔
929
    }
28✔
930

241,087✔
931
    /**
241,087✔
932
     * Checks if column is an auto-generated primary key, but the current insertion specifies a value for it.
241,087✔
933
     *
241,087✔
934
     * @param column
241,087✔
935
     */
241,087✔
936
    protected isOverridingAutoIncrementBehavior(
241,087✔
937
        column: ColumnMetadata,
60,552✔
938
    ): boolean {
60,552✔
939
        return (
60,552✔
940
            column.isPrimary &&
60,552✔
941
            column.isGenerated &&
60,552✔
942
            column.generationStrategy === "increment" &&
60,552✔
943
            this.getValueSets().some(
15,876✔
944
                (valueSet) =>
15,876✔
945
                    column.getEntityValue(valueSet) !== undefined &&
21,618✔
946
                    column.getEntityValue(valueSet) !== null,
15,876✔
947
            )
60,552✔
948
        )
60,552✔
949
    }
60,552✔
950

241,087✔
951
    /**
241,087✔
952
     * Creates MERGE express used to perform insert query.
241,087✔
953
     */
241,087✔
954
    protected createMergeExpression() {
241,087✔
955
        if (!this.connection.driver.supportedUpsertTypes.includes("merge-into"))
138✔
956
            throw new TypeORMError(
138!
957
                `Upsert type "merge-into" is not supported by current database driver`,
×
958
            )
×
959

138✔
960
        if (
138✔
961
            this.expressionMap.onUpdate?.upsertType &&
138✔
962
            this.expressionMap.onUpdate.upsertType !== "merge-into"
114✔
963
        ) {
138!
964
            throw new TypeORMError(
×
965
                `Upsert type "${this.expressionMap.onUpdate.upsertType}" is not supported by current database driver`,
×
966
            )
×
967
        }
×
968
        // const mainAlias = this.expressionMap.mainAlias!
138✔
969
        const tableName = this.getTableName(this.getMainTableName())
138✔
970
        const tableAlias = this.escape(this.alias)
138✔
971
        const columns = this.getInsertedColumns()
138✔
972
        const columnsExpression = this.createColumnNamesExpression()
138✔
973

138✔
974
        let query = `MERGE INTO ${tableName} ${this.escape(this.alias)}`
138✔
975

138✔
976
        const mergeSourceAlias = this.escape("mergeIntoSource")
138✔
977

138✔
978
        const mergeSourceExpression =
138✔
979
            this.createMergeIntoSourceExpression(mergeSourceAlias)
138✔
980

138✔
981
        query += ` ${mergeSourceExpression}`
138✔
982

138✔
983
        // build on condition
138✔
984
        if (this.expressionMap.onIgnore) {
138✔
985
            const primaryKey = columns.find((column) => column.isPrimary)
6✔
986
            if (primaryKey) {
6✔
987
                query += ` ON (${tableAlias}.${this.escape(
6✔
988
                    primaryKey.databaseName,
6✔
989
                )} = ${mergeSourceAlias}.${this.escape(
6✔
990
                    primaryKey.databaseName,
6✔
991
                )})`
6✔
992
            } else {
6!
993
                query += `ON (${this.expressionMap
×
994
                    .mainAlias!.metadata.uniques.map((unique) => {
×
995
                        return `(${unique.columns
×
996
                            .map((column) => {
×
997
                                return `${tableAlias}.${this.escape(
×
998
                                    column.databaseName,
×
999
                                )} = ${mergeSourceAlias}.${this.escape(
×
1000
                                    column.databaseName,
×
1001
                                )}`
×
1002
                            })
×
1003
                            .join(" AND ")})`
×
1004
                    })
×
1005
                    .join(" OR ")})`
×
1006
            }
×
1007
        } else if (this.expressionMap.onUpdate) {
138✔
1008
            const { conflict, indexPredicate } = this.expressionMap.onUpdate
132✔
1009

132✔
1010
            if (indexPredicate) {
132!
1011
                throw new TypeORMError(
×
1012
                    `indexPredicate option is not supported by upsert type "merge-into"`,
×
1013
                )
×
1014
            }
×
1015

132✔
1016
            if (Array.isArray(conflict)) {
132✔
1017
                query += ` ON (${conflict
132✔
1018
                    .map(
132✔
1019
                        (column) =>
132✔
1020
                            `${tableAlias}.${this.escape(
132✔
1021
                                column,
132✔
1022
                            )} = ${mergeSourceAlias}.${this.escape(column)}`,
132✔
1023
                    )
132✔
1024
                    .join(" AND ")})`
132✔
1025
            } else if (conflict) {
132!
1026
                query += ` ON (${tableAlias}.${this.escape(
×
1027
                    conflict,
×
1028
                )} = ${mergeSourceAlias}.${this.escape(conflict)})`
×
1029
            } else {
×
1030
                query += `ON (${this.expressionMap
×
1031
                    .mainAlias!.metadata.uniques.map((unique) => {
×
1032
                        return `(${unique.columns
×
1033
                            .map((column) => {
×
1034
                                return `${tableAlias}.${this.escape(
×
1035
                                    column.databaseName,
×
1036
                                )} = ${mergeSourceAlias}.${this.escape(
×
1037
                                    column.databaseName,
×
1038
                                )}`
×
1039
                            })
×
1040
                            .join(" AND ")})`
×
1041
                    })
×
1042
                    .join(" OR ")})`
×
1043
            }
×
1044
        }
132✔
1045

138✔
1046
        if (this.expressionMap.onUpdate) {
138✔
1047
            const {
132✔
1048
                overwrite,
132✔
1049
                columns,
132✔
1050
                conflict,
132✔
1051
                skipUpdateIfNoValuesChanged,
132✔
1052
            } = this.expressionMap.onUpdate
132✔
1053
            let updateExpression = ""
132✔
1054

132✔
1055
            if (Array.isArray(overwrite)) {
132✔
1056
                updateExpression += (overwrite || columns)
132!
1057
                    ?.filter((column) => !conflict?.includes(column))
132✔
1058
                    .map(
132✔
1059
                        (column) =>
132✔
1060
                            `${tableAlias}.${this.escape(
162✔
1061
                                column,
162✔
1062
                            )} = ${mergeSourceAlias}.${this.escape(column)}`,
132✔
1063
                    )
132✔
1064
                    .join(", ")
132✔
1065
            }
132✔
1066

132✔
1067
            if (Array.isArray(overwrite) && skipUpdateIfNoValuesChanged) {
132✔
1068
                this.expressionMap.onUpdate.overwriteCondition ??= []
6✔
1069
                const wheres = overwrite.map<WhereClause>((column) => ({
6✔
1070
                    type: "or",
6✔
1071
                    condition: {
6✔
1072
                        operator: "notEqual",
6✔
1073
                        parameters: [
6✔
1074
                            `${tableAlias}.${this.escape(column)}`,
6✔
1075
                            `${mergeSourceAlias}.${this.escape(column)}`,
6✔
1076
                        ],
6✔
1077
                    },
6✔
1078
                }))
6✔
1079
                this.expressionMap.onUpdate.overwriteCondition.push({
6✔
1080
                    type: "and",
6✔
1081
                    condition: wheres,
6✔
1082
                })
6✔
1083
            }
6✔
1084
            const mergeCondition = this.createUpsertConditionExpression()
132✔
1085
            if (updateExpression.trim()) {
132✔
1086
                if (
126✔
1087
                    (this.connection.driver.options.type === "mssql" ||
126!
1088
                        this.connection.driver.options.type === "sap") &&
126!
1089
                    mergeCondition != ""
84✔
1090
                ) {
126!
1091
                    query += ` WHEN MATCHED AND ${mergeCondition} THEN UPDATE SET ${updateExpression}`
8✔
1092
                } else {
126✔
1093
                    query += ` WHEN MATCHED THEN UPDATE SET ${updateExpression}`
118✔
1094
                    if (mergeCondition != "") {
118!
1095
                        query += ` WHERE ${mergeCondition}`
4✔
1096
                    }
4✔
1097
                }
118✔
1098
            }
126✔
1099
        }
132✔
1100

138✔
1101
        const valuesExpression =
138✔
1102
            this.createMergeIntoInsertValuesExpression(mergeSourceAlias)
138✔
1103
        const returningExpression =
138✔
1104
            this.connection.driver.options.type === "mssql"
138✔
1105
                ? this.createReturningExpression("insert")
138!
1106
                : null
138!
1107

138✔
1108
        query += " WHEN NOT MATCHED THEN INSERT"
138✔
1109

138✔
1110
        // add columns expression
138✔
1111
        if (columnsExpression) {
138✔
1112
            query += `(${columnsExpression})`
138✔
1113
        }
138✔
1114

138✔
1115
        // add VALUES expression
138✔
1116
        if (valuesExpression) {
138✔
1117
            query += ` VALUES ${valuesExpression}`
138✔
1118
        }
138✔
1119

138✔
1120
        // add OUTPUT expression
138✔
1121
        if (
138✔
1122
            returningExpression &&
138!
1123
            this.connection.driver.options.type === "mssql"
24✔
1124
        ) {
138!
1125
            query += ` OUTPUT ${returningExpression}`
24✔
1126
        }
24✔
1127
        if (this.connection.driver.options.type === "mssql") {
138!
1128
            query += `;`
46✔
1129
        }
46✔
1130
        return query
138✔
1131
    }
138✔
1132

241,087✔
1133
    /**
241,087✔
1134
     * Creates list of values needs to be inserted in the VALUES expression.
241,087✔
1135
     */
241,087✔
1136
    protected createMergeIntoSourceExpression(
241,087✔
1137
        mergeSourceAlias: string,
138✔
1138
    ): string {
138✔
1139
        const valueSets = this.getValueSets()
138✔
1140
        const columns = this.getInsertedColumns()
138✔
1141

138✔
1142
        let expression = "USING ("
138✔
1143
        // if column metadatas are given then apply all necessary operations with values
138✔
1144
        if (columns.length > 0) {
138✔
1145
            if (this.connection.driver.options.type === "mssql") {
138!
1146
                expression += "VALUES "
46✔
1147
            }
46✔
1148
            valueSets.forEach((valueSet, valueSetIndex) => {
138✔
1149
                columns.forEach((column, columnIndex) => {
198✔
1150
                    if (columnIndex === 0) {
798✔
1151
                        if (this.connection.driver.options.type === "mssql") {
198!
1152
                            expression += "("
66✔
1153
                        } else {
198!
1154
                            expression += "SELECT "
132✔
1155
                        }
132✔
1156
                    }
198✔
1157

798✔
1158
                    const value = column.getEntityValue(valueSet)
798✔
1159

798✔
1160
                    if (
798✔
1161
                        value === undefined &&
798✔
1162
                        !(
324✔
1163
                            column.isGenerated &&
324✔
1164
                            column.generationStrategy === "uuid" &&
324✔
1165
                            !this.connection.driver.isUUIDGenerationSupported()
36✔
1166
                        )
798✔
1167
                    ) {
798✔
1168
                        if (
300✔
1169
                            column.default !== undefined &&
300✔
1170
                            column.default !== null
60✔
1171
                        ) {
300✔
1172
                            // try to use default defined in the column
60✔
1173
                            expression +=
60✔
1174
                                this.connection.driver.normalizeDefault(column)
60✔
1175
                        } else {
300✔
1176
                            expression += "NULL" // otherwise simply use NULL and pray if column is nullable
240✔
1177
                        }
240✔
1178
                    } else if (value === null) {
798!
1179
                        expression += "NULL"
×
1180
                    } else {
498✔
1181
                        expression += this.createColumnValueExpression(
498✔
1182
                            valueSets,
498✔
1183
                            valueSetIndex,
498✔
1184
                            column,
498✔
1185
                        )
498✔
1186
                    }
498✔
1187

798✔
1188
                    if (this.connection.driver.options.type !== "mssql")
798✔
1189
                        expression += ` AS ${this.escape(column.databaseName)}`
798!
1190

798✔
1191
                    if (columnIndex === columns.length - 1) {
798✔
1192
                        if (valueSetIndex === valueSets.length - 1) {
198✔
1193
                            if (
138✔
1194
                                ["oracle", "sap"].includes(
138✔
1195
                                    this.connection.driver.options.type,
138✔
1196
                                )
138✔
1197
                            ) {
138!
1198
                                expression +=
92✔
1199
                                    " FROM " +
92✔
1200
                                    this.connection.driver.dummyTableName
92✔
1201
                            } else if (
138!
1202
                                this.connection.driver.options.type === "mssql"
46✔
1203
                            ) {
46✔
1204
                                expression += ")"
46✔
1205
                            }
46✔
1206
                        } else {
198✔
1207
                            if (
60✔
1208
                                ["oracle", "sap"].includes(
60✔
1209
                                    this.connection.driver.options.type,
60✔
1210
                                ) &&
60!
1211
                                valueSets.length > 1
40✔
1212
                            ) {
60!
1213
                                expression +=
40✔
1214
                                    " FROM " +
40✔
1215
                                    this.connection.driver.dummyTableName +
40✔
1216
                                    " UNION ALL "
40✔
1217
                            } else if (
60!
1218
                                this.connection.driver.options.type === "mssql"
20✔
1219
                            ) {
20✔
1220
                                expression += "), "
20✔
1221
                            } else {
20!
1222
                                expression += " UNION ALL "
×
1223
                            }
×
1224
                        }
60✔
1225
                    } else {
798✔
1226
                        expression += ", "
600✔
1227
                    }
600✔
1228
                })
198✔
1229
            })
138✔
1230
        } else {
138!
1231
            // for tables without metadata
×
1232
            throw new TypeORMError(
×
1233
                'Upsert type "merge-into" is not supported without metadata tables',
×
1234
            )
×
1235
        }
×
1236
        expression += `) ${mergeSourceAlias}`
138✔
1237
        if (this.connection.driver.options.type === "mssql")
138✔
1238
            expression += ` (${columns
138!
1239
                .map((column) => this.escape(column.databaseName))
46✔
1240
                .join(", ")})`
46✔
1241
        return expression
138✔
1242
    }
138✔
1243

241,087✔
1244
    /**
241,087✔
1245
     * Creates list of values needs to be inserted in the VALUES expression.
241,087✔
1246
     */
241,087✔
1247
    protected createMergeIntoInsertValuesExpression(
241,087✔
1248
        mergeSourceAlias: string,
138✔
1249
    ): string {
138✔
1250
        const columns = this.getInsertedColumns()
138✔
1251

138✔
1252
        let expression = ""
138✔
1253
        // if column metadatas are given then apply all necessary operations with values
138✔
1254
        if (columns.length > 0) {
138✔
1255
            columns.forEach((column, columnIndex) => {
138✔
1256
                if (columnIndex === 0) {
570✔
1257
                    expression += "("
138✔
1258
                }
138✔
1259

570✔
1260
                if (
570✔
1261
                    (column.isGenerated &&
570✔
1262
                        column.generationStrategy === "uuid" &&
570✔
1263
                        this.connection.driver.isUUIDGenerationSupported()) ||
570✔
1264
                    (column.isGenerated && column.generationStrategy !== "uuid")
562!
1265
                ) {
570!
1266
                    expression += `DEFAULT`
20✔
1267
                } else {
570✔
1268
                    expression += `${mergeSourceAlias}.${this.escape(
550✔
1269
                        column.databaseName,
550✔
1270
                    )}`
550✔
1271
                }
550✔
1272

570✔
1273
                if (columnIndex === columns.length - 1) {
570✔
1274
                    expression += ")"
138✔
1275
                } else {
570✔
1276
                    expression += ", "
432✔
1277
                }
432✔
1278
            })
138✔
1279
        } else {
138!
1280
            // for tables without metadata
×
1281
            throw new TypeORMError(
×
1282
                'Upsert type "merge-into" is not supported without metadata tables',
×
1283
            )
×
1284
        }
×
1285
        if (expression === "()") return ""
138!
1286
        return expression
138✔
1287
    }
138✔
1288

241,087✔
1289
    /**
241,087✔
1290
     * Create upsert search condition expression.
241,087✔
1291
     */
241,087✔
1292
    protected createUpsertConditionExpression() {
241,087✔
1293
        if (!this.expressionMap.onUpdate.overwriteCondition) return ""
199!
1294
        const conditionsArray = []
79✔
1295

79✔
1296
        const whereExpression = this.createWhereClausesExpression(
79✔
1297
            this.expressionMap.onUpdate.overwriteCondition,
79✔
1298
        )
79✔
1299

79✔
1300
        if (whereExpression.length > 0 && whereExpression !== "1=1") {
199✔
1301
            conditionsArray.push(whereExpression)
79✔
1302
        }
79✔
1303

79✔
1304
        if (this.expressionMap.mainAlias!.hasMetadata) {
79✔
1305
            const metadata = this.expressionMap.mainAlias!.metadata
79✔
1306
            // Adds the global condition of "non-deleted" for the entity with delete date columns in select query.
79✔
1307
            if (
79✔
1308
                this.expressionMap.queryType === "select" &&
79!
1309
                !this.expressionMap.withDeleted &&
79!
1310
                metadata.deleteDateColumn
×
1311
            ) {
79!
1312
                const column = this.expressionMap.aliasNamePrefixingEnabled
×
1313
                    ? this.expressionMap.mainAlias!.name +
×
1314
                      "." +
×
1315
                      metadata.deleteDateColumn.propertyName
×
1316
                    : metadata.deleteDateColumn.propertyName
×
1317

×
1318
                const condition = `${column} IS NULL`
×
1319
                conditionsArray.push(condition)
×
1320
            }
×
1321

79✔
1322
            if (metadata.discriminatorColumn && metadata.parentEntityMetadata) {
79!
1323
                const column = this.expressionMap.aliasNamePrefixingEnabled
×
1324
                    ? this.expressionMap.mainAlias!.name +
×
1325
                      "." +
×
1326
                      metadata.discriminatorColumn.databaseName
×
1327
                    : metadata.discriminatorColumn.databaseName
×
1328

×
1329
                const condition = `${column} IN (:...discriminatorColumnValues)`
×
1330
                conditionsArray.push(condition)
×
1331
            }
×
1332
        }
79✔
1333

79✔
1334
        if (this.expressionMap.extraAppendedAndWhereCondition) {
199!
1335
            const condition = this.expressionMap.extraAppendedAndWhereCondition
×
1336
            conditionsArray.push(condition)
×
1337
        }
×
1338

79✔
1339
        let condition = ""
79✔
1340

79✔
1341
        if (!conditionsArray.length) {
199!
1342
            condition += ""
×
1343
        } else if (conditionsArray.length === 1) {
199✔
1344
            condition += `${conditionsArray[0]}`
79✔
1345
        } else {
79!
1346
            condition += `( ${conditionsArray.join(" ) AND ( ")} )`
×
1347
        }
×
1348

79✔
1349
        return condition
79✔
1350
    }
79✔
1351

241,087✔
1352
    protected createColumnValueExpression(
241,087✔
1353
        valueSets: ObjectLiteral[],
2,333,606✔
1354
        valueSetIndex: number,
2,333,606✔
1355
        column: ColumnMetadata,
2,333,606✔
1356
    ): string {
2,333,606✔
1357
        const valueSet = valueSets[valueSetIndex]
2,333,606✔
1358
        let expression = ""
2,333,606✔
1359

2,333,606✔
1360
        // extract real value from the entity
2,333,606✔
1361
        let value = column.getEntityValue(valueSet)
2,333,606✔
1362

2,333,606✔
1363
        // if column is relational and value is an object then get real referenced column value from this object
2,333,606✔
1364
        // for example column value is { question: { id: 1 } }, value will be equal to { id: 1 }
2,333,606✔
1365
        // and we extract "1" from this object
2,333,606✔
1366
        /*if (column.referencedColumn && value instanceof Object && !(typeof value === "function")) { // todo: check if we still need it since getEntityValue already has similar code
2,333,606✔
1367
            value = column.referencedColumn.getEntityValue(value);
2,333,606✔
1368
        }*/
2,333,606✔
1369

2,333,606✔
1370
        if (!(typeof value === "function")) {
2,333,606✔
1371
            // make sure our value is normalized by a driver
2,333,424✔
1372
            value = this.connection.driver.preparePersistentValue(value, column)
2,333,424✔
1373
        }
2,333,424✔
1374

2,333,606✔
1375
        // newly inserted entities always have a version equal to 1 (first version)
2,333,606✔
1376
        // also, user-specified version must be empty
2,333,606✔
1377
        if (column.isVersion && value === undefined) {
2,333,606✔
1378
            expression += "1"
626✔
1379

626✔
1380
            // } else if (column.isNestedSetLeft) {
626✔
1381
            //     const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
626✔
1382
            //     const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
626✔
1383
            //     const subQuery = `(SELECT c.max + 1 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
626✔
1384
            //     expression += subQuery;
626✔
1385
            //
626✔
1386
            // } else if (column.isNestedSetRight) {
626✔
1387
            //     const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
626✔
1388
            //     const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
626✔
1389
            //     const subQuery = `(SELECT c.max + 2 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
626✔
1390
            //     expression += subQuery;
626✔
1391
        } else if (column.isDiscriminator) {
2,333,606✔
1392
            expression += this.createParameter(
1,577✔
1393
                this.expressionMap.mainAlias!.metadata.discriminatorValue,
1,577✔
1394
            )
1,577✔
1395
            // return "1";
1,577✔
1396

1,577✔
1397
            // for create and update dates we insert current date
1,577✔
1398
            // no, we don't do it because this constant is already in "default" value of the column
1,577✔
1399
            // with extended timestamp functionality, like CURRENT_TIMESTAMP(6) for example
1,577✔
1400
            // } else if (column.isCreateDate || column.isUpdateDate) {
1,577✔
1401
            //     return "CURRENT_TIMESTAMP";
1,577✔
1402

1,577✔
1403
            // if column is generated uuid and database does not support its generation and custom generated value was not provided by a user - we generate a new uuid value for insertion
1,577✔
1404
        } else if (
2,332,980✔
1405
            column.isGenerated &&
2,331,403✔
1406
            column.generationStrategy === "uuid" &&
2,331,403✔
1407
            !this.connection.driver.isUUIDGenerationSupported() &&
2,331,403!
1408
            value === undefined
2,445✔
1409
        ) {
2,331,403!
1410
            value = uuidv4()
1,432✔
1411
            expression += this.createParameter(value)
1,432✔
1412

1,432✔
1413
            if (!(valueSetIndex in this.expressionMap.locallyGenerated)) {
1,432✔
1414
                this.expressionMap.locallyGenerated[valueSetIndex] = {}
1,390✔
1415
            }
1,390✔
1416
            column.setEntityValue(
1,432✔
1417
                this.expressionMap.locallyGenerated[valueSetIndex],
1,432✔
1418
                value,
1,432✔
1419
            )
1,432✔
1420

1,432✔
1421
            // if value for this column was not provided then insert default value
1,432✔
1422
        } else if (value === undefined) {
2,331,403✔
1423
            if (
538,268✔
1424
                (this.connection.driver.options.type === "oracle" &&
538,268!
1425
                    valueSets.length > 1) ||
538,268✔
1426
                DriverUtils.isSQLiteFamily(this.connection.driver) ||
538,268!
1427
                this.connection.driver.options.type === "sap" ||
538,268!
1428
                this.connection.driver.options.type === "spanner"
479,870✔
1429
            ) {
538,268!
1430
                // unfortunately sqlite does not support DEFAULT expression in INSERT queries
58,398✔
1431
                if (column.default !== undefined && column.default !== null) {
58,398✔
1432
                    // try to use default defined in the column
4,062✔
1433
                    expression +=
4,062✔
1434
                        this.connection.driver.normalizeDefault(column)
4,062✔
1435
                } else if (
58,398✔
1436
                    this.connection.driver.options.type === "spanner" &&
54,336!
1437
                    column.isGenerated &&
54,336!
1438
                    column.generationStrategy === "uuid"
×
1439
                ) {
54,336!
1440
                    expression += "GENERATE_UUID()" // Produces a random universally unique identifier (UUID) as a STRING value.
×
1441
                } else {
54,336✔
1442
                    expression += "NULL" // otherwise simply use NULL and pray if column is nullable
54,336✔
1443
                }
54,336✔
1444
            } else {
538,268!
1445
                expression += "DEFAULT"
479,870✔
1446
            }
479,870✔
1447
        } else if (
2,329,971✔
1448
            value === null &&
1,791,703✔
1449
            (this.connection.driver.options.type === "spanner" ||
1,626✔
1450
                this.connection.driver.options.type === "oracle")
1,626✔
1451
        ) {
1,791,703!
1452
            expression += "NULL"
110✔
1453

110✔
1454
            // support for SQL expressions in queries
110✔
1455
        } else if (typeof value === "function") {
1,791,703✔
1456
            expression += value()
188✔
1457

188✔
1458
            // just any other regular value
188✔
1459
        } else {
1,791,593✔
1460
            if (this.connection.driver.options.type === "mssql")
1,791,405✔
1461
                value = (
1,791,405!
1462
                    this.connection.driver as SqlServerDriver
48,808✔
1463
                ).parametrizeValue(column, value)
48,808✔
1464

1,791,405✔
1465
            // we need to store array values in a special class to make sure parameter replacement will work correctly
1,791,405✔
1466
            // if (value instanceof Array)
1,791,405✔
1467
            //     value = new ArrayParameter(value);
1,791,405✔
1468

1,791,405✔
1469
            const paramName = this.createParameter(value)
1,791,405✔
1470

1,791,405✔
1471
            if (
1,791,405✔
1472
                (DriverUtils.isMySQLFamily(this.connection.driver) ||
1,791,405!
1473
                    this.connection.driver.options.type === "aurora-mysql") &&
1,791,405!
1474
                this.connection.driver.spatialTypes.includes(column.type)
595,797✔
1475
            ) {
1,791,405!
1476
                const useLegacy = (
56✔
1477
                    this.connection.driver as MysqlDriver | AuroraMysqlDriver
56✔
1478
                ).options.legacySpatialSupport
56✔
1479
                const geomFromText = useLegacy
56✔
1480
                    ? "GeomFromText"
56✔
1481
                    : "ST_GeomFromText"
56✔
1482
                if (column.srid != null) {
56✔
1483
                    expression += `${geomFromText}(${paramName}, ${column.srid})`
24✔
1484
                } else {
40✔
1485
                    expression += `${geomFromText}(${paramName})`
32✔
1486
                }
32✔
1487
            } else if (
1,791,405✔
1488
                DriverUtils.isPostgresFamily(this.connection.driver) &&
1,791,349!
1489
                this.connection.driver.spatialTypes.includes(column.type)
771,371✔
1490
            ) {
1,791,349!
1491
                if (column.srid != null) {
44!
1492
                    expression += `ST_SetSRID(ST_GeomFromGeoJSON(${paramName}), ${column.srid})::${column.type}`
×
1493
                } else {
44✔
1494
                    expression += `ST_GeomFromGeoJSON(${paramName})::${column.type}`
44✔
1495
                }
44✔
1496
            } else if (
1,791,349✔
1497
                this.connection.driver.options.type === "mssql" &&
1,791,305!
1498
                this.connection.driver.spatialTypes.includes(column.type)
48,808✔
1499
            ) {
1,791,305!
1500
                expression +=
14✔
1501
                    column.type +
14✔
1502
                    "::STGeomFromText(" +
14✔
1503
                    paramName +
14✔
1504
                    ", " +
14✔
1505
                    (column.srid || "0") +
14✔
1506
                    ")"
14✔
1507
            } else {
1,791,305✔
1508
                expression += paramName
1,791,291✔
1509
            }
1,791,291✔
1510
        }
1,791,405✔
1511
        return expression
2,333,606✔
1512
    }
2,333,606✔
1513
}
241,087✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc