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 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
 
@@ -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
- * [Known Issues](#known-issues)
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, 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).
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) 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)).
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
- ## Known issues
2224
+ ## Diagnostics Channel
2143
2225
 
2144
- ### Tedious
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
- - 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))
2147
- - 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))
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 acquirePromise = shared.Promise.resolve(this._acquire()).catch(err => {
375
- this.emit('error', err)
376
- throw err
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(callback)
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 new shared.Promise((resolve, reject) => {
426
- return this._connect(err => {
427
- if (err) return reject(err)
428
- resolve(this)
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(statement, callback)
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 new shared.Promise((resolve, reject) => {
202
- this._prepare(statement, err => {
203
- if (err) return reject(err)
204
- resolve(this)
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
- return this._execute(values, callback)
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 new shared.Promise((resolve, reject) => {
283
- this._execute(values, (err, recordset) => {
284
- if (err) return reject(err)
285
- resolve(recordset)
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(callback)
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 => {