mssql 12.2.3 → 12.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Microsoft SQL Server client for Node.js**
4
4
 
5
- [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Appveyor CI][appveyor-image]][appveyor-url] [![Join the chat at https://gitter.im/patriksimek/node-mssql](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/patriksimek/node-mssql?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5
+ [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url]
6
6
 
7
7
  Supported TDS drivers:
8
8
 
@@ -133,9 +133,12 @@ const config = {
133
133
  ### Connections
134
134
 
135
135
  * [Pool Management](#pool-management)
136
+ * [Connection Validation](#connection-validation)
136
137
  * [ConnectionPool](#connections-1)
137
138
  * [connect](#connect-callback)
138
139
  * [close](#close)
140
+ * [Pool properties](#pool-properties)
141
+ * [parseConnectionString](#connectionpoolparseconnectionstring-connectionstring)
139
142
 
140
143
  ### Requests
141
144
 
@@ -143,6 +146,8 @@ const config = {
143
146
  * [execute](#execute-procedure-callback)
144
147
  * [input](#input-name-type-value)
145
148
  * [output](#output-name-type-value)
149
+ * [replaceInput](#replaceinput-name-type-value-1)
150
+ * [replaceOutput](#replaceoutput-name-type-value)
146
151
  * [toReadableStream](#toReadableStream)
147
152
  * [pipe](#pipe-stream)
148
153
  * [query](#query-command-callback)
@@ -180,8 +185,10 @@ const config = {
180
185
  * [Metadata](#metadata)
181
186
  * [Data Types](#data-types)
182
187
  * [SQL injection](#sql-injection)
183
- * [Known Issues](#known-issues)
184
188
  * [Contributing](https://github.com/tediousjs/node-mssql/wiki/Contributing)
189
+ * [11.x to 12.x changes](#11x-to-12x-changes)
190
+ * [10.x to 11.x changes](#10x-to-11x-changes)
191
+ * [9.x to 10.x changes](#9x-to-10x-changes)
185
192
  * [8.x to 9.x changes](#8x-to-9x-changes)
186
193
  * [7.x to 8.x changes](#7x-to-8x-changes)
187
194
  * [6.x to 7.x changes](#6x-to-7x-changes)
@@ -574,6 +581,30 @@ sql.query('SELECT * FROM [example]').then((result) => {
574
581
  })
575
582
  ```
576
583
 
584
+ ### Connection Validation
585
+
586
+ When a connection is acquired from the pool, it can be validated to ensure it is still usable. This is controlled by the `validateConnection` config option.
587
+
588
+ ```javascript
589
+ const config = {
590
+ server: 'localhost',
591
+ // ...
592
+ validateConnection: true // default
593
+ }
594
+ ```
595
+
596
+ The following values are supported:
597
+
598
+ | Value | Description |
599
+ |---|---|
600
+ | `true` (default) | Executes `SELECT 1` against the connection before handing it to the caller. This is the most thorough check — it verifies end-to-end connectivity — but adds a round-trip query for every pool acquisition. |
601
+ | `'socket'` | Performs a lightweight, synchronous check of the underlying connection state and TCP socket health. No SQL query is executed. This is significantly cheaper at scale and catches most failure modes (closed connections, destroyed sockets, wrong protocol state), but will not detect issues like server-side session invalidation. **Tedious driver only** — with msnodesqlv8, this value falls back to `SELECT 1` behaviour because native ODBC connections do not expose socket-level properties. |
602
+ | `false` | Disables validation entirely. The connection is assumed to be healthy if it has not been flagged as closed or errored. Use this only if your application already handles stale connection errors gracefully. |
603
+
604
+ #### When to use `'socket'` mode
605
+
606
+ If your application maintains a large connection pool and you see high volumes of `SELECT 1` queries in your SQL Server monitoring, switching to `'socket'` mode can dramatically reduce overhead. TCP keepalive (enabled by default in tedious at 30-second intervals) will independently detect and close dead connections over time, so the socket-level check provides a good balance between reliability and performance.
607
+
577
608
  ## Configuration
578
609
 
579
610
  The following is an example configuration object:
@@ -608,6 +639,7 @@ const config = {
608
639
  - **pool.min** - The minimum of connections there can be in the pool (default: `0`).
609
640
  - **pool.idleTimeoutMillis** - The Number of milliseconds before closing an unused connection (default: `30000`).
610
641
  - **arrayRowMode** - Return row results as a an array instead of a keyed object. Also adds `columns` array. (default: `false`) See [Handling Duplicate Column Names](#handling-duplicate-column-names)
642
+ - **validateConnection** - Controls how connections are validated when acquired from the pool. See [Connection Validation](#connection-validation) for details. (default: `true`)
611
643
 
612
644
  Complete list of pool options can be found [here](https://github.com/vincit/tarn.js/#usage).
613
645
 
@@ -698,7 +730,7 @@ ___
698
730
 
699
731
  ### MSNodeSQLv8
700
732
 
701
- Alternative driver, requires Node.js v10+ or newer; Windows (32 or 64-bit) or Linux/macOS (64-bit only). It's not part of the default package so it must be [installed](#msnodesqlv8-driver) in addition. Supports [Windows/Trusted Connection authentication](#windows-authentication-example-using-msnodesqlv8).
733
+ Alternative driver for Windows (32 or 64-bit) or Linux/macOS (64-bit only). It's not part of the default package so it must be [installed](#msnodesqlv8-driver) in addition. Supports [Windows/Trusted Connection authentication](#windows-authentication-example-using-msnodesqlv8).
702
734
 
703
735
  **To use this driver you must use this `require` statement:**
704
736
 
@@ -790,6 +822,36 @@ __Example__
790
822
  pool.close()
791
823
  ```
792
824
 
825
+ ---------------------------------------
826
+
827
+ ### Pool properties
828
+
829
+ These properties are available on a connected `ConnectionPool` instance (after `connect()` has resolved):
830
+
831
+ - **pool.healthy** - `Boolean` - Whether the pool is able to create new connections.
832
+ - **pool.size** - `Number` - Total number of connections in the pool (free + used + pending creation).
833
+ - **pool.available** - `Number` - Number of free connections in the pool.
834
+ - **pool.pending** - `Number` - Number of pending connection acquisition requests.
835
+ - **pool.borrowed** - `Number` - Number of connections currently in use.
836
+ - **pool.connected** - `Boolean` - Whether the pool is connected.
837
+ - **pool.connecting** - `Boolean` - Whether the pool is currently connecting.
838
+
839
+ ---------------------------------------
840
+
841
+ ### ConnectionPool.parseConnectionString (connectionString)
842
+
843
+ Parses a connection string into a configuration object. This is a static method.
844
+
845
+ __Arguments__
846
+
847
+ - **connectionString** - Classic or Azure AD connection string.
848
+
849
+ __Example__
850
+
851
+ ```javascript
852
+ const config = sql.ConnectionPool.parseConnectionString('Server=localhost,1433;Database=mydb;User Id=sa;Password=pwd')
853
+ ```
854
+
793
855
  ## Request
794
856
 
795
857
  ```javascript
@@ -877,6 +939,13 @@ __JS Data Type To SQL Data Type Map__
877
939
 
878
940
  Default data type for unknown object is `sql.NVarChar`.
879
941
 
942
+ When a `Number` value is provided without an explicit type, the library inspects the value to choose the best SQL type:
943
+ - Integers within the 32-bit signed range → `sql.Int`
944
+ - Integers outside the 32-bit range → `sql.BigInt`
945
+ - Non-integer numbers → `sql.Float`
946
+
947
+ JavaScript `bigint` primitives follow the same range logic (`sql.Int` for values within the 32-bit signed range, `sql.BigInt` otherwise).
948
+
880
949
  You can define your own type map.
881
950
 
882
951
  ```javascript
@@ -920,6 +989,46 @@ __Errors__ (synchronous)
920
989
 
921
990
  ---------------------------------------
922
991
 
992
+ ### replaceInput (name, type, value)
993
+
994
+ Replace an existing input parameter on the request. If the parameter was previously added with `input()`, it is removed and re-added with the new type and value. Useful when building queries dynamically or re-using a `Request` object.
995
+
996
+ __Arguments__
997
+
998
+ - **name** - Name of the input parameter without @ char.
999
+ - **type** - SQL data type of input parameter.
1000
+ - **value** - Input parameter value.
1001
+
1002
+ Unlike `input()`, `replaceInput()` requires an explicit SQL type — auto type inference is not supported.
1003
+
1004
+ __Example__
1005
+
1006
+ ```javascript
1007
+ request.input('myval', sql.Int, 1)
1008
+ request.replaceInput('myval', sql.Int, 2)
1009
+ ```
1010
+
1011
+ ---------------------------------------
1012
+
1013
+ ### replaceOutput (name, type, [value])
1014
+
1015
+ Replace an existing output parameter on the request.
1016
+
1017
+ __Arguments__
1018
+
1019
+ - **name** - Name of the output parameter without @ char.
1020
+ - **type** - SQL data type of output parameter.
1021
+ - **value** - Output parameter value initial value. Optional.
1022
+
1023
+ __Example__
1024
+
1025
+ ```javascript
1026
+ request.output('myval', sql.Int)
1027
+ request.replaceOutput('myval', sql.BigInt)
1028
+ ```
1029
+
1030
+ ---------------------------------------
1031
+
923
1032
  ### toReadableStream
924
1033
 
925
1034
  Convert request to a Node.js ReadableStream
@@ -1018,7 +1127,7 @@ request.query('select 1 as number; select 2 as number', (err, result) => {
1018
1127
 
1019
1128
  ### batch (batch, [callback])
1020
1129
 
1021
- Execute the SQL command. Unlike [query](#query-command-callback), it doesn't use `sp_executesql`, so is not likely that SQL Server will reuse the execution plan it generates for the SQL. Use this only in special cases, for example when you need to execute commands like `create procedure` which can't be executed with [query](#query-command-callback) or if you're executing statements longer than 4000 chars on SQL Server 2000. Also you should use this if you're plan to work with local temporary tables ([more information here](http://weblogs.sqlteam.com/mladenp/archive/2006/11/03/17197.aspx)).
1130
+ Execute the SQL command. Unlike [query](#query-command-callback), it doesn't use `sp_executesql`, so is not likely that SQL Server will reuse the execution plan it generates for the SQL. Use this only in special cases, for example when you need to execute commands like `create procedure` which can't be executed with [query](#query-command-callback). Also you should use this if you plan to work with local temporary tables ([more information here](http://weblogs.sqlteam.com/mladenp/archive/2006/11/03/17197.aspx)).
1022
1131
 
1023
1132
  NOTE: Table-Valued Parameter (TVP) is not supported in batch.
1024
1133
 
@@ -1046,8 +1155,6 @@ __Errors__
1046
1155
  - ENOTBEGUN (`TransactionError`) - Transaction has not begun.
1047
1156
  - EABORT (`TransactionError`) - Transaction was aborted (by user or because of an error).
1048
1157
 
1049
- You can enable multiple recordsets in queries with the `request.multiple = true` command.
1050
-
1051
1158
  ---------------------------------------
1052
1159
 
1053
1160
  ### bulk (table, [options,] [callback])
@@ -2113,12 +2220,12 @@ request.query('select @myval as myval', (err, result) => {
2113
2220
  })
2114
2221
  ```
2115
2222
 
2116
- ## Known issues
2117
-
2118
- ### Tedious
2223
+ ## 11.x to 12.x changes
2119
2224
 
2120
- - If you're facing problems with connecting SQL Server 2000, try setting the default TDS version to 7.1 with `config.options.tdsVersion = '7_1'` ([issue](https://github.com/tediousjs/node-mssql/issues/36))
2121
- - If you're executing a statement longer than 4000 chars on SQL Server 2000, always use [batch](#batch-batch-callback) instead of [query](#query-command-callback) ([issue](https://github.com/tediousjs/node-mssql/issues/68))
2225
+ - Config objects are no longer cloned by the library. Mutating a config object after passing it to a `ConnectionPool` results in undefined behaviour.
2226
+ - Removed `rfdc` dependency
2227
+ - Upgraded to tedious version 19
2228
+ - Upgraded `@tediousjs/connection-string` to 0.6.x
2122
2229
 
2123
2230
  ## 10.x to 11.x changes
2124
2231
 
@@ -2194,10 +2301,6 @@ to create new connections or not
2194
2301
  [npm-url]: https://www.npmjs.com/package/mssql
2195
2302
  [downloads-image]: https://img.shields.io/npm/dm/mssql.svg?style=flat-square
2196
2303
  [downloads-url]: https://www.npmjs.com/package/mssql
2197
- [david-image]: https://img.shields.io/david/tediousjs/node-mssql.svg?style=flat-square
2198
- [david-url]: https://david-dm.org/tediousjs/node-mssql
2199
- [appveyor-image]: https://ci.appveyor.com/api/projects/status/e5gq1a0ujwams9t7/branch/master?svg=true
2200
- [appveyor-url]: https://ci.appveyor.com/project/tediousjs/node-mssql
2201
2304
 
2202
2305
  [tedious-url]: https://www.npmjs.com/package/tedious
2203
2306
  [msnodesqlv8-url]: https://www.npmjs.com/package/msnodesqlv8
@@ -117,15 +117,36 @@ class ConnectionPool extends BaseConnectionPool {
117
117
  }
118
118
 
119
119
  _poolValidate (tedious) {
120
- if (tedious && !tedious.closed && !tedious.hasError) {
121
- return !this.config.validateConnection || new shared.Promise((resolve) => {
122
- const req = new tds.Request('SELECT 1;', (err) => {
123
- resolve(!err)
124
- })
125
- tedious.execSql(req)
126
- })
120
+ if (!tedious || tedious.closed || tedious.hasError) {
121
+ return false
122
+ }
123
+
124
+ const mode = this.config.validateConnection
125
+
126
+ if (!mode) {
127
+ return true
128
+ }
129
+
130
+ // Socket-level validation: check connection state and socket health
131
+ // without executing a SQL query. Much cheaper than SELECT 1 at scale.
132
+ if (mode === 'socket') {
133
+ if (tedious.state !== tedious.STATE.LOGGED_IN) {
134
+ return false
135
+ }
136
+ if (!tedious.socket || tedious.socket.destroyed || !tedious.socket.writable) {
137
+ return false
138
+ }
139
+ return true
127
140
  }
128
- return false
141
+
142
+ // SQL-level validation (default): execute SELECT 1 to verify the
143
+ // connection is fully functional end-to-end.
144
+ return new shared.Promise((resolve) => {
145
+ const req = new tds.Request('SELECT 1;', (err) => {
146
+ resolve(!err)
147
+ })
148
+ tedious.execSql(req)
149
+ })
129
150
  }
130
151
 
131
152
  _poolDestroy (tedious) {
@@ -200,9 +200,13 @@ const parameterCorrection = function (value) {
200
200
  }
201
201
 
202
202
  for (const col of value.columns) {
203
+ const tediousType = getTediousType(col.type)
204
+ if (tediousType === tds.TYPES.Variant) {
205
+ throw new RequestError(`Column '${col.name}' in TVP '${value.schema ? value.schema + '.' : ''}${value.name}' uses sql_variant which is not supported by the tedious driver for TVP column types. Consider using a more specific data type.`, 'EARGS')
206
+ }
203
207
  tvp.columns.push({
204
208
  name: col.name,
205
- type: getTediousType(col.type),
209
+ type: tediousType,
206
210
  length: col.length,
207
211
  scale: col.scale,
208
212
  precision: col.precision
@@ -664,9 +668,18 @@ class Request extends BaseRequest {
664
668
  } catch (e) {
665
669
  e.message = `Validation failed for parameter '${name}'. ${e.message}`
666
670
  const err = new RequestError(e, 'EPARAM')
671
+ delete this._cancel
672
+
673
+ if (!hasReturned) {
674
+ for (const event in errorHandlers) {
675
+ connection.removeListener(event, errorHandlers[event])
676
+ }
667
677
 
668
- this.parent.release(connection)
669
- return callback(err)
678
+ this.parent.release(connection)
679
+ hasReturned = true
680
+ return callback(err)
681
+ }
682
+ return
670
683
  }
671
684
  }
672
685
 
@@ -703,21 +716,23 @@ class Request extends BaseRequest {
703
716
 
704
717
  req.sqlTextOrProcedure = `declare ${declarations.join(', ')};select ${assigns.join(', ')};${req.sqlTextOrProcedure};${batchHasOutput ? (`select 1 as [___return___], ${selects.join(', ')}`) : ''}`
705
718
  }
706
- } else {
707
- for (const name in this.parameters) {
708
- if (!objectHasProperty(this.parameters, name)) {
709
- continue
710
- }
711
- const param = this.parameters[name]
712
- if (param.io === 1) {
713
- req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
714
- } else {
715
- req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
716
- }
717
- }
718
719
  }
719
720
 
720
721
  try {
722
+ if (!this._isBatch) {
723
+ for (const name in this.parameters) {
724
+ if (!objectHasProperty(this.parameters, name)) {
725
+ continue
726
+ }
727
+ const param = this.parameters[name]
728
+ if (param.io === 1) {
729
+ req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
730
+ } else {
731
+ req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
732
+ }
733
+ }
734
+ }
735
+
721
736
  connection[this._isBatch ? 'execSqlBatch' : 'execSql'](req)
722
737
  } catch (error) {
723
738
  handleError(true, connection, error)
@@ -983,19 +998,34 @@ class Request extends BaseRequest {
983
998
  output[parameterName] = value
984
999
  })
985
1000
 
986
- for (const name in this.parameters) {
987
- if (!objectHasProperty(this.parameters, name)) {
988
- continue
1001
+ try {
1002
+ for (const name in this.parameters) {
1003
+ if (!objectHasProperty(this.parameters, name)) {
1004
+ continue
1005
+ }
1006
+ const param = this.parameters[name]
1007
+ if (param.io === 1) {
1008
+ req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
1009
+ } else {
1010
+ req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
1011
+ }
989
1012
  }
990
- const param = this.parameters[name]
991
- if (param.io === 1) {
992
- req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
993
- } else {
994
- req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
1013
+
1014
+ connection.callProcedure(req)
1015
+ } catch (error) {
1016
+ const err = error instanceof RequestError ? error : new RequestError(error, 'EREQUEST')
1017
+ delete this._cancel
1018
+
1019
+ if (!hasReturned) {
1020
+ for (const event in errorHandlers) {
1021
+ connection.removeListener(event, errorHandlers[event])
1022
+ }
1023
+
1024
+ this.parent.release(connection)
1025
+ hasReturned = true
1026
+ callback(err)
995
1027
  }
996
1028
  }
997
-
998
- connection.callProcedure(req)
999
1029
  })
1000
1030
  })
1001
1031
  }
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "azure",
22
22
  "node-mssql"
23
23
  ],
24
- "version": "12.2.3",
24
+ "version": "12.3.1",
25
25
  "main": "index.js",
26
26
  "type": "commonjs",
27
27
  "repository": {