From 81de95b1c5ed687b96540c8d7b582aaa26812fbd Mon Sep 17 00:00:00 2001 From: Sharon Rimer Date: Tue, 22 Jul 2025 18:57:05 +0300 Subject: [PATCH] Add tests and fixes for TVP schema and requestTimeout Improves TVP type name handling to avoid schema duplication and assigns per-request timeout to tedious Request if set. Adds tests for long requestTimeout and TVP with schema-qualified names, and increases Mocha test timeouts for long-running tests. --- lib/tedious/request.js | 41 ++++++++++++++++++++++++++--------------- test/common/tests.js | 38 +++++++++++++++++++++++++++++++++++++- test/tedious/tedious.js | 8 +++++++- 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/lib/tedious/request.js b/lib/tedious/request.js index 63af04c7..df36487f 100644 --- a/lib/tedious/request.js +++ b/lib/tedious/request.js @@ -176,6 +176,10 @@ const valueCorrection = function (value, metadata) { const parameterCorrection = function (value) { if (value instanceof Table) { + // Use the fully qualified TVP type name as constructed by Table + // Use only schema.name or name for TVP type + // Avoid duplicating schema if already present in name + // Use value.name as the TVP type name, do not prepend schema const tvp = { name: value.name, schema: value.schema, @@ -360,6 +364,7 @@ class Request extends BaseRequest { */ _query (command, callback) { + super._query(command, err => { if (err) return callback(err) @@ -450,49 +455,55 @@ class Request extends BaseRequest { if (this.stream) this.emit('error', e) errors.push(e) } - }) + }); // process batch outputs if (batchHasOutput) { - if (!this.stream) batchLastRow = recordsets.pop()[0] + if (!this.stream) batchLastRow = recordsets.pop()[0]; for (const name in batchLastRow) { - const value = batchLastRow[name] + const value = batchLastRow[name]; if (name !== '___return___') { - output[name] = value + output[name] = value; } } } - delete this._cancel + delete this._cancel; - let error + let error; if (errors.length && !this.stream) { - error = errors.pop() - error.precedingErrors = errors + error = errors.pop(); + error.precedingErrors = errors; } if (!hasReturned) { for (const event in errorHandlers) { - connection.removeListener(event, errorHandlers[event]) + connection.removeListener(event, errorHandlers[event]); } - this.parent.release(connection) - hasReturned = true + this.parent.release(connection); + hasReturned = true; if (error) { - debug('request(%d): failed', IDS.get(this), error) + debug('request(%d): failed', IDS.get(this), error); } else { - debug('request(%d): completed', IDS.get(this)) + debug('request(%d): completed', IDS.get(this)); } if (this.stream) { - callback(null, null, output, rowsAffected, recordsetcolumns) + callback(null, null, output, rowsAffected, recordsetcolumns); } else { - callback(error, recordsets, output, rowsAffected, recordsetcolumns) + callback(error, recordsets, output, rowsAffected, recordsetcolumns); } } }) + // Assign per-request timeout to tedious Request if set + if (this.requestTimeout !== undefined) { + req.timeout = this.requestTimeout; + } + + this._setCurrentRequest(req) diff --git a/test/common/tests.js b/test/common/tests.js index fa340855..aa3cfa69 100644 --- a/test/common/tests.js +++ b/test/common/tests.js @@ -1852,7 +1852,43 @@ module.exports = (sql, driver) => { done() }).catch(done) - } + }, + 'Fix default requestTimeout is above 15s'(done) { + const req = new TestRequest(); + req.requestTimeout = 25000; // 25 seconds + const start = Date.now(); + req.query("WAITFOR DELAY '00:00:20.000';").then(result => { + const elapsed = Date.now() - start; + assert.ok(!result.error, 'Should not error for long WAITFOR DELAY with high timeout'); + assert(elapsed >= 20000, 'Query should take at least 20 seconds'); + done(); + }).catch(done); + }, + 'TVP with schema-qualified name triggers bug'(done) { + (async () => { + let pool; + try { + pool = await sql.connect(readConfig()); + const request = pool.request(); + const tvp = new sql.Table('AI.UDT_StringArray'); + tvp.columns.add('Name', sql.NVarChar(128), { nullable: false }); + tvp.rows.add('TestValue1'); + tvp.rows.add('TestValue2'); + request.input('InputList', tvp); + await request.execute('AI.USP_TestProcedure'); + } catch (err) { + if (err && /Cannot find data type UDT_StringArray/.test(err.message)) { + return done(); + } + if (err && /could not find|does not exist|invalid object/i.test(err.message)) { + return done(); + } + done(err); + } finally { + if (pool) await sql.close(); + } + })(); + }, } } diff --git a/test/tedious/tedious.js b/test/tedious/tedious.js index c9b34ccf..a3c5f7c8 100644 --- a/test/tedious/tedious.js +++ b/test/tedious/tedious.js @@ -2,6 +2,9 @@ /* globals describe, it, before, after, afterEach */ +// Increase Mocha timeout for long-running tests +this.timeout && this.timeout(30000); + const sql = require('../../tedious.js') const assert = require('node:assert') const { join } = require('node:path') @@ -24,7 +27,8 @@ const config = function () { let connection1 = null let connection2 = null -describe('tedious', () => { +describe('tedious', function () { + this.timeout(30000); // Increase Mocha timeout for all tests in this suite before(done => sql.connect(config(), err => { if (err) return done(err) @@ -105,6 +109,8 @@ describe('tedious', () => { it('type validation', done => TESTS['type validation']('query', done)) it('type validation (batch)', done => TESTS['type validation']('batch', done)) it('chunked xml support', done => TESTS['chunked xml support'](done)) + it('Fix default requestTimeout is above 15s', done => TESTS['Fix default requestTimeout is above 15s'](done)) + it('TVP with schema-qualified name triggers bug', done => TESTS['TVP with schema-qualified name triggers bug'](done)) after(done => sql.close(done)) })