From a8723fbd2fa23476ea404da51b887281fb433dde Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Tue, 19 Apr 2022 12:21:36 +0100 Subject: [PATCH] fix: treat bigint as string in msnodesqlv8 driver BigInt types will lose precision if they are above the MAX_SAFE_INTEGER value. A work around for this is to use strings to represent the numbers instead. --- CHANGELOG.txt | 6 ++++- lib/msnodesqlv8/connection-pool.js | 2 ++ lib/msnodesqlv8/request.js | 2 +- test/cleanup.sql | 3 +++ test/common/tests.js | 24 +++++++++++++++++++ test/msnodesqlv8/msnodesqlv8.js | 38 ++++++++++++++++-------------- test/prepare.sql | 4 ++++ test/tedious/tedious.js | 38 ++++++++++++++++-------------- 8 files changed, 79 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 65c4e6c4..d6c6e989 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,7 @@ +Unreleased +------------------- +[fix] BigInt type in nodemsqlv8 now treated as strings in parity with the tedious drive ([#1387](https://github.com/tediousjs/node-mssql/pull/1387)) + v10.0.0 (2023-09-06) ------------------- [change] Upgrade tedious to v16 ([#1547](https://github.com/tediousjs/node-mssql/pull/1547)) @@ -69,7 +73,7 @@ v8.1.1 (2022-05-18) v8.1.0 (2022-04-06) ------------------- -[new] MSSQL CLI tool now accepts some options to allow overriding config file ((#1381](https://github.com/tediousjs/node-mssql/pull/1381)) +[new] MSSQL CLI tool now accepts some options to allow overriding config file ([#1381](https://github.com/tediousjs/node-mssql/pull/1381)) [fix] nodemsqlv8 driver tests working against Node 10 ([#1368](https://github.com/tediousjs/node-mssql/pull/1368)) v8.0.2 (2022-02-07) diff --git a/lib/msnodesqlv8/connection-pool.js b/lib/msnodesqlv8/connection-pool.js index 09b367f4..7a529179 100644 --- a/lib/msnodesqlv8/connection-pool.js +++ b/lib/msnodesqlv8/connection-pool.js @@ -47,6 +47,8 @@ class ConnectionPool extends BaseConnectionPool { return reject(err) } + tds.setUseNumericString(true) + IDS.add(tds, 'Connection', connedtionId) tds.setUseUTC(this.config.options.useUTC) debug('connection(%d): established', IDS.get(tds)) diff --git a/lib/msnodesqlv8/request.js b/lib/msnodesqlv8/request.js index c43502ef..dfd1d9f9 100644 --- a/lib/msnodesqlv8/request.js +++ b/lib/msnodesqlv8/request.js @@ -31,6 +31,7 @@ const castParameter = function (value, type) { case TYPES.NChar: case TYPES.Xml: case TYPES.Text: + case TYPES.BigInt: case TYPES.NText: if ((typeof value !== 'string') && !(value instanceof String)) { value = value.toString() @@ -39,7 +40,6 @@ const castParameter = function (value, type) { case TYPES.Int: case TYPES.TinyInt: - case TYPES.BigInt: case TYPES.SmallInt: if ((typeof value !== 'number') && !(value instanceof Number)) { value = parseInt(value) diff --git a/test/cleanup.sql b/test/cleanup.sql index 05b28409..a1ccf2f8 100644 --- a/test/cleanup.sql +++ b/test/cleanup.sql @@ -52,6 +52,9 @@ if exists (select * from sys.tables where name = 'bulk_table5') if exists (select * from sys.tables where name = 'rowsaffected_test') exec('drop table [dbo].[rowsaffected_test]') +if exists (select * from sys.tables where name = 'bignumbers') + exec('drop table [dbo].[bignumbers]') + if exists (select * from sys.tables where name = 'streaming') exec('drop table [dbo].[streaming]') diff --git a/test/common/tests.js b/test/common/tests.js index 849e1fd1..690fc898 100644 --- a/test/common/tests.js +++ b/test/common/tests.js @@ -1276,6 +1276,30 @@ module.exports = (sql, driver) => { }).catch(done) }, + 'BigInt parameters' (done) { + const req = new TestRequest() + req.input('bignumber', sql.BigInt, '9223372036854775807') + req.query('INSERT INTO [dbo].[bignumbers] (bignumber) VALUES (@bignumber)') + .then(() => { + const req2 = new TestRequest() + return req2.query('SELECT * FROM [dbo].[bignumbers]') + }) + .then((result) => { + assert.strictEqual(result.recordsets.length, 1) + assert.strictEqual(result.recordset[0].bignumber, '9223372036854775807') + done() + }) + .catch(done) + }, + + 'BigInt casted types' (done) { + const req = new TestRequest() + req.query('SELECT cast(9223372036854775807 AS BigInt) as bignumber').then(result => { + assert.strictEqual(result.recordset[0].bignumber, '9223372036854775807') + done() + }).catch(done) + }, + 'dataLength type correction' (done) { sql.on('error', err => console.error(err)) const req = new TestRequest() diff --git a/test/msnodesqlv8/msnodesqlv8.js b/test/msnodesqlv8/msnodesqlv8.js index d42e7b67..50fc30af 100644 --- a/test/msnodesqlv8/msnodesqlv8.js +++ b/test/msnodesqlv8/msnodesqlv8.js @@ -8,9 +8,10 @@ const sql = require('../../msnodesqlv8') const TESTS = require('../common/tests.js')(sql, 'msnodesqlv8') const TIMES = require('../common/times.js')(sql, 'msnodesqlv8') const versionHelper = require('../common/versionhelper') +const { readFileSync } = require('node:fs') const config = function () { - const cfg = JSON.parse(require('node:fs').readFileSync(join(__dirname, '../.mssql.json'))) + const cfg = JSON.parse(readFileSync(join(__dirname, '../.mssql.json'))) cfg.driver = 'msnodesqlv8' return cfg } @@ -19,23 +20,22 @@ let connection1 = null let connection2 = null describe('msnodesqlv8', function () { - before(done => - sql.connect(config(), function (err) { - if (err) return done(err) - - let req = new sql.Request() - req.batch(require('node:fs').readFileSync(join(__dirname, '../cleanup.sql'), 'utf8'), function (err) { - if (err) return done(err) - - req = new sql.Request() - req.batch(require('node:fs').readFileSync(join(__dirname, '../prepare.sql'), 'utf8'), function (err) { - if (err) return done(err) - - sql.close(done) + before(done => { + try { + sql.connect(config()) + .then(() => { + return new sql.Request().query(readFileSync(join(__dirname, '../cleanup.sql'), 'utf8')) }) - }) - }) - ) + .then(() => { + return new sql.Request().query(readFileSync(join(__dirname, '../prepare.sql'), 'utf8')) + }) + .catch(done) + .then(() => sql.close()) + .then(() => done()) + } catch (e) { + done(e) + } + }) afterEach(() => sql.valueHandler.clear()) describe('basic test suite', function () { @@ -91,6 +91,8 @@ describe('msnodesqlv8', function () { it('connection healthy works', done => TESTS['connection healthy works'](config(), done)) it('healthy connection goes bad', done => TESTS['healthy connection goes bad'](config(), done)) it('request timeout', done => TESTS['request timeout'](done)) + it('BigInt parameters', done => TESTS['BigInt parameters'](done)) + it('BigInt casted types', done => TESTS['BigInt casted types'](done)) it('dataLength type correction', done => TESTS['dataLength type correction'](done)) it('chunked xml support', done => TESTS['chunked xml support'](done)) @@ -245,7 +247,7 @@ describe('msnodesqlv8', function () { if (err) return done(err) const req = new sql.Request() - req.query(require('node:fs').readFileSync(join(__dirname, '../cleanup.sql'), 'utf8'), function (err) { + req.query(readFileSync(join(__dirname, '../cleanup.sql'), 'utf8'), function (err) { if (err) return done(err) sql.close(done) diff --git a/test/prepare.sql b/test/prepare.sql index 5a527ec5..84675056 100644 --- a/test/prepare.sql +++ b/test/prepare.sql @@ -145,6 +145,10 @@ exec('create table [dbo].[rowsaffected_test] ( a int not null )') +exec('create table [dbo].[bignumbers] ( + bignumber bigint +)') + ;with nums as ( select 0 AS n diff --git a/test/tedious/tedious.js b/test/tedious/tedious.js index 558ba228..38b27dbc 100644 --- a/test/tedious/tedious.js +++ b/test/tedious/tedious.js @@ -5,6 +5,7 @@ const sql = require('../../tedious.js') const assert = require('node:assert') const { join } = require('node:path') +const { readFileSync } = require('node:fs') const TESTS = require('../common/tests.js')(sql, 'tedious') const TIMES = require('../common/times.js')(sql, 'tedious') @@ -16,7 +17,7 @@ if (parseInt(process.version.match(/^v(\d+)\./)[1]) > 0) { } const config = function () { - const cfg = JSON.parse(require('node:fs').readFileSync(join(__dirname, '../.mssql.json'))) + const cfg = JSON.parse(readFileSync(join(__dirname, '../.mssql.json'))) cfg.driver = 'tedious' return cfg } @@ -25,23 +26,22 @@ let connection1 = null let connection2 = null describe('tedious', () => { - before(done => - sql.connect(config(), err => { - if (err) return done(err) - - let req = new sql.Request() - req.query(require('node:fs').readFileSync(join(__dirname, '../cleanup.sql'), 'utf8'), err => { - if (err) return done(err) - - req = new sql.Request() - req.query(require('node:fs').readFileSync(join(__dirname, '../prepare.sql'), 'utf8'), err => { - if (err) return done(err) - - sql.close(done) + before(done => { + try { + sql.connect(config()) + .then(() => { + return new sql.Request().query(readFileSync(join(__dirname, '../cleanup.sql'), 'utf8')) }) - }) - }) - ) + .then(() => { + return new sql.Request().query(readFileSync(join(__dirname, '../prepare.sql'), 'utf8')) + }) + .catch(done) + .then(() => sql.close()) + .then(() => done()) + } catch (e) { + done(e) + } + }) afterEach(() => sql.valueHandler.clear()) describe('basic test suite', () => { @@ -100,6 +100,8 @@ describe('tedious', () => { it('connection healthy works', done => TESTS['connection healthy works'](config(), done)) it('healthy connection goes bad', done => TESTS['healthy connection goes bad'](config(), done)) it('request timeout', done => TESTS['request timeout'](done, 'tedious', /Timeout: Request failed to complete in 1000ms/)) + it('BigInt parameters', done => TESTS['BigInt parameters'](done)) + it('BigInt casted types', done => TESTS['BigInt casted types'](done)) it('dataLength type correction', done => TESTS['dataLength type correction'](done)) it('type validation', done => TESTS['type validation']('query', done)) it('type validation (batch)', done => TESTS['type validation']('batch', done)) @@ -345,7 +347,7 @@ describe('tedious', () => { if (err) return done(err) const req = new sql.Request() - req.query(require('node:fs').readFileSync(join(__dirname, '../cleanup.sql'), 'utf8'), function (err) { + req.query(readFileSync(join(__dirname, '../cleanup.sql'), 'utf8'), function (err) { if (err) return done(err) sql.close(done)