Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/bulk-load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,10 +429,13 @@ class BulkLoad extends EventEmitter {
collation: this.collation
};

if ((type.id & 0x30) === 0x20) {
if (column.length == null && type.resolveLength) {
column.length = type.resolveLength(column);
}
// Note: this is deliberately not keyed off the legacy variable-length
// type id bit pattern ((type.id & 0x30) === 0x20), as that pattern does
// not hold for type ids introduced in TDS 7.2 and later (e.g. XMLTYPE
// 0xF1 or VECTORTYPE 0xF5). A type has a length whenever it can resolve
// one, mirroring how precision and scale are resolved below.
if (column.length == null && type.resolveLength) {
column.length = type.resolveLength(column);
}

if (type.resolvePrecision && column.precision == null) {
Expand Down
15 changes: 9 additions & 6 deletions src/rpcrequest-payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,15 @@ class RpcRequestPayload implements Iterable<Buffer> {

const type = parameter.type;

if ((type.id & 0x30) === 0x20) {
if (parameter.length) {
param.length = parameter.length;
} else if (type.resolveLength) {
param.length = type.resolveLength(parameter);
}
// Note: this is deliberately not keyed off the legacy variable-length
// type id bit pattern ((type.id & 0x30) === 0x20), as that pattern does
// not hold for type ids introduced in TDS 7.2 and later (e.g. XMLTYPE
// 0xF1 or VECTORTYPE 0xF5). A type has a length whenever it can resolve
// one, mirroring how precision and scale are resolved below.
if (parameter.length) {
param.length = parameter.length;
} else if (type.resolveLength) {
param.length = type.resolveLength(parameter);
}

if (parameter.precision) {
Expand Down
92 changes: 92 additions & 0 deletions test/unit/rpcrequest-payload-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { assert } from 'chai';

import RpcRequestPayload from '../../src/rpcrequest-payload';
import { typeByName as TYPES, type DataType, type Parameter, type ParameterData } from '../../src/data-type';
import { type InternalConnectionOptions } from '../../src/connection';

const options = { tdsVersion: '7_4' } as InternalConnectionOptions;
const txnDescriptor = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]);

describe('RpcRequestPayload', function() {
describe('parameter length resolution', function() {
// A stub type whose id does _not_ match the legacy variable-length type
// id bit pattern ((id & 0x30) === 0x20), like the type ids introduced
// in TDS 7.2 and later (e.g. XMLTYPE 0xF1 or VECTORTYPE 0xF5).
function buildModernIdType(receivedLengths: (number | undefined)[]): DataType {
return {
id: 0xF5,
type: 'STUB',
name: 'Stub',

declaration() {
return 'stub';
},

resolveLength(parameter: Parameter) {
return (parameter.value as { length: number }).length;
},

generateTypeInfo(parameter: ParameterData) {
receivedLengths.push(parameter.length);
return Buffer.from([this.id]);
},

generateParameterLength(parameter: ParameterData) {
receivedLengths.push(parameter.length);
return Buffer.alloc(0);
},

* generateParameterData(parameter: ParameterData) {
receivedLengths.push(parameter.length);
},

validate(value) {
return value;
}
};
}

it('resolves the length for types with ids outside the legacy variable-length id bit pattern', function() {
const receivedLengths: (number | undefined)[] = [];
const parameter: Parameter = {
type: buildModernIdType(receivedLengths),
name: 'value',
value: { length: 3 },
output: false
};

assert.isNotEmpty([...new RpcRequestPayload('proc', [parameter], txnDescriptor, options, undefined)]);
assert.deepEqual(receivedLengths, [3, 3, 3]);
});

it('prefers an explicitly specified length', function() {
const receivedLengths: (number | undefined)[] = [];
const parameter: Parameter = {
type: buildModernIdType(receivedLengths),
name: 'value',
value: { length: 3 },
length: 42,
output: false
};

assert.isNotEmpty([...new RpcRequestPayload('proc', [parameter], txnDescriptor, options, undefined)]);
assert.deepEqual(receivedLengths, [42, 42, 42]);
});

it('keeps resolving the length for legacy variable-length type ids', function() {
const parameter: Parameter = {
type: TYPES.VarBinary,
name: 'value',
value: Buffer.from([1, 2, 3]),
output: false
};

const data = Buffer.concat([...new RpcRequestPayload('proc', [parameter], txnDescriptor, options, undefined)]);

// TYPE_INFO for a varbinary(3) parameter: BIGVARBINARYTYPE with a
// maximum length of 3 bytes, followed by the 3 byte value.
const typeInfo = Buffer.from([0xA5, 0x03, 0x00]);
assert.notStrictEqual(data.indexOf(typeInfo), -1);
});
});
});
Loading