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

typeorm / typeorm / 23390157208

21 Mar 2026 10:26PM UTC coverage: 56.678% (-16.6%) from 73.277%
23390157208

Pull #12252

github

web-flow
Merge 5b60ba41c into 7038fa166
Pull Request #12252: fix: unskip cascade soft remove test

17767 of 26580 branches covered (66.84%)

Branch coverage included in aggregate %.

64033 of 117744 relevant lines covered (54.38%)

1514.83 hits per line

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

81.73
/src/driver/sqlserver/SqlServerDriver.ts
1
import type { ObjectLiteral } from "../../common/ObjectLiteral"
28✔
2
import type { DataSource } from "../../data-source/DataSource"
28✔
3
import { TypeORMError } from "../../error"
28✔
4
import { ConnectionIsNotSetError } from "../../error/ConnectionIsNotSetError"
28✔
5
import { DriverPackageNotInstalledError } from "../../error/DriverPackageNotInstalledError"
28✔
6
import { FindOperator } from "../../find-options/FindOperator"
28✔
7
import type { ColumnMetadata } from "../../metadata/ColumnMetadata"
28✔
8
import type { EntityMetadata } from "../../metadata/EntityMetadata"
28✔
9
import { PlatformTools } from "../../platform/PlatformTools"
28✔
10
import { RdbmsSchemaBuilder } from "../../schema-builder/RdbmsSchemaBuilder"
28✔
11
import type { Table } from "../../schema-builder/table/Table"
28✔
12
import { TableColumn } from "../../schema-builder/table/TableColumn"
28✔
13
import type { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
28✔
14
import type { View } from "../../schema-builder/view/View"
28✔
15
import { ApplyValueTransformers } from "../../util/ApplyValueTransformers"
28✔
16
import { DateUtils } from "../../util/DateUtils"
28✔
17
import { InstanceChecker } from "../../util/InstanceChecker"
28✔
18
import { OrmUtils } from "../../util/OrmUtils"
28✔
19
import type { Driver, ReturningType } from "../Driver"
28✔
20
import { DriverUtils } from "../DriverUtils"
28✔
21
import type { ColumnType } from "../types/ColumnTypes"
28✔
22
import type { CteCapabilities } from "../types/CteCapabilities"
28✔
23
import type { DataTypeDefaults } from "../types/DataTypeDefaults"
28✔
24
import type { MappedColumnTypes } from "../types/MappedColumnTypes"
28✔
25
import type { ReplicationMode } from "../types/ReplicationMode"
28✔
26
import type { UpsertType } from "../types/UpsertType"
28✔
27
import { MssqlParameter } from "./MssqlParameter"
28✔
28
import type { SqlServerConnectionCredentialsOptions } from "./SqlServerConnectionCredentialsOptions"
28✔
29
import type { SqlServerDataSourceOptions } from "./SqlServerDataSourceOptions"
28✔
30
import { SqlServerQueryRunner } from "./SqlServerQueryRunner"
28✔
31

28✔
32
/**
28✔
33
 * Organizes communication with SQL Server DBMS.
28✔
34
 */
28✔
35
export class SqlServerDriver implements Driver {
28✔
36
    // -------------------------------------------------------------------------
28✔
37
    // Public Properties
28✔
38
    // -------------------------------------------------------------------------
28✔
39

28✔
40
    /**
28✔
41
     * Connection used by driver.
28✔
42
     */
28✔
43
    connection: DataSource
28✔
44

28✔
45
    /**
28✔
46
     * SQL Server library.
28✔
47
     */
28✔
48
    mssql: any
28✔
49

28✔
50
    /**
28✔
51
     * Pool for master database.
28✔
52
     */
28✔
53
    master: any
28✔
54

28✔
55
    /**
28✔
56
     * Pool for slave databases.
28✔
57
     * Used in replication.
28✔
58
     */
28✔
59
    slaves: any[] = []
28✔
60

28✔
61
    // -------------------------------------------------------------------------
28✔
62
    // Public Implemented Properties
28✔
63
    // -------------------------------------------------------------------------
28✔
64

28✔
65
    /**
28✔
66
     * Connection options.
28✔
67
     */
28✔
68
    options: SqlServerDataSourceOptions
28✔
69

28✔
70
    /**
28✔
71
     * Database name used to perform all write queries.
28✔
72
     */
28✔
73
    database?: string
28✔
74

28✔
75
    /**
28✔
76
     * Schema name used to perform all write queries.
28✔
77
     */
28✔
78
    schema?: string
28✔
79

28✔
80
    /**
28✔
81
     * Schema that's used internally by SQL Server for object resolution.
28✔
82
     *
28✔
83
     * Because we never set this we have to track it in separately from the `schema` so
28✔
84
     * we know when we have to specify the full schema or not.
28✔
85
     *
28✔
86
     * In most cases this will be `dbo`.
28✔
87
     */
28✔
88
    searchSchema?: string
28✔
89

28✔
90
    /**
28✔
91
     * Indicates if replication is enabled.
28✔
92
     */
28✔
93
    isReplicated: boolean = false
28✔
94

28✔
95
    /**
28✔
96
     * Indicates if tree tables are supported by this driver.
28✔
97
     */
28✔
98
    treeSupport = true
28✔
99

28✔
100
    /**
28✔
101
     * Represent transaction support by this driver
28✔
102
     */
28✔
103
    transactionSupport = "simple" as const
28✔
104

28✔
105
    /**
28✔
106
     * Gets list of supported column data types by a driver.
28✔
107
     * @see https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql
28✔
108
     */
28✔
109
    supportedDataTypes: ColumnType[] = [
28✔
110
        "int",
28✔
111
        "bigint",
28✔
112
        "bit",
28✔
113
        "decimal",
28✔
114
        "money",
28✔
115
        "numeric",
28✔
116
        "smallint",
28✔
117
        "smallmoney",
28✔
118
        "tinyint",
28✔
119
        "float",
28✔
120
        "real",
28✔
121
        "date",
28✔
122
        "datetime2",
28✔
123
        "datetime",
28✔
124
        "datetimeoffset",
28✔
125
        "smalldatetime",
28✔
126
        "time",
28✔
127
        "char",
28✔
128
        "varchar",
28✔
129
        "text",
28✔
130
        "nchar",
28✔
131
        "nvarchar",
28✔
132
        "ntext",
28✔
133
        "binary",
28✔
134
        "image",
28✔
135
        "varbinary",
28✔
136
        "hierarchyid",
28✔
137
        "sql_variant",
28✔
138
        "timestamp",
28✔
139
        "uniqueidentifier",
28✔
140
        "xml",
28✔
141
        "geometry",
28✔
142
        "geography",
28✔
143
        "rowversion",
28✔
144
        "vector",
28✔
145
    ]
28✔
146

28✔
147
    /**
28✔
148
     * Returns type of upsert supported by driver if any
28✔
149
     */
28✔
150
    supportedUpsertTypes: UpsertType[] = ["merge-into"]
28✔
151

28✔
152
    /**
28✔
153
     * Gets list of spatial column data types.
28✔
154
     */
28✔
155
    spatialTypes: ColumnType[] = ["geometry", "geography"]
28✔
156

28✔
157
    /**
28✔
158
     * Gets list of column data types that support length by a driver.
28✔
159
     */
28✔
160
    withLengthColumnTypes: ColumnType[] = [
28✔
161
        "char",
28✔
162
        "varchar",
28✔
163
        "nchar",
28✔
164
        "nvarchar",
28✔
165
        "binary",
28✔
166
        "varbinary",
28✔
167
        "vector",
28✔
168
    ]
28✔
169

28✔
170
    /**
28✔
171
     * Gets list of column data types that support precision by a driver.
28✔
172
     */
28✔
173
    withPrecisionColumnTypes: ColumnType[] = [
28✔
174
        "decimal",
28✔
175
        "numeric",
28✔
176
        "time",
28✔
177
        "datetime2",
28✔
178
        "datetimeoffset",
28✔
179
    ]
28✔
180

28✔
181
    /**
28✔
182
     * Gets list of column data types that support scale by a driver.
28✔
183
     */
28✔
184
    withScaleColumnTypes: ColumnType[] = ["decimal", "numeric"]
28✔
185

28✔
186
    /**
28✔
187
     * Orm has special columns and we need to know what database column types should be for those types.
28✔
188
     * Column types are driver dependant.
28✔
189
     */
28✔
190
    mappedDataTypes: MappedColumnTypes = {
28✔
191
        createDate: "datetime2",
28✔
192
        createDateDefault: "getdate()",
28✔
193
        updateDate: "datetime2",
28✔
194
        updateDateDefault: "getdate()",
28✔
195
        deleteDate: "datetime2",
28✔
196
        deleteDateNullable: true,
28✔
197
        version: "int",
28✔
198
        treeLevel: "int",
28✔
199
        migrationId: "int",
28✔
200
        migrationName: "varchar",
28✔
201
        migrationTimestamp: "bigint",
28✔
202
        cacheId: "int",
28✔
203
        cacheIdentifier: "nvarchar",
28✔
204
        cacheTime: "bigint",
28✔
205
        cacheDuration: "int",
28✔
206
        cacheQuery: "nvarchar(MAX)" as any,
28✔
207
        cacheResult: "nvarchar(MAX)" as any,
28✔
208
        metadataType: "varchar",
28✔
209
        metadataDatabase: "varchar",
28✔
210
        metadataSchema: "varchar",
28✔
211
        metadataTable: "varchar",
28✔
212
        metadataName: "varchar",
28✔
213
        metadataValue: "nvarchar(MAX)" as any,
28✔
214
    }
28✔
215

28✔
216
    /**
28✔
217
     * The prefix used for the parameters
28✔
218
     */
28✔
219
    parametersPrefix: string = "@"
28✔
220

28✔
221
    /**
28✔
222
     * Default values of length, precision and scale depends on column data type.
28✔
223
     * Used in the cases when length/precision/scale is not specified by user.
28✔
224
     */
28✔
225
    dataTypeDefaults: DataTypeDefaults = {
28✔
226
        char: { length: 1 },
28✔
227
        nchar: { length: 1 },
28✔
228
        varchar: { length: 255 },
28✔
229
        nvarchar: { length: 255 },
28✔
230
        binary: { length: 1 },
28✔
231
        varbinary: { length: 1 },
28✔
232
        decimal: { precision: 18, scale: 0 },
28✔
233
        numeric: { precision: 18, scale: 0 },
28✔
234
        time: { precision: 7 },
28✔
235
        datetime2: { precision: 7 },
28✔
236
        datetimeoffset: { precision: 7 },
28✔
237
        vector: { length: 255 }, // default length if not provided a value
28✔
238
    }
28✔
239

28✔
240
    cteCapabilities: CteCapabilities = {
28✔
241
        enabled: true,
28✔
242
        // todo: enable it for SQL Server - it's partially supported, but there are issues with generation of non-standard OUTPUT clause
28✔
243
        writable: false,
28✔
244
    }
28✔
245

28✔
246
    /**
28✔
247
     * Max length allowed by MSSQL Server for aliases (identifiers).
28✔
248
     * @see https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server
28✔
249
     */
28✔
250
    maxAliasLength = 128
28✔
251

28✔
252
    // -------------------------------------------------------------------------
28✔
253
    // Constructor
28✔
254
    // -------------------------------------------------------------------------
28✔
255

28✔
256
    constructor(connection: DataSource) {
28✔
257
        this.connection = connection
2✔
258
        this.options = connection.options as SqlServerDataSourceOptions
2✔
259
        this.isReplicated = this.options.replication ? true : false
2!
260

2✔
261
        // load mssql package
2✔
262
        this.loadDependencies()
2✔
263

2✔
264
        this.database = DriverUtils.buildDriverOptions(
2✔
265
            this.options.replication
2✔
266
                ? this.options.replication.master
2!
267
                : this.options,
2✔
268
        ).database
2✔
269
        this.schema = DriverUtils.buildDriverOptions(this.options).schema
2✔
270

2✔
271
        // Object.assign(connection.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
2✔
272
        // validate options to make sure everything is set
2✔
273
        // if (!this.options.host)
2✔
274
        // throw new DriverOptionNotSetError("host");
2✔
275
        // if (!this.options.username)
2✔
276
        //     throw new DriverOptionNotSetError("username");
2✔
277
        // if (!this.options.database)
2✔
278
        //     throw new DriverOptionNotSetError("database");
2✔
279
    }
2✔
280

28✔
281
    // -------------------------------------------------------------------------
28✔
282
    // Public Implemented Methods
28✔
283
    // -------------------------------------------------------------------------
28✔
284

28✔
285
    /**
28✔
286
     * Performs connection to the database.
28✔
287
     * Based on pooling options, it can either create connection immediately,
28✔
288
     * either create a pool and create connection when needed.
28✔
289
     */
28✔
290
    async connect(): Promise<void> {
28✔
291
        if (this.options.replication) {
2!
292
            this.slaves = await Promise.all(
×
293
                this.options.replication.slaves.map((slave) => {
×
294
                    return this.createPool(this.options, slave)
×
295
                }),
×
296
            )
×
297
            this.master = await this.createPool(
×
298
                this.options,
×
299
                this.options.replication.master,
×
300
            )
×
301
        } else {
2✔
302
            this.master = await this.createPool(this.options, this.options)
2✔
303
        }
2✔
304

2✔
305
        if (!this.database || !this.searchSchema) {
2✔
306
            const queryRunner = this.createQueryRunner("master")
2✔
307

2✔
308
            if (!this.database) {
2!
309
                this.database = await queryRunner.getCurrentDatabase()
×
310
            }
×
311

2✔
312
            if (!this.searchSchema) {
2✔
313
                this.searchSchema = await queryRunner.getCurrentSchema()
2✔
314
            }
2✔
315

2✔
316
            await queryRunner.release()
2✔
317
        }
2✔
318

2✔
319
        if (!this.schema) {
2✔
320
            this.schema = this.searchSchema
2✔
321
        }
2✔
322
    }
2✔
323

28✔
324
    /**
28✔
325
     * Makes any action after connection (e.g. create extensions in Postgres driver).
28✔
326
     */
28✔
327
    afterConnect(): Promise<void> {
28✔
328
        return Promise.resolve()
2✔
329
    }
2✔
330

28✔
331
    /**
28✔
332
     * Closes connection with the database.
28✔
333
     */
28✔
334
    async disconnect(): Promise<void> {
28✔
335
        if (!this.master) {
2!
336
            throw new ConnectionIsNotSetError("mssql")
×
337
        }
×
338
        await this.closePool(this.master)
2✔
339
        await Promise.all(this.slaves.map((slave) => this.closePool(slave)))
2✔
340
        this.master = undefined
2✔
341
        this.slaves = []
2✔
342
    }
2✔
343

28✔
344
    /**
28✔
345
     * Closes connection pool.
28✔
346
     * @param pool
28✔
347
     */
28✔
348
    protected async closePool(pool: any): Promise<void> {
28✔
349
        return new Promise<void>((ok, fail) => {
2✔
350
            pool.close((err: any) => (err ? fail(err) : ok()))
2!
351
        })
2✔
352
    }
2✔
353

28✔
354
    /**
28✔
355
     * Creates a schema builder used to build and sync a schema.
28✔
356
     */
28✔
357
    createSchemaBuilder() {
28✔
358
        return new RdbmsSchemaBuilder(this.connection)
4✔
359
    }
4✔
360

28✔
361
    /**
28✔
362
     * Creates a query runner used to execute database queries.
28✔
363
     * @param mode
28✔
364
     */
28✔
365
    createQueryRunner(mode: ReplicationMode) {
28✔
366
        return new SqlServerQueryRunner(this, mode)
34✔
367
    }
34✔
368

28✔
369
    /**
28✔
370
     * Replaces parameters in the given sql with special escaping character
28✔
371
     * and an array of parameter names to be passed to a query.
28✔
372
     * @param sql
28✔
373
     * @param parameters
28✔
374
     */
28✔
375
    escapeQueryWithParameters(
28✔
376
        sql: string,
70✔
377
        parameters: ObjectLiteral,
70✔
378
    ): [string, any[]] {
70✔
379
        const escapedParameters: any[] = []
70✔
380
        if (!parameters || !Object.keys(parameters).length)
70✔
381
            return [sql, escapedParameters]
70✔
382

60✔
383
        const parameterIndexMap = new Map<string, number>()
60✔
384
        sql = sql.replace(
60✔
385
            /:(\.\.\.)?([A-Za-z0-9_.]+)/g,
60✔
386
            (full, isArray: string, key: string): string => {
60✔
387
                if (!parameters.hasOwnProperty(key)) {
104!
388
                    return full
×
389
                }
×
390

104✔
391
                if (parameterIndexMap.has(key)) {
104!
392
                    return this.parametersPrefix + parameterIndexMap.get(key)
×
393
                }
×
394

104✔
395
                const value: any = parameters[key]
104✔
396

104✔
397
                if (isArray) {
104!
398
                    return value
×
399
                        .map((v: any) => {
×
400
                            escapedParameters.push(v)
×
401
                            return this.createParameter(
×
402
                                key,
×
403
                                escapedParameters.length - 1,
×
404
                            )
×
405
                        })
×
406
                        .join(", ")
×
407
                }
×
408

104✔
409
                if (typeof value === "function") {
104!
410
                    return value()
×
411
                }
×
412

104✔
413
                escapedParameters.push(value)
104✔
414
                parameterIndexMap.set(key, escapedParameters.length - 1)
104✔
415
                return this.createParameter(key, escapedParameters.length - 1)
104✔
416
            },
60✔
417
        ) // todo: make replace only in value statements, otherwise problems
60✔
418
        return [sql, escapedParameters]
60✔
419
    }
60✔
420

28✔
421
    /**
28✔
422
     * Escapes a column name.
28✔
423
     * @param columnName
28✔
424
     */
28✔
425
    escape(columnName: string): string {
28✔
426
        return `"${columnName.replaceAll('"', '""')}"`
709✔
427
    }
709✔
428

28✔
429
    /**
28✔
430
     * Build full table name with database name, schema name and table name.
28✔
431
     * E.g. myDB.mySchema.myTable
28✔
432
     * @param tableName
28✔
433
     * @param schema
28✔
434
     * @param database
28✔
435
     */
28✔
436
    buildTableName(
28✔
437
        tableName: string,
466✔
438
        schema?: string,
466✔
439
        database?: string,
466✔
440
    ): string {
466✔
441
        const tablePath = [tableName]
466✔
442

466✔
443
        if (schema) {
466✔
444
            tablePath.unshift(schema)
444✔
445
        }
444✔
446

466✔
447
        if (database) {
466✔
448
            if (!schema) {
448✔
449
                tablePath.unshift("")
4✔
450
            }
4✔
451

448✔
452
            tablePath.unshift(database)
448✔
453
        }
448✔
454

466✔
455
        return tablePath.join(".")
466✔
456
    }
466✔
457

28✔
458
    /**
28✔
459
     * Parse a target table name or other types and return a normalized table definition.
28✔
460
     * @param target
28✔
461
     */
28✔
462
    parseTableName(
28✔
463
        target: EntityMetadata | Table | View | TableForeignKey | string,
816✔
464
    ): { database?: string; schema?: string; tableName: string } {
816✔
465
        const driverDatabase = this.database
816✔
466
        const driverSchema = this.schema
816✔
467

816✔
468
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
816✔
469
            const parsed = this.parseTableName(target.name)
268✔
470

268✔
471
            return {
268✔
472
                database: target.database || parsed.database || driverDatabase,
268!
473
                schema: target.schema || parsed.schema || driverSchema,
268!
474
                tableName: parsed.tableName,
268✔
475
            }
268✔
476
        }
268✔
477

548✔
478
        if (InstanceChecker.isTableForeignKey(target)) {
816✔
479
            const parsed = this.parseTableName(target.referencedTableName)
12✔
480

12✔
481
            return {
12✔
482
                database:
12✔
483
                    target.referencedDatabase ||
12✔
484
                    parsed.database ||
12!
485
                    driverDatabase,
12✔
486
                schema:
12✔
487
                    target.referencedSchema || parsed.schema || driverSchema,
12!
488
                tableName: parsed.tableName,
12✔
489
            }
12✔
490
        }
12✔
491

536✔
492
        if (InstanceChecker.isEntityMetadata(target)) {
816✔
493
            // EntityMetadata tableName is never a path
228✔
494

228✔
495
            return {
228✔
496
                database: target.database || driverDatabase,
228✔
497
                schema: target.schema || driverSchema,
228✔
498
                tableName: target.tableName,
228✔
499
            }
228✔
500
        }
228✔
501

308✔
502
        const parts = target.split(".")
308✔
503

308✔
504
        if (parts.length === 3) {
816✔
505
            return {
28✔
506
                database: parts[0] || driverDatabase,
28!
507
                schema: parts[1] || driverSchema,
28✔
508
                tableName: parts[2],
28✔
509
            }
28✔
510
        } else if (parts.length === 2) {
816!
511
            return {
×
512
                database: driverDatabase,
×
513
                schema: parts[0],
×
514
                tableName: parts[1],
×
515
            }
×
516
        } else {
280✔
517
            return {
280✔
518
                database: driverDatabase,
280✔
519
                schema: driverSchema,
280✔
520
                tableName: target,
280✔
521
            }
280✔
522
        }
280✔
523
    }
816✔
524

28✔
525
    /**
28✔
526
     * Prepares given value to a value to be persisted, based on its column type and metadata.
28✔
527
     * @param value
28✔
528
     * @param columnMetadata
28✔
529
     */
28✔
530
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
28✔
531
        if (columnMetadata.transformer)
72✔
532
            value = ApplyValueTransformers.transformTo(
72!
533
                columnMetadata.transformer,
×
534
                value,
×
535
            )
×
536

72✔
537
        if (value === null || value === undefined) return value
72✔
538

44✔
539
        if (columnMetadata.type === Boolean) {
72!
540
            return value === true ? 1 : 0
×
541
        } else if (columnMetadata.type === "date") {
72!
542
            return DateUtils.mixedDateToDate(value, columnMetadata.utc)
×
543
        } else if (columnMetadata.type === "time") {
44!
544
            return DateUtils.mixedTimeToDate(value)
×
545
        } else if (
44✔
546
            columnMetadata.type === "datetime" ||
44✔
547
            columnMetadata.type === "smalldatetime" ||
44✔
548
            columnMetadata.type === Date
44✔
549
        ) {
44!
550
            return DateUtils.mixedDateToDate(value, false, false)
×
551
        } else if (
44✔
552
            columnMetadata.type === "datetime2" ||
44✔
553
            columnMetadata.type === "datetimeoffset"
44✔
554
        ) {
44!
555
            return DateUtils.mixedDateToDate(value, false, true)
×
556
        } else if (columnMetadata.type === "simple-array") {
44!
557
            return DateUtils.simpleArrayToString(value)
×
558
        } else if (columnMetadata.type === "simple-json") {
44!
559
            return DateUtils.simpleJsonToString(value)
×
560
        } else if (columnMetadata.type === "simple-enum") {
44!
561
            return DateUtils.simpleEnumToString(value)
×
562
        } else if (columnMetadata.type === "vector") {
44!
563
            if (Array.isArray(value)) {
×
564
                return JSON.stringify(value)
×
565
            } else {
×
566
                return value
×
567
            }
×
568
        }
×
569

44✔
570
        return value
44✔
571
    }
44✔
572

28✔
573
    /**
28✔
574
     * Prepares given value to a value to be persisted, based on its column type or metadata.
28✔
575
     * @param value
28✔
576
     * @param columnMetadata
28✔
577
     */
28✔
578
    prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
28✔
579
        if (value === null || value === undefined)
268✔
580
            return columnMetadata.transformer
268✔
581
                ? ApplyValueTransformers.transformFrom(
62!
582
                      columnMetadata.transformer,
×
583
                      value,
×
584
                  )
62✔
585
                : value
62✔
586

206✔
587
        if (columnMetadata.type === Boolean) {
268!
588
            value = value ? true : false
×
589
        } else if (
268✔
590
            columnMetadata.type === "datetime" ||
206✔
591
            columnMetadata.type === Date ||
206✔
592
            columnMetadata.type === "datetime2" ||
206✔
593
            columnMetadata.type === "smalldatetime" ||
206✔
594
            columnMetadata.type === "datetimeoffset"
160✔
595
        ) {
206✔
596
            value = DateUtils.normalizeHydratedDate(value)
46✔
597
        } else if (columnMetadata.type === "date") {
206!
598
            value = DateUtils.mixedDateToDateString(value, {
×
599
                utc: columnMetadata.utc,
×
600
            })
×
601
        } else if (columnMetadata.type === "time") {
160!
602
            value = DateUtils.mixedTimeToString(value)
×
603
        } else if (columnMetadata.type === "simple-array") {
160!
604
            value = DateUtils.stringToSimpleArray(value)
×
605
        } else if (columnMetadata.type === "simple-json") {
160!
606
            value = DateUtils.stringToSimpleJson(value)
×
607
        } else if (columnMetadata.type === "simple-enum") {
160!
608
            value = DateUtils.stringToSimpleEnum(value, columnMetadata)
×
609
        } else if (columnMetadata.type === "vector") {
160!
610
            if (typeof value === "string") {
×
611
                try {
×
612
                    value = JSON.parse(value)
×
613
                } catch (e) {
×
614
                    // If parsing fails, return the value as-is
×
615
                }
×
616
            }
×
617
        } else if (columnMetadata.type === Number) {
160✔
618
            // convert to number if number
114✔
619
            value = !isNaN(+value) ? parseInt(value) : value
114!
620
        }
114✔
621

206✔
622
        if (columnMetadata.transformer)
206✔
623
            value = ApplyValueTransformers.transformFrom(
268!
624
                columnMetadata.transformer,
×
625
                value,
×
626
            )
×
627

206✔
628
        return value
206✔
629
    }
206✔
630

28✔
631
    /**
28✔
632
     * Creates a database type from a given column metadata.
28✔
633
     * @param column
28✔
634
     * @param column.type
28✔
635
     * @param column.length
28✔
636
     * @param column.precision
28✔
637
     * @param column.scale
28✔
638
     */
28✔
639
    normalizeType(column: {
28✔
640
        type?: ColumnType
196✔
641
        length?: number | string
196✔
642
        precision?: number | null
196✔
643
        scale?: number
196✔
644
    }): string {
196✔
645
        if (column.type === Number || column.type === "integer") {
196✔
646
            return "int"
80✔
647
        } else if (column.type === String) {
196✔
648
            return "nvarchar"
40✔
649
        } else if (column.type === Date) {
116!
650
            return "datetime"
×
651
        } else if (column.type === Boolean) {
76!
652
            return "bit"
×
653
        } else if (
76✔
654
            typeof column.type === "function" &&
76!
655
            column.type.prototype instanceof Uint8Array
×
656
        ) {
76!
657
            return "binary"
×
658
        } else if (column.type === "uuid") {
76!
659
            return "uniqueidentifier"
×
660
        } else if (
76✔
661
            column.type === "simple-array" ||
76✔
662
            column.type === "simple-json"
76✔
663
        ) {
76!
664
            return "ntext"
×
665
        } else if (column.type === "simple-enum") {
76!
666
            return "nvarchar"
×
667
        } else if (column.type === "dec") {
76!
668
            return "decimal"
×
669
        } else if (column.type === "double precision") {
76!
670
            return "float"
×
671
        } else if (column.type === "rowversion") {
76!
672
            return "timestamp" // the rowversion type's name in SQL server metadata is timestamp
×
673
        } else {
76✔
674
            return (column.type as string) || ""
76!
675
        }
76✔
676
    }
196✔
677

28✔
678
    /**
28✔
679
     * Normalizes "default" value of the column.
28✔
680
     * @param columnMetadata
28✔
681
     */
28✔
682
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
28✔
683
        const defaultValue = columnMetadata.default
68✔
684

68✔
685
        if (typeof defaultValue === "number") {
68!
686
            return `${defaultValue}`
×
687
        }
×
688

68✔
689
        if (typeof defaultValue === "boolean") {
68!
690
            return defaultValue ? "1" : "0"
×
691
        }
×
692

68✔
693
        if (typeof defaultValue === "function") {
68!
694
            const value = defaultValue()
×
695
            if (value.toUpperCase() === "CURRENT_TIMESTAMP") {
×
696
                return "getdate()"
×
697
            }
×
698
            return value
×
699
        }
×
700

68✔
701
        if (typeof defaultValue === "string") {
68!
702
            return `'${defaultValue}'`
×
703
        }
×
704

68✔
705
        if (defaultValue === undefined || defaultValue === null) {
68!
706
            return undefined
68✔
707
        }
68✔
708

×
709
        return `${defaultValue}`
×
710
    }
×
711

28✔
712
    /**
28✔
713
     * Normalizes "isUnique" value of the column.
28✔
714
     * @param column
28✔
715
     */
28✔
716
    normalizeIsUnique(column: ColumnMetadata): boolean {
28✔
717
        return column.entityMetadata.uniques.some(
72✔
718
            (uq) => uq.columns.length === 1 && uq.columns[0] === column,
72✔
719
        )
72✔
720
    }
72✔
721

28✔
722
    /**
28✔
723
     * Returns default column lengths, which is required on column creation.
28✔
724
     * @param column
28✔
725
     */
28✔
726
    getColumnLength(column: ColumnMetadata | TableColumn): string {
28✔
727
        if (column.length) return column.length.toString()
132✔
728

116✔
729
        if (
116✔
730
            column.type === "varchar" ||
116✔
731
            column.type === "nvarchar" ||
132✔
732
            column.type === String
116✔
733
        )
132✔
734
            return "255"
132✔
735

100✔
736
        return ""
100✔
737
    }
100✔
738

28✔
739
    /**
28✔
740
     * Creates column type definition including length, precision and scale
28✔
741
     * @param column
28✔
742
     */
28✔
743
    createFullType(column: TableColumn): string {
28✔
744
        // The Database Engine determines the data type of the computed column by applying the rules
52✔
745
        // of data type precedence to the expressions specified in the formula.
52✔
746
        if (column.asExpression) return ""
52!
747

52✔
748
        let type = column.type
52✔
749

52✔
750
        // Handle vector type with length (dimensions)
52✔
751
        if (column.type === "vector") {
52!
752
            type = `vector(${column.length})`
×
753
        }
×
754
        // used 'getColumnLength()' method, because SqlServer sets `varchar` and `nvarchar` length to 1 by default.
52✔
755
        else if (this.getColumnLength(column)) {
52✔
756
            type += `(${this.getColumnLength(column)})`
8✔
757
        } else if (
52✔
758
            column.precision !== null &&
44✔
759
            column.precision !== undefined &&
44!
760
            column.scale !== null &&
44!
761
            column.scale !== undefined
×
762
        ) {
44!
763
            type += `(${column.precision},${column.scale})`
×
764
        } else if (
44✔
765
            column.precision !== null &&
44✔
766
            column.precision !== undefined
44✔
767
        ) {
44!
768
            type += `(${column.precision})`
×
769
        }
×
770

52✔
771
        if (column.isArray) type += " array"
52!
772

52✔
773
        return type
52✔
774
    }
52✔
775

28✔
776
    /**
28✔
777
     * Obtains a new database connection to a master server.
28✔
778
     * Used for replication.
28✔
779
     * If replication is not setup then returns default connection's database connection.
28✔
780
     */
28✔
781
    obtainMasterConnection(): Promise<any> {
28✔
782
        if (!this.master) {
172!
783
            return Promise.reject(new TypeORMError("Driver not Connected"))
×
784
        }
×
785

172✔
786
        return Promise.resolve(this.master)
172✔
787
    }
172✔
788

28✔
789
    /**
28✔
790
     * Obtains a new database connection to a slave server.
28✔
791
     * Used for replication.
28✔
792
     * If replication is not setup then returns master (default) connection's database connection.
28✔
793
     */
28✔
794
    obtainSlaveConnection(): Promise<any> {
28✔
795
        if (!this.slaves.length) return this.obtainMasterConnection()
10✔
796

×
797
        const random = Math.floor(Math.random() * this.slaves.length)
×
798
        return Promise.resolve(this.slaves[random])
×
799
    }
×
800

28✔
801
    /**
28✔
802
     * Creates generated map of values generated or returned by database after INSERT query.
28✔
803
     * @param metadata
28✔
804
     * @param insertResult
28✔
805
     */
28✔
806
    createGeneratedMap(metadata: EntityMetadata, insertResult: ObjectLiteral) {
28✔
807
        if (!insertResult) return undefined
50✔
808

44✔
809
        return Object.keys(insertResult).reduce((map, key) => {
44✔
810
            const column = metadata.findColumnWithDatabaseName(key)
60✔
811
            if (column) {
60✔
812
                OrmUtils.mergeDeep(
60✔
813
                    map,
60✔
814
                    column.createValueMap(
60✔
815
                        this.prepareHydratedValue(insertResult[key], column),
60✔
816
                    ),
60✔
817
                )
60✔
818
            }
60✔
819
            return map
60✔
820
        }, {} as ObjectLiteral)
44✔
821
    }
44✔
822

28✔
823
    /**
28✔
824
     * Differentiate columns of this table and columns from the given column metadatas columns
28✔
825
     * and returns only changed.
28✔
826
     * @param tableColumns
28✔
827
     * @param columnMetadatas
28✔
828
     */
28✔
829
    findChangedColumns(
28✔
830
        tableColumns: TableColumn[],
12✔
831
        columnMetadatas: ColumnMetadata[],
12✔
832
    ): ColumnMetadata[] {
12✔
833
        return columnMetadatas.filter((columnMetadata) => {
12✔
834
            const tableColumn = tableColumns.find(
36✔
835
                (c) => c.name === columnMetadata.databaseName,
36✔
836
            )
36✔
837
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
36!
838

36✔
839
            const isColumnChanged =
36✔
840
                tableColumn.name !== columnMetadata.databaseName ||
36✔
841
                this.compareColumnType(tableColumn, columnMetadata) ||
36✔
842
                this.compareColumnLength(tableColumn, columnMetadata) ||
36✔
843
                tableColumn.precision !== columnMetadata.precision ||
36✔
844
                tableColumn.scale !== columnMetadata.scale ||
36✔
845
                // || tableColumn.comment !== columnMetadata.comment || // todo
36✔
846
                tableColumn.isGenerated !== columnMetadata.isGenerated ||
36✔
847
                (!tableColumn.isGenerated &&
36✔
848
                    this.lowerDefaultValueIfNecessary(
32✔
849
                        this.normalizeDefault(columnMetadata),
32✔
850
                    ) !==
32✔
851
                        this.lowerDefaultValueIfNecessary(
32✔
852
                            tableColumn.default,
32✔
853
                        )) || // we included check for generated here, because generated columns already can have default values
36✔
854
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
36✔
855
                tableColumn.isNullable !== columnMetadata.isNullable ||
36✔
856
                tableColumn.asExpression !== columnMetadata.asExpression ||
36✔
857
                tableColumn.generatedType !== columnMetadata.generatedType ||
36✔
858
                tableColumn.isUnique !==
36✔
859
                    this.normalizeIsUnique(columnMetadata) ||
36✔
860
                (tableColumn.enum &&
36!
861
                    columnMetadata.enum &&
36!
862
                    !OrmUtils.isArraysEqual(
×
863
                        tableColumn.enum,
×
864
                        columnMetadata.enum.map((val) => val + ""),
×
865
                    ))
36✔
866

36✔
867
            // DEBUG SECTION
36✔
868
            // if (isColumnChanged) {
36✔
869
            //     console.log("table:", columnMetadata.entityMetadata.tableName)
36✔
870
            //     console.log(
36✔
871
            //         "name:",
36✔
872
            //         tableColumn.name,
36✔
873
            //         columnMetadata.databaseName,
36✔
874
            //     )
36✔
875
            //     console.log(
36✔
876
            //         "type:",
36✔
877
            //         tableColumn.type,
36✔
878
            //         this.normalizeType(columnMetadata),
36✔
879
            //         this.compareColumnType(tableColumn, columnMetadata),
36✔
880
            //     )
36✔
881
            //     console.log(
36✔
882
            //         "length:",
36✔
883
            //         tableColumn.length,
36✔
884
            //         columnMetadata.length,
36✔
885
            //         this.compareColumnLength(tableColumn, columnMetadata),
36✔
886
            //     )
36✔
887
            //     console.log(
36✔
888
            //         "precision:",
36✔
889
            //         tableColumn.precision,
36✔
890
            //         columnMetadata.precision,
36✔
891
            //     )
36✔
892
            //     console.log("scale:", tableColumn.scale, columnMetadata.scale)
36✔
893
            //     console.log(
36✔
894
            //         "isGenerated:",
36✔
895
            //         tableColumn.isGenerated,
36✔
896
            //         columnMetadata.isGenerated,
36✔
897
            //     )
36✔
898
            //     console.log(
36✔
899
            //         "isGenerated 2:",
36✔
900
            //         !tableColumn.isGenerated &&
36✔
901
            //             this.lowerDefaultValueIfNecessary(
36✔
902
            //                 this.normalizeDefault(columnMetadata),
36✔
903
            //             ) !==
36✔
904
            //                 this.lowerDefaultValueIfNecessary(
36✔
905
            //                     tableColumn.default,
36✔
906
            //                 ),
36✔
907
            //     )
36✔
908
            //     console.log(
36✔
909
            //         "isPrimary:",
36✔
910
            //         tableColumn.isPrimary,
36✔
911
            //         columnMetadata.isPrimary,
36✔
912
            //     )
36✔
913
            //     console.log(
36✔
914
            //         "isNullable:",
36✔
915
            //         tableColumn.isNullable,
36✔
916
            //         columnMetadata.isNullable,
36✔
917
            //     )
36✔
918
            //     console.log(
36✔
919
            //         "asExpression:",
36✔
920
            //         tableColumn.asExpression,
36✔
921
            //         columnMetadata.asExpression,
36✔
922
            //     )
36✔
923
            //     console.log(
36✔
924
            //         "generatedType:",
36✔
925
            //         tableColumn.generatedType,
36✔
926
            //         columnMetadata.generatedType,
36✔
927
            //     )
36✔
928
            //     console.log(
36✔
929
            //         "isUnique:",
36✔
930
            //         tableColumn.isUnique,
36✔
931
            //         this.normalizeIsUnique(columnMetadata),
36✔
932
            //     )
36✔
933
            //     console.log("==========================================")
36✔
934
            // }
36✔
935

36✔
936
            return isColumnChanged
36✔
937
        })
12✔
938
    }
12✔
939

28✔
940
    /**
28✔
941
     * Returns true if driver supports RETURNING / OUTPUT statement.
28✔
942
     * @param returningType
28✔
943
     */
28✔
944
    isReturningSqlSupported(returningType: ReturningType): boolean {
28✔
945
        if (
92✔
946
            this.options.options &&
92!
947
            this.options.options.disableOutputReturning
×
948
        ) {
92!
949
            return false
×
950
        }
×
951
        return true
92✔
952
    }
92✔
953

28✔
954
    /**
28✔
955
     * Returns true if driver supports uuid values generation on its own.
28✔
956
     */
28✔
957
    isUUIDGenerationSupported(): boolean {
28✔
958
        return true
×
959
    }
×
960

28✔
961
    /**
28✔
962
     * Returns true if driver supports fulltext indices.
28✔
963
     */
28✔
964
    isFullTextColumnTypeSupported(): boolean {
28✔
965
        return false
×
966
    }
×
967

28✔
968
    /**
28✔
969
     * Creates an escaped parameter.
28✔
970
     * @param parameterName
28✔
971
     * @param index
28✔
972
     */
28✔
973
    createParameter(parameterName: string, index: number): string {
28✔
974
        return this.parametersPrefix + index
104✔
975
    }
104✔
976

28✔
977
    // -------------------------------------------------------------------------
28✔
978
    // Public Methods
28✔
979
    // -------------------------------------------------------------------------
28✔
980

28✔
981
    /**
28✔
982
     * Sql server's parameters needs to be wrapped into special object with type information about this value.
28✔
983
     * This method wraps given value into MssqlParameter based on its column definition.
28✔
984
     * @param column
28✔
985
     * @param value
28✔
986
     */
28✔
987
    parametrizeValue(column: ColumnMetadata, value: any) {
28✔
988
        // if its already MssqlParameter then simply return it
46✔
989
        if (InstanceChecker.isMssqlParameter(value)) return value
46!
990

46✔
991
        const normalizedType = this.normalizeType({ type: column.type })
46✔
992
        if (column.length) {
46!
993
            return new MssqlParameter(
×
994
                value,
×
995
                normalizedType as any,
×
996
                column.length as any,
×
997
            )
×
998
        } else if (
46✔
999
            column.precision !== null &&
46✔
1000
            column.precision !== undefined &&
46!
1001
            column.scale !== null &&
46!
1002
            column.scale !== undefined
×
1003
        ) {
46!
1004
            return new MssqlParameter(
×
1005
                value,
×
1006
                normalizedType as any,
×
1007
                column.precision,
×
1008
                column.scale,
×
1009
            )
×
1010
        } else if (
46✔
1011
            column.precision !== null &&
46✔
1012
            column.precision !== undefined
46✔
1013
        ) {
46!
1014
            return new MssqlParameter(
×
1015
                value,
×
1016
                normalizedType as any,
×
1017
                column.precision,
×
1018
            )
×
1019
        } else if (column.scale !== null && column.scale !== undefined) {
46!
1020
            return new MssqlParameter(
×
1021
                value,
×
1022
                normalizedType as any,
×
1023
                column.scale,
×
1024
            )
×
1025
        }
×
1026

46✔
1027
        return new MssqlParameter(value, normalizedType as any)
46✔
1028
    }
46✔
1029

28✔
1030
    /**
28✔
1031
     * Recursively wraps values (including those inside FindOperators) into MssqlParameter instances,
28✔
1032
     * ensuring correct type metadata is passed to the SQL Server driver.
28✔
1033
     *
28✔
1034
     * - If the value is a FindOperator containing an array, all elements are individually parametrized.
28✔
1035
     * - If the value is a non-raw FindOperator, a transformation is applied to its internal value.
28✔
1036
     * - Otherwise, the value is passed directly to parametrizeValue for wrapping.
28✔
1037
     *
28✔
1038
     * This ensures SQL Server receives properly typed parameters for queries involving operators like
28✔
1039
     * In, MoreThan, Between, etc.
28✔
1040
     * @param column
28✔
1041
     * @param value
28✔
1042
     */
28✔
1043
    parametrizeValues(column: ColumnMetadata, value: any) {
28✔
1044
        if (value instanceof FindOperator) {
4✔
1045
            if (value.type !== "raw") {
2✔
1046
                value.transformValue({
2✔
1047
                    to: (v) => this.parametrizeValues(column, v),
2✔
1048
                    from: (v) => v,
2✔
1049
                })
2✔
1050
            }
2✔
1051

2✔
1052
            return value
2✔
1053
        }
2✔
1054

2✔
1055
        return this.parametrizeValue(column, value)
2✔
1056
    }
2✔
1057

28✔
1058
    /**
28✔
1059
     * Sql server's parameters needs to be wrapped into special object with type information about this value.
28✔
1060
     * This method wraps all values of the given object into MssqlParameter based on their column definitions in the given table.
28✔
1061
     * @param tablePath
28✔
1062
     * @param map
28✔
1063
     */
28✔
1064
    parametrizeMap(tablePath: string, map: ObjectLiteral): ObjectLiteral {
28✔
1065
        // find metadata for the given table
×
1066
        if (!this.connection.hasMetadata(tablePath))
×
1067
            // if no metadata found then we can't proceed because we don't have columns and their types
×
1068
            return map
×
1069
        const metadata = this.connection.getMetadata(tablePath)
×
1070

×
1071
        return Object.keys(map).reduce((newMap, key) => {
×
1072
            const value = map[key]
×
1073

×
1074
            // find column metadata
×
1075
            const column = metadata.findColumnWithDatabaseName(key)
×
1076
            if (!column)
×
1077
                // if we didn't find a column then we can't proceed because we don't have a column type
×
1078
                return value
×
1079

×
1080
            newMap[key] = this.parametrizeValue(column, value)
×
1081
            return newMap
×
1082
        }, {} as ObjectLiteral)
×
1083
    }
×
1084

28✔
1085
    buildTableVariableDeclaration(
28✔
1086
        identifier: string,
10✔
1087
        columns: ColumnMetadata[],
10✔
1088
    ): string {
10✔
1089
        const outputColumns = columns.map((column) => {
10✔
1090
            return `${this.escape(column.databaseName)} ${this.createFullType(
16✔
1091
                new TableColumn({
16✔
1092
                    name: column.databaseName,
16✔
1093
                    type: this.normalizeType(column),
16✔
1094
                    length: column.length,
16✔
1095
                    isNullable: column.isNullable,
16✔
1096
                    isArray: column.isArray,
16✔
1097
                }),
16✔
1098
            )}`
16✔
1099
        })
10✔
1100

10✔
1101
        return `DECLARE ${identifier} TABLE (${outputColumns.join(", ")})`
10✔
1102
    }
10✔
1103

28✔
1104
    // -------------------------------------------------------------------------
28✔
1105
    // Protected Methods
28✔
1106
    // -------------------------------------------------------------------------
28✔
1107

28✔
1108
    /**
28✔
1109
     * If driver dependency is not given explicitly, then try to load it via "require".
28✔
1110
     */
28✔
1111
    protected loadDependencies(): void {
28✔
1112
        try {
2✔
1113
            const mssql = this.options.driver || PlatformTools.load("mssql")
2✔
1114
            this.mssql = mssql
2✔
1115
        } catch (e) {
2!
1116
            // todo: better error for browser env
×
1117
            throw new DriverPackageNotInstalledError("SQL Server", "mssql")
×
1118
        }
×
1119
    }
2✔
1120

28✔
1121
    protected compareColumnType(
28✔
1122
        tableColumn: TableColumn,
36✔
1123
        columnMetadata: ColumnMetadata,
36✔
1124
    ): boolean {
36✔
1125
        // The Database Engine determines the data type of the computed column by applying the rules
36✔
1126
        // of data type precedence to the expressions specified in the formula.
36✔
1127
        if (columnMetadata.asExpression) return false
36!
1128

36✔
1129
        return tableColumn.type !== this.normalizeType(columnMetadata)
36✔
1130
    }
36✔
1131

28✔
1132
    protected compareColumnLength(
28✔
1133
        tableColumn: TableColumn,
36✔
1134
        columnMetadata: ColumnMetadata,
36✔
1135
    ): boolean {
36✔
1136
        // The Database Engine determines the data type of the computed column by applying the rules
36✔
1137
        // of data type precedence to the expressions specified in the formula.
36✔
1138
        if (columnMetadata.asExpression) return false
36!
1139

36✔
1140
        return (
36✔
1141
            tableColumn.length.toUpperCase() !==
36✔
1142
            this.getColumnLength(columnMetadata).toUpperCase()
36✔
1143
        )
36✔
1144
    }
36✔
1145

28✔
1146
    protected lowerDefaultValueIfNecessary(value: string | undefined) {
28✔
1147
        // SqlServer saves function calls in default value as lowercase https://github.com/typeorm/typeorm/issues/2733
64✔
1148
        if (!value) {
64✔
1149
            return value
64✔
1150
        }
64✔
1151
        return value
×
1152
            .split(`'`)
×
1153
            .map((v, i) => {
×
1154
                return i % 2 === 1 ? v : v.toLowerCase()
×
1155
            })
×
1156
            .join(`'`)
×
1157
    }
×
1158

28✔
1159
    /**
28✔
1160
     * Creates a new connection pool for a given database credentials.
28✔
1161
     * @param options
28✔
1162
     * @param credentials
28✔
1163
     */
28✔
1164
    protected createPool(
28✔
1165
        options: SqlServerDataSourceOptions,
2✔
1166
        credentials: SqlServerConnectionCredentialsOptions,
2✔
1167
    ): Promise<any> {
2✔
1168
        credentials = Object.assign(
2✔
1169
            {},
2✔
1170
            credentials,
2✔
1171
            DriverUtils.buildDriverOptions(credentials),
2✔
1172
        ) // todo: do it better way
2✔
1173

2✔
1174
        // build connection options for the driver
2✔
1175
        const connectionOptions = Object.assign(
2✔
1176
            {},
2✔
1177
            {
2✔
1178
                connectionTimeout: this.options.connectionTimeout,
2✔
1179
                requestTimeout: this.options.requestTimeout,
2✔
1180
                stream: this.options.stream,
2✔
1181
                pool: this.options.pool,
2✔
1182
                options: this.options.options,
2✔
1183
            },
2✔
1184
            {
2✔
1185
                server: credentials.host,
2✔
1186
                database: credentials.database,
2✔
1187
                port: credentials.port,
2✔
1188
                user: credentials.username,
2✔
1189
                password: credentials.password,
2✔
1190
                authentication: credentials.authentication,
2✔
1191
            },
2✔
1192
            options.extra || {},
2!
1193
        )
2✔
1194

2✔
1195
        // set default useUTC option if it hasn't been set
2✔
1196
        if (!connectionOptions.options) {
2✔
1197
            connectionOptions.options = { useUTC: false }
2✔
1198
        } else if (!connectionOptions.options.useUTC) {
2!
1199
            Object.assign(connectionOptions.options, { useUTC: false })
×
1200
        }
×
1201

2✔
1202
        // Match the next release of tedious for configuration options
2✔
1203
        // Also prevents warning messages.
2✔
1204
        Object.assign(connectionOptions.options, { enableArithAbort: true })
2✔
1205

2✔
1206
        // pooling is enabled either when its set explicitly to true,
2✔
1207
        // either when its not defined at all (e.g. enabled by default)
2✔
1208
        return new Promise<void>((ok, fail) => {
2✔
1209
            const pool = new this.mssql.ConnectionPool(connectionOptions)
2✔
1210

2✔
1211
            const { logger } = this.connection
2✔
1212

2✔
1213
            const poolErrorHandler =
2✔
1214
                (options.pool && options.pool.errorHandler) ||
2!
1215
                ((error: any) =>
2!
1216
                    logger.log("warn", `MSSQL pool raised an error. ${error}`))
2✔
1217
            /**
2✔
1218
             * Attaching an error handler to pool errors is essential, as, otherwise, errors raised will go unhandled and
2✔
1219
             * cause the hosting app to crash.
2✔
1220
             */
2✔
1221
            pool.on("error", poolErrorHandler)
2✔
1222

2✔
1223
            const connection = pool.connect((err: any) => {
2✔
1224
                if (err) return fail(err)
2!
1225
                ok(connection)
2✔
1226
            })
2✔
1227
        })
2✔
1228
    }
2✔
1229
}
28✔
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