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 +118 -15
- package/lib/tedious/connection-pool.js +29 -8
- package/lib/tedious/request.js +55 -25
- package/package.json +1 -1
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]
|
|
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
|
|
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)
|
|
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
|
-
##
|
|
2117
|
-
|
|
2118
|
-
### Tedious
|
|
2223
|
+
## 11.x to 12.x changes
|
|
2119
2224
|
|
|
2120
|
-
-
|
|
2121
|
-
-
|
|
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
|
|
121
|
-
return
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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) {
|
package/lib/tedious/request.js
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
669
|
-
|
|
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
|
-
|
|
987
|
-
|
|
988
|
-
|
|
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
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
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
|
}
|