mssql 12.3.0 → 12.4.0
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 +189 -14
- package/lib/base/connection-pool.js +61 -10
- package/lib/base/index.js +2 -0
- package/lib/base/prepared-statement.js +62 -14
- package/lib/base/request.js +137 -65
- package/lib/base/transaction.js +32 -1
- package/lib/diagnostics.js +153 -0
- package/lib/msnodesqlv8/connection-pool.js +15 -2
- package/lib/tedious/connection-pool.js +13 -0
- package/lib/tedious/request.js +55 -25
- package/lib/tedious/transaction.js +5 -0
- package/lib/udt.js +25 -23
- package/lib/utils.js +10 -1
- package/package.json +2 -2
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
|
|
|
@@ -137,6 +137,8 @@ const config = {
|
|
|
137
137
|
* [ConnectionPool](#connections-1)
|
|
138
138
|
* [connect](#connect-callback)
|
|
139
139
|
* [close](#close)
|
|
140
|
+
* [Pool properties](#pool-properties)
|
|
141
|
+
* [parseConnectionString](#connectionpoolparseconnectionstring-connectionstring)
|
|
140
142
|
|
|
141
143
|
### Requests
|
|
142
144
|
|
|
@@ -144,6 +146,8 @@ const config = {
|
|
|
144
146
|
* [execute](#execute-procedure-callback)
|
|
145
147
|
* [input](#input-name-type-value)
|
|
146
148
|
* [output](#output-name-type-value)
|
|
149
|
+
* [replaceInput](#replaceinput-name-type-value-1)
|
|
150
|
+
* [replaceOutput](#replaceoutput-name-type-value)
|
|
147
151
|
* [toReadableStream](#toReadableStream)
|
|
148
152
|
* [pipe](#pipe-stream)
|
|
149
153
|
* [query](#query-command-callback)
|
|
@@ -181,8 +185,11 @@ const config = {
|
|
|
181
185
|
* [Metadata](#metadata)
|
|
182
186
|
* [Data Types](#data-types)
|
|
183
187
|
* [SQL injection](#sql-injection)
|
|
184
|
-
* [
|
|
188
|
+
* [Diagnostics Channel](#diagnostics-channel)
|
|
185
189
|
* [Contributing](https://github.com/tediousjs/node-mssql/wiki/Contributing)
|
|
190
|
+
* [11.x to 12.x changes](#11x-to-12x-changes)
|
|
191
|
+
* [10.x to 11.x changes](#10x-to-11x-changes)
|
|
192
|
+
* [9.x to 10.x changes](#9x-to-10x-changes)
|
|
186
193
|
* [8.x to 9.x changes](#8x-to-9x-changes)
|
|
187
194
|
* [7.x to 8.x changes](#7x-to-8x-changes)
|
|
188
195
|
* [6.x to 7.x changes](#6x-to-7x-changes)
|
|
@@ -724,7 +731,7 @@ ___
|
|
|
724
731
|
|
|
725
732
|
### MSNodeSQLv8
|
|
726
733
|
|
|
727
|
-
Alternative driver
|
|
734
|
+
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).
|
|
728
735
|
|
|
729
736
|
**To use this driver you must use this `require` statement:**
|
|
730
737
|
|
|
@@ -816,6 +823,36 @@ __Example__
|
|
|
816
823
|
pool.close()
|
|
817
824
|
```
|
|
818
825
|
|
|
826
|
+
---------------------------------------
|
|
827
|
+
|
|
828
|
+
### Pool properties
|
|
829
|
+
|
|
830
|
+
These properties are available on a connected `ConnectionPool` instance (after `connect()` has resolved):
|
|
831
|
+
|
|
832
|
+
- **pool.healthy** - `Boolean` - Whether the pool is able to create new connections.
|
|
833
|
+
- **pool.size** - `Number` - Total number of connections in the pool (free + used + pending creation).
|
|
834
|
+
- **pool.available** - `Number` - Number of free connections in the pool.
|
|
835
|
+
- **pool.pending** - `Number` - Number of pending connection acquisition requests.
|
|
836
|
+
- **pool.borrowed** - `Number` - Number of connections currently in use.
|
|
837
|
+
- **pool.connected** - `Boolean` - Whether the pool is connected.
|
|
838
|
+
- **pool.connecting** - `Boolean` - Whether the pool is currently connecting.
|
|
839
|
+
|
|
840
|
+
---------------------------------------
|
|
841
|
+
|
|
842
|
+
### ConnectionPool.parseConnectionString (connectionString)
|
|
843
|
+
|
|
844
|
+
Parses a connection string into a configuration object. This is a static method.
|
|
845
|
+
|
|
846
|
+
__Arguments__
|
|
847
|
+
|
|
848
|
+
- **connectionString** - Classic or Azure AD connection string.
|
|
849
|
+
|
|
850
|
+
__Example__
|
|
851
|
+
|
|
852
|
+
```javascript
|
|
853
|
+
const config = sql.ConnectionPool.parseConnectionString('Server=localhost,1433;Database=mydb;User Id=sa;Password=pwd')
|
|
854
|
+
```
|
|
855
|
+
|
|
819
856
|
## Request
|
|
820
857
|
|
|
821
858
|
```javascript
|
|
@@ -903,6 +940,13 @@ __JS Data Type To SQL Data Type Map__
|
|
|
903
940
|
|
|
904
941
|
Default data type for unknown object is `sql.NVarChar`.
|
|
905
942
|
|
|
943
|
+
When a `Number` value is provided without an explicit type, the library inspects the value to choose the best SQL type:
|
|
944
|
+
- Integers within the 32-bit signed range → `sql.Int`
|
|
945
|
+
- Integers outside the 32-bit range → `sql.BigInt`
|
|
946
|
+
- Non-integer numbers → `sql.Float`
|
|
947
|
+
|
|
948
|
+
JavaScript `bigint` primitives follow the same range logic (`sql.Int` for values within the 32-bit signed range, `sql.BigInt` otherwise).
|
|
949
|
+
|
|
906
950
|
You can define your own type map.
|
|
907
951
|
|
|
908
952
|
```javascript
|
|
@@ -946,6 +990,46 @@ __Errors__ (synchronous)
|
|
|
946
990
|
|
|
947
991
|
---------------------------------------
|
|
948
992
|
|
|
993
|
+
### replaceInput (name, type, value)
|
|
994
|
+
|
|
995
|
+
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.
|
|
996
|
+
|
|
997
|
+
__Arguments__
|
|
998
|
+
|
|
999
|
+
- **name** - Name of the input parameter without @ char.
|
|
1000
|
+
- **type** - SQL data type of input parameter.
|
|
1001
|
+
- **value** - Input parameter value.
|
|
1002
|
+
|
|
1003
|
+
Unlike `input()`, `replaceInput()` requires an explicit SQL type — auto type inference is not supported.
|
|
1004
|
+
|
|
1005
|
+
__Example__
|
|
1006
|
+
|
|
1007
|
+
```javascript
|
|
1008
|
+
request.input('myval', sql.Int, 1)
|
|
1009
|
+
request.replaceInput('myval', sql.Int, 2)
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
---------------------------------------
|
|
1013
|
+
|
|
1014
|
+
### replaceOutput (name, type, [value])
|
|
1015
|
+
|
|
1016
|
+
Replace an existing output parameter on the request.
|
|
1017
|
+
|
|
1018
|
+
__Arguments__
|
|
1019
|
+
|
|
1020
|
+
- **name** - Name of the output parameter without @ char.
|
|
1021
|
+
- **type** - SQL data type of output parameter.
|
|
1022
|
+
- **value** - Output parameter value initial value. Optional.
|
|
1023
|
+
|
|
1024
|
+
__Example__
|
|
1025
|
+
|
|
1026
|
+
```javascript
|
|
1027
|
+
request.output('myval', sql.Int)
|
|
1028
|
+
request.replaceOutput('myval', sql.BigInt)
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
---------------------------------------
|
|
1032
|
+
|
|
949
1033
|
### toReadableStream
|
|
950
1034
|
|
|
951
1035
|
Convert request to a Node.js ReadableStream
|
|
@@ -1044,7 +1128,7 @@ request.query('select 1 as number; select 2 as number', (err, result) => {
|
|
|
1044
1128
|
|
|
1045
1129
|
### batch (batch, [callback])
|
|
1046
1130
|
|
|
1047
|
-
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)
|
|
1131
|
+
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)).
|
|
1048
1132
|
|
|
1049
1133
|
NOTE: Table-Valued Parameter (TVP) is not supported in batch.
|
|
1050
1134
|
|
|
@@ -1072,8 +1156,6 @@ __Errors__
|
|
|
1072
1156
|
- ENOTBEGUN (`TransactionError`) - Transaction has not begun.
|
|
1073
1157
|
- EABORT (`TransactionError`) - Transaction was aborted (by user or because of an error).
|
|
1074
1158
|
|
|
1075
|
-
You can enable multiple recordsets in queries with the `request.multiple = true` command.
|
|
1076
|
-
|
|
1077
1159
|
---------------------------------------
|
|
1078
1160
|
|
|
1079
1161
|
### bulk (table, [options,] [callback])
|
|
@@ -2139,12 +2221,109 @@ request.query('select @myval as myval', (err, result) => {
|
|
|
2139
2221
|
})
|
|
2140
2222
|
```
|
|
2141
2223
|
|
|
2142
|
-
##
|
|
2224
|
+
## Diagnostics Channel
|
|
2143
2225
|
|
|
2144
|
-
|
|
2226
|
+
node-mssql publishes telemetry through Node.js [`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html), enabling APM tools and custom instrumentation to observe queries, connections, and internal events without modifying application code. When no subscribers are active, overhead is near-zero.
|
|
2227
|
+
|
|
2228
|
+
All channel name constants are exported from the package:
|
|
2229
|
+
|
|
2230
|
+
```js
|
|
2231
|
+
const { CHANNELS } = require('mssql')
|
|
2232
|
+
```
|
|
2233
|
+
|
|
2234
|
+
### TracingChannels (async lifecycle)
|
|
2235
|
+
|
|
2236
|
+
These use [`TracingChannel`](https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel) to wrap async operations, emitting `start`, `end`, `asyncStart`, `asyncEnd`, and `error` sub-events. Subscribe via `tracing:<name>:<event>`:
|
|
2237
|
+
|
|
2238
|
+
```js
|
|
2239
|
+
const dc = require('node:diagnostics_channel')
|
|
2240
|
+
const { CHANNELS } = require('mssql')
|
|
2241
|
+
|
|
2242
|
+
dc.subscribe(`tracing:${CHANNELS.TRACE_QUERY}:start`, ({ command, requestId }) => {
|
|
2243
|
+
console.log(`[${requestId}] Query: ${command}`)
|
|
2244
|
+
})
|
|
2245
|
+
|
|
2246
|
+
dc.subscribe(`tracing:${CHANNELS.TRACE_QUERY}:error`, ({ requestId, error }) => {
|
|
2247
|
+
console.error(`[${requestId}] Failed:`, error.message)
|
|
2248
|
+
})
|
|
2249
|
+
```
|
|
2250
|
+
|
|
2251
|
+
| Constant | Channel name | Wraps |
|
|
2252
|
+
|---|---|---|
|
|
2253
|
+
| `TRACE_QUERY` | `mssql:query` | `request.query()` |
|
|
2254
|
+
| `TRACE_BATCH` | `mssql:batch` | `request.batch()` |
|
|
2255
|
+
| `TRACE_EXECUTE` | `mssql:execute` | `request.execute()` |
|
|
2256
|
+
| `TRACE_BULK` | `mssql:bulk` | `request.bulk()` |
|
|
2257
|
+
| `TRACE_CONNECT` | `mssql:connect` | `pool.connect()` |
|
|
2258
|
+
| `TRACE_POOL_ACQUIRE` | `mssql:pool:acquire` | Pool connection acquire (wait time) |
|
|
2259
|
+
| `TRACE_PREPARED_STATEMENT_PREPARE` | `mssql:prepared-statement:prepare` | `ps.prepare()` |
|
|
2260
|
+
| `TRACE_PREPARED_STATEMENT_EXECUTE` | `mssql:prepared-statement:execute` | `ps.execute()` |
|
|
2261
|
+
|
|
2262
|
+
TracingChannel contexts include identifiers (`requestId`, `poolId`), operation details (SQL text, procedure name, parameter names), and — on completion — `result` or `error`. Parameter **values** are never included (only their names).
|
|
2263
|
+
|
|
2264
|
+
> **Note on SQL text in trace contexts:** `command` / `procedure` / `statement` fields contain the SQL text as sent to the server, to support OTel `db.query.text` conventions. Because node-mssql is parameterised-query-first, user-supplied values flow through `parameters` and do not appear in the SQL text. Avoid hard-coding credentials, tokens, or PII as inline SQL literals — anything hard-coded into a raw query will appear verbatim in trace contexts.
|
|
2265
|
+
|
|
2266
|
+
> **Note on identifiers:** `connectionId`, `poolId`, `requestId`, `transactionId`, and `preparedStatementId` are monotonically increasing integers scoped to the current node process. They are not stable across restarts and cannot be used to correlate activity across processes.
|
|
2267
|
+
|
|
2268
|
+
> **Note:** TracingChannel instrumentation fires for both the promise and callback APIs. The callback API is traced via Node's `TracingChannel#traceCallback`, which emits the same `start` / `end` / `asyncStart` / `asyncEnd` / `error` sub-events as the promise path, so subscribers do not need to branch by API style. Point-event channels (connection, transaction, pool lifecycle) likewise fire regardless of API style.
|
|
2269
|
+
|
|
2270
|
+
### Point-event channels
|
|
2271
|
+
|
|
2272
|
+
These emit single events at state transitions via `dc.subscribe()`:
|
|
2273
|
+
|
|
2274
|
+
```js
|
|
2275
|
+
dc.subscribe(CHANNELS.CONNECTION_RELEASE, ({ connectionId, poolId }) => {
|
|
2276
|
+
console.log(`Pool ${poolId}: connection ${connectionId} released`)
|
|
2277
|
+
})
|
|
2278
|
+
```
|
|
2279
|
+
|
|
2280
|
+
| Constant | Channel name | Description |
|
|
2281
|
+
|---|---|---|
|
|
2282
|
+
| `CONNECTION_ACQUIRE` | `mssql:connection:acquire` | Connection borrowed from pool |
|
|
2283
|
+
| `CONNECTION_RELEASE` | `mssql:connection:release` | Connection returned to pool |
|
|
2284
|
+
| `CONNECTION_CREATE` | `mssql:connection:create` | New connection created in pool |
|
|
2285
|
+
| `CONNECTION_DESTROY` | `mssql:connection:destroy` | Connection destroyed |
|
|
2286
|
+
| `POOL_CLOSE` | `mssql:pool:close` | Pool closed (includes `reason`: `'closed'` or `'error'`; `error` on failure) |
|
|
2287
|
+
| `TRANSACTION_BEGIN` | `mssql:transaction:begin` | Transaction begun (includes numeric `isolationLevel` and `isolationLevelName`) |
|
|
2288
|
+
| `TRANSACTION_COMMIT` | `mssql:transaction:commit` | Transaction committed |
|
|
2289
|
+
| `TRANSACTION_ROLLBACK` | `mssql:transaction:rollback` | Transaction rolled back (includes `aborted` flag) |
|
|
2290
|
+
| `REQUEST_CANCEL` | `mssql:request:cancel` | Request cancelled |
|
|
2291
|
+
| `PREPARED_STATEMENT_UNPREPARE` | `mssql:prepared-statement:unprepare` | Prepared statement released |
|
|
2292
|
+
|
|
2293
|
+
### Example: OpenTelemetry Spans
|
|
2294
|
+
|
|
2295
|
+
```js
|
|
2296
|
+
const dc = require('node:diagnostics_channel')
|
|
2297
|
+
const { trace, SpanKind, SpanStatusCode } = require('@opentelemetry/api')
|
|
2298
|
+
const { CHANNELS } = require('mssql')
|
|
2299
|
+
|
|
2300
|
+
const tracer = trace.getTracer('mssql')
|
|
2301
|
+
const queryTC = dc.tracingChannel(CHANNELS.TRACE_QUERY)
|
|
2302
|
+
|
|
2303
|
+
queryTC.subscribe({
|
|
2304
|
+
start (ctx) {
|
|
2305
|
+
ctx.span = tracer.startSpan('mssql.query', {
|
|
2306
|
+
kind: SpanKind.CLIENT,
|
|
2307
|
+
attributes: { 'db.system': 'mssql', 'db.query.text': ctx.command },
|
|
2308
|
+
})
|
|
2309
|
+
},
|
|
2310
|
+
asyncEnd (ctx) { ctx.span?.end() },
|
|
2311
|
+
error (ctx) {
|
|
2312
|
+
if (ctx.span) {
|
|
2313
|
+
ctx.span.recordException(ctx.error)
|
|
2314
|
+
ctx.span.setStatus({ code: SpanStatusCode.ERROR })
|
|
2315
|
+
ctx.span.end()
|
|
2316
|
+
}
|
|
2317
|
+
},
|
|
2318
|
+
})
|
|
2319
|
+
```
|
|
2320
|
+
|
|
2321
|
+
## 11.x to 12.x changes
|
|
2145
2322
|
|
|
2146
|
-
-
|
|
2147
|
-
-
|
|
2323
|
+
- Config objects are no longer cloned by the library. Mutating a config object after passing it to a `ConnectionPool` results in undefined behaviour.
|
|
2324
|
+
- Removed `rfdc` dependency
|
|
2325
|
+
- Upgraded to tedious version 19
|
|
2326
|
+
- Upgraded `@tediousjs/connection-string` to 0.6.x
|
|
2148
2327
|
|
|
2149
2328
|
## 10.x to 11.x changes
|
|
2150
2329
|
|
|
@@ -2220,10 +2399,6 @@ to create new connections or not
|
|
|
2220
2399
|
[npm-url]: https://www.npmjs.com/package/mssql
|
|
2221
2400
|
[downloads-image]: https://img.shields.io/npm/dm/mssql.svg?style=flat-square
|
|
2222
2401
|
[downloads-url]: https://www.npmjs.com/package/mssql
|
|
2223
|
-
[david-image]: https://img.shields.io/david/tediousjs/node-mssql.svg?style=flat-square
|
|
2224
|
-
[david-url]: https://david-dm.org/tediousjs/node-mssql
|
|
2225
|
-
[appveyor-image]: https://ci.appveyor.com/api/projects/status/e5gq1a0ujwams9t7/branch/master?svg=true
|
|
2226
|
-
[appveyor-url]: https://ci.appveyor.com/project/tediousjs/node-mssql
|
|
2227
2402
|
|
|
2228
2403
|
[tedious-url]: https://www.npmjs.com/package/tedious
|
|
2229
2404
|
[msnodesqlv8-url]: https://www.npmjs.com/package/msnodesqlv8
|
|
@@ -8,6 +8,7 @@ const { IDS } = require('../utils')
|
|
|
8
8
|
const ConnectionError = require('../error/connection-error')
|
|
9
9
|
const shared = require('../shared')
|
|
10
10
|
const { MSSQLError } = require('../error')
|
|
11
|
+
const { CHANNELS, tracePromise, traceCallback, publish } = require('../diagnostics')
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Class ConnectionPool.
|
|
@@ -371,10 +372,26 @@ class ConnectionPool extends EventEmitter {
|
|
|
371
372
|
*/
|
|
372
373
|
|
|
373
374
|
acquire (requester, callback) {
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
375
|
+
const requestId = IDS.get(requester)
|
|
376
|
+
const poolId = IDS.get(this)
|
|
377
|
+
|
|
378
|
+
const acquirePromise = tracePromise(CHANNELS.TRACE_POOL_ACQUIRE, () => {
|
|
379
|
+
return shared.Promise.resolve(this._acquire()).catch(err => {
|
|
380
|
+
this.emit('error', err)
|
|
381
|
+
throw err
|
|
382
|
+
}).then(connection => {
|
|
383
|
+
publish(CHANNELS.CONNECTION_ACQUIRE, () => ({
|
|
384
|
+
connectionId: IDS.get(connection),
|
|
385
|
+
requestId,
|
|
386
|
+
poolId
|
|
387
|
+
}))
|
|
388
|
+
return connection
|
|
389
|
+
})
|
|
390
|
+
}, () => ({
|
|
391
|
+
poolId,
|
|
392
|
+
requestId
|
|
393
|
+
}))
|
|
394
|
+
|
|
378
395
|
if (typeof callback === 'function') {
|
|
379
396
|
acquirePromise.then(connection => callback(null, connection, this.config)).catch(callback)
|
|
380
397
|
return this
|
|
@@ -403,6 +420,11 @@ class ConnectionPool extends EventEmitter {
|
|
|
403
420
|
release (connection) {
|
|
404
421
|
debug('connection(%d): released', IDS.get(connection))
|
|
405
422
|
|
|
423
|
+
publish(CHANNELS.CONNECTION_RELEASE, () => ({
|
|
424
|
+
connectionId: IDS.get(connection),
|
|
425
|
+
poolId: IDS.get(this)
|
|
426
|
+
}))
|
|
427
|
+
|
|
406
428
|
if (this.pool) {
|
|
407
429
|
this.pool.release(connection)
|
|
408
430
|
}
|
|
@@ -418,16 +440,36 @@ class ConnectionPool extends EventEmitter {
|
|
|
418
440
|
|
|
419
441
|
connect (callback) {
|
|
420
442
|
if (typeof callback === 'function') {
|
|
421
|
-
this._connect(
|
|
443
|
+
traceCallback(CHANNELS.TRACE_CONNECT, this._connect, 0, () => ({
|
|
444
|
+
server: this.config.server,
|
|
445
|
+
port: this.config.port,
|
|
446
|
+
database: this.config.database,
|
|
447
|
+
poolId: IDS.get(this),
|
|
448
|
+
poolConfig: {
|
|
449
|
+
min: (this.config.pool && this.config.pool.min) || 0,
|
|
450
|
+
max: (this.config.pool && this.config.pool.max) || 10
|
|
451
|
+
}
|
|
452
|
+
}), this, [callback])
|
|
422
453
|
return this
|
|
423
454
|
}
|
|
424
455
|
|
|
425
|
-
return
|
|
426
|
-
return
|
|
427
|
-
|
|
428
|
-
|
|
456
|
+
return tracePromise(CHANNELS.TRACE_CONNECT, () => {
|
|
457
|
+
return new shared.Promise((resolve, reject) => {
|
|
458
|
+
return this._connect(err => {
|
|
459
|
+
if (err) return reject(err)
|
|
460
|
+
resolve(this)
|
|
461
|
+
})
|
|
429
462
|
})
|
|
430
|
-
})
|
|
463
|
+
}, () => ({
|
|
464
|
+
server: this.config.server,
|
|
465
|
+
port: this.config.port,
|
|
466
|
+
database: this.config.database,
|
|
467
|
+
poolId: IDS.get(this),
|
|
468
|
+
poolConfig: {
|
|
469
|
+
min: (this.config.pool && this.config.pool.min) || 0,
|
|
470
|
+
max: (this.config.pool && this.config.pool.max) || 10
|
|
471
|
+
}
|
|
472
|
+
}))
|
|
431
473
|
}
|
|
432
474
|
|
|
433
475
|
/**
|
|
@@ -559,11 +601,20 @@ class ConnectionPool extends EventEmitter {
|
|
|
559
601
|
|
|
560
602
|
this.pool.destroy().then(() => {
|
|
561
603
|
debug('pool(%d): pool closed, removing pool reference and executing close callbacks', IDS.get(this))
|
|
604
|
+
publish(CHANNELS.POOL_CLOSE, () => ({
|
|
605
|
+
poolId: IDS.get(this),
|
|
606
|
+
reason: 'closed'
|
|
607
|
+
}))
|
|
562
608
|
this.pool = null
|
|
563
609
|
this._closeStack.forEach(cb => {
|
|
564
610
|
setImmediate(cb, null)
|
|
565
611
|
})
|
|
566
612
|
}).catch(err => {
|
|
613
|
+
publish(CHANNELS.POOL_CLOSE, () => ({
|
|
614
|
+
poolId: IDS.get(this),
|
|
615
|
+
reason: 'error',
|
|
616
|
+
error: err
|
|
617
|
+
}))
|
|
567
618
|
this.pool = null
|
|
568
619
|
this._closeStack.forEach(cb => {
|
|
569
620
|
setImmediate(cb, err)
|
package/lib/base/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const Table = require('../table')
|
|
|
10
10
|
const ISOLATION_LEVEL = require('../isolationlevel')
|
|
11
11
|
const { TYPES } = require('../datatypes')
|
|
12
12
|
const { connect, close, on, off, removeListener, query, batch } = require('../global-connection')
|
|
13
|
+
const { CHANNELS } = require('../diagnostics')
|
|
13
14
|
|
|
14
15
|
module.exports = {
|
|
15
16
|
ConnectionPool,
|
|
@@ -31,6 +32,7 @@ module.exports = {
|
|
|
31
32
|
Table,
|
|
32
33
|
ISOLATION_LEVEL,
|
|
33
34
|
TYPES,
|
|
35
|
+
CHANNELS,
|
|
34
36
|
MAX: 65535, // (1 << 16) - 1
|
|
35
37
|
map: shared.map,
|
|
36
38
|
getTypeByValue: shared.getTypeByValue,
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const debug = require('debug')('mssql:base')
|
|
4
4
|
const { EventEmitter } = require('node:events')
|
|
5
|
-
const { IDS, objectHasProperty } = require('../utils')
|
|
5
|
+
const { IDS, objectHasProperty, getPoolId } = require('../utils')
|
|
6
6
|
const globalConnection = require('../global-connection')
|
|
7
7
|
const { TransactionError, PreparedStatementError } = require('../error')
|
|
8
8
|
const shared = require('../shared')
|
|
9
9
|
const { TYPES, declare } = require('../datatypes')
|
|
10
|
+
const { CHANNELS, tracePromise, traceCallback, publish } = require('../diagnostics')
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Class PreparedStatement.
|
|
@@ -194,16 +195,28 @@ class PreparedStatement extends EventEmitter {
|
|
|
194
195
|
|
|
195
196
|
prepare (statement, callback) {
|
|
196
197
|
if (typeof callback === 'function') {
|
|
197
|
-
this._prepare
|
|
198
|
+
traceCallback(CHANNELS.TRACE_PREPARED_STATEMENT_PREPARE, this._prepare, 1, () => ({
|
|
199
|
+
statement: statement || this.statement,
|
|
200
|
+
parameters: Object.keys(this.parameters),
|
|
201
|
+
preparedStatementId: IDS.get(this),
|
|
202
|
+
poolId: getPoolId(this)
|
|
203
|
+
}), this, [statement, callback])
|
|
198
204
|
return this
|
|
199
205
|
}
|
|
200
206
|
|
|
201
|
-
return
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
207
|
+
return tracePromise(CHANNELS.TRACE_PREPARED_STATEMENT_PREPARE, () => {
|
|
208
|
+
return new shared.Promise((resolve, reject) => {
|
|
209
|
+
this._prepare(statement, err => {
|
|
210
|
+
if (err) return reject(err)
|
|
211
|
+
resolve(this)
|
|
212
|
+
})
|
|
205
213
|
})
|
|
206
|
-
})
|
|
214
|
+
}, () => ({
|
|
215
|
+
statement: statement || this.statement,
|
|
216
|
+
parameters: Object.keys(this.parameters),
|
|
217
|
+
preparedStatementId: IDS.get(this),
|
|
218
|
+
poolId: getPoolId(this)
|
|
219
|
+
}))
|
|
207
220
|
}
|
|
208
221
|
|
|
209
222
|
/**
|
|
@@ -233,6 +246,7 @@ class PreparedStatement extends EventEmitter {
|
|
|
233
246
|
this._acquiredConfig = config
|
|
234
247
|
|
|
235
248
|
const req = new shared.driver.Request(this)
|
|
249
|
+
req._internal = true
|
|
236
250
|
req.stream = false
|
|
237
251
|
req.output('handle', TYPES.Int)
|
|
238
252
|
req.input('params', TYPES.NVarChar, ((() => {
|
|
@@ -276,15 +290,35 @@ class PreparedStatement extends EventEmitter {
|
|
|
276
290
|
|
|
277
291
|
execute (values, callback) {
|
|
278
292
|
if (this.stream || (typeof callback === 'function')) {
|
|
279
|
-
|
|
293
|
+
if (typeof callback !== 'function') {
|
|
294
|
+
// Stream mode without a callback: no async boundary for traceCallback
|
|
295
|
+
// to hook — fall through to the untraced call. Subscribers interested
|
|
296
|
+
// in streaming completion should listen to Request events.
|
|
297
|
+
return this._execute(values, callback)
|
|
298
|
+
}
|
|
299
|
+
return traceCallback(CHANNELS.TRACE_PREPARED_STATEMENT_EXECUTE, this._execute, 1, () => ({
|
|
300
|
+
statement: this.statement,
|
|
301
|
+
parameters: Object.keys(this.parameters),
|
|
302
|
+
handle: this._handle,
|
|
303
|
+
preparedStatementId: IDS.get(this),
|
|
304
|
+
poolId: getPoolId(this)
|
|
305
|
+
}), this, [values, callback])
|
|
280
306
|
}
|
|
281
307
|
|
|
282
|
-
return
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
308
|
+
return tracePromise(CHANNELS.TRACE_PREPARED_STATEMENT_EXECUTE, () => {
|
|
309
|
+
return new shared.Promise((resolve, reject) => {
|
|
310
|
+
this._execute(values, (err, recordset) => {
|
|
311
|
+
if (err) return reject(err)
|
|
312
|
+
resolve(recordset)
|
|
313
|
+
})
|
|
286
314
|
})
|
|
287
|
-
})
|
|
315
|
+
}, () => ({
|
|
316
|
+
statement: this.statement,
|
|
317
|
+
parameters: Object.keys(this.parameters),
|
|
318
|
+
handle: this._handle,
|
|
319
|
+
preparedStatementId: IDS.get(this),
|
|
320
|
+
poolId: getPoolId(this)
|
|
321
|
+
}))
|
|
288
322
|
}
|
|
289
323
|
|
|
290
324
|
/**
|
|
@@ -295,6 +329,7 @@ class PreparedStatement extends EventEmitter {
|
|
|
295
329
|
|
|
296
330
|
_execute (values, callback) {
|
|
297
331
|
const req = new shared.driver.Request(this)
|
|
332
|
+
req._internal = true
|
|
298
333
|
req.stream = this.stream
|
|
299
334
|
req.arrayRowMode = this.arrayRowMode
|
|
300
335
|
req.input('handle', TYPES.Int, this._handle)
|
|
@@ -334,13 +369,25 @@ class PreparedStatement extends EventEmitter {
|
|
|
334
369
|
|
|
335
370
|
unprepare (callback) {
|
|
336
371
|
if (typeof callback === 'function') {
|
|
337
|
-
this._unprepare(
|
|
372
|
+
this._unprepare(err => {
|
|
373
|
+
if (!err) {
|
|
374
|
+
publish(CHANNELS.PREPARED_STATEMENT_UNPREPARE, () => ({
|
|
375
|
+
preparedStatementId: IDS.get(this),
|
|
376
|
+
poolId: getPoolId(this)
|
|
377
|
+
}))
|
|
378
|
+
}
|
|
379
|
+
callback(err)
|
|
380
|
+
})
|
|
338
381
|
return this
|
|
339
382
|
}
|
|
340
383
|
|
|
341
384
|
return new shared.Promise((resolve, reject) => {
|
|
342
385
|
this._unprepare(err => {
|
|
343
386
|
if (err) return reject(err)
|
|
387
|
+
publish(CHANNELS.PREPARED_STATEMENT_UNPREPARE, () => ({
|
|
388
|
+
preparedStatementId: IDS.get(this),
|
|
389
|
+
poolId: getPoolId(this)
|
|
390
|
+
}))
|
|
344
391
|
resolve()
|
|
345
392
|
})
|
|
346
393
|
})
|
|
@@ -363,6 +410,7 @@ class PreparedStatement extends EventEmitter {
|
|
|
363
410
|
}
|
|
364
411
|
|
|
365
412
|
const req = new shared.driver.Request(this)
|
|
413
|
+
req._internal = true
|
|
366
414
|
req.stream = false
|
|
367
415
|
req.input('handle', TYPES.Int, this._handle)
|
|
368
416
|
req.execute('sp_unprepare', err => {
|