diff --git a/lib/base/index.js b/lib/base/index.js index 80ca5fc8..bd29209a 100644 --- a/lib/base/index.js +++ b/lib/base/index.js @@ -54,6 +54,13 @@ Object.defineProperty(module.exports, 'Promise', { } }) +Object.defineProperty(module.exports, 'valueHandler', { + enumerable: true, + value: shared.valueHandler, + writable: false, + configurable: false +}) + for (const key in TYPES) { const value = TYPES[key] module.exports.exports[key] = value diff --git a/lib/msnodesqlv8/index.js b/lib/msnodesqlv8/index.js index da4e68b8..1e68b7d3 100644 --- a/lib/msnodesqlv8/index.js +++ b/lib/msnodesqlv8/index.js @@ -22,6 +22,13 @@ Object.defineProperty(module.exports, 'Promise', { } }) +Object.defineProperty(module.exports, 'valueHandler', { + enumerable: true, + value: base.valueHandler, + writable: false, + configurable: false +}) + base.driver.name = 'msnodesqlv8' base.driver.ConnectionPool = ConnectionPool base.driver.Transaction = Transaction diff --git a/lib/msnodesqlv8/request.js b/lib/msnodesqlv8/request.js index abd9f74d..c6431058 100644 --- a/lib/msnodesqlv8/request.js +++ b/lib/msnodesqlv8/request.js @@ -8,6 +8,7 @@ const { IDS, objectHasProperty } = require('../utils') const { TYPES, DECLARATIONS, declare } = require('../datatypes') const { PARSERS: UDT } = require('../udt') const Table = require('../table') +const { valueHandler } = require('../shared') const JSON_COLUMN_ID = 'JSON_F52E2B61-18A1-11d1-B105-00805F49916B' const XML_COLUMN_ID = 'XML_F52E2B61-18A1-11d1-B105-00805F49916B' @@ -122,7 +123,12 @@ const createColumns = function (metadata, arrayRowMode) { } const valueCorrection = function (value, metadata) { - if ((metadata.sqlType === 'time') && (value != null)) { + const type = metadata && objectHasProperty(metadata, 'sqlType') && objectHasProperty(DECLARATIONS, metadata.sqlType) + ? DECLARATIONS[metadata.sqlType] + : null + if (type && valueHandler.has(type)) { + return valueHandler.get(type)(value) + } else if ((metadata.sqlType === 'time') && (value != null)) { value.setFullYear(1970) return value } else if ((metadata.sqlType === 'udt') && (value != null)) { diff --git a/lib/shared.js b/lib/shared.js index 5dc982c3..16ef7d79 100644 --- a/lib/shared.js +++ b/lib/shared.js @@ -105,3 +105,10 @@ Object.defineProperty(module.exports, 'Promise', { PromiseLibrary = value } }) + +Object.defineProperty(module.exports, 'valueHandler', { + enumerable: true, + value: new Map(), + writable: false, + configurable: false +}) diff --git a/lib/tedious/index.js b/lib/tedious/index.js index 0a0d2af5..88eea038 100644 --- a/lib/tedious/index.js +++ b/lib/tedious/index.js @@ -22,6 +22,13 @@ Object.defineProperty(module.exports, 'Promise', { } }) +Object.defineProperty(module.exports, 'valueHandler', { + enumerable: true, + value: base.valueHandler, + writable: false, + configurable: false +}) + base.driver.name = 'tedious' base.driver.ConnectionPool = ConnectionPool base.driver.Transaction = Transaction diff --git a/lib/tedious/request.js b/lib/tedious/request.js index 639c5eb3..6c6b806e 100644 --- a/lib/tedious/request.js +++ b/lib/tedious/request.js @@ -8,6 +8,7 @@ const { IDS, objectHasProperty } = require('../utils') const { TYPES, DECLARATIONS, declare, cast } = require('../datatypes') const Table = require('../table') const { PARSERS: UDT } = require('../udt') +const { valueHandler } = require('../shared') const JSON_COLUMN_ID = 'JSON_F52E2B61-18A1-11d1-B105-00805F49916B' const XML_COLUMN_ID = 'XML_F52E2B61-18A1-11d1-B105-00805F49916B' @@ -159,7 +160,10 @@ const createColumns = function (metadata, arrayRowMode) { } const valueCorrection = function (value, metadata) { - if ((metadata.type === tds.TYPES.UDT) && (value != null)) { + const type = getMssqlType(metadata.type) + if (valueHandler.has(type)) { + return valueHandler.get(type)(value) + } else if ((metadata.type === tds.TYPES.UDT) && (value != null)) { if (UDT[metadata.udtInfo.typeName]) { return UDT[metadata.udtInfo.typeName](value) } else { diff --git a/test/common/tests.js b/test/common/tests.js index 813fe783..9e215046 100644 --- a/test/common/tests.js +++ b/test/common/tests.js @@ -123,6 +123,19 @@ module.exports = (sql, driver) => { } return { + 'value handler' (done) { + let callCount = 0 + const callArgs = [] + // assign a "spy" to the valuehandler for varchar + sql.valueHandler.set(sql.TYPES.VarChar, function () { callCount++; callArgs.push(arguments); return arguments[0].toUpperCase() }) + sql.query('SELECT TOP 1 * FROM [streaming] ').then((result) => { + assert.strictEqual(callCount, 1) + assert.strictEqual(result.recordset.length, 1) + assert.notStrictEqual(result.recordset[0], callArgs[0]) + assert.notStrictEqual(result.recordset[0], callArgs[0][0].toUpperCase()) + done() + }).catch(done) + }, 'stored procedure' (mode, done) { const req = new TestRequest() req.input('in', sql.Int, null) diff --git a/test/common/unit.js b/test/common/unit.js index 9c18eecc..1efc449e 100644 --- a/test/common/unit.js +++ b/test/common/unit.js @@ -1,6 +1,6 @@ 'use strict' -/* globals describe, it */ +/* globals describe, it, afterEach */ const sql = require('../../') const assert = require('assert') @@ -352,3 +352,37 @@ describe('config cloning', () => { assert.notDeepStrictEqual(options, pool.config.options) }) }) + +describe('value handlers', () => { + afterEach('reset valueHandler', () => { + sql.valueHandler.clear() + }) + it('can set a value handler', () => { + assert.strictEqual(sql.valueHandler instanceof Map, true) + assert.strictEqual(sql.valueHandler.size, 0) + sql.valueHandler.set(sql.TYPES.Int, (value) => value.toUpperCase()) + assert.strictEqual(sql.valueHandler.size, 1) + assert.strictEqual(sql.valueHandler.has(sql.TYPES.Int), true) + }) + it('can delete a value handler', () => { + assert.strictEqual(sql.valueHandler instanceof Map, true) + assert.strictEqual(sql.valueHandler.size, 0) + sql.valueHandler.set(sql.TYPES.Int, (value) => value.toUpperCase()) + assert.strictEqual(sql.valueHandler.size, 1) + assert.strictEqual(sql.valueHandler.has(sql.TYPES.Int), true) + sql.valueHandler.delete(sql.TYPES.Int) + assert.strictEqual(sql.valueHandler.has(sql.TYPES.Int), false) + assert.strictEqual(sql.valueHandler.size, 0) + }) + it('can reset all value handlers', () => { + assert.strictEqual(sql.valueHandler instanceof Map, true) + assert.strictEqual(sql.valueHandler.size, 0) + sql.valueHandler.set(sql.TYPES.Int, (value) => value.toUpperCase()) + sql.valueHandler.set(sql.TYPES.BigInt, (value) => value.toUpperCase()) + assert.strictEqual(sql.valueHandler.size, 2) + assert.strictEqual(sql.valueHandler.has(sql.TYPES.Int), true) + assert.strictEqual(sql.valueHandler.has(sql.TYPES.BigInt), true) + sql.valueHandler.clear() + assert.strictEqual(sql.valueHandler.size, 0) + }) +}) diff --git a/test/msnodesqlv8/msnodesqlv8.js b/test/msnodesqlv8/msnodesqlv8.js index 51c5c00d..96d766c7 100644 --- a/test/msnodesqlv8/msnodesqlv8.js +++ b/test/msnodesqlv8/msnodesqlv8.js @@ -36,6 +36,7 @@ describe('msnodesqlv8', function () { }) }) ) + afterEach(() => sql.valueHandler.clear()) describe('basic test suite', function () { before(function (done) { @@ -43,6 +44,7 @@ describe('msnodesqlv8', function () { sql.connect(cfg, done) }) + it('value handler', done => TESTS['value handler'](done)) it('stored procedure (exec)', done => TESTS['stored procedure']('execute', done)) it('stored procedure (batch)', done => TESTS['stored procedure']('batch', done)) it('user defined types', done => TESTS['user defined types'](done)) diff --git a/test/tedious/tedious.js b/test/tedious/tedious.js index b9c0d416..99caffbf 100644 --- a/test/tedious/tedious.js +++ b/test/tedious/tedious.js @@ -42,6 +42,7 @@ describe('tedious', () => { }) }) ) + afterEach(() => sql.valueHandler.clear()) describe('basic test suite', () => { before((done) => { @@ -50,6 +51,7 @@ describe('tedious', () => { sql.connect(cfg, done) }) + it('value handler', done => TESTS['value handler'](done)) it('stored procedure (exec)', done => TESTS['stored procedure']('execute', done)) it('stored procedure (batch)', done => TESTS['stored procedure']('batch', done)) it('user defined types', done => TESTS['user defined types'](done))