Skip to content

Commit 0db36a4

Browse files
dhensbyclaude
andcommitted
fix: prevent TypeError in batch with output parameters when sql errors
When a batch with declared output parameters fails before the appended `select 1 as [___return___], ...` trailer can run (e.g. a compile error or a runtime error under SET XACT_ABORT ON), `recordsets` is empty and `recordsets.pop()[0]` throws synchronously inside the tedious request callback, preventing the user callback from being invoked and leaving the request promise unsettled. Guard the access with optional chaining so the SQL error propagates through the existing callback path and rejects the promise normally. Fixes #1863 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 204a9b3 commit 0db36a4

4 files changed

Lines changed: 19 additions & 1 deletion

File tree

lib/tedious/request.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ class Request extends BaseRequest {
477477

478478
// process batch outputs
479479
if (batchHasOutput) {
480-
if (!this.stream) batchLastRow = recordsets.pop()[0]
480+
if (!this.stream) batchLastRow = recordsets.pop()?.[0]
481481

482482
for (const name in batchLastRow) {
483483
const value = batchLastRow[name]

test/common/tests.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,22 @@ module.exports = (sql, driver) => {
644644
}).catch(done)
645645
},
646646

647+
'batch with output parameters and sql error' (done) {
648+
const req = new TestRequest()
649+
req.output('out', sql.Int)
650+
req.batch('select * from notexistingtable').then(() => {
651+
done(new Error('expected batch to reject with sql error'))
652+
}).catch(err => {
653+
try {
654+
assert.ok(err instanceof sql.RequestError, `expected RequestError, got ${err && err.constructor.name}: ${err && err.message}`)
655+
assert.strictEqual(err.code, 'EREQUEST')
656+
done()
657+
} catch (assertionError) {
658+
done(assertionError)
659+
}
660+
})
661+
},
662+
647663
'create procedure batch' (done) {
648664
let req = new TestRequest()
649665
req.batch('create procedure #temporary as select 1 as num').then(result => {

test/msnodesqlv8/msnodesqlv8.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ describe('msnodesqlv8', function () {
7474
it('query with pipe and back pressure', (done) => TESTS['query with pipe and back pressure'](done))
7575
it('batch', done => TESTS.batch(done))
7676
it('batch (stream)', done => TESTS.batch(done, true))
77+
it('batch with output parameters and sql error', done => TESTS['batch with output parameters and sql error'](done))
7778
it('create procedure batch', done => TESTS['create procedure batch'](done))
7879
it('prepared statement', done => TESTS['prepared statement'](done))
7980
it('prepared statement that fails to prepare throws', done => TESTS['prepared statement that fails to prepare throws'](done))

test/tedious/tedious.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ describe('tedious', () => {
8181
it('query with pipe and back pressure', (done) => TESTS['query with pipe and back pressure'](done))
8282
it('query with duplicate output column names', done => TESTS['query with duplicate output column names'](done))
8383
it('batch', done => TESTS.batch(done))
84+
it('batch with output parameters and sql error', done => TESTS['batch with output parameters and sql error'](done))
8485
it('create procedure batch', done => TESTS['create procedure batch'](done))
8586
it('prepared statement', done => TESTS['prepared statement'](done))
8687
it('prepared statement that fails to prepare throws', done => TESTS['prepared statement that fails to prepare throws'](done))

0 commit comments

Comments
 (0)