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/lib/base/request.js
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
const debug = require('debug')('mssql:base')
|
|
4
4
|
const { EventEmitter } = require('node:events')
|
|
5
5
|
const { Readable } = require('node:stream')
|
|
6
|
-
const { IDS, objectHasProperty } = require('../utils')
|
|
6
|
+
const { IDS, objectHasProperty, getPoolId } = require('../utils')
|
|
7
7
|
const globalConnection = require('../global-connection')
|
|
8
8
|
const { RequestError, ConnectionError } = require('../error')
|
|
9
9
|
const { TYPES } = require('../datatypes')
|
|
10
10
|
const shared = require('../shared')
|
|
11
|
+
const { CHANNELS, tracePromise, traceCallback, publish } = require('../diagnostics')
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Class Request.
|
|
@@ -26,10 +27,11 @@ class Request extends EventEmitter {
|
|
|
26
27
|
/**
|
|
27
28
|
* Create new Request.
|
|
28
29
|
*
|
|
29
|
-
* @param {Connection|ConnectionPool|Transaction|PreparedStatement} parent If omitted, global connection is used instead.
|
|
30
|
+
* @param {Connection|ConnectionPool|Transaction|PreparedStatement} [parent] If omitted, global connection is used instead.
|
|
31
|
+
* @param {{ requestTimeout?: number }} [overrides]
|
|
30
32
|
*/
|
|
31
33
|
|
|
32
|
-
constructor (parent) {
|
|
34
|
+
constructor (parent, overrides = {}) {
|
|
33
35
|
super()
|
|
34
36
|
|
|
35
37
|
IDS.add(this, 'Request')
|
|
@@ -37,10 +39,15 @@ class Request extends EventEmitter {
|
|
|
37
39
|
|
|
38
40
|
this.canceled = false
|
|
39
41
|
this._paused = false
|
|
42
|
+
this._internal = false
|
|
40
43
|
this.parent = parent || globalConnection.pool
|
|
41
44
|
this.parameters = {}
|
|
42
45
|
this.stream = null
|
|
43
46
|
this.arrayRowMode = null
|
|
47
|
+
this.overrides = {}
|
|
48
|
+
if (Number.isFinite(overrides?.requestTimeout) && overrides.requestTimeout >= 0) {
|
|
49
|
+
this.overrides.requestTimeout = overrides.requestTimeout
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
get paused () {
|
|
@@ -218,6 +225,24 @@ class Request extends EventEmitter {
|
|
|
218
225
|
return this.output(name, type, value)
|
|
219
226
|
}
|
|
220
227
|
|
|
228
|
+
_getParameterNames () {
|
|
229
|
+
return Object.keys(this.parameters)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
_tracedPromise (channel, contextFactory, fn) {
|
|
233
|
+
if (this._internal) {
|
|
234
|
+
return fn()
|
|
235
|
+
}
|
|
236
|
+
return tracePromise(channel, fn, contextFactory)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
_tracedCallback (channel, contextFactory, fn, position, args) {
|
|
240
|
+
if (this._internal) {
|
|
241
|
+
return fn.apply(this, args)
|
|
242
|
+
}
|
|
243
|
+
return traceCallback(channel, fn, position, contextFactory, this, args)
|
|
244
|
+
}
|
|
245
|
+
|
|
221
246
|
/**
|
|
222
247
|
* Execute the SQL batch.
|
|
223
248
|
*
|
|
@@ -232,7 +257,11 @@ class Request extends EventEmitter {
|
|
|
232
257
|
this.rowsAffected = 0
|
|
233
258
|
|
|
234
259
|
if (typeof callback === 'function') {
|
|
235
|
-
this.
|
|
260
|
+
this._tracedCallback(CHANNELS.TRACE_BATCH, () => ({
|
|
261
|
+
command: batch,
|
|
262
|
+
requestId: IDS.get(this),
|
|
263
|
+
poolId: getPoolId(this)
|
|
264
|
+
}), this._batch, 1, [batch, (err, recordsets, output, rowsAffected) => {
|
|
236
265
|
if (this.stream) {
|
|
237
266
|
if (err) this.emit('error', err)
|
|
238
267
|
err = null
|
|
@@ -250,7 +279,7 @@ class Request extends EventEmitter {
|
|
|
250
279
|
output,
|
|
251
280
|
rowsAffected
|
|
252
281
|
})
|
|
253
|
-
})
|
|
282
|
+
}])
|
|
254
283
|
return this
|
|
255
284
|
}
|
|
256
285
|
|
|
@@ -261,24 +290,31 @@ class Request extends EventEmitter {
|
|
|
261
290
|
batch = this._template(strings, values)
|
|
262
291
|
}
|
|
263
292
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
293
|
+
const batchCommand = batch
|
|
294
|
+
return this._tracedPromise(CHANNELS.TRACE_BATCH, () => ({
|
|
295
|
+
command: batchCommand,
|
|
296
|
+
requestId: IDS.get(this),
|
|
297
|
+
poolId: getPoolId(this)
|
|
298
|
+
}), () => {
|
|
299
|
+
return new shared.Promise((resolve, reject) => {
|
|
300
|
+
this._batch(batchCommand, (err, recordsets, output, rowsAffected) => {
|
|
301
|
+
if (this.stream) {
|
|
302
|
+
if (err) this.emit('error', err)
|
|
303
|
+
err = null
|
|
304
|
+
|
|
305
|
+
this.emit('done', {
|
|
306
|
+
output,
|
|
307
|
+
rowsAffected
|
|
308
|
+
})
|
|
309
|
+
}
|
|
269
310
|
|
|
270
|
-
|
|
311
|
+
if (err) return reject(err)
|
|
312
|
+
resolve({
|
|
313
|
+
recordsets,
|
|
314
|
+
recordset: recordsets && recordsets[0],
|
|
271
315
|
output,
|
|
272
316
|
rowsAffected
|
|
273
317
|
})
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (err) return reject(err)
|
|
277
|
-
resolve({
|
|
278
|
-
recordsets,
|
|
279
|
-
recordset: recordsets && recordsets[0],
|
|
280
|
-
output,
|
|
281
|
-
rowsAffected
|
|
282
318
|
})
|
|
283
319
|
})
|
|
284
320
|
})
|
|
@@ -324,7 +360,12 @@ class Request extends EventEmitter {
|
|
|
324
360
|
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
|
325
361
|
|
|
326
362
|
if (this.stream || typeof callback === 'function') {
|
|
327
|
-
this.
|
|
363
|
+
this._tracedCallback(CHANNELS.TRACE_BULK, () => ({
|
|
364
|
+
table: table.path || table.name,
|
|
365
|
+
rowCount: table.rows ? table.rows.length : 0,
|
|
366
|
+
requestId: IDS.get(this),
|
|
367
|
+
poolId: getPoolId(this)
|
|
368
|
+
}), this._bulk, 2, [table, options, (err, rowsAffected) => {
|
|
328
369
|
if (this.stream) {
|
|
329
370
|
if (err) this.emit('error', err)
|
|
330
371
|
return this.emit('done', {
|
|
@@ -336,15 +377,22 @@ class Request extends EventEmitter {
|
|
|
336
377
|
callback(null, {
|
|
337
378
|
rowsAffected
|
|
338
379
|
})
|
|
339
|
-
})
|
|
380
|
+
}])
|
|
340
381
|
return this
|
|
341
382
|
}
|
|
342
383
|
|
|
343
|
-
return
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
384
|
+
return this._tracedPromise(CHANNELS.TRACE_BULK, () => ({
|
|
385
|
+
table: table.path || table.name,
|
|
386
|
+
rowCount: table.rows ? table.rows.length : 0,
|
|
387
|
+
requestId: IDS.get(this),
|
|
388
|
+
poolId: getPoolId(this)
|
|
389
|
+
}), () => {
|
|
390
|
+
return new shared.Promise((resolve, reject) => {
|
|
391
|
+
this._bulk(table, options, (err, rowsAffected) => {
|
|
392
|
+
if (err) return reject(err)
|
|
393
|
+
resolve({
|
|
394
|
+
rowsAffected
|
|
395
|
+
})
|
|
348
396
|
})
|
|
349
397
|
})
|
|
350
398
|
})
|
|
@@ -429,7 +477,12 @@ class Request extends EventEmitter {
|
|
|
429
477
|
this.rowsAffected = 0
|
|
430
478
|
|
|
431
479
|
if (typeof callback === 'function') {
|
|
432
|
-
this.
|
|
480
|
+
this._tracedCallback(CHANNELS.TRACE_QUERY, () => ({
|
|
481
|
+
command,
|
|
482
|
+
parameters: this._getParameterNames(),
|
|
483
|
+
requestId: IDS.get(this),
|
|
484
|
+
poolId: getPoolId(this)
|
|
485
|
+
}), this._query, 1, [command, (err, recordsets, output, rowsAffected, columns) => {
|
|
433
486
|
if (this.stream) {
|
|
434
487
|
if (err) this.emit('error', err)
|
|
435
488
|
err = null
|
|
@@ -449,7 +502,7 @@ class Request extends EventEmitter {
|
|
|
449
502
|
}
|
|
450
503
|
if (this.arrayRowMode) result.columns = columns
|
|
451
504
|
callback(null, result)
|
|
452
|
-
})
|
|
505
|
+
}])
|
|
453
506
|
return this
|
|
454
507
|
}
|
|
455
508
|
|
|
@@ -460,27 +513,34 @@ class Request extends EventEmitter {
|
|
|
460
513
|
command = this._template(strings, values)
|
|
461
514
|
}
|
|
462
515
|
|
|
463
|
-
return
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
516
|
+
return this._tracedPromise(CHANNELS.TRACE_QUERY, () => ({
|
|
517
|
+
command,
|
|
518
|
+
parameters: this._getParameterNames(),
|
|
519
|
+
requestId: IDS.get(this),
|
|
520
|
+
poolId: getPoolId(this)
|
|
521
|
+
}), () => {
|
|
522
|
+
return new shared.Promise((resolve, reject) => {
|
|
523
|
+
this._query(command, (err, recordsets, output, rowsAffected, columns) => {
|
|
524
|
+
if (this.stream) {
|
|
525
|
+
if (err) this.emit('error', err)
|
|
526
|
+
err = null
|
|
527
|
+
|
|
528
|
+
this.emit('done', {
|
|
529
|
+
output,
|
|
530
|
+
rowsAffected
|
|
531
|
+
})
|
|
532
|
+
}
|
|
468
533
|
|
|
469
|
-
|
|
534
|
+
if (err) return reject(err)
|
|
535
|
+
const result = {
|
|
536
|
+
recordsets,
|
|
537
|
+
recordset: recordsets && recordsets[0],
|
|
470
538
|
output,
|
|
471
539
|
rowsAffected
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const result = {
|
|
477
|
-
recordsets,
|
|
478
|
-
recordset: recordsets && recordsets[0],
|
|
479
|
-
output,
|
|
480
|
-
rowsAffected
|
|
481
|
-
}
|
|
482
|
-
if (this.arrayRowMode) result.columns = columns
|
|
483
|
-
resolve(result)
|
|
540
|
+
}
|
|
541
|
+
if (this.arrayRowMode) result.columns = columns
|
|
542
|
+
resolve(result)
|
|
543
|
+
})
|
|
484
544
|
})
|
|
485
545
|
})
|
|
486
546
|
}
|
|
@@ -518,7 +578,12 @@ class Request extends EventEmitter {
|
|
|
518
578
|
this.rowsAffected = 0
|
|
519
579
|
|
|
520
580
|
if (typeof callback === 'function') {
|
|
521
|
-
this.
|
|
581
|
+
this._tracedCallback(CHANNELS.TRACE_EXECUTE, () => ({
|
|
582
|
+
procedure: command,
|
|
583
|
+
parameters: this._getParameterNames(),
|
|
584
|
+
requestId: IDS.get(this),
|
|
585
|
+
poolId: getPoolId(this)
|
|
586
|
+
}), this._execute, 1, [command, (err, recordsets, output, returnValue, rowsAffected, columns) => {
|
|
522
587
|
if (this.stream) {
|
|
523
588
|
if (err) this.emit('error', err)
|
|
524
589
|
err = null
|
|
@@ -540,33 +605,40 @@ class Request extends EventEmitter {
|
|
|
540
605
|
}
|
|
541
606
|
if (this.arrayRowMode) result.columns = columns
|
|
542
607
|
callback(null, result)
|
|
543
|
-
})
|
|
608
|
+
}])
|
|
544
609
|
return this
|
|
545
610
|
}
|
|
546
611
|
|
|
547
|
-
return
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
612
|
+
return this._tracedPromise(CHANNELS.TRACE_EXECUTE, () => ({
|
|
613
|
+
procedure: command,
|
|
614
|
+
parameters: this._getParameterNames(),
|
|
615
|
+
requestId: IDS.get(this),
|
|
616
|
+
poolId: getPoolId(this)
|
|
617
|
+
}), () => {
|
|
618
|
+
return new shared.Promise((resolve, reject) => {
|
|
619
|
+
this._execute(command, (err, recordsets, output, returnValue, rowsAffected, columns) => {
|
|
620
|
+
if (this.stream) {
|
|
621
|
+
if (err) this.emit('error', err)
|
|
622
|
+
err = null
|
|
623
|
+
|
|
624
|
+
this.emit('done', {
|
|
625
|
+
output,
|
|
626
|
+
rowsAffected,
|
|
627
|
+
returnValue
|
|
628
|
+
})
|
|
629
|
+
}
|
|
552
630
|
|
|
553
|
-
|
|
631
|
+
if (err) return reject(err)
|
|
632
|
+
const result = {
|
|
633
|
+
recordsets,
|
|
634
|
+
recordset: recordsets && recordsets[0],
|
|
554
635
|
output,
|
|
555
636
|
rowsAffected,
|
|
556
637
|
returnValue
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const result = {
|
|
562
|
-
recordsets,
|
|
563
|
-
recordset: recordsets && recordsets[0],
|
|
564
|
-
output,
|
|
565
|
-
rowsAffected,
|
|
566
|
-
returnValue
|
|
567
|
-
}
|
|
568
|
-
if (this.arrayRowMode) result.columns = columns
|
|
569
|
-
resolve(result)
|
|
638
|
+
}
|
|
639
|
+
if (this.arrayRowMode) result.columns = columns
|
|
640
|
+
resolve(result)
|
|
641
|
+
})
|
|
570
642
|
})
|
|
571
643
|
})
|
|
572
644
|
}
|
|
@@ -598,6 +670,11 @@ class Request extends EventEmitter {
|
|
|
598
670
|
|
|
599
671
|
cancel () {
|
|
600
672
|
this._cancel()
|
|
673
|
+
if (!this._internal) {
|
|
674
|
+
publish(CHANNELS.REQUEST_CANCEL, () => ({
|
|
675
|
+
requestId: IDS.get(this)
|
|
676
|
+
}))
|
|
677
|
+
}
|
|
601
678
|
return true
|
|
602
679
|
}
|
|
603
680
|
|
package/lib/base/transaction.js
CHANGED
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const debug = require('debug')('mssql:base')
|
|
4
4
|
const { EventEmitter } = require('node:events')
|
|
5
|
-
const { IDS } = require('../utils')
|
|
5
|
+
const { IDS, getPoolId } = require('../utils')
|
|
6
6
|
const globalConnection = require('../global-connection')
|
|
7
7
|
const { TransactionError } = require('../error')
|
|
8
8
|
const shared = require('../shared')
|
|
9
9
|
const ISOLATION_LEVEL = require('../isolationlevel')
|
|
10
|
+
const { CHANNELS, publish } = require('../diagnostics')
|
|
11
|
+
|
|
12
|
+
const ISOLATION_LEVEL_NAMES = Object.fromEntries(
|
|
13
|
+
Object.entries(ISOLATION_LEVEL).map(([name, value]) => [value, name])
|
|
14
|
+
)
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* Class Transaction.
|
|
@@ -23,10 +28,11 @@ class Transaction extends EventEmitter {
|
|
|
23
28
|
/**
|
|
24
29
|
* Create new Transaction.
|
|
25
30
|
*
|
|
26
|
-
* @param {Connection} [parent] If
|
|
31
|
+
* @param {Connection} [parent] If omitted, global connection is used instead.
|
|
32
|
+
* @param {{ requestTimeout?: number }} [overrides]
|
|
27
33
|
*/
|
|
28
34
|
|
|
29
|
-
constructor (parent) {
|
|
35
|
+
constructor (parent, overrides = {}) {
|
|
30
36
|
super()
|
|
31
37
|
|
|
32
38
|
IDS.add(this, 'Transaction')
|
|
@@ -35,6 +41,10 @@ class Transaction extends EventEmitter {
|
|
|
35
41
|
this.parent = parent || globalConnection.pool
|
|
36
42
|
this.isolationLevel = Transaction.defaultIsolationLevel
|
|
37
43
|
this.name = ''
|
|
44
|
+
this.overrides = {}
|
|
45
|
+
if (Number.isFinite(overrides?.requestTimeout) && overrides.requestTimeout >= 0) {
|
|
46
|
+
this.overrides.requestTimeout = overrides.requestTimeout
|
|
47
|
+
}
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
get config () {
|
|
@@ -101,6 +111,12 @@ class Transaction extends EventEmitter {
|
|
|
101
111
|
if (typeof callback === 'function') {
|
|
102
112
|
this._begin(isolationLevel, err => {
|
|
103
113
|
if (!err) {
|
|
114
|
+
publish(CHANNELS.TRANSACTION_BEGIN, () => ({
|
|
115
|
+
transactionId: IDS.get(this),
|
|
116
|
+
isolationLevel: this.isolationLevel,
|
|
117
|
+
isolationLevelName: ISOLATION_LEVEL_NAMES[this.isolationLevel],
|
|
118
|
+
poolId: getPoolId(this)
|
|
119
|
+
}))
|
|
104
120
|
this.emit('begin')
|
|
105
121
|
}
|
|
106
122
|
callback(err)
|
|
@@ -111,6 +127,12 @@ class Transaction extends EventEmitter {
|
|
|
111
127
|
return new shared.Promise((resolve, reject) => {
|
|
112
128
|
this._begin(isolationLevel, err => {
|
|
113
129
|
if (err) return reject(err)
|
|
130
|
+
publish(CHANNELS.TRANSACTION_BEGIN, () => ({
|
|
131
|
+
transactionId: IDS.get(this),
|
|
132
|
+
isolationLevel: this.isolationLevel,
|
|
133
|
+
isolationLevelName: ISOLATION_LEVEL_NAMES[this.isolationLevel],
|
|
134
|
+
poolId: getPoolId(this)
|
|
135
|
+
}))
|
|
114
136
|
this.emit('begin')
|
|
115
137
|
resolve(this)
|
|
116
138
|
})
|
|
@@ -155,6 +177,9 @@ class Transaction extends EventEmitter {
|
|
|
155
177
|
if (typeof callback === 'function') {
|
|
156
178
|
this._commit(err => {
|
|
157
179
|
if (!err) {
|
|
180
|
+
publish(CHANNELS.TRANSACTION_COMMIT, () => ({
|
|
181
|
+
transactionId: IDS.get(this)
|
|
182
|
+
}))
|
|
158
183
|
this.emit('commit')
|
|
159
184
|
}
|
|
160
185
|
callback(err)
|
|
@@ -165,6 +190,9 @@ class Transaction extends EventEmitter {
|
|
|
165
190
|
return new shared.Promise((resolve, reject) => {
|
|
166
191
|
this._commit(err => {
|
|
167
192
|
if (err) return reject(err)
|
|
193
|
+
publish(CHANNELS.TRANSACTION_COMMIT, () => ({
|
|
194
|
+
transactionId: IDS.get(this)
|
|
195
|
+
}))
|
|
168
196
|
this.emit('commit')
|
|
169
197
|
resolve()
|
|
170
198
|
})
|
|
@@ -196,11 +224,16 @@ class Transaction extends EventEmitter {
|
|
|
196
224
|
/**
|
|
197
225
|
* Returns new request using this transaction.
|
|
198
226
|
*
|
|
227
|
+
* @param {{ requestTimeout?: number }} [config]
|
|
199
228
|
* @return {Request}
|
|
200
229
|
*/
|
|
201
230
|
|
|
202
|
-
request () {
|
|
203
|
-
|
|
231
|
+
request (config) {
|
|
232
|
+
const overrides = { ...this.overrides }
|
|
233
|
+
if (Number.isFinite(config?.requestTimeout) && config.requestTimeout >= 0) {
|
|
234
|
+
overrides.requestTimeout = config.requestTimeout
|
|
235
|
+
}
|
|
236
|
+
return new shared.driver.Request(this, overrides)
|
|
204
237
|
}
|
|
205
238
|
|
|
206
239
|
/**
|
|
@@ -214,6 +247,10 @@ class Transaction extends EventEmitter {
|
|
|
214
247
|
if (typeof callback === 'function') {
|
|
215
248
|
this._rollback(err => {
|
|
216
249
|
if (!err) {
|
|
250
|
+
publish(CHANNELS.TRANSACTION_ROLLBACK, () => ({
|
|
251
|
+
transactionId: IDS.get(this),
|
|
252
|
+
aborted: this._aborted
|
|
253
|
+
}))
|
|
217
254
|
this.emit('rollback', this._aborted)
|
|
218
255
|
}
|
|
219
256
|
callback(err)
|
|
@@ -224,6 +261,10 @@ class Transaction extends EventEmitter {
|
|
|
224
261
|
return new shared.Promise((resolve, reject) => {
|
|
225
262
|
return this._rollback(err => {
|
|
226
263
|
if (err) return reject(err)
|
|
264
|
+
publish(CHANNELS.TRANSACTION_ROLLBACK, () => ({
|
|
265
|
+
transactionId: IDS.get(this),
|
|
266
|
+
aborted: this._aborted
|
|
267
|
+
}))
|
|
227
268
|
this.emit('rollback', this._aborted)
|
|
228
269
|
resolve()
|
|
229
270
|
})
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const dc = require('node:diagnostics_channel')
|
|
4
|
+
|
|
5
|
+
// TracingChannel names
|
|
6
|
+
const TRACE_QUERY = 'mssql:query'
|
|
7
|
+
const TRACE_BATCH = 'mssql:batch'
|
|
8
|
+
const TRACE_EXECUTE = 'mssql:execute'
|
|
9
|
+
const TRACE_BULK = 'mssql:bulk'
|
|
10
|
+
const TRACE_CONNECT = 'mssql:connect'
|
|
11
|
+
const TRACE_POOL_ACQUIRE = 'mssql:pool:acquire'
|
|
12
|
+
const TRACE_PREPARED_STATEMENT_PREPARE = 'mssql:prepared-statement:prepare'
|
|
13
|
+
const TRACE_PREPARED_STATEMENT_EXECUTE = 'mssql:prepared-statement:execute'
|
|
14
|
+
|
|
15
|
+
// Point-event channel names
|
|
16
|
+
const CONNECTION_ACQUIRE = 'mssql:connection:acquire'
|
|
17
|
+
const CONNECTION_RELEASE = 'mssql:connection:release'
|
|
18
|
+
const CONNECTION_CREATE = 'mssql:connection:create'
|
|
19
|
+
const CONNECTION_DESTROY = 'mssql:connection:destroy'
|
|
20
|
+
const POOL_CLOSE = 'mssql:pool:close'
|
|
21
|
+
const TRANSACTION_BEGIN = 'mssql:transaction:begin'
|
|
22
|
+
const TRANSACTION_COMMIT = 'mssql:transaction:commit'
|
|
23
|
+
const TRANSACTION_ROLLBACK = 'mssql:transaction:rollback'
|
|
24
|
+
const REQUEST_CANCEL = 'mssql:request:cancel'
|
|
25
|
+
const PREPARED_STATEMENT_UNPREPARE = 'mssql:prepared-statement:unprepare'
|
|
26
|
+
|
|
27
|
+
const CHANNELS = Object.freeze({
|
|
28
|
+
TRACE_QUERY,
|
|
29
|
+
TRACE_BATCH,
|
|
30
|
+
TRACE_EXECUTE,
|
|
31
|
+
TRACE_BULK,
|
|
32
|
+
TRACE_CONNECT,
|
|
33
|
+
TRACE_POOL_ACQUIRE,
|
|
34
|
+
TRACE_PREPARED_STATEMENT_PREPARE,
|
|
35
|
+
TRACE_PREPARED_STATEMENT_EXECUTE,
|
|
36
|
+
CONNECTION_ACQUIRE,
|
|
37
|
+
CONNECTION_RELEASE,
|
|
38
|
+
CONNECTION_CREATE,
|
|
39
|
+
CONNECTION_DESTROY,
|
|
40
|
+
POOL_CLOSE,
|
|
41
|
+
TRANSACTION_BEGIN,
|
|
42
|
+
TRANSACTION_COMMIT,
|
|
43
|
+
TRANSACTION_ROLLBACK,
|
|
44
|
+
REQUEST_CANCEL,
|
|
45
|
+
PREPARED_STATEMENT_UNPREPARE
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Pre-create TracingChannel instances at module load time
|
|
49
|
+
const tracingChannels = {
|
|
50
|
+
[TRACE_QUERY]: dc.tracingChannel(TRACE_QUERY),
|
|
51
|
+
[TRACE_BATCH]: dc.tracingChannel(TRACE_BATCH),
|
|
52
|
+
[TRACE_EXECUTE]: dc.tracingChannel(TRACE_EXECUTE),
|
|
53
|
+
[TRACE_BULK]: dc.tracingChannel(TRACE_BULK),
|
|
54
|
+
[TRACE_CONNECT]: dc.tracingChannel(TRACE_CONNECT),
|
|
55
|
+
[TRACE_POOL_ACQUIRE]: dc.tracingChannel(TRACE_POOL_ACQUIRE),
|
|
56
|
+
[TRACE_PREPARED_STATEMENT_PREPARE]: dc.tracingChannel(TRACE_PREPARED_STATEMENT_PREPARE),
|
|
57
|
+
[TRACE_PREPARED_STATEMENT_EXECUTE]: dc.tracingChannel(TRACE_PREPARED_STATEMENT_EXECUTE)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Pre-create point-event channel instances at module load time
|
|
61
|
+
const pointChannels = {
|
|
62
|
+
[CONNECTION_ACQUIRE]: dc.channel(CONNECTION_ACQUIRE),
|
|
63
|
+
[CONNECTION_RELEASE]: dc.channel(CONNECTION_RELEASE),
|
|
64
|
+
[CONNECTION_CREATE]: dc.channel(CONNECTION_CREATE),
|
|
65
|
+
[CONNECTION_DESTROY]: dc.channel(CONNECTION_DESTROY),
|
|
66
|
+
[POOL_CLOSE]: dc.channel(POOL_CLOSE),
|
|
67
|
+
[TRANSACTION_BEGIN]: dc.channel(TRANSACTION_BEGIN),
|
|
68
|
+
[TRANSACTION_COMMIT]: dc.channel(TRANSACTION_COMMIT),
|
|
69
|
+
[TRANSACTION_ROLLBACK]: dc.channel(TRANSACTION_ROLLBACK),
|
|
70
|
+
[REQUEST_CANCEL]: dc.channel(REQUEST_CANCEL),
|
|
71
|
+
[PREPARED_STATEMENT_UNPREPARE]: dc.channel(PREPARED_STATEMENT_UNPREPARE)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// TracingChannel.hasSubscribers was added in Node 22. On 18.19 / 20 the
|
|
75
|
+
// aggregate property is undefined, so we fall back to checking the
|
|
76
|
+
// sub-channels directly (these have existed since the original
|
|
77
|
+
// diagnostics_channel API). Preserves the zero-cost fast path across all
|
|
78
|
+
// supported runtimes.
|
|
79
|
+
function tracingChannelHasSubscribers (tc) {
|
|
80
|
+
if (typeof tc.hasSubscribers === 'boolean') return tc.hasSubscribers
|
|
81
|
+
return tc.start.hasSubscribers ||
|
|
82
|
+
tc.end.hasSubscribers ||
|
|
83
|
+
tc.asyncStart.hasSubscribers ||
|
|
84
|
+
tc.asyncEnd.hasSubscribers ||
|
|
85
|
+
tc.error.hasSubscribers
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Trace an async operation using a TracingChannel.
|
|
90
|
+
*
|
|
91
|
+
* When subscribers are active, wraps `fn` with TracingChannel.tracePromise().
|
|
92
|
+
* When no subscribers are active, calls `fn` directly with zero overhead
|
|
93
|
+
* (no context allocation).
|
|
94
|
+
*
|
|
95
|
+
* @param {string} name - TracingChannel name (one of CHANNELS.TRACE_*)
|
|
96
|
+
* @param {Function} fn - The function to trace (must return a Promise)
|
|
97
|
+
* @param {Function} contextFactory - Factory function returning the context object
|
|
98
|
+
* @returns {Promise} The return value of fn
|
|
99
|
+
*/
|
|
100
|
+
function tracePromise (name, fn, contextFactory) {
|
|
101
|
+
const channel = tracingChannels[name]
|
|
102
|
+
if (tracingChannelHasSubscribers(channel)) {
|
|
103
|
+
return channel.tracePromise(fn, contextFactory())
|
|
104
|
+
}
|
|
105
|
+
return fn()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Trace a callback-style async operation using a TracingChannel.
|
|
110
|
+
*
|
|
111
|
+
* When subscribers are active, delegates to TracingChannel.traceCallback,
|
|
112
|
+
* which replaces the callback at `position` in `args` with a wrapped
|
|
113
|
+
* version that publishes to start/end/asyncStart/asyncEnd/error. When
|
|
114
|
+
* no subscribers are active, calls `fn` directly with zero overhead.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} name - TracingChannel name (one of CHANNELS.TRACE_*)
|
|
117
|
+
* @param {Function} fn - The function to call (receives callback at `position`)
|
|
118
|
+
* @param {number} position - Index of the callback within `args`
|
|
119
|
+
* @param {Function} contextFactory - Factory function returning the context object
|
|
120
|
+
* @param {*} thisArg - `this` binding for fn
|
|
121
|
+
* @param {Array} args - Arguments to pass to fn (includes the callback at `position`)
|
|
122
|
+
* @returns {*} The return value of fn
|
|
123
|
+
*/
|
|
124
|
+
function traceCallback (name, fn, position, contextFactory, thisArg, args) {
|
|
125
|
+
const channel = tracingChannels[name]
|
|
126
|
+
if (tracingChannelHasSubscribers(channel)) {
|
|
127
|
+
return channel.traceCallback(fn, position, contextFactory(), thisArg, ...args)
|
|
128
|
+
}
|
|
129
|
+
return fn.apply(thisArg, args)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Publish a point event on a named channel.
|
|
134
|
+
*
|
|
135
|
+
* Only allocates the message object when subscribers are active.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} name - Point-event channel name (one of CHANNELS.*)
|
|
138
|
+
* @param {Function} factory - Factory function that returns the message object
|
|
139
|
+
*/
|
|
140
|
+
function publish (name, factory) {
|
|
141
|
+
const channel = pointChannels[name]
|
|
142
|
+
if (channel.hasSubscribers) {
|
|
143
|
+
channel.publish(factory())
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
CHANNELS,
|
|
149
|
+
tracingChannels,
|
|
150
|
+
tracePromise,
|
|
151
|
+
traceCallback,
|
|
152
|
+
publish
|
|
153
|
+
}
|
|
@@ -8,6 +8,7 @@ const shared = require('../shared')
|
|
|
8
8
|
const ConnectionError = require('../error/connection-error')
|
|
9
9
|
const { platform } = require('node:os')
|
|
10
10
|
const { build } = require('@tediousjs/connection-string')
|
|
11
|
+
const { CHANNELS, publish } = require('../diagnostics')
|
|
11
12
|
|
|
12
13
|
const DEFAULT_CONNECTION_DRIVER = ['darwin', 'linux'].includes(platform()) ? 'ODBC Driver 17 for SQL Server' : 'SQL Server Native Client 11.0'
|
|
13
14
|
|
|
@@ -58,6 +59,12 @@ class ConnectionPool extends BaseConnectionPool {
|
|
|
58
59
|
IDS.add(tds, 'Connection', connedtionId)
|
|
59
60
|
tds.setUseUTC(this.config.options.useUTC)
|
|
60
61
|
debug('connection(%d): established', IDS.get(tds))
|
|
62
|
+
publish(CHANNELS.CONNECTION_CREATE, () => ({
|
|
63
|
+
connectionId: IDS.get(tds),
|
|
64
|
+
poolId: IDS.get(this),
|
|
65
|
+
server: this.config.server,
|
|
66
|
+
database: this.config.database
|
|
67
|
+
}))
|
|
61
68
|
resolve(tds)
|
|
62
69
|
})
|
|
63
70
|
})
|
|
@@ -80,9 +87,15 @@ class ConnectionPool extends BaseConnectionPool {
|
|
|
80
87
|
resolve()
|
|
81
88
|
return
|
|
82
89
|
}
|
|
83
|
-
|
|
90
|
+
const connectionId = IDS.get(tds)
|
|
91
|
+
const poolId = IDS.get(this)
|
|
92
|
+
debug('connection(%d): destroying', connectionId)
|
|
84
93
|
tds.close(() => {
|
|
85
|
-
debug('connection(%d): destroyed',
|
|
94
|
+
debug('connection(%d): destroyed', connectionId)
|
|
95
|
+
publish(CHANNELS.CONNECTION_DESTROY, () => ({
|
|
96
|
+
connectionId,
|
|
97
|
+
poolId
|
|
98
|
+
}))
|
|
86
99
|
resolve()
|
|
87
100
|
})
|
|
88
101
|
})
|
|
@@ -172,7 +172,7 @@ class Request extends BaseRequest {
|
|
|
172
172
|
setImmediate(callback, new RequestError("You can't use table variables for bulk insert.", 'ENAME'))
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
this.parent.acquire(this, (err, connection) => {
|
|
175
|
+
this.parent.acquire(this, (err, connection, config) => {
|
|
176
176
|
let hasReturned = false
|
|
177
177
|
if (!err) {
|
|
178
178
|
debug('connection(%d): borrowed to request #%d', IDS.get(connection), IDS.get(this))
|
|
@@ -244,7 +244,10 @@ class Request extends BaseRequest {
|
|
|
244
244
|
objectid = table.path
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
return connection.queryRaw(
|
|
247
|
+
return connection.queryRaw({
|
|
248
|
+
query_str: `if object_id('${objectid.replace(/'/g, '\'\'')}') is null ${table.declare()}`,
|
|
249
|
+
query_timeout: (this.overrides.requestTimeout ?? config.requestTimeout) / 1000 // msnodesqlv8 timeouts are in seconds (<1 second not supported)
|
|
250
|
+
}, function (err) {
|
|
248
251
|
if (err) { return done(err) }
|
|
249
252
|
go()
|
|
250
253
|
})
|
|
@@ -389,7 +392,7 @@ class Request extends BaseRequest {
|
|
|
389
392
|
|
|
390
393
|
const req = connection.queryRaw({
|
|
391
394
|
query_str: command,
|
|
392
|
-
query_timeout: config.requestTimeout / 1000 // msnodesqlv8 timeouts are in seconds (<1 second not supported)
|
|
395
|
+
query_timeout: (this.overrides.requestTimeout ?? config.requestTimeout) / 1000 // msnodesqlv8 timeouts are in seconds (<1 second not supported)
|
|
393
396
|
}, params)
|
|
394
397
|
|
|
395
398
|
this._setCurrentRequest(req)
|