diff --git a/src/data-types/tvp.ts b/src/data-types/tvp.ts index 118157bf4..aba75940d 100644 --- a/src/data-types/tvp.ts +++ b/src/data-types/tvp.ts @@ -1,4 +1,5 @@ import { type DataType } from '../data-type'; +import { InputError } from '../errors'; import WritableTrackingBuffer from '../tracking-buffer/writable-tracking-buffer'; const TVP_ROW_TOKEN = Buffer.from([0x01]); @@ -83,8 +84,15 @@ const TVP: DataType = { const column = columns[k]; const value = row[k]; + let paramValue; + try { + paramValue = column.type.validate(value, parameter.collation); + } catch (error) { + throw new InputError(`TVP column '${column.name}' has invalid data at row index ${i}`, { cause: error }); + } + const param = { - value: column.type.validate(value, parameter.collation), + value: paramValue, length: column.length, scale: column.scale, precision: column.precision diff --git a/src/errors.ts b/src/errors.ts index 097d7c91e..049826d9e 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -26,3 +26,5 @@ export class RequestError extends Error { this.code = code; } } + +export class InputError extends TypeError {} diff --git a/src/rpcrequest-payload.ts b/src/rpcrequest-payload.ts index 20aff27a9..d2d15e2ae 100644 --- a/src/rpcrequest-payload.ts +++ b/src/rpcrequest-payload.ts @@ -3,6 +3,7 @@ import { writeToTrackingBuffer } from './all-headers'; import { type Parameter, type ParameterData } from './data-type'; import { type InternalConnectionOptions } from './connection'; import { Collation } from './collation'; +import { InputError } from './errors'; // const OPTION = { // WITH_RECOMPILE: 0x01, @@ -113,7 +114,11 @@ class RpcRequestPayload implements Iterable { yield type.generateTypeInfo(param, this.options); yield type.generateParameterLength(param, this.options); - yield * type.generateParameterData(param, this.options); + try { + yield * type.generateParameterData(param, this.options); + } catch (error) { + throw new InputError(`Input parameter '${parameter.name}' could not be validated`, { cause: error }); + } } } diff --git a/test/integration/tvp-test.js b/test/integration/tvp-test.js index a9dd0c0c6..955895bb3 100644 --- a/test/integration/tvp-test.js +++ b/test/integration/tvp-test.js @@ -9,6 +9,7 @@ import Request from '../../src/request'; import { debugOptionsFromEnv } from '../helpers/debug-options-from-env'; import defaultConfig from '../config'; +import { InputError } from '../../src/errors'; function getConfig() { const config = { @@ -181,8 +182,14 @@ describe('calling a procedure that takes and returns a TVP', function() { it('correctly handles validation errors', function(done) { const request = new Request('__tediousTvpTest', (err) => { - assert.instanceOf(err, TypeError); - assert.strictEqual(err?.message, 'Value must be between 0 and 255, inclusive.'); + assert.instanceOf(err, InputError); + assert.strictEqual(err?.message, 'Input parameter \'tvp\' could not be validated'); + + assert.instanceOf(err?.cause, InputError); + assert.strictEqual(/** @type {InputError} */(err?.cause).message, 'TVP column \'b\' has invalid data at row index 0'); + + assert.instanceOf(/** @type {InputError} */(err?.cause).cause, TypeError); + assert.strictEqual(/** @type {TypeError} */(/** @type {InputError} */(err?.cause).cause).message, 'Value must be between 0 and 255, inclusive.'); const request = new Request('SELECT 1', done); connection.execSql(request);