Skip to content

Commit fa5746d

Browse files
authored
Merge pull request #1838 from dhensby/fix/tvp-schema-name
fix: include schema in TVP type declaration for sp_executesql
2 parents 2f39ff5 + 06ba07e commit fa5746d

2 files changed

Lines changed: 155 additions & 1 deletion

File tree

lib/tedious/request.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ const N_TYPES = {
2323
NumericN: 0x6C
2424
}
2525

26+
// Workaround for tedious TVP declaration not including schema name.
27+
// See: https://github.com/tediousjs/node-mssql/issues/1759
28+
const SchemaAwareTVP = Object.create(tds.TYPES.TVP, {
29+
declaration: {
30+
value: function (parameter) {
31+
const value = parameter.value
32+
if (value && value.schema) {
33+
return value.schema + '.' + value.name + ' readonly'
34+
}
35+
return value.name + ' readonly'
36+
},
37+
writable: true,
38+
configurable: true
39+
}
40+
})
41+
2642
const getTediousType = function (type) {
2743
switch (type) {
2844
case TYPES.VarChar: return tds.TYPES.VarChar
@@ -54,7 +70,7 @@ const getTediousType = function (type) {
5470
case TYPES.Binary: return tds.TYPES.Binary
5571
case TYPES.VarBinary: return tds.TYPES.VarBinary
5672
case TYPES.UDT: case TYPES.Geography: case TYPES.Geometry: return tds.TYPES.UDT
57-
case TYPES.TVP: return tds.TYPES.TVP
73+
case TYPES.TVP: return SchemaAwareTVP
5874
case TYPES.Variant: return tds.TYPES.Variant
5975
default: return type
6076
}

test/common/unit.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,4 +724,142 @@ describe('connection string auth - tedious', () => {
724724
}
725725
})
726726
})
727+
728+
describe('TVP declaration with schema', () => {
729+
const tds = require('tedious')
730+
731+
it('documents upstream tedious bug: schema missing from TVP declaration', () => {
732+
const tvp = new sql.Table('AI.UDT_StringArray')
733+
tvp.columns.add('Name', sql.NVarChar(128), { nullable: false })
734+
tvp.rows.add('TestValue1')
735+
736+
const tvpValue = {
737+
name: tvp.name,
738+
schema: tvp.schema,
739+
columns: [],
740+
rows: tvp.rows
741+
}
742+
743+
// tedious's own TVP type does NOT include schema in declaration
744+
const req = new tds.Request('SELECT 1')
745+
req.addParameter('InputList', tds.TYPES.TVP, tvpValue)
746+
const paramsStr = req.makeParamsParameter(req.parameters)
747+
assert.strictEqual(paramsStr, '@InputList UDT_StringArray readonly',
748+
'tedious itself does not include schema - this documents the upstream bug')
749+
})
750+
751+
it('schema-aware TVP type includes schema in declaration', () => {
752+
const tvp = new sql.Table('AI.UDT_StringArray')
753+
tvp.columns.add('Name', sql.NVarChar(128), { nullable: false })
754+
tvp.rows.add('TestValue1')
755+
756+
const tvpValue = {
757+
name: tvp.name,
758+
schema: tvp.schema,
759+
columns: [],
760+
rows: tvp.rows
761+
}
762+
763+
assert.strictEqual(tvp.name, 'UDT_StringArray')
764+
assert.strictEqual(tvp.schema, 'AI')
765+
766+
// Use the patched SchemaAwareTVP type from lib/tedious/request.js
767+
// Since it's not exported, recreate the same pattern to verify behavior
768+
const SchemaAwareTVP = Object.create(tds.TYPES.TVP, {
769+
declaration: {
770+
value: function (parameter) {
771+
const value = parameter.value
772+
if (value && value.schema) {
773+
return value.schema + '.' + value.name + ' readonly'
774+
}
775+
return value.name + ' readonly'
776+
},
777+
writable: true,
778+
configurable: true
779+
}
780+
})
781+
782+
const req = new tds.Request('SELECT 1')
783+
req.addParameter('InputList', SchemaAwareTVP, tvpValue)
784+
const paramsStr = req.makeParamsParameter(req.parameters)
785+
assert.strictEqual(paramsStr, '@InputList AI.UDT_StringArray readonly')
786+
})
787+
788+
it('schema-aware TVP works without schema', () => {
789+
const tvp = new sql.Table('UDT_StringArray')
790+
tvp.columns.add('Name', sql.NVarChar(128), { nullable: false })
791+
792+
const tvpValue = {
793+
name: tvp.name,
794+
schema: tvp.schema,
795+
columns: [],
796+
rows: tvp.rows
797+
}
798+
799+
assert.strictEqual(tvp.name, 'UDT_StringArray')
800+
assert.strictEqual(tvp.schema, null)
801+
802+
const SchemaAwareTVP = Object.create(tds.TYPES.TVP, {
803+
declaration: {
804+
value: function (parameter) {
805+
const value = parameter.value
806+
if (value && value.schema) {
807+
return value.schema + '.' + value.name + ' readonly'
808+
}
809+
return value.name + ' readonly'
810+
},
811+
writable: true,
812+
configurable: true
813+
}
814+
})
815+
816+
const req = new tds.Request('SELECT 1')
817+
req.addParameter('InputList', SchemaAwareTVP, tvpValue)
818+
const paramsStr = req.makeParamsParameter(req.parameters)
819+
assert.strictEqual(paramsStr, '@InputList UDT_StringArray readonly')
820+
})
821+
822+
it('schema-aware TVP inherits generateTypeInfo from tedious TVP', () => {
823+
const SchemaAwareTVP = Object.create(tds.TYPES.TVP, {
824+
declaration: {
825+
value: function (parameter) {
826+
const value = parameter.value
827+
if (value && value.schema) {
828+
return value.schema + '.' + value.name + ' readonly'
829+
}
830+
return value.name + ' readonly'
831+
},
832+
writable: true,
833+
configurable: true
834+
}
835+
})
836+
837+
// Verify it inherits all other methods from tedious TVP
838+
assert.strictEqual(SchemaAwareTVP.id, tds.TYPES.TVP.id)
839+
assert.strictEqual(SchemaAwareTVP.type, tds.TYPES.TVP.type)
840+
assert.strictEqual(SchemaAwareTVP.name, tds.TYPES.TVP.name)
841+
assert.strictEqual(SchemaAwareTVP.generateTypeInfo, tds.TYPES.TVP.generateTypeInfo)
842+
assert.strictEqual(SchemaAwareTVP.generateParameterLength, tds.TYPES.TVP.generateParameterLength)
843+
assert.strictEqual(SchemaAwareTVP.generateParameterData, tds.TYPES.TVP.generateParameterData)
844+
assert.strictEqual(SchemaAwareTVP.validate, tds.TYPES.TVP.validate)
845+
// declaration is overridden
846+
assert.notStrictEqual(SchemaAwareTVP.declaration, tds.TYPES.TVP.declaration)
847+
})
848+
})
849+
850+
describe('msnodesqlv8 TVP declaration', () => {
851+
const { declare, TYPES } = require('../../lib/datatypes')
852+
853+
it('includes schema in TVP declaration when tvpType is schema-qualified', () => {
854+
// msnodesqlv8 uses declare() from datatypes.js, which uses tvpType
855+
// When tvpType includes the schema, the declaration is correct
856+
const result = declare(TYPES.TVP, { tvpType: 'AI.UDT_StringArray' })
857+
assert.strictEqual(result, 'AI.UDT_StringArray readonly')
858+
})
859+
860+
it('works without schema in tvpType', () => {
861+
const result = declare(TYPES.TVP, { tvpType: 'UDT_StringArray' })
862+
assert.strictEqual(result, 'UDT_StringArray readonly')
863+
})
864+
})
727865
})

0 commit comments

Comments
 (0)