From e53d9c9818c57fe71251c4e437512a53bbdc124e Mon Sep 17 00:00:00 2001 From: alexpenev-s Date: Fri, 30 Jun 2017 09:11:00 +0300 Subject: [PATCH] Error out on unbound parameters --- lib/protocol/Statement.js | 69 ++++++++++++++++++++------------------- test/lib.Statement.js | 50 +++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 39 deletions(-) diff --git a/lib/protocol/Statement.js b/lib/protocol/Statement.js index 54a2e61..1b2c2f6 100644 --- a/lib/protocol/Statement.js +++ b/lib/protocol/Statement.js @@ -104,54 +104,55 @@ Statement.prototype._normalizeInputParameters = function _normalizeInputParamete return metadata.dataType; } + function isDefined(metadata) { + return typeof values[metadata.name] !== 'undefined' + } + function getObjectValue(metadata) { - if (!util.isUndefined(values[metadata.name])) { - return values[metadata.name]; - } - return null; + return values[metadata.name]; } var parameters = { types: inputParameterMetadata.map(getDataType), values: undefined }; - if (util.isArray(values)) { - if (!values.length) { - throw new Error('Invalid input parameter values'); - } - parameters.values = values; - return parameters; - } - parameters.values = inputParameterMetadata.map(getObjectValue); - return parameters; + parameters.values = Array.isArray(values) ? values : inputParameterMetadata.filter(isDefined).map(getObjectValue); + var vals = parameters.values.length && Array.isArray(parameters.values[0]) ? parameters.values : [parameters.values]; + var unbound = vals.some(function (e) { + return e.length !== parameters.types.length; + }) + return unbound ? undefined : parameters; }; Statement.prototype._execute = function _execute(values, options, cb) { - try { - var settings = util.filter.call(this._settings, [ - 'averageRowLength', - 'fetchSize', - 'readSize', - 'rowsAsArray', - 'nestTables', - 'columnNameProperty' - ]); - var result = this._createResult(this._connection, util.extend(settings, options)); - result.setResultSetMetadata(this.resultSetMetadata); - result.setParameterMetadata(this.parameterMetadata.filter(isOutputParameter)); - this._connection.execute({ - functionCode: this.functionCode, - statementId: this.id, - parameters: this._normalizeInputParameters(values) - }, function handleReply(err, reply) { - result.handle(err, reply, cb); - }); - } catch (err) { + var settings = util.filter.call(this._settings, [ + 'averageRowLength', + 'fetchSize', + 'readSize', + 'rowsAsArray', + 'nestTables', + 'columnNameProperty' + ]); + var result = this._createResult(this._connection, util.extend(settings, options)); + var inputParams = this._normalizeInputParameters(values); + if (inputParams === undefined) { process.nextTick(function () { - cb(err); + cb(new Error('Unbound parameters found.')); }); + return this; } + + result.setResultSetMetadata(this.resultSetMetadata); + result.setParameterMetadata(this.parameterMetadata.filter(isOutputParameter)); + this._connection.execute({ + functionCode: this.functionCode, + statementId: this.id, + parameters: inputParams + }, function handleReply(err, reply) { + result.handle(err, reply, cb); + }); + return this; }; diff --git a/test/lib.Statement.js b/test/lib.Statement.js index 4377760..7fd1b54 100644 --- a/test/lib.Statement.js +++ b/test/lib.Statement.js @@ -77,7 +77,7 @@ describe('Lib', function () { }); }); - it('should execute a statement with empty parameter values', function ( + it('should generate an error on statements with empty parameter values', function ( done) { var statement = createStatement(); var values = []; @@ -87,6 +87,46 @@ describe('Lib', function () { }); }); + it('should generate an error on statements with unbound input parameters (array)', function ( + done) { + var statement = createStatement(); + var values = { 'bad': 1 }; + statement.execute(values, function (err) { + err.should.be.instanceof(Error); + done(); + }); + }); + + it('should generate an error on statements with unbound input parameters (object)', function ( + done) { + var statement = createStatement({ + parameterMetadata: [{ + dataType: TypeCode.TINYINT, + ioType: IoType.INPUT, + name: 'A' + }, { + dataType: TypeCode.SMALLINT, + ioType: IoType.OUTPUT, + name: 'B' + }, { + dataType: TypeCode.INT, + ioType: IoType.IN_OUT, + name: 'C' + }, { + dataType: TypeCode.BIGINT, + ioType: IoType.INPUT, + name: 'D' + }] + }); + + var statement = createStatement(); + var values = [1, 2]; + statement.execute(values, function (err) { + err.should.be.instanceof(Error); + done(); + }); + }); + it('should execute a statement with an options object', function ( done) { var replies = null; @@ -185,7 +225,8 @@ describe('Lib', function () { }); statement._normalizeInputParameters({ A: 1, - C: 3 + C: 3, + D: null }).should.eql({ types: [TypeCode.TINYINT, TypeCode.INT, TypeCode.BIGINT], values: [1, 3, null] @@ -199,9 +240,8 @@ describe('Lib', function () { statement._normalizeInputParameters().should.equal(EMPTY_BUFFER); }); - it('should raise an error for empty parameters values', function () { - Statement.prototype._normalizeInputParameters.bind(createStatement(), []) - .should.throw(); + it('should return undefined for non-bound parameters', function () { + (Statement.prototype._normalizeInputParameters.bind(createStatement(), [])() === undefined).should.be.true; }); });