mssql 12.3.1 → 12.5.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 +116 -3
- package/lib/base/connection-pool.js +67 -14
- package/lib/base/index.js +2 -0
- package/lib/base/prepared-statement.js +72 -19
- package/lib/base/request.js +144 -67
- package/lib/base/transaction.js +46 -5
- package/lib/diagnostics.js +153 -0
- package/lib/msnodesqlv8/connection-pool.js +15 -2
- package/lib/msnodesqlv8/request.js +6 -3
- package/lib/tedious/connection-pool.js +13 -0
- package/lib/tedious/request.js +11 -0
- package/lib/tedious/transaction.js +7 -2
- package/lib/udt.js +25 -23
- package/lib/utils.js +10 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -185,6 +185,7 @@ const config = {
|
|
|
185
185
|
* [Metadata](#metadata)
|
|
186
186
|
* [Data Types](#data-types)
|
|
187
187
|
* [SQL injection](#sql-injection)
|
|
188
|
+
* [Diagnostics Channel](#diagnostics-channel)
|
|
188
189
|
* [Contributing](https://github.com/tediousjs/node-mssql/wiki/Contributing)
|
|
189
190
|
* [11.x to 12.x changes](#11x-to-12x-changes)
|
|
190
191
|
* [10.x to 11.x changes](#10x-to-11x-changes)
|
|
@@ -855,11 +856,22 @@ const config = sql.ConnectionPool.parseConnectionString('Server=localhost,1433;D
|
|
|
855
856
|
## Request
|
|
856
857
|
|
|
857
858
|
```javascript
|
|
858
|
-
const request = new sql.Request(/* [pool or transaction] */)
|
|
859
|
+
const request = new sql.Request(/* [pool or transaction], [options] */)
|
|
859
860
|
```
|
|
860
861
|
|
|
861
862
|
If you omit pool/transaction argument, global pool is used instead.
|
|
862
863
|
|
|
864
|
+
The optional `options` argument allows per-request configuration overrides:
|
|
865
|
+
|
|
866
|
+
- **requestTimeout** - Override the pool's default request timeout (in ms) for this request only. This applies to queries and stored procedure executions; it does not apply to bulk data transfers (`request.bulk()`), which stream to completion as long as the connection is healthy. If you need to bound a bulk transfer, wrap the call with your own timer and call `request.cancel()`.
|
|
867
|
+
|
|
868
|
+
```javascript
|
|
869
|
+
// Request with a 60-second timeout instead of the pool default
|
|
870
|
+
const request = new sql.Request(pool, { requestTimeout: 60000 })
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
**Note:** When using the global pool, you must still pass `undefined` as the first argument to use options: `new sql.Request(undefined, { requestTimeout: 60000 })`.
|
|
874
|
+
|
|
863
875
|
### Events
|
|
864
876
|
|
|
865
877
|
- **recordset(columns)** - Dispatched when metadata for new recordset are parsed.
|
|
@@ -1225,11 +1237,13 @@ request.cancel()
|
|
|
1225
1237
|
**IMPORTANT:** always use `Transaction` class to create transactions - it ensures that all your requests are executed on one connection. Once you call `begin`, a single connection is acquired from the connection pool and all subsequent requests (initialized with the `Transaction` object) are executed exclusively on this connection. After you call `commit` or `rollback`, connection is then released back to the connection pool.
|
|
1226
1238
|
|
|
1227
1239
|
```javascript
|
|
1228
|
-
const transaction = new sql.Transaction(/* [pool] */)
|
|
1240
|
+
const transaction = new sql.Transaction(/* [pool], [options] */)
|
|
1229
1241
|
```
|
|
1230
1242
|
|
|
1231
1243
|
If you omit connection argument, global connection is used instead.
|
|
1232
1244
|
|
|
1245
|
+
The optional `options` argument allows per-transaction configuration overrides (e.g. `{ requestTimeout: 60000 }`). These are inherited by any requests created from this transaction unless overridden at the request level. Note that the timeout applies to data requests only, not to the `begin`/`commit`/`rollback` operations themselves.
|
|
1246
|
+
|
|
1233
1247
|
__Example__
|
|
1234
1248
|
|
|
1235
1249
|
```javascript
|
|
@@ -1377,11 +1391,13 @@ __Errors__
|
|
|
1377
1391
|
**IMPORTANT:** always use `PreparedStatement` class to create prepared statements - it ensures that all your executions of prepared statement are executed on one connection. Once you call `prepare`, a single connection is acquired from the connection pool and all subsequent executions are executed exclusively on this connection. After you call `unprepare`, the connection is then released back to the connection pool.
|
|
1378
1392
|
|
|
1379
1393
|
```javascript
|
|
1380
|
-
const ps = new sql.PreparedStatement(/* [pool] */)
|
|
1394
|
+
const ps = new sql.PreparedStatement(/* [pool], [options] */)
|
|
1381
1395
|
```
|
|
1382
1396
|
|
|
1383
1397
|
If you omit the connection argument, the global connection is used instead.
|
|
1384
1398
|
|
|
1399
|
+
The optional `options` argument allows per-statement configuration overrides (e.g. `{ requestTimeout: 60000 }`). The timeout is applied to the `prepare`, `execute`, and `unprepare` operations.
|
|
1400
|
+
|
|
1385
1401
|
__Example__
|
|
1386
1402
|
|
|
1387
1403
|
```javascript
|
|
@@ -2220,6 +2236,103 @@ request.query('select @myval as myval', (err, result) => {
|
|
|
2220
2236
|
})
|
|
2221
2237
|
```
|
|
2222
2238
|
|
|
2239
|
+
## Diagnostics Channel
|
|
2240
|
+
|
|
2241
|
+
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.
|
|
2242
|
+
|
|
2243
|
+
All channel name constants are exported from the package:
|
|
2244
|
+
|
|
2245
|
+
```js
|
|
2246
|
+
const { CHANNELS } = require('mssql')
|
|
2247
|
+
```
|
|
2248
|
+
|
|
2249
|
+
### TracingChannels (async lifecycle)
|
|
2250
|
+
|
|
2251
|
+
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>`:
|
|
2252
|
+
|
|
2253
|
+
```js
|
|
2254
|
+
const dc = require('node:diagnostics_channel')
|
|
2255
|
+
const { CHANNELS } = require('mssql')
|
|
2256
|
+
|
|
2257
|
+
dc.subscribe(`tracing:${CHANNELS.TRACE_QUERY}:start`, ({ command, requestId }) => {
|
|
2258
|
+
console.log(`[${requestId}] Query: ${command}`)
|
|
2259
|
+
})
|
|
2260
|
+
|
|
2261
|
+
dc.subscribe(`tracing:${CHANNELS.TRACE_QUERY}:error`, ({ requestId, error }) => {
|
|
2262
|
+
console.error(`[${requestId}] Failed:`, error.message)
|
|
2263
|
+
})
|
|
2264
|
+
```
|
|
2265
|
+
|
|
2266
|
+
| Constant | Channel name | Wraps |
|
|
2267
|
+
|---|---|---|
|
|
2268
|
+
| `TRACE_QUERY` | `mssql:query` | `request.query()` |
|
|
2269
|
+
| `TRACE_BATCH` | `mssql:batch` | `request.batch()` |
|
|
2270
|
+
| `TRACE_EXECUTE` | `mssql:execute` | `request.execute()` |
|
|
2271
|
+
| `TRACE_BULK` | `mssql:bulk` | `request.bulk()` |
|
|
2272
|
+
| `TRACE_CONNECT` | `mssql:connect` | `pool.connect()` |
|
|
2273
|
+
| `TRACE_POOL_ACQUIRE` | `mssql:pool:acquire` | Pool connection acquire (wait time) |
|
|
2274
|
+
| `TRACE_PREPARED_STATEMENT_PREPARE` | `mssql:prepared-statement:prepare` | `ps.prepare()` |
|
|
2275
|
+
| `TRACE_PREPARED_STATEMENT_EXECUTE` | `mssql:prepared-statement:execute` | `ps.execute()` |
|
|
2276
|
+
|
|
2277
|
+
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).
|
|
2278
|
+
|
|
2279
|
+
> **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.
|
|
2280
|
+
|
|
2281
|
+
> **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.
|
|
2282
|
+
|
|
2283
|
+
> **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.
|
|
2284
|
+
|
|
2285
|
+
### Point-event channels
|
|
2286
|
+
|
|
2287
|
+
These emit single events at state transitions via `dc.subscribe()`:
|
|
2288
|
+
|
|
2289
|
+
```js
|
|
2290
|
+
dc.subscribe(CHANNELS.CONNECTION_RELEASE, ({ connectionId, poolId }) => {
|
|
2291
|
+
console.log(`Pool ${poolId}: connection ${connectionId} released`)
|
|
2292
|
+
})
|
|
2293
|
+
```
|
|
2294
|
+
|
|
2295
|
+
| Constant | Channel name | Description |
|
|
2296
|
+
|---|---|---|
|
|
2297
|
+
| `CONNECTION_ACQUIRE` | `mssql:connection:acquire` | Connection borrowed from pool |
|
|
2298
|
+
| `CONNECTION_RELEASE` | `mssql:connection:release` | Connection returned to pool |
|
|
2299
|
+
| `CONNECTION_CREATE` | `mssql:connection:create` | New connection created in pool |
|
|
2300
|
+
| `CONNECTION_DESTROY` | `mssql:connection:destroy` | Connection destroyed |
|
|
2301
|
+
| `POOL_CLOSE` | `mssql:pool:close` | Pool closed (includes `reason`: `'closed'` or `'error'`; `error` on failure) |
|
|
2302
|
+
| `TRANSACTION_BEGIN` | `mssql:transaction:begin` | Transaction begun (includes numeric `isolationLevel` and `isolationLevelName`) |
|
|
2303
|
+
| `TRANSACTION_COMMIT` | `mssql:transaction:commit` | Transaction committed |
|
|
2304
|
+
| `TRANSACTION_ROLLBACK` | `mssql:transaction:rollback` | Transaction rolled back (includes `aborted` flag) |
|
|
2305
|
+
| `REQUEST_CANCEL` | `mssql:request:cancel` | Request cancelled |
|
|
2306
|
+
| `PREPARED_STATEMENT_UNPREPARE` | `mssql:prepared-statement:unprepare` | Prepared statement released |
|
|
2307
|
+
|
|
2308
|
+
### Example: OpenTelemetry Spans
|
|
2309
|
+
|
|
2310
|
+
```js
|
|
2311
|
+
const dc = require('node:diagnostics_channel')
|
|
2312
|
+
const { trace, SpanKind, SpanStatusCode } = require('@opentelemetry/api')
|
|
2313
|
+
const { CHANNELS } = require('mssql')
|
|
2314
|
+
|
|
2315
|
+
const tracer = trace.getTracer('mssql')
|
|
2316
|
+
const queryTC = dc.tracingChannel(CHANNELS.TRACE_QUERY)
|
|
2317
|
+
|
|
2318
|
+
queryTC.subscribe({
|
|
2319
|
+
start (ctx) {
|
|
2320
|
+
ctx.span = tracer.startSpan('mssql.query', {
|
|
2321
|
+
kind: SpanKind.CLIENT,
|
|
2322
|
+
attributes: { 'db.system': 'mssql', 'db.query.text': ctx.command },
|
|
2323
|
+
})
|
|
2324
|
+
},
|
|
2325
|
+
asyncEnd (ctx) { ctx.span?.end() },
|
|
2326
|
+
error (ctx) {
|
|
2327
|
+
if (ctx.span) {
|
|
2328
|
+
ctx.span.recordException(ctx.error)
|
|
2329
|
+
ctx.span.setStatus({ code: SpanStatusCode.ERROR })
|
|
2330
|
+
ctx.span.end()
|
|
2331
|
+
}
|
|
2332
|
+
},
|
|
2333
|
+
})
|
|
2334
|
+
```
|
|
2335
|
+
|
|
2223
2336
|
## 11.x to 12.x changes
|
|
2224
2337
|
|
|
2225
2338
|
- Config objects are no longer cloned by the library. Mutating a config object after passing it to a `ConnectionPool` results in undefined behaviour.
|
|
@@ -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)
|
|
@@ -576,21 +627,23 @@ class ConnectionPool extends EventEmitter {
|
|
|
576
627
|
/**
|
|
577
628
|
* Returns new request using this connection.
|
|
578
629
|
*
|
|
630
|
+
* @param {{ requestTimeout?: number }} [conf] Per-request overrides.
|
|
579
631
|
* @return {Request}
|
|
580
632
|
*/
|
|
581
633
|
|
|
582
|
-
request () {
|
|
583
|
-
return new shared.driver.Request(this)
|
|
634
|
+
request (conf) {
|
|
635
|
+
return new shared.driver.Request(this, conf)
|
|
584
636
|
}
|
|
585
637
|
|
|
586
638
|
/**
|
|
587
639
|
* Returns new transaction using this connection.
|
|
588
640
|
*
|
|
641
|
+
* @param {{ requestTimeout?: number }} [conf] Per-transaction overrides, cascaded to child requests.
|
|
589
642
|
* @return {Transaction}
|
|
590
643
|
*/
|
|
591
644
|
|
|
592
|
-
transaction () {
|
|
593
|
-
return new shared.driver.Transaction(this)
|
|
645
|
+
transaction (conf) {
|
|
646
|
+
return new shared.driver.Transaction(this, conf)
|
|
594
647
|
}
|
|
595
648
|
|
|
596
649
|
/**
|
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.
|
|
@@ -20,10 +21,11 @@ class PreparedStatement extends EventEmitter {
|
|
|
20
21
|
/**
|
|
21
22
|
* Creates a new Prepared Statement.
|
|
22
23
|
*
|
|
23
|
-
* @param {ConnectionPool|Transaction} [
|
|
24
|
+
* @param {ConnectionPool|Transaction} [parent]
|
|
25
|
+
* @param {{ requestTimeout?: number }} [overrides]
|
|
24
26
|
*/
|
|
25
27
|
|
|
26
|
-
constructor (parent) {
|
|
28
|
+
constructor (parent, overrides = {}) {
|
|
27
29
|
super()
|
|
28
30
|
|
|
29
31
|
IDS.add(this, 'PreparedStatement')
|
|
@@ -33,6 +35,10 @@ class PreparedStatement extends EventEmitter {
|
|
|
33
35
|
this._handle = 0
|
|
34
36
|
this.prepared = false
|
|
35
37
|
this.parameters = {}
|
|
38
|
+
this.overrides = {}
|
|
39
|
+
if (Number.isFinite(overrides?.requestTimeout) && overrides.requestTimeout >= 0) {
|
|
40
|
+
this.overrides.requestTimeout = overrides.requestTimeout
|
|
41
|
+
}
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
get config () {
|
|
@@ -194,16 +200,28 @@ class PreparedStatement extends EventEmitter {
|
|
|
194
200
|
|
|
195
201
|
prepare (statement, callback) {
|
|
196
202
|
if (typeof callback === 'function') {
|
|
197
|
-
this._prepare
|
|
203
|
+
traceCallback(CHANNELS.TRACE_PREPARED_STATEMENT_PREPARE, this._prepare, 1, () => ({
|
|
204
|
+
statement: statement || this.statement,
|
|
205
|
+
parameters: Object.keys(this.parameters),
|
|
206
|
+
preparedStatementId: IDS.get(this),
|
|
207
|
+
poolId: getPoolId(this)
|
|
208
|
+
}), this, [statement, callback])
|
|
198
209
|
return this
|
|
199
210
|
}
|
|
200
211
|
|
|
201
|
-
return
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
return tracePromise(CHANNELS.TRACE_PREPARED_STATEMENT_PREPARE, () => {
|
|
213
|
+
return new shared.Promise((resolve, reject) => {
|
|
214
|
+
this._prepare(statement, err => {
|
|
215
|
+
if (err) return reject(err)
|
|
216
|
+
resolve(this)
|
|
217
|
+
})
|
|
205
218
|
})
|
|
206
|
-
})
|
|
219
|
+
}, () => ({
|
|
220
|
+
statement: statement || this.statement,
|
|
221
|
+
parameters: Object.keys(this.parameters),
|
|
222
|
+
preparedStatementId: IDS.get(this),
|
|
223
|
+
poolId: getPoolId(this)
|
|
224
|
+
}))
|
|
207
225
|
}
|
|
208
226
|
|
|
209
227
|
/**
|
|
@@ -232,7 +250,8 @@ class PreparedStatement extends EventEmitter {
|
|
|
232
250
|
this._acquiredConnection = connection
|
|
233
251
|
this._acquiredConfig = config
|
|
234
252
|
|
|
235
|
-
const req = new shared.driver.Request(this)
|
|
253
|
+
const req = new shared.driver.Request(this, this.overrides)
|
|
254
|
+
req._internal = true
|
|
236
255
|
req.stream = false
|
|
237
256
|
req.output('handle', TYPES.Int)
|
|
238
257
|
req.input('params', TYPES.NVarChar, ((() => {
|
|
@@ -276,15 +295,35 @@ class PreparedStatement extends EventEmitter {
|
|
|
276
295
|
|
|
277
296
|
execute (values, callback) {
|
|
278
297
|
if (this.stream || (typeof callback === 'function')) {
|
|
279
|
-
|
|
298
|
+
if (typeof callback !== 'function') {
|
|
299
|
+
// Stream mode without a callback: no async boundary for traceCallback
|
|
300
|
+
// to hook — fall through to the untraced call. Subscribers interested
|
|
301
|
+
// in streaming completion should listen to Request events.
|
|
302
|
+
return this._execute(values, callback)
|
|
303
|
+
}
|
|
304
|
+
return traceCallback(CHANNELS.TRACE_PREPARED_STATEMENT_EXECUTE, this._execute, 1, () => ({
|
|
305
|
+
statement: this.statement,
|
|
306
|
+
parameters: Object.keys(this.parameters),
|
|
307
|
+
handle: this._handle,
|
|
308
|
+
preparedStatementId: IDS.get(this),
|
|
309
|
+
poolId: getPoolId(this)
|
|
310
|
+
}), this, [values, callback])
|
|
280
311
|
}
|
|
281
312
|
|
|
282
|
-
return
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
313
|
+
return tracePromise(CHANNELS.TRACE_PREPARED_STATEMENT_EXECUTE, () => {
|
|
314
|
+
return new shared.Promise((resolve, reject) => {
|
|
315
|
+
this._execute(values, (err, recordset) => {
|
|
316
|
+
if (err) return reject(err)
|
|
317
|
+
resolve(recordset)
|
|
318
|
+
})
|
|
286
319
|
})
|
|
287
|
-
})
|
|
320
|
+
}, () => ({
|
|
321
|
+
statement: this.statement,
|
|
322
|
+
parameters: Object.keys(this.parameters),
|
|
323
|
+
handle: this._handle,
|
|
324
|
+
preparedStatementId: IDS.get(this),
|
|
325
|
+
poolId: getPoolId(this)
|
|
326
|
+
}))
|
|
288
327
|
}
|
|
289
328
|
|
|
290
329
|
/**
|
|
@@ -294,7 +333,8 @@ class PreparedStatement extends EventEmitter {
|
|
|
294
333
|
*/
|
|
295
334
|
|
|
296
335
|
_execute (values, callback) {
|
|
297
|
-
const req = new shared.driver.Request(this)
|
|
336
|
+
const req = new shared.driver.Request(this, this.overrides)
|
|
337
|
+
req._internal = true
|
|
298
338
|
req.stream = this.stream
|
|
299
339
|
req.arrayRowMode = this.arrayRowMode
|
|
300
340
|
req.input('handle', TYPES.Int, this._handle)
|
|
@@ -334,13 +374,25 @@ class PreparedStatement extends EventEmitter {
|
|
|
334
374
|
|
|
335
375
|
unprepare (callback) {
|
|
336
376
|
if (typeof callback === 'function') {
|
|
337
|
-
this._unprepare(
|
|
377
|
+
this._unprepare(err => {
|
|
378
|
+
if (!err) {
|
|
379
|
+
publish(CHANNELS.PREPARED_STATEMENT_UNPREPARE, () => ({
|
|
380
|
+
preparedStatementId: IDS.get(this),
|
|
381
|
+
poolId: getPoolId(this)
|
|
382
|
+
}))
|
|
383
|
+
}
|
|
384
|
+
callback(err)
|
|
385
|
+
})
|
|
338
386
|
return this
|
|
339
387
|
}
|
|
340
388
|
|
|
341
389
|
return new shared.Promise((resolve, reject) => {
|
|
342
390
|
this._unprepare(err => {
|
|
343
391
|
if (err) return reject(err)
|
|
392
|
+
publish(CHANNELS.PREPARED_STATEMENT_UNPREPARE, () => ({
|
|
393
|
+
preparedStatementId: IDS.get(this),
|
|
394
|
+
poolId: getPoolId(this)
|
|
395
|
+
}))
|
|
344
396
|
resolve()
|
|
345
397
|
})
|
|
346
398
|
})
|
|
@@ -362,7 +414,8 @@ class PreparedStatement extends EventEmitter {
|
|
|
362
414
|
return setImmediate(callback, new TransactionError("Can't unprepare the statement. There is a request in progress.", 'EREQINPROG'))
|
|
363
415
|
}
|
|
364
416
|
|
|
365
|
-
const req = new shared.driver.Request(this)
|
|
417
|
+
const req = new shared.driver.Request(this, this.overrides)
|
|
418
|
+
req._internal = true
|
|
366
419
|
req.stream = false
|
|
367
420
|
req.input('handle', TYPES.Int, this._handle)
|
|
368
421
|
req.execute('sp_unprepare', err => {
|