Skip to content

Commit e53d194

Browse files
authored
Merge pull request #1841 from dhensby/fix/tvp-variant-error
fix: clear error when sql_variant used in TVP columns
2 parents 70b2b50 + 839ce85 commit e53d194

File tree

2 files changed

+81
-25
lines changed

2 files changed

+81
-25
lines changed

lib/tedious/request.js

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,13 @@ const parameterCorrection = function (value) {
200200
}
201201

202202
for (const col of value.columns) {
203+
const tediousType = getTediousType(col.type)
204+
if (tediousType === tds.TYPES.Variant) {
205+
throw new RequestError(`Column '${col.name}' in TVP '${value.schema ? value.schema + '.' : ''}${value.name}' uses sql_variant which is not supported by the tedious driver for TVP column types. Consider using a more specific data type.`, 'EARGS')
206+
}
203207
tvp.columns.push({
204208
name: col.name,
205-
type: getTediousType(col.type),
209+
type: tediousType,
206210
length: col.length,
207211
scale: col.scale,
208212
precision: col.precision
@@ -664,9 +668,18 @@ class Request extends BaseRequest {
664668
} catch (e) {
665669
e.message = `Validation failed for parameter '${name}'. ${e.message}`
666670
const err = new RequestError(e, 'EPARAM')
671+
delete this._cancel
672+
673+
if (!hasReturned) {
674+
for (const event in errorHandlers) {
675+
connection.removeListener(event, errorHandlers[event])
676+
}
667677

668-
this.parent.release(connection)
669-
return callback(err)
678+
this.parent.release(connection)
679+
hasReturned = true
680+
return callback(err)
681+
}
682+
return
670683
}
671684
}
672685

@@ -703,21 +716,23 @@ class Request extends BaseRequest {
703716

704717
req.sqlTextOrProcedure = `declare ${declarations.join(', ')};select ${assigns.join(', ')};${req.sqlTextOrProcedure};${batchHasOutput ? (`select 1 as [___return___], ${selects.join(', ')}`) : ''}`
705718
}
706-
} else {
707-
for (const name in this.parameters) {
708-
if (!objectHasProperty(this.parameters, name)) {
709-
continue
710-
}
711-
const param = this.parameters[name]
712-
if (param.io === 1) {
713-
req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
714-
} else {
715-
req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
716-
}
717-
}
718719
}
719720

720721
try {
722+
if (!this._isBatch) {
723+
for (const name in this.parameters) {
724+
if (!objectHasProperty(this.parameters, name)) {
725+
continue
726+
}
727+
const param = this.parameters[name]
728+
if (param.io === 1) {
729+
req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
730+
} else {
731+
req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
732+
}
733+
}
734+
}
735+
721736
connection[this._isBatch ? 'execSqlBatch' : 'execSql'](req)
722737
} catch (error) {
723738
handleError(true, connection, error)
@@ -983,19 +998,34 @@ class Request extends BaseRequest {
983998
output[parameterName] = value
984999
})
9851000

986-
for (const name in this.parameters) {
987-
if (!objectHasProperty(this.parameters, name)) {
988-
continue
1001+
try {
1002+
for (const name in this.parameters) {
1003+
if (!objectHasProperty(this.parameters, name)) {
1004+
continue
1005+
}
1006+
const param = this.parameters[name]
1007+
if (param.io === 1) {
1008+
req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
1009+
} else {
1010+
req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
1011+
}
9891012
}
990-
const param = this.parameters[name]
991-
if (param.io === 1) {
992-
req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
993-
} else {
994-
req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), { length: param.length, scale: param.scale, precision: param.precision })
1013+
1014+
connection.callProcedure(req)
1015+
} catch (error) {
1016+
const err = error instanceof RequestError ? error : new RequestError(error, 'EREQUEST')
1017+
delete this._cancel
1018+
1019+
if (!hasReturned) {
1020+
for (const event in errorHandlers) {
1021+
connection.removeListener(event, errorHandlers[event])
1022+
}
1023+
1024+
this.parent.release(connection)
1025+
hasReturned = true
1026+
callback(err)
9951027
}
9961028
}
997-
998-
connection.callProcedure(req)
9991029
})
10001030
})
10011031
}

test/common/unit.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,32 @@ describe('connection string auth - tedious', () => {
864864
})
865865
})
866866

867+
describe('TVP sql_variant validation', () => {
868+
it('throws a clear error when a TVP column uses sql_variant', (done) => {
869+
const tvp = new sql.Table('dbo.GridFilter')
870+
tvp.columns.add('FieldName', sql.NVarChar(128))
871+
tvp.columns.add('Value1', sql.Variant)
872+
873+
const Request = require('../../lib/tedious/request')
874+
const fakeConn = { on: () => {}, removeListener: () => {} }
875+
const mockPool = {
876+
config: {},
877+
connected: true,
878+
acquire: (req, cb) => cb(null, fakeConn, {}),
879+
release: () => {}
880+
}
881+
const req = new Request(mockPool)
882+
req.input('Filters', tvp)
883+
884+
req.query('SELECT 1', (err) => {
885+
assert.ok(err, 'Expected an error')
886+
assert.ok(err.message.includes('sql_variant'), `Error message should mention sql_variant, got: ${err.message}`)
887+
assert.ok(err.message.includes('Value1'), `Error message should mention column name, got: ${err.message}`)
888+
done()
889+
})
890+
})
891+
})
892+
867893
describe('_poolValidate', () => {
868894
// Reset Promise in case earlier tests replaced it (e.g. FakePromise)
869895
before(() => { sql.Promise = Promise })

0 commit comments

Comments
 (0)