• 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

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

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

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

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

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

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

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

26✔
65
    /**
26✔
66
     * Connection options.
26✔
67
     */
26✔
68
    options: SqlServerConnectionOptions
26✔
69

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

26✔
253
    // -------------------------------------------------------------------------
26✔
254
    // Constructor
26✔
255
    // -------------------------------------------------------------------------
26✔
256

26✔
257
    constructor(connection: DataSource) {
26✔
258
        this.connection = connection
978✔
259
        this.options = connection.options as SqlServerConnectionOptions
978✔
260
        this.isReplicated = this.options.replication ? true : false
978!
261

978✔
262
        // load mssql package
978✔
263
        this.loadDependencies()
978✔
264

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

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

26✔
282
    // -------------------------------------------------------------------------
26✔
283
    // Public Implemented Methods
26✔
284
    // -------------------------------------------------------------------------
26✔
285

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

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

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

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

974✔
317
            await queryRunner.release()
974✔
318
        }
974✔
319

974✔
320
        if (!this.schema) {
974✔
321
            this.schema = this.searchSchema
970✔
322
        }
970✔
323
    }
974✔
324

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

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

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

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

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

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

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

70,234✔
391
                if (parameterIndexMap.has(key)) {
70,252✔
392
                    return this.parametersPrefix + parameterIndexMap.get(key)
28✔
393
                }
28✔
394

70,206✔
395
                const value: any = parameters[key]
70,206✔
396

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

69,990✔
409
                if (typeof value === "function") {
70,252!
410
                    return value()
×
411
                }
×
412

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

26✔
421
    /**
26✔
422
     * Escapes a column name.
26✔
423
     */
26✔
424
    escape(columnName: string): string {
26✔
425
        return `"${columnName}"`
2,866,008✔
426
    }
2,866,008✔
427

26✔
428
    /**
26✔
429
     * Build full table name with database name, schema name and table name.
26✔
430
     * E.g. myDB.mySchema.myTable
26✔
431
     */
26✔
432
    buildTableName(
26✔
433
        tableName: string,
659,868✔
434
        schema?: string,
659,868✔
435
        database?: string,
659,868✔
436
    ): string {
659,868✔
437
        const tablePath = [tableName]
659,868✔
438

659,868✔
439
        if (schema) {
659,868✔
440
            tablePath.unshift(schema)
643,150✔
441
        }
643,150✔
442

659,868✔
443
        if (database) {
659,868✔
444
            if (!schema) {
646,392✔
445
                tablePath.unshift("")
3,314✔
446
            }
3,314✔
447

646,392✔
448
            tablePath.unshift(database)
646,392✔
449
        }
646,392✔
450

659,868✔
451
        return tablePath.join(".")
659,868✔
452
    }
659,868✔
453

26✔
454
    /**
26✔
455
     * Parse a target table name or other types and return a normalized table definition.
26✔
456
     */
26✔
457
    parseTableName(
26✔
458
        target: EntityMetadata | Table | View | TableForeignKey | string,
1,063,192✔
459
    ): { database?: string; schema?: string; tableName: string } {
1,063,192✔
460
        const driverDatabase = this.database
1,063,192✔
461
        const driverSchema = this.schema
1,063,192✔
462

1,063,192✔
463
        if (InstanceChecker.isTable(target) || InstanceChecker.isView(target)) {
1,063,192✔
464
            const parsed = this.parseTableName(target.name)
350,702✔
465

350,702✔
466
            return {
350,702✔
467
                database: target.database || parsed.database || driverDatabase,
350,702!
468
                schema: target.schema || parsed.schema || driverSchema,
350,702!
469
                tableName: parsed.tableName,
350,702✔
470
            }
350,702✔
471
        }
350,702✔
472

712,490✔
473
        if (InstanceChecker.isTableForeignKey(target)) {
1,063,192✔
474
            const parsed = this.parseTableName(target.referencedTableName)
6,974✔
475

6,974✔
476
            return {
6,974✔
477
                database:
6,974✔
478
                    target.referencedDatabase ||
6,974✔
479
                    parsed.database ||
6,974!
480
                    driverDatabase,
6,974✔
481
                schema:
6,974✔
482
                    target.referencedSchema || parsed.schema || driverSchema,
6,974!
483
                tableName: parsed.tableName,
6,974✔
484
            }
6,974✔
485
        }
6,974✔
486

705,516✔
487
        if (InstanceChecker.isEntityMetadata(target)) {
1,063,192✔
488
            // EntityMetadata tableName is never a path
327,484✔
489

327,484✔
490
            return {
327,484✔
491
                database: target.database || driverDatabase,
327,484✔
492
                schema: target.schema || driverSchema,
327,484✔
493
                tableName: target.tableName,
327,484✔
494
            }
327,484✔
495
        }
327,484✔
496

378,032✔
497
        const parts = target.split(".")
378,032✔
498

378,032✔
499
        if (parts.length === 3) {
1,063,192✔
500
            return {
20,616✔
501
                database: parts[0] || driverDatabase,
20,616!
502
                schema: parts[1] || driverSchema,
20,616✔
503
                tableName: parts[2],
20,616✔
504
            }
20,616✔
505
        } else if (parts.length === 2) {
1,063,192✔
506
            return {
584✔
507
                database: driverDatabase,
584✔
508
                schema: parts[0],
584✔
509
                tableName: parts[1],
584✔
510
            }
584✔
511
        } else {
357,416✔
512
            return {
356,832✔
513
                database: driverDatabase,
356,832✔
514
                schema: driverSchema,
356,832✔
515
                tableName: target,
356,832✔
516
            }
356,832✔
517
        }
356,832✔
518
    }
1,063,192✔
519

26✔
520
    /**
26✔
521
     * Prepares given value to a value to be persisted, based on its column type and metadata.
26✔
522
     */
26✔
523
    preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
524
        if (columnMetadata.transformer)
55,098✔
525
            value = ApplyValueTransformers.transformTo(
55,098✔
526
                columnMetadata.transformer,
62✔
527
                value,
62✔
528
            )
62✔
529

55,098✔
530
        if (value === null || value === undefined) return value
55,098✔
531

50,238✔
532
        if (columnMetadata.type === Boolean) {
55,098✔
533
            return value === true ? 1 : 0
4,018✔
534
        } else if (columnMetadata.type === "date") {
55,098✔
535
            return DateUtils.mixedDateToDate(value)
6✔
536
        } else if (columnMetadata.type === "time") {
46,220✔
537
            return DateUtils.mixedTimeToDate(value)
12✔
538
        } else if (
46,214✔
539
            columnMetadata.type === "datetime" ||
46,202✔
540
            columnMetadata.type === "smalldatetime" ||
46,202✔
541
            columnMetadata.type === Date
46,194✔
542
        ) {
46,202✔
543
            return DateUtils.mixedDateToDate(value, false, false)
52✔
544
        } else if (
46,202✔
545
            columnMetadata.type === "datetime2" ||
46,150✔
546
            columnMetadata.type === "datetimeoffset"
46,126✔
547
        ) {
46,150✔
548
            return DateUtils.mixedDateToDate(value, false, true)
32✔
549
        } else if (columnMetadata.type === "simple-array") {
46,150✔
550
            return DateUtils.simpleArrayToString(value)
2✔
551
        } else if (columnMetadata.type === "simple-json") {
46,118✔
552
            return DateUtils.simpleJsonToString(value)
10✔
553
        } else if (columnMetadata.type === "simple-enum") {
46,116✔
554
            return DateUtils.simpleEnumToString(value)
20✔
555
        } else if (columnMetadata.type === "vector") {
46,106✔
556
            if (Array.isArray(value)) {
18✔
557
                return JSON.stringify(value)
18✔
558
            } else {
18!
559
                return value
×
560
            }
×
561
        }
18✔
562

46,068✔
563
        return value
46,068✔
564
    }
46,068✔
565

26✔
566
    /**
26✔
567
     * Prepares given value to a value to be persisted, based on its column type or metadata.
26✔
568
     */
26✔
569
    prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
26✔
570
        if (value === null || value === undefined)
136,074✔
571
            return columnMetadata.transformer
136,074✔
572
                ? ApplyValueTransformers.transformFrom(
7,920✔
573
                      columnMetadata.transformer,
24✔
574
                      value,
24✔
575
                  )
7,920✔
576
                : value
7,920✔
577

128,154✔
578
        if (columnMetadata.type === Boolean) {
136,074✔
579
            value = value ? true : false
1,424✔
580
        } else if (
136,074✔
581
            columnMetadata.type === "datetime" ||
126,730✔
582
            columnMetadata.type === Date ||
126,730✔
583
            columnMetadata.type === "datetime2" ||
126,730✔
584
            columnMetadata.type === "smalldatetime" ||
126,730✔
585
            columnMetadata.type === "datetimeoffset"
125,726✔
586
        ) {
126,730✔
587
            value = DateUtils.normalizeHydratedDate(value)
1,012✔
588
        } else if (columnMetadata.type === "date") {
126,730✔
589
            value = DateUtils.mixedDateToDateString(value)
6✔
590
        } else if (columnMetadata.type === "time") {
125,718✔
591
            value = DateUtils.mixedTimeToString(value)
12✔
592
        } else if (columnMetadata.type === "simple-array") {
125,712✔
593
            value = DateUtils.stringToSimpleArray(value)
2✔
594
        } else if (columnMetadata.type === "simple-json") {
125,700✔
595
            value = DateUtils.stringToSimpleJson(value)
10✔
596
        } else if (columnMetadata.type === "simple-enum") {
125,698✔
597
            value = DateUtils.stringToSimpleEnum(value, columnMetadata)
80✔
598
        } else if (columnMetadata.type === "vector") {
125,688✔
599
            if (typeof value === "string") {
6✔
600
                try {
6✔
601
                    value = JSON.parse(value)
6✔
602
                } catch (e) {
6!
603
                    // If parsing fails, return the value as-is
×
604
                }
×
605
            }
6✔
606
        } else if (columnMetadata.type === Number) {
125,608✔
607
            // convert to number if number
34,052✔
608
            value = !isNaN(+value) ? parseInt(value) : value
34,052!
609
        }
34,052✔
610

128,154✔
611
        if (columnMetadata.transformer)
128,154✔
612
            value = ApplyValueTransformers.transformFrom(
136,074✔
613
                columnMetadata.transformer,
134✔
614
                value,
134✔
615
            )
134✔
616

128,154✔
617
        return value
128,154✔
618
    }
128,154✔
619

26✔
620
    /**
26✔
621
     * Creates a database type from a given column metadata.
26✔
622
     */
26✔
623
    normalizeType(column: {
26✔
624
        type?: ColumnType
185,226✔
625
        length?: number | string
185,226✔
626
        precision?: number | null
185,226✔
627
        scale?: number
185,226✔
628
    }): string {
185,226✔
629
        if (column.type === Number || column.type === "integer") {
185,226✔
630
            return "int"
66,332✔
631
        } else if (column.type === String) {
185,226✔
632
            return "nvarchar"
51,980✔
633
        } else if (column.type === Date) {
118,894✔
634
            return "datetime"
576✔
635
        } else if (column.type === Boolean) {
66,914✔
636
            return "bit"
5,856✔
637
        } else if ((column.type as any) === Buffer) {
66,338✔
638
            return "binary"
16✔
639
        } else if (column.type === "uuid") {
60,482✔
640
            return "uniqueidentifier"
1,784✔
641
        } else if (
60,466✔
642
            column.type === "simple-array" ||
58,682✔
643
            column.type === "simple-json"
58,666✔
644
        ) {
58,682✔
645
            return "ntext"
76✔
646
        } else if (column.type === "simple-enum") {
58,682✔
647
            return "nvarchar"
178✔
648
        } else if (column.type === "dec") {
58,606✔
649
            return "decimal"
32✔
650
        } else if (column.type === "double precision") {
58,428✔
651
            return "float"
56✔
652
        } else if (column.type === "rowversion") {
58,396✔
653
            return "timestamp" // the rowversion type's name in SQL server metadata is timestamp
14✔
654
        } else {
58,340✔
655
            return (column.type as string) || ""
58,326✔
656
        }
58,326✔
657
    }
185,226✔
658

26✔
659
    /**
26✔
660
     * Normalizes "default" value of the column.
26✔
661
     */
26✔
662
    normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
26✔
663
        const defaultValue = columnMetadata.default
57,670✔
664

57,670✔
665
        if (typeof defaultValue === "number") {
57,670✔
666
            return `${defaultValue}`
1,404✔
667
        }
1,404✔
668

56,266✔
669
        if (typeof defaultValue === "boolean") {
57,670✔
670
            return defaultValue ? "1" : "0"
36✔
671
        }
36✔
672

56,230✔
673
        if (typeof defaultValue === "function") {
57,670✔
674
            const value = defaultValue()
1,332✔
675
            if (value.toUpperCase() === "CURRENT_TIMESTAMP") {
1,332✔
676
                return "getdate()"
84✔
677
            }
84✔
678
            return value
1,248✔
679
        }
1,248✔
680

54,898✔
681
        if (typeof defaultValue === "string") {
57,670✔
682
            return `'${defaultValue}'`
1,216✔
683
        }
1,216✔
684

53,682✔
685
        if (defaultValue === undefined || defaultValue === null) {
57,670✔
686
            return undefined
53,682✔
687
        }
53,682✔
688

×
689
        return `${defaultValue}`
×
690
    }
×
691

26✔
692
    /**
26✔
693
     * Normalizes "isUnique" value of the column.
26✔
694
     */
26✔
695
    normalizeIsUnique(column: ColumnMetadata): boolean {
26✔
696
        return column.entityMetadata.uniques.some(
65,254✔
697
            (uq) => uq.columns.length === 1 && uq.columns[0] === column,
65,254✔
698
        )
65,254✔
699
    }
65,254✔
700

26✔
701
    /**
26✔
702
     * Returns default column lengths, which is required on column creation.
26✔
703
     */
26✔
704
    getColumnLength(column: ColumnMetadata | TableColumn): string {
26✔
705
        if (column.length) return column.length.toString()
117,458✔
706

91,074✔
707
        if (
91,074✔
708
            column.type === "varchar" ||
91,074✔
709
            column.type === "nvarchar" ||
117,458✔
710
            column.type === String
88,794✔
711
        )
117,458✔
712
            return "255"
117,458✔
713

64,752✔
714
        return ""
64,752✔
715
    }
64,752✔
716

26✔
717
    /**
26✔
718
     * Creates column type definition including length, precision and scale
26✔
719
     */
26✔
720
    createFullType(column: TableColumn): string {
26✔
721
        // The Database Engine determines the data type of the computed column by applying the rules
39,108✔
722
        // of data type precedence to the expressions specified in the formula.
39,108✔
723
        if (column.asExpression) return ""
39,108✔
724

39,030✔
725
        let type = column.type
39,030✔
726

39,030✔
727
        // Handle vector type with length (dimensions)
39,030✔
728
        if (column.type === "vector") {
39,108✔
729
            type = `vector(${column.length})`
28✔
730
        }
28✔
731
        // used 'getColumnLength()' method, because SqlServer sets `varchar` and `nvarchar` length to 1 by default.
39,002✔
732
        else if (this.getColumnLength(column)) {
39,002✔
733
            type += `(${this.getColumnLength(column)})`
13,020✔
734
        } else if (
39,002✔
735
            column.precision !== null &&
25,982✔
736
            column.precision !== undefined &&
25,982✔
737
            column.scale !== null &&
25,982✔
738
            column.scale !== undefined
42✔
739
        ) {
25,982✔
740
            type += `(${column.precision},${column.scale})`
22✔
741
        } else if (
25,982✔
742
            column.precision !== null &&
25,960✔
743
            column.precision !== undefined
25,960✔
744
        ) {
25,960✔
745
            type += `(${column.precision})`
20✔
746
        }
20✔
747

39,030✔
748
        if (column.isArray) type += " array"
39,108!
749

39,030✔
750
        return type
39,030✔
751
    }
39,030✔
752

26✔
753
    /**
26✔
754
     * Obtains a new database connection to a master server.
26✔
755
     * Used for replication.
26✔
756
     * If replication is not setup then returns default connection's database connection.
26✔
757
     */
26✔
758
    obtainMasterConnection(): Promise<any> {
26✔
759
        if (!this.master) {
124,266!
760
            return Promise.reject(new TypeORMError("Driver not Connected"))
×
761
        }
×
762

124,266✔
763
        return Promise.resolve(this.master)
124,266✔
764
    }
124,266✔
765

26✔
766
    /**
26✔
767
     * Obtains a new database connection to a slave server.
26✔
768
     * Used for replication.
26✔
769
     * If replication is not setup then returns master (default) connection's database connection.
26✔
770
     */
26✔
771
    obtainSlaveConnection(): Promise<any> {
26✔
772
        if (!this.slaves.length) return this.obtainMasterConnection()
4,588✔
773

×
774
        const random = Math.floor(Math.random() * this.slaves.length)
×
775
        return Promise.resolve(this.slaves[random])
×
776
    }
×
777

26✔
778
    /**
26✔
779
     * Creates generated map of values generated or returned by database after INSERT query.
26✔
780
     */
26✔
781
    createGeneratedMap(metadata: EntityMetadata, insertResult: ObjectLiteral) {
26✔
782
        if (!insertResult) return undefined
19,550✔
783

7,712✔
784
        return Object.keys(insertResult).reduce((map, key) => {
7,712✔
785
            const column = metadata.findColumnWithDatabaseName(key)
9,236✔
786
            if (column) {
9,236✔
787
                OrmUtils.mergeDeep(
9,236✔
788
                    map,
9,236✔
789
                    column.createValueMap(
9,236✔
790
                        this.prepareHydratedValue(insertResult[key], column),
9,236✔
791
                    ),
9,236✔
792
                )
9,236✔
793
            }
9,236✔
794
            return map
9,236✔
795
        }, {} as ObjectLiteral)
7,712✔
796
    }
7,712✔
797

26✔
798
    /**
26✔
799
     * Differentiate columns of this table and columns from the given column metadatas columns
26✔
800
     * and returns only changed.
26✔
801
     */
26✔
802
    findChangedColumns(
26✔
803
        tableColumns: TableColumn[],
10,134✔
804
        columnMetadatas: ColumnMetadata[],
10,134✔
805
    ): ColumnMetadata[] {
10,134✔
806
        return columnMetadatas.filter((columnMetadata) => {
10,134✔
807
            const tableColumn = tableColumns.find(
32,628✔
808
                (c) => c.name === columnMetadata.databaseName,
32,628✔
809
            )
32,628✔
810
            if (!tableColumn) return false // we don't need new columns, we only need exist and changed
32,628✔
811

32,620✔
812
            const isColumnChanged =
32,620✔
813
                tableColumn.name !== columnMetadata.databaseName ||
32,620✔
814
                this.compareColumnType(tableColumn, columnMetadata) ||
32,628✔
815
                this.compareColumnLength(tableColumn, columnMetadata) ||
32,628✔
816
                tableColumn.precision !== columnMetadata.precision ||
32,628✔
817
                tableColumn.scale !== columnMetadata.scale ||
32,628✔
818
                // || tableColumn.comment !== columnMetadata.comment || // todo
32,584✔
819
                tableColumn.isGenerated !== columnMetadata.isGenerated ||
32,628✔
820
                (!tableColumn.isGenerated &&
32,580✔
821
                    this.lowerDefaultValueIfNecessary(
26,412✔
822
                        this.normalizeDefault(columnMetadata),
26,412✔
823
                    ) !==
26,412✔
824
                        this.lowerDefaultValueIfNecessary(
26,412✔
825
                            tableColumn.default,
26,412✔
826
                        )) || // we included check for generated here, because generated columns already can have default values
32,628✔
827
                tableColumn.isPrimary !== columnMetadata.isPrimary ||
32,628✔
828
                tableColumn.isNullable !== columnMetadata.isNullable ||
32,628✔
829
                tableColumn.asExpression !== columnMetadata.asExpression ||
32,628✔
830
                tableColumn.generatedType !== columnMetadata.generatedType ||
32,628✔
831
                tableColumn.isUnique !==
32,578✔
832
                    this.normalizeIsUnique(columnMetadata) ||
32,628✔
833
                (tableColumn.enum &&
32,576✔
834
                    columnMetadata.enum &&
32,576✔
835
                    !OrmUtils.isArraysEqual(
54✔
836
                        tableColumn.enum,
54✔
837
                        columnMetadata.enum.map((val) => val + ""),
54✔
838
                    ))
32,576✔
839

32,628✔
840
            // DEBUG SECTION
32,628✔
841
            // if (isColumnChanged) {
32,628✔
842
            //     console.log("table:", columnMetadata.entityMetadata.tableName)
32,628✔
843
            //     console.log(
32,628✔
844
            //         "name:",
32,628✔
845
            //         tableColumn.name,
32,628✔
846
            //         columnMetadata.databaseName,
32,628✔
847
            //     )
32,628✔
848
            //     console.log(
32,628✔
849
            //         "type:",
32,628✔
850
            //         tableColumn.type,
32,628✔
851
            //         this.normalizeType(columnMetadata),
32,628✔
852
            //         this.compareColumnType(tableColumn, columnMetadata),
32,628✔
853
            //     )
32,628✔
854
            //     console.log(
32,628✔
855
            //         "length:",
32,628✔
856
            //         tableColumn.length,
32,628✔
857
            //         columnMetadata.length,
32,628✔
858
            //         this.compareColumnLength(tableColumn, columnMetadata),
32,628✔
859
            //     )
32,628✔
860
            //     console.log(
32,628✔
861
            //         "precision:",
32,628✔
862
            //         tableColumn.precision,
32,628✔
863
            //         columnMetadata.precision,
32,628✔
864
            //     )
32,628✔
865
            //     console.log("scale:", tableColumn.scale, columnMetadata.scale)
32,628✔
866
            //     console.log(
32,628✔
867
            //         "isGenerated:",
32,628✔
868
            //         tableColumn.isGenerated,
32,628✔
869
            //         columnMetadata.isGenerated,
32,628✔
870
            //     )
32,628✔
871
            //     console.log(
32,628✔
872
            //         "isGenerated 2:",
32,628✔
873
            //         !tableColumn.isGenerated &&
32,628✔
874
            //             this.lowerDefaultValueIfNecessary(
32,628✔
875
            //                 this.normalizeDefault(columnMetadata),
32,628✔
876
            //             ) !==
32,628✔
877
            //                 this.lowerDefaultValueIfNecessary(
32,628✔
878
            //                     tableColumn.default,
32,628✔
879
            //                 ),
32,628✔
880
            //     )
32,628✔
881
            //     console.log(
32,628✔
882
            //         "isPrimary:",
32,628✔
883
            //         tableColumn.isPrimary,
32,628✔
884
            //         columnMetadata.isPrimary,
32,628✔
885
            //     )
32,628✔
886
            //     console.log(
32,628✔
887
            //         "isNullable:",
32,628✔
888
            //         tableColumn.isNullable,
32,628✔
889
            //         columnMetadata.isNullable,
32,628✔
890
            //     )
32,628✔
891
            //     console.log(
32,628✔
892
            //         "asExpression:",
32,628✔
893
            //         tableColumn.asExpression,
32,628✔
894
            //         columnMetadata.asExpression,
32,628✔
895
            //     )
32,628✔
896
            //     console.log(
32,628✔
897
            //         "generatedType:",
32,628✔
898
            //         tableColumn.generatedType,
32,628✔
899
            //         columnMetadata.generatedType,
32,628✔
900
            //     )
32,628✔
901
            //     console.log(
32,628✔
902
            //         "isUnique:",
32,628✔
903
            //         tableColumn.isUnique,
32,628✔
904
            //         this.normalizeIsUnique(columnMetadata),
32,628✔
905
            //     )
32,628✔
906
            //     console.log("==========================================")
32,628✔
907
            // }
32,628✔
908

32,628✔
909
            return isColumnChanged
32,628✔
910
        })
10,134✔
911
    }
10,134✔
912

26✔
913
    /**
26✔
914
     * Returns true if driver supports RETURNING / OUTPUT statement.
26✔
915
     */
26✔
916
    isReturningSqlSupported(): boolean {
26✔
917
        if (
39,184✔
918
            this.options.options &&
39,184✔
919
            this.options.options.disableOutputReturning
16✔
920
        ) {
39,184!
921
            return false
×
922
        }
×
923
        return true
39,184✔
924
    }
39,184✔
925

26✔
926
    /**
26✔
927
     * Returns true if driver supports uuid values generation on its own.
26✔
928
     */
26✔
929
    isUUIDGenerationSupported(): boolean {
26✔
930
        return true
252✔
931
    }
252✔
932

26✔
933
    /**
26✔
934
     * Returns true if driver supports fulltext indices.
26✔
935
     */
26✔
936
    isFullTextColumnTypeSupported(): boolean {
26✔
937
        return false
90✔
938
    }
90✔
939

26✔
940
    /**
26✔
941
     * Creates an escaped parameter.
26✔
942
     */
26✔
943
    createParameter(parameterName: string, index: number): string {
26✔
944
        return this.parametersPrefix + index
70,740✔
945
    }
70,740✔
946

26✔
947
    // -------------------------------------------------------------------------
26✔
948
    // Public Methods
26✔
949
    // -------------------------------------------------------------------------
26✔
950

26✔
951
    /**
26✔
952
     * Sql server's parameters needs to be wrapped into special object with type information about this value.
26✔
953
     * This method wraps given value into MssqlParameter based on its column definition.
26✔
954
     */
26✔
955
    parametrizeValue(column: ColumnMetadata, value: any) {
26✔
956
        // if its already MssqlParameter then simply return it
51,848✔
957
        if (InstanceChecker.isMssqlParameter(value)) return value
51,848✔
958

51,844✔
959
        const normalizedType = this.normalizeType({ type: column.type })
51,844✔
960
        if (column.length) {
51,848✔
961
            return new MssqlParameter(
788✔
962
                value,
788✔
963
                normalizedType as any,
788✔
964
                column.length as any,
788✔
965
            )
788✔
966
        } else if (
51,848✔
967
            column.precision !== null &&
51,056✔
968
            column.precision !== undefined &&
51,056✔
969
            column.scale !== null &&
51,056✔
970
            column.scale !== undefined
12✔
971
        ) {
51,056✔
972
            return new MssqlParameter(
6✔
973
                value,
6✔
974
                normalizedType as any,
6✔
975
                column.precision,
6✔
976
                column.scale,
6✔
977
            )
6✔
978
        } else if (
51,056✔
979
            column.precision !== null &&
51,050✔
980
            column.precision !== undefined
51,050✔
981
        ) {
51,050✔
982
            return new MssqlParameter(
6✔
983
                value,
6✔
984
                normalizedType as any,
6✔
985
                column.precision,
6✔
986
            )
6✔
987
        } else if (column.scale !== null && column.scale !== undefined) {
51,050!
988
            return new MssqlParameter(
×
989
                value,
×
990
                normalizedType as any,
×
991
                column.scale,
×
992
            )
×
993
        }
×
994

51,044✔
995
        return new MssqlParameter(value, normalizedType as any)
51,044✔
996
    }
51,044✔
997

26✔
998
    /**
26✔
999
     * Recursively wraps values (including those inside FindOperators) into MssqlParameter instances,
26✔
1000
     * ensuring correct type metadata is passed to the SQL Server driver.
26✔
1001
     *
26✔
1002
     * - If the value is a FindOperator containing an array, all elements are individually parametrized.
26✔
1003
     * - If the value is a non-raw FindOperator, a transformation is applied to its internal value.
26✔
1004
     * - Otherwise, the value is passed directly to parametrizeValue for wrapping.
26✔
1005
     *
26✔
1006
     * This ensures SQL Server receives properly typed parameters for queries involving operators like
26✔
1007
     * In, MoreThan, Between, etc.
26✔
1008
     */
26✔
1009
    parametrizeValues(column: ColumnMetadata, value: any) {
26✔
1010
        if (value instanceof FindOperator) {
1,522✔
1011
            if (value.type !== "raw") {
132✔
1012
                value.transformValue({
112✔
1013
                    to: (v) => this.parametrizeValues(column, v),
112✔
1014
                    from: (v) => v,
112✔
1015
                })
112✔
1016
            }
112✔
1017

132✔
1018
            return value
132✔
1019
        }
132✔
1020

1,390✔
1021
        return this.parametrizeValue(column, value)
1,390✔
1022
    }
1,390✔
1023

26✔
1024
    /**
26✔
1025
     * Sql server's parameters needs to be wrapped into special object with type information about this value.
26✔
1026
     * This method wraps all values of the given object into MssqlParameter based on their column definitions in the given table.
26✔
1027
     */
26✔
1028
    parametrizeMap(tablePath: string, map: ObjectLiteral): ObjectLiteral {
26✔
1029
        // find metadata for the given table
×
1030
        if (!this.connection.hasMetadata(tablePath))
×
1031
            // if no metadata found then we can't proceed because we don't have columns and their types
×
1032
            return map
×
1033
        const metadata = this.connection.getMetadata(tablePath)
×
1034

×
1035
        return Object.keys(map).reduce((newMap, key) => {
×
1036
            const value = map[key]
×
1037

×
1038
            // find column metadata
×
1039
            const column = metadata.findColumnWithDatabaseName(key)
×
1040
            if (!column)
×
1041
                // if we didn't find a column then we can't proceed because we don't have a column type
×
1042
                return value
×
1043

×
1044
            newMap[key] = this.parametrizeValue(column, value)
×
1045
            return newMap
×
1046
        }, {} as ObjectLiteral)
×
1047
    }
×
1048

26✔
1049
    buildTableVariableDeclaration(
26✔
1050
        identifier: string,
5,714✔
1051
        columns: ColumnMetadata[],
5,714✔
1052
    ): string {
5,714✔
1053
        const outputColumns = columns.map((column) => {
5,714✔
1054
            return `${this.escape(column.databaseName)} ${this.createFullType(
7,210✔
1055
                new TableColumn({
7,210✔
1056
                    name: column.databaseName,
7,210✔
1057
                    type: this.normalizeType(column),
7,210✔
1058
                    length: column.length,
7,210✔
1059
                    isNullable: column.isNullable,
7,210✔
1060
                    isArray: column.isArray,
7,210✔
1061
                }),
7,210✔
1062
            )}`
7,210✔
1063
        })
5,714✔
1064

5,714✔
1065
        return `DECLARE ${identifier} TABLE (${outputColumns.join(", ")})`
5,714✔
1066
    }
5,714✔
1067

26✔
1068
    // -------------------------------------------------------------------------
26✔
1069
    // Protected Methods
26✔
1070
    // -------------------------------------------------------------------------
26✔
1071

26✔
1072
    /**
26✔
1073
     * If driver dependency is not given explicitly, then try to load it via "require".
26✔
1074
     */
26✔
1075
    protected loadDependencies(): void {
26✔
1076
        try {
978✔
1077
            const mssql = this.options.driver || PlatformTools.load("mssql")
978✔
1078
            this.mssql = mssql
978✔
1079
        } catch (e) {
978!
1080
            // todo: better error for browser env
×
1081
            throw new DriverPackageNotInstalledError("SQL Server", "mssql")
×
1082
        }
×
1083
    }
978✔
1084

26✔
1085
    protected compareColumnType(
26✔
1086
        tableColumn: TableColumn,
32,620✔
1087
        columnMetadata: ColumnMetadata,
32,620✔
1088
    ): boolean {
32,620✔
1089
        // The Database Engine determines the data type of the computed column by applying the rules
32,620✔
1090
        // of data type precedence to the expressions specified in the formula.
32,620✔
1091
        if (columnMetadata.asExpression) return false
32,620✔
1092

32,556✔
1093
        return tableColumn.type !== this.normalizeType(columnMetadata)
32,556✔
1094
    }
32,556✔
1095

26✔
1096
    protected compareColumnLength(
26✔
1097
        tableColumn: TableColumn,
32,610✔
1098
        columnMetadata: ColumnMetadata,
32,610✔
1099
    ): boolean {
32,610✔
1100
        // The Database Engine determines the data type of the computed column by applying the rules
32,610✔
1101
        // of data type precedence to the expressions specified in the formula.
32,610✔
1102
        if (columnMetadata.asExpression) return false
32,610✔
1103

32,546✔
1104
        return (
32,546✔
1105
            tableColumn.length.toUpperCase() !==
32,546✔
1106
            this.getColumnLength(columnMetadata).toUpperCase()
32,546✔
1107
        )
32,546✔
1108
    }
32,546✔
1109

26✔
1110
    protected lowerDefaultValueIfNecessary(value: string | undefined) {
26✔
1111
        // SqlServer saves function calls in default value as lowercase https://github.com/typeorm/typeorm/issues/2733
52,824✔
1112
        if (!value) {
52,824✔
1113
            return value
48,792✔
1114
        }
48,792✔
1115
        return value
4,032✔
1116
            .split(`'`)
4,032✔
1117
            .map((v, i) => {
4,032✔
1118
                return i % 2 === 1 ? v : v.toLowerCase()
6,544✔
1119
            })
4,032✔
1120
            .join(`'`)
4,032✔
1121
    }
4,032✔
1122

26✔
1123
    /**
26✔
1124
     * Creates a new connection pool for a given database credentials.
26✔
1125
     */
26✔
1126
    protected createPool(
26✔
1127
        options: SqlServerConnectionOptions,
974✔
1128
        credentials: SqlServerConnectionCredentialsOptions,
974✔
1129
    ): Promise<any> {
974✔
1130
        credentials = Object.assign(
974✔
1131
            {},
974✔
1132
            credentials,
974✔
1133
            DriverUtils.buildDriverOptions(credentials),
974✔
1134
        ) // todo: do it better way
974✔
1135

974✔
1136
        // todo: credentials.domain is deprecation. remove it in future
974✔
1137
        const authentication = !credentials.domain
974✔
1138
            ? credentials.authentication
974✔
1139
            : {
974!
1140
                  type: "ntlm",
×
1141
                  options: {
×
1142
                      domain: credentials.domain,
×
1143
                      userName: credentials.username,
×
1144
                      password: credentials.password,
×
1145
                  },
×
1146
              }
×
1147
        // build connection options for the driver
974✔
1148
        const connectionOptions = Object.assign(
974✔
1149
            {},
974✔
1150
            {
974✔
1151
                connectionTimeout: this.options.connectionTimeout,
974✔
1152
                requestTimeout: this.options.requestTimeout,
974✔
1153
                stream: this.options.stream,
974✔
1154
                pool: this.options.pool,
974✔
1155
                options: this.options.options,
974✔
1156
            },
974✔
1157
            {
974✔
1158
                server: credentials.host,
974✔
1159
                database: credentials.database,
974✔
1160
                port: credentials.port,
974✔
1161
                user: credentials.username,
974✔
1162
                password: credentials.password,
974✔
1163
                authentication: authentication,
974✔
1164
            },
974✔
1165
            options.extra || {},
974!
1166
        )
974✔
1167

974✔
1168
        // set default useUTC option if it hasn't been set
974✔
1169
        if (!connectionOptions.options) {
974✔
1170
            connectionOptions.options = { useUTC: false }
970✔
1171
        } else if (!connectionOptions.options.useUTC) {
974✔
1172
            Object.assign(connectionOptions.options, { useUTC: false })
4✔
1173
        }
4✔
1174

974✔
1175
        // Match the next release of tedious for configuration options
974✔
1176
        // Also prevents warning messages.
974✔
1177
        Object.assign(connectionOptions.options, { enableArithAbort: true })
974✔
1178

974✔
1179
        // pooling is enabled either when its set explicitly to true,
974✔
1180
        // either when its not defined at all (e.g. enabled by default)
974✔
1181
        return new Promise<void>((ok, fail) => {
974✔
1182
            const pool = new this.mssql.ConnectionPool(connectionOptions)
974✔
1183

974✔
1184
            const { logger } = this.connection
974✔
1185

974✔
1186
            const poolErrorHandler =
974✔
1187
                (options.pool && options.pool.errorHandler) ||
974!
1188
                ((error: any) =>
974✔
1189
                    logger.log("warn", `MSSQL pool raised an error. ${error}`))
974✔
1190
            /**
974✔
1191
             * Attaching an error handler to pool errors is essential, as, otherwise, errors raised will go unhandled and
974✔
1192
             * cause the hosting app to crash.
974✔
1193
             */
974✔
1194
            pool.on("error", poolErrorHandler)
974✔
1195

974✔
1196
            const connection = pool.connect((err: any) => {
974✔
1197
                if (err) return fail(err)
974!
1198
                ok(connection)
974✔
1199
            })
974✔
1200
        })
974✔
1201
    }
974✔
1202
}
26✔
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